diff --git a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs
index 946546263ea72..b81a4bfe149dc 100644
--- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs
+++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs
@@ -1,4 +1,4 @@
-use super::{CompileTimeEvalContext, CompileTimeInterpreter, ConstEvalErr, MemoryExtra};
+use super::{CompileTimeEvalContext, CompileTimeInterpreter, ConstEvalErr};
 use crate::interpret::eval_nullary_intrinsic;
 use crate::interpret::{
     intern_const_alloc_recursive, Allocation, ConstAlloc, ConstValue, CtfeValidationMode, GlobalId,
@@ -100,8 +100,7 @@ pub(super) fn mk_eval_cx<'mir, 'tcx>(
         tcx,
         root_span,
         param_env,
-        CompileTimeInterpreter::new(tcx.const_eval_limit()),
-        MemoryExtra { can_access_statics },
+        CompileTimeInterpreter::new(tcx.const_eval_limit(), can_access_statics),
     )
 }
 
@@ -285,10 +284,9 @@ pub fn eval_to_allocation_raw_provider<'tcx>(
         tcx,
         tcx.def_span(def.did),
         key.param_env,
-        CompileTimeInterpreter::new(tcx.const_eval_limit()),
         // Statics (and promoteds inside statics) may access other statics, because unlike consts
         // they do not have to behave "as if" they were evaluated at runtime.
-        MemoryExtra { can_access_statics: is_static },
+        CompileTimeInterpreter::new(tcx.const_eval_limit(), /*can_access_statics:*/ is_static),
     );
 
     let res = ecx.load_mir(cid.instance.def, cid.promoted);
diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs
index 2b58c1e8233c1..5aae4a9096004 100644
--- a/compiler/rustc_const_eval/src/const_eval/machine.rs
+++ b/compiler/rustc_const_eval/src/const_eval/machine.rs
@@ -93,10 +93,7 @@ pub struct CompileTimeInterpreter<'mir, 'tcx> {
 
     /// The virtual call stack.
     pub(crate) stack: Vec<Frame<'mir, 'tcx, AllocId, ()>>,
-}
 
-#[derive(Copy, Clone, Debug)]
-pub struct MemoryExtra {
     /// We need to make sure consts never point to anything mutable, even recursively. That is
     /// relied on for pattern matching on consts with references.
     /// To achieve this, two pieces have to work together:
@@ -107,8 +104,12 @@ pub struct MemoryExtra {
 }
 
 impl<'mir, 'tcx> CompileTimeInterpreter<'mir, 'tcx> {
-    pub(super) fn new(const_eval_limit: Limit) -> Self {
-        CompileTimeInterpreter { steps_remaining: const_eval_limit.0, stack: Vec::new() }
+    pub(super) fn new(const_eval_limit: Limit, can_access_statics: bool) -> Self {
+        CompileTimeInterpreter {
+            steps_remaining: const_eval_limit.0,
+            stack: Vec::new(),
+            can_access_statics,
+        }
     }
 }
 
@@ -233,8 +234,6 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
 
     type MemoryKind = MemoryKind;
 
-    type MemoryExtra = MemoryExtra;
-
     const PANIC_ON_ALLOC_FAIL: bool = false; // will be raised as a proper error
 
     fn load_mir(
@@ -345,7 +344,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
                     Err(err) => throw_ub_format!("align has to be a power of 2, {}", err),
                 };
 
-                let ptr = ecx.memory.allocate(
+                let ptr = ecx.allocate_ptr(
                     Size::from_bytes(size as u64),
                     align,
                     interpret::MemoryKind::Machine(MemoryKind::Heap),
@@ -365,14 +364,14 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
 
                 // If an allocation is created in an another const,
                 // we don't deallocate it.
-                let (alloc_id, _, _) = ecx.memory.ptr_get_alloc(ptr)?;
+                let (alloc_id, _, _) = ecx.ptr_get_alloc_id(ptr)?;
                 let is_allocated_in_another_const = matches!(
                     ecx.tcx.get_global_alloc(alloc_id),
                     Some(interpret::GlobalAlloc::Memory(_))
                 );
 
                 if !is_allocated_in_another_const {
-                    ecx.memory.deallocate(
+                    ecx.deallocate_ptr(
                         ptr,
                         Some((size, align)),
                         interpret::MemoryKind::Machine(MemoryKind::Heap),
@@ -472,7 +471,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
     }
 
     fn before_access_global(
-        memory_extra: &MemoryExtra,
+        machine: &Self,
         alloc_id: AllocId,
         alloc: ConstAllocation<'tcx>,
         static_def_id: Option<DefId>,
@@ -488,7 +487,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
             }
         } else {
             // Read access. These are usually allowed, with some exceptions.
-            if memory_extra.can_access_statics {
+            if machine.can_access_statics {
                 // Machine configuration allows us read from anything (e.g., `static` initializer).
                 Ok(())
             } else if static_def_id.is_some() {
diff --git a/compiler/rustc_const_eval/src/interpret/cast.rs b/compiler/rustc_const_eval/src/interpret/cast.rs
index 5be0a183cf6b4..a244b79ed0754 100644
--- a/compiler/rustc_const_eval/src/interpret/cast.rs
+++ b/compiler/rustc_const_eval/src/interpret/cast.rs
@@ -56,7 +56,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                         )
                         .ok_or_else(|| err_inval!(TooGeneric))?;
 
-                        let fn_ptr = self.memory.create_fn_alloc(FnVal::Instance(instance));
+                        let fn_ptr = self.create_fn_alloc_ptr(FnVal::Instance(instance));
                         self.write_pointer(fn_ptr, dest)?;
                     }
                     _ => span_bug!(self.cur_span(), "reify fn pointer on {:?}", src.layout.ty),
@@ -87,7 +87,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                             substs,
                             ty::ClosureKind::FnOnce,
                         );
-                        let fn_ptr = self.memory.create_fn_alloc(FnVal::Instance(instance));
+                        let fn_ptr = self.create_fn_alloc_ptr(FnVal::Instance(instance));
                         self.write_pointer(fn_ptr, dest)?;
                     }
                     _ => span_bug!(self.cur_span(), "closure fn pointer on {:?}", src.layout.ty),
@@ -153,8 +153,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                 return Ok(**src);
             } else {
                 // Casting the metadata away from a fat ptr.
-                assert_eq!(src.layout.size, 2 * self.memory.pointer_size());
-                assert_eq!(dest_layout.size, self.memory.pointer_size());
+                assert_eq!(src.layout.size, 2 * self.pointer_size());
+                assert_eq!(dest_layout.size, self.pointer_size());
                 assert!(src.layout.ty.is_unsafe_ptr());
                 return match **src {
                     Immediate::ScalarPair(data, _) => Ok(data.into()),
diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs
index 1b8186b5aadba..a2ea0f516bfa4 100644
--- a/compiler/rustc_const_eval/src/interpret/eval_context.rs
+++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs
@@ -22,9 +22,9 @@ use rustc_span::{Pos, Span};
 use rustc_target::abi::{call::FnAbi, Align, HasDataLayout, Size, TargetDataLayout};
 
 use super::{
-    AllocCheck, AllocId, GlobalId, Immediate, InterpErrorInfo, InterpResult, MPlaceTy, Machine,
-    MemPlace, MemPlaceMeta, Memory, MemoryKind, Operand, Place, PlaceTy, Pointer,
-    PointerArithmetic, Provenance, Scalar, ScalarMaybeUninit, StackPopJump,
+    AllocId, GlobalId, Immediate, InterpErrorInfo, InterpResult, MPlaceTy, Machine, MemPlace,
+    MemPlaceMeta, Memory, MemoryKind, Operand, Place, PlaceTy, PointerArithmetic, Provenance,
+    Scalar, ScalarMaybeUninit, StackPopJump,
 };
 use crate::transform::validate::equal_up_to_regions;
 
@@ -413,13 +413,12 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         root_span: Span,
         param_env: ty::ParamEnv<'tcx>,
         machine: M,
-        memory_extra: M::MemoryExtra,
     ) -> Self {
         InterpCx {
             machine,
             tcx: tcx.at(root_span),
             param_env,
-            memory: Memory::new(tcx, memory_extra),
+            memory: Memory::new(),
             recursion_limit: tcx.recursion_limit(),
         }
     }
@@ -433,49 +432,6 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
             .map_or(self.tcx.span, |f| f.current_span())
     }
 
-    #[inline(always)]
-    pub fn scalar_to_ptr(&self, scalar: Scalar<M::PointerTag>) -> Pointer<Option<M::PointerTag>> {
-        self.memory.scalar_to_ptr(scalar)
-    }
-
-    /// Test if this value might be null.
-    /// If the machine does not support ptr-to-int casts, this is conservative.
-    pub fn scalar_may_be_null(&self, scalar: Scalar<M::PointerTag>) -> bool {
-        match scalar.try_to_int() {
-            Ok(int) => int.is_null(),
-            Err(_) => {
-                // Can only happen during CTFE.
-                let ptr = self.scalar_to_ptr(scalar);
-                match self.memory.ptr_try_get_alloc(ptr) {
-                    Ok((alloc_id, offset, _)) => {
-                        let (size, _align) = self
-                            .memory
-                            .get_size_and_align(alloc_id, AllocCheck::MaybeDead)
-                            .expect("alloc info with MaybeDead cannot fail");
-                        // If the pointer is out-of-bounds, it may be null.
-                        // Note that one-past-the-end (offset == size) is still inbounds, and never null.
-                        offset > size
-                    }
-                    Err(_offset) => bug!("a non-int scalar is always a pointer"),
-                }
-            }
-        }
-    }
-
-    /// Call this to turn untagged "global" pointers (obtained via `tcx`) into
-    /// the machine pointer to the allocation.  Must never be used
-    /// for any other pointers, nor for TLS statics.
-    ///
-    /// Using the resulting pointer represents a *direct* access to that memory
-    /// (e.g. by directly using a `static`),
-    /// as opposed to access through a pointer that was created by the program.
-    ///
-    /// This function can fail only if `ptr` points to an `extern static`.
-    #[inline(always)]
-    pub fn global_base_pointer(&self, ptr: Pointer) -> InterpResult<'tcx, Pointer<M::PointerTag>> {
-        self.memory.global_base_pointer(ptr)
-    }
-
     #[inline(always)]
     pub(crate) fn stack(&self) -> &[Frame<'mir, 'tcx, M::PointerTag, M::FrameExtra>] {
         M::stack(self)
@@ -949,9 +905,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
             trace!(
                 "deallocating local {:?}: {:?}",
                 local,
-                self.memory.dump_alloc(ptr.provenance.unwrap().get_alloc_id())
+                self.dump_alloc(ptr.provenance.unwrap().get_alloc_id())
             );
-            self.memory.deallocate(ptr, None, MemoryKind::Stack)?;
+            self.deallocate_ptr(ptr, None, MemoryKind::Stack)?;
         };
         Ok(())
     }
@@ -1057,7 +1013,7 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> std::fmt::Debug
                     }
                 }
 
-                write!(fmt, ": {:?}", self.ecx.memory.dump_allocs(allocs))
+                write!(fmt, ": {:?}", self.ecx.dump_allocs(allocs))
             }
             Place::Ptr(mplace) => match mplace.ptr.provenance.map(Provenance::get_alloc_id) {
                 Some(alloc_id) => write!(
@@ -1065,7 +1021,7 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> std::fmt::Debug
                     "by align({}) ref {:?}: {:?}",
                     mplace.align.bytes(),
                     mplace.ptr,
-                    self.ecx.memory.dump_alloc(alloc_id)
+                    self.ecx.dump_alloc(alloc_id)
                 ),
                 ptr => write!(fmt, " integral by ref: {:?}", ptr),
             },
diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs
index c80d7d7178742..a956a478cef9e 100644
--- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs
+++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs
@@ -318,7 +318,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                 // exception from the exception.)
                 // This is the dual to the special exception for offset-by-0
                 // in the inbounds pointer offset operation (see `ptr_offset_inbounds` below).
-                match (self.memory.ptr_try_get_alloc(a), self.memory.ptr_try_get_alloc(b)) {
+                match (self.ptr_try_get_alloc_id(a), self.ptr_try_get_alloc_id(b)) {
                     (Err(a), Err(b)) if a == b && a != 0 => {
                         // Both are the same non-null integer.
                         self.write_scalar(Scalar::from_machine_isize(0, self), dest)?;
@@ -335,13 +335,13 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                             );
                         }
                         // And they must both be valid for zero-sized accesses ("in-bounds or one past the end").
-                        self.memory.check_ptr_access_align(
+                        self.check_ptr_access_align(
                             a,
                             Size::ZERO,
                             Align::ONE,
                             CheckInAllocMsg::OffsetFromTest,
                         )?;
-                        self.memory.check_ptr_access_align(
+                        self.check_ptr_access_align(
                             b,
                             Size::ZERO,
                             Align::ONE,
@@ -545,7 +545,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         let min_ptr = if offset_bytes >= 0 { ptr } else { offset_ptr };
         let size = offset_bytes.unsigned_abs();
         // This call handles checking for integer/null pointers.
-        self.memory.check_ptr_access_align(
+        self.check_ptr_access_align(
             min_ptr,
             Size::from_bytes(size),
             Align::ONE,
@@ -577,7 +577,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         let src = self.read_pointer(&src)?;
         let dst = self.read_pointer(&dst)?;
 
-        self.memory.copy(src, align, dst, align, size, nonoverlapping)
+        self.mem_copy(src, align, dst, align, size, nonoverlapping)
     }
 
     pub(crate) fn write_bytes_intrinsic(
@@ -600,7 +600,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
             .ok_or_else(|| err_ub_format!("overflow computing total size of `write_bytes`"))?;
 
         let bytes = std::iter::repeat(byte).take(len.bytes_usize());
-        self.memory.write_bytes(dst, bytes)
+        self.write_bytes_ptr(dst, bytes)
     }
 
     pub(crate) fn raw_eq_intrinsic(
@@ -613,8 +613,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
 
         let lhs = self.read_pointer(lhs)?;
         let rhs = self.read_pointer(rhs)?;
-        let lhs_bytes = self.memory.read_bytes(lhs, layout.size)?;
-        let rhs_bytes = self.memory.read_bytes(rhs, layout.size)?;
+        let lhs_bytes = self.read_bytes_ptr(lhs, layout.size)?;
+        let rhs_bytes = self.read_bytes_ptr(rhs, layout.size)?;
         Ok(Scalar::from_bool(lhs_bytes == rhs_bytes))
     }
 }
diff --git a/compiler/rustc_const_eval/src/interpret/machine.rs b/compiler/rustc_const_eval/src/interpret/machine.rs
index 7d75c84d10835..39aec33819ce8 100644
--- a/compiler/rustc_const_eval/src/interpret/machine.rs
+++ b/compiler/rustc_const_eval/src/interpret/machine.rs
@@ -14,7 +14,7 @@ use rustc_target::spec::abi::Abi;
 
 use super::{
     AllocId, AllocRange, Allocation, ConstAllocation, Frame, ImmTy, InterpCx, InterpResult,
-    LocalValue, MemPlace, Memory, MemoryKind, OpTy, Operand, PlaceTy, Pointer, Provenance, Scalar,
+    LocalValue, MemPlace, MemoryKind, OpTy, Operand, PlaceTy, Pointer, Provenance, Scalar,
     StackPopUnwind,
 };
 
@@ -96,11 +96,6 @@ pub trait Machine<'mir, 'tcx>: Sized {
     /// Extra data stored in every call frame.
     type FrameExtra;
 
-    /// Extra data stored in memory. A reference to this is available when `AllocExtra`
-    /// gets initialized, so you can e.g., have an `Rc` here if there is global state you
-    /// need access to in the `AllocExtra` hooks.
-    type MemoryExtra;
-
     /// Extra data stored in every allocation.
     type AllocExtra: Debug + Clone + 'static;
 
@@ -123,11 +118,11 @@ pub trait Machine<'mir, 'tcx>: Sized {
     const PANIC_ON_ALLOC_FAIL: bool;
 
     /// Whether memory accesses should be alignment-checked.
-    fn enforce_alignment(memory_extra: &Self::MemoryExtra) -> bool;
+    fn enforce_alignment(ecx: &InterpCx<'mir, 'tcx, Self>) -> bool;
 
     /// Whether, when checking alignment, we should `force_int` and thus support
     /// custom alignment logic based on whatever the integer address happens to be.
-    fn force_int_for_alignment_check(memory_extra: &Self::MemoryExtra) -> bool;
+    fn force_int_for_alignment_check(ecx: &InterpCx<'mir, 'tcx, Self>) -> bool;
 
     /// Whether to enforce the validity invariant
     fn enforce_validity(ecx: &InterpCx<'mir, 'tcx, Self>) -> bool;
@@ -251,7 +246,7 @@ pub trait Machine<'mir, 'tcx>: Sized {
     /// `def_id` is `Some` if this is the "lazy" allocation of a static.
     #[inline]
     fn before_access_global(
-        _memory_extra: &Self::MemoryExtra,
+        _machine: &Self,
         _alloc_id: AllocId,
         _allocation: ConstAllocation<'tcx>,
         _static_def_id: Option<DefId>,
@@ -270,7 +265,7 @@ pub trait Machine<'mir, 'tcx>: Sized {
 
     /// Return the root pointer for the given `extern static`.
     fn extern_static_base_pointer(
-        mem: &Memory<'mir, 'tcx, Self>,
+        ecx: &InterpCx<'mir, 'tcx, Self>,
         def_id: DefId,
     ) -> InterpResult<'tcx, Pointer<Self::PointerTag>>;
 
@@ -279,19 +274,19 @@ pub trait Machine<'mir, 'tcx>: Sized {
     ///
     /// Not called on `extern` or thread-local statics (those use the methods above).
     fn tag_alloc_base_pointer(
-        mem: &Memory<'mir, 'tcx, Self>,
+        ecx: &InterpCx<'mir, 'tcx, Self>,
         ptr: Pointer,
     ) -> Pointer<Self::PointerTag>;
 
     /// "Int-to-pointer cast"
     fn ptr_from_addr(
-        mem: &Memory<'mir, 'tcx, Self>,
+        ecx: &InterpCx<'mir, 'tcx, Self>,
         addr: u64,
     ) -> Pointer<Option<Self::PointerTag>>;
 
     /// Convert a pointer with provenance into an allocation-offset pair.
     fn ptr_get_alloc(
-        mem: &Memory<'mir, 'tcx, Self>,
+        ecx: &InterpCx<'mir, 'tcx, Self>,
         ptr: Pointer<Self::PointerTag>,
     ) -> (AllocId, Size);
 
@@ -309,7 +304,7 @@ pub trait Machine<'mir, 'tcx>: Sized {
     /// cache the result. (This relies on `AllocMap::get_or` being able to add the
     /// owned allocation to the map even when the map is shared.)
     fn init_allocation_extra<'b>(
-        mem: &Memory<'mir, 'tcx, Self>,
+        ecx: &InterpCx<'mir, 'tcx, Self>,
         id: AllocId,
         alloc: Cow<'b, Allocation>,
         kind: Option<MemoryKind<Self::MemoryKind>>,
@@ -322,7 +317,7 @@ pub trait Machine<'mir, 'tcx>: Sized {
     /// need to mutate.
     #[inline(always)]
     fn memory_read(
-        _memory_extra: &Self::MemoryExtra,
+        _machine: &Self,
         _alloc_extra: &Self::AllocExtra,
         _tag: Self::PointerTag,
         _range: AllocRange,
@@ -333,7 +328,7 @@ pub trait Machine<'mir, 'tcx>: Sized {
     /// Hook for performing extra checks on a memory write access.
     #[inline(always)]
     fn memory_written(
-        _memory_extra: &mut Self::MemoryExtra,
+        _machine: &mut Self,
         _alloc_extra: &mut Self::AllocExtra,
         _tag: Self::PointerTag,
         _range: AllocRange,
@@ -344,7 +339,7 @@ pub trait Machine<'mir, 'tcx>: Sized {
     /// Hook for performing extra operations on a memory deallocation.
     #[inline(always)]
     fn memory_deallocated(
-        _memory_extra: &mut Self::MemoryExtra,
+        _machine: &mut Self,
         _alloc_extra: &mut Self::AllocExtra,
         _tag: Self::PointerTag,
         _range: AllocRange,
@@ -408,14 +403,14 @@ pub macro compile_time_machine(<$mir: lifetime, $tcx: lifetime>) {
     type FrameExtra = ();
 
     #[inline(always)]
-    fn enforce_alignment(_memory_extra: &Self::MemoryExtra) -> bool {
+    fn enforce_alignment(_ecx: &InterpCx<$mir, $tcx, Self>) -> bool {
         // We do not check for alignment to avoid having to carry an `Align`
         // in `ConstValue::ByRef`.
         false
     }
 
     #[inline(always)]
-    fn force_int_for_alignment_check(_memory_extra: &Self::MemoryExtra) -> bool {
+    fn force_int_for_alignment_check(_ecx: &InterpCx<$mir, $tcx, Self>) -> bool {
         // We do not support `force_int`.
         false
     }
@@ -444,7 +439,7 @@ pub macro compile_time_machine(<$mir: lifetime, $tcx: lifetime>) {
 
     #[inline(always)]
     fn init_allocation_extra<'b>(
-        _mem: &Memory<$mir, $tcx, Self>,
+        _ecx: &InterpCx<$mir, $tcx, Self>,
         _id: AllocId,
         alloc: Cow<'b, Allocation>,
         _kind: Option<MemoryKind<Self::MemoryKind>>,
@@ -454,28 +449,28 @@ pub macro compile_time_machine(<$mir: lifetime, $tcx: lifetime>) {
     }
 
     fn extern_static_base_pointer(
-        mem: &Memory<$mir, $tcx, Self>,
+        ecx: &InterpCx<$mir, $tcx, Self>,
         def_id: DefId,
     ) -> InterpResult<$tcx, Pointer> {
         // Use the `AllocId` associated with the `DefId`. Any actual *access* will fail.
-        Ok(Pointer::new(mem.tcx.create_static_alloc(def_id), Size::ZERO))
+        Ok(Pointer::new(ecx.tcx.create_static_alloc(def_id), Size::ZERO))
     }
 
     #[inline(always)]
     fn tag_alloc_base_pointer(
-        _mem: &Memory<$mir, $tcx, Self>,
+        _ecx: &InterpCx<$mir, $tcx, Self>,
         ptr: Pointer<AllocId>,
     ) -> Pointer<AllocId> {
         ptr
     }
 
     #[inline(always)]
-    fn ptr_from_addr(_mem: &Memory<$mir, $tcx, Self>, addr: u64) -> Pointer<Option<AllocId>> {
+    fn ptr_from_addr(_ecx: &InterpCx<$mir, $tcx, Self>, addr: u64) -> Pointer<Option<AllocId>> {
         Pointer::new(None, Size::from_bytes(addr))
     }
 
     #[inline(always)]
-    fn ptr_get_alloc(_mem: &Memory<$mir, $tcx, Self>, ptr: Pointer<AllocId>) -> (AllocId, Size) {
+    fn ptr_get_alloc(_ecx: &InterpCx<$mir, $tcx, Self>, ptr: Pointer<AllocId>) -> (AllocId, Size) {
         // We know `offset` is relative to the allocation, so we can use `into_parts`.
         let (alloc_id, offset) = ptr.into_parts();
         (alloc_id, offset)
diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs
index 4a235809119f6..fcfcd5d8989f5 100644
--- a/compiler/rustc_const_eval/src/interpret/memory.rs
+++ b/compiler/rustc_const_eval/src/interpret/memory.rs
@@ -17,10 +17,10 @@ use rustc_ast::Mutability;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_middle::mir::display_allocation;
 use rustc_middle::ty::{Instance, ParamEnv, TyCtxt};
-use rustc_target::abi::{Align, HasDataLayout, Size, TargetDataLayout};
+use rustc_target::abi::{Align, HasDataLayout, Size};
 
 use super::{
-    alloc_range, AllocId, AllocMap, AllocRange, Allocation, CheckInAllocMsg, GlobalAlloc,
+    alloc_range, AllocId, AllocMap, AllocRange, Allocation, CheckInAllocMsg, GlobalAlloc, InterpCx,
     InterpResult, Machine, MayLeak, Pointer, PointerArithmetic, Provenance, Scalar,
     ScalarMaybeUninit,
 };
@@ -108,19 +108,6 @@ pub struct Memory<'mir, 'tcx, M: Machine<'mir, 'tcx>> {
     /// that do not exist any more.
     // FIXME: this should not be public, but interning currently needs access to it
     pub(super) dead_alloc_map: FxHashMap<AllocId, (Size, Align)>,
-
-    /// Extra data added by the machine.
-    pub extra: M::MemoryExtra,
-
-    /// Lets us implement `HasDataLayout`, which is awfully convenient.
-    pub tcx: TyCtxt<'tcx>,
-}
-
-impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> HasDataLayout for Memory<'mir, 'tcx, M> {
-    #[inline]
-    fn data_layout(&self) -> &TargetDataLayout {
-        &self.tcx.data_layout
-    }
 }
 
 /// A reference to some allocation that was already bounds-checked for the given region
@@ -142,16 +129,21 @@ pub struct AllocRefMut<'a, 'tcx, Tag, Extra> {
 }
 
 impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
-    pub fn new(tcx: TyCtxt<'tcx>, extra: M::MemoryExtra) -> Self {
+    pub fn new() -> Self {
         Memory {
             alloc_map: M::MemoryMap::default(),
             extra_fn_ptr_map: FxHashMap::default(),
             dead_alloc_map: FxHashMap::default(),
-            extra,
-            tcx,
         }
     }
 
+    /// This is used by [priroda](https://github.com/oli-obk/priroda)
+    pub fn alloc_map(&self) -> &M::MemoryMap {
+        &self.alloc_map
+    }
+}
+
+impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
     /// Call this to turn untagged "global" pointers (obtained via `tcx`) into
     /// the machine pointer to the allocation.  Must never be used
     /// for any other pointers, nor for TLS statics.
@@ -182,7 +174,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
         Ok(M::tag_alloc_base_pointer(self, Pointer::new(alloc_id, offset)))
     }
 
-    pub fn create_fn_alloc(
+    pub fn create_fn_alloc_ptr(
         &mut self,
         fn_val: FnVal<'tcx, M::ExtraFnVal>,
     ) -> Pointer<M::PointerTag> {
@@ -191,7 +183,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
             FnVal::Other(extra) => {
                 // FIXME(RalfJung): Should we have a cache here?
                 let id = self.tcx.reserve_alloc_id();
-                let old = self.extra_fn_ptr_map.insert(id, extra);
+                let old = self.memory.extra_fn_ptr_map.insert(id, extra);
                 assert!(old.is_none());
                 id
             }
@@ -201,17 +193,17 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
         self.global_base_pointer(Pointer::from(id)).unwrap()
     }
 
-    pub fn allocate(
+    pub fn allocate_ptr(
         &mut self,
         size: Size,
         align: Align,
         kind: MemoryKind<M::MemoryKind>,
     ) -> InterpResult<'static, Pointer<M::PointerTag>> {
         let alloc = Allocation::uninit(size, align, M::PANIC_ON_ALLOC_FAIL)?;
-        Ok(self.allocate_with(alloc, kind))
+        Ok(self.allocate_raw_ptr(alloc, kind))
     }
 
-    pub fn allocate_bytes(
+    pub fn allocate_bytes_ptr(
         &mut self,
         bytes: &[u8],
         align: Align,
@@ -219,10 +211,10 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
         mutability: Mutability,
     ) -> Pointer<M::PointerTag> {
         let alloc = Allocation::from_bytes(bytes, align, mutability);
-        self.allocate_with(alloc, kind)
+        self.allocate_raw_ptr(alloc, kind)
     }
 
-    pub fn allocate_with(
+    pub fn allocate_raw_ptr(
         &mut self,
         alloc: Allocation,
         kind: MemoryKind<M::MemoryKind>,
@@ -234,11 +226,11 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
             "dynamically allocating global memory"
         );
         let alloc = M::init_allocation_extra(self, id, Cow::Owned(alloc), Some(kind));
-        self.alloc_map.insert(id, (kind, alloc.into_owned()));
+        self.memory.alloc_map.insert(id, (kind, alloc.into_owned()));
         M::tag_alloc_base_pointer(self, Pointer::from(id))
     }
 
-    pub fn reallocate(
+    pub fn reallocate_ptr(
         &mut self,
         ptr: Pointer<Option<M::PointerTag>>,
         old_size_and_align: Option<(Size, Align)>,
@@ -246,7 +238,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
         new_align: Align,
         kind: MemoryKind<M::MemoryKind>,
     ) -> InterpResult<'tcx, Pointer<M::PointerTag>> {
-        let (alloc_id, offset, ptr) = self.ptr_get_alloc(ptr)?;
+        let (alloc_id, offset, ptr) = self.ptr_get_alloc_id(ptr)?;
         if offset.bytes() != 0 {
             throw_ub_format!(
                 "reallocating {:?} which does not point to the beginning of an object",
@@ -256,13 +248,13 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
 
         // For simplicities' sake, we implement reallocate as "alloc, copy, dealloc".
         // This happens so rarely, the perf advantage is outweighed by the maintenance cost.
-        let new_ptr = self.allocate(new_size, new_align, kind)?;
+        let new_ptr = self.allocate_ptr(new_size, new_align, kind)?;
         let old_size = match old_size_and_align {
             Some((size, _align)) => size,
-            None => self.get_raw(alloc_id)?.size(),
+            None => self.get_alloc_raw(alloc_id)?.size(),
         };
         // This will also call the access hooks.
-        self.copy(
+        self.mem_copy(
             ptr.into(),
             Align::ONE,
             new_ptr.into(),
@@ -270,19 +262,19 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
             old_size.min(new_size),
             /*nonoverlapping*/ true,
         )?;
-        self.deallocate(ptr.into(), old_size_and_align, kind)?;
+        self.deallocate_ptr(ptr.into(), old_size_and_align, kind)?;
 
         Ok(new_ptr)
     }
 
     #[instrument(skip(self), level = "debug")]
-    pub fn deallocate(
+    pub fn deallocate_ptr(
         &mut self,
         ptr: Pointer<Option<M::PointerTag>>,
         old_size_and_align: Option<(Size, Align)>,
         kind: MemoryKind<M::MemoryKind>,
     ) -> InterpResult<'tcx> {
-        let (alloc_id, offset, ptr) = self.ptr_get_alloc(ptr)?;
+        let (alloc_id, offset, ptr) = self.ptr_get_alloc_id(ptr)?;
         trace!("deallocating: {}", alloc_id);
 
         if offset.bytes() != 0 {
@@ -292,7 +284,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
             );
         }
 
-        let Some((alloc_kind, mut alloc)) = self.alloc_map.remove(&alloc_id) else {
+        let Some((alloc_kind, mut alloc)) = self.memory.alloc_map.remove(&alloc_id) else {
             // Deallocating global memory -- always an error
             return Err(match self.tcx.get_global_alloc(alloc_id) {
                 Some(GlobalAlloc::Function(..)) => {
@@ -335,14 +327,14 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
         // Let the machine take some extra action
         let size = alloc.size();
         M::memory_deallocated(
-            &mut self.extra,
+            &mut self.machine,
             &mut alloc.extra,
             ptr.provenance,
             alloc_range(Size::ZERO, size),
         )?;
 
         // Don't forget to remember size and align of this now-dead allocation
-        let old = self.dead_alloc_map.insert(alloc_id, (size, alloc.align));
+        let old = self.memory.dead_alloc_map.insert(alloc_id, (size, alloc.align));
         if old.is_some() {
             bug!("Nothing can be deallocated twice");
         }
@@ -358,7 +350,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
         size: Size,
         align: Align,
     ) -> InterpResult<'tcx, Option<(AllocId, Size, Pointer<M::PointerTag>)>> {
-        let align = M::enforce_alignment(&self.extra).then_some(align);
+        let align = M::enforce_alignment(&self).then_some(align);
         self.check_and_deref_ptr(
             ptr,
             size,
@@ -366,7 +358,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
             CheckInAllocMsg::MemoryAccessTest,
             |alloc_id, offset, ptr| {
                 let (size, align) =
-                    self.get_size_and_align(alloc_id, AllocCheck::Dereferenceable)?;
+                    self.get_alloc_size_and_align(alloc_id, AllocCheck::Dereferenceable)?;
                 Ok((size, align, (alloc_id, offset, ptr)))
             },
         )
@@ -392,7 +384,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
                 | CheckInAllocMsg::OffsetFromTest
                 | CheckInAllocMsg::InboundsTest => AllocCheck::Live,
             };
-            let (size, align) = self.get_size_and_align(alloc_id, check)?;
+            let (size, align) = self.get_alloc_size_and_align(alloc_id, check)?;
             Ok((size, align, ()))
         })?;
         Ok(())
@@ -427,7 +419,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
             }
         }
 
-        Ok(match self.ptr_try_get_alloc(ptr) {
+        Ok(match self.ptr_try_get_alloc_id(ptr) {
             Err(addr) => {
                 // We couldn't get a proper allocation. This is only okay if the access size is 0,
                 // and the address is not null.
@@ -456,7 +448,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
                 // Test align. Check this last; if both bounds and alignment are violated
                 // we want the error to be about the bounds.
                 if let Some(align) = align {
-                    if M::force_int_for_alignment_check(&self.extra) {
+                    if M::force_int_for_alignment_check(self) {
                         let addr = Scalar::from_pointer(ptr, &self.tcx)
                             .to_machine_usize(&self.tcx)
                             .expect("ptr-to-int cast for align check should never fail");
@@ -479,7 +471,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
 }
 
 /// Allocation accessors
-impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
+impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
     /// Helper function to obtain a global (tcx) allocation.
     /// This attempts to return a reference to an existing allocation if
     /// one can be found in `tcx`. That, however, is only possible if `tcx` and
@@ -517,7 +509,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
                 (self.tcx.eval_static_initializer(def_id)?, Some(def_id))
             }
         };
-        M::before_access_global(&self.extra, id, alloc, def_id, is_write)?;
+        M::before_access_global(&self.machine, id, alloc, def_id, is_write)?;
         // We got tcx memory. Let the machine initialize its "extra" stuff.
         let alloc = M::init_allocation_extra(
             self,
@@ -530,7 +522,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
 
     /// Gives raw access to the `Allocation`, without bounds or alignment checks.
     /// The caller is responsible for calling the access hooks!
-    fn get_raw(
+    fn get_alloc_raw(
         &self,
         id: AllocId,
     ) -> InterpResult<'tcx, &Allocation<M::PointerTag, M::AllocExtra>> {
@@ -538,7 +530,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
         // ways of "erroring": An actual error, or because we got a reference from
         // `get_global_alloc` that we can actually use directly without inserting anything anywhere.
         // So the error type is `InterpResult<'tcx, &Allocation<M::PointerTag>>`.
-        let a = self.alloc_map.get_or(id, || {
+        let a = self.memory.alloc_map.get_or(id, || {
             let alloc = self.get_global_alloc(id, /*is_write*/ false).map_err(Err)?;
             match alloc {
                 Cow::Borrowed(alloc) => {
@@ -564,27 +556,27 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
     }
 
     /// "Safe" (bounds and align-checked) allocation access.
-    pub fn get<'a>(
+    pub fn get_ptr_alloc<'a>(
         &'a self,
         ptr: Pointer<Option<M::PointerTag>>,
         size: Size,
         align: Align,
     ) -> InterpResult<'tcx, Option<AllocRef<'a, 'tcx, M::PointerTag, M::AllocExtra>>> {
-        let align = M::enforce_alignment(&self.extra).then_some(align);
+        let align = M::enforce_alignment(self).then_some(align);
         let ptr_and_alloc = self.check_and_deref_ptr(
             ptr,
             size,
             align,
             CheckInAllocMsg::MemoryAccessTest,
             |alloc_id, offset, ptr| {
-                let alloc = self.get_raw(alloc_id)?;
+                let alloc = self.get_alloc_raw(alloc_id)?;
                 Ok((alloc.size(), alloc.align, (alloc_id, offset, ptr, alloc)))
             },
         )?;
         if let Some((alloc_id, offset, ptr, alloc)) = ptr_and_alloc {
             let range = alloc_range(offset, size);
-            M::memory_read(&self.extra, &alloc.extra, ptr.provenance, range)?;
-            Ok(Some(AllocRef { alloc, range, tcx: self.tcx, alloc_id }))
+            M::memory_read(&self.machine, &alloc.extra, ptr.provenance, range)?;
+            Ok(Some(AllocRef { alloc, range, tcx: *self.tcx, alloc_id }))
         } else {
             // Even in this branch we have to be sure that we actually access the allocation, in
             // order to ensure that `static FOO: Type = FOO;` causes a cycle error instead of
@@ -596,7 +588,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
 
     /// Return the `extra` field of the given allocation.
     pub fn get_alloc_extra<'a>(&'a self, id: AllocId) -> InterpResult<'tcx, &'a M::AllocExtra> {
-        Ok(&self.get_raw(id)?.extra)
+        Ok(&self.get_alloc_raw(id)?.extra)
     }
 
     /// Gives raw mutable access to the `Allocation`, without bounds or alignment checks.
@@ -604,16 +596,15 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
     ///
     /// Also returns a ptr to `self.extra` so that the caller can use it in parallel with the
     /// allocation.
-    fn get_raw_mut(
+    fn get_alloc_raw_mut(
         &mut self,
         id: AllocId,
-    ) -> InterpResult<'tcx, (&mut Allocation<M::PointerTag, M::AllocExtra>, &mut M::MemoryExtra)>
-    {
+    ) -> InterpResult<'tcx, (&mut Allocation<M::PointerTag, M::AllocExtra>, &mut M)> {
         // We have "NLL problem case #3" here, which cannot be worked around without loss of
         // efficiency even for the common case where the key is in the map.
         // <https://rust-lang.github.io/rfcs/2094-nll.html#problem-case-3-conditional-control-flow-across-functions>
         // (Cannot use `get_mut_or` since `get_global_alloc` needs `&self`.)
-        if self.alloc_map.get_mut(id).is_none() {
+        if self.memory.alloc_map.get_mut(id).is_none() {
             // Slow path.
             // Allocation not found locally, go look global.
             let alloc = self.get_global_alloc(id, /*is_write*/ true)?;
@@ -621,18 +612,18 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
                 "I got a global allocation that I have to copy but the machine does \
                     not expect that to happen",
             );
-            self.alloc_map.insert(id, (MemoryKind::Machine(kind), alloc.into_owned()));
+            self.memory.alloc_map.insert(id, (MemoryKind::Machine(kind), alloc.into_owned()));
         }
 
-        let (_kind, alloc) = self.alloc_map.get_mut(id).unwrap();
+        let (_kind, alloc) = self.memory.alloc_map.get_mut(id).unwrap();
         if alloc.mutability == Mutability::Not {
             throw_ub!(WriteToReadOnly(id))
         }
-        Ok((alloc, &mut self.extra))
+        Ok((alloc, &mut self.machine))
     }
 
     /// "Safe" (bounds and align-checked) allocation access.
-    pub fn get_mut<'a>(
+    pub fn get_ptr_alloc_mut<'a>(
         &'a mut self,
         ptr: Pointer<Option<M::PointerTag>>,
         size: Size,
@@ -640,12 +631,12 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
     ) -> InterpResult<'tcx, Option<AllocRefMut<'a, 'tcx, M::PointerTag, M::AllocExtra>>> {
         let parts = self.get_ptr_access(ptr, size, align)?;
         if let Some((alloc_id, offset, ptr)) = parts {
-            let tcx = self.tcx;
+            let tcx = *self.tcx;
             // FIXME: can we somehow avoid looking up the allocation twice here?
             // We cannot call `get_raw_mut` inside `check_and_deref_ptr` as that would duplicate `&mut self`.
-            let (alloc, extra) = self.get_raw_mut(alloc_id)?;
+            let (alloc, machine) = self.get_alloc_raw_mut(alloc_id)?;
             let range = alloc_range(offset, size);
-            M::memory_written(extra, &mut alloc.extra, ptr.provenance, range)?;
+            M::memory_written(machine, &mut alloc.extra, ptr.provenance, range)?;
             Ok(Some(AllocRefMut { alloc, range, tcx, alloc_id }))
         } else {
             Ok(None)
@@ -656,16 +647,16 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
     pub fn get_alloc_extra_mut<'a>(
         &'a mut self,
         id: AllocId,
-    ) -> InterpResult<'tcx, (&'a mut M::AllocExtra, &'a mut M::MemoryExtra)> {
-        let (alloc, memory_extra) = self.get_raw_mut(id)?;
-        Ok((&mut alloc.extra, memory_extra))
+    ) -> InterpResult<'tcx, (&'a mut M::AllocExtra, &'a mut M)> {
+        let (alloc, machine) = self.get_alloc_raw_mut(id)?;
+        Ok((&mut alloc.extra, machine))
     }
 
     /// Obtain the size and alignment of an allocation, even if that allocation has
     /// been deallocated.
     ///
     /// If `liveness` is `AllocCheck::MaybeDead`, this function always returns `Ok`.
-    pub fn get_size_and_align(
+    pub fn get_alloc_size_and_align(
         &self,
         id: AllocId,
         liveness: AllocCheck,
@@ -674,7 +665,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
         // Don't use `self.get_raw` here as that will
         // a) cause cycles in case `id` refers to a static
         // b) duplicate a global's allocation in miri
-        if let Some((_, alloc)) = self.alloc_map.get(id) {
+        if let Some((_, alloc)) = self.memory.alloc_map.get(id) {
             return Ok((alloc.size(), alloc.align));
         }
 
@@ -713,6 +704,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
                     // Deallocated pointers are allowed, we should be able to find
                     // them in the map.
                     Ok(*self
+                        .memory
                         .dead_alloc_map
                         .get(&id)
                         .expect("deallocated pointers should all be recorded in `dead_alloc_map`"))
@@ -724,7 +716,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
     }
 
     fn get_fn_alloc(&self, id: AllocId) -> Option<FnVal<'tcx, M::ExtraFnVal>> {
-        if let Some(extra) = self.extra_fn_ptr_map.get(&id) {
+        if let Some(extra) = self.memory.extra_fn_ptr_map.get(&id) {
             Some(FnVal::Other(*extra))
         } else {
             match self.tcx.get_global_alloc(id) {
@@ -734,12 +726,12 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
         }
     }
 
-    pub fn get_fn(
+    pub fn get_ptr_fn(
         &self,
         ptr: Pointer<Option<M::PointerTag>>,
     ) -> InterpResult<'tcx, FnVal<'tcx, M::ExtraFnVal>> {
         trace!("get_fn({:?})", ptr);
-        let (alloc_id, offset, _ptr) = self.ptr_get_alloc(ptr)?;
+        let (alloc_id, offset, _ptr) = self.ptr_get_alloc_id(ptr)?;
         if offset.bytes() != 0 {
             throw_ub!(InvalidFunctionPointer(Pointer::new(alloc_id, offset)))
         }
@@ -747,8 +739,8 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
             .ok_or_else(|| err_ub!(InvalidFunctionPointer(Pointer::new(alloc_id, offset))).into())
     }
 
-    pub fn mark_immutable(&mut self, id: AllocId) -> InterpResult<'tcx> {
-        self.get_raw_mut(id)?.0.mutability = Mutability::Not;
+    pub fn alloc_mark_immutable(&mut self, id: AllocId) -> InterpResult<'tcx> {
+        self.get_alloc_raw_mut(id)?.0.mutability = Mutability::Not;
         Ok(())
     }
 
@@ -765,7 +757,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
     pub fn dump_allocs<'a>(&'a self, mut allocs: Vec<AllocId>) -> DumpAllocs<'a, 'mir, 'tcx, M> {
         allocs.sort();
         allocs.dedup();
-        DumpAllocs { mem: self, allocs }
+        DumpAllocs { ecx: self, allocs }
     }
 
     /// Print leaked memory. Allocations reachable from `static_roots` or a `Global` allocation
@@ -775,14 +767,15 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
         let reachable = {
             let mut reachable = FxHashSet::default();
             let global_kind = M::GLOBAL_KIND.map(MemoryKind::Machine);
-            let mut todo: Vec<_> = self.alloc_map.filter_map_collect(move |&id, &(kind, _)| {
-                if Some(kind) == global_kind { Some(id) } else { None }
-            });
+            let mut todo: Vec<_> =
+                self.memory.alloc_map.filter_map_collect(move |&id, &(kind, _)| {
+                    if Some(kind) == global_kind { Some(id) } else { None }
+                });
             todo.extend(static_roots);
             while let Some(id) = todo.pop() {
                 if reachable.insert(id) {
                     // This is a new allocation, add its relocations to `todo`.
-                    if let Some((_, alloc)) = self.alloc_map.get(id) {
+                    if let Some((_, alloc)) = self.memory.alloc_map.get(id) {
                         todo.extend(alloc.relocations().values().map(|tag| tag.get_alloc_id()));
                     }
                 }
@@ -791,7 +784,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
         };
 
         // All allocations that are *not* `reachable` and *not* `may_leak` are considered leaking.
-        let leaks: Vec<_> = self.alloc_map.filter_map_collect(|&id, &(kind, _)| {
+        let leaks: Vec<_> = self.memory.alloc_map.filter_map_collect(|&id, &(kind, _)| {
             if kind.may_leak() || reachable.contains(&id) { None } else { Some(id) }
         });
         let n = leaks.len();
@@ -800,17 +793,12 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
         }
         n
     }
-
-    /// This is used by [priroda](https://github.com/oli-obk/priroda)
-    pub fn alloc_map(&self) -> &M::MemoryMap {
-        &self.alloc_map
-    }
 }
 
 #[doc(hidden)]
 /// There's no way to use this directly, it's just a helper struct for the `dump_alloc(s)` methods.
 pub struct DumpAllocs<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> {
-    mem: &'a Memory<'mir, 'tcx, M>,
+    ecx: &'a InterpCx<'mir, 'tcx, M>,
     allocs: Vec<AllocId>,
 }
 
@@ -840,25 +828,25 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> std::fmt::Debug for DumpAllocs<'a,
             }
 
             write!(fmt, "{}", id)?;
-            match self.mem.alloc_map.get(id) {
+            match self.ecx.memory.alloc_map.get(id) {
                 Some(&(kind, ref alloc)) => {
                     // normal alloc
                     write!(fmt, " ({}, ", kind)?;
                     write_allocation_track_relocs(
                         &mut *fmt,
-                        self.mem.tcx,
+                        *self.ecx.tcx,
                         &mut allocs_to_print,
                         alloc,
                     )?;
                 }
                 None => {
                     // global alloc
-                    match self.mem.tcx.get_global_alloc(id) {
+                    match self.ecx.tcx.get_global_alloc(id) {
                         Some(GlobalAlloc::Memory(alloc)) => {
                             write!(fmt, " (unchanged global, ")?;
                             write_allocation_track_relocs(
                                 &mut *fmt,
-                                self.mem.tcx,
+                                *self.ecx.tcx,
                                 &mut allocs_to_print,
                                 alloc.inner(),
                             )?;
@@ -867,7 +855,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> std::fmt::Debug for DumpAllocs<'a,
                             write!(fmt, " (fn: {})", func)?;
                         }
                         Some(GlobalAlloc::Static(did)) => {
-                            write!(fmt, " (static: {})", self.mem.tcx.def_path_str(did))?;
+                            write!(fmt, " (static: {})", self.ecx.tcx.def_path_str(did))?;
                         }
                         None => {
                             write!(fmt, " (deallocated)")?;
@@ -923,16 +911,16 @@ impl<'tcx, 'a, Tag: Provenance, Extra> AllocRef<'a, 'tcx, Tag, Extra> {
     }
 }
 
-impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
+impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
     /// Reads the given number of bytes from memory. Returns them as a slice.
     ///
     /// Performs appropriate bounds checks.
-    pub fn read_bytes(
+    pub fn read_bytes_ptr(
         &self,
         ptr: Pointer<Option<M::PointerTag>>,
         size: Size,
     ) -> InterpResult<'tcx, &[u8]> {
-        let Some(alloc_ref) = self.get(ptr, size, Align::ONE)? else {
+        let Some(alloc_ref) = self.get_ptr_alloc(ptr, size, Align::ONE)? else {
             // zero-sized access
             return Ok(&[]);
         };
@@ -947,7 +935,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
     /// Writes the given stream of bytes into memory.
     ///
     /// Performs appropriate bounds checks.
-    pub fn write_bytes(
+    pub fn write_bytes_ptr(
         &mut self,
         ptr: Pointer<Option<M::PointerTag>>,
         src: impl IntoIterator<Item = u8>,
@@ -958,7 +946,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
         assert_eq!(lower, len, "can only write iterators with a precise length");
 
         let size = Size::from_bytes(len);
-        let Some(alloc_ref) = self.get_mut(ptr, size, Align::ONE)? else {
+        let Some(alloc_ref) = self.get_ptr_alloc_mut(ptr, size, Align::ONE)? else {
             // zero-sized access
             assert_matches!(
                 src.next(),
@@ -984,7 +972,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
         Ok(())
     }
 
-    pub fn copy(
+    pub fn mem_copy(
         &mut self,
         src: Pointer<Option<M::PointerTag>>,
         src_align: Align,
@@ -993,10 +981,10 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
         size: Size,
         nonoverlapping: bool,
     ) -> InterpResult<'tcx> {
-        self.copy_repeatedly(src, src_align, dest, dest_align, size, 1, nonoverlapping)
+        self.mem_copy_repeatedly(src, src_align, dest, dest_align, size, 1, nonoverlapping)
     }
 
-    pub fn copy_repeatedly(
+    pub fn mem_copy_repeatedly(
         &mut self,
         src: Pointer<Option<M::PointerTag>>,
         src_align: Align,
@@ -1019,9 +1007,9 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
             // Zero-sized *source*, that means dst is also zero-sized and we have nothing to do.
             return Ok(());
         };
-        let src_alloc = self.get_raw(src_alloc_id)?;
+        let src_alloc = self.get_alloc_raw(src_alloc_id)?;
         let src_range = alloc_range(src_offset, size);
-        M::memory_read(&self.extra, &src_alloc.extra, src.provenance, src_range)?;
+        M::memory_read(&self.machine, &src_alloc.extra, src.provenance, src_range)?;
         // We need the `dest` ptr for the next operation, so we get it now.
         // We already did the source checks and called the hooks so we are good to return early.
         let Some((dest_alloc_id, dest_offset, dest)) = dest_parts else {
@@ -1044,7 +1032,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
         let compressed = src_alloc.compress_uninit_range(src_range);
 
         // Destination alloc preparations and access hooks.
-        let (dest_alloc, extra) = self.get_raw_mut(dest_alloc_id)?;
+        let (dest_alloc, extra) = self.get_alloc_raw_mut(dest_alloc_id)?;
         let dest_range = alloc_range(dest_offset, size * num_copies);
         M::memory_written(extra, &mut dest_alloc.extra, dest.provenance, dest_range)?;
         let dest_bytes = dest_alloc
@@ -1112,7 +1100,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
 }
 
 /// Machine pointer introspection.
-impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
+impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
     pub fn scalar_to_ptr(&self, scalar: Scalar<M::PointerTag>) -> Pointer<Option<M::PointerTag>> {
         // We use `to_bits_or_ptr_internal` since we are just implementing the method people need to
         // call to force getting out a pointer.
@@ -1129,9 +1117,32 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
         }
     }
 
+    /// Test if this value might be null.
+    /// If the machine does not support ptr-to-int casts, this is conservative.
+    pub fn scalar_may_be_null(&self, scalar: Scalar<M::PointerTag>) -> bool {
+        match scalar.try_to_int() {
+            Ok(int) => int.is_null(),
+            Err(_) => {
+                // Can only happen during CTFE.
+                let ptr = self.scalar_to_ptr(scalar);
+                match self.ptr_try_get_alloc_id(ptr) {
+                    Ok((alloc_id, offset, _)) => {
+                        let (size, _align) = self
+                            .get_alloc_size_and_align(alloc_id, AllocCheck::MaybeDead)
+                            .expect("alloc info with MaybeDead cannot fail");
+                        // If the pointer is out-of-bounds, it may be null.
+                        // Note that one-past-the-end (offset == size) is still inbounds, and never null.
+                        offset > size
+                    }
+                    Err(_offset) => bug!("a non-int scalar is always a pointer"),
+                }
+            }
+        }
+    }
+
     /// Turning a "maybe pointer" into a proper pointer (and some information
     /// about where it points), or an absolute address.
-    pub fn ptr_try_get_alloc(
+    pub fn ptr_try_get_alloc_id(
         &self,
         ptr: Pointer<Option<M::PointerTag>>,
     ) -> Result<(AllocId, Size, Pointer<M::PointerTag>), u64> {
@@ -1146,11 +1157,11 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
 
     /// Turning a "maybe pointer" into a proper pointer (and some information about where it points).
     #[inline(always)]
-    pub fn ptr_get_alloc(
+    pub fn ptr_get_alloc_id(
         &self,
         ptr: Pointer<Option<M::PointerTag>>,
     ) -> InterpResult<'tcx, (AllocId, Size, Pointer<M::PointerTag>)> {
-        self.ptr_try_get_alloc(ptr).map_err(|offset| {
+        self.ptr_try_get_alloc_id(ptr).map_err(|offset| {
             err_ub!(DanglingIntPointer(offset, CheckInAllocMsg::InboundsTest)).into()
         })
     }
diff --git a/compiler/rustc_const_eval/src/interpret/operand.rs b/compiler/rustc_const_eval/src/interpret/operand.rs
index 9000567558b84..3012f763b80c0 100644
--- a/compiler/rustc_const_eval/src/interpret/operand.rs
+++ b/compiler/rustc_const_eval/src/interpret/operand.rs
@@ -257,7 +257,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
             return Ok(None);
         }
 
-        let Some(alloc) = self.get_alloc(mplace)? else {
+        let Some(alloc) = self.get_place_alloc(mplace)? else {
             return Ok(Some(ImmTy {
                 // zero-sized type
                 imm: Scalar::ZST.into(),
@@ -340,7 +340,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
     // Turn the wide MPlace into a string (must already be dereferenced!)
     pub fn read_str(&self, mplace: &MPlaceTy<'tcx, M::PointerTag>) -> InterpResult<'tcx, &str> {
         let len = mplace.len(self)?;
-        let bytes = self.memory.read_bytes(mplace.ptr, Size::from_bytes(len))?;
+        let bytes = self.read_bytes_ptr(mplace.ptr, Size::from_bytes(len))?;
         let str = std::str::from_utf8(bytes).map_err(|err| err_ub!(InvalidStr(err)))?;
         Ok(str)
     }
diff --git a/compiler/rustc_const_eval/src/interpret/place.rs b/compiler/rustc_const_eval/src/interpret/place.rs
index 5b8d74b43078e..c37973bd1663c 100644
--- a/compiler/rustc_const_eval/src/interpret/place.rs
+++ b/compiler/rustc_const_eval/src/interpret/place.rs
@@ -306,25 +306,25 @@ where
     }
 
     #[inline]
-    pub(super) fn get_alloc(
+    pub(super) fn get_place_alloc(
         &self,
         place: &MPlaceTy<'tcx, M::PointerTag>,
     ) -> InterpResult<'tcx, Option<AllocRef<'_, 'tcx, M::PointerTag, M::AllocExtra>>> {
         assert!(!place.layout.is_unsized());
         assert!(!place.meta.has_meta());
         let size = place.layout.size;
-        self.memory.get(place.ptr, size, place.align)
+        self.get_ptr_alloc(place.ptr, size, place.align)
     }
 
     #[inline]
-    pub(super) fn get_alloc_mut(
+    pub(super) fn get_place_alloc_mut(
         &mut self,
         place: &MPlaceTy<'tcx, M::PointerTag>,
     ) -> InterpResult<'tcx, Option<AllocRefMut<'_, 'tcx, M::PointerTag, M::AllocExtra>>> {
         assert!(!place.layout.is_unsized());
         assert!(!place.meta.has_meta());
         let size = place.layout.size;
-        self.memory.get_mut(place.ptr, size, place.align)
+        self.get_ptr_alloc_mut(place.ptr, size, place.align)
     }
 
     /// Check if this mplace is dereferenceable and sufficiently aligned.
@@ -337,8 +337,8 @@ where
             .size_and_align_of_mplace(&mplace)?
             .unwrap_or((mplace.layout.size, mplace.layout.align.abi));
         assert!(mplace.mplace.align <= align, "dynamic alignment less strict than static one?");
-        let align = M::enforce_alignment(&self.memory.extra).then_some(align);
-        self.memory.check_ptr_access_align(mplace.ptr, size, align.unwrap_or(Align::ONE), msg)?;
+        let align = M::enforce_alignment(self).then_some(align);
+        self.check_ptr_access_align(mplace.ptr, size, align.unwrap_or(Align::ONE), msg)?;
         Ok(())
     }
 
@@ -748,7 +748,7 @@ where
 
         // Invalid places are a thing: the return place of a diverging function
         let tcx = *self.tcx;
-        let Some(mut alloc) = self.get_alloc_mut(dest)? else {
+        let Some(mut alloc) = self.get_place_alloc_mut(dest)? else {
             // zero-sized access
             return Ok(());
         };
@@ -857,8 +857,7 @@ where
         });
         assert_eq!(src.meta, dest.meta, "Can only copy between equally-sized instances");
 
-        self.memory
-            .copy(src.ptr, src.align, dest.ptr, dest.align, size, /*nonoverlapping*/ true)
+        self.mem_copy(src.ptr, src.align, dest.ptr, dest.align, size, /*nonoverlapping*/ true)
     }
 
     /// Copies the data from an operand to a place. The layouts may disagree, but they must
@@ -942,7 +941,7 @@ where
                         let (size, align) = self
                             .size_and_align_of(&meta, &local_layout)?
                             .expect("Cannot allocate for non-dyn-sized type");
-                        let ptr = self.memory.allocate(size, align, MemoryKind::Stack)?;
+                        let ptr = self.allocate_ptr(size, align, MemoryKind::Stack)?;
                         let mplace = MemPlace { ptr: ptr.into(), align, meta };
                         if let LocalValue::Live(Operand::Immediate(value)) = local_val {
                             // Preserve old value.
@@ -979,7 +978,7 @@ where
         layout: TyAndLayout<'tcx>,
         kind: MemoryKind<M::MemoryKind>,
     ) -> InterpResult<'static, MPlaceTy<'tcx, M::PointerTag>> {
-        let ptr = self.memory.allocate(layout.size, layout.align.abi, kind)?;
+        let ptr = self.allocate_ptr(layout.size, layout.align.abi, kind)?;
         Ok(MPlaceTy::from_aligned_ptr(ptr.into(), layout))
     }
 
@@ -990,7 +989,7 @@ where
         kind: MemoryKind<M::MemoryKind>,
         mutbl: Mutability,
     ) -> MPlaceTy<'tcx, M::PointerTag> {
-        let ptr = self.memory.allocate_bytes(str.as_bytes(), Align::ONE, kind, mutbl);
+        let ptr = self.allocate_bytes_ptr(str.as_bytes(), Align::ONE, kind, mutbl);
         let meta = Scalar::from_machine_usize(u64::try_from(str.len()).unwrap(), self);
         let mplace =
             MemPlace { ptr: ptr.into(), align: Align::ONE, meta: MemPlaceMeta::Meta(meta) };
diff --git a/compiler/rustc_const_eval/src/interpret/step.rs b/compiler/rustc_const_eval/src/interpret/step.rs
index 4272bfd5d6c6f..84563daa0880d 100644
--- a/compiler/rustc_const_eval/src/interpret/step.rs
+++ b/compiler/rustc_const_eval/src/interpret/step.rs
@@ -225,7 +225,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
 
                 if length == 0 {
                     // Nothing to copy... but let's still make sure that `dest` as a place is valid.
-                    self.get_alloc_mut(&dest)?;
+                    self.get_place_alloc_mut(&dest)?;
                 } else {
                     // Write the src to the first element.
                     let first = self.mplace_field(&dest, 0)?;
@@ -241,7 +241,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                     // that place might be more aligned than its type mandates (a `u8` array could
                     // be 4-aligned if it sits at the right spot in a struct). Instead we use
                     // `first.layout.align`, i.e., the alignment given by the type.
-                    self.memory.copy_repeatedly(
+                    self.mem_copy_repeatedly(
                         first_ptr,
                         first.align,
                         rest_ptr,
diff --git a/compiler/rustc_const_eval/src/interpret/terminator.rs b/compiler/rustc_const_eval/src/interpret/terminator.rs
index d33358499e237..a7587652114bf 100644
--- a/compiler/rustc_const_eval/src/interpret/terminator.rs
+++ b/compiler/rustc_const_eval/src/interpret/terminator.rs
@@ -72,7 +72,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                 let (fn_val, fn_abi, with_caller_location) = match *func.layout.ty.kind() {
                     ty::FnPtr(_sig) => {
                         let fn_ptr = self.read_pointer(&func)?;
-                        let fn_val = self.memory.get_fn(fn_ptr)?;
+                        let fn_val = self.get_ptr_fn(fn_ptr)?;
                         (fn_val, self.fn_abi_of_fn_ptr(fn_sig_binder, extra_args)?, false)
                     }
                     ty::FnDef(def_id, substs) => {
diff --git a/compiler/rustc_const_eval/src/interpret/traits.rs b/compiler/rustc_const_eval/src/interpret/traits.rs
index fc60a40e2ada6..5cf3807faaa6d 100644
--- a/compiler/rustc_const_eval/src/interpret/traits.rs
+++ b/compiler/rustc_const_eval/src/interpret/traits.rs
@@ -32,7 +32,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
 
         let vtable_allocation = self.tcx.vtable_allocation((ty, poly_trait_ref));
 
-        let vtable_ptr = self.memory.global_base_pointer(Pointer::from(vtable_allocation))?;
+        let vtable_ptr = self.global_base_pointer(Pointer::from(vtable_allocation))?;
 
         Ok(vtable_ptr.into())
     }
@@ -48,11 +48,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         let ptr_size = self.pointer_size();
         let vtable_slot = vtable.offset(ptr_size * idx, self)?;
         let vtable_slot = self
-            .memory
-            .get(vtable_slot, ptr_size, self.tcx.data_layout.pointer_align.abi)?
+            .get_ptr_alloc(vtable_slot, ptr_size, self.tcx.data_layout.pointer_align.abi)?
             .expect("cannot be a ZST");
         let fn_ptr = self.scalar_to_ptr(vtable_slot.read_ptr_sized(Size::ZERO)?.check_init()?);
-        self.memory.get_fn(fn_ptr)
+        self.get_ptr_fn(fn_ptr)
     }
 
     /// Returns the drop fn instance as well as the actual dynamic type.
@@ -63,8 +62,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         let pointer_size = self.pointer_size();
         // We don't care about the pointee type; we just want a pointer.
         let vtable = self
-            .memory
-            .get(
+            .get_ptr_alloc(
                 vtable,
                 pointer_size * u64::try_from(COMMON_VTABLE_ENTRIES.len()).unwrap(),
                 self.tcx.data_layout.pointer_align.abi,
@@ -77,7 +75,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
             .check_init()?;
         // We *need* an instance here, no other kind of function value, to be able
         // to determine the type.
-        let drop_instance = self.memory.get_fn(self.scalar_to_ptr(drop_fn))?.as_instance()?;
+        let drop_instance = self.get_ptr_fn(self.scalar_to_ptr(drop_fn))?.as_instance()?;
         trace!("Found drop fn: {:?}", drop_instance);
         let fn_sig = drop_instance.ty(*self.tcx, self.param_env).fn_sig(*self.tcx);
         let fn_sig = self.tcx.normalize_erasing_late_bound_regions(self.param_env, fn_sig);
@@ -99,8 +97,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         // We check for `size = 3 * ptr_size`, which covers the drop fn (unused here),
         // the size, and the align (which we read below).
         let vtable = self
-            .memory
-            .get(
+            .get_ptr_alloc(
                 vtable,
                 pointer_size * u64::try_from(COMMON_VTABLE_ENTRIES.len()).unwrap(),
                 self.tcx.data_layout.pointer_align.abi,
@@ -132,8 +129,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
 
         let vtable_slot = vtable.offset(pointer_size * idx, self)?;
         let new_vtable = self
-            .memory
-            .get(vtable_slot, pointer_size, self.tcx.data_layout.pointer_align.abi)?
+            .get_ptr_alloc(vtable_slot, pointer_size, self.tcx.data_layout.pointer_align.abi)?
             .expect("cannot be a ZST");
 
         let new_vtable = self.scalar_to_ptr(new_vtable.read_ptr_sized(Size::ZERO)?.check_init()?);
diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs
index 9da7f5e30cb9e..42ff7b1945918 100644
--- a/compiler/rustc_const_eval/src/interpret/validity.rs
+++ b/compiler/rustc_const_eval/src/interpret/validity.rs
@@ -315,7 +315,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
                 let vtable = self.ecx.scalar_to_ptr(meta.unwrap_meta());
                 // Direct call to `check_ptr_access_align` checks alignment even on CTFE machines.
                 try_validation!(
-                    self.ecx.memory.check_ptr_access_align(
+                    self.ecx.check_ptr_access_align(
                         vtable,
                         3 * self.ecx.tcx.data_layout.pointer_size, // drop, size, align
                         self.ecx.tcx.data_layout.pointer_align.abi,
@@ -403,7 +403,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
             .unwrap_or_else(|| (place.layout.size, place.layout.align.abi));
         // Direct call to `check_ptr_access_align` checks alignment even on CTFE machines.
         try_validation!(
-            self.ecx.memory.check_ptr_access_align(
+            self.ecx.check_ptr_access_align(
                 place.ptr,
                 size,
                 align,
@@ -432,7 +432,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
         if let Some(ref mut ref_tracking) = self.ref_tracking {
             // Proceed recursively even for ZST, no reason to skip them!
             // `!` is a ZST and we want to validate it.
-            if let Ok((alloc_id, _offset, _ptr)) = self.ecx.memory.ptr_try_get_alloc(place.ptr) {
+            if let Ok((alloc_id, _offset, _ptr)) = self.ecx.ptr_try_get_alloc_id(place.ptr) {
                 // Special handling for pointers to statics (irrespective of their type).
                 let alloc_kind = self.ecx.tcx.get_global_alloc(alloc_id);
                 if let Some(GlobalAlloc::Static(did)) = alloc_kind {
@@ -579,7 +579,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
                 if let Some(_) = self.ref_tracking {
                     let ptr = self.ecx.scalar_to_ptr(value);
                     let _fn = try_validation!(
-                        self.ecx.memory.get_fn(ptr),
+                        self.ecx.get_ptr_fn(ptr),
                         self.path,
                         err_ub!(DanglingIntPointer(0, _)) =>
                             { "a null function pointer" },
@@ -825,7 +825,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
                 let mplace = op.assert_mem_place(); // strings are never immediate
                 let len = mplace.len(self.ecx)?;
                 try_validation!(
-                    self.ecx.memory.read_bytes(mplace.ptr, Size::from_bytes(len)),
+                    self.ecx.read_bytes_ptr(mplace.ptr, Size::from_bytes(len)),
                     self.path,
                     err_ub!(InvalidUninitBytes(..)) => { "uninitialized data in `str`" },
                     err_unsup!(ReadPointerAsBytes) => { "a pointer in `str`" },
@@ -861,7 +861,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
                 // to reject those pointers, we just do not have the machinery to
                 // talk about parts of a pointer.
                 // We also accept uninit, for consistency with the slow path.
-                let Some(alloc) = self.ecx.memory.get(mplace.ptr, size, mplace.align)? else {
+                let Some(alloc) = self.ecx.get_ptr_alloc(mplace.ptr, size, mplace.align)? else {
                     // Size 0, nothing more to check.
                     return Ok(());
                 };
diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs
index 00ecbbbb93b98..32c52a6a8a6d9 100644
--- a/compiler/rustc_errors/src/diagnostic.rs
+++ b/compiler/rustc_errors/src/diagnostic.rs
@@ -378,6 +378,12 @@ impl Diagnostic {
         self
     }
 
+    /// Add a help message attached to this diagnostic with a customizable highlighted message.
+    pub fn highlighted_help(&mut self, msg: Vec<(String, Style)>) -> &mut Self {
+        self.sub_with_highlights(Level::Help, msg, MultiSpan::new(), None);
+        self
+    }
+
     /// Prints the span with some help above it.
     /// This is like [`Diagnostic::help()`], but it gets its own span.
     pub fn span_help<S: Into<MultiSpan>>(&mut self, sp: S, msg: &str) -> &mut Self {
diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs
index 37425c91157b3..45a215354d081 100644
--- a/compiler/rustc_middle/src/ty/mod.rs
+++ b/compiler/rustc_middle/src/ty/mod.rs
@@ -41,7 +41,7 @@ use rustc_hir::Node;
 use rustc_macros::HashStable;
 use rustc_query_system::ich::StableHashingContext;
 use rustc_session::cstore::CrateStoreDyn;
-use rustc_span::symbol::{kw, Ident, Symbol};
+use rustc_span::symbol::{kw, sym, Ident, Symbol};
 use rustc_span::Span;
 use rustc_target::abi::Align;
 
@@ -2206,7 +2206,7 @@ impl<'tcx> TyCtxt<'tcx> {
         self.impl_trait_ref(def_id).map(|tr| tr.def_id)
     }
 
-    /// If the given defid describes a method belonging to an impl, returns the
+    /// If the given `DefId` describes a method belonging to an impl, returns the
     /// `DefId` of the impl that the method belongs to; otherwise, returns `None`.
     pub fn impl_of_method(self, def_id: DefId) -> Option<DefId> {
         self.opt_associated_item(def_id).and_then(|trait_item| match trait_item.container {
@@ -2215,6 +2215,11 @@ impl<'tcx> TyCtxt<'tcx> {
         })
     }
 
+    /// If the given `DefId` belongs to a trait that was automatically derived, returns `true`.
+    pub fn is_builtin_derive(self, def_id: DefId) -> bool {
+        self.has_attr(def_id, sym::automatically_derived)
+    }
+
     /// Looks up the span of `impl_did` if the impl is local; otherwise returns `Err`
     /// with the name of the crate containing the impl.
     pub fn span_of_impl(self, impl_did: DefId) -> Result<Span, Symbol> {
diff --git a/compiler/rustc_mir_transform/src/check_packed_ref.rs b/compiler/rustc_mir_transform/src/check_packed_ref.rs
index f0367958ef8c5..4bf66cd4c9f2e 100644
--- a/compiler/rustc_mir_transform/src/check_packed_ref.rs
+++ b/compiler/rustc_mir_transform/src/check_packed_ref.rs
@@ -1,10 +1,9 @@
-use rustc_hir::def_id::{DefId, LocalDefId};
+use rustc_hir::def_id::LocalDefId;
 use rustc_middle::mir::visit::{PlaceContext, Visitor};
 use rustc_middle::mir::*;
 use rustc_middle::ty::query::Providers;
 use rustc_middle::ty::{self, TyCtxt};
 use rustc_session::lint::builtin::UNALIGNED_REFERENCES;
-use rustc_span::symbol::sym;
 
 use crate::util;
 use crate::MirLint;
@@ -50,22 +49,6 @@ fn unsafe_derive_on_repr_packed(tcx: TyCtxt<'_>, def_id: LocalDefId) {
     });
 }
 
-fn builtin_derive_def_id(tcx: TyCtxt<'_>, def_id: DefId) -> Option<DefId> {
-    debug!("builtin_derive_def_id({:?})", def_id);
-    if let Some(impl_def_id) = tcx.impl_of_method(def_id) {
-        if tcx.has_attr(impl_def_id, sym::automatically_derived) {
-            debug!("builtin_derive_def_id({:?}) - is {:?}", def_id, impl_def_id);
-            Some(impl_def_id)
-        } else {
-            debug!("builtin_derive_def_id({:?}) - not automatically derived", def_id);
-            None
-        }
-    } else {
-        debug!("builtin_derive_def_id({:?}) - not a method", def_id);
-        None
-    }
-}
-
 impl<'tcx> Visitor<'tcx> for PackedRefChecker<'_, 'tcx> {
     fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) {
         // Make sure we know where in the MIR we are.
@@ -83,7 +66,11 @@ impl<'tcx> Visitor<'tcx> for PackedRefChecker<'_, 'tcx> {
         if context.is_borrow() {
             if util::is_disaligned(self.tcx, self.body, self.param_env, *place) {
                 let def_id = self.body.source.instance.def_id();
-                if let Some(impl_def_id) = builtin_derive_def_id(self.tcx, def_id) {
+                if let Some(impl_def_id) = self
+                    .tcx
+                    .impl_of_method(def_id)
+                    .filter(|&def_id| self.tcx.is_builtin_derive(def_id))
+                {
                     // If a method is defined in the local crate,
                     // the impl containing that method should also be.
                     self.tcx.ensure().unsafe_derive_on_repr_packed(impl_def_id.expect_local());
diff --git a/compiler/rustc_mir_transform/src/const_prop.rs b/compiler/rustc_mir_transform/src/const_prop.rs
index e1dbf90b5b9e7..49ebdd302545a 100644
--- a/compiler/rustc_mir_transform/src/const_prop.rs
+++ b/compiler/rustc_mir_transform/src/const_prop.rs
@@ -184,8 +184,6 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine<'mir, 'tcx>
 
     type MemoryKind = !;
 
-    type MemoryExtra = ();
-
     fn load_mir(
         _ecx: &InterpCx<'mir, 'tcx, Self>,
         _instance: ty::InstanceDef<'tcx>,
@@ -267,7 +265,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine<'mir, 'tcx>
     }
 
     fn before_access_global(
-        _memory_extra: &(),
+        _machine: &Self,
         _alloc_id: AllocId,
         alloc: ConstAllocation<'tcx, Self::PointerTag, Self::AllocExtra>,
         _static_def_id: Option<DefId>,
@@ -377,7 +375,6 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
             span,
             param_env,
             ConstPropMachine::new(only_propagate_inside_block_locals, can_const_prop),
-            (),
         );
 
         let ret = ecx
diff --git a/compiler/rustc_mir_transform/src/const_prop_lint.rs b/compiler/rustc_mir_transform/src/const_prop_lint.rs
index 5be745bc1de38..602aa14b9e6c6 100644
--- a/compiler/rustc_mir_transform/src/const_prop_lint.rs
+++ b/compiler/rustc_mir_transform/src/const_prop_lint.rs
@@ -180,8 +180,6 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine<'mir, 'tcx>
 
     type MemoryKind = !;
 
-    type MemoryExtra = ();
-
     fn load_mir(
         _ecx: &InterpCx<'mir, 'tcx, Self>,
         _instance: ty::InstanceDef<'tcx>,
@@ -263,7 +261,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine<'mir, 'tcx>
     }
 
     fn before_access_global(
-        _memory_extra: &(),
+        _machine: &Self,
         _alloc_id: AllocId,
         alloc: ConstAllocation<'tcx, Self::PointerTag, Self::AllocExtra>,
         _static_def_id: Option<DefId>,
@@ -374,7 +372,6 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
             span,
             param_env,
             ConstPropMachine::new(only_propagate_inside_block_locals, can_const_prop),
-            (),
         );
 
         let ret = ecx
diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs
index 400adf20cba7c..6fedabc816cff 100644
--- a/compiler/rustc_resolve/src/late.rs
+++ b/compiler/rustc_resolve/src/late.rs
@@ -2297,21 +2297,19 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
         let prev = self.diagnostic_metadata.current_block_could_be_bare_struct_literal.take();
         if let (true, [Stmt { kind: StmtKind::Expr(expr), .. }]) =
             (block.could_be_bare_literal, &block.stmts[..])
+            && let ExprKind::Type(..) = expr.kind
         {
-            if let ExprKind::Type(..) = expr.kind {
-                self.diagnostic_metadata.current_block_could_be_bare_struct_literal =
-                    Some(block.span);
-            }
+            self.diagnostic_metadata.current_block_could_be_bare_struct_literal =
+            Some(block.span);
         }
         // Descend into the block.
         for stmt in &block.stmts {
-            if let StmtKind::Item(ref item) = stmt.kind {
-                if let ItemKind::MacroDef(..) = item.kind {
-                    num_macro_definition_ribs += 1;
-                    let res = self.r.local_def_id(item.id).to_def_id();
-                    self.ribs[ValueNS].push(Rib::new(MacroDefinition(res)));
-                    self.label_ribs.push(Rib::new(MacroDefinition(res)));
-                }
+            if let StmtKind::Item(ref item) = stmt.kind
+                && let ItemKind::MacroDef(..) = item.kind {
+                num_macro_definition_ribs += 1;
+                let res = self.r.local_def_id(item.id).to_def_id();
+                self.ribs[ValueNS].push(Rib::new(MacroDefinition(res)));
+                self.label_ribs.push(Rib::new(MacroDefinition(res)));
             }
 
             self.visit_stmt(stmt);
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index 2737c26708bc4..f5803aaa0786e 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -180,6 +180,9 @@ symbols! {
         Error,
         File,
         FileType,
+        Fn,
+        FnMut,
+        FnOnce,
         FormatSpec,
         Formatter,
         From,
@@ -248,6 +251,7 @@ symbols! {
         RustcEncodable,
         Send,
         SeqCst,
+        SliceIndex,
         Some,
         String,
         StructuralEq,
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
index 0a22c02520939..27748eef8f20b 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
@@ -2,10 +2,11 @@ pub mod on_unimplemented;
 pub mod suggestions;
 
 use super::{
-    EvaluationResult, FulfillmentContext, FulfillmentError, FulfillmentErrorCode,
-    MismatchedProjectionTypes, Obligation, ObligationCause, ObligationCauseCode,
-    OnUnimplementedDirective, OnUnimplementedNote, OutputTypeParameterMismatch, Overflow,
-    PredicateObligation, SelectionContext, SelectionError, TraitNotObjectSafe,
+    DerivedObligationCause, EvaluationResult, FulfillmentContext, FulfillmentError,
+    FulfillmentErrorCode, ImplDerivedObligationCause, MismatchedProjectionTypes, Obligation,
+    ObligationCause, ObligationCauseCode, OnUnimplementedDirective, OnUnimplementedNote,
+    OutputTypeParameterMismatch, Overflow, PredicateObligation, SelectionContext, SelectionError,
+    TraitNotObjectSafe,
 };
 
 use crate::infer::error_reporting::{TyCategory, TypeAnnotationNeeded as ErrorCode};
@@ -14,6 +15,7 @@ use crate::infer::{self, InferCtxt, TyCtxtInferExt};
 use rustc_data_structures::fx::FxHashMap;
 use rustc_errors::{
     pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed,
+    Style,
 };
 use rustc_hir as hir;
 use rustc_hir::def_id::DefId;
@@ -28,7 +30,7 @@ use rustc_middle::traits::select::OverflowError;
 use rustc_middle::ty::error::ExpectedFound;
 use rustc_middle::ty::fold::TypeFolder;
 use rustc_middle::ty::{
-    self, SubtypePredicate, ToPolyTraitRef, ToPredicate, Ty, TyCtxt, TypeFoldable,
+    self, SubtypePredicate, ToPolyTraitRef, ToPredicate, TraitRef, Ty, TyCtxt, TypeFoldable,
 };
 use rustc_span::symbol::{kw, sym};
 use rustc_span::{ExpnKind, MultiSpan, Span, DUMMY_SP};
@@ -354,7 +356,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
                         let have_alt_message = message.is_some() || label.is_some();
                         let is_try_conversion = self.is_try_conversion(span, trait_ref.def_id());
                         let is_unsize =
-                            { Some(trait_ref.def_id()) == self.tcx.lang_items().unsize_trait() };
+                            Some(trait_ref.def_id()) == self.tcx.lang_items().unsize_trait();
                         let (message, note, append_const_msg) = if is_try_conversion {
                             (
                                 Some(format!(
@@ -363,7 +365,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
                                 )),
                                 Some(
                                     "the question mark operation (`?`) implicitly performs a \
-                                        conversion on the error value using the `From` trait"
+                                     conversion on the error value using the `From` trait"
                                         .to_owned(),
                                 ),
                                 Some(None),
@@ -525,10 +527,12 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
                         }
 
                         self.suggest_floating_point_literal(&obligation, &mut err, &trait_ref);
-                        self.suggest_dereferences(&obligation, &mut err, trait_predicate);
-                        self.suggest_fn_call(&obligation, &mut err, trait_predicate);
-                        self.suggest_remove_reference(&obligation, &mut err, trait_predicate);
-                        self.suggest_semicolon_removal(
+                        let mut suggested =
+                            self.suggest_dereferences(&obligation, &mut err, trait_predicate);
+                        suggested |= self.suggest_fn_call(&obligation, &mut err, trait_predicate);
+                        suggested |=
+                            self.suggest_remove_reference(&obligation, &mut err, trait_predicate);
+                        suggested |= self.suggest_semicolon_removal(
                             &obligation,
                             &mut err,
                             span,
@@ -654,10 +658,80 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
                                 trait_predicate,
                                 obligation.cause.body_id,
                             );
-                        } else if !have_alt_message {
+                        } else if !suggested {
                             // Can't show anything else useful, try to find similar impls.
                             let impl_candidates = self.find_similar_impl_candidates(trait_ref);
-                            self.report_similar_impl_candidates(impl_candidates, &mut err);
+                            if !self.report_similar_impl_candidates(
+                                impl_candidates,
+                                trait_ref,
+                                &mut err,
+                            ) {
+                                // This is *almost* equivalent to
+                                // `obligation.cause.code().peel_derives()`, but it gives us the
+                                // trait predicate for that corresponding root obligation. This
+                                // lets us get a derived obligation from a type parameter, like
+                                // when calling `string.strip_suffix(p)` where `p` is *not* an
+                                // implementer of `Pattern<'_>`.
+                                let mut code = obligation.cause.code();
+                                let mut trait_pred = trait_predicate;
+                                let mut peeled = false;
+                                loop {
+                                    match &*code {
+                                        ObligationCauseCode::FunctionArgumentObligation {
+                                            parent_code,
+                                            ..
+                                        } => {
+                                            code = &parent_code;
+                                        }
+                                        ObligationCauseCode::ImplDerivedObligation(
+                                            box ImplDerivedObligationCause {
+                                                derived:
+                                                    DerivedObligationCause {
+                                                        parent_code,
+                                                        parent_trait_pred,
+                                                    },
+                                                ..
+                                            },
+                                        )
+                                        | ObligationCauseCode::BuiltinDerivedObligation(
+                                            DerivedObligationCause {
+                                                parent_code,
+                                                parent_trait_pred,
+                                            },
+                                        )
+                                        | ObligationCauseCode::DerivedObligation(
+                                            DerivedObligationCause {
+                                                parent_code,
+                                                parent_trait_pred,
+                                            },
+                                        ) => {
+                                            peeled = true;
+                                            code = &parent_code;
+                                            trait_pred = *parent_trait_pred;
+                                        }
+                                        _ => break,
+                                    };
+                                }
+                                let def_id = trait_pred.def_id();
+                                // Mention *all* the `impl`s for the *top most* obligation, the
+                                // user might have meant to use one of them, if any found. We skip
+                                // auto-traits or fundamental traits that might not be exactly what
+                                // the user might expect to be presented with. Instead this is
+                                // useful for less general traits.
+                                if peeled
+                                    && !self.tcx.trait_is_auto(def_id)
+                                    && !self.tcx.lang_items().items().contains(&Some(def_id))
+                                {
+                                    let trait_ref = trait_pred.to_poly_trait_ref();
+                                    let impl_candidates =
+                                        self.find_similar_impl_candidates(trait_ref);
+                                    self.report_similar_impl_candidates(
+                                        impl_candidates,
+                                        trait_ref,
+                                        &mut err,
+                                    );
+                                }
+                            }
                         }
 
                         // Changing mutability doesn't make a difference to whether we have
@@ -682,7 +756,6 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
                             });
                             let unit_obligation = obligation.with(predicate.to_predicate(tcx));
                             if self.predicate_may_hold(&unit_obligation) {
-                                err.note("this trait is implemented for `()`");
                                 err.note(
                                     "this error might have been caused by changes to \
                                     Rust's type-inference algorithm (see issue #48950 \
@@ -1307,8 +1380,9 @@ trait InferCtxtPrivExt<'hir, 'tcx> {
     fn report_similar_impl_candidates(
         &self,
         impl_candidates: Vec<ImplCandidate<'tcx>>,
+        trait_ref: ty::PolyTraitRef<'tcx>,
         err: &mut Diagnostic,
-    );
+    ) -> bool;
 
     /// Gets the parent trait chain start
     fn get_parent_trait_ref(
@@ -1319,7 +1393,11 @@ trait InferCtxtPrivExt<'hir, 'tcx> {
     /// If the `Self` type of the unsatisfied trait `trait_ref` implements a trait
     /// with the same path as `trait_ref`, a help message about
     /// a probable version mismatch is added to `err`
-    fn note_version_mismatch(&self, err: &mut Diagnostic, trait_ref: &ty::PolyTraitRef<'tcx>);
+    fn note_version_mismatch(
+        &self,
+        err: &mut Diagnostic,
+        trait_ref: &ty::PolyTraitRef<'tcx>,
+    ) -> bool;
 
     /// Creates a `PredicateObligation` with `new_self_ty` replacing the existing type in the
     /// `trait_ref`.
@@ -1681,15 +1759,83 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> {
     fn report_similar_impl_candidates(
         &self,
         impl_candidates: Vec<ImplCandidate<'tcx>>,
+        trait_ref: ty::PolyTraitRef<'tcx>,
         err: &mut Diagnostic,
-    ) {
+    ) -> bool {
+        let report = |mut candidates: Vec<TraitRef<'tcx>>, err: &mut Diagnostic| {
+            candidates.sort();
+            candidates.dedup();
+            let len = candidates.len();
+            if candidates.len() == 0 {
+                return false;
+            }
+            if candidates.len() == 1 {
+                err.highlighted_help(vec![
+                    (
+                        format!("the trait `{}` ", candidates[0].print_only_trait_path()),
+                        Style::NoStyle,
+                    ),
+                    ("is".to_string(), Style::Highlight),
+                    (" implemented for `".to_string(), Style::NoStyle),
+                    (candidates[0].self_ty().to_string(), Style::Highlight),
+                    ("`".to_string(), Style::NoStyle),
+                ]);
+                return true;
+            }
+            let trait_ref = TraitRef::identity(self.tcx, candidates[0].def_id);
+            // Check if the trait is the same in all cases. If so, we'll only show the type.
+            let mut traits: Vec<_> =
+                candidates.iter().map(|c| c.print_only_trait_path().to_string()).collect();
+            traits.sort();
+            traits.dedup();
+
+            let mut candidates: Vec<String> = candidates
+                .into_iter()
+                .map(|c| {
+                    if traits.len() == 1 {
+                        format!("\n  {}", c.self_ty())
+                    } else {
+                        format!("\n  {}", c)
+                    }
+                })
+                .collect();
+
+            candidates.sort();
+            candidates.dedup();
+            let end = if candidates.len() <= 9 { candidates.len() } else { 8 };
+            err.help(&format!(
+                "the following other types implement trait `{}`:{}{}",
+                trait_ref.print_only_trait_path(),
+                candidates[..end].join(""),
+                if len > 9 { format!("\nand {} others", len - 8) } else { String::new() }
+            ));
+            true
+        };
+
+        let def_id = trait_ref.def_id();
         if impl_candidates.is_empty() {
-            return;
+            if self.tcx.trait_is_auto(def_id)
+                || self.tcx.lang_items().items().contains(&Some(def_id))
+                || self.tcx.get_diagnostic_name(def_id).is_some()
+            {
+                // Mentioning implementers of `Copy`, `Debug` and friends is not useful.
+                return false;
+            }
+            let normalized_impl_candidates: Vec<_> = self
+                .tcx
+                .all_impls(def_id)
+                // Ignore automatically derived impls and `!Trait` impls.
+                .filter(|&def_id| {
+                    self.tcx.impl_polarity(def_id) != ty::ImplPolarity::Negative
+                        || self.tcx.is_builtin_derive(def_id)
+                })
+                .filter_map(|def_id| self.tcx.impl_trait_ref(def_id))
+                // Avoid mentioning type parameters.
+                .filter(|trait_ref| !matches!(trait_ref.self_ty().kind(), ty::Param(_)))
+                .collect();
+            return report(normalized_impl_candidates, err);
         }
 
-        let len = impl_candidates.len();
-        let end = if impl_candidates.len() <= 5 { impl_candidates.len() } else { 4 };
-
         let normalize = |candidate| {
             self.tcx.infer_ctxt().enter(|ref infcx| {
                 let normalized = infcx
@@ -1697,8 +1843,8 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> {
                     .normalize(candidate)
                     .ok();
                 match normalized {
-                    Some(normalized) => format!("\n  {}", normalized.value),
-                    None => format!("\n  {}", candidate),
+                    Some(normalized) => normalized.value,
+                    None => candidate,
                 }
             })
         };
@@ -1717,17 +1863,14 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> {
             })
             .collect::<Vec<_>>();
         normalized_impl_candidates_and_similarities.sort();
+        normalized_impl_candidates_and_similarities.dedup();
 
         let normalized_impl_candidates = normalized_impl_candidates_and_similarities
             .into_iter()
             .map(|(_, normalized)| normalized)
             .collect::<Vec<_>>();
 
-        err.help(&format!(
-            "the following implementations were found:{}{}",
-            normalized_impl_candidates[..end].join(""),
-            if len > 5 { format!("\nand {} others", len - 4) } else { String::new() }
-        ));
+        report(normalized_impl_candidates, err)
     }
 
     /// Gets the parent trait chain start
@@ -1758,7 +1901,11 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> {
     /// If the `Self` type of the unsatisfied trait `trait_ref` implements a trait
     /// with the same path as `trait_ref`, a help message about
     /// a probable version mismatch is added to `err`
-    fn note_version_mismatch(&self, err: &mut Diagnostic, trait_ref: &ty::PolyTraitRef<'tcx>) {
+    fn note_version_mismatch(
+        &self,
+        err: &mut Diagnostic,
+        trait_ref: &ty::PolyTraitRef<'tcx>,
+    ) -> bool {
         let get_trait_impl = |trait_def_id| {
             self.tcx.find_map_relevant_impl(trait_def_id, trait_ref.skip_binder().self_ty(), Some)
         };
@@ -1769,6 +1916,7 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> {
             .filter(|trait_def_id| *trait_def_id != trait_ref.def_id())
             .filter(|trait_def_id| self.tcx.def_path_str(*trait_def_id) == required_trait_path)
             .collect();
+        let mut suggested = false;
         for trait_with_same_path in traits_with_same_path {
             if let Some(impl_def_id) = get_trait_impl(trait_with_same_path) {
                 let impl_span = self.tcx.def_span(impl_def_id);
@@ -1779,8 +1927,10 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> {
                     trait_crate
                 );
                 err.note(&crate_msg);
+                suggested = true;
             }
         }
+        suggested
     }
 
     fn mk_trait_obligation_with_new_self_ty(
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 105e338048680..c5324bf85a71d 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
@@ -1,6 +1,6 @@
 use super::{
-    EvaluationResult, Obligation, ObligationCause, ObligationCauseCode, PredicateObligation,
-    SelectionContext,
+    DerivedObligationCause, EvaluationResult, ImplDerivedObligationCause, Obligation,
+    ObligationCause, ObligationCauseCode, PredicateObligation, SelectionContext,
 };
 
 use crate::autoderef::Autoderef;
@@ -58,7 +58,7 @@ pub trait InferCtxtExt<'tcx> {
         obligation: &PredicateObligation<'tcx>,
         err: &mut Diagnostic,
         trait_pred: ty::PolyTraitPredicate<'tcx>,
-    );
+    ) -> bool;
 
     fn get_closure_name(&self, def_id: DefId, err: &mut Diagnostic, msg: &str) -> Option<String>;
 
@@ -67,7 +67,7 @@ pub trait InferCtxtExt<'tcx> {
         obligation: &PredicateObligation<'tcx>,
         err: &mut Diagnostic,
         trait_pred: ty::PolyTraitPredicate<'tcx>,
-    );
+    ) -> bool;
 
     fn suggest_add_reference_to_arg(
         &self,
@@ -90,7 +90,7 @@ pub trait InferCtxtExt<'tcx> {
         obligation: &PredicateObligation<'tcx>,
         err: &mut Diagnostic,
         trait_pred: ty::PolyTraitPredicate<'tcx>,
-    );
+    ) -> bool;
 
     fn suggest_remove_await(&self, obligation: &PredicateObligation<'tcx>, err: &mut Diagnostic);
 
@@ -107,7 +107,7 @@ pub trait InferCtxtExt<'tcx> {
         err: &mut Diagnostic,
         span: Span,
         trait_pred: ty::PolyTraitPredicate<'tcx>,
-    );
+    ) -> bool;
 
     fn return_type_span(&self, obligation: &PredicateObligation<'tcx>) -> Option<Span>;
 
@@ -502,54 +502,87 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
         obligation: &PredicateObligation<'tcx>,
         err: &mut Diagnostic,
         trait_pred: ty::PolyTraitPredicate<'tcx>,
-    ) {
+    ) -> bool {
         // It only make sense when suggesting dereferences for arguments
-        let code = if let ObligationCauseCode::FunctionArgumentObligation { parent_code, .. } =
-            obligation.cause.code()
-        {
-            parent_code.clone()
-        } else {
-            return;
+        let ObligationCauseCode::FunctionArgumentObligation { .. } = obligation.cause.code() else {
+            return false;
         };
         let param_env = obligation.param_env;
         let body_id = obligation.cause.body_id;
         let span = obligation.cause.span;
-        let real_trait_pred = match &*code {
-            ObligationCauseCode::ImplDerivedObligation(cause) => cause.derived.parent_trait_pred,
-            ObligationCauseCode::DerivedObligation(cause)
-            | ObligationCauseCode::BuiltinDerivedObligation(cause) => cause.parent_trait_pred,
-            _ => trait_pred,
-        };
-        let Some(real_ty) = real_trait_pred.self_ty().no_bound_vars() else {
-            return;
-        };
+        let mut real_trait_pred = trait_pred;
+        let mut code = obligation.cause.code();
+        loop {
+            match &code {
+                ObligationCauseCode::FunctionArgumentObligation { parent_code, .. } => {
+                    code = &parent_code;
+                }
+                ObligationCauseCode::ImplDerivedObligation(box ImplDerivedObligationCause {
+                    derived: DerivedObligationCause { parent_code, parent_trait_pred },
+                    ..
+                })
+                | ObligationCauseCode::BuiltinDerivedObligation(DerivedObligationCause {
+                    parent_code,
+                    parent_trait_pred,
+                })
+                | ObligationCauseCode::DerivedObligation(DerivedObligationCause {
+                    parent_code,
+                    parent_trait_pred,
+                }) => {
+                    code = &parent_code;
+                    real_trait_pred = *parent_trait_pred;
+                }
+                _ => break,
+            };
+            let Some(real_ty) = real_trait_pred.self_ty().no_bound_vars() else {
+                continue;
+            };
 
-        if let ty::Ref(region, base_ty, mutbl) = *real_ty.kind() {
-            let mut autoderef = Autoderef::new(self, param_env, body_id, span, base_ty, span);
-            if let Some(steps) = autoderef.find_map(|(ty, steps)| {
-                // Re-add the `&`
-                let ty = self.tcx.mk_ref(region, TypeAndMut { ty, mutbl });
-                let obligation =
-                    self.mk_trait_obligation_with_new_self_ty(param_env, real_trait_pred, ty);
-                Some(steps).filter(|_| self.predicate_may_hold(&obligation))
-            }) {
-                if steps > 0 {
-                    if let Ok(src) = self.tcx.sess.source_map().span_to_snippet(span) {
-                        // Don't care about `&mut` because `DerefMut` is used less
-                        // often and user will not expect autoderef happens.
-                        if src.starts_with('&') && !src.starts_with("&mut ") {
-                            let derefs = "*".repeat(steps);
-                            err.span_suggestion(
-                                span,
-                                "consider adding dereference here",
-                                format!("&{}{}", derefs, &src[1..]),
-                                Applicability::MachineApplicable,
-                            );
+            if let ty::Ref(region, base_ty, mutbl) = *real_ty.kind() {
+                let mut autoderef = Autoderef::new(self, param_env, body_id, span, base_ty, span);
+                if let Some(steps) = autoderef.find_map(|(ty, steps)| {
+                    // Re-add the `&`
+                    let ty = self.tcx.mk_ref(region, TypeAndMut { ty, mutbl });
+                    let obligation =
+                        self.mk_trait_obligation_with_new_self_ty(param_env, real_trait_pred, ty);
+                    Some(steps).filter(|_| self.predicate_may_hold(&obligation))
+                }) {
+                    if steps > 0 {
+                        if let Ok(src) = self.tcx.sess.source_map().span_to_snippet(span) {
+                            // Don't care about `&mut` because `DerefMut` is used less
+                            // often and user will not expect autoderef happens.
+                            if src.starts_with('&') && !src.starts_with("&mut ") {
+                                let derefs = "*".repeat(steps);
+                                err.span_suggestion(
+                                    span,
+                                    "consider dereferencing here",
+                                    format!("&{}{}", derefs, &src[1..]),
+                                    Applicability::MachineApplicable,
+                                );
+                                return true;
+                            }
                         }
                     }
+                } else if real_trait_pred != trait_pred {
+                    // This branch addresses #87437.
+                    let obligation = self.mk_trait_obligation_with_new_self_ty(
+                        param_env,
+                        real_trait_pred,
+                        base_ty,
+                    );
+                    if self.predicate_may_hold(&obligation) {
+                        err.span_suggestion_verbose(
+                            span.shrink_to_lo(),
+                            "consider dereferencing here",
+                            "*".to_string(),
+                            Applicability::MachineApplicable,
+                        );
+                        return true;
+                    }
                 }
             }
         }
+        false
     }
 
     /// Given a closure's `DefId`, return the given name of the closure.
@@ -592,22 +625,22 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
         obligation: &PredicateObligation<'tcx>,
         err: &mut Diagnostic,
         trait_pred: ty::PolyTraitPredicate<'tcx>,
-    ) {
+    ) -> bool {
         let Some(self_ty) = trait_pred.self_ty().no_bound_vars() else {
-            return;
+            return false;
         };
 
         let (def_id, output_ty, callable) = match *self_ty.kind() {
             ty::Closure(def_id, substs) => (def_id, substs.as_closure().sig().output(), "closure"),
             ty::FnDef(def_id, _) => (def_id, self_ty.fn_sig(self.tcx).output(), "function"),
-            _ => return,
+            _ => return false,
         };
         let msg = format!("use parentheses to call the {}", callable);
 
         // `mk_trait_obligation_with_new_self_ty` only works for types with no escaping bound
         // variables, so bail out if we have any.
         let Some(output_ty) = output_ty.no_bound_vars() else {
-            return;
+            return false;
         };
 
         let new_obligation =
@@ -619,7 +652,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
                 | EvaluationResult::EvaluatedToOkModuloRegions
                 | EvaluationResult::EvaluatedToAmbig,
             ) => {}
-            _ => return,
+            _ => return false,
         }
         let hir = self.tcx.hir();
         // Get the name of the callable and the arguments to be used in the suggestion.
@@ -630,7 +663,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
             })) => {
                 err.span_label(*span, "consider calling this closure");
                 let Some(name) = self.get_closure_name(def_id, err, &msg) else {
-                    return;
+                    return false;
                 };
                 let args = decl.inputs.iter().map(|_| "_").collect::<Vec<_>>().join(", ");
                 let sugg = format!("({})", args);
@@ -658,7 +691,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
                 let sugg = format!("({})", args);
                 (format!("{}{}", ident, sugg), sugg)
             }
-            _ => return,
+            _ => return false,
         };
         if matches!(obligation.cause.code(), ObligationCauseCode::FunctionArgumentObligation { .. })
         {
@@ -675,6 +708,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
         } else {
             err.help(&format!("{}: `{}`", msg, snippet));
         }
+        true
     }
 
     fn suggest_add_reference_to_arg(
@@ -845,19 +879,20 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
         obligation: &PredicateObligation<'tcx>,
         err: &mut Diagnostic,
         trait_pred: ty::PolyTraitPredicate<'tcx>,
-    ) {
+    ) -> bool {
         let span = obligation.cause.span;
 
+        let mut suggested = false;
         if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) {
             let refs_number =
                 snippet.chars().filter(|c| !c.is_whitespace()).take_while(|c| *c == '&').count();
             if let Some('\'') = snippet.chars().filter(|c| !c.is_whitespace()).nth(refs_number) {
                 // Do not suggest removal of borrow from type arguments.
-                return;
+                return false;
             }
 
             let Some(mut suggested_ty) = trait_pred.self_ty().no_bound_vars() else {
-                return;
+                return false;
             };
 
             for refs_remaining in 0..refs_number {
@@ -893,10 +928,12 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
                         String::new(),
                         Applicability::MachineApplicable,
                     );
+                    suggested = true;
                     break;
                 }
             }
         }
+        suggested
     }
 
     fn suggest_remove_await(&self, obligation: &PredicateObligation<'tcx>, err: &mut Diagnostic) {
@@ -1033,7 +1070,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
         err: &mut Diagnostic,
         span: Span,
         trait_pred: ty::PolyTraitPredicate<'tcx>,
-    ) {
+    ) -> bool {
         let hir = self.tcx.hir();
         let parent_node = hir.get_parent_node(obligation.cause.body_id);
         let node = hir.find(parent_node);
@@ -1052,7 +1089,9 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
         {
             let sp = self.tcx.sess.source_map().end_point(stmt.span);
             err.span_label(sp, "consider removing this semicolon");
+            return true;
         }
+        false
     }
 
     fn return_type_span(&self, obligation: &PredicateObligation<'tcx>) -> Option<Span> {
@@ -1082,8 +1121,8 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
         }
 
         let hir = self.tcx.hir();
-        let parent_node = hir.get_parent_node(obligation.cause.body_id);
-        let node = hir.find(parent_node);
+        let fn_hir_id = hir.get_parent_node(obligation.cause.body_id);
+        let node = hir.find(fn_hir_id);
         let Some(hir::Node::Item(hir::Item {
             kind: hir::ItemKind::Fn(sig, _, body_id),
             ..
@@ -1121,16 +1160,17 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
         visitor.visit_body(&body);
 
         let typeck_results = self.in_progress_typeck_results.map(|t| t.borrow()).unwrap();
+        let Some(liberated_sig) = typeck_results.liberated_fn_sigs().get(fn_hir_id) else { return false; };
 
-        let mut ret_types = visitor
+        let ret_types = visitor
             .returns
             .iter()
-            .filter_map(|expr| typeck_results.node_type_opt(expr.hir_id))
-            .map(|ty| self.resolve_vars_if_possible(ty));
+            .filter_map(|expr| Some((expr.span, typeck_results.node_type_opt(expr.hir_id)?)))
+            .map(|(expr_span, ty)| (expr_span, self.resolve_vars_if_possible(ty)));
         let (last_ty, all_returns_have_same_type, only_never_return) = ret_types.clone().fold(
             (None, true, true),
             |(last_ty, mut same, only_never_return): (std::option::Option<Ty<'_>>, bool, bool),
-             ty| {
+             (_, ty)| {
                 let ty = self.resolve_vars_if_possible(ty);
                 same &=
                     !matches!(ty.kind(), ty::Error(_))
@@ -1151,39 +1191,60 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
                 (Some(ty), same, only_never_return && matches!(ty.kind(), ty::Never))
             },
         );
-        let all_returns_conform_to_trait =
-            if let Some(ty_ret_ty) = typeck_results.node_type_opt(ret_ty.hir_id) {
-                match ty_ret_ty.kind() {
-                    ty::Dynamic(predicates, _) => {
-                        let cause = ObligationCause::misc(ret_ty.span, ret_ty.hir_id);
-                        let param_env = ty::ParamEnv::empty();
-                        only_never_return
-                            || ret_types.all(|returned_ty| {
-                                predicates.iter().all(|predicate| {
-                                    let pred = predicate.with_self_ty(self.tcx, returned_ty);
-                                    let obl = Obligation::new(cause.clone(), param_env, pred);
-                                    self.predicate_may_hold(&obl)
-                                })
+        let mut spans_and_needs_box = vec![];
+
+        match liberated_sig.output().kind() {
+            ty::Dynamic(predicates, _) => {
+                let cause = ObligationCause::misc(ret_ty.span, fn_hir_id);
+                let param_env = ty::ParamEnv::empty();
+
+                if !only_never_return {
+                    for (expr_span, return_ty) in ret_types {
+                        let self_ty_satisfies_dyn_predicates = |self_ty| {
+                            predicates.iter().all(|predicate| {
+                                let pred = predicate.with_self_ty(self.tcx, self_ty);
+                                let obl = Obligation::new(cause.clone(), param_env, pred);
+                                self.predicate_may_hold(&obl)
                             })
+                        };
+
+                        if let ty::Adt(def, substs) = return_ty.kind()
+                            && def.is_box()
+                            && self_ty_satisfies_dyn_predicates(substs.type_at(0))
+                        {
+                            spans_and_needs_box.push((expr_span, false));
+                        } else if self_ty_satisfies_dyn_predicates(return_ty) {
+                            spans_and_needs_box.push((expr_span, true));
+                        } else {
+                            return false;
+                        }
                     }
-                    _ => false,
                 }
-            } else {
-                true
-            };
+            }
+            _ => return false,
+        };
 
         let sm = self.tcx.sess.source_map();
-        let (true, hir::TyKind::TraitObject(..), Ok(snippet), true) = (
-            // Verify that we're dealing with a return `dyn Trait`
-            ret_ty.span.overlaps(span),
-            &ret_ty.kind,
-            sm.span_to_snippet(ret_ty.span),
-            // If any of the return types does not conform to the trait, then we can't
-            // suggest `impl Trait` nor trait objects: it is a type mismatch error.
-            all_returns_conform_to_trait,
-        ) else {
+        if !ret_ty.span.overlaps(span) {
             return false;
+        }
+        let snippet = if let hir::TyKind::TraitObject(..) = ret_ty.kind {
+            if let Ok(snippet) = sm.span_to_snippet(ret_ty.span) {
+                snippet
+            } else {
+                return false;
+            }
+        } else {
+            // Substitute the type, so we can print a fixup given `type Alias = dyn Trait`
+            let name = liberated_sig.output().to_string();
+            let name =
+                name.strip_prefix('(').and_then(|name| name.strip_suffix(')')).unwrap_or(&name);
+            if !name.starts_with("dyn ") {
+                return false;
+            }
+            name.to_owned()
         };
+
         err.code(error_code!(E0746));
         err.set_primary_message("return type cannot have an unboxed trait object");
         err.children.clear();
@@ -1193,6 +1254,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
         let trait_obj_msg = "for information on trait objects, see \
             <https://doc.rust-lang.org/book/ch17-02-trait-objects.html\
             #using-trait-objects-that-allow-for-values-of-different-types>";
+
         let has_dyn = snippet.split_whitespace().next().map_or(false, |s| s == "dyn");
         let trait_obj = if has_dyn { &snippet[4..] } else { &snippet };
         if only_never_return {
@@ -1220,26 +1282,25 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
         } else {
             if is_object_safe {
                 // Suggest `-> Box<dyn Trait>` and `Box::new(returned_value)`.
-                // Get all the return values and collect their span and suggestion.
-                let mut suggestions: Vec<_> = visitor
-                    .returns
-                    .iter()
-                    .flat_map(|expr| {
-                        [
-                            (expr.span.shrink_to_lo(), "Box::new(".to_string()),
-                            (expr.span.shrink_to_hi(), ")".to_string()),
-                        ]
-                        .into_iter()
-                    })
-                    .collect();
-                if !suggestions.is_empty() {
-                    // Add the suggestion for the return type.
-                    suggestions.push((ret_ty.span, format!("Box<dyn {}>", trait_obj)));
-                    err.multipart_suggestion(
-                        "return a boxed trait object instead",
-                        suggestions,
-                        Applicability::MaybeIncorrect,
-                    );
+                err.multipart_suggestion(
+                    "return a boxed trait object instead",
+                    vec![
+                        (ret_ty.span.shrink_to_lo(), "Box<".to_string()),
+                        (span.shrink_to_hi(), ">".to_string()),
+                    ],
+                    Applicability::MaybeIncorrect,
+                );
+                for (span, needs_box) in spans_and_needs_box {
+                    if needs_box {
+                        err.multipart_suggestion(
+                            "... and box this value",
+                            vec![
+                                (span.shrink_to_lo(), "Box::new(".to_string()),
+                                (span.shrink_to_hi(), ")".to_string()),
+                            ],
+                            Applicability::MaybeIncorrect,
+                        );
+                    }
                 }
             } else {
                 // This is currently not possible to trigger because E0038 takes precedence, but
@@ -2714,13 +2775,15 @@ fn suggest_trait_object_return_type_alternatives(
         Applicability::MaybeIncorrect,
     );
     if is_object_safe {
-        err.span_suggestion(
-            ret_ty,
+        err.multipart_suggestion(
             &format!(
                 "use a boxed trait object if all return paths implement trait `{}`",
                 trait_obj,
             ),
-            format!("Box<dyn {}>", trait_obj),
+            vec![
+                (ret_ty.shrink_to_lo(), "Box<".to_string()),
+                (ret_ty.shrink_to_hi(), ">".to_string()),
+            ],
             Applicability::MaybeIncorrect,
         );
     }
diff --git a/compiler/rustc_typeck/src/check/method/mod.rs b/compiler/rustc_typeck/src/check/method/mod.rs
index adc284785c294..8137d70292182 100644
--- a/compiler/rustc_typeck/src/check/method/mod.rs
+++ b/compiler/rustc_typeck/src/check/method/mod.rs
@@ -8,7 +8,6 @@ pub mod probe;
 mod suggest;
 
 pub use self::suggest::SelfSource;
-pub use self::CandidateSource::*;
 pub use self::MethodError::*;
 
 use crate::check::FnCtxt;
@@ -82,8 +81,8 @@ pub struct NoMatchData<'tcx> {
 // candidate can arise. Used for error reporting only.
 #[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
 pub enum CandidateSource {
-    ImplSource(DefId),
-    TraitSource(DefId /* trait id */),
+    Impl(DefId),
+    Trait(DefId /* trait id */),
 }
 
 impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
@@ -237,8 +236,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         match *source {
                             // Note: this cannot come from an inherent impl,
                             // because the first probing succeeded.
-                            ImplSource(def) => self.tcx.trait_id_of_impl(def),
-                            TraitSource(_) => None,
+                            CandidateSource::Impl(def) => self.tcx.trait_id_of_impl(def),
+                            CandidateSource::Trait(_) => None,
                         }
                     })
                     .collect(),
diff --git a/compiler/rustc_typeck/src/check/method/probe.rs b/compiler/rustc_typeck/src/check/method/probe.rs
index 6cae7ab1221d1..83474bd7e72f7 100644
--- a/compiler/rustc_typeck/src/check/method/probe.rs
+++ b/compiler/rustc_typeck/src/check/method/probe.rs
@@ -1,7 +1,7 @@
 use super::suggest;
+use super::CandidateSource;
 use super::MethodError;
 use super::NoMatchData;
-use super::{CandidateSource, ImplSource, TraitSource};
 
 use crate::check::FnCtxt;
 use crate::errors::MethodCallOnUnknownType;
@@ -694,7 +694,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
         for item in self.impl_or_trait_item(impl_def_id) {
             if !self.has_applicable_self(&item) {
                 // No receiver declared. Not a candidate.
-                self.record_static_candidate(ImplSource(impl_def_id));
+                self.record_static_candidate(CandidateSource::Impl(impl_def_id));
                 continue;
             }
 
@@ -848,7 +848,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
             debug!("elaborate_bounds(bound_trait_ref={:?})", bound_trait_ref);
             for item in self.impl_or_trait_item(bound_trait_ref.def_id()) {
                 if !self.has_applicable_self(&item) {
-                    self.record_static_candidate(TraitSource(bound_trait_ref.def_id()));
+                    self.record_static_candidate(CandidateSource::Trait(bound_trait_ref.def_id()));
                 } else {
                     mk_cand(self, bound_trait_ref, item);
                 }
@@ -946,7 +946,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
                 // Check whether `trait_def_id` defines a method with suitable name.
                 if !self.has_applicable_self(&item) {
                     debug!("method has inapplicable self");
-                    self.record_static_candidate(TraitSource(trait_def_id));
+                    self.record_static_candidate(CandidateSource::Trait(trait_def_id));
                     continue;
                 }
 
@@ -1018,8 +1018,8 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
             Some(Err(MethodError::Ambiguity(v))) => v
                 .into_iter()
                 .map(|source| match source {
-                    TraitSource(id) => id,
-                    ImplSource(impl_id) => match tcx.trait_id_of_impl(impl_id) {
+                    CandidateSource::Trait(id) => id,
+                    CandidateSource::Impl(impl_id) => match tcx.trait_id_of_impl(impl_id) {
                         Some(id) => id,
                         None => span_bug!(span, "found inherent method when looking at traits"),
                     },
@@ -1417,8 +1417,10 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
 
     fn candidate_source(&self, candidate: &Candidate<'tcx>, self_ty: Ty<'tcx>) -> CandidateSource {
         match candidate.kind {
-            InherentImplCandidate(..) => ImplSource(candidate.item.container.id()),
-            ObjectCandidate | WhereClauseCandidate(_) => TraitSource(candidate.item.container.id()),
+            InherentImplCandidate(..) => CandidateSource::Impl(candidate.item.container.id()),
+            ObjectCandidate | WhereClauseCandidate(_) => {
+                CandidateSource::Trait(candidate.item.container.id())
+            }
             TraitCandidate(trait_ref) => self.probe(|_| {
                 let _ = self
                     .at(&ObligationCause::dummy(), self.param_env)
@@ -1428,9 +1430,9 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
                     Ok(Some(traits::ImplSource::UserDefined(ref impl_data))) => {
                         // If only a single impl matches, make the error message point
                         // to that impl.
-                        ImplSource(impl_data.impl_def_id)
+                        CandidateSource::Impl(impl_data.impl_def_id)
                     }
-                    _ => TraitSource(candidate.item.container.id()),
+                    _ => CandidateSource::Trait(candidate.item.container.id()),
                 }
             }),
         }
diff --git a/compiler/rustc_typeck/src/check/method/suggest.rs b/compiler/rustc_typeck/src/check/method/suggest.rs
index b05f0e4d3c389..c2cb233f5b84c 100644
--- a/compiler/rustc_typeck/src/check/method/suggest.rs
+++ b/compiler/rustc_typeck/src/check/method/suggest.rs
@@ -110,7 +110,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
             for (idx, source) in sources.iter().take(limit).enumerate() {
                 match *source {
-                    CandidateSource::ImplSource(impl_did) => {
+                    CandidateSource::Impl(impl_did) => {
                         // Provide the best span we can. Use the item, if local to crate, else
                         // the impl, if local to crate (item may be defaulted), else nothing.
                         let Some(item) = self.associated_value(impl_did, item_name).or_else(|| {
@@ -193,7 +193,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                             );
                         }
                     }
-                    CandidateSource::TraitSource(trait_did) => {
+                    CandidateSource::Trait(trait_did) => {
                         let Some(item) = self.associated_value(trait_did, item_name) else { continue };
                         let item_span = self
                             .tcx
@@ -515,23 +515,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     custom_span_label = true;
                 }
                 if static_sources.len() == 1 {
-                    let ty_str = if let Some(CandidateSource::ImplSource(impl_did)) =
-                        static_sources.get(0)
-                    {
-                        // When the "method" is resolved through dereferencing, we really want the
-                        // original type that has the associated function for accurate suggestions.
-                        // (#61411)
-                        let ty = tcx.at(span).type_of(*impl_did);
-                        match (&ty.peel_refs().kind(), &actual.peel_refs().kind()) {
-                            (ty::Adt(def, _), ty::Adt(def_actual, _)) if def == def_actual => {
-                                // Use `actual` as it will have more `substs` filled in.
-                                self.ty_to_value_string(actual.peel_refs())
+                    let ty_str =
+                        if let Some(CandidateSource::Impl(impl_did)) = static_sources.get(0) {
+                            // When the "method" is resolved through dereferencing, we really want the
+                            // original type that has the associated function for accurate suggestions.
+                            // (#61411)
+                            let ty = tcx.at(span).type_of(*impl_did);
+                            match (&ty.peel_refs().kind(), &actual.peel_refs().kind()) {
+                                (ty::Adt(def, _), ty::Adt(def_actual, _)) if def == def_actual => {
+                                    // Use `actual` as it will have more `substs` filled in.
+                                    self.ty_to_value_string(actual.peel_refs())
+                                }
+                                _ => self.ty_to_value_string(ty.peel_refs()),
                             }
-                            _ => self.ty_to_value_string(ty.peel_refs()),
-                        }
-                    } else {
-                        self.ty_to_value_string(actual.peel_refs())
-                    };
+                        } else {
+                            self.ty_to_value_string(actual.peel_refs())
+                        };
                     if let SelfSource::MethodCall(expr) = source {
                         err.span_suggestion(
                             expr.span.to(span),
diff --git a/library/core/src/ops/function.rs b/library/core/src/ops/function.rs
index e5c4798afcbff..8383f92643550 100644
--- a/library/core/src/ops/function.rs
+++ b/library/core/src/ops/function.rs
@@ -53,6 +53,7 @@
 /// ```
 #[lang = "fn"]
 #[stable(feature = "rust1", since = "1.0.0")]
+#[rustc_diagnostic_item = "Fn"]
 #[rustc_paren_sugar]
 #[rustc_on_unimplemented(
     on(
@@ -133,6 +134,7 @@ pub trait Fn<Args>: FnMut<Args> {
 /// ```
 #[lang = "fn_mut"]
 #[stable(feature = "rust1", since = "1.0.0")]
+#[rustc_diagnostic_item = "FnMut"]
 #[rustc_paren_sugar]
 #[rustc_on_unimplemented(
     on(
@@ -205,6 +207,7 @@ pub trait FnMut<Args>: FnOnce<Args> {
 /// ```
 #[lang = "fn_once"]
 #[stable(feature = "rust1", since = "1.0.0")]
+#[rustc_diagnostic_item = "FnOnce"]
 #[rustc_paren_sugar]
 #[rustc_on_unimplemented(
     on(
diff --git a/library/core/src/ptr/const_ptr.rs b/library/core/src/ptr/const_ptr.rs
index 209ea5cb0439b..f862912432e56 100644
--- a/library/core/src/ptr/const_ptr.rs
+++ b/library/core/src/ptr/const_ptr.rs
@@ -152,9 +152,17 @@ impl<T: ?Sized> *const T {
 
     /// Gets the "address" portion of the pointer.
     ///
-    /// This is equivalent to `self as usize`, which semantically discards
-    /// *provenance* and *address-space* information. To properly restore that information,
-    /// use [`with_addr`][pointer::with_addr] or [`map_addr`][pointer::map_addr].
+    /// This is similar to `self as usize`, which semantically discards *provenance* and
+    /// *address-space* information. However, unlike `self as usize`, casting the returned address
+    /// back to a pointer yields [`invalid`][], which is undefined behavior to dereference. To
+    /// properly restore the lost information and obtain a dereferencable pointer, use
+    /// [`with_addr`][pointer::with_addr] or [`map_addr`][pointer::map_addr].
+    ///
+    /// If using those APIs is not possible because there is no way to preserve a pointer with the
+    /// required provenance, use [`expose_addr`][pointer::expose_addr] and
+    /// [`from_exposed_addr`][from_exposed_addr] instead. However, note that this makes
+    /// your code less portable and less amenable to tools that check for compliance with the Rust
+    /// memory model.
     ///
     /// On most platforms this will produce a value with the same bytes as the original
     /// pointer, because all the bytes are dedicated to describing the address.
@@ -162,8 +170,9 @@ impl<T: ?Sized> *const T {
     /// perform a change of representation to produce a value containing only the address
     /// portion of the pointer. What that means is up to the platform to define.
     ///
-    /// This API and its claimed semantics are part of the Strict Provenance experiment,
-    /// see the [module documentation][crate::ptr] for details.
+    /// This API and its claimed semantics are part of the Strict Provenance experiment, and as such
+    /// might change in the future (including possibly weakening this so it becomes wholly
+    /// equivalent to `self as usize`). See the [module documentation][crate::ptr] for details.
     #[must_use]
     #[inline]
     #[unstable(feature = "strict_provenance", issue = "95228")]
@@ -175,6 +184,41 @@ impl<T: ?Sized> *const T {
         self as usize
     }
 
+    /// Gets the "address" portion of the pointer, and 'exposes' the "provenance" part for future
+    /// use in [`from_exposed_addr`][].
+    ///
+    /// This is equivalent to `self as usize`, which semantically discards *provenance* and
+    /// *address-space* information. Furthermore, this (like the `as` cast) has the implicit
+    /// side-effect of marking the provenance as 'exposed', so on platforms that support it you can
+    /// later call [`from_exposed_addr`][] to reconstitute the original pointer including its
+    /// provenance. (Reconstructing address space information, if required, is your responsibility.)
+    ///
+    /// Using this method means that code is *not* following Strict Provenance rules. Supporting
+    /// [`from_exposed_addr`][] complicates specification and reasoning and may not be supported by
+    /// tools that help you to stay conformant with the Rust memory model, so it is recommended to
+    /// use [`addr`][pointer::addr] wherever possible.
+    ///
+    /// On most platforms this will produce a value with the same bytes as the original pointer,
+    /// because all the bytes are dedicated to describing the address. Platforms which need to store
+    /// additional information in the pointer may not support this operation, since the 'expose'
+    /// side-effect which is required for [`from_exposed_addr`][] to work is typically not
+    /// available.
+    ///
+    /// This API and its claimed semantics are part of the Strict Provenance experiment, see the
+    /// [module documentation][crate::ptr] for details.
+    ///
+    /// [`from_exposed_addr`]: from_exposed_addr
+    #[must_use]
+    #[inline]
+    #[unstable(feature = "strict_provenance", issue = "95228")]
+    pub fn expose_addr(self) -> usize
+    where
+        T: Sized,
+    {
+        // FIXME(strict_provenance_magic): I am magic and should be a compiler intrinsic.
+        self as usize
+    }
+
     /// Creates a new pointer with the given address.
     ///
     /// This performs the same operation as an `addr as ptr` cast, but copies
diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs
index b93c2ea961565..ba8b0670147ae 100644
--- a/library/core/src/ptr/mod.rs
+++ b/library/core/src/ptr/mod.rs
@@ -70,7 +70,7 @@
 //! interpretation of provenance. It's ok if your code doesn't strictly conform to it.**
 //!
 //! [Strict Provenance][] is an experimental set of APIs that help tools that try
-//! to validate the memory-safety of your program's execution. Notably this includes [miri][]
+//! to validate the memory-safety of your program's execution. Notably this includes [Miri][]
 //! and [CHERI][], which can detect when you access out of bounds memory or otherwise violate
 //! Rust's memory model.
 //!
@@ -136,7 +136,7 @@
 //!
 //! The strict provenance experiment is mostly only interested in exploring stricter *spatial*
 //! provenance. In this sense it can be thought of as a subset of the more ambitious and
-//! formal [Stacked Borrows][] research project, which is what tools like [miri][] are based on.
+//! formal [Stacked Borrows][] research project, which is what tools like [Miri][] are based on.
 //! In particular, Stacked Borrows is necessary to properly describe what borrows are allowed
 //! to do and when they become invalidated. This necessarily involves much more complex
 //! *temporal* reasoning than simply identifying allocations. Adjusting APIs and code
@@ -170,7 +170,8 @@
 //! Under Strict Provenance, a usize *cannot* accurately represent a pointer, and converting from
 //! a pointer to a usize is generally an operation which *only* extracts the address. It is
 //! therefore *impossible* to construct a valid pointer from a usize because there is no way
-//! to restore the address-space and provenance.
+//! to restore the address-space and provenance. In other words, pointer-integer-pointer
+//! roundtrips are not possible (in the sense that the resulting pointer is not dereferencable).
 //!
 //! The key insight to making this model *at all* viable is the [`with_addr`][] method:
 //!
@@ -194,10 +195,10 @@
 //! and then immediately converting back to a pointer. To make this use case more ergonomic,
 //! we provide the [`map_addr`][] method.
 //!
-//! To help make it clear that code is "following" Strict Provenance semantics, we also
-//! provide an [`addr`][] method which is currently equivalent to `ptr as usize`. In the
-//! future we may provide a lint for pointer<->integer casts to help you audit if your
-//! code conforms to strict provenance.
+//! To help make it clear that code is "following" Strict Provenance semantics, we also provide an
+//! [`addr`][] method which promises that the returned address is not part of a
+//! pointer-usize-pointer roundtrip. In the future we may provide a lint for pointer<->integer
+//! casts to help you audit if your code conforms to strict provenance.
 //!
 //!
 //! ## Using Strict Provenance
@@ -310,6 +311,41 @@
 //!   For instance, ARM explicitly supports high-bit tagging, and so CHERI on ARM inherits
 //!   that and should support it.
 //!
+//! ## Pointer-usize-pointer roundtrips and 'exposed' provenance
+//!
+//! **This section is *non-normative* and is part of the [Strict Provenance] experiment.**
+//!
+//! As discussed above, pointer-usize-pointer roundtrips are not possible under [Strict Provenance].
+//! However, there exists legacy Rust code that is full of such roundtrips, and legacy platform APIs
+//! regularly assume that `usize` can capture all the information that makes up a pointer. There
+//! also might be code that cannot be ported to Strict Provenance (which is something we would [like
+//! to hear about][Strict Provenance]).
+//!
+//! For situations like this, there is a fallback plan, a way to 'opt out' of Strict Provenance.
+//! However, note that this makes your code a lot harder to specify, and the code will not work
+//! (well) with tools like [Miri] and [CHERI].
+//!
+//! This fallback plan is provided by the [`expose_addr`] and [`from_exposed_addr`] methods (which
+//! are equivalent to `as` casts between pointers and integers). [`expose_addr`] is a lot like
+//! [`addr`], but additionally adds the provenance of the pointer to a global list of 'exposed'
+//! provenances. (This list is purely conceptual, it exists for the purpose of specifying Rust but
+//! is not materialized in actual executions, except in tools like [Miri].) [`from_exposed_addr`]
+//! can be used to construct a pointer with one of these previously 'exposed' provenances.
+//! [`from_exposed_addr`] takes only `addr: usize` as arguments, so unlike in [`with_addr`] there is
+//! no indication of what the correct provenance for the returned pointer is -- and that is exactly
+//! what makes pointer-usize-pointer roundtrips so tricky to rigorously specify! There is no
+//! algorithm that decides which provenance will be used. You can think of this as "guessing" the
+//! right provenance, and the guess will be "maximally in your favor", in the sense that if there is
+//! any way to avoid undefined behavior, then that is the guess that will be taken. However, if
+//! there is *no* previously 'exposed' provenance that justifies the way the returned pointer will
+//! be used, the program has undefined behavior.
+//!
+//! Using [`expose_addr`] or [`from_exposed_addr`] (or the equivalent `as` casts) means that code is
+//! *not* following Strict Provenance rules. The goal of the Strict Provenance experiment is to
+//! determine whether it is possible to use Rust without [`expose_addr`] and [`from_exposed_addr`].
+//! If this is successful, it would be a major win for avoiding specification complexity and to
+//! facilitate adoption of tools like [CHERI] and [Miri] that can be a big help in increasing the
+//! confidence in (unsafe) Rust code.
 //!
 //! [aliasing]: ../../nomicon/aliasing.html
 //! [book]: ../../book/ch19-01-unsafe-rust.html#dereferencing-a-raw-pointer
@@ -322,7 +358,9 @@
 //! [`map_addr`]: pointer::map_addr
 //! [`addr`]: pointer::addr
 //! [`ptr::invalid`]: core::ptr::invalid
-//! [miri]: https://github.com/rust-lang/miri
+//! [`expose_addr`]: pointer::expose_addr
+//! [`from_exposed_addr`]: from_exposed_addr
+//! [Miri]: https://github.com/rust-lang/miri
 //! [CHERI]: https://www.cl.cam.ac.uk/research/security/ctsrd/cheri/
 //! [Strict Provenance]: https://github.com/rust-lang/rust/issues/95228
 //! [Stacked Borrows]: https://plv.mpi-sws.org/rustbelt/stacked-borrows/
@@ -547,6 +585,78 @@ pub const fn invalid_mut<T>(addr: usize) -> *mut T {
     addr as *mut T
 }
 
+/// Convert an address back to a pointer, picking up a previously 'exposed' provenance.
+///
+/// This is equivalent to `addr as *const T`. The provenance of the returned pointer is that of *any*
+/// pointer that was previously passed to [`expose_addr`][pointer::expose_addr] or a `ptr as usize`
+/// cast. If there is no previously 'exposed' provenance that justifies the way this pointer will be
+/// used, the program has undefined behavior. Note that there is no algorithm that decides which
+/// provenance will be used. You can think of this as "guessing" the right provenance, and the guess
+/// will be "maximally in your favor", in the sense that if there is any way to avoid undefined
+/// behavior, then that is the guess that will be taken.
+///
+/// On platforms with multiple address spaces, it is your responsibility to ensure that the
+/// address makes sense in the address space that this pointer will be used with.
+///
+/// Using this method means that code is *not* following strict provenance rules. "Guessing" a
+/// suitable provenance complicates specification and reasoning and may not be supported by
+/// tools that help you to stay conformant with the Rust memory model, so it is recommended to
+/// use [`with_addr`][pointer::with_addr] wherever possible.
+///
+/// On most platforms this will produce a value with the same bytes as the address. Platforms
+/// which need to store additional information in a pointer may not support this operation,
+/// since it is generally not possible to actually *compute* which provenance the returned
+/// pointer has to pick up.
+///
+/// This API and its claimed semantics are part of the Strict Provenance experiment, see the
+/// [module documentation][crate::ptr] for details.
+#[must_use]
+#[inline]
+#[unstable(feature = "strict_provenance", issue = "95228")]
+pub fn from_exposed_addr<T>(addr: usize) -> *const T
+where
+    T: Sized,
+{
+    // FIXME(strict_provenance_magic): I am magic and should be a compiler intrinsic.
+    addr as *const T
+}
+
+/// Convert an address back to a mutable pointer, picking up a previously 'exposed' provenance.
+///
+/// This is equivalent to `addr as *mut T`. The provenance of the returned pointer is that of *any*
+/// pointer that was previously passed to [`expose_addr`][pointer::expose_addr] or a `ptr as usize`
+/// cast. If there is no previously 'exposed' provenance that justifies the way this pointer will be
+/// used, the program has undefined behavior. Note that there is no algorithm that decides which
+/// provenance will be used. You can think of this as "guessing" the right provenance, and the guess
+/// will be "maximally in your favor", in the sense that if there is any way to avoid undefined
+/// behavior, then that is the guess that will be taken.
+///
+/// On platforms with multiple address spaces, it is your responsibility to ensure that the
+/// address makes sense in the address space that this pointer will be used with.
+///
+/// Using this method means that code is *not* following strict provenance rules. "Guessing" a
+/// suitable provenance complicates specification and reasoning and may not be supported by
+/// tools that help you to stay conformant with the Rust memory model, so it is recommended to
+/// use [`with_addr`][pointer::with_addr] wherever possible.
+///
+/// On most platforms this will produce a value with the same bytes as the address. Platforms
+/// which need to store additional information in a pointer may not support this operation,
+/// since it is generally not possible to actually *compute* which provenance the returned
+/// pointer has to pick up.
+///
+/// This API and its claimed semantics are part of the Strict Provenance experiment, see the
+/// [module documentation][crate::ptr] for details.
+#[must_use]
+#[inline]
+#[unstable(feature = "strict_provenance", issue = "95228")]
+pub fn from_exposed_addr_mut<T>(addr: usize) -> *mut T
+where
+    T: Sized,
+{
+    // FIXME(strict_provenance_magic): I am magic and should be a compiler intrinsic.
+    addr as *mut T
+}
+
 /// Forms a raw slice from a pointer and a length.
 ///
 /// The `len` argument is the number of **elements**, not the number of bytes.
@@ -763,7 +873,7 @@ pub const unsafe fn swap_nonoverlapping<T>(x: *mut T, y: *mut T, count: usize) {
         );
     }
 
-    // NOTE(scottmcm) MIRI is disabled here as reading in smaller units is a
+    // NOTE(scottmcm) Miri is disabled here as reading in smaller units is a
     // pessimization for it.  Also, if the type contains any unaligned pointers,
     // copying those over multiple reads is difficult to support.
     #[cfg(not(miri))]
diff --git a/library/core/src/ptr/mut_ptr.rs b/library/core/src/ptr/mut_ptr.rs
index bfc89625935d9..5db9c3e941eb4 100644
--- a/library/core/src/ptr/mut_ptr.rs
+++ b/library/core/src/ptr/mut_ptr.rs
@@ -156,9 +156,17 @@ impl<T: ?Sized> *mut T {
 
     /// Gets the "address" portion of the pointer.
     ///
-    /// This is equivalent to `self as usize`, which semantically discards
-    /// *provenance* and *address-space* information. To properly restore that information,
-    /// use [`with_addr`][pointer::with_addr] or [`map_addr`][pointer::map_addr].
+    /// This is similar to `self as usize`, which semantically discards *provenance* and
+    /// *address-space* information. However, unlike `self as usize`, casting the returned address
+    /// back to a pointer yields [`invalid`][], which is undefined behavior to dereference. To
+    /// properly restore the lost information and obtain a dereferencable pointer, use
+    /// [`with_addr`][pointer::with_addr] or [`map_addr`][pointer::map_addr].
+    ///
+    /// If using those APIs is not possible because there is no way to preserve a pointer with the
+    /// required provenance, use [`expose_addr`][pointer::expose_addr] and
+    /// [`from_exposed_addr_mut`][from_exposed_addr_mut] instead. However, note that this makes
+    /// your code less portable and less amenable to tools that check for compliance with the Rust
+    /// memory model.
     ///
     /// On most platforms this will produce a value with the same bytes as the original
     /// pointer, because all the bytes are dedicated to describing the address.
@@ -166,8 +174,9 @@ impl<T: ?Sized> *mut T {
     /// perform a change of representation to produce a value containing only the address
     /// portion of the pointer. What that means is up to the platform to define.
     ///
-    /// This API and its claimed semantics are part of the Strict Provenance experiment,
-    /// see the [module documentation][crate::ptr] for details.
+    /// This API and its claimed semantics are part of the Strict Provenance experiment, and as such
+    /// might change in the future (including possibly weakening this so it becomes wholly
+    /// equivalent to `self as usize`). See the [module documentation][crate::ptr] for details.
     #[must_use]
     #[inline]
     #[unstable(feature = "strict_provenance", issue = "95228")]
@@ -179,6 +188,41 @@ impl<T: ?Sized> *mut T {
         self as usize
     }
 
+    /// Gets the "address" portion of the pointer, and 'exposes' the "provenance" part for future
+    /// use in [`from_exposed_addr`][].
+    ///
+    /// This is equivalent to `self as usize`, which semantically discards *provenance* and
+    /// *address-space* information. Furthermore, this (like the `as` cast) has the implicit
+    /// side-effect of marking the provenance as 'exposed', so on platforms that support it you can
+    /// later call [`from_exposed_addr_mut`][] to reconstitute the original pointer including its
+    /// provenance. (Reconstructing address space information, if required, is your responsibility.)
+    ///
+    /// Using this method means that code is *not* following Strict Provenance rules. Supporting
+    /// [`from_exposed_addr_mut`][] complicates specification and reasoning and may not be supported
+    /// by tools that help you to stay conformant with the Rust memory model, so it is recommended
+    /// to use [`addr`][pointer::addr] wherever possible.
+    ///
+    /// On most platforms this will produce a value with the same bytes as the original pointer,
+    /// because all the bytes are dedicated to describing the address. Platforms which need to store
+    /// additional information in the pointer may not support this operation, since the 'expose'
+    /// side-effect which is required for [`from_exposed_addr_mut`][] to work is typically not
+    /// available.
+    ///
+    /// This API and its claimed semantics are part of the Strict Provenance experiment, see the
+    /// [module documentation][crate::ptr] for details.
+    ///
+    /// [`from_exposed_addr_mut`]: from_exposed_addr_mut
+    #[must_use]
+    #[inline]
+    #[unstable(feature = "strict_provenance", issue = "95228")]
+    pub fn expose_addr(self) -> usize
+    where
+        T: Sized,
+    {
+        // FIXME(strict_provenance_magic): I am magic and should be a compiler intrinsic.
+        self as usize
+    }
+
     /// Creates a new pointer with the given address.
     ///
     /// This performs the same operation as an `addr as ptr` cast, but copies
diff --git a/library/core/src/ptr/non_null.rs b/library/core/src/ptr/non_null.rs
index 7516d4bba4cdf..6f402924e75df 100644
--- a/library/core/src/ptr/non_null.rs
+++ b/library/core/src/ptr/non_null.rs
@@ -256,8 +256,10 @@ impl<T: ?Sized> NonNull<T> {
 
     /// Gets the "address" portion of the pointer.
     ///
+    /// For more details see the equivalent method on a raw pointer, [`pointer::addr`].
+    ///
     /// This API and its claimed semantics are part of the Strict Provenance experiment,
-    /// see the [module documentation][crate::ptr] for details.
+    /// see the [`ptr` module documentation][crate::ptr].
     #[must_use]
     #[inline]
     #[unstable(feature = "strict_provenance", issue = "95228")]
@@ -272,8 +274,10 @@ impl<T: ?Sized> NonNull<T> {
 
     /// Creates a new pointer with the given address.
     ///
+    /// For more details see the equivalent method on a raw pointer, [`pointer::with_addr`].
+    ///
     /// This API and its claimed semantics are part of the Strict Provenance experiment,
-    /// see the [module documentation][crate::ptr] for details.
+    /// see the [`ptr` module documentation][crate::ptr].
     #[must_use]
     #[inline]
     #[unstable(feature = "strict_provenance", issue = "95228")]
@@ -287,10 +291,10 @@ impl<T: ?Sized> NonNull<T> {
 
     /// Creates a new pointer by mapping `self`'s address to a new one.
     ///
-    /// This is a convenience for [`with_addr`][Self::with_addr], see that method for details.
+    /// For more details see the equivalent method on a raw pointer, [`pointer::map_addr`].
     ///
     /// This API and its claimed semantics are part of the Strict Provenance experiment,
-    /// see the [module documentation][crate::ptr] for details.
+    /// see the [`ptr` module documentation][crate::ptr].
     #[must_use]
     #[inline]
     #[unstable(feature = "strict_provenance", issue = "95228")]
diff --git a/library/core/src/slice/index.rs b/library/core/src/slice/index.rs
index 880d0f80cc8a0..68de4e4e1f32a 100644
--- a/library/core/src/slice/index.rs
+++ b/library/core/src/slice/index.rs
@@ -140,6 +140,7 @@ mod private_slice_index {
 /// Implementations of this trait have to promise that if the argument
 /// to `get_(mut_)unchecked` is a safe reference, then so is the result.
 #[stable(feature = "slice_get_slice", since = "1.28.0")]
+#[rustc_diagnostic_item = "SliceIndex"]
 #[rustc_on_unimplemented(
     on(T = "str", label = "string indices are ranges of `usize`",),
     on(
diff --git a/src/test/ui/allocator/not-an-allocator.rs b/src/test/ui/allocator/not-an-allocator.rs
index 417c371bdbf61..4a538d4d0a0d8 100644
--- a/src/test/ui/allocator/not-an-allocator.rs
+++ b/src/test/ui/allocator/not-an-allocator.rs
@@ -1,8 +1,8 @@
 #[global_allocator]
 static A: usize = 0;
-//~^ the trait bound `usize:
-//~| the trait bound `usize:
-//~| the trait bound `usize:
-//~| the trait bound `usize:
+//~^ ERROR E0277
+//~| ERROR E0277
+//~| ERROR E0277
+//~| ERROR E0277
 
 fn main() {}
diff --git a/src/test/ui/allocator/not-an-allocator.stderr b/src/test/ui/allocator/not-an-allocator.stderr
index c0f6118a9f163..62bb0679f813a 100644
--- a/src/test/ui/allocator/not-an-allocator.stderr
+++ b/src/test/ui/allocator/not-an-allocator.stderr
@@ -6,6 +6,7 @@ LL | #[global_allocator]
 LL | static A: usize = 0;
    |           ^^^^^ the trait `GlobalAlloc` is not implemented for `usize`
    |
+   = help: the trait `GlobalAlloc` is implemented for `System`
    = note: this error originates in the attribute macro `global_allocator` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error[E0277]: the trait bound `usize: GlobalAlloc` is not satisfied
@@ -16,6 +17,7 @@ LL | #[global_allocator]
 LL | static A: usize = 0;
    |           ^^^^^ the trait `GlobalAlloc` is not implemented for `usize`
    |
+   = help: the trait `GlobalAlloc` is implemented for `System`
    = note: this error originates in the attribute macro `global_allocator` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error[E0277]: the trait bound `usize: GlobalAlloc` is not satisfied
@@ -26,6 +28,7 @@ LL | #[global_allocator]
 LL | static A: usize = 0;
    |           ^^^^^ the trait `GlobalAlloc` is not implemented for `usize`
    |
+   = help: the trait `GlobalAlloc` is implemented for `System`
    = note: this error originates in the attribute macro `global_allocator` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error[E0277]: the trait bound `usize: GlobalAlloc` is not satisfied
@@ -36,6 +39,7 @@ LL | #[global_allocator]
 LL | static A: usize = 0;
    |           ^^^^^ the trait `GlobalAlloc` is not implemented for `usize`
    |
+   = help: the trait `GlobalAlloc` is implemented for `System`
    = note: this error originates in the attribute macro `global_allocator` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: aborting due to 4 previous errors
diff --git a/src/test/ui/associated-types/associated-types-path-2.stderr b/src/test/ui/associated-types/associated-types-path-2.stderr
index f56631b12aa67..2d8d513409d3a 100644
--- a/src/test/ui/associated-types/associated-types-path-2.stderr
+++ b/src/test/ui/associated-types/associated-types-path-2.stderr
@@ -15,8 +15,7 @@ error[E0277]: the trait bound `u32: Foo` is not satisfied
 LL |     f1(2u32, 4u32);
    |     ^^ the trait `Foo` is not implemented for `u32`
    |
-   = help: the following implementations were found:
-             <i32 as Foo>
+   = help: the trait `Foo` is implemented for `i32`
 note: required by a bound in `f1`
   --> $DIR/associated-types-path-2.rs:13:14
    |
@@ -29,8 +28,7 @@ error[E0277]: the trait bound `u32: Foo` is not satisfied
 LL |     f1(2u32, 4u32);
    |              ^^^^ the trait `Foo` is not implemented for `u32`
    |
-   = help: the following implementations were found:
-             <i32 as Foo>
+   = help: the trait `Foo` is implemented for `i32`
 
 error[E0277]: the trait bound `u32: Foo` is not satisfied
   --> $DIR/associated-types-path-2.rs:35:8
@@ -40,8 +38,7 @@ LL |     f1(2u32, 4i32);
    |     |
    |     required by a bound introduced by this call
    |
-   = help: the following implementations were found:
-             <i32 as Foo>
+   = help: the trait `Foo` is implemented for `i32`
 note: required by a bound in `f1`
   --> $DIR/associated-types-path-2.rs:13:14
    |
@@ -54,8 +51,7 @@ error[E0277]: the trait bound `u32: Foo` is not satisfied
 LL |     f1(2u32, 4i32);
    |              ^^^^ the trait `Foo` is not implemented for `u32`
    |
-   = help: the following implementations were found:
-             <i32 as Foo>
+   = help: the trait `Foo` is implemented for `i32`
 
 error[E0308]: mismatched types
   --> $DIR/associated-types-path-2.rs:41:18
diff --git a/src/test/ui/associated-types/hr-associated-type-bound-1.stderr b/src/test/ui/associated-types/hr-associated-type-bound-1.stderr
index ec28ca240be2b..73b5e1053fb9d 100644
--- a/src/test/ui/associated-types/hr-associated-type-bound-1.stderr
+++ b/src/test/ui/associated-types/hr-associated-type-bound-1.stderr
@@ -4,8 +4,7 @@ error[E0277]: the trait bound `str: Clone` is not satisfied
 LL |     type U = str;
    |              ^^^ the trait `Clone` is not implemented for `str`
    |
-   = help: the following implementations were found:
-             <String as Clone>
+   = help: the trait `Clone` is implemented for `String`
 note: required by a bound in `X`
   --> $DIR/hr-associated-type-bound-1.rs:3:33
    |
diff --git a/src/test/ui/associated-types/hr-associated-type-bound-param-1.stderr b/src/test/ui/associated-types/hr-associated-type-bound-param-1.stderr
index e48ef8d17d1de..af2e616896a87 100644
--- a/src/test/ui/associated-types/hr-associated-type-bound-param-1.stderr
+++ b/src/test/ui/associated-types/hr-associated-type-bound-param-1.stderr
@@ -4,8 +4,7 @@ error[E0277]: the trait bound `str: Clone` is not satisfied
 LL |     type V = str;
    |              ^^^ the trait `Clone` is not implemented for `str`
    |
-   = help: the following implementations were found:
-             <String as Clone>
+   = help: the trait `Clone` is implemented for `String`
 note: required by a bound in `Y`
   --> $DIR/hr-associated-type-bound-param-1.rs:4:36
    |
diff --git a/src/test/ui/associated-types/hr-associated-type-bound-param-2.stderr b/src/test/ui/associated-types/hr-associated-type-bound-param-2.stderr
index 2fb3af38c0d9b..5809c407a5f7f 100644
--- a/src/test/ui/associated-types/hr-associated-type-bound-param-2.stderr
+++ b/src/test/ui/associated-types/hr-associated-type-bound-param-2.stderr
@@ -4,8 +4,7 @@ error[E0277]: the trait bound `str: Clone` is not satisfied
 LL |     T: Z<'a, u16>,
    |        ^^^^^^^^^^ the trait `Clone` is not implemented for `str`
    |
-   = help: the following implementations were found:
-             <String as Clone>
+   = help: the trait `Clone` is implemented for `String`
 note: required by a bound in `Z`
   --> $DIR/hr-associated-type-bound-param-2.rs:6:35
    |
@@ -21,8 +20,7 @@ error[E0277]: the trait bound `str: Clone` is not satisfied
 LL |     T: Z<'a, u16>,
    |        ^^^^^^^^^^ the trait `Clone` is not implemented for `str`
    |
-   = help: the following implementations were found:
-             <String as Clone>
+   = help: the trait `Clone` is implemented for `String`
 note: required by a bound in `Z`
   --> $DIR/hr-associated-type-bound-param-2.rs:6:35
    |
@@ -38,8 +36,7 @@ error[E0277]: the trait bound `str: Clone` is not satisfied
 LL |     type W = str;
    |              ^^^ the trait `Clone` is not implemented for `str`
    |
-   = help: the following implementations were found:
-             <String as Clone>
+   = help: the trait `Clone` is implemented for `String`
 note: required by a bound in `Z`
   --> $DIR/hr-associated-type-bound-param-2.rs:6:35
    |
diff --git a/src/test/ui/associated-types/hr-associated-type-bound-param-3.stderr b/src/test/ui/associated-types/hr-associated-type-bound-param-3.stderr
index 775f45ca82965..84d5e0494cb69 100644
--- a/src/test/ui/associated-types/hr-associated-type-bound-param-3.stderr
+++ b/src/test/ui/associated-types/hr-associated-type-bound-param-3.stderr
@@ -4,8 +4,7 @@ error[E0277]: the trait bound `str: Clone` is not satisfied
 LL |     type U = str;
    |              ^^^ the trait `Clone` is not implemented for `str`
    |
-   = help: the following implementations were found:
-             <String as Clone>
+   = help: the trait `Clone` is implemented for `String`
 note: required by a bound in `X`
   --> $DIR/hr-associated-type-bound-param-3.rs:4:33
    |
diff --git a/src/test/ui/associated-types/hr-associated-type-bound-param-4.stderr b/src/test/ui/associated-types/hr-associated-type-bound-param-4.stderr
index 4e9b64ba832ad..ee1d5d324954a 100644
--- a/src/test/ui/associated-types/hr-associated-type-bound-param-4.stderr
+++ b/src/test/ui/associated-types/hr-associated-type-bound-param-4.stderr
@@ -4,8 +4,7 @@ error[E0277]: the trait bound `str: Clone` is not satisfied
 LL |     type U = str;
    |              ^^^ the trait `Clone` is not implemented for `str`
    |
-   = help: the following implementations were found:
-             <String as Clone>
+   = help: the trait `Clone` is implemented for `String`
 note: required by a bound in `X`
   --> $DIR/hr-associated-type-bound-param-4.rs:4:36
    |
diff --git a/src/test/ui/associated-types/hr-associated-type-bound-param-5.stderr b/src/test/ui/associated-types/hr-associated-type-bound-param-5.stderr
index d00abf30d3b05..ece3151ba978c 100644
--- a/src/test/ui/associated-types/hr-associated-type-bound-param-5.stderr
+++ b/src/test/ui/associated-types/hr-associated-type-bound-param-5.stderr
@@ -4,8 +4,7 @@ error[E0277]: the trait bound `str: Clone` is not satisfied
 LL |     type U = str;
    |              ^^^ the trait `Clone` is not implemented for `str`
    |
-   = help: the following implementations were found:
-             <String as Clone>
+   = help: the trait `Clone` is implemented for `String`
 note: required by a bound in `X`
   --> $DIR/hr-associated-type-bound-param-5.rs:17:45
    |
@@ -21,8 +20,7 @@ error[E0277]: the trait bound `str: Clone` is not satisfied
 LL |     type U = str;
    |              ^^^ the trait `Clone` is not implemented for `str`
    |
-   = help: the following implementations were found:
-             <String as Clone>
+   = help: the trait `Clone` is implemented for `String`
 note: required by a bound in `X`
   --> $DIR/hr-associated-type-bound-param-5.rs:17:45
    |
diff --git a/src/test/ui/associated-types/issue-65774-1.stderr b/src/test/ui/associated-types/issue-65774-1.stderr
index 82c520b99b170..e468a1b3ba484 100644
--- a/src/test/ui/associated-types/issue-65774-1.stderr
+++ b/src/test/ui/associated-types/issue-65774-1.stderr
@@ -4,6 +4,7 @@ error[E0277]: the trait bound `T: MyDisplay` is not satisfied
 LL |     type MpuConfig: MyDisplay = T;
    |                                 ^ the trait `MyDisplay` is not implemented for `T`
    |
+   = help: the trait `MyDisplay` is implemented for `&'a mut T`
 note: required by a bound in `MPU::MpuConfig`
   --> $DIR/issue-65774-1.rs:10:21
    |
@@ -16,6 +17,7 @@ error[E0277]: the trait bound `T: MyDisplay` is not satisfied
 LL |         let closure = |config: &mut <S as MPU>::MpuConfig| writer.my_write(&config);
    |                                                                            ^^^^^^^ the trait `MyDisplay` is not implemented for `T`
    |
+   = help: the trait `MyDisplay` is implemented for `&'a mut T`
 note: required because of the requirements on the impl of `MyDisplay` for `&mut T`
   --> $DIR/issue-65774-1.rs:5:24
    |
diff --git a/src/test/ui/associated-types/issue-65774-2.stderr b/src/test/ui/associated-types/issue-65774-2.stderr
index 349cef9df72dd..4cef4db4698a7 100644
--- a/src/test/ui/associated-types/issue-65774-2.stderr
+++ b/src/test/ui/associated-types/issue-65774-2.stderr
@@ -4,6 +4,7 @@ error[E0277]: the trait bound `T: MyDisplay` is not satisfied
 LL |     type MpuConfig: MyDisplay = T;
    |                                 ^ the trait `MyDisplay` is not implemented for `T`
    |
+   = help: the trait `MyDisplay` is implemented for `&'a mut T`
 note: required by a bound in `MPU::MpuConfig`
   --> $DIR/issue-65774-2.rs:10:21
    |
@@ -16,6 +17,7 @@ error[E0277]: the trait bound `T: MyDisplay` is not satisfied
 LL |         writer.my_write(valref)
    |                         ^^^^^^ the trait `MyDisplay` is not implemented for `T`
    |
+   = help: the trait `MyDisplay` is implemented for `&'a mut T`
    = note: required for the cast to the object type `dyn MyDisplay`
 
 error: aborting due to 2 previous errors
diff --git a/src/test/ui/auto-traits/typeck-default-trait-impl-precedence.stderr b/src/test/ui/auto-traits/typeck-default-trait-impl-precedence.stderr
index 5755778fef266..985cdce1224da 100644
--- a/src/test/ui/auto-traits/typeck-default-trait-impl-precedence.stderr
+++ b/src/test/ui/auto-traits/typeck-default-trait-impl-precedence.stderr
@@ -4,8 +4,7 @@ error[E0277]: the trait bound `u32: Signed` is not satisfied
 LL |     is_defaulted::<&'static u32>();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Signed` is not implemented for `u32`
    |
-   = help: the following implementations were found:
-             <i32 as Signed>
+   = help: the trait `Signed` is implemented for `i32`
 note: required because of the requirements on the impl of `Defaulted` for `&'static u32`
   --> $DIR/typeck-default-trait-impl-precedence.rs:10:19
    |
diff --git a/src/test/ui/binop/binop-mul-i32-f32.stderr b/src/test/ui/binop/binop-mul-i32-f32.stderr
index 4a67fe2379b8d..21c490965b147 100644
--- a/src/test/ui/binop/binop-mul-i32-f32.stderr
+++ b/src/test/ui/binop/binop-mul-i32-f32.stderr
@@ -5,6 +5,16 @@ LL |     x * y
    |       ^ no implementation for `i32 * f32`
    |
    = help: the trait `Mul<f32>` is not implemented for `i32`
+   = help: the following other types implement trait `Mul<Rhs>`:
+             <&'a f32 as Mul<f32>>
+             <&'a f64 as Mul<f64>>
+             <&'a i128 as Mul<i128>>
+             <&'a i16 as Mul<i16>>
+             <&'a i32 as Mul<i32>>
+             <&'a i64 as Mul<i64>>
+             <&'a i8 as Mul<i8>>
+             <&'a isize as Mul<isize>>
+           and 49 others
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/binop/issue-77910-1.stderr b/src/test/ui/binop/issue-77910-1.stderr
index db854ae80d738..95ee51a88261a 100644
--- a/src/test/ui/binop/issue-77910-1.stderr
+++ b/src/test/ui/binop/issue-77910-1.stderr
@@ -16,6 +16,16 @@ LL |     assert_eq!(foo, y);
    |     ^^^^^^^^^^^^^^^^^^ `for<'r> fn(&'r i32) -> &'r i32 {foo}` cannot be formatted using `{:?}` because it doesn't implement `Debug`
    |
    = help: the trait `Debug` is not implemented for `for<'r> fn(&'r i32) -> &'r i32 {foo}`
+   = help: the following other types implement trait `Debug`:
+             extern "C" fn() -> Ret
+             extern "C" fn(A) -> Ret
+             extern "C" fn(A, ...) -> Ret
+             extern "C" fn(A, B) -> Ret
+             extern "C" fn(A, B, ...) -> Ret
+             extern "C" fn(A, B, C) -> Ret
+             extern "C" fn(A, B, C, ...) -> Ret
+             extern "C" fn(A, B, C, D) -> Ret
+           and 68 others
    = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: aborting due to 2 previous errors
diff --git a/src/test/ui/binop/shift-various-bad-types.stderr b/src/test/ui/binop/shift-various-bad-types.stderr
index 932a435143b0c..38db66f86b461 100644
--- a/src/test/ui/binop/shift-various-bad-types.stderr
+++ b/src/test/ui/binop/shift-various-bad-types.stderr
@@ -5,6 +5,16 @@ LL |     22 >> p.char;
    |        ^^ no implementation for `{integer} >> char`
    |
    = help: the trait `Shr<char>` is not implemented for `{integer}`
+   = help: the following other types implement trait `Shr<Rhs>`:
+             <&'a i128 as Shr<i128>>
+             <&'a i128 as Shr<i16>>
+             <&'a i128 as Shr<i32>>
+             <&'a i128 as Shr<i64>>
+             <&'a i128 as Shr<i8>>
+             <&'a i128 as Shr<isize>>
+             <&'a i128 as Shr<u128>>
+             <&'a i128 as Shr<u16>>
+           and 568 others
 
 error[E0277]: no implementation for `{integer} >> &str`
   --> $DIR/shift-various-bad-types.rs:12:8
@@ -13,6 +23,16 @@ LL |     22 >> p.str;
    |        ^^ no implementation for `{integer} >> &str`
    |
    = help: the trait `Shr<&str>` is not implemented for `{integer}`
+   = help: the following other types implement trait `Shr<Rhs>`:
+             <&'a i128 as Shr<i128>>
+             <&'a i128 as Shr<i16>>
+             <&'a i128 as Shr<i32>>
+             <&'a i128 as Shr<i64>>
+             <&'a i128 as Shr<i8>>
+             <&'a i128 as Shr<isize>>
+             <&'a i128 as Shr<u128>>
+             <&'a i128 as Shr<u16>>
+           and 568 others
 
 error[E0277]: no implementation for `{integer} >> &Panolpy`
   --> $DIR/shift-various-bad-types.rs:15:8
@@ -21,6 +41,16 @@ LL |     22 >> p;
    |        ^^ no implementation for `{integer} >> &Panolpy`
    |
    = help: the trait `Shr<&Panolpy>` is not implemented for `{integer}`
+   = help: the following other types implement trait `Shr<Rhs>`:
+             <&'a i128 as Shr<i128>>
+             <&'a i128 as Shr<i16>>
+             <&'a i128 as Shr<i32>>
+             <&'a i128 as Shr<i64>>
+             <&'a i128 as Shr<i8>>
+             <&'a i128 as Shr<isize>>
+             <&'a i128 as Shr<u128>>
+             <&'a i128 as Shr<u16>>
+           and 568 others
 
 error[E0308]: mismatched types
   --> $DIR/shift-various-bad-types.rs:25:18
diff --git a/src/test/ui/block-result/issue-22645.stderr b/src/test/ui/block-result/issue-22645.stderr
index 397bdac60513e..31663e8e84d72 100644
--- a/src/test/ui/block-result/issue-22645.stderr
+++ b/src/test/ui/block-result/issue-22645.stderr
@@ -4,8 +4,7 @@ error[E0277]: the trait bound `{integer}: Scalar` is not satisfied
 LL |   b + 3
    |     ^ the trait `Scalar` is not implemented for `{integer}`
    |
-   = help: the following implementations were found:
-             <f64 as Scalar>
+   = help: the trait `Scalar` is implemented for `f64`
 note: required because of the requirements on the impl of `Add<{integer}>` for `Bob`
   --> $DIR/issue-22645.rs:8:19
    |
diff --git a/src/test/ui/chalkify/chalk_initial_program.stderr b/src/test/ui/chalkify/chalk_initial_program.stderr
index 7b0b3f85b3915..343c0a31862b9 100644
--- a/src/test/ui/chalkify/chalk_initial_program.stderr
+++ b/src/test/ui/chalkify/chalk_initial_program.stderr
@@ -4,9 +4,9 @@ error[E0277]: the trait bound `f32: Foo` is not satisfied
 LL |     gimme::<f32>();
    |             ^^^ the trait `Foo` is not implemented for `f32`
    |
-   = help: the following implementations were found:
-             <i32 as Foo>
-             <u32 as Foo>
+   = help: the following other types implement trait `Foo`:
+             i32
+             u32
 note: required by a bound in `gimme`
   --> $DIR/chalk_initial_program.rs:9:13
    |
diff --git a/src/test/ui/chalkify/generic_impls.stderr b/src/test/ui/chalkify/generic_impls.stderr
index 0c7fcd04dfd2d..d4a8354d3fc80 100644
--- a/src/test/ui/chalkify/generic_impls.stderr
+++ b/src/test/ui/chalkify/generic_impls.stderr
@@ -4,8 +4,7 @@ error[E0277]: the trait bound `(Option<T>, f32): Foo` is not satisfied
 LL |     gimme::<(Option<T>, f32)>();
    |             ^^^^^^^^^^^^^^^^ the trait `Foo` is not implemented for `(Option<T>, f32)`
    |
-   = help: the following implementations were found:
-             <(T, u32) as Foo>
+   = help: the trait `Foo` is implemented for `(T, u32)`
 note: required by a bound in `gimme`
   --> $DIR/generic_impls.rs:7:13
    |
@@ -18,8 +17,7 @@ error[E0277]: the trait bound `(i32, f32): Foo` is not satisfied
 LL |     gimme::<(i32, f32)>();
    |             ^^^^^^^^^^ the trait `Foo` is not implemented for `(i32, f32)`
    |
-   = help: the following implementations were found:
-             <(T, u32) as Foo>
+   = help: the trait `Foo` is implemented for `(T, u32)`
 note: required by a bound in `gimme`
   --> $DIR/generic_impls.rs:7:13
    |
diff --git a/src/test/ui/chalkify/impl_wf.stderr b/src/test/ui/chalkify/impl_wf.stderr
index 2bc9f077f0285..a142459bcb4a5 100644
--- a/src/test/ui/chalkify/impl_wf.stderr
+++ b/src/test/ui/chalkify/impl_wf.stderr
@@ -17,8 +17,7 @@ error[E0277]: the trait bound `f32: Foo` is not satisfied
 LL | impl Baz<f32> for f32 { }
    |      ^^^^^^^^ the trait `Foo` is not implemented for `f32`
    |
-   = help: the following implementations were found:
-             <i32 as Foo>
+   = help: the trait `Foo` is implemented for `i32`
 note: required by a bound in `Baz`
   --> $DIR/impl_wf.rs:18:31
    |
diff --git a/src/test/ui/chalkify/impl_wf_2.stderr b/src/test/ui/chalkify/impl_wf_2.stderr
index 30cec80b036c6..1c1df644b0610 100644
--- a/src/test/ui/chalkify/impl_wf_2.stderr
+++ b/src/test/ui/chalkify/impl_wf_2.stderr
@@ -4,8 +4,7 @@ error[E0277]: the trait bound `f32: Foo` is not satisfied
 LL |     type Item = f32;
    |                 ^^^ the trait `Foo` is not implemented for `f32`
    |
-   = help: the following implementations were found:
-             <i32 as Foo>
+   = help: the trait `Foo` is implemented for `i32`
 note: required by a bound in `Bar::Item`
   --> $DIR/impl_wf_2.rs:8:16
    |
diff --git a/src/test/ui/chalkify/type_inference.stderr b/src/test/ui/chalkify/type_inference.stderr
index 14d43c1474c57..508a6dd1388cb 100644
--- a/src/test/ui/chalkify/type_inference.stderr
+++ b/src/test/ui/chalkify/type_inference.stderr
@@ -6,9 +6,9 @@ LL |     only_bar(x);
    |     |
    |     required by a bound introduced by this call
    |
-   = help: the following implementations were found:
-             <i32 as Bar>
-             <u32 as Bar>
+   = help: the following other types implement trait `Bar`:
+             i32
+             u32
 note: required by a bound in `only_bar`
   --> $DIR/type_inference.rs:12:16
    |
diff --git a/src/test/ui/chalkify/type_wf.stderr b/src/test/ui/chalkify/type_wf.stderr
index 6abd8b28760d8..7f8566082cd26 100644
--- a/src/test/ui/chalkify/type_wf.stderr
+++ b/src/test/ui/chalkify/type_wf.stderr
@@ -4,8 +4,7 @@ error[E0277]: the trait bound `{float}: Foo` is not satisfied
 LL |     let s = S {
    |             ^ the trait `Foo` is not implemented for `{float}`
    |
-   = help: the following implementations were found:
-             <i32 as Foo>
+   = help: the trait `Foo` is implemented for `i32`
 note: required by a bound in `S`
   --> $DIR/type_wf.rs:6:13
    |
diff --git a/src/test/ui/coercion/coerce-issue-49593-box-never.nofallback.stderr b/src/test/ui/coercion/coerce-issue-49593-box-never.nofallback.stderr
index fbaa874792a01..a4b10a4c339f9 100644
--- a/src/test/ui/coercion/coerce-issue-49593-box-never.nofallback.stderr
+++ b/src/test/ui/coercion/coerce-issue-49593-box-never.nofallback.stderr
@@ -4,6 +4,16 @@ error[E0277]: the trait bound `(): std::error::Error` is not satisfied
 LL |     /* *mut $0 is coerced to Box<dyn Error> here */ Box::<_ /* ! */>::new(x)
    |                                                     ^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::error::Error` is not implemented for `()`
    |
+   = help: the following other types implement trait `std::error::Error`:
+             !
+             &'a T
+             AccessError
+             AddrParseError
+             Arc<T>
+             BorrowError
+             BorrowMutError
+             Box<T>
+           and 42 others
    = note: required for the cast to the object type `dyn std::error::Error`
 
 error[E0277]: the trait bound `(): std::error::Error` is not satisfied
@@ -12,6 +22,16 @@ error[E0277]: the trait bound `(): std::error::Error` is not satisfied
 LL |     /* *mut $0 is coerced to *mut Error here */ raw_ptr_box::<_ /* ! */>(x)
    |                                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::error::Error` is not implemented for `()`
    |
+   = help: the following other types implement trait `std::error::Error`:
+             !
+             &'a T
+             AccessError
+             AddrParseError
+             Arc<T>
+             BorrowError
+             BorrowMutError
+             Box<T>
+           and 42 others
    = note: required for the cast to the object type `(dyn std::error::Error + 'static)`
 
 error: aborting due to 2 previous errors
diff --git a/src/test/ui/const-generics/associated-type-bound-fail.stderr b/src/test/ui/const-generics/associated-type-bound-fail.stderr
index 9dc677ef6ed48..da2558229a758 100644
--- a/src/test/ui/const-generics/associated-type-bound-fail.stderr
+++ b/src/test/ui/const-generics/associated-type-bound-fail.stderr
@@ -4,8 +4,7 @@ error[E0277]: the trait bound `u16: Bar<N>` is not satisfied
 LL |     type Assoc = u16;
    |                  ^^^ the trait `Bar<N>` is not implemented for `u16`
    |
-   = help: the following implementations were found:
-             <u16 as Bar<3_usize>>
+   = help: the trait `Bar<3_usize>` is implemented for `u16`
 note: required by a bound in `Foo::Assoc`
   --> $DIR/associated-type-bound-fail.rs:4:17
    |
diff --git a/src/test/ui/const-generics/defaults/rp_impl_trait_fail.stderr b/src/test/ui/const-generics/defaults/rp_impl_trait_fail.stderr
index 81cfcb3560661..48c61c22e7a32 100644
--- a/src/test/ui/const-generics/defaults/rp_impl_trait_fail.stderr
+++ b/src/test/ui/const-generics/defaults/rp_impl_trait_fail.stderr
@@ -4,8 +4,7 @@ error[E0277]: the trait bound `Uwu<10_u32, 12_u32>: Trait` is not satisfied
 LL | fn rawr() -> impl Trait {
    |              ^^^^^^^^^^ the trait `Trait` is not implemented for `Uwu<10_u32, 12_u32>`
    |
-   = help: the following implementations were found:
-             <Uwu<N> as Trait>
+   = help: the trait `Trait` is implemented for `Uwu<N>`
 
 error[E0277]: the trait bound `Uwu<10_u32, 12_u32>: Trait` is not satisfied
   --> $DIR/rp_impl_trait_fail.rs:6:25
@@ -18,8 +17,7 @@ LL | |     Uwu::<10, 12>
 LL | | }
    | |_^ the trait `Trait` is not implemented for `Uwu<10_u32, 12_u32>`
    |
-   = help: the following implementations were found:
-             <Uwu<N> as Trait>
+   = help: the trait `Trait` is implemented for `Uwu<N>`
 
 error[E0277]: the trait bound `u32: Traitor<N, N>` is not satisfied
   --> $DIR/rp_impl_trait_fail.rs:18:26
@@ -27,7 +25,7 @@ error[E0277]: the trait bound `u32: Traitor<N, N>` is not satisfied
 LL | fn uwu<const N: u8>() -> impl Traitor<N> {
    |                          ^^^^^^^^^^^^^^^ the trait `Traitor<N, N>` is not implemented for `u32`
    |
-   = help: the following implementations were found:
+   = help: the following other types implement trait `Traitor<N, M>`:
              <u32 as Traitor<N, 2_u8>>
              <u64 as Traitor<1_u8, 2_u8>>
 
@@ -42,7 +40,7 @@ LL | |     1_u32
 LL | | }
    | |_^ the trait `Traitor<N, N>` is not implemented for `u32`
    |
-   = help: the following implementations were found:
+   = help: the following other types implement trait `Traitor<N, M>`:
              <u32 as Traitor<N, 2_u8>>
              <u64 as Traitor<1_u8, 2_u8>>
 
@@ -52,9 +50,9 @@ error[E0277]: the trait bound `u64: Traitor<1_u8, 1_u8>` is not satisfied
 LL | fn owo() -> impl Traitor {
    |             ^^^^^^^^^^^^ the trait `Traitor<1_u8, 1_u8>` is not implemented for `u64`
    |
-   = help: the following implementations were found:
-             <u64 as Traitor<1_u8, 2_u8>>
+   = help: the following other types implement trait `Traitor<N, M>`:
              <u32 as Traitor<N, 2_u8>>
+             <u64 as Traitor<1_u8, 2_u8>>
 
 error[E0277]: the trait bound `u64: Traitor<1_u8, 1_u8>` is not satisfied
   --> $DIR/rp_impl_trait_fail.rs:24:26
@@ -67,9 +65,9 @@ LL | |     1_u64
 LL | | }
    | |_^ the trait `Traitor<1_u8, 1_u8>` is not implemented for `u64`
    |
-   = help: the following implementations were found:
-             <u64 as Traitor<1_u8, 2_u8>>
+   = help: the following other types implement trait `Traitor<N, M>`:
              <u32 as Traitor<N, 2_u8>>
+             <u64 as Traitor<1_u8, 2_u8>>
 
 error: aborting due to 6 previous errors
 
diff --git a/src/test/ui/const-generics/defaults/trait_objects_fail.stderr b/src/test/ui/const-generics/defaults/trait_objects_fail.stderr
index 13d03c2d42d31..7f8a1f742d840 100644
--- a/src/test/ui/const-generics/defaults/trait_objects_fail.stderr
+++ b/src/test/ui/const-generics/defaults/trait_objects_fail.stderr
@@ -6,8 +6,7 @@ LL |     foo(&10_u32);
    |     |
    |     required by a bound introduced by this call
    |
-   = help: the following implementations were found:
-             <u32 as Trait<2_u8>>
+   = help: the trait `Trait<2_u8>` is implemented for `u32`
    = note: required for the cast to the object type `dyn Trait`
 
 error[E0277]: the trait bound `bool: Traitor<{_: u8}, {_: u8}>` is not satisfied
@@ -18,8 +17,7 @@ LL |     bar(&true);
    |     |
    |     required by a bound introduced by this call
    |
-   = help: the following implementations were found:
-             <bool as Traitor<2_u8, 3_u8>>
+   = help: the trait `Traitor<2_u8, 3_u8>` is implemented for `bool`
    = note: required for the cast to the object type `dyn Traitor<{_: u8}, {_: u8}>`
 
 error: aborting due to 2 previous errors
diff --git a/src/test/ui/const-generics/defaults/wfness.stderr b/src/test/ui/const-generics/defaults/wfness.stderr
index facf0ae19f734..8b405d6753e56 100644
--- a/src/test/ui/const-generics/defaults/wfness.stderr
+++ b/src/test/ui/const-generics/defaults/wfness.stderr
@@ -10,8 +10,7 @@ error[E0277]: the trait bound `(): Trait<2_u8>` is not satisfied
 LL | struct WhereClause<const N: u8 = 2> where (): Trait<N>;
    |                                               ^^^^^^^^ the trait `Trait<2_u8>` is not implemented for `()`
    |
-   = help: the following implementations were found:
-             <() as Trait<3_u8>>
+   = help: the trait `Trait<3_u8>` is implemented for `()`
 
 error[E0277]: the trait bound `(): Trait<1_u8>` is not satisfied
   --> $DIR/wfness.rs:14:13
@@ -19,8 +18,7 @@ error[E0277]: the trait bound `(): Trait<1_u8>` is not satisfied
 LL | fn foo() -> DependentDefaultWfness {
    |             ^^^^^^^^^^^^^^^^^^^^^^ the trait `Trait<1_u8>` is not implemented for `()`
    |
-   = help: the following implementations were found:
-             <() as Trait<3_u8>>
+   = help: the trait `Trait<3_u8>` is implemented for `()`
 note: required by a bound in `WhereClause`
   --> $DIR/wfness.rs:6:47
    |
diff --git a/src/test/ui/const-generics/exhaustive-value.stderr b/src/test/ui/const-generics/exhaustive-value.stderr
index 1a8f4abf52aa3..9c1b086f4da9a 100644
--- a/src/test/ui/const-generics/exhaustive-value.stderr
+++ b/src/test/ui/const-generics/exhaustive-value.stderr
@@ -4,12 +4,16 @@ error[E0277]: the trait bound `(): Foo<N>` is not satisfied
 LL |     <() as Foo<N>>::test()
    |     ^^^^^^^^^^^^^^^^^^^^ the trait `Foo<N>` is not implemented for `()`
    |
-   = help: the following implementations were found:
+   = help: the following other types implement trait `Foo<N>`:
              <() as Foo<0_u8>>
              <() as Foo<100_u8>>
              <() as Foo<101_u8>>
              <() as Foo<102_u8>>
-           and 252 others
+             <() as Foo<103_u8>>
+             <() as Foo<104_u8>>
+             <() as Foo<105_u8>>
+             <() as Foo<106_u8>>
+           and 248 others
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/const-generics/generic_const_exprs/issue-85848.stderr b/src/test/ui/const-generics/generic_const_exprs/issue-85848.stderr
index 4202cbae7eb29..27f5dce9fb266 100644
--- a/src/test/ui/const-generics/generic_const_exprs/issue-85848.stderr
+++ b/src/test/ui/const-generics/generic_const_exprs/issue-85848.stderr
@@ -6,6 +6,7 @@ LL |     writes_to_specific_path(&cap);
    |     |
    |     required by a bound introduced by this call
    |
+   = help: the trait `Delegates<U>` is implemented for `T`
 note: required because of the requirements on the impl of `Contains<(), true>` for `&C`
   --> $DIR/issue-85848.rs:21:12
    |
diff --git a/src/test/ui/const-generics/issues/issue-67185-2.stderr b/src/test/ui/const-generics/issues/issue-67185-2.stderr
index 89aa3d395e25b..c7be8e14a10d5 100644
--- a/src/test/ui/const-generics/issues/issue-67185-2.stderr
+++ b/src/test/ui/const-generics/issues/issue-67185-2.stderr
@@ -4,9 +4,9 @@ error[E0277]: the trait bound `[u16; 3]: Bar` is not satisfied
 LL |     <u8 as Baz>::Quaks: Bar,
    |     ^^^^^^^^^^^^^^^^^^^^^^^ the trait `Bar` is not implemented for `[u16; 3]`
    |
-   = help: the following implementations were found:
-             <[[u16; 3]; 3] as Bar>
-             <[u16; 4] as Bar>
+   = help: the following other types implement trait `Bar`:
+             [[u16; 3]; 3]
+             [u16; 4]
    = help: see issue #48214
    = help: add `#![feature(trivial_bounds)]` to the crate attributes to enable
 
@@ -16,9 +16,9 @@ error[E0277]: the trait bound `[[u16; 3]; 2]: Bar` is not satisfied
 LL |     [<u8 as Baz>::Quaks; 2]: Bar,
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Bar` is not implemented for `[[u16; 3]; 2]`
    |
-   = help: the following implementations were found:
-             <[[u16; 3]; 3] as Bar>
-             <[u16; 4] as Bar>
+   = help: the following other types implement trait `Bar`:
+             [[u16; 3]; 3]
+             [u16; 4]
    = help: see issue #48214
    = help: add `#![feature(trivial_bounds)]` to the crate attributes to enable
 
@@ -28,9 +28,9 @@ error[E0277]: the trait bound `[u16; 3]: Bar` is not satisfied
 LL | impl Foo for FooImpl {}
    |      ^^^ the trait `Bar` is not implemented for `[u16; 3]`
    |
-   = help: the following implementations were found:
-             <[[u16; 3]; 3] as Bar>
-             <[u16; 4] as Bar>
+   = help: the following other types implement trait `Bar`:
+             [[u16; 3]; 3]
+             [u16; 4]
 note: required by a bound in `Foo`
   --> $DIR/issue-67185-2.rs:15:25
    |
@@ -46,9 +46,9 @@ error[E0277]: the trait bound `[[u16; 3]; 2]: Bar` is not satisfied
 LL | impl Foo for FooImpl {}
    |      ^^^ the trait `Bar` is not implemented for `[[u16; 3]; 2]`
    |
-   = help: the following implementations were found:
-             <[[u16; 3]; 3] as Bar>
-             <[u16; 4] as Bar>
+   = help: the following other types implement trait `Bar`:
+             [[u16; 3]; 3]
+             [u16; 4]
 note: required by a bound in `Foo`
   --> $DIR/issue-67185-2.rs:14:30
    |
@@ -64,9 +64,9 @@ error[E0277]: the trait bound `[[u16; 3]; 2]: Bar` is not satisfied
 LL | fn f(_: impl Foo) {}
    |              ^^^ the trait `Bar` is not implemented for `[[u16; 3]; 2]`
    |
-   = help: the following implementations were found:
-             <[[u16; 3]; 3] as Bar>
-             <[u16; 4] as Bar>
+   = help: the following other types implement trait `Bar`:
+             [[u16; 3]; 3]
+             [u16; 4]
 note: required by a bound in `Foo`
   --> $DIR/issue-67185-2.rs:14:30
    |
@@ -82,9 +82,9 @@ error[E0277]: the trait bound `[u16; 3]: Bar` is not satisfied
 LL | fn f(_: impl Foo) {}
    |              ^^^ the trait `Bar` is not implemented for `[u16; 3]`
    |
-   = help: the following implementations were found:
-             <[[u16; 3]; 3] as Bar>
-             <[u16; 4] as Bar>
+   = help: the following other types implement trait `Bar`:
+             [[u16; 3]; 3]
+             [u16; 4]
 note: required by a bound in `Foo`
   --> $DIR/issue-67185-2.rs:15:25
    |
diff --git a/src/test/ui/const-generics/occurs-check/unused-substs-1.stderr b/src/test/ui/const-generics/occurs-check/unused-substs-1.stderr
index 486a298a9ffe5..8431d989278ba 100644
--- a/src/test/ui/const-generics/occurs-check/unused-substs-1.stderr
+++ b/src/test/ui/const-generics/occurs-check/unused-substs-1.stderr
@@ -4,8 +4,7 @@ error[E0277]: the trait bound `A<{_: usize}>: Bar<{_: usize}>` is not satisfied
 LL |     let _ = A;
    |             ^ the trait `Bar<{_: usize}>` is not implemented for `A<{_: usize}>`
    |
-   = help: the following implementations were found:
-             <A<7_usize> as Bar<N>>
+   = help: the trait `Bar<N>` is implemented for `A<7_usize>`
 note: required by a bound in `A`
   --> $DIR/unused-substs-1.rs:9:11
    |
diff --git a/src/test/ui/consts/const-blocks/fn-call-in-non-const.stderr b/src/test/ui/consts/const-blocks/fn-call-in-non-const.stderr
index 329c6cb2b1285..52a1669e33002 100644
--- a/src/test/ui/consts/const-blocks/fn-call-in-non-const.stderr
+++ b/src/test/ui/consts/const-blocks/fn-call-in-non-const.stderr
@@ -4,8 +4,7 @@ error[E0277]: the trait bound `Option<Bar>: Copy` is not satisfied
 LL |     let _: [Option<Bar>; 2] = [no_copy(); 2];
    |                               ^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `Option<Bar>`
    |
-   = help: the following implementations were found:
-             <Option<T> as Copy>
+   = help: the trait `Copy` is implemented for `Option<T>`
    = note: the `Copy` trait is required because the repeated element will be copied
    = help: consider creating a new `const` item and initializing it with the result of the function call to be used in the repeat position, like `const VAL: Type = const_fn();` and `let x = [VAL; 42];`
    = help: create an inline `const` block, see RFC #2920 <https://github.com/rust-lang/rfcs/pull/2920> for more information
diff --git a/src/test/ui/consts/const-blocks/migrate-fail.stderr b/src/test/ui/consts/const-blocks/migrate-fail.stderr
index 0fdbbc36288e9..318fec60290ee 100644
--- a/src/test/ui/consts/const-blocks/migrate-fail.stderr
+++ b/src/test/ui/consts/const-blocks/migrate-fail.stderr
@@ -4,8 +4,7 @@ error[E0277]: the trait bound `Option<Bar>: Copy` is not satisfied
 LL |         let arr: [Option<Bar>; 2] = [x; 2];
    |                                     ^^^^^^ the trait `Copy` is not implemented for `Option<Bar>`
    |
-   = help: the following implementations were found:
-             <Option<T> as Copy>
+   = help: the trait `Copy` is implemented for `Option<T>`
    = note: the `Copy` trait is required because the repeated element will be copied
 
 error[E0277]: the trait bound `Option<Bar>: Copy` is not satisfied
@@ -14,8 +13,7 @@ error[E0277]: the trait bound `Option<Bar>: Copy` is not satisfied
 LL |         let arr: [Option<Bar>; 2] = [x; 2];
    |                                     ^^^^^^ the trait `Copy` is not implemented for `Option<Bar>`
    |
-   = help: the following implementations were found:
-             <Option<T> as Copy>
+   = help: the trait `Copy` is implemented for `Option<T>`
    = note: the `Copy` trait is required because the repeated element will be copied
 
 error: aborting due to 2 previous errors
diff --git a/src/test/ui/consts/const-blocks/nll-fail.stderr b/src/test/ui/consts/const-blocks/nll-fail.stderr
index 8122085635977..5a34361aa83b6 100644
--- a/src/test/ui/consts/const-blocks/nll-fail.stderr
+++ b/src/test/ui/consts/const-blocks/nll-fail.stderr
@@ -4,8 +4,7 @@ error[E0277]: the trait bound `Option<Bar>: Copy` is not satisfied
 LL |         let arr: [Option<Bar>; 2] = [x; 2];
    |                                     ^^^^^^ the trait `Copy` is not implemented for `Option<Bar>`
    |
-   = help: the following implementations were found:
-             <Option<T> as Copy>
+   = help: the trait `Copy` is implemented for `Option<T>`
    = note: the `Copy` trait is required because the repeated element will be copied
 
 error[E0277]: the trait bound `Option<Bar>: Copy` is not satisfied
@@ -14,8 +13,7 @@ error[E0277]: the trait bound `Option<Bar>: Copy` is not satisfied
 LL |         let arr: [Option<Bar>; 2] = [x; 2];
    |                                     ^^^^^^ the trait `Copy` is not implemented for `Option<Bar>`
    |
-   = help: the following implementations were found:
-             <Option<T> as Copy>
+   = help: the trait `Copy` is implemented for `Option<T>`
    = note: the `Copy` trait is required because the repeated element will be copied
 
 error: aborting due to 2 previous errors
diff --git a/src/test/ui/consts/const-blocks/trait-error.stderr b/src/test/ui/consts/const-blocks/trait-error.stderr
index 26e2848e7f7a1..6979ff36176cd 100644
--- a/src/test/ui/consts/const-blocks/trait-error.stderr
+++ b/src/test/ui/consts/const-blocks/trait-error.stderr
@@ -4,8 +4,7 @@ error[E0277]: the trait bound `Foo<String>: Copy` is not satisfied
 LL |     [Foo(String::new()); 4];
    |     ^^^^^^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `Foo<String>`
    |
-   = help: the following implementations were found:
-             <Foo<T> as Copy>
+   = help: the trait `Copy` is implemented for `Foo<T>`
    = note: the `Copy` trait is required because the repeated element will be copied
 
 error: aborting due to previous error
diff --git a/src/test/ui/consts/const-eval/const-eval-overflow-3b.stderr b/src/test/ui/consts/const-eval/const-eval-overflow-3b.stderr
index 2b96b66819286..0e6be6d01ed69 100644
--- a/src/test/ui/consts/const-eval/const-eval-overflow-3b.stderr
+++ b/src/test/ui/consts/const-eval/const-eval-overflow-3b.stderr
@@ -11,6 +11,16 @@ LL |     = [0; (i8::MAX + 1u8) as usize];
    |                    ^ no implementation for `i8 + u8`
    |
    = help: the trait `Add<u8>` is not implemented for `i8`
+   = help: the following other types implement trait `Add<Rhs>`:
+             <&'a f32 as Add<f32>>
+             <&'a f64 as Add<f64>>
+             <&'a i128 as Add<i128>>
+             <&'a i16 as Add<i16>>
+             <&'a i32 as Add<i32>>
+             <&'a i64 as Add<i64>>
+             <&'a i8 as Add<i8>>
+             <&'a isize as Add<isize>>
+           and 48 others
 
 error: aborting due to 2 previous errors
 
diff --git a/src/test/ui/consts/const-eval/const-eval-overflow-4b.stderr b/src/test/ui/consts/const-eval/const-eval-overflow-4b.stderr
index f59ff329d18ac..4fa017e04e97b 100644
--- a/src/test/ui/consts/const-eval/const-eval-overflow-4b.stderr
+++ b/src/test/ui/consts/const-eval/const-eval-overflow-4b.stderr
@@ -11,6 +11,16 @@ LL |     : [u32; (i8::MAX as i8 + 1u8) as usize]
    |                            ^ no implementation for `i8 + u8`
    |
    = help: the trait `Add<u8>` is not implemented for `i8`
+   = help: the following other types implement trait `Add<Rhs>`:
+             <&'a f32 as Add<f32>>
+             <&'a f64 as Add<f64>>
+             <&'a i128 as Add<i128>>
+             <&'a i16 as Add<i16>>
+             <&'a i32 as Add<i32>>
+             <&'a i64 as Add<i64>>
+             <&'a i8 as Add<i8>>
+             <&'a isize as Add<isize>>
+           and 48 others
 
 error[E0604]: only `u8` can be cast as `char`, not `i8`
   --> $DIR/const-eval-overflow-4b.rs:22:13
diff --git a/src/test/ui/consts/too_generic_eval_ice.stderr b/src/test/ui/consts/too_generic_eval_ice.stderr
index ac104ed4a5a58..8de61fcfb7330 100644
--- a/src/test/ui/consts/too_generic_eval_ice.stderr
+++ b/src/test/ui/consts/too_generic_eval_ice.stderr
@@ -21,6 +21,16 @@ LL |         [5; Self::HOST_SIZE] == [6; 0]
    |                              ^^ no implementation for `[{integer}; _] == [{integer}; 0]`
    |
    = help: the trait `PartialEq<[{integer}; 0]>` is not implemented for `[{integer}; _]`
+   = help: the following other types implement trait `PartialEq<Rhs>`:
+             <&[B] as PartialEq<[A; N]>>
+             <&[T] as PartialEq<Vec<U, A>>>
+             <&mut [B] as PartialEq<[A; N]>>
+             <&mut [T] as PartialEq<Vec<U, A>>>
+             <[A; N] as PartialEq<&[B]>>
+             <[A; N] as PartialEq<&mut [B]>>
+             <[A; N] as PartialEq<[B; N]>>
+             <[A; N] as PartialEq<[B]>>
+           and 3 others
 
 error: aborting due to 3 previous errors
 
diff --git a/src/test/ui/did_you_mean/issue-21659-show-relevant-trait-impls-1.stderr b/src/test/ui/did_you_mean/issue-21659-show-relevant-trait-impls-1.stderr
index 8aedb4229e6d3..26764bc0ee5cc 100644
--- a/src/test/ui/did_you_mean/issue-21659-show-relevant-trait-impls-1.stderr
+++ b/src/test/ui/did_you_mean/issue-21659-show-relevant-trait-impls-1.stderr
@@ -4,7 +4,7 @@ error[E0277]: the trait bound `Bar: Foo<usize>` is not satisfied
 LL |     f1.foo(1usize);
    |        ^^^ the trait `Foo<usize>` is not implemented for `Bar`
    |
-   = help: the following implementations were found:
+   = help: the following other types implement trait `Foo<A>`:
              <Bar as Foo<i32>>
              <Bar as Foo<u8>>
 
diff --git a/src/test/ui/did_you_mean/issue-21659-show-relevant-trait-impls-2.stderr b/src/test/ui/did_you_mean/issue-21659-show-relevant-trait-impls-2.stderr
index d6d5ce4d1a7d2..bb175367e1f9e 100644
--- a/src/test/ui/did_you_mean/issue-21659-show-relevant-trait-impls-2.stderr
+++ b/src/test/ui/did_you_mean/issue-21659-show-relevant-trait-impls-2.stderr
@@ -4,12 +4,13 @@ error[E0277]: the trait bound `Bar: Foo<usize>` is not satisfied
 LL |     f1.foo(1usize);
    |        ^^^ the trait `Foo<usize>` is not implemented for `Bar`
    |
-   = help: the following implementations were found:
+   = help: the following other types implement trait `Foo<A>`:
              <Bar as Foo<i16>>
              <Bar as Foo<i32>>
              <Bar as Foo<i8>>
              <Bar as Foo<u16>>
-           and 2 others
+             <Bar as Foo<u32>>
+             <Bar as Foo<u8>>
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/did_you_mean/issue-39802-show-5-trait-impls.stderr b/src/test/ui/did_you_mean/issue-39802-show-5-trait-impls.stderr
index dff980301911c..d27b05fe7f7df 100644
--- a/src/test/ui/did_you_mean/issue-39802-show-5-trait-impls.stderr
+++ b/src/test/ui/did_you_mean/issue-39802-show-5-trait-impls.stderr
@@ -6,12 +6,16 @@ LL |     Foo::<i32>::bar(&1i8);
    |     |
    |     required by a bound introduced by this call
    |
-   = help: the following implementations were found:
+   = help: the following other types implement trait `Foo<B>`:
              <i8 as Foo<bool>>
              <i8 as Foo<u16>>
              <i8 as Foo<u32>>
              <i8 as Foo<u64>>
-           and 5 others
+             <i8 as Foo<u8>>
+             <u8 as Foo<bool>>
+             <u8 as Foo<u16>>
+             <u8 as Foo<u32>>
+             <u8 as Foo<u64>>
 
 error[E0277]: the trait bound `u8: Foo<i32>` is not satisfied
   --> $DIR/issue-39802-show-5-trait-impls.rs:25:21
@@ -21,12 +25,16 @@ LL |     Foo::<i32>::bar(&1u8);
    |     |
    |     required by a bound introduced by this call
    |
-   = help: the following implementations were found:
+   = help: the following other types implement trait `Foo<B>`:
+             <i8 as Foo<bool>>
+             <i8 as Foo<u16>>
+             <i8 as Foo<u32>>
+             <i8 as Foo<u64>>
+             <i8 as Foo<u8>>
              <u8 as Foo<bool>>
              <u8 as Foo<u16>>
              <u8 as Foo<u32>>
              <u8 as Foo<u64>>
-           and 5 others
 
 error[E0277]: the trait bound `bool: Foo<i32>` is not satisfied
   --> $DIR/issue-39802-show-5-trait-impls.rs:26:21
@@ -36,12 +44,13 @@ LL |     Foo::<i32>::bar(&true);
    |     |
    |     required by a bound introduced by this call
    |
-   = help: the following implementations were found:
+   = help: the following other types implement trait `Foo<B>`:
              <bool as Foo<bool>>
              <bool as Foo<i8>>
              <bool as Foo<u16>>
              <bool as Foo<u32>>
-           and 2 others
+             <bool as Foo<u64>>
+             <bool as Foo<u8>>
 
 error: aborting due to 3 previous errors
 
diff --git a/src/test/ui/did_you_mean/issue-49746-unicode-confusable-in-float-literal-expt.stderr b/src/test/ui/did_you_mean/issue-49746-unicode-confusable-in-float-literal-expt.stderr
index 83fc37e7e537f..26986684f0c0a 100644
--- a/src/test/ui/did_you_mean/issue-49746-unicode-confusable-in-float-literal-expt.stderr
+++ b/src/test/ui/did_you_mean/issue-49746-unicode-confusable-in-float-literal-expt.stderr
@@ -22,6 +22,16 @@ LL | const UNIVERSAL_GRAVITATIONAL_CONSTANT: f64 = 6.674e−11; // m³⋅kg⁻¹
    |                                                     ^ no implementation for `{float} - {integer}`
    |
    = help: the trait `Sub<{integer}>` is not implemented for `{float}`
+   = help: the following other types implement trait `Sub<Rhs>`:
+             <&'a f32 as Sub<f32>>
+             <&'a f64 as Sub<f64>>
+             <&'a i128 as Sub<i128>>
+             <&'a i16 as Sub<i16>>
+             <&'a i32 as Sub<i32>>
+             <&'a i64 as Sub<i64>>
+             <&'a i8 as Sub<i8>>
+             <&'a isize as Sub<isize>>
+           and 48 others
 
 error: aborting due to 3 previous errors
 
diff --git a/src/test/ui/feature-gates/feature-gate-trivial_bounds.stderr b/src/test/ui/feature-gates/feature-gate-trivial_bounds.stderr
index 28c49c33bf6b5..1b87ebd9f20ce 100644
--- a/src/test/ui/feature-gates/feature-gate-trivial_bounds.stderr
+++ b/src/test/ui/feature-gates/feature-gate-trivial_bounds.stderr
@@ -4,6 +4,7 @@ error[E0277]: the trait bound `i32: Foo` is not satisfied
 LL | enum E where i32: Foo { V }
    |              ^^^^^^^^ the trait `Foo` is not implemented for `i32`
    |
+   = help: the trait `Foo` is implemented for `()`
    = help: see issue #48214
    = help: add `#![feature(trivial_bounds)]` to the crate attributes to enable
 
@@ -13,6 +14,7 @@ error[E0277]: the trait bound `i32: Foo` is not satisfied
 LL | struct S where i32: Foo;
    |                ^^^^^^^^ the trait `Foo` is not implemented for `i32`
    |
+   = help: the trait `Foo` is implemented for `()`
    = help: see issue #48214
    = help: add `#![feature(trivial_bounds)]` to the crate attributes to enable
 
@@ -22,6 +24,7 @@ error[E0277]: the trait bound `i32: Foo` is not satisfied
 LL | trait T where i32: Foo {}
    |               ^^^^^^^^ the trait `Foo` is not implemented for `i32`
    |
+   = help: the trait `Foo` is implemented for `()`
    = help: see issue #48214
    = help: add `#![feature(trivial_bounds)]` to the crate attributes to enable
 
@@ -31,6 +34,7 @@ error[E0277]: the trait bound `i32: Foo` is not satisfied
 LL | union U where i32: Foo { f: i32 }
    |               ^^^^^^^^ the trait `Foo` is not implemented for `i32`
    |
+   = help: the trait `Foo` is implemented for `()`
    = help: see issue #48214
    = help: add `#![feature(trivial_bounds)]` to the crate attributes to enable
 
@@ -40,6 +44,7 @@ error[E0277]: the trait bound `i32: Foo` is not satisfied
 LL | impl Foo for () where i32: Foo {
    |                       ^^^^^^^^ the trait `Foo` is not implemented for `i32`
    |
+   = help: the trait `Foo` is implemented for `()`
    = help: see issue #48214
    = help: add `#![feature(trivial_bounds)]` to the crate attributes to enable
 
@@ -49,6 +54,7 @@ error[E0277]: the trait bound `i32: Foo` is not satisfied
 LL | fn f() where i32: Foo
    |              ^^^^^^^^ the trait `Foo` is not implemented for `i32`
    |
+   = help: the trait `Foo` is implemented for `()`
    = help: see issue #48214
    = help: add `#![feature(trivial_bounds)]` to the crate attributes to enable
 
diff --git a/src/test/ui/fmt/ifmt-unimpl.stderr b/src/test/ui/fmt/ifmt-unimpl.stderr
index 6e8dd79219106..5204afa4e6bc5 100644
--- a/src/test/ui/fmt/ifmt-unimpl.stderr
+++ b/src/test/ui/fmt/ifmt-unimpl.stderr
@@ -4,6 +4,16 @@ error[E0277]: the trait bound `str: UpperHex` is not satisfied
 LL |     format!("{:X}", "3");
    |                     ^^^ the trait `UpperHex` is not implemented for `str`
    |
+   = help: the following other types implement trait `UpperHex`:
+             &T
+             &mut T
+             NonZeroI128
+             NonZeroI16
+             NonZeroI32
+             NonZeroI64
+             NonZeroI8
+             NonZeroIsize
+           and 21 others
    = note: required because of the requirements on the impl of `UpperHex` for `&str`
 note: required by a bound in `ArgumentV1::<'a>::new_upper_hex`
   --> $SRC_DIR/core/src/fmt/mod.rs:LL:COL
diff --git a/src/test/ui/generic-associated-types/bugs/issue-88460.stderr b/src/test/ui/generic-associated-types/bugs/issue-88460.stderr
index 0b83e9da1ab7a..98c304cc90b53 100644
--- a/src/test/ui/generic-associated-types/bugs/issue-88460.stderr
+++ b/src/test/ui/generic-associated-types/bugs/issue-88460.stderr
@@ -4,6 +4,7 @@ error[E0277]: the trait bound `for<'a> <_ as Trait>::Assoc<'a>: Marker` is not s
 LL |     test(Foo);
    |     ^^^^ the trait `for<'a> Marker` is not implemented for `<_ as Trait>::Assoc<'a>`
    |
+   = help: the trait `Marker` is implemented for `()`
 note: required by a bound in `test`
   --> $DIR/issue-88460.rs:17:27
    |
diff --git a/src/test/ui/impl-trait/cross-return-site-inference.stderr b/src/test/ui/impl-trait/cross-return-site-inference.stderr
index 06afb938c5fad..d458c7be783d5 100644
--- a/src/test/ui/impl-trait/cross-return-site-inference.stderr
+++ b/src/test/ui/impl-trait/cross-return-site-inference.stderr
@@ -7,6 +7,7 @@ LL |     Err("whoops")?;
    |                  ^ the trait `From<&str>` is not implemented for `impl Debug`
    |
    = note: the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait
+   = help: the trait `FromResidual<Result<Infallible, E>>` is implemented for `Result<T, F>`
    = note: required because of the requirements on the impl of `FromResidual<Result<Infallible, &str>>` for `Result<(), impl Debug>`
 
 error[E0277]: the trait bound `impl Debug: From<&str>` is not satisfied
diff --git a/src/test/ui/impl-trait/dyn-trait-return-should-be-impl-trait.stderr b/src/test/ui/impl-trait/dyn-trait-return-should-be-impl-trait.stderr
index 0d4f82bfc153f..f90399b6b9458 100644
--- a/src/test/ui/impl-trait/dyn-trait-return-should-be-impl-trait.stderr
+++ b/src/test/ui/impl-trait/dyn-trait-return-should-be-impl-trait.stderr
@@ -81,7 +81,7 @@ LL | fn bak() -> impl Trait { unimplemented!() }
 help: use a boxed trait object if all return paths implement trait `Trait`
    |
 LL | fn bak() -> Box<dyn Trait> { unimplemented!() }
-   |             ~~~~~~~~~~~~~~
+   |             ++++         +
 
 error[E0746]: return type cannot have an unboxed trait object
   --> $DIR/dyn-trait-return-should-be-impl-trait.rs:19:13
@@ -95,12 +95,16 @@ LL | fn bal() -> dyn Trait {
    = note: you can create a new `enum` with a variant for each returned type
 help: return a boxed trait object instead
    |
-LL ~ fn bal() -> Box<dyn Trait> {
-LL |     if true {
-LL ~         return Box::new(Struct);
-LL |     }
-LL ~     Box::new(42)
+LL | fn bal() -> Box<dyn Trait> {
+   |             ++++         +
+help: ... and box this value
+   |
+LL |         return Box::new(Struct);
+   |                +++++++++      +
+help: ... and box this value
    |
+LL |     Box::new(42)
+   |     +++++++++  +
 
 error[E0308]: `if` and `else` have incompatible types
   --> $DIR/dyn-trait-return-should-be-impl-trait.rs:29:9
@@ -126,12 +130,16 @@ LL | fn bax() -> dyn Trait {
    = note: you can create a new `enum` with a variant for each returned type
 help: return a boxed trait object instead
    |
-LL ~ fn bax() -> Box<dyn Trait> {
-LL |     if true {
-LL ~         Box::new(Struct)
-LL |     } else {
-LL ~         Box::new(42)
+LL | fn bax() -> Box<dyn Trait> {
+   |             ++++         +
+help: ... and box this value
+   |
+LL |         Box::new(Struct)
+   |         +++++++++      +
+help: ... and box this value
    |
+LL |         Box::new(42)
+   |         +++++++++  +
 
 error[E0308]: mismatched types
   --> $DIR/dyn-trait-return-should-be-impl-trait.rs:34:16
diff --git a/src/test/ui/impl-trait/equality.stderr b/src/test/ui/impl-trait/equality.stderr
index 804ccbcc6c0ab..f14b447b07730 100644
--- a/src/test/ui/impl-trait/equality.stderr
+++ b/src/test/ui/impl-trait/equality.stderr
@@ -24,6 +24,16 @@ LL |         n + sum_to(n - 1)
    |           ^ no implementation for `u32 + impl Foo`
    |
    = help: the trait `Add<impl Foo>` is not implemented for `u32`
+   = help: the following other types implement trait `Add<Rhs>`:
+             <&'a f32 as Add<f32>>
+             <&'a f64 as Add<f64>>
+             <&'a i128 as Add<i128>>
+             <&'a i16 as Add<i16>>
+             <&'a i32 as Add<i32>>
+             <&'a i64 as Add<i64>>
+             <&'a i8 as Add<i8>>
+             <&'a isize as Add<isize>>
+           and 48 others
 
 error: aborting due to 2 previous errors; 1 warning emitted
 
diff --git a/src/test/ui/impl-trait/issues/issue-62742.stderr b/src/test/ui/impl-trait/issues/issue-62742.stderr
index 28068b7548ce5..70d693b8bee95 100644
--- a/src/test/ui/impl-trait/issues/issue-62742.stderr
+++ b/src/test/ui/impl-trait/issues/issue-62742.stderr
@@ -4,8 +4,7 @@ error[E0277]: the trait bound `RawImpl<_>: Raw<_>` is not satisfied
 LL |     WrongImpl::foo(0i32);
    |     ^^^^^^^^^ the trait `Raw<_>` is not implemented for `RawImpl<_>`
    |
-   = help: the following implementations were found:
-             <RawImpl<T> as Raw<[T]>>
+   = help: the trait `Raw<[T]>` is implemented for `RawImpl<T>`
 note: required by a bound in `SafeImpl`
   --> $DIR/issue-62742.rs:26:35
    |
@@ -40,8 +39,7 @@ error[E0277]: the trait bound `RawImpl<()>: Raw<()>` is not satisfied
 LL |     WrongImpl::<()>::foo(0i32);
    |     ^^^^^^^^^^^^^^^ the trait `Raw<()>` is not implemented for `RawImpl<()>`
    |
-   = help: the following implementations were found:
-             <RawImpl<T> as Raw<[T]>>
+   = help: the trait `Raw<[T]>` is implemented for `RawImpl<T>`
 note: required by a bound in `SafeImpl`
   --> $DIR/issue-62742.rs:26:35
    |
diff --git a/src/test/ui/impl-trait/nested-return-type2-tait.stderr b/src/test/ui/impl-trait/nested-return-type2-tait.stderr
index 776c06b20dfc2..81a75e39c9177 100644
--- a/src/test/ui/impl-trait/nested-return-type2-tait.stderr
+++ b/src/test/ui/impl-trait/nested-return-type2-tait.stderr
@@ -4,6 +4,7 @@ error[E0277]: the trait bound `Sendable: Duh` is not satisfied
 LL | fn foo() -> impl Trait<Assoc = Sendable> {
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Duh` is not implemented for `Sendable`
    |
+   = help: the trait `Duh` is implemented for `i32`
 note: required because of the requirements on the impl of `Trait` for `[closure@$DIR/nested-return-type2-tait.rs:28:5: 28:10]`
   --> $DIR/nested-return-type2-tait.rs:14:31
    |
@@ -21,6 +22,7 @@ LL | |     || 42
 LL | | }
    | |_^ the trait `Duh` is not implemented for `Sendable`
    |
+   = help: the trait `Duh` is implemented for `i32`
 note: required because of the requirements on the impl of `Trait` for `[closure@$DIR/nested-return-type2-tait.rs:28:5: 28:10]`
   --> $DIR/nested-return-type2-tait.rs:14:31
    |
diff --git a/src/test/ui/impl-trait/nested-return-type2-tait2.stderr b/src/test/ui/impl-trait/nested-return-type2-tait2.stderr
index 4993202e2532e..42e65e692488d 100644
--- a/src/test/ui/impl-trait/nested-return-type2-tait2.stderr
+++ b/src/test/ui/impl-trait/nested-return-type2-tait2.stderr
@@ -4,6 +4,7 @@ error[E0277]: the trait bound `Sendable: Duh` is not satisfied
 LL |     || 42
    |     ^^^^^ the trait `Duh` is not implemented for `Sendable`
    |
+   = help: the trait `Duh` is implemented for `i32`
 note: required because of the requirements on the impl of `Trait` for `[closure@$DIR/nested-return-type2-tait2.rs:27:5: 27:10]`
   --> $DIR/nested-return-type2-tait2.rs:14:31
    |
diff --git a/src/test/ui/impl-trait/nested-return-type2-tait3.stderr b/src/test/ui/impl-trait/nested-return-type2-tait3.stderr
index efeaf059a3bb2..4d3691d0e07fa 100644
--- a/src/test/ui/impl-trait/nested-return-type2-tait3.stderr
+++ b/src/test/ui/impl-trait/nested-return-type2-tait3.stderr
@@ -4,6 +4,7 @@ error[E0277]: the trait bound `impl Send: Duh` is not satisfied
 LL |     || 42
    |     ^^^^^ the trait `Duh` is not implemented for `impl Send`
    |
+   = help: the trait `Duh` is implemented for `i32`
 note: required because of the requirements on the impl of `Trait` for `[closure@$DIR/nested-return-type2-tait3.rs:26:5: 26:10]`
   --> $DIR/nested-return-type2-tait3.rs:14:31
    |
diff --git a/src/test/ui/impl-trait/nested_impl_trait.stderr b/src/test/ui/impl-trait/nested_impl_trait.stderr
index 26b48c7cdf718..bb4ae5e828254 100644
--- a/src/test/ui/impl-trait/nested_impl_trait.stderr
+++ b/src/test/ui/impl-trait/nested_impl_trait.stderr
@@ -52,6 +52,7 @@ error[E0277]: the trait bound `impl Debug: From<impl Into<u32>>` is not satisfie
 LL | fn bad_in_ret_position(x: impl Into<u32>) -> impl Into<impl Debug> { x }
    |                                              ^^^^^^^^^^^^^^^^^^^^^ the trait `From<impl Into<u32>>` is not implemented for `impl Debug`
    |
+   = help: the trait `Into<U>` is implemented for `T`
    = note: required because of the requirements on the impl of `Into<impl Debug>` for `impl Into<u32>`
 
 error[E0277]: the trait bound `impl Debug: From<impl Into<u32>>` is not satisfied
@@ -60,6 +61,7 @@ error[E0277]: the trait bound `impl Debug: From<impl Into<u32>>` is not satisfie
 LL |     fn bad(x: impl Into<u32>) -> impl Into<impl Debug> { x }
    |                                  ^^^^^^^^^^^^^^^^^^^^^ the trait `From<impl Into<u32>>` is not implemented for `impl Debug`
    |
+   = help: the trait `Into<U>` is implemented for `T`
    = note: required because of the requirements on the impl of `Into<impl Debug>` for `impl Into<u32>`
 
 error: aborting due to 8 previous errors
diff --git a/src/test/ui/impl-trait/point-to-type-err-cause-on-impl-trait-return.stderr b/src/test/ui/impl-trait/point-to-type-err-cause-on-impl-trait-return.stderr
index 0c595f441ba8e..10510c1754eda 100644
--- a/src/test/ui/impl-trait/point-to-type-err-cause-on-impl-trait-return.stderr
+++ b/src/test/ui/impl-trait/point-to-type-err-cause-on-impl-trait-return.stderr
@@ -103,13 +103,16 @@ LL | fn hat() -> dyn std::fmt::Display {
    = note: you can create a new `enum` with a variant for each returned type
 help: return a boxed trait object instead
    |
-LL ~ fn hat() -> Box<dyn std::fmt::Display> {
-LL |     match 13 {
-LL |         0 => {
-LL ~             return Box::new(0i32);
-LL |         }
-LL |         _ => {
- ...
+LL | fn hat() -> Box<dyn std::fmt::Display> {
+   |             ++++                     +
+help: ... and box this value
+   |
+LL |             return Box::new(0i32);
+   |                    +++++++++    +
+help: ... and box this value
+   |
+LL |             Box::new(1u32)
+   |             +++++++++    +
 
 error[E0308]: `match` arms have incompatible types
   --> $DIR/point-to-type-err-cause-on-impl-trait-return.rs:80:14
@@ -135,12 +138,20 @@ LL | fn pug() -> dyn std::fmt::Display {
    = note: you can create a new `enum` with a variant for each returned type
 help: return a boxed trait object instead
    |
-LL ~ fn pug() -> Box<dyn std::fmt::Display> {
-LL |     match 13 {
-LL ~         0 => Box::new(0i32),
-LL ~         1 => Box::new(1u32),
-LL ~         _ => Box::new(2u32),
+LL | fn pug() -> Box<dyn std::fmt::Display> {
+   |             ++++                     +
+help: ... and box this value
+   |
+LL |         0 => Box::new(0i32),
+   |              +++++++++    +
+help: ... and box this value
    |
+LL |         1 => Box::new(1u32),
+   |              +++++++++    +
+help: ... and box this value
+   |
+LL |         _ => Box::new(2u32),
+   |              +++++++++    +
 
 error[E0308]: `if` and `else` have incompatible types
   --> $DIR/point-to-type-err-cause-on-impl-trait-return.rs:89:9
@@ -166,12 +177,16 @@ LL | fn man() -> dyn std::fmt::Display {
    = note: you can create a new `enum` with a variant for each returned type
 help: return a boxed trait object instead
    |
-LL ~ fn man() -> Box<dyn std::fmt::Display> {
-LL |     if false {
-LL ~         Box::new(0i32)
-LL |     } else {
-LL ~         Box::new(1u32)
+LL | fn man() -> Box<dyn std::fmt::Display> {
+   |             ++++                     +
+help: ... and box this value
+   |
+LL |         Box::new(0i32)
+   |         +++++++++    +
+help: ... and box this value
    |
+LL |         Box::new(1u32)
+   |         +++++++++    +
 
 error: aborting due to 14 previous errors
 
diff --git a/src/test/ui/impl-trait/recursive-type-alias-impl-trait-declaration-too-subtle-2.stderr b/src/test/ui/impl-trait/recursive-type-alias-impl-trait-declaration-too-subtle-2.stderr
index 5476bf9d27745..6cd63db44fa07 100644
--- a/src/test/ui/impl-trait/recursive-type-alias-impl-trait-declaration-too-subtle-2.stderr
+++ b/src/test/ui/impl-trait/recursive-type-alias-impl-trait-declaration-too-subtle-2.stderr
@@ -5,6 +5,7 @@ LL |     Bar
    |     ^^^ no implementation for `Bar == (Bar, i32)`
    |
    = help: the trait `PartialEq<(Bar, i32)>` is not implemented for `Bar`
+   = help: the trait `PartialEq<(Foo, i32)>` is implemented for `Bar`
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/index-help.stderr b/src/test/ui/index-help.stderr
index 78a8f439a71cc..7f51a48111b78 100644
--- a/src/test/ui/index-help.stderr
+++ b/src/test/ui/index-help.stderr
@@ -5,6 +5,7 @@ LL |     x[0i32];
    |     ^^^^^^^ slice indices are of type `usize` or ranges of `usize`
    |
    = help: the trait `SliceIndex<[{integer}]>` is not implemented for `i32`
+   = help: the trait `SliceIndex<[T]>` is implemented for `usize`
    = note: required because of the requirements on the impl of `Index<i32>` for `Vec<{integer}>`
 
 error: aborting due to previous error
diff --git a/src/test/ui/indexing-requires-a-uint.stderr b/src/test/ui/indexing-requires-a-uint.stderr
index 24f42f22f37df..dae11a702fb73 100644
--- a/src/test/ui/indexing-requires-a-uint.stderr
+++ b/src/test/ui/indexing-requires-a-uint.stderr
@@ -5,6 +5,7 @@ LL |     [0][0u8];
    |     ^^^^^^^^ slice indices are of type `usize` or ranges of `usize`
    |
    = help: the trait `SliceIndex<[{integer}]>` is not implemented for `u8`
+   = help: the trait `SliceIndex<[T]>` is implemented for `usize`
    = note: required because of the requirements on the impl of `Index<u8>` for `[{integer}]`
 
 error[E0308]: mismatched types
diff --git a/src/test/ui/integral-indexing.stderr b/src/test/ui/integral-indexing.stderr
index fcd79d19aac6e..be3398552dc0a 100644
--- a/src/test/ui/integral-indexing.stderr
+++ b/src/test/ui/integral-indexing.stderr
@@ -5,6 +5,7 @@ LL |     v[3u8];
    |     ^^^^^^ slice indices are of type `usize` or ranges of `usize`
    |
    = help: the trait `SliceIndex<[isize]>` is not implemented for `u8`
+   = help: the trait `SliceIndex<[T]>` is implemented for `usize`
    = note: required because of the requirements on the impl of `Index<u8>` for `Vec<isize>`
 
 error[E0277]: the type `[isize]` cannot be indexed by `i8`
@@ -14,6 +15,7 @@ LL |     v[3i8];
    |     ^^^^^^ slice indices are of type `usize` or ranges of `usize`
    |
    = help: the trait `SliceIndex<[isize]>` is not implemented for `i8`
+   = help: the trait `SliceIndex<[T]>` is implemented for `usize`
    = note: required because of the requirements on the impl of `Index<i8>` for `Vec<isize>`
 
 error[E0277]: the type `[isize]` cannot be indexed by `u32`
@@ -23,6 +25,7 @@ LL |     v[3u32];
    |     ^^^^^^^ slice indices are of type `usize` or ranges of `usize`
    |
    = help: the trait `SliceIndex<[isize]>` is not implemented for `u32`
+   = help: the trait `SliceIndex<[T]>` is implemented for `usize`
    = note: required because of the requirements on the impl of `Index<u32>` for `Vec<isize>`
 
 error[E0277]: the type `[isize]` cannot be indexed by `i32`
@@ -32,6 +35,7 @@ LL |     v[3i32];
    |     ^^^^^^^ slice indices are of type `usize` or ranges of `usize`
    |
    = help: the trait `SliceIndex<[isize]>` is not implemented for `i32`
+   = help: the trait `SliceIndex<[T]>` is implemented for `usize`
    = note: required because of the requirements on the impl of `Index<i32>` for `Vec<isize>`
 
 error[E0277]: the type `[u8]` cannot be indexed by `u8`
@@ -41,6 +45,7 @@ LL |     s.as_bytes()[3u8];
    |     ^^^^^^^^^^^^^^^^^ slice indices are of type `usize` or ranges of `usize`
    |
    = help: the trait `SliceIndex<[u8]>` is not implemented for `u8`
+   = help: the trait `SliceIndex<[T]>` is implemented for `usize`
    = note: required because of the requirements on the impl of `Index<u8>` for `[u8]`
 
 error[E0277]: the type `[u8]` cannot be indexed by `i8`
@@ -50,6 +55,7 @@ LL |     s.as_bytes()[3i8];
    |     ^^^^^^^^^^^^^^^^^ slice indices are of type `usize` or ranges of `usize`
    |
    = help: the trait `SliceIndex<[u8]>` is not implemented for `i8`
+   = help: the trait `SliceIndex<[T]>` is implemented for `usize`
    = note: required because of the requirements on the impl of `Index<i8>` for `[u8]`
 
 error[E0277]: the type `[u8]` cannot be indexed by `u32`
@@ -59,6 +65,7 @@ LL |     s.as_bytes()[3u32];
    |     ^^^^^^^^^^^^^^^^^^ slice indices are of type `usize` or ranges of `usize`
    |
    = help: the trait `SliceIndex<[u8]>` is not implemented for `u32`
+   = help: the trait `SliceIndex<[T]>` is implemented for `usize`
    = note: required because of the requirements on the impl of `Index<u32>` for `[u8]`
 
 error[E0277]: the type `[u8]` cannot be indexed by `i32`
@@ -68,6 +75,7 @@ LL |     s.as_bytes()[3i32];
    |     ^^^^^^^^^^^^^^^^^^ slice indices are of type `usize` or ranges of `usize`
    |
    = help: the trait `SliceIndex<[u8]>` is not implemented for `i32`
+   = help: the trait `SliceIndex<[T]>` is implemented for `usize`
    = note: required because of the requirements on the impl of `Index<i32>` for `[u8]`
 
 error: aborting due to 8 previous errors
diff --git a/src/test/ui/issues/issue-11771.stderr b/src/test/ui/issues/issue-11771.stderr
index 9f250925e5080..161fce4b0315c 100644
--- a/src/test/ui/issues/issue-11771.stderr
+++ b/src/test/ui/issues/issue-11771.stderr
@@ -5,6 +5,16 @@ LL |     1 +
    |       ^ no implementation for `{integer} + ()`
    |
    = help: the trait `Add<()>` is not implemented for `{integer}`
+   = help: the following other types implement trait `Add<Rhs>`:
+             <&'a f32 as Add<f32>>
+             <&'a f64 as Add<f64>>
+             <&'a i128 as Add<i128>>
+             <&'a i16 as Add<i16>>
+             <&'a i32 as Add<i32>>
+             <&'a i64 as Add<i64>>
+             <&'a i8 as Add<i8>>
+             <&'a isize as Add<isize>>
+           and 48 others
 
 error[E0277]: cannot add `()` to `{integer}`
   --> $DIR/issue-11771.rs:8:7
@@ -13,6 +23,16 @@ LL |     1 +
    |       ^ no implementation for `{integer} + ()`
    |
    = help: the trait `Add<()>` is not implemented for `{integer}`
+   = help: the following other types implement trait `Add<Rhs>`:
+             <&'a f32 as Add<f32>>
+             <&'a f64 as Add<f64>>
+             <&'a i128 as Add<i128>>
+             <&'a i16 as Add<i16>>
+             <&'a i32 as Add<i32>>
+             <&'a i64 as Add<i64>>
+             <&'a i8 as Add<i8>>
+             <&'a isize as Add<isize>>
+           and 48 others
 
 error: aborting due to 2 previous errors
 
diff --git a/src/test/ui/issues/issue-18107.stderr b/src/test/ui/issues/issue-18107.stderr
index 1eb6822b8a11a..28478457b296d 100644
--- a/src/test/ui/issues/issue-18107.stderr
+++ b/src/test/ui/issues/issue-18107.stderr
@@ -15,7 +15,7 @@ LL |     impl AbstractRenderer
 help: use a boxed trait object if all return paths implement trait `AbstractRenderer`
    |
 LL |     Box<dyn AbstractRenderer>
-   |
+   |     ++++                    +
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/issues/issue-24352.stderr b/src/test/ui/issues/issue-24352.stderr
index 69731bfe7ee7d..118f37f6971ef 100644
--- a/src/test/ui/issues/issue-24352.stderr
+++ b/src/test/ui/issues/issue-24352.stderr
@@ -5,6 +5,16 @@ LL |     1.0f64 - 1
    |            ^ no implementation for `f64 - {integer}`
    |
    = help: the trait `Sub<{integer}>` is not implemented for `f64`
+   = help: the following other types implement trait `Sub<Rhs>`:
+             <&'a f32 as Sub<f32>>
+             <&'a f64 as Sub<f64>>
+             <&'a i128 as Sub<i128>>
+             <&'a i16 as Sub<i16>>
+             <&'a i32 as Sub<i32>>
+             <&'a i64 as Sub<i64>>
+             <&'a i8 as Sub<i8>>
+             <&'a isize as Sub<isize>>
+           and 48 others
 help: consider using a floating-point literal by writing it with `.0`
    |
 LL |     1.0f64 - 1.0
diff --git a/src/test/ui/issues/issue-32709.stderr b/src/test/ui/issues/issue-32709.stderr
index b4c3f148e32b5..ed5addcbec517 100644
--- a/src/test/ui/issues/issue-32709.stderr
+++ b/src/test/ui/issues/issue-32709.stderr
@@ -7,6 +7,7 @@ LL |     Err(5)?;
    |           ^ the trait `From<{integer}>` is not implemented for `()`
    |
    = note: the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait
+   = help: the trait `FromResidual<Result<Infallible, E>>` is implemented for `Result<T, F>`
    = note: required because of the requirements on the impl of `FromResidual<Result<Infallible, {integer}>>` for `Result<i32, ()>`
 
 error: aborting due to previous error
diff --git a/src/test/ui/issues/issue-34334.stderr b/src/test/ui/issues/issue-34334.stderr
index cd697445faff3..48ae2df691c3d 100644
--- a/src/test/ui/issues/issue-34334.stderr
+++ b/src/test/ui/issues/issue-34334.stderr
@@ -19,6 +19,7 @@ LL |     let sr2: Vec<(u32, _, _)> = sr.iter().map(|(faction, th_sender, th_rece
    |                                                                                       ^^^^^^^ value of type `Vec<(u32, _, _)>` cannot be built from `std::iter::Iterator<Item=()>`
    |
    = help: the trait `FromIterator<()>` is not implemented for `Vec<(u32, _, _)>`
+   = help: the trait `FromIterator<T>` is implemented for `Vec<T>`
 note: required by a bound in `collect`
   --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL
    |
diff --git a/src/test/ui/issues/issue-45801.stderr b/src/test/ui/issues/issue-45801.stderr
index 099cf4e383426..8967f49df02a5 100644
--- a/src/test/ui/issues/issue-45801.stderr
+++ b/src/test/ui/issues/issue-45801.stderr
@@ -4,8 +4,7 @@ error[E0277]: the trait bound `Params: Plugin<i32>` is not satisfied
 LL |     req.get_ref::<Params>();
    |         ^^^^^^^ the trait `Plugin<i32>` is not implemented for `Params`
    |
-   = help: the following implementations were found:
-             <Params as Plugin<Foo>>
+   = help: the trait `Plugin<Foo>` is implemented for `Params`
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/issues/issue-50582.stderr b/src/test/ui/issues/issue-50582.stderr
index 465fb9baac3f0..3d527eb6b4e4a 100644
--- a/src/test/ui/issues/issue-50582.stderr
+++ b/src/test/ui/issues/issue-50582.stderr
@@ -14,6 +14,16 @@ LL |     Vec::<[(); 1 + for x in 0..1 {}]>::new();
    |                  ^ no implementation for `{integer} + ()`
    |
    = help: the trait `Add<()>` is not implemented for `{integer}`
+   = help: the following other types implement trait `Add<Rhs>`:
+             <&'a f32 as Add<f32>>
+             <&'a f64 as Add<f64>>
+             <&'a i128 as Add<i128>>
+             <&'a i16 as Add<i16>>
+             <&'a i32 as Add<i32>>
+             <&'a i64 as Add<i64>>
+             <&'a i8 as Add<i8>>
+             <&'a isize as Add<isize>>
+           and 48 others
 
 error: aborting due to 2 previous errors
 
diff --git a/src/test/ui/issues/issue-59488.stderr b/src/test/ui/issues/issue-59488.stderr
index 47fd9fdb65472..76a47c49bbafb 100644
--- a/src/test/ui/issues/issue-59488.stderr
+++ b/src/test/ui/issues/issue-59488.stderr
@@ -94,6 +94,16 @@ LL |     assert_eq!(Foo::Bar, i);
    |     ^^^^^^^^^^^^^^^^^^^^^^^ `fn(usize) -> Foo {Foo::Bar}` cannot be formatted using `{:?}` because it doesn't implement `Debug`
    |
    = help: the trait `Debug` is not implemented for `fn(usize) -> Foo {Foo::Bar}`
+   = help: the following other types implement trait `Debug`:
+             extern "C" fn() -> Ret
+             extern "C" fn(A) -> Ret
+             extern "C" fn(A, ...) -> Ret
+             extern "C" fn(A, B) -> Ret
+             extern "C" fn(A, B, ...) -> Ret
+             extern "C" fn(A, B, C) -> Ret
+             extern "C" fn(A, B, C, ...) -> Ret
+             extern "C" fn(A, B, C, D) -> Ret
+           and 68 others
    = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: aborting due to 9 previous errors
diff --git a/src/test/ui/issues/issue-66923-show-error-for-correct-call.stderr b/src/test/ui/issues/issue-66923-show-error-for-correct-call.stderr
index 6a96709cbacdb..d6e39251632ba 100644
--- a/src/test/ui/issues/issue-66923-show-error-for-correct-call.stderr
+++ b/src/test/ui/issues/issue-66923-show-error-for-correct-call.stderr
@@ -5,6 +5,7 @@ LL |     let x2: Vec<f64> = x1.into_iter().collect();
    |                                       ^^^^^^^ value of type `Vec<f64>` cannot be built from `std::iter::Iterator<Item=&f64>`
    |
    = help: the trait `FromIterator<&f64>` is not implemented for `Vec<f64>`
+   = help: the trait `FromIterator<T>` is implemented for `Vec<T>`
 note: required by a bound in `collect`
   --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL
    |
@@ -18,6 +19,7 @@ LL |     let x3 = x1.into_iter().collect::<Vec<f64>>();
    |                             ^^^^^^^ value of type `Vec<f64>` cannot be built from `std::iter::Iterator<Item=&f64>`
    |
    = help: the trait `FromIterator<&f64>` is not implemented for `Vec<f64>`
+   = help: the trait `FromIterator<T>` is implemented for `Vec<T>`
 note: required by a bound in `collect`
   --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL
    |
diff --git a/src/test/ui/kindck/kindck-copy.stderr b/src/test/ui/kindck/kindck-copy.stderr
index e147366a22410..1c61c85368be9 100644
--- a/src/test/ui/kindck/kindck-copy.stderr
+++ b/src/test/ui/kindck/kindck-copy.stderr
@@ -4,12 +4,16 @@ error[E0277]: the trait bound `&'static mut isize: Copy` is not satisfied
 LL |     assert_copy::<&'static mut isize>();
    |                   ^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `&'static mut isize`
    |
-   = help: the following implementations were found:
-             <isize as Copy>
-             <f32 as Copy>
-             <f64 as Copy>
-             <i128 as Copy>
-           and 10 others
+   = help: the following other types implement trait `Copy`:
+             f32
+             f64
+             i128
+             i16
+             i32
+             i64
+             i8
+             isize
+           and 6 others
 note: required by a bound in `assert_copy`
   --> $DIR/kindck-copy.rs:5:18
    |
@@ -22,12 +26,16 @@ error[E0277]: the trait bound `&'a mut isize: Copy` is not satisfied
 LL |     assert_copy::<&'a mut isize>();
    |                   ^^^^^^^^^^^^^ the trait `Copy` is not implemented for `&'a mut isize`
    |
-   = help: the following implementations were found:
-             <isize as Copy>
-             <f32 as Copy>
-             <f64 as Copy>
-             <i128 as Copy>
-           and 10 others
+   = help: the following other types implement trait `Copy`:
+             f32
+             f64
+             i128
+             i16
+             i32
+             i64
+             i8
+             isize
+           and 6 others
 note: required by a bound in `assert_copy`
   --> $DIR/kindck-copy.rs:5:18
    |
diff --git a/src/test/ui/kindck/kindck-impl-type-params.nll.stderr b/src/test/ui/kindck/kindck-impl-type-params.nll.stderr
index 7129bad8a978a..94f802620a302 100644
--- a/src/test/ui/kindck/kindck-impl-type-params.nll.stderr
+++ b/src/test/ui/kindck/kindck-impl-type-params.nll.stderr
@@ -72,6 +72,7 @@ error[E0277]: the trait bound `String: Copy` is not satisfied
 LL |     let a = t as Box<dyn Gettable<String>>;
    |             ^ the trait `Copy` is not implemented for `String`
    |
+   = help: the trait `Gettable<T>` is implemented for `S<T>`
 note: required because of the requirements on the impl of `Gettable<String>` for `S<String>`
   --> $DIR/kindck-impl-type-params.rs:14:32
    |
@@ -85,6 +86,7 @@ error[E0277]: the trait bound `Foo: Copy` is not satisfied
 LL |     let a: Box<dyn Gettable<Foo>> = t;
    |                                     ^ the trait `Copy` is not implemented for `Foo`
    |
+   = help: the trait `Gettable<T>` is implemented for `S<T>`
 note: required because of the requirements on the impl of `Gettable<Foo>` for `S<Foo>`
   --> $DIR/kindck-impl-type-params.rs:14:32
    |
diff --git a/src/test/ui/kindck/kindck-impl-type-params.stderr b/src/test/ui/kindck/kindck-impl-type-params.stderr
index 3558f0c9e6294..64bbc841b330f 100644
--- a/src/test/ui/kindck/kindck-impl-type-params.stderr
+++ b/src/test/ui/kindck/kindck-impl-type-params.stderr
@@ -80,6 +80,7 @@ error[E0277]: the trait bound `String: Copy` is not satisfied
 LL |     let a = t as Box<dyn Gettable<String>>;
    |             ^ the trait `Copy` is not implemented for `String`
    |
+   = help: the trait `Gettable<T>` is implemented for `S<T>`
 note: required because of the requirements on the impl of `Gettable<String>` for `S<String>`
   --> $DIR/kindck-impl-type-params.rs:14:32
    |
@@ -93,6 +94,7 @@ error[E0277]: the trait bound `Foo: Copy` is not satisfied
 LL |     let a: Box<dyn Gettable<Foo>> = t;
    |                                     ^ the trait `Copy` is not implemented for `Foo`
    |
+   = help: the trait `Gettable<T>` is implemented for `S<T>`
 note: required because of the requirements on the impl of `Gettable<Foo>` for `S<Foo>`
   --> $DIR/kindck-impl-type-params.rs:14:32
    |
diff --git a/src/test/ui/lexer/lex-bad-char-literals-6.stderr b/src/test/ui/lexer/lex-bad-char-literals-6.stderr
index 4332bdefcb258..afef0cb603485 100644
--- a/src/test/ui/lexer/lex-bad-char-literals-6.stderr
+++ b/src/test/ui/lexer/lex-bad-char-literals-6.stderr
@@ -38,6 +38,16 @@ LL |     if x == y {}
    |          ^^ no implementation for `&str == char`
    |
    = help: the trait `PartialEq<char>` is not implemented for `&str`
+   = help: the following other types implement trait `PartialEq<Rhs>`:
+             <&'a str as PartialEq<OsString>>
+             <&'a str as PartialEq<String>>
+             <&'b str as PartialEq<Cow<'a, str>>>
+             <String as PartialEq<&'a str>>
+             <String as PartialEq<Cow<'a, str>>>
+             <String as PartialEq<str>>
+             <String as PartialEq>
+             <str as PartialEq<Cow<'a, str>>>
+           and 4 others
 
 error[E0308]: mismatched types
   --> $DIR/lex-bad-char-literals-6.rs:15:20
@@ -54,6 +64,16 @@ LL |     if x == z {}
    |          ^^ no implementation for `&str == char`
    |
    = help: the trait `PartialEq<char>` is not implemented for `&str`
+   = help: the following other types implement trait `PartialEq<Rhs>`:
+             <&'a str as PartialEq<OsString>>
+             <&'a str as PartialEq<String>>
+             <&'b str as PartialEq<Cow<'a, str>>>
+             <String as PartialEq<&'a str>>
+             <String as PartialEq<Cow<'a, str>>>
+             <String as PartialEq<str>>
+             <String as PartialEq>
+             <str as PartialEq<Cow<'a, str>>>
+           and 4 others
 
 error: aborting due to 6 previous errors
 
diff --git a/src/test/ui/mismatched_types/binops.stderr b/src/test/ui/mismatched_types/binops.stderr
index 19e921dd04d73..3de652d87ec54 100644
--- a/src/test/ui/mismatched_types/binops.stderr
+++ b/src/test/ui/mismatched_types/binops.stderr
@@ -5,6 +5,16 @@ LL |     1 + Some(1);
    |       ^ no implementation for `{integer} + Option<{integer}>`
    |
    = help: the trait `Add<Option<{integer}>>` is not implemented for `{integer}`
+   = help: the following other types implement trait `Add<Rhs>`:
+             <&'a f32 as Add<f32>>
+             <&'a f64 as Add<f64>>
+             <&'a i128 as Add<i128>>
+             <&'a i16 as Add<i16>>
+             <&'a i32 as Add<i32>>
+             <&'a i64 as Add<i64>>
+             <&'a i8 as Add<i8>>
+             <&'a isize as Add<isize>>
+           and 48 others
 
 error[E0277]: cannot subtract `Option<{integer}>` from `usize`
   --> $DIR/binops.rs:3:16
@@ -13,6 +23,16 @@ LL |     2 as usize - Some(1);
    |                ^ no implementation for `usize - Option<{integer}>`
    |
    = help: the trait `Sub<Option<{integer}>>` is not implemented for `usize`
+   = help: the following other types implement trait `Sub<Rhs>`:
+             <&'a f32 as Sub<f32>>
+             <&'a f64 as Sub<f64>>
+             <&'a i128 as Sub<i128>>
+             <&'a i16 as Sub<i16>>
+             <&'a i32 as Sub<i32>>
+             <&'a i64 as Sub<i64>>
+             <&'a i8 as Sub<i8>>
+             <&'a isize as Sub<isize>>
+           and 48 others
 
 error[E0277]: cannot multiply `{integer}` by `()`
   --> $DIR/binops.rs:4:7
@@ -21,6 +41,16 @@ LL |     3 * ();
    |       ^ no implementation for `{integer} * ()`
    |
    = help: the trait `Mul<()>` is not implemented for `{integer}`
+   = help: the following other types implement trait `Mul<Rhs>`:
+             <&'a f32 as Mul<f32>>
+             <&'a f64 as Mul<f64>>
+             <&'a i128 as Mul<i128>>
+             <&'a i16 as Mul<i16>>
+             <&'a i32 as Mul<i32>>
+             <&'a i64 as Mul<i64>>
+             <&'a i8 as Mul<i8>>
+             <&'a isize as Mul<isize>>
+           and 49 others
 
 error[E0277]: cannot divide `{integer}` by `&str`
   --> $DIR/binops.rs:5:7
@@ -29,6 +59,16 @@ LL |     4 / "";
    |       ^ no implementation for `{integer} / &str`
    |
    = help: the trait `Div<&str>` is not implemented for `{integer}`
+   = help: the following other types implement trait `Div<Rhs>`:
+             <&'a f32 as Div<f32>>
+             <&'a f64 as Div<f64>>
+             <&'a i128 as Div<i128>>
+             <&'a i16 as Div<i16>>
+             <&'a i32 as Div<i32>>
+             <&'a i64 as Div<i64>>
+             <&'a i8 as Div<i8>>
+             <&'a isize as Div<isize>>
+           and 54 others
 
 error[E0277]: can't compare `{integer}` with `String`
   --> $DIR/binops.rs:6:7
@@ -37,6 +77,16 @@ LL |     5 < String::new();
    |       ^ no implementation for `{integer} < String` and `{integer} > String`
    |
    = help: the trait `PartialOrd<String>` is not implemented for `{integer}`
+   = help: the following other types implement trait `PartialOrd<Rhs>`:
+             f32
+             f64
+             i128
+             i16
+             i32
+             i64
+             i8
+             isize
+           and 6 others
 
 error[E0277]: can't compare `{integer}` with `Result<{integer}, _>`
   --> $DIR/binops.rs:7:7
@@ -45,6 +95,16 @@ LL |     6 == Ok(1);
    |       ^^ no implementation for `{integer} == Result<{integer}, _>`
    |
    = help: the trait `PartialEq<Result<{integer}, _>>` is not implemented for `{integer}`
+   = help: the following other types implement trait `PartialEq<Rhs>`:
+             f32
+             f64
+             i128
+             i16
+             i32
+             i64
+             i8
+             isize
+           and 6 others
 
 error: aborting due to 6 previous errors
 
diff --git a/src/test/ui/never_type/defaulted-never-note.fallback.stderr b/src/test/ui/never_type/defaulted-never-note.fallback.stderr
index b105f03f81cdd..4c8b492247352 100644
--- a/src/test/ui/never_type/defaulted-never-note.fallback.stderr
+++ b/src/test/ui/never_type/defaulted-never-note.fallback.stderr
@@ -4,7 +4,7 @@ error[E0277]: the trait bound `!: ImplementedForUnitButNotNever` is not satisfie
 LL |     foo(_x);
    |     ^^^ the trait `ImplementedForUnitButNotNever` is not implemented for `!`
    |
-   = note: this trait is implemented for `()`
+   = help: the trait `ImplementedForUnitButNotNever` is implemented for `()`
    = note: this error might have been caused by changes to Rust's type-inference algorithm (see issue #48950 <https://github.com/rust-lang/rust/issues/48950> for more information)
    = help: did you intend to use the type `()` here instead?
 note: required by a bound in `foo`
diff --git a/src/test/ui/never_type/defaulted-never-note.rs b/src/test/ui/never_type/defaulted-never-note.rs
index 54f551759cb39..aefc739a0a0b0 100644
--- a/src/test/ui/never_type/defaulted-never-note.rs
+++ b/src/test/ui/never_type/defaulted-never-note.rs
@@ -30,7 +30,7 @@ fn smeg() {
     foo(_x);
     //[fallback]~^ ERROR the trait bound
     //[fallback]~| NOTE the trait `ImplementedForUnitButNotNever` is not implemented
-    //[fallback]~| NOTE this trait is implemented for `()`
+    //[fallback]~| HELP trait `ImplementedForUnitButNotNever` is implemented for `()`
     //[fallback]~| NOTE this error might have been caused
     //[fallback]~| HELP did you intend
 }
diff --git a/src/test/ui/never_type/diverging-fallback-no-leak.fallback.stderr b/src/test/ui/never_type/diverging-fallback-no-leak.fallback.stderr
index 72cd69380573e..dee2b1d704b86 100644
--- a/src/test/ui/never_type/diverging-fallback-no-leak.fallback.stderr
+++ b/src/test/ui/never_type/diverging-fallback-no-leak.fallback.stderr
@@ -4,7 +4,9 @@ error[E0277]: the trait bound `!: Test` is not satisfied
 LL |     unconstrained_arg(return);
    |     ^^^^^^^^^^^^^^^^^ the trait `Test` is not implemented for `!`
    |
-   = note: this trait is implemented for `()`
+   = help: the following other types implement trait `Test`:
+             ()
+             i32
    = note: this error might have been caused by changes to Rust's type-inference algorithm (see issue #48950 <https://github.com/rust-lang/rust/issues/48950> for more information)
    = help: did you intend to use the type `()` here instead?
 note: required by a bound in `unconstrained_arg`
diff --git a/src/test/ui/never_type/impl_trait_fallback2.stderr b/src/test/ui/never_type/impl_trait_fallback2.stderr
index 2f50b9d245900..4a78e73e5f6d5 100644
--- a/src/test/ui/never_type/impl_trait_fallback2.stderr
+++ b/src/test/ui/never_type/impl_trait_fallback2.stderr
@@ -3,6 +3,8 @@ error[E0277]: the trait bound `(): T` is not satisfied
    |
 LL | fn should_ret_unit() -> impl T {
    |                         ^^^^^^ the trait `T` is not implemented for `()`
+   |
+   = help: the trait `T` is implemented for `i32`
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/never_type/issue-13352.stderr b/src/test/ui/never_type/issue-13352.stderr
index b98f6c93923b9..fed780e68957f 100644
--- a/src/test/ui/never_type/issue-13352.stderr
+++ b/src/test/ui/never_type/issue-13352.stderr
@@ -5,6 +5,16 @@ LL |     2_usize + (loop {});
    |             ^ no implementation for `usize + ()`
    |
    = help: the trait `Add<()>` is not implemented for `usize`
+   = help: the following other types implement trait `Add<Rhs>`:
+             <&'a f32 as Add<f32>>
+             <&'a f64 as Add<f64>>
+             <&'a i128 as Add<i128>>
+             <&'a i16 as Add<i16>>
+             <&'a i32 as Add<i32>>
+             <&'a i64 as Add<i64>>
+             <&'a i8 as Add<i8>>
+             <&'a isize as Add<isize>>
+           and 48 others
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/never_type/never-value-fallback-issue-66757.nofallback.stderr b/src/test/ui/never_type/never-value-fallback-issue-66757.nofallback.stderr
index a14253e384a3a..e2045591390e1 100644
--- a/src/test/ui/never_type/never-value-fallback-issue-66757.nofallback.stderr
+++ b/src/test/ui/never_type/never-value-fallback-issue-66757.nofallback.stderr
@@ -4,8 +4,7 @@ error[E0277]: the trait bound `E: From<()>` is not satisfied
 LL |     <E as From<_>>::from(never);
    |     ^^^^^^^^^^^^^^^^^^^^ the trait `From<()>` is not implemented for `E`
    |
-   = help: the following implementations were found:
-             <E as From<!>>
+   = help: the trait `From<!>` is implemented for `E`
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/numbers-arithmetic/not-suggest-float-literal.stderr b/src/test/ui/numbers-arithmetic/not-suggest-float-literal.stderr
index ce9a08a15897a..6aa1ad8dd899f 100644
--- a/src/test/ui/numbers-arithmetic/not-suggest-float-literal.stderr
+++ b/src/test/ui/numbers-arithmetic/not-suggest-float-literal.stderr
@@ -5,6 +5,16 @@ LL |     x + 100.0
    |       ^ no implementation for `u8 + {float}`
    |
    = help: the trait `Add<{float}>` is not implemented for `u8`
+   = help: the following other types implement trait `Add<Rhs>`:
+             <&'a f32 as Add<f32>>
+             <&'a f64 as Add<f64>>
+             <&'a i128 as Add<i128>>
+             <&'a i16 as Add<i16>>
+             <&'a i32 as Add<i32>>
+             <&'a i64 as Add<i64>>
+             <&'a i8 as Add<i8>>
+             <&'a isize as Add<isize>>
+           and 48 others
 
 error[E0277]: cannot add `&str` to `f64`
   --> $DIR/not-suggest-float-literal.rs:6:7
@@ -13,6 +23,16 @@ LL |     x + "foo"
    |       ^ no implementation for `f64 + &str`
    |
    = help: the trait `Add<&str>` is not implemented for `f64`
+   = help: the following other types implement trait `Add<Rhs>`:
+             <&'a f32 as Add<f32>>
+             <&'a f64 as Add<f64>>
+             <&'a i128 as Add<i128>>
+             <&'a i16 as Add<i16>>
+             <&'a i32 as Add<i32>>
+             <&'a i64 as Add<i64>>
+             <&'a i8 as Add<i8>>
+             <&'a isize as Add<isize>>
+           and 48 others
 
 error[E0277]: cannot add `{integer}` to `f64`
   --> $DIR/not-suggest-float-literal.rs:11:7
@@ -21,6 +41,16 @@ LL |     x + y
    |       ^ no implementation for `f64 + {integer}`
    |
    = help: the trait `Add<{integer}>` is not implemented for `f64`
+   = help: the following other types implement trait `Add<Rhs>`:
+             <&'a f32 as Add<f32>>
+             <&'a f64 as Add<f64>>
+             <&'a i128 as Add<i128>>
+             <&'a i16 as Add<i16>>
+             <&'a i32 as Add<i32>>
+             <&'a i64 as Add<i64>>
+             <&'a i8 as Add<i8>>
+             <&'a isize as Add<isize>>
+           and 48 others
 
 error[E0277]: cannot subtract `{float}` from `u8`
   --> $DIR/not-suggest-float-literal.rs:15:7
@@ -29,6 +59,16 @@ LL |     x - 100.0
    |       ^ no implementation for `u8 - {float}`
    |
    = help: the trait `Sub<{float}>` is not implemented for `u8`
+   = help: the following other types implement trait `Sub<Rhs>`:
+             <&'a f32 as Sub<f32>>
+             <&'a f64 as Sub<f64>>
+             <&'a i128 as Sub<i128>>
+             <&'a i16 as Sub<i16>>
+             <&'a i32 as Sub<i32>>
+             <&'a i64 as Sub<i64>>
+             <&'a i8 as Sub<i8>>
+             <&'a isize as Sub<isize>>
+           and 48 others
 
 error[E0277]: cannot subtract `&str` from `f64`
   --> $DIR/not-suggest-float-literal.rs:19:7
@@ -37,6 +77,16 @@ LL |     x - "foo"
    |       ^ no implementation for `f64 - &str`
    |
    = help: the trait `Sub<&str>` is not implemented for `f64`
+   = help: the following other types implement trait `Sub<Rhs>`:
+             <&'a f32 as Sub<f32>>
+             <&'a f64 as Sub<f64>>
+             <&'a i128 as Sub<i128>>
+             <&'a i16 as Sub<i16>>
+             <&'a i32 as Sub<i32>>
+             <&'a i64 as Sub<i64>>
+             <&'a i8 as Sub<i8>>
+             <&'a isize as Sub<isize>>
+           and 48 others
 
 error[E0277]: cannot subtract `{integer}` from `f64`
   --> $DIR/not-suggest-float-literal.rs:24:7
@@ -45,6 +95,16 @@ LL |     x - y
    |       ^ no implementation for `f64 - {integer}`
    |
    = help: the trait `Sub<{integer}>` is not implemented for `f64`
+   = help: the following other types implement trait `Sub<Rhs>`:
+             <&'a f32 as Sub<f32>>
+             <&'a f64 as Sub<f64>>
+             <&'a i128 as Sub<i128>>
+             <&'a i16 as Sub<i16>>
+             <&'a i32 as Sub<i32>>
+             <&'a i64 as Sub<i64>>
+             <&'a i8 as Sub<i8>>
+             <&'a isize as Sub<isize>>
+           and 48 others
 
 error[E0277]: cannot multiply `u8` by `{float}`
   --> $DIR/not-suggest-float-literal.rs:28:7
@@ -53,6 +113,16 @@ LL |     x * 100.0
    |       ^ no implementation for `u8 * {float}`
    |
    = help: the trait `Mul<{float}>` is not implemented for `u8`
+   = help: the following other types implement trait `Mul<Rhs>`:
+             <&'a f32 as Mul<f32>>
+             <&'a f64 as Mul<f64>>
+             <&'a i128 as Mul<i128>>
+             <&'a i16 as Mul<i16>>
+             <&'a i32 as Mul<i32>>
+             <&'a i64 as Mul<i64>>
+             <&'a i8 as Mul<i8>>
+             <&'a isize as Mul<isize>>
+           and 49 others
 
 error[E0277]: cannot multiply `f64` by `&str`
   --> $DIR/not-suggest-float-literal.rs:32:7
@@ -61,6 +131,16 @@ LL |     x * "foo"
    |       ^ no implementation for `f64 * &str`
    |
    = help: the trait `Mul<&str>` is not implemented for `f64`
+   = help: the following other types implement trait `Mul<Rhs>`:
+             <&'a f32 as Mul<f32>>
+             <&'a f64 as Mul<f64>>
+             <&'a i128 as Mul<i128>>
+             <&'a i16 as Mul<i16>>
+             <&'a i32 as Mul<i32>>
+             <&'a i64 as Mul<i64>>
+             <&'a i8 as Mul<i8>>
+             <&'a isize as Mul<isize>>
+           and 49 others
 
 error[E0277]: cannot multiply `f64` by `{integer}`
   --> $DIR/not-suggest-float-literal.rs:37:7
@@ -69,6 +149,16 @@ LL |     x * y
    |       ^ no implementation for `f64 * {integer}`
    |
    = help: the trait `Mul<{integer}>` is not implemented for `f64`
+   = help: the following other types implement trait `Mul<Rhs>`:
+             <&'a f32 as Mul<f32>>
+             <&'a f64 as Mul<f64>>
+             <&'a i128 as Mul<i128>>
+             <&'a i16 as Mul<i16>>
+             <&'a i32 as Mul<i32>>
+             <&'a i64 as Mul<i64>>
+             <&'a i8 as Mul<i8>>
+             <&'a isize as Mul<isize>>
+           and 49 others
 
 error[E0277]: cannot divide `u8` by `{float}`
   --> $DIR/not-suggest-float-literal.rs:41:7
@@ -77,6 +167,16 @@ LL |     x / 100.0
    |       ^ no implementation for `u8 / {float}`
    |
    = help: the trait `Div<{float}>` is not implemented for `u8`
+   = help: the following other types implement trait `Div<Rhs>`:
+             <&'a f32 as Div<f32>>
+             <&'a f64 as Div<f64>>
+             <&'a i128 as Div<i128>>
+             <&'a i16 as Div<i16>>
+             <&'a i32 as Div<i32>>
+             <&'a i64 as Div<i64>>
+             <&'a i8 as Div<i8>>
+             <&'a isize as Div<isize>>
+           and 54 others
 
 error[E0277]: cannot divide `f64` by `&str`
   --> $DIR/not-suggest-float-literal.rs:45:7
@@ -85,6 +185,16 @@ LL |     x / "foo"
    |       ^ no implementation for `f64 / &str`
    |
    = help: the trait `Div<&str>` is not implemented for `f64`
+   = help: the following other types implement trait `Div<Rhs>`:
+             <&'a f32 as Div<f32>>
+             <&'a f64 as Div<f64>>
+             <&'a i128 as Div<i128>>
+             <&'a i16 as Div<i16>>
+             <&'a i32 as Div<i32>>
+             <&'a i64 as Div<i64>>
+             <&'a i8 as Div<i8>>
+             <&'a isize as Div<isize>>
+           and 54 others
 
 error[E0277]: cannot divide `f64` by `{integer}`
   --> $DIR/not-suggest-float-literal.rs:50:7
@@ -93,6 +203,16 @@ LL |     x / y
    |       ^ no implementation for `f64 / {integer}`
    |
    = help: the trait `Div<{integer}>` is not implemented for `f64`
+   = help: the following other types implement trait `Div<Rhs>`:
+             <&'a f32 as Div<f32>>
+             <&'a f64 as Div<f64>>
+             <&'a i128 as Div<i128>>
+             <&'a i16 as Div<i16>>
+             <&'a i32 as Div<i32>>
+             <&'a i64 as Div<i64>>
+             <&'a i8 as Div<i8>>
+             <&'a isize as Div<isize>>
+           and 54 others
 
 error: aborting due to 12 previous errors
 
diff --git a/src/test/ui/numbers-arithmetic/suggest-float-literal.stderr b/src/test/ui/numbers-arithmetic/suggest-float-literal.stderr
index eb0be785061e6..988379e582a34 100644
--- a/src/test/ui/numbers-arithmetic/suggest-float-literal.stderr
+++ b/src/test/ui/numbers-arithmetic/suggest-float-literal.stderr
@@ -5,6 +5,16 @@ LL |     x + 100
    |       ^ no implementation for `f32 + {integer}`
    |
    = help: the trait `Add<{integer}>` is not implemented for `f32`
+   = help: the following other types implement trait `Add<Rhs>`:
+             <&'a f32 as Add<f32>>
+             <&'a f64 as Add<f64>>
+             <&'a i128 as Add<i128>>
+             <&'a i16 as Add<i16>>
+             <&'a i32 as Add<i32>>
+             <&'a i64 as Add<i64>>
+             <&'a i8 as Add<i8>>
+             <&'a isize as Add<isize>>
+           and 48 others
 help: consider using a floating-point literal by writing it with `.0`
    |
 LL |     x + 100.0
@@ -17,6 +27,16 @@ LL |     x + 100
    |       ^ no implementation for `f64 + {integer}`
    |
    = help: the trait `Add<{integer}>` is not implemented for `f64`
+   = help: the following other types implement trait `Add<Rhs>`:
+             <&'a f32 as Add<f32>>
+             <&'a f64 as Add<f64>>
+             <&'a i128 as Add<i128>>
+             <&'a i16 as Add<i16>>
+             <&'a i32 as Add<i32>>
+             <&'a i64 as Add<i64>>
+             <&'a i8 as Add<i8>>
+             <&'a isize as Add<isize>>
+           and 48 others
 help: consider using a floating-point literal by writing it with `.0`
    |
 LL |     x + 100.0
@@ -29,6 +49,16 @@ LL |     x - 100
    |       ^ no implementation for `f32 - {integer}`
    |
    = help: the trait `Sub<{integer}>` is not implemented for `f32`
+   = help: the following other types implement trait `Sub<Rhs>`:
+             <&'a f32 as Sub<f32>>
+             <&'a f64 as Sub<f64>>
+             <&'a i128 as Sub<i128>>
+             <&'a i16 as Sub<i16>>
+             <&'a i32 as Sub<i32>>
+             <&'a i64 as Sub<i64>>
+             <&'a i8 as Sub<i8>>
+             <&'a isize as Sub<isize>>
+           and 48 others
 help: consider using a floating-point literal by writing it with `.0`
    |
 LL |     x - 100.0
@@ -41,6 +71,16 @@ LL |     x - 100
    |       ^ no implementation for `f64 - {integer}`
    |
    = help: the trait `Sub<{integer}>` is not implemented for `f64`
+   = help: the following other types implement trait `Sub<Rhs>`:
+             <&'a f32 as Sub<f32>>
+             <&'a f64 as Sub<f64>>
+             <&'a i128 as Sub<i128>>
+             <&'a i16 as Sub<i16>>
+             <&'a i32 as Sub<i32>>
+             <&'a i64 as Sub<i64>>
+             <&'a i8 as Sub<i8>>
+             <&'a isize as Sub<isize>>
+           and 48 others
 help: consider using a floating-point literal by writing it with `.0`
    |
 LL |     x - 100.0
@@ -53,6 +93,16 @@ LL |     x * 100
    |       ^ no implementation for `f32 * {integer}`
    |
    = help: the trait `Mul<{integer}>` is not implemented for `f32`
+   = help: the following other types implement trait `Mul<Rhs>`:
+             <&'a f32 as Mul<f32>>
+             <&'a f64 as Mul<f64>>
+             <&'a i128 as Mul<i128>>
+             <&'a i16 as Mul<i16>>
+             <&'a i32 as Mul<i32>>
+             <&'a i64 as Mul<i64>>
+             <&'a i8 as Mul<i8>>
+             <&'a isize as Mul<isize>>
+           and 49 others
 help: consider using a floating-point literal by writing it with `.0`
    |
 LL |     x * 100.0
@@ -65,6 +115,16 @@ LL |     x * 100
    |       ^ no implementation for `f64 * {integer}`
    |
    = help: the trait `Mul<{integer}>` is not implemented for `f64`
+   = help: the following other types implement trait `Mul<Rhs>`:
+             <&'a f32 as Mul<f32>>
+             <&'a f64 as Mul<f64>>
+             <&'a i128 as Mul<i128>>
+             <&'a i16 as Mul<i16>>
+             <&'a i32 as Mul<i32>>
+             <&'a i64 as Mul<i64>>
+             <&'a i8 as Mul<i8>>
+             <&'a isize as Mul<isize>>
+           and 49 others
 help: consider using a floating-point literal by writing it with `.0`
    |
 LL |     x * 100.0
@@ -77,6 +137,16 @@ LL |     x / 100
    |       ^ no implementation for `f32 / {integer}`
    |
    = help: the trait `Div<{integer}>` is not implemented for `f32`
+   = help: the following other types implement trait `Div<Rhs>`:
+             <&'a f32 as Div<f32>>
+             <&'a f64 as Div<f64>>
+             <&'a i128 as Div<i128>>
+             <&'a i16 as Div<i16>>
+             <&'a i32 as Div<i32>>
+             <&'a i64 as Div<i64>>
+             <&'a i8 as Div<i8>>
+             <&'a isize as Div<isize>>
+           and 54 others
 help: consider using a floating-point literal by writing it with `.0`
    |
 LL |     x / 100.0
@@ -89,6 +159,16 @@ LL |     x / 100
    |       ^ no implementation for `f64 / {integer}`
    |
    = help: the trait `Div<{integer}>` is not implemented for `f64`
+   = help: the following other types implement trait `Div<Rhs>`:
+             <&'a f32 as Div<f32>>
+             <&'a f64 as Div<f64>>
+             <&'a i128 as Div<i128>>
+             <&'a i16 as Div<i16>>
+             <&'a i32 as Div<i32>>
+             <&'a i64 as Div<i64>>
+             <&'a i8 as Div<i8>>
+             <&'a isize as Div<isize>>
+           and 54 others
 help: consider using a floating-point literal by writing it with `.0`
    |
 LL |     x / 100.0
diff --git a/src/test/ui/on-unimplemented/impl-substs.stderr b/src/test/ui/on-unimplemented/impl-substs.stderr
index db66ab0bfaec1..a0fad0acd0b5c 100644
--- a/src/test/ui/on-unimplemented/impl-substs.stderr
+++ b/src/test/ui/on-unimplemented/impl-substs.stderr
@@ -7,6 +7,7 @@ LL |     Foo::<usize>::foo((1i32, 1i32, 1i32));
    |     required by a bound introduced by this call
    |
    = help: the trait `Foo<usize>` is not implemented for `(i32, i32, i32)`
+   = help: the trait `Foo<A>` is implemented for `(A, B, C)`
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/on-unimplemented/multiple-impls.stderr b/src/test/ui/on-unimplemented/multiple-impls.stderr
index 76aa128e242f9..06e1a222af881 100644
--- a/src/test/ui/on-unimplemented/multiple-impls.stderr
+++ b/src/test/ui/on-unimplemented/multiple-impls.stderr
@@ -7,6 +7,9 @@ LL |     Index::index(&[] as &[i32], 2u32);
    |     required by a bound introduced by this call
    |
    = help: the trait `Index<u32>` is not implemented for `[i32]`
+   = help: the following other types implement trait `Index<Idx>`:
+             <[i32] as Index<Bar<usize>>>
+             <[i32] as Index<Foo<usize>>>
 
 error[E0277]: the trait bound `[i32]: Index<Foo<u32>>` is not satisfied
   --> $DIR/multiple-impls.rs:36:18
@@ -17,6 +20,9 @@ LL |     Index::index(&[] as &[i32], Foo(2u32));
    |     required by a bound introduced by this call
    |
    = help: the trait `Index<Foo<u32>>` is not implemented for `[i32]`
+   = help: the following other types implement trait `Index<Idx>`:
+             <[i32] as Index<Bar<usize>>>
+             <[i32] as Index<Foo<usize>>>
 
 error[E0277]: the trait bound `[i32]: Index<Bar<u32>>` is not satisfied
   --> $DIR/multiple-impls.rs:39:18
@@ -27,6 +33,9 @@ LL |     Index::index(&[] as &[i32], Bar(2u32));
    |     required by a bound introduced by this call
    |
    = help: the trait `Index<Bar<u32>>` is not implemented for `[i32]`
+   = help: the following other types implement trait `Index<Idx>`:
+             <[i32] as Index<Bar<usize>>>
+             <[i32] as Index<Foo<usize>>>
 
 error[E0277]: the trait bound `[i32]: Index<u32>` is not satisfied
   --> $DIR/multiple-impls.rs:33:5
@@ -35,6 +44,9 @@ LL |     Index::index(&[] as &[i32], 2u32);
    |     ^^^^^^^^^^^^ trait message
    |
    = help: the trait `Index<u32>` is not implemented for `[i32]`
+   = help: the following other types implement trait `Index<Idx>`:
+             <[i32] as Index<Bar<usize>>>
+             <[i32] as Index<Foo<usize>>>
 
 error[E0277]: the trait bound `[i32]: Index<Foo<u32>>` is not satisfied
   --> $DIR/multiple-impls.rs:36:5
@@ -43,6 +55,9 @@ LL |     Index::index(&[] as &[i32], Foo(2u32));
    |     ^^^^^^^^^^^^ on impl for Foo
    |
    = help: the trait `Index<Foo<u32>>` is not implemented for `[i32]`
+   = help: the following other types implement trait `Index<Idx>`:
+             <[i32] as Index<Bar<usize>>>
+             <[i32] as Index<Foo<usize>>>
 
 error[E0277]: the trait bound `[i32]: Index<Bar<u32>>` is not satisfied
   --> $DIR/multiple-impls.rs:39:5
@@ -51,6 +66,9 @@ LL |     Index::index(&[] as &[i32], Bar(2u32));
    |     ^^^^^^^^^^^^ on impl for Bar
    |
    = help: the trait `Index<Bar<u32>>` is not implemented for `[i32]`
+   = help: the following other types implement trait `Index<Idx>`:
+             <[i32] as Index<Bar<usize>>>
+             <[i32] as Index<Foo<usize>>>
 
 error: aborting due to 6 previous errors
 
diff --git a/src/test/ui/on-unimplemented/on-impl.stderr b/src/test/ui/on-unimplemented/on-impl.stderr
index 940763fae51b8..769a3d77a5726 100644
--- a/src/test/ui/on-unimplemented/on-impl.stderr
+++ b/src/test/ui/on-unimplemented/on-impl.stderr
@@ -7,6 +7,7 @@ LL |     Index::<u32>::index(&[1, 2, 3] as &[i32], 2u32);
    |     required by a bound introduced by this call
    |
    = help: the trait `Index<u32>` is not implemented for `[i32]`
+   = help: the trait `Index<usize>` is implemented for `[i32]`
 
 error[E0277]: the trait bound `[i32]: Index<u32>` is not satisfied
   --> $DIR/on-impl.rs:22:5
@@ -15,6 +16,7 @@ LL |     Index::<u32>::index(&[1, 2, 3] as &[i32], 2u32);
    |     ^^^^^^^^^^^^^^^^^^^ a usize is required to index into a slice
    |
    = help: the trait `Index<u32>` is not implemented for `[i32]`
+   = help: the trait `Index<usize>` is implemented for `[i32]`
 
 error: aborting due to 2 previous errors
 
diff --git a/src/test/ui/on-unimplemented/slice-index.stderr b/src/test/ui/on-unimplemented/slice-index.stderr
index 44b8b0d8d8444..ae7d2e1d82393 100644
--- a/src/test/ui/on-unimplemented/slice-index.stderr
+++ b/src/test/ui/on-unimplemented/slice-index.stderr
@@ -5,6 +5,7 @@ LL |     x[1i32];
    |     ^^^^^^^ slice indices are of type `usize` or ranges of `usize`
    |
    = help: the trait `SliceIndex<[i32]>` is not implemented for `i32`
+   = help: the trait `SliceIndex<[T]>` is implemented for `usize`
    = note: required because of the requirements on the impl of `Index<i32>` for `[i32]`
 
 error[E0277]: the type `[i32]` cannot be indexed by `RangeTo<i32>`
@@ -14,6 +15,9 @@ LL |     x[..1i32];
    |     ^^^^^^^^^ slice indices are of type `usize` or ranges of `usize`
    |
    = help: the trait `SliceIndex<[i32]>` is not implemented for `RangeTo<i32>`
+   = help: the following other types implement trait `SliceIndex<T>`:
+             <RangeTo<usize> as SliceIndex<[T]>>
+             <RangeTo<usize> as SliceIndex<str>>
    = note: required because of the requirements on the impl of `Index<RangeTo<i32>>` for `[i32]`
 
 error: aborting due to 2 previous errors
diff --git a/src/test/ui/range/range-1.stderr b/src/test/ui/range/range-1.stderr
index 2ce4e1553d28e..0bbed8704243f 100644
--- a/src/test/ui/range/range-1.stderr
+++ b/src/test/ui/range/range-1.stderr
@@ -10,6 +10,16 @@ error[E0277]: the trait bound `bool: Step` is not satisfied
 LL |     for i in false..true {}
    |              ^^^^^^^^^^^ the trait `Step` is not implemented for `bool`
    |
+   = help: the following other types implement trait `Step`:
+             char
+             i128
+             i16
+             i32
+             i64
+             i8
+             isize
+             u128
+           and 5 others
    = note: required because of the requirements on the impl of `Iterator` for `std::ops::Range<bool>`
    = note: required because of the requirements on the impl of `IntoIterator` for `std::ops::Range<bool>`
 
diff --git a/src/test/ui/rfc-1937-termination-trait/termination-trait-test-wrong-type.stderr b/src/test/ui/rfc-1937-termination-trait/termination-trait-test-wrong-type.stderr
index f8eaf61d7d7ec..96a899ecca581 100644
--- a/src/test/ui/rfc-1937-termination-trait/termination-trait-test-wrong-type.stderr
+++ b/src/test/ui/rfc-1937-termination-trait/termination-trait-test-wrong-type.stderr
@@ -9,6 +9,10 @@ LL | | }
    | |_^ `main` can only return types that implement `Termination`
    |
    = help: the trait `Termination` is not implemented for `Result<f32, ParseFloatError>`
+   = help: the following other types implement trait `Termination`:
+             Result<!, E>
+             Result<(), E>
+             Result<Infallible, E>
 note: required by a bound in `assert_test_result`
   --> $SRC_DIR/test/src/lib.rs:LL:COL
    |
diff --git a/src/test/ui/span/multiline-span-simple.stderr b/src/test/ui/span/multiline-span-simple.stderr
index 13ef0d1820881..c0d9a8634e487 100644
--- a/src/test/ui/span/multiline-span-simple.stderr
+++ b/src/test/ui/span/multiline-span-simple.stderr
@@ -5,6 +5,16 @@ LL |     foo(1 as u32 +
    |                  ^ no implementation for `u32 + ()`
    |
    = help: the trait `Add<()>` is not implemented for `u32`
+   = help: the following other types implement trait `Add<Rhs>`:
+             <&'a f32 as Add<f32>>
+             <&'a f64 as Add<f64>>
+             <&'a i128 as Add<i128>>
+             <&'a i16 as Add<i16>>
+             <&'a i32 as Add<i32>>
+             <&'a i64 as Add<i64>>
+             <&'a i8 as Add<i8>>
+             <&'a isize as Add<isize>>
+           and 48 others
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/specialization/default-associated-type-bound-1.stderr b/src/test/ui/specialization/default-associated-type-bound-1.stderr
index f88acfb2e793e..af9f2f7c2e9a6 100644
--- a/src/test/ui/specialization/default-associated-type-bound-1.stderr
+++ b/src/test/ui/specialization/default-associated-type-bound-1.stderr
@@ -14,8 +14,7 @@ error[E0277]: the trait bound `str: Clone` is not satisfied
 LL |     default type U = str;
    |                      ^^^ the trait `Clone` is not implemented for `str`
    |
-   = help: the following implementations were found:
-             <String as Clone>
+   = help: the trait `Clone` is implemented for `String`
 note: required by a bound in `X::U`
   --> $DIR/default-associated-type-bound-1.rs:8:13
    |
diff --git a/src/test/ui/str/str-idx.stderr b/src/test/ui/str/str-idx.stderr
index 9ab409bbdcdf8..45450788b9cbd 100644
--- a/src/test/ui/str/str-idx.stderr
+++ b/src/test/ui/str/str-idx.stderr
@@ -7,6 +7,7 @@ LL |     let _: u8 = s[4];
    = help: the trait `SliceIndex<str>` is not implemented for `{integer}`
    = note: you can use `.chars().nth()` or `.bytes().nth()`
            for more information, see chapter 8 in The Book: <https://doc.rust-lang.org/book/ch08-02-strings.html#indexing-into-strings>
+   = help: the trait `SliceIndex<[T]>` is implemented for `usize`
    = note: required because of the requirements on the impl of `Index<{integer}>` for `str`
 
 error[E0277]: the type `str` cannot be indexed by `{integer}`
@@ -20,6 +21,7 @@ LL |     let _ = s.get(4);
    = help: the trait `SliceIndex<str>` is not implemented for `{integer}`
    = note: you can use `.chars().nth()` or `.bytes().nth()`
            for more information, see chapter 8 in The Book: <https://doc.rust-lang.org/book/ch08-02-strings.html#indexing-into-strings>
+   = help: the trait `SliceIndex<[T]>` is implemented for `usize`
 note: required by a bound in `core::str::<impl str>::get`
   --> $SRC_DIR/core/src/str/mod.rs:LL:COL
    |
@@ -37,6 +39,7 @@ LL |     let _ = s.get_unchecked(4);
    = help: the trait `SliceIndex<str>` is not implemented for `{integer}`
    = note: you can use `.chars().nth()` or `.bytes().nth()`
            for more information, see chapter 8 in The Book: <https://doc.rust-lang.org/book/ch08-02-strings.html#indexing-into-strings>
+   = help: the trait `SliceIndex<[T]>` is implemented for `usize`
 note: required by a bound in `core::str::<impl str>::get_unchecked`
   --> $SRC_DIR/core/src/str/mod.rs:LL:COL
    |
diff --git a/src/test/ui/str/str-mut-idx.stderr b/src/test/ui/str/str-mut-idx.stderr
index 5956e363b0c09..9ae085630279e 100644
--- a/src/test/ui/str/str-mut-idx.stderr
+++ b/src/test/ui/str/str-mut-idx.stderr
@@ -31,6 +31,7 @@ LL |     s[1usize] = bot();
    |     ^^^^^^^^^ string indices are ranges of `usize`
    |
    = help: the trait `SliceIndex<str>` is not implemented for `usize`
+   = help: the trait `SliceIndex<[T]>` is implemented for `usize`
    = note: required because of the requirements on the impl of `Index<usize>` for `str`
 
 error[E0277]: the type `str` cannot be indexed by `{integer}`
@@ -44,6 +45,7 @@ LL |     s.get_mut(1);
    = help: the trait `SliceIndex<str>` is not implemented for `{integer}`
    = note: you can use `.chars().nth()` or `.bytes().nth()`
            for more information, see chapter 8 in The Book: <https://doc.rust-lang.org/book/ch08-02-strings.html#indexing-into-strings>
+   = help: the trait `SliceIndex<[T]>` is implemented for `usize`
 note: required by a bound in `core::str::<impl str>::get_mut`
   --> $SRC_DIR/core/src/str/mod.rs:LL:COL
    |
@@ -61,6 +63,7 @@ LL |     s.get_unchecked_mut(1);
    = help: the trait `SliceIndex<str>` is not implemented for `{integer}`
    = note: you can use `.chars().nth()` or `.bytes().nth()`
            for more information, see chapter 8 in The Book: <https://doc.rust-lang.org/book/ch08-02-strings.html#indexing-into-strings>
+   = help: the trait `SliceIndex<[T]>` is implemented for `usize`
 note: required by a bound in `core::str::<impl str>::get_unchecked_mut`
   --> $SRC_DIR/core/src/str/mod.rs:LL:COL
    |
diff --git a/src/test/ui/suggestions/imm-ref-trait-object-literal-bound-regions.stderr b/src/test/ui/suggestions/imm-ref-trait-object-literal-bound-regions.stderr
index 64a62524653b7..0783f04dc9bd3 100644
--- a/src/test/ui/suggestions/imm-ref-trait-object-literal-bound-regions.stderr
+++ b/src/test/ui/suggestions/imm-ref-trait-object-literal-bound-regions.stderr
@@ -4,8 +4,7 @@ error[E0277]: the trait bound `for<'b> &'b S: Trait` is not satisfied
 LL |     foo::<S>(s);
    |     ^^^^^^^^ the trait `for<'b> Trait` is not implemented for `&'b S`
    |
-   = help: the following implementations were found:
-             <&'a mut S as Trait>
+   = help: the trait `Trait` is implemented for `&'a mut S`
 note: required by a bound in `foo`
   --> $DIR/imm-ref-trait-object-literal-bound-regions.rs:11:20
    |
diff --git a/src/test/ui/suggestions/imm-ref-trait-object-literal.stderr b/src/test/ui/suggestions/imm-ref-trait-object-literal.stderr
index 001af27b2484e..5f3f62a7b7570 100644
--- a/src/test/ui/suggestions/imm-ref-trait-object-literal.stderr
+++ b/src/test/ui/suggestions/imm-ref-trait-object-literal.stderr
@@ -6,8 +6,7 @@ LL |   foo(&s);
    |   |
    |   required by a bound introduced by this call
    |
-   = help: the following implementations were found:
-             <&'a mut S as Trait>
+   = help: the trait `Trait` is implemented for `&'a mut S`
 note: required by a bound in `foo`
   --> $DIR/imm-ref-trait-object-literal.rs:7:11
    |
diff --git a/src/test/ui/suggestions/impl-trait-return-trailing-semicolon.stderr b/src/test/ui/suggestions/impl-trait-return-trailing-semicolon.stderr
index ba6967e78e163..0de765588e5b7 100644
--- a/src/test/ui/suggestions/impl-trait-return-trailing-semicolon.stderr
+++ b/src/test/ui/suggestions/impl-trait-return-trailing-semicolon.stderr
@@ -15,6 +15,8 @@ LL | |     5;
 LL | |
 LL | | }
    | |_^ the trait `Bar` is not implemented for `()`
+   |
+   = help: the trait `Bar` is implemented for `u8`
 
 error: aborting due to 2 previous errors
 
diff --git a/src/test/ui/suggestions/into-str.stderr b/src/test/ui/suggestions/into-str.stderr
index 470c0bfcf73e5..8ae5c84794734 100644
--- a/src/test/ui/suggestions/into-str.stderr
+++ b/src/test/ui/suggestions/into-str.stderr
@@ -7,12 +7,13 @@ LL |     foo(String::new());
    |     required by a bound introduced by this call
    |
    = note: to coerce a `String` into a `&str`, use `&*` as a prefix
-   = help: the following implementations were found:
+   = help: the following other types implement trait `From<T>`:
              <String as From<&String>>
              <String as From<&mut str>>
              <String as From<&str>>
              <String as From<Box<str>>>
-           and 2 others
+             <String as From<Cow<'a, str>>>
+             <String as From<char>>
    = note: required because of the requirements on the impl of `Into<&str>` for `String`
 note: required by a bound in `foo`
   --> $DIR/into-str.rs:1:31
diff --git a/src/test/ui/suggestions/issue-71394-no-from-impl.stderr b/src/test/ui/suggestions/issue-71394-no-from-impl.stderr
index 7972437771399..383f40d47fa44 100644
--- a/src/test/ui/suggestions/issue-71394-no-from-impl.stderr
+++ b/src/test/ui/suggestions/issue-71394-no-from-impl.stderr
@@ -4,7 +4,7 @@ error[E0277]: the trait bound `&[i8]: From<&[u8]>` is not satisfied
 LL |     let _: &[i8] = data.into();
    |                         ^^^^ the trait `From<&[u8]>` is not implemented for `&[i8]`
    |
-   = help: the following implementations were found:
+   = help: the following other types implement trait `From<T>`:
              <[T; LANES] as From<Simd<T, LANES>>>
              <[bool; LANES] as From<Mask<T, LANES>>>
    = note: required because of the requirements on the impl of `Into<&[i8]>` for `&[u8]`
diff --git a/src/test/ui/suggestions/issue-84973-negative.stderr b/src/test/ui/suggestions/issue-84973-negative.stderr
index bacab64e2642c..15559d4ae2c2f 100644
--- a/src/test/ui/suggestions/issue-84973-negative.stderr
+++ b/src/test/ui/suggestions/issue-84973-negative.stderr
@@ -6,8 +6,7 @@ LL |     bar(a);
    |     |
    |     required by a bound introduced by this call
    |
-   = help: the following implementations were found:
-             <&f32 as Tr>
+   = help: the trait `Tr` is implemented for `&f32`
 note: required by a bound in `bar`
   --> $DIR/issue-84973-negative.rs:5:11
    |
diff --git a/src/test/ui/traits/associated_type_bound/check-trait-object-bounds-1.stderr b/src/test/ui/traits/associated_type_bound/check-trait-object-bounds-1.stderr
index 907a1bd75a064..bfbbe7fd25702 100644
--- a/src/test/ui/traits/associated_type_bound/check-trait-object-bounds-1.stderr
+++ b/src/test/ui/traits/associated_type_bound/check-trait-object-bounds-1.stderr
@@ -4,8 +4,7 @@ error[E0277]: the trait bound `str: Clone` is not satisfied
 LL |     f::<dyn X<Y = str>>();
    |     ^^^^^^^^^^^^^^^^^^^ the trait `Clone` is not implemented for `str`
    |
-   = help: the following implementations were found:
-             <String as Clone>
+   = help: the trait `Clone` is implemented for `String`
 note: required by a bound in `f`
   --> $DIR/check-trait-object-bounds-1.rs:7:9
    |
diff --git a/src/test/ui/traits/associated_type_bound/check-trait-object-bounds-4.stderr b/src/test/ui/traits/associated_type_bound/check-trait-object-bounds-4.stderr
index b27f8d791a50f..3ca36d5d2ff19 100644
--- a/src/test/ui/traits/associated_type_bound/check-trait-object-bounds-4.stderr
+++ b/src/test/ui/traits/associated_type_bound/check-trait-object-bounds-4.stderr
@@ -4,8 +4,7 @@ error[E0277]: the trait bound `str: Clone` is not satisfied
 LL |     f::<dyn X<Y = str>>();
    |     ^^^^^^^^^^^^^^^^^^^ the trait `Clone` is not implemented for `str`
    |
-   = help: the following implementations were found:
-             <String as Clone>
+   = help: the trait `Clone` is implemented for `String`
 note: required by a bound in `f`
   --> $DIR/check-trait-object-bounds-4.rs:10:9
    |
diff --git a/src/test/ui/traits/bound/assoc-fn-bound-root-obligation.rs b/src/test/ui/traits/bound/assoc-fn-bound-root-obligation.rs
new file mode 100644
index 0000000000000..1d234518056fd
--- /dev/null
+++ b/src/test/ui/traits/bound/assoc-fn-bound-root-obligation.rs
@@ -0,0 +1,12 @@
+fn strip_lf(s: &str) -> &str {
+    s.strip_suffix(b'\n').unwrap_or(s)
+    //~^ ERROR expected a `FnMut<(char,)>` closure, found `u8`
+    //~| NOTE expected an `FnMut<(char,)>` closure, found `u8`
+    //~| NOTE required by a bound introduced by this call
+    //~| HELP the trait `FnMut<(char,)>` is not implemented for `u8`
+    //~| HELP the following other types implement trait `Pattern<'a>`:
+    //~| NOTE required because of the requirements on the impl of `Pattern<'_>` for `u8`
+
+}
+
+fn main() {}
diff --git a/src/test/ui/traits/bound/assoc-fn-bound-root-obligation.stderr b/src/test/ui/traits/bound/assoc-fn-bound-root-obligation.stderr
new file mode 100644
index 0000000000000..115539a6dc28d
--- /dev/null
+++ b/src/test/ui/traits/bound/assoc-fn-bound-root-obligation.stderr
@@ -0,0 +1,23 @@
+error[E0277]: expected a `FnMut<(char,)>` closure, found `u8`
+  --> $DIR/assoc-fn-bound-root-obligation.rs:2:20
+   |
+LL |     s.strip_suffix(b'\n').unwrap_or(s)
+   |       ------------ ^^^^^ expected an `FnMut<(char,)>` closure, found `u8`
+   |       |
+   |       required by a bound introduced by this call
+   |
+   = help: the trait `FnMut<(char,)>` is not implemented for `u8`
+   = help: the following other types implement trait `Pattern<'a>`:
+             &'b String
+             &'b [char; N]
+             &'b [char]
+             &'b str
+             &'c &'b str
+             [char; N]
+             char
+             pattern::MultiCharEqPattern<C>
+   = note: required because of the requirements on the impl of `Pattern<'_>` for `u8`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/src/test/ui/traits/bound/same-crate-name.rs b/src/test/ui/traits/bound/same-crate-name.rs
index 1012edb109336..8d646a414599c 100644
--- a/src/test/ui/traits/bound/same-crate-name.rs
+++ b/src/test/ui/traits/bound/same-crate-name.rs
@@ -50,6 +50,6 @@ fn main() {
         // impls for the correct trait where the path is not misleading.
         a::try_foo(other_variant_implements_correct_trait);
         //~^ ERROR E0277
-        //~| the following implementations were found:
+        //~| the trait `main::a::Bar` is implemented for `ImplementsTraitForUsize<usize>`
     }
 }
diff --git a/src/test/ui/traits/bound/same-crate-name.stderr b/src/test/ui/traits/bound/same-crate-name.stderr
index 81e5589d6eb35..ef39a70066db6 100644
--- a/src/test/ui/traits/bound/same-crate-name.stderr
+++ b/src/test/ui/traits/bound/same-crate-name.stderr
@@ -12,6 +12,7 @@ help: trait impl with same name found
 LL | impl Bar for Foo {}
    | ^^^^^^^^^^^^^^^^^^^
    = note: perhaps two different versions of crate `crate_a2` are being used?
+   = help: the trait `main::a::Bar` is implemented for `ImplementsTraitForUsize<usize>`
 note: required by a bound in `try_foo`
   --> $DIR/auxiliary/crate_a1.rs:3:24
    |
@@ -26,6 +27,7 @@ LL |         a::try_foo(implements_no_traits);
    |         |
    |         required by a bound introduced by this call
    |
+   = help: the trait `main::a::Bar` is implemented for `ImplementsTraitForUsize<usize>`
 note: required by a bound in `try_foo`
   --> $DIR/auxiliary/crate_a1.rs:3:24
    |
@@ -46,6 +48,7 @@ help: trait impl with same name found
 LL | impl Bar for ImplementsWrongTraitConditionally<isize> {}
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    = note: perhaps two different versions of crate `crate_a2` are being used?
+   = help: the trait `main::a::Bar` is implemented for `ImplementsTraitForUsize<usize>`
 note: required by a bound in `try_foo`
   --> $DIR/auxiliary/crate_a1.rs:3:24
    |
@@ -60,8 +63,7 @@ LL |         a::try_foo(other_variant_implements_correct_trait);
    |         |
    |         required by a bound introduced by this call
    |
-   = help: the following implementations were found:
-             <ImplementsTraitForUsize<usize> as main::a::Bar>
+   = help: the trait `main::a::Bar` is implemented for `ImplementsTraitForUsize<usize>`
 note: required by a bound in `try_foo`
   --> $DIR/auxiliary/crate_a1.rs:3:24
    |
diff --git a/src/test/ui/traits/coercion-generic-bad.stderr b/src/test/ui/traits/coercion-generic-bad.stderr
index f367d396da0c2..b213ee635df59 100644
--- a/src/test/ui/traits/coercion-generic-bad.stderr
+++ b/src/test/ui/traits/coercion-generic-bad.stderr
@@ -4,8 +4,7 @@ error[E0277]: the trait bound `Struct: Trait<isize>` is not satisfied
 LL |     let s: Box<dyn Trait<isize>> = Box::new(Struct { person: "Fred" });
    |                                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Trait<isize>` is not implemented for `Struct`
    |
-   = help: the following implementations were found:
-             <Struct as Trait<&'static str>>
+   = help: the trait `Trait<&'static str>` is implemented for `Struct`
    = note: required for the cast to the object type `dyn Trait<isize>`
 
 error: aborting due to previous error
diff --git a/src/test/ui/traits/inheritance/repeated-supertrait-ambig.stderr b/src/test/ui/traits/inheritance/repeated-supertrait-ambig.stderr
index 2bff84363e9e5..cab0ccdf710da 100644
--- a/src/test/ui/traits/inheritance/repeated-supertrait-ambig.stderr
+++ b/src/test/ui/traits/inheritance/repeated-supertrait-ambig.stderr
@@ -3,6 +3,10 @@ error[E0277]: the trait bound `dyn CompareToInts: CompareTo<i32>` is not satisfi
    |
 LL |     c.same_as(22)
    |       ^^^^^^^ the trait `CompareTo<i32>` is not implemented for `dyn CompareToInts`
+   |
+   = help: the following other types implement trait `CompareTo<T>`:
+             <i64 as CompareTo<i64>>
+             <i64 as CompareTo<u64>>
 
 error[E0277]: the trait bound `C: CompareTo<i32>` is not satisfied
   --> $DIR/repeated-supertrait-ambig.rs:30:7
@@ -20,6 +24,10 @@ error[E0277]: the trait bound `dyn CompareToInts: CompareTo<i32>` is not satisfi
    |
 LL |     <dyn CompareToInts>::same_as(c, 22)
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `CompareTo<i32>` is not implemented for `dyn CompareToInts`
+   |
+   = help: the following other types implement trait `CompareTo<T>`:
+             <i64 as CompareTo<i64>>
+             <i64 as CompareTo<u64>>
 
 error[E0277]: the trait bound `C: CompareTo<i32>` is not satisfied
   --> $DIR/repeated-supertrait-ambig.rs:38:5
@@ -38,7 +46,7 @@ error[E0277]: the trait bound `i64: CompareTo<i32>` is not satisfied
 LL |     assert_eq!(22_i64.same_as(22), true);
    |                       ^^^^^^^ the trait `CompareTo<i32>` is not implemented for `i64`
    |
-   = help: the following implementations were found:
+   = help: the following other types implement trait `CompareTo<T>`:
              <i64 as CompareTo<i64>>
              <i64 as CompareTo<u64>>
 
diff --git a/src/test/ui/traits/issue-79458.stderr b/src/test/ui/traits/issue-79458.stderr
index b970012837313..cf2e4edf9f0a9 100644
--- a/src/test/ui/traits/issue-79458.stderr
+++ b/src/test/ui/traits/issue-79458.stderr
@@ -7,10 +7,10 @@ LL | struct Foo<'a, T> {
 LL |     bar: &'a mut T
    |     ^^^^^^^^^^^^^^ the trait `Clone` is not implemented for `&mut T`
    |
-   = help: the following implementations were found:
-             <&T as Clone>
-             <*const T as Clone>
-             <*mut T as Clone>
+   = help: the following other types implement trait `Clone`:
+             &T
+             *const T
+             *mut T
    = note: `Clone` is implemented for `&T`, but not for `&mut T`
    = note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info)
 
diff --git a/src/test/ui/traits/issue-91594.stderr b/src/test/ui/traits/issue-91594.stderr
index 10298a0c7037a..f2b3de13beb9e 100644
--- a/src/test/ui/traits/issue-91594.stderr
+++ b/src/test/ui/traits/issue-91594.stderr
@@ -4,8 +4,7 @@ error[E0277]: the trait bound `Foo: HasComponent<()>` is not satisfied
 LL | impl HasComponent<<Foo as Component<Foo>>::Interface> for Foo {}
    |      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `HasComponent<()>` is not implemented for `Foo`
    |
-   = help: the following implementations were found:
-             <Foo as HasComponent<<Foo as Component<Foo>>::Interface>>
+   = help: the trait `HasComponent<<Foo as Component<Foo>>::Interface>` is implemented for `Foo`
 note: required because of the requirements on the impl of `Component<Foo>` for `Foo`
   --> $DIR/issue-91594.rs:13:27
    |
diff --git a/src/test/ui/traits/map-types.stderr b/src/test/ui/traits/map-types.stderr
index 71006e1f4e2d8..a4686edb71757 100644
--- a/src/test/ui/traits/map-types.stderr
+++ b/src/test/ui/traits/map-types.stderr
@@ -4,6 +4,7 @@ error[E0277]: the trait bound `Box<dyn Map<isize, isize>>: Map<usize, isize>` is
 LL |     let y: Box<dyn Map<usize, isize>> = Box::new(x);
    |                                         ^^^^^^^^^^^ the trait `Map<usize, isize>` is not implemented for `Box<dyn Map<isize, isize>>`
    |
+   = help: the trait `Map<K, V>` is implemented for `HashMap<K, V>`
    = note: required for the cast to the object type `dyn Map<usize, isize>`
 
 error: aborting due to previous error
diff --git a/src/test/ui/traits/reservation-impl/no-use.stderr b/src/test/ui/traits/reservation-impl/no-use.stderr
index 3d5bf3448bd5d..cefb2a8792f17 100644
--- a/src/test/ui/traits/reservation-impl/no-use.stderr
+++ b/src/test/ui/traits/reservation-impl/no-use.stderr
@@ -6,8 +6,7 @@ LL |     <() as MyTrait>::foo(&());
    |     |
    |     required by a bound introduced by this call
    |
-   = help: the following implementations were found:
-             <() as MyTrait>
+   = help: the trait `MyTrait` is implemented for `()`
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/traits/suggest-deferences/issue-39029.stderr b/src/test/ui/traits/suggest-deferences/issue-39029.stderr
index 2c225f4311d01..5c324cd38a3ab 100644
--- a/src/test/ui/traits/suggest-deferences/issue-39029.stderr
+++ b/src/test/ui/traits/suggest-deferences/issue-39029.stderr
@@ -5,7 +5,7 @@ LL |     let _errors = TcpListener::bind(&bad);
    |                   ----------------- ^^^^
    |                   |                 |
    |                   |                 the trait `ToSocketAddrs` is not implemented for `NoToSocketAddrs`
-   |                   |                 help: consider adding dereference here: `&*bad`
+   |                   |                 help: consider dereferencing here: `&*bad`
    |                   required by a bound introduced by this call
    |
    = note: required because of the requirements on the impl of `ToSocketAddrs` for `&NoToSocketAddrs`
diff --git a/src/test/ui/traits/suggest-deferences/issue-62530.stderr b/src/test/ui/traits/suggest-deferences/issue-62530.stderr
index 299219431ef11..d129328dae8ef 100644
--- a/src/test/ui/traits/suggest-deferences/issue-62530.stderr
+++ b/src/test/ui/traits/suggest-deferences/issue-62530.stderr
@@ -5,11 +5,9 @@ LL |     takes_type_parameter(&string);  // Error
    |     -------------------- ^^^^^^^
    |     |                    |
    |     |                    the trait `SomeTrait` is not implemented for `&String`
-   |     |                    help: consider adding dereference here: `&*string`
+   |     |                    help: consider dereferencing here: `&*string`
    |     required by a bound introduced by this call
    |
-   = help: the following implementations were found:
-             <&str as SomeTrait>
 note: required by a bound in `takes_type_parameter`
   --> $DIR/issue-62530.rs:4:44
    |
diff --git a/src/test/ui/traits/suggest-deferences/multiple-0.stderr b/src/test/ui/traits/suggest-deferences/multiple-0.stderr
index bf9f85f1b4595..efb3c7d123f70 100644
--- a/src/test/ui/traits/suggest-deferences/multiple-0.stderr
+++ b/src/test/ui/traits/suggest-deferences/multiple-0.stderr
@@ -5,7 +5,7 @@ LL |     foo(&baz);
    |     --- ^^^^
    |     |   |
    |     |   the trait `Happy` is not implemented for `&Baz`
-   |     |   help: consider adding dereference here: `&***baz`
+   |     |   help: consider dereferencing here: `&***baz`
    |     required by a bound introduced by this call
    |
 note: required by a bound in `foo`
diff --git a/src/test/ui/traits/suggest-deferences/multiple-1.stderr b/src/test/ui/traits/suggest-deferences/multiple-1.stderr
index 040fbb3e3e699..6e12321c2333b 100644
--- a/src/test/ui/traits/suggest-deferences/multiple-1.stderr
+++ b/src/test/ui/traits/suggest-deferences/multiple-1.stderr
@@ -6,6 +6,7 @@ LL |     foo(&mut baz);
    |     |
    |     required by a bound introduced by this call
    |
+   = help: the trait `Happy` is implemented for `&mut LDM`
 note: required by a bound in `foo`
   --> $DIR/multiple-1.rs:45:26
    |
diff --git a/src/test/ui/traits/suggest-deferences/root-obligation.fixed b/src/test/ui/traits/suggest-deferences/root-obligation.fixed
new file mode 100644
index 0000000000000..7a8433f90572e
--- /dev/null
+++ b/src/test/ui/traits/suggest-deferences/root-obligation.fixed
@@ -0,0 +1,13 @@
+// run-rustfix
+
+fn get_vowel_count(string: &str) -> usize {
+    string
+        .chars()
+        .filter(|c| "aeiou".contains(*c))
+        //~^ ERROR expected a `Fn<(char,)>` closure, found `char`
+        .count()
+}
+
+fn main() {
+    let _ = get_vowel_count("asdf");
+}
diff --git a/src/test/ui/traits/suggest-deferences/root-obligation.rs b/src/test/ui/traits/suggest-deferences/root-obligation.rs
new file mode 100644
index 0000000000000..51bac2107e3b4
--- /dev/null
+++ b/src/test/ui/traits/suggest-deferences/root-obligation.rs
@@ -0,0 +1,13 @@
+// run-rustfix
+
+fn get_vowel_count(string: &str) -> usize {
+    string
+        .chars()
+        .filter(|c| "aeiou".contains(c))
+        //~^ ERROR expected a `Fn<(char,)>` closure, found `char`
+        .count()
+}
+
+fn main() {
+    let _ = get_vowel_count("asdf");
+}
diff --git a/src/test/ui/traits/suggest-deferences/root-obligation.stderr b/src/test/ui/traits/suggest-deferences/root-obligation.stderr
new file mode 100644
index 0000000000000..16e03e79c7571
--- /dev/null
+++ b/src/test/ui/traits/suggest-deferences/root-obligation.stderr
@@ -0,0 +1,24 @@
+error[E0277]: expected a `Fn<(char,)>` closure, found `char`
+  --> $DIR/root-obligation.rs:6:38
+   |
+LL |         .filter(|c| "aeiou".contains(c))
+   |                             -------- ^ expected an `Fn<(char,)>` closure, found `char`
+   |                             |
+   |                             required by a bound introduced by this call
+   |
+   = help: the trait `Fn<(char,)>` is not implemented for `char`
+   = note: required because of the requirements on the impl of `FnOnce<(char,)>` for `&char`
+   = note: required because of the requirements on the impl of `Pattern<'_>` for `&char`
+note: required by a bound in `core::str::<impl str>::contains`
+  --> $SRC_DIR/core/src/str/mod.rs:LL:COL
+   |
+LL |     pub fn contains<'a, P: Pattern<'a>>(&'a self, pat: P) -> bool {
+   |                            ^^^^^^^^^^^ required by this bound in `core::str::<impl str>::contains`
+help: consider dereferencing here
+   |
+LL |         .filter(|c| "aeiou".contains(*c))
+   |                                      +
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/src/test/ui/try-block/try-block-bad-type.stderr b/src/test/ui/try-block/try-block-bad-type.stderr
index cf3a07808de68..f9906713f8427 100644
--- a/src/test/ui/try-block/try-block-bad-type.stderr
+++ b/src/test/ui/try-block/try-block-bad-type.stderr
@@ -5,8 +5,7 @@ LL |         Err("")?;
    |                ^ the trait `From<&str>` is not implemented for `TryFromSliceError`
    |
    = note: the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait
-   = help: the following implementations were found:
-             <TryFromSliceError as From<Infallible>>
+   = help: the trait `From<Infallible>` is implemented for `TryFromSliceError`
    = note: required because of the requirements on the impl of `FromResidual<Result<Infallible, &str>>` for `Result<u32, TryFromSliceError>`
 
 error[E0271]: type mismatch resolving `<Result<i32, i32> as Try>::Output == &str`
diff --git a/src/test/ui/try-trait/bad-interconversion.stderr b/src/test/ui/try-trait/bad-interconversion.stderr
index 171051156b7f2..1a4105231dc75 100644
--- a/src/test/ui/try-trait/bad-interconversion.stderr
+++ b/src/test/ui/try-trait/bad-interconversion.stderr
@@ -7,12 +7,16 @@ LL |     Ok(Err(123_i32)?)
    |                    ^ the trait `From<i32>` is not implemented for `u8`
    |
    = note: the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait
-   = help: the following implementations were found:
-             <u8 as From<NonZeroU8>>
-             <u8 as From<bool>>
+   = help: the following other types implement trait `From<T>`:
              <f32 as From<i16>>
              <f32 as From<i8>>
-           and 71 others
+             <f32 as From<u16>>
+             <f32 as From<u8>>
+             <f64 as From<f32>>
+             <f64 as From<i16>>
+             <f64 as From<i32>>
+             <f64 as From<i8>>
+           and 67 others
    = note: required because of the requirements on the impl of `FromResidual<Result<Infallible, i32>>` for `Result<u64, u8>`
 
 error[E0277]: the `?` operator can only be used on `Result`s, not `Option`s, in a function that returns `Result`
@@ -27,6 +31,7 @@ LL | | }
    | |_- this function returns a `Result`
    |
    = help: the trait `FromResidual<Option<Infallible>>` is not implemented for `Result<u64, String>`
+   = help: the trait `FromResidual<Result<Infallible, E>>` is implemented for `Result<T, F>`
 
 error[E0277]: the `?` operator can only be used on `Result`s in a function that returns `Result`
   --> $DIR/bad-interconversion.rs:17:31
@@ -39,6 +44,7 @@ LL | | }
    | |_- this function returns a `Result`
    |
    = help: the trait `FromResidual<ControlFlow<{integer}, Infallible>>` is not implemented for `Result<u64, String>`
+   = help: the trait `FromResidual<Result<Infallible, E>>` is implemented for `Result<T, F>`
 
 error[E0277]: the `?` operator can only be used on `Option`s, not `Result`s, in a function that returns `Option`
   --> $DIR/bad-interconversion.rs:22:22
@@ -51,6 +57,7 @@ LL | | }
    | |_- this function returns an `Option`
    |
    = help: the trait `FromResidual<Result<Infallible, &str>>` is not implemented for `Option<u16>`
+   = help: the trait `FromResidual` is implemented for `Option<T>`
 
 error[E0277]: the `?` operator can only be used on `Option`s in a function that returns `Option`
   --> $DIR/bad-interconversion.rs:27:33
@@ -63,6 +70,7 @@ LL | | }
    | |_- this function returns an `Option`
    |
    = help: the trait `FromResidual<ControlFlow<{integer}, Infallible>>` is not implemented for `Option<u64>`
+   = help: the trait `FromResidual` is implemented for `Option<T>`
 
 error[E0277]: the `?` operator can only be used on `ControlFlow`s in a function that returns `ControlFlow`
   --> $DIR/bad-interconversion.rs:32:39
@@ -75,6 +83,7 @@ LL | | }
    | |_- this function returns a `ControlFlow`
    |
    = help: the trait `FromResidual<Result<Infallible, &str>>` is not implemented for `ControlFlow<String>`
+   = help: the trait `FromResidual` is implemented for `ControlFlow<B, C>`
 
 error[E0277]: the `?` operator can only be used on `ControlFlow`s in a function that returns `ControlFlow`
   --> $DIR/bad-interconversion.rs:37:12
@@ -88,6 +97,7 @@ LL | | }
    | |_- this function returns a `ControlFlow`
    |
    = help: the trait `FromResidual<Option<Infallible>>` is not implemented for `ControlFlow<u64>`
+   = help: the trait `FromResidual` is implemented for `ControlFlow<B, C>`
 
 error[E0277]: the `?` operator in a function that returns `ControlFlow<B, _>` can only be used on other `ControlFlow<B, _>`s (with the same Break type)
   --> $DIR/bad-interconversion.rs:43:29
@@ -102,6 +112,7 @@ LL | | }
    |
    = help: the trait `FromResidual<ControlFlow<u8, Infallible>>` is not implemented for `ControlFlow<i64>`
    = note: unlike `Result`, there's no `From`-conversion performed for `ControlFlow`
+   = help: the trait `FromResidual` is implemented for `ControlFlow<B, C>`
 
 error: aborting due to 8 previous errors
 
diff --git a/src/test/ui/try-trait/option-to-result.stderr b/src/test/ui/try-trait/option-to-result.stderr
index aadfbf61f28f8..b0e4de8cb4bf5 100644
--- a/src/test/ui/try-trait/option-to-result.stderr
+++ b/src/test/ui/try-trait/option-to-result.stderr
@@ -10,6 +10,7 @@ LL | | }
    | |_- this function returns a `Result`
    |
    = help: the trait `FromResidual<Option<Infallible>>` is not implemented for `Result<(), ()>`
+   = help: the trait `FromResidual<Result<Infallible, E>>` is implemented for `Result<T, F>`
 
 error[E0277]: the `?` operator can only be used on `Option`s, not `Result`s, in a function that returns `Option`
   --> $DIR/option-to-result.rs:11:6
@@ -23,6 +24,7 @@ LL | | }
    | |_- this function returns an `Option`
    |
    = help: the trait `FromResidual<Result<Infallible, i32>>` is not implemented for `Option<i32>`
+   = help: the trait `FromResidual` is implemented for `Option<T>`
 
 error: aborting due to 2 previous errors
 
diff --git a/src/test/ui/try-trait/try-on-option.stderr b/src/test/ui/try-trait/try-on-option.stderr
index 24db9f5243703..7b2a9a16f900b 100644
--- a/src/test/ui/try-trait/try-on-option.stderr
+++ b/src/test/ui/try-trait/try-on-option.stderr
@@ -10,6 +10,7 @@ LL | | }
    | |_- this function returns a `Result`
    |
    = help: the trait `FromResidual<Option<Infallible>>` is not implemented for `Result<u32, ()>`
+   = help: the trait `FromResidual<Result<Infallible, E>>` is implemented for `Result<T, F>`
 
 error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `FromResidual`)
   --> $DIR/try-on-option.rs:11:6
diff --git a/src/test/ui/type-alias-impl-trait/issue-60371.stderr b/src/test/ui/type-alias-impl-trait/issue-60371.stderr
index 5fec078956bfc..d0c04371bd793 100644
--- a/src/test/ui/type-alias-impl-trait/issue-60371.stderr
+++ b/src/test/ui/type-alias-impl-trait/issue-60371.stderr
@@ -13,8 +13,7 @@ error[E0277]: the trait bound `(): Bug` is not satisfied
 LL |     const FUN: fn() -> Self::Item = || ();
    |                                        ^^ the trait `Bug` is not implemented for `()`
    |
-   = help: the following implementations were found:
-             <&() as Bug>
+   = help: the trait `Bug` is implemented for `&()`
 
 error: aborting due to 2 previous errors
 
diff --git a/src/test/ui/type-alias-impl-trait/nested-tait-inference.stderr b/src/test/ui/type-alias-impl-trait/nested-tait-inference.stderr
index eb72e88769100..0df2b57d373b6 100644
--- a/src/test/ui/type-alias-impl-trait/nested-tait-inference.stderr
+++ b/src/test/ui/type-alias-impl-trait/nested-tait-inference.stderr
@@ -4,8 +4,7 @@ error[E0277]: the trait bound `(): Foo<FooX>` is not satisfied
 LL | fn foo() -> impl Foo<FooX> {
    |             ^^^^^^^^^^^^^^ the trait `Foo<FooX>` is not implemented for `()`
    |
-   = help: the following implementations were found:
-             <() as Foo<()>>
+   = help: the trait `Foo<()>` is implemented for `()`
 
 error[E0277]: the trait bound `(): Foo<FooX>` is not satisfied
   --> $DIR/nested-tait-inference.rs:12:28
@@ -19,8 +18,7 @@ LL | |     ()
 LL | | }
    | |_^ the trait `Foo<FooX>` is not implemented for `()`
    |
-   = help: the following implementations were found:
-             <() as Foo<()>>
+   = help: the trait `Foo<()>` is implemented for `()`
 
 error: aborting due to 2 previous errors
 
diff --git a/src/test/ui/type-alias-impl-trait/nested-tait-inference2.stderr b/src/test/ui/type-alias-impl-trait/nested-tait-inference2.stderr
index 1372a018667c2..264e8024fac3a 100644
--- a/src/test/ui/type-alias-impl-trait/nested-tait-inference2.stderr
+++ b/src/test/ui/type-alias-impl-trait/nested-tait-inference2.stderr
@@ -4,7 +4,7 @@ error[E0277]: the trait bound `(): Foo<FooX>` is not satisfied
 LL | fn foo() -> impl Foo<FooX> {
    |             ^^^^^^^^^^^^^^ the trait `Foo<FooX>` is not implemented for `()`
    |
-   = help: the following implementations were found:
+   = help: the following other types implement trait `Foo<A>`:
              <() as Foo<()>>
              <() as Foo<u32>>
 
@@ -19,7 +19,7 @@ LL | |     ()
 LL | | }
    | |_^ the trait `Foo<FooX>` is not implemented for `()`
    |
-   = help: the following implementations were found:
+   = help: the following other types implement trait `Foo<A>`:
              <() as Foo<()>>
              <() as Foo<u32>>
 
diff --git a/src/test/ui/type-alias-impl-trait/self-referential-2.stderr b/src/test/ui/type-alias-impl-trait/self-referential-2.stderr
index 6997676260dd6..348696f25e3b5 100644
--- a/src/test/ui/type-alias-impl-trait/self-referential-2.stderr
+++ b/src/test/ui/type-alias-impl-trait/self-referential-2.stderr
@@ -5,6 +5,16 @@ LL |     42_i32
    |     ^^^^^^ no implementation for `i32 == Foo`
    |
    = help: the trait `PartialEq<Foo>` is not implemented for `i32`
+   = help: the following other types implement trait `PartialEq<Rhs>`:
+             f32
+             f64
+             i128
+             i16
+             i32
+             i64
+             i8
+             isize
+           and 6 others
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/type-alias-impl-trait/self-referential-4.stderr b/src/test/ui/type-alias-impl-trait/self-referential-4.stderr
index 4a6ee2f9279d1..838264794194c 100644
--- a/src/test/ui/type-alias-impl-trait/self-referential-4.stderr
+++ b/src/test/ui/type-alias-impl-trait/self-referential-4.stderr
@@ -5,6 +5,16 @@ LL |     i
    |     ^ no implementation for `&i32 == Bar<'b, 'static>`
    |
    = help: the trait `PartialEq<Bar<'b, 'static>>` is not implemented for `&i32`
+   = help: the following other types implement trait `PartialEq<Rhs>`:
+             f32
+             f64
+             i128
+             i16
+             i32
+             i64
+             i8
+             isize
+           and 6 others
 
 error[E0277]: can't compare `&i32` with `Foo<'static, 'b>`
   --> $DIR/self-referential-4.rs:12:5
@@ -13,6 +23,16 @@ LL |     i
    |     ^ no implementation for `&i32 == Foo<'static, 'b>`
    |
    = help: the trait `PartialEq<Foo<'static, 'b>>` is not implemented for `&i32`
+   = help: the following other types implement trait `PartialEq<Rhs>`:
+             f32
+             f64
+             i128
+             i16
+             i32
+             i64
+             i8
+             isize
+           and 6 others
 
 error[E0277]: can't compare `&i32` with `Moo<'static, 'a>`
   --> $DIR/self-referential-4.rs:18:5
@@ -21,6 +41,16 @@ LL |     i
    |     ^ no implementation for `&i32 == Moo<'static, 'a>`
    |
    = help: the trait `PartialEq<Moo<'static, 'a>>` is not implemented for `&i32`
+   = help: the following other types implement trait `PartialEq<Rhs>`:
+             f32
+             f64
+             i128
+             i16
+             i32
+             i64
+             i8
+             isize
+           and 6 others
 
 error: aborting due to 3 previous errors
 
diff --git a/src/test/ui/type-alias-impl-trait/self-referential.stderr b/src/test/ui/type-alias-impl-trait/self-referential.stderr
index 0626e6be0d5a5..2ebb15b880308 100644
--- a/src/test/ui/type-alias-impl-trait/self-referential.stderr
+++ b/src/test/ui/type-alias-impl-trait/self-referential.stderr
@@ -5,6 +5,16 @@ LL |     i
    |     ^ no implementation for `&i32 == Bar<'b, 'a>`
    |
    = help: the trait `PartialEq<Bar<'b, 'a>>` is not implemented for `&i32`
+   = help: the following other types implement trait `PartialEq<Rhs>`:
+             f32
+             f64
+             i128
+             i16
+             i32
+             i64
+             i8
+             isize
+           and 6 others
 
 error[E0277]: can't compare `&i32` with `(i32, &i32)`
   --> $DIR/self-referential.rs:12:10
@@ -13,6 +23,16 @@ LL |     (42, i)
    |          ^ no implementation for `&i32 == (i32, &i32)`
    |
    = help: the trait `PartialEq<(i32, &i32)>` is not implemented for `&i32`
+   = help: the following other types implement trait `PartialEq<Rhs>`:
+             f32
+             f64
+             i128
+             i16
+             i32
+             i64
+             i8
+             isize
+           and 6 others
 
 error[E0277]: can't compare `&i32` with `(i32, Moo<'b, 'a>::{opaque#0})`
   --> $DIR/self-referential.rs:18:10
@@ -21,6 +41,16 @@ LL |     (42, i)
    |          ^ no implementation for `&i32 == (i32, Moo<'b, 'a>::{opaque#0})`
    |
    = help: the trait `PartialEq<(i32, Moo<'b, 'a>::{opaque#0})>` is not implemented for `&i32`
+   = help: the following other types implement trait `PartialEq<Rhs>`:
+             f32
+             f64
+             i128
+             i16
+             i32
+             i64
+             i8
+             isize
+           and 6 others
 
 error: aborting due to 3 previous errors
 
diff --git a/src/test/ui/type/type-check-defaults.stderr b/src/test/ui/type/type-check-defaults.stderr
index 2a61547997b63..f292877f30ea2 100644
--- a/src/test/ui/type/type-check-defaults.stderr
+++ b/src/test/ui/type/type-check-defaults.stderr
@@ -65,6 +65,16 @@ LL | trait ProjectionPred<T:Iterator = IntoIter<i32>> where T::Item : Add<u8> {}
    |                                                                  ^^^^^^^ no implementation for `i32 + u8`
    |
    = help: the trait `Add<u8>` is not implemented for `i32`
+   = help: the following other types implement trait `Add<Rhs>`:
+             <&'a f32 as Add<f32>>
+             <&'a f64 as Add<f64>>
+             <&'a i128 as Add<i128>>
+             <&'a i16 as Add<i16>>
+             <&'a i32 as Add<i32>>
+             <&'a i64 as Add<i64>>
+             <&'a i8 as Add<i8>>
+             <&'a isize as Add<isize>>
+           and 48 others
 
 error: aborting due to 7 previous errors
 
diff --git a/src/test/ui/typeck/issue-81293.stderr b/src/test/ui/typeck/issue-81293.stderr
index c545a563b0d0a..9658288ac8b28 100644
--- a/src/test/ui/typeck/issue-81293.stderr
+++ b/src/test/ui/typeck/issue-81293.stderr
@@ -20,6 +20,16 @@ LL |     a = c + b * 5;
    |           ^ no implementation for `usize + u16`
    |
    = help: the trait `Add<u16>` is not implemented for `usize`
+   = help: the following other types implement trait `Add<Rhs>`:
+             <&'a f32 as Add<f32>>
+             <&'a f64 as Add<f64>>
+             <&'a i128 as Add<i128>>
+             <&'a i16 as Add<i16>>
+             <&'a i32 as Add<i32>>
+             <&'a i64 as Add<i64>>
+             <&'a i8 as Add<i8>>
+             <&'a isize as Add<isize>>
+           and 48 others
 
 error: aborting due to 3 previous errors
 
diff --git a/src/test/ui/typeck/issue-90101.stderr b/src/test/ui/typeck/issue-90101.stderr
index 998b636887f23..ab9a72edfe98d 100644
--- a/src/test/ui/typeck/issue-90101.stderr
+++ b/src/test/ui/typeck/issue-90101.stderr
@@ -6,7 +6,7 @@ LL |     func(Path::new("hello").to_path_buf().to_string_lossy(), "world")
    |     |
    |     required by a bound introduced by this call
    |
-   = help: the following implementations were found:
+   = help: the following other types implement trait `From<T>`:
              <PathBuf as From<&T>>
              <PathBuf as From<Box<Path>>>
              <PathBuf as From<Cow<'a, Path>>>
diff --git a/src/test/ui/ufcs/ufcs-qpath-self-mismatch.stderr b/src/test/ui/ufcs/ufcs-qpath-self-mismatch.stderr
index d0bc432a1cd18..74766d9fdd1b1 100644
--- a/src/test/ui/ufcs/ufcs-qpath-self-mismatch.stderr
+++ b/src/test/ui/ufcs/ufcs-qpath-self-mismatch.stderr
@@ -5,6 +5,16 @@ LL |     <i32 as Add<u32>>::add(1, 2);
    |     ^^^^^^^^^^^^^^^^^^^^^^ no implementation for `i32 + u32`
    |
    = help: the trait `Add<u32>` is not implemented for `i32`
+   = help: the following other types implement trait `Add<Rhs>`:
+             <&'a f32 as Add<f32>>
+             <&'a f64 as Add<f64>>
+             <&'a i128 as Add<i128>>
+             <&'a i16 as Add<i16>>
+             <&'a i32 as Add<i32>>
+             <&'a i64 as Add<i64>>
+             <&'a i8 as Add<i8>>
+             <&'a isize as Add<isize>>
+           and 48 others
 
 error[E0308]: mismatched types
   --> $DIR/ufcs-qpath-self-mismatch.rs:6:28
diff --git a/src/test/ui/unevaluated_fixed_size_array_len.stderr b/src/test/ui/unevaluated_fixed_size_array_len.stderr
index 03932d5ed0347..5e67b2c44f2be 100644
--- a/src/test/ui/unevaluated_fixed_size_array_len.stderr
+++ b/src/test/ui/unevaluated_fixed_size_array_len.stderr
@@ -4,8 +4,7 @@ error[E0277]: the trait bound `[(); 0]: Foo` is not satisfied
 LL |     <[(); 0] as Foo>::foo()
    |     ^^^^^^^^^^^^^^^^^^^^^ the trait `Foo` is not implemented for `[(); 0]`
    |
-   = help: the following implementations were found:
-             <[(); 1] as Foo>
+   = help: the trait `Foo` is implemented for `[(); 1]`
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/unsized/box-instead-of-dyn-fn.rs b/src/test/ui/unsized/box-instead-of-dyn-fn.rs
new file mode 100644
index 0000000000000..2fa741bc1c50b
--- /dev/null
+++ b/src/test/ui/unsized/box-instead-of-dyn-fn.rs
@@ -0,0 +1,15 @@
+use std::fmt::Debug;
+
+// Test to suggest boxing the return type, and the closure branch of the `if`
+
+fn print_on_or_the_other<'a>(a: i32, b: &'a String) -> dyn Fn() + 'a {
+    //~^ ERROR return type cannot have an unboxed trait object
+    if a % 2 == 0 {
+        move || println!("{a}")
+    } else {
+        Box::new(move || println!("{}", b))
+        //~^ ERROR `if` and `else` have incompatible types
+    }
+}
+
+fn main() {}
diff --git a/src/test/ui/unsized/box-instead-of-dyn-fn.stderr b/src/test/ui/unsized/box-instead-of-dyn-fn.stderr
new file mode 100644
index 0000000000000..80f61cb3eae11
--- /dev/null
+++ b/src/test/ui/unsized/box-instead-of-dyn-fn.stderr
@@ -0,0 +1,39 @@
+error[E0308]: `if` and `else` have incompatible types
+  --> $DIR/box-instead-of-dyn-fn.rs:10:9
+   |
+LL | /     if a % 2 == 0 {
+LL | |         move || println!("{a}")
+   | |         ----------------------- expected because of this
+LL | |     } else {
+LL | |         Box::new(move || println!("{}", b))
+   | |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected closure, found struct `Box`
+LL | |
+LL | |     }
+   | |_____- `if` and `else` have incompatible types
+   |
+   = note: expected type `[closure@$DIR/box-instead-of-dyn-fn.rs:8:9: 8:32]`
+            found struct `Box<[closure@$DIR/box-instead-of-dyn-fn.rs:10:18: 10:43]>`
+
+error[E0746]: return type cannot have an unboxed trait object
+  --> $DIR/box-instead-of-dyn-fn.rs:5:56
+   |
+LL | fn print_on_or_the_other<'a>(a: i32, b: &'a String) -> dyn Fn() + 'a {
+   |                                                        ^^^^^^^^^^^^^ doesn't have a size known at compile-time
+   |
+   = note: for information on trait objects, see <https://doc.rust-lang.org/book/ch17-02-trait-objects.html#using-trait-objects-that-allow-for-values-of-different-types>
+   = note: if all the returned values were of the same type you could use `impl Fn() + 'a` as the return type
+   = note: for information on `impl Trait`, see <https://doc.rust-lang.org/book/ch10-02-traits.html#returning-types-that-implement-traits>
+   = note: you can create a new `enum` with a variant for each returned type
+help: return a boxed trait object instead
+   |
+LL | fn print_on_or_the_other<'a>(a: i32, b: &'a String) -> Box<dyn Fn() + 'a> {
+   |                                                        ++++             +
+help: ... and box this value
+   |
+LL |         Box::new(move || println!("{a}"))
+   |         +++++++++                       +
+
+error: aborting due to 2 previous errors
+
+Some errors have detailed explanations: E0308, E0746.
+For more information about an error, try `rustc --explain E0308`.
diff --git a/src/test/ui/unsized/issue-91801.rs b/src/test/ui/unsized/issue-91801.rs
new file mode 100644
index 0000000000000..096b1a93574fc
--- /dev/null
+++ b/src/test/ui/unsized/issue-91801.rs
@@ -0,0 +1,19 @@
+pub struct Something;
+
+type Validator<'a> = dyn 'a + Send + Sync + Fn(&'a Something) -> Result<(), ()>;
+
+pub static ALL_VALIDATORS: &[(&'static str, &'static Validator)] =
+    &[("validate that credits and debits balance", &validate_something)];
+
+fn or<'a>(first: &'static Validator<'a>, second: &'static Validator<'a>) -> Validator<'a> {
+    //~^ ERROR return type cannot have an unboxed trait object
+    return Box::new(move |something: &'_ Something| -> Result<(), ()> {
+        first(something).or_else(|_| second(something))
+    });
+}
+
+fn validate_something(_: &Something) -> Result<(), ()> {
+    Ok(())
+}
+
+fn main() {}
diff --git a/src/test/ui/unsized/issue-91801.stderr b/src/test/ui/unsized/issue-91801.stderr
new file mode 100644
index 0000000000000..e854514958629
--- /dev/null
+++ b/src/test/ui/unsized/issue-91801.stderr
@@ -0,0 +1,15 @@
+error[E0746]: return type cannot have an unboxed trait object
+  --> $DIR/issue-91801.rs:8:77
+   |
+LL | fn or<'a>(first: &'static Validator<'a>, second: &'static Validator<'a>) -> Validator<'a> {
+   |                                                                             ^^^^^^^^^^^^^ doesn't have a size known at compile-time
+   |
+   = note: for information on `impl Trait`, see <https://doc.rust-lang.org/book/ch10-02-traits.html#returning-types-that-implement-traits>
+help: use `impl Fn(&'a Something) -> Result<(), ()> + Send + Sync + 'a` as the return type, as all return paths are of type `Box<[closure@$DIR/issue-91801.rs:10:21: 12:6]>`, which implements `Fn(&'a Something) -> Result<(), ()> + Send + Sync + 'a`
+   |
+LL | fn or<'a>(first: &'static Validator<'a>, second: &'static Validator<'a>) -> impl Fn(&'a Something) -> Result<(), ()> + Send + Sync + 'a {
+   |                                                                             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0746`.
diff --git a/src/test/ui/unsized/issue-91803.rs b/src/test/ui/unsized/issue-91803.rs
new file mode 100644
index 0000000000000..c74897cc4bc50
--- /dev/null
+++ b/src/test/ui/unsized/issue-91803.rs
@@ -0,0 +1,8 @@
+trait Foo<'a> {}
+
+fn or<'a>(first: &'static dyn Foo<'a>) -> dyn Foo<'a> {
+    //~^ ERROR return type cannot have an unboxed trait object
+    return Box::new(panic!());
+}
+
+fn main() {}
diff --git a/src/test/ui/unsized/issue-91803.stderr b/src/test/ui/unsized/issue-91803.stderr
new file mode 100644
index 0000000000000..2dad9e8929421
--- /dev/null
+++ b/src/test/ui/unsized/issue-91803.stderr
@@ -0,0 +1,15 @@
+error[E0746]: return type cannot have an unboxed trait object
+  --> $DIR/issue-91803.rs:3:43
+   |
+LL | fn or<'a>(first: &'static dyn Foo<'a>) -> dyn Foo<'a> {
+   |                                           ^^^^^^^^^^^ doesn't have a size known at compile-time
+   |
+   = note: for information on `impl Trait`, see <https://doc.rust-lang.org/book/ch10-02-traits.html#returning-types-that-implement-traits>
+help: use `impl Foo<'a>` as the return type, as all return paths are of type `Box<_>`, which implements `Foo<'a>`
+   |
+LL | fn or<'a>(first: &'static dyn Foo<'a>) -> impl Foo<'a> {
+   |                                           ~~~~~~~~~~~~
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0746`.
diff --git a/src/test/ui/wf/hir-wf-check-erase-regions.stderr b/src/test/ui/wf/hir-wf-check-erase-regions.stderr
index 0d9b9627562f9..037f8b9f33770 100644
--- a/src/test/ui/wf/hir-wf-check-erase-regions.stderr
+++ b/src/test/ui/wf/hir-wf-check-erase-regions.stderr
@@ -5,6 +5,7 @@ LL |     type IntoIter = std::iter::Flatten<std::slice::Iter<'a, T>>;
    |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `&T` is not an iterator
    |
    = help: the trait `Iterator` is not implemented for `&T`
+   = help: the trait `Iterator` is implemented for `&mut I`
    = note: required because of the requirements on the impl of `IntoIterator` for `&T`
 note: required by a bound in `Flatten`
   --> $SRC_DIR/core/src/iter/adapters/flatten.rs:LL:COL
@@ -19,6 +20,7 @@ LL |     fn into_iter(self) -> Self::IntoIter {
    |                           ^^^^^^^^^^^^^^ `&T` is not an iterator
    |
    = help: the trait `Iterator` is not implemented for `&T`
+   = help: the trait `Iterator` is implemented for `&mut I`
    = note: required because of the requirements on the impl of `IntoIterator` for `&T`
 note: required by a bound in `Flatten`
   --> $SRC_DIR/core/src/iter/adapters/flatten.rs:LL:COL