Rollup merge of #125395 - madsmtm:fix-doc-summary, r=lqd

Remove unnecessary `.md` from the documentation sidebar

Oversight from https://github.com/rust-lang/rust/pull/124772, caught in https://github.com/rust-lang/rust/pull/124772#discussion_r1609227398.

r? weihanglo
diff --git a/compiler/rustc_ast/src/attr/mod.rs b/compiler/rustc_ast/src/attr/mod.rs
index 0684163..d5c9fc9 100644
--- a/compiler/rustc_ast/src/attr/mod.rs
+++ b/compiler/rustc_ast/src/attr/mod.rs
@@ -13,6 +13,7 @@
 use rustc_index::bit_set::GrowableBitSet;
 use rustc_span::symbol::{sym, Ident, Symbol};
 use rustc_span::Span;
+use smallvec::{smallvec, SmallVec};
 use std::iter;
 use std::sync::atomic::{AtomicU32, Ordering};
 use thin_vec::{thin_vec, ThinVec};
@@ -87,10 +88,20 @@
             AttrKind::DocComment(..) => None,
         }
     }
+
     pub fn name_or_empty(&self) -> Symbol {
         self.ident().unwrap_or_else(Ident::empty).name
     }
 
+    pub fn path(&self) -> SmallVec<[Symbol; 1]> {
+        match &self.kind {
+            AttrKind::Normal(normal) => {
+                normal.item.path.segments.iter().map(|s| s.ident.name).collect()
+            }
+            AttrKind::DocComment(..) => smallvec![sym::doc],
+        }
+    }
+
     #[inline]
     pub fn has_name(&self, name: Symbol) -> bool {
         match &self.kind {
diff --git a/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs b/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs
index 5aa8fe2..b23ad2e 100644
--- a/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs
+++ b/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs
@@ -136,7 +136,7 @@
 
     fn convert(
         &mut self,
-        predicate: ty::OutlivesPredicate<ty::GenericArg<'tcx>, ty::Region<'tcx>>,
+        predicate: ty::OutlivesPredicate<'tcx, ty::GenericArg<'tcx>>,
         constraint_category: ConstraintCategory<'tcx>,
     ) {
         debug!("generate: constraints at: {:#?}", self.locations);
@@ -276,7 +276,7 @@
         &self,
         ty: Ty<'tcx>,
         next_outlives_predicates: &mut Vec<(
-            ty::OutlivesPredicate<ty::GenericArg<'tcx>, ty::Region<'tcx>>,
+            ty::OutlivesPredicate<'tcx, ty::GenericArg<'tcx>>,
             ConstraintCategory<'tcx>,
         )>,
     ) -> Ty<'tcx> {
diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs
index 4eb24d7..66a7a2e 100644
--- a/compiler/rustc_codegen_ssa/src/lib.rs
+++ b/compiler/rustc_codegen_ssa/src/lib.rs
@@ -195,6 +195,7 @@
     EmptyVersionNumber,
     EncodingVersionMismatch { version_array: String, rlink_version: u32 },
     RustcVersionMismatch { rustc_version: String },
+    CorruptFile,
 }
 
 pub fn provide(providers: &mut Providers) {
@@ -265,7 +266,9 @@
             });
         }
 
-        let mut decoder = MemDecoder::new(&data[4..], 0);
+        let Ok(mut decoder) = MemDecoder::new(&data[4..], 0) else {
+            return Err(CodegenErrors::CorruptFile);
+        };
         let rustc_version = decoder.read_str();
         if rustc_version != sess.cfg_version {
             return Err(CodegenErrors::RustcVersionMismatch {
diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs
index 836e548..310fd46 100644
--- a/compiler/rustc_const_eval/src/const_eval/machine.rs
+++ b/compiler/rustc_const_eval/src/const_eval/machine.rs
@@ -25,9 +25,9 @@
 use crate::errors::{LongRunning, LongRunningWarn};
 use crate::fluent_generated as fluent;
 use crate::interpret::{
-    self, compile_time_machine, err_ub, throw_exhaust, throw_inval, throw_ub_custom,
+    self, compile_time_machine, err_ub, throw_exhaust, throw_inval, throw_ub_custom, throw_unsup,
     throw_unsup_format, AllocId, AllocRange, ConstAllocation, CtfeProvenance, FnArg, FnVal, Frame,
-    ImmTy, InterpCx, InterpResult, MPlaceTy, OpTy, Pointer, PointerArithmetic, Scalar,
+    GlobalAlloc, ImmTy, InterpCx, InterpResult, MPlaceTy, OpTy, Pointer, PointerArithmetic, Scalar,
 };
 
 use super::error::*;
@@ -759,11 +759,21 @@
         ecx: &InterpCx<'mir, 'tcx, Self>,
         alloc_id: AllocId,
     ) -> InterpResult<'tcx> {
+        // Check if this is the currently evaluated static.
         if Some(alloc_id) == ecx.machine.static_root_ids.map(|(id, _)| id) {
-            Err(ConstEvalErrKind::RecursiveStatic.into())
-        } else {
-            Ok(())
+            return Err(ConstEvalErrKind::RecursiveStatic.into());
         }
+        // If this is another static, make sure we fire off the query to detect cycles.
+        // But only do that when checks for static recursion are enabled.
+        if ecx.machine.static_root_ids.is_some() {
+            if let Some(GlobalAlloc::Static(def_id)) = ecx.tcx.try_get_global_alloc(alloc_id) {
+                if ecx.tcx.is_foreign_item(def_id) {
+                    throw_unsup!(ExternStatic(def_id));
+                }
+                ecx.ctfe_query(|tcx| tcx.eval_static_initializer(def_id))?;
+            }
+        }
+        Ok(())
     }
 }
 
diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs
index dce4d56..72dad56 100644
--- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs
+++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs
@@ -255,6 +255,7 @@
                                     name = intrinsic_name,
                                 );
                             }
+                            // This will always return 0.
                             (a, b)
                         }
                         (Err(_), _) | (_, Err(_)) => {
diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs
index 350fd48..737f2fd 100644
--- a/compiler/rustc_const_eval/src/interpret/memory.rs
+++ b/compiler/rustc_const_eval/src/interpret/memory.rs
@@ -413,6 +413,8 @@
     /// to the allocation it points to. Supports both shared and mutable references, as the actual
     /// checking is offloaded to a helper closure.
     ///
+    /// `alloc_size` will only get called for non-zero-sized accesses.
+    ///
     /// Returns `None` if and only if the size is 0.
     fn check_and_deref_ptr<T>(
         &self,
@@ -425,18 +427,19 @@
             M::ProvenanceExtra,
         ) -> InterpResult<'tcx, (Size, Align, T)>,
     ) -> InterpResult<'tcx, Option<T>> {
+        // Everything is okay with size 0.
+        if size.bytes() == 0 {
+            return Ok(None);
+        }
+
         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.
-                if size.bytes() > 0 || addr == 0 {
-                    throw_ub!(DanglingIntPointer(addr, msg));
-                }
-                None
+                // We couldn't get a proper allocation.
+                throw_ub!(DanglingIntPointer(addr, msg));
             }
             Ok((alloc_id, offset, prov)) => {
                 let (alloc_size, _alloc_align, ret_val) = alloc_size(alloc_id, offset, prov)?;
-                // Test bounds. This also ensures non-null.
+                // Test bounds.
                 // It is sufficient to check this for the end pointer. Also check for overflow!
                 if offset.checked_add(size, &self.tcx).map_or(true, |end| end > alloc_size) {
                     throw_ub!(PointerOutOfBounds {
@@ -447,14 +450,8 @@
                         msg,
                     })
                 }
-                // Ensure we never consider the null pointer dereferenceable.
-                if M::Provenance::OFFSET_IS_ADDR {
-                    assert_ne!(ptr.addr(), Size::ZERO);
-                }
 
-                // We can still be zero-sized in this branch, in which case we have to
-                // return `None`.
-                if size.bytes() == 0 { None } else { Some(ret_val) }
+                Some(ret_val)
             }
         })
     }
@@ -641,16 +638,18 @@
             size,
             CheckInAllocMsg::MemoryAccessTest,
             |alloc_id, offset, prov| {
-                if !self.memory.validation_in_progress.get() {
-                    // We want to call the hook on *all* accesses that involve an AllocId,
-                    // including zero-sized accesses. That means we have to do it here
-                    // rather than below in the `Some` branch.
-                    M::before_alloc_read(self, alloc_id)?;
-                }
                 let alloc = self.get_alloc_raw(alloc_id)?;
                 Ok((alloc.size(), alloc.align, (alloc_id, offset, prov, alloc)))
             },
         )?;
+        // We want to call the hook on *all* accesses that involve an AllocId, including zero-sized
+        // accesses. That means we cannot rely on the closure above or the `Some` branch below. We
+        // do this after `check_and_deref_ptr` to ensure some basic sanity has already been checked.
+        if !self.memory.validation_in_progress.get() {
+            if let Ok((alloc_id, ..)) = self.ptr_try_get_alloc_id(ptr) {
+                M::before_alloc_read(self, alloc_id)?;
+            }
+        }
 
         if let Some((alloc_id, offset, prov, alloc)) = ptr_and_alloc {
             let range = alloc_range(offset, size);
diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs
index 2bd4d9d..e36d301 100644
--- a/compiler/rustc_const_eval/src/interpret/validity.rs
+++ b/compiler/rustc_const_eval/src/interpret/validity.rs
@@ -434,6 +434,11 @@
                 found_bytes: has.bytes()
             },
         );
+        // Make sure this is non-null. We checked dereferenceability above, but if `size` is zero
+        // that does not imply non-null.
+        if self.ecx.scalar_may_be_null(Scalar::from_maybe_pointer(place.ptr(), self.ecx))? {
+            throw_validation_failure!(self.path, NullPtr { ptr_kind })
+        }
         // Do not allow pointers to uninhabited types.
         if place.layout.abi.is_uninhabited() {
             let ty = place.layout.ty;
@@ -456,8 +461,8 @@
             // `!` is a ZST and we want to validate it.
             if let Ok((alloc_id, _offset, _prov)) = self.ecx.ptr_try_get_alloc_id(place.ptr()) {
                 let mut skip_recursive_check = false;
-                let alloc_actual_mutbl = mutability(self.ecx, alloc_id);
-                if let GlobalAlloc::Static(did) = self.ecx.tcx.global_alloc(alloc_id) {
+                if let Some(GlobalAlloc::Static(did)) = self.ecx.tcx.try_get_global_alloc(alloc_id)
+                {
                     let DefKind::Static { nested, .. } = self.ecx.tcx.def_kind(did) else { bug!() };
                     // Special handling for pointers to statics (irrespective of their type).
                     assert!(!self.ecx.tcx.is_thread_local_static(did));
@@ -495,6 +500,7 @@
                 // If this allocation has size zero, there is no actual mutability here.
                 let (size, _align, _alloc_kind) = self.ecx.get_alloc_info(alloc_id);
                 if size != Size::ZERO {
+                    let alloc_actual_mutbl = mutability(self.ecx, alloc_id);
                     // Mutable pointer to immutable memory is no good.
                     if ptr_expected_mutbl == Mutability::Mut
                         && alloc_actual_mutbl == Mutability::Not
@@ -831,6 +837,8 @@
         trace!("visit_value: {:?}, {:?}", *op, op.layout);
 
         // Check primitive types -- the leaves of our recursive descent.
+        // We assume that the Scalar validity range does not restrict these values
+        // any further than `try_visit_primitive` does!
         if self.try_visit_primitive(op)? {
             return Ok(());
         }
diff --git a/compiler/rustc_driver_impl/messages.ftl b/compiler/rustc_driver_impl/messages.ftl
index 5b39248..31837e0 100644
--- a/compiler/rustc_driver_impl/messages.ftl
+++ b/compiler/rustc_driver_impl/messages.ftl
@@ -10,6 +10,8 @@
 driver_impl_ice_path_error_env = the environment variable `RUSTC_ICE` is set to `{$env_var}`
 driver_impl_ice_version = rustc {$version} running on {$triple}
 
+driver_impl_rlink_corrupt_file = corrupt metadata encountered in `{$file}`
+
 driver_impl_rlink_empty_version_number = The input does not contain version number
 
 driver_impl_rlink_encoding_version_mismatch = .rlink file was produced with encoding version `{$version_array}`, but the current version is `{$rlink_version}`
diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs
index ba6b9ef..5532eff 100644
--- a/compiler/rustc_driver_impl/src/lib.rs
+++ b/compiler/rustc_driver_impl/src/lib.rs
@@ -96,7 +96,7 @@
 
 use crate::session_diagnostics::{
     RLinkEmptyVersionNumber, RLinkEncodingVersionMismatch, RLinkRustcVersionMismatch,
-    RLinkWrongFileType, RlinkNotAFile, RlinkUnableToRead,
+    RLinkWrongFileType, RlinkCorruptFile, RlinkNotAFile, RlinkUnableToRead,
 };
 
 rustc_fluent_macro::fluent_messages! { "../messages.ftl" }
@@ -645,8 +645,7 @@
                 match err {
                     CodegenErrors::WrongFileType => dcx.emit_fatal(RLinkWrongFileType),
                     CodegenErrors::EmptyVersionNumber => dcx.emit_fatal(RLinkEmptyVersionNumber),
-                    CodegenErrors::EncodingVersionMismatch { version_array, rlink_version } => sess
-                        .dcx()
+                    CodegenErrors::EncodingVersionMismatch { version_array, rlink_version } => dcx
                         .emit_fatal(RLinkEncodingVersionMismatch { version_array, rlink_version }),
                     CodegenErrors::RustcVersionMismatch { rustc_version } => {
                         dcx.emit_fatal(RLinkRustcVersionMismatch {
@@ -654,6 +653,9 @@
                             current_version: sess.cfg_version,
                         })
                     }
+                    CodegenErrors::CorruptFile => {
+                        dcx.emit_fatal(RlinkCorruptFile { file });
+                    }
                 };
             }
         };
diff --git a/compiler/rustc_driver_impl/src/session_diagnostics.rs b/compiler/rustc_driver_impl/src/session_diagnostics.rs
index 1a9683e..449878f 100644
--- a/compiler/rustc_driver_impl/src/session_diagnostics.rs
+++ b/compiler/rustc_driver_impl/src/session_diagnostics.rs
@@ -33,6 +33,12 @@
 pub(crate) struct RlinkNotAFile;
 
 #[derive(Diagnostic)]
+#[diag(driver_impl_rlink_corrupt_file)]
+pub(crate) struct RlinkCorruptFile<'a> {
+    pub file: &'a std::path::Path,
+}
+
+#[derive(Diagnostic)]
 #[diag(driver_impl_ice)]
 pub(crate) struct Ice;
 
diff --git a/compiler/rustc_errors/src/diagnostic_impls.rs b/compiler/rustc_errors/src/diagnostic_impls.rs
index 662c8a7..ee6df8e 100644
--- a/compiler/rustc_errors/src/diagnostic_impls.rs
+++ b/compiler/rustc_errors/src/diagnostic_impls.rs
@@ -118,6 +118,15 @@
     }
 }
 
+impl<I: rustc_type_ir::Interner, T> IntoDiagArg for rustc_type_ir::Binder<I, T>
+where
+    T: IntoDiagArg,
+{
+    fn into_diag_arg(self) -> DiagArgValue {
+        self.skip_binder().into_diag_arg()
+    }
+}
+
 into_diag_arg_for_number!(i8, u8, i16, u16, i32, u32, i64, u64, i128, u128, isize, usize);
 
 impl IntoDiagArg for bool {
diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs
index ebaa9f8..0b4a871 100644
--- a/compiler/rustc_feature/src/builtin_attrs.rs
+++ b/compiler/rustc_feature/src/builtin_attrs.rs
@@ -515,12 +515,6 @@
         EncodeCrossCrate::Yes, experimental!(deprecated_safe),
     ),
 
-    // RFC 2397
-    gated!(
-        do_not_recommend, Normal, template!(Word), WarnFollowing,
-        EncodeCrossCrate::Yes, experimental!(do_not_recommend)
-    ),
-
     // `#[cfi_encoding = ""]`
     gated!(
         cfi_encoding, Normal, template!(NameValueStr: "encoding"), ErrorPreceding,
diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs
index 00356ec..168a370 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs
@@ -18,7 +18,7 @@
 use rustc_infer::traits::FulfillmentError;
 use rustc_middle::bug;
 use rustc_middle::query::Key;
-use rustc_middle::ty::print::PrintTraitRefExt as _;
+use rustc_middle::ty::print::{PrintPolyTraitRefExt as _, PrintTraitRefExt as _};
 use rustc_middle::ty::GenericParamDefKind;
 use rustc_middle::ty::{self, suggest_constraining_type_param};
 use rustc_middle::ty::{AdtDef, Ty, TyCtxt, TypeVisitableExt};
diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
index 8caeb85..0cd77fe 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
@@ -40,6 +40,7 @@
 use rustc_infer::traits::ObligationCause;
 use rustc_middle::middle::stability::AllowUnstable;
 use rustc_middle::mir::interpret::{LitToConstError, LitToConstInput};
+use rustc_middle::ty::print::PrintPolyTraitRefExt as _;
 use rustc_middle::ty::{
     self, Const, GenericArgKind, GenericArgsRef, GenericParamDefKind, ParamEnv, Ty, TyCtxt,
     TypeVisitableExt,
diff --git a/compiler/rustc_hir_analysis/src/outlives/utils.rs b/compiler/rustc_hir_analysis/src/outlives/utils.rs
index 95290bb..5086c2a 100644
--- a/compiler/rustc_hir_analysis/src/outlives/utils.rs
+++ b/compiler/rustc_hir_analysis/src/outlives/utils.rs
@@ -9,7 +9,7 @@
 /// Tracks the `T: 'a` or `'a: 'a` predicates that we have inferred
 /// must be added to the struct header.
 pub(crate) type RequiredPredicates<'tcx> =
-    FxIndexMap<ty::OutlivesPredicate<GenericArg<'tcx>, ty::Region<'tcx>>, Span>;
+    FxIndexMap<ty::OutlivesPredicate<'tcx, ty::GenericArg<'tcx>>, Span>;
 
 /// Given a requirement `T: 'a` or `'b: 'a`, deduce the
 /// outlives_component and add it to `required_predicates`
diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs
index 6da5adc..7916366 100644
--- a/compiler/rustc_hir_typeck/src/demand.rs
+++ b/compiler/rustc_hir_typeck/src/demand.rs
@@ -373,8 +373,11 @@
                 let Some(arg_ty) = self.node_ty_opt(args[idx].hir_id) else {
                     return false;
                 };
-                let possible_rcvr_ty = expr_finder.uses.iter().find_map(|binding| {
+                let possible_rcvr_ty = expr_finder.uses.iter().rev().find_map(|binding| {
                     let possible_rcvr_ty = self.node_ty_opt(binding.hir_id)?;
+                    if possible_rcvr_ty.is_ty_var() {
+                        return None;
+                    }
                     // Fudge the receiver, so we can do new inference on it.
                     let possible_rcvr_ty = possible_rcvr_ty.fold_with(&mut fudger);
                     let method = self
@@ -386,6 +389,12 @@
                             binding,
                         )
                         .ok()?;
+                    // Make sure we select the same method that we started with...
+                    if Some(method.def_id)
+                        != self.typeck_results.borrow().type_dependent_def_id(call_expr.hir_id)
+                    {
+                        return None;
+                    }
                     // Unify the method signature with our incompatible arg, to
                     // do inference in the *opposite* direction and to find out
                     // what our ideal rcvr ty would look like.
@@ -456,6 +465,12 @@
                 ) else {
                     continue;
                 };
+                // Make sure we select the same method that we started with...
+                if Some(method.def_id)
+                    != self.typeck_results.borrow().type_dependent_def_id(parent_expr.hir_id)
+                {
+                    continue;
+                }
 
                 let ideal_rcvr_ty = rcvr_ty.fold_with(&mut fudger);
                 let ideal_method = self
@@ -505,13 +520,19 @@
                     // blame arg, if possible. Don't do this if we're coming from
                     // arg mismatch code, because we'll possibly suggest a mutually
                     // incompatible fix at the original mismatch site.
+                    // HACK(compiler-errors): We don't actually consider the implications
+                    // of our inference guesses in `emit_type_mismatch_suggestions`, so
+                    // only suggest things when we know our type error is precisely due to
+                    // a type mismatch, and not via some projection or something. See #116155.
                     if matches!(source, TypeMismatchSource::Ty(_))
                         && let Some(ideal_method) = ideal_method
-                        && let ideal_arg_ty = self.resolve_vars_if_possible(ideal_method.sig.inputs()[idx + 1])
-                        // HACK(compiler-errors): We don't actually consider the implications
-                        // of our inference guesses in `emit_type_mismatch_suggestions`, so
-                        // only suggest things when we know our type error is precisely due to
-                        // a type mismatch, and not via some projection or something. See #116155.
+                        && Some(ideal_method.def_id)
+                            == self
+                                .typeck_results
+                                .borrow()
+                                .type_dependent_def_id(parent_expr.hir_id)
+                        && let ideal_arg_ty =
+                            self.resolve_vars_if_possible(ideal_method.sig.inputs()[idx + 1])
                         && !ideal_arg_ty.has_non_region_infer()
                     {
                         self.emit_type_mismatch_suggestions(
diff --git a/compiler/rustc_hir_typeck/src/upvar.rs b/compiler/rustc_hir_typeck/src/upvar.rs
index 9d16f0d..e29a410 100644
--- a/compiler/rustc_hir_typeck/src/upvar.rs
+++ b/compiler/rustc_hir_typeck/src/upvar.rs
@@ -204,6 +204,60 @@
             fake_reads: Default::default(),
         };
 
+        let _ = euv::ExprUseVisitor::new(
+            &FnCtxt::new(self, self.tcx.param_env(closure_def_id), closure_def_id),
+            &mut delegate,
+        )
+        .consume_body(body);
+
+        // There are several curious situations with coroutine-closures where
+        // analysis is too aggressive with borrows when the coroutine-closure is
+        // marked `move`. Specifically:
+        //
+        // 1. If the coroutine-closure was inferred to be `FnOnce` during signature
+        // inference, then it's still possible that we try to borrow upvars from
+        // the coroutine-closure because they are not used by the coroutine body
+        // in a way that forces a move. See the test:
+        // `async-await/async-closures/force-move-due-to-inferred-kind.rs`.
+        //
+        // 2. If the coroutine-closure is forced to be `FnOnce` due to the way it
+        // uses its upvars, but not *all* upvars would force the closure to `FnOnce`.
+        // See the test: `async-await/async-closures/force-move-due-to-actually-fnonce.rs`.
+        //
+        // This would lead to an impossible to satisfy situation, since `AsyncFnOnce`
+        // coroutine bodies can't borrow from their parent closure. To fix this,
+        // we force the inner coroutine to also be `move`. This only matters for
+        // coroutine-closures that are `move` since otherwise they themselves will
+        // be borrowing from the outer environment, so there's no self-borrows occuring.
+        //
+        // One *important* note is that we do a call to `process_collected_capture_information`
+        // to eagerly test whether the coroutine would end up `FnOnce`, but we do this
+        // *before* capturing all the closure args by-value below, since that would always
+        // cause the analysis to return `FnOnce`.
+        if let UpvarArgs::Coroutine(..) = args
+            && let hir::CoroutineKind::Desugared(_, hir::CoroutineSource::Closure) =
+                self.tcx.coroutine_kind(closure_def_id).expect("coroutine should have kind")
+            && let parent_hir_id =
+                self.tcx.local_def_id_to_hir_id(self.tcx.local_parent(closure_def_id))
+            && let parent_ty = self.node_ty(parent_hir_id)
+            && let hir::CaptureBy::Value { move_kw } =
+                self.tcx.hir_node(parent_hir_id).expect_closure().capture_clause
+        {
+            // (1.) Closure signature inference forced this closure to `FnOnce`.
+            if let Some(ty::ClosureKind::FnOnce) = self.closure_kind(parent_ty) {
+                capture_clause = hir::CaptureBy::Value { move_kw };
+            }
+            // (2.) The way that the closure uses its upvars means it's `FnOnce`.
+            else if let (_, ty::ClosureKind::FnOnce, _) = self
+                .process_collected_capture_information(
+                    capture_clause,
+                    &delegate.capture_information,
+                )
+            {
+                capture_clause = hir::CaptureBy::Value { move_kw };
+            }
+        }
+
         // As noted in `lower_coroutine_body_with_moved_arguments`, we default the capture mode
         // to `ByRef` for the `async {}` block internal to async fns/closure. This means
         // that we would *not* be moving all of the parameters into the async block by default.
@@ -253,34 +307,6 @@
             }
         }
 
-        let _ = euv::ExprUseVisitor::new(
-            &FnCtxt::new(self, self.tcx.param_env(closure_def_id), closure_def_id),
-            &mut delegate,
-        )
-        .consume_body(body);
-
-        // If a coroutine is comes from a coroutine-closure that is `move`, but
-        // the coroutine-closure was inferred to be `FnOnce` during signature
-        // inference, then it's still possible that we try to borrow upvars from
-        // the coroutine-closure because they are not used by the coroutine body
-        // in a way that forces a move.
-        //
-        // This would lead to an impossible to satisfy situation, since `AsyncFnOnce`
-        // coroutine bodies can't borrow from their parent closure. To fix this,
-        // we force the inner coroutine to also be `move`. This only matters for
-        // coroutine-closures that are `move` since otherwise they themselves will
-        // be borrowing from the outer environment, so there's no self-borrows occuring.
-        if let UpvarArgs::Coroutine(..) = args
-            && let hir::CoroutineKind::Desugared(_, hir::CoroutineSource::Closure) =
-                self.tcx.coroutine_kind(closure_def_id).expect("coroutine should have kind")
-            && let parent_hir_id =
-                self.tcx.local_def_id_to_hir_id(self.tcx.local_parent(closure_def_id))
-            && let parent_ty = self.node_ty(parent_hir_id)
-            && let Some(ty::ClosureKind::FnOnce) = self.closure_kind(parent_ty)
-        {
-            capture_clause = self.tcx.hir_node(parent_hir_id).expect_closure().capture_clause;
-        }
-
         debug!(
             "For closure={:?}, capture_information={:#?}",
             closure_def_id, delegate.capture_information
@@ -289,7 +315,7 @@
         self.log_capture_analysis_first_pass(closure_def_id, &delegate.capture_information, span);
 
         let (capture_information, closure_kind, origin) = self
-            .process_collected_capture_information(capture_clause, delegate.capture_information);
+            .process_collected_capture_information(capture_clause, &delegate.capture_information);
 
         self.compute_min_captures(closure_def_id, capture_information, span);
 
@@ -545,13 +571,14 @@
     fn process_collected_capture_information(
         &self,
         capture_clause: hir::CaptureBy,
-        capture_information: InferredCaptureInformation<'tcx>,
+        capture_information: &InferredCaptureInformation<'tcx>,
     ) -> (InferredCaptureInformation<'tcx>, ty::ClosureKind, Option<(Span, Place<'tcx>)>) {
         let mut closure_kind = ty::ClosureKind::LATTICE_BOTTOM;
         let mut origin: Option<(Span, Place<'tcx>)> = None;
 
         let processed = capture_information
-            .into_iter()
+            .iter()
+            .cloned()
             .map(|(place, mut capture_info)| {
                 // Apply rules for safety before inferring closure kind
                 let (place, capture_kind) =
diff --git a/compiler/rustc_incremental/messages.ftl b/compiler/rustc_incremental/messages.ftl
index e74173b..de2177e 100644
--- a/compiler/rustc_incremental/messages.ftl
+++ b/compiler/rustc_incremental/messages.ftl
@@ -21,6 +21,8 @@
 incremental_copy_workproduct_to_cache =
     error copying object file `{$from}` to incremental directory as `{$to}`: {$err}
 
+incremental_corrupt_file = corrupt incremental compilation artifact found at `{$path}`. This file will automatically be ignored and deleted. If you see this message repeatedly or can provoke it without manually manipulating the compiler's artifacts, please file an issue. The incremental compilation system relies on hardlinks and filesystem locks behaving correctly, and may not deal well with OS crashes, so whatever information you can provide about your filesystem or other state may be very relevant.
+
 incremental_create_dep_graph = failed to create dependency graph at `{$path}`: {$err}
 
 incremental_create_incr_comp_dir =
diff --git a/compiler/rustc_incremental/src/errors.rs b/compiler/rustc_incremental/src/errors.rs
index 61bb035..e94a7fb 100644
--- a/compiler/rustc_incremental/src/errors.rs
+++ b/compiler/rustc_incremental/src/errors.rs
@@ -306,3 +306,9 @@
     pub path: &'a Path,
     pub err: std::io::Error,
 }
+
+#[derive(Diagnostic)]
+#[diag(incremental_corrupt_file)]
+pub struct CorruptFile<'a> {
+    pub path: &'a Path,
+}
diff --git a/compiler/rustc_incremental/src/persist/load.rs b/compiler/rustc_incremental/src/persist/load.rs
index 26aaa247..9e6ce06 100644
--- a/compiler/rustc_incremental/src/persist/load.rs
+++ b/compiler/rustc_incremental/src/persist/load.rs
@@ -115,7 +115,12 @@
 
         if let LoadResult::Ok { data: (work_products_data, start_pos) } = load_result {
             // Decode the list of work_products
-            let mut work_product_decoder = MemDecoder::new(&work_products_data[..], start_pos);
+            let Ok(mut work_product_decoder) =
+                MemDecoder::new(&work_products_data[..], start_pos)
+            else {
+                sess.dcx().emit_warn(errors::CorruptFile { path: &work_products_path });
+                return LoadResult::DataOutOfDate;
+            };
             let work_products: Vec<SerializedWorkProduct> =
                 Decodable::decode(&mut work_product_decoder);
 
@@ -145,7 +150,10 @@
         LoadResult::DataOutOfDate => LoadResult::DataOutOfDate,
         LoadResult::LoadDepGraph(path, err) => LoadResult::LoadDepGraph(path, err),
         LoadResult::Ok { data: (bytes, start_pos) } => {
-            let mut decoder = MemDecoder::new(&bytes, start_pos);
+            let Ok(mut decoder) = MemDecoder::new(&bytes, start_pos) else {
+                sess.dcx().emit_warn(errors::CorruptFile { path: &path });
+                return LoadResult::DataOutOfDate;
+            };
             let prev_commandline_args_hash = u64::decode(&mut decoder);
 
             if prev_commandline_args_hash != expected_hash {
@@ -181,9 +189,14 @@
 
     let _prof_timer = sess.prof.generic_activity("incr_comp_load_query_result_cache");
 
-    match load_data(&query_cache_path(sess), sess) {
+    let path = query_cache_path(sess);
+    match load_data(&path, sess) {
         LoadResult::Ok { data: (bytes, start_pos) } => {
-            Some(OnDiskCache::new(sess, bytes, start_pos))
+            let cache = OnDiskCache::new(sess, bytes, start_pos).unwrap_or_else(|()| {
+                sess.dcx().emit_warn(errors::CorruptFile { path: &path });
+                OnDiskCache::new_empty(sess.source_map())
+            });
+            Some(cache)
         }
         _ => Some(OnDiskCache::new_empty(sess.source_map())),
     }
diff --git a/compiler/rustc_infer/src/infer/outlives/env.rs b/compiler/rustc_infer/src/infer/outlives/env.rs
index c44a508..5bcb4f2 100644
--- a/compiler/rustc_infer/src/infer/outlives/env.rs
+++ b/compiler/rustc_infer/src/infer/outlives/env.rs
@@ -64,8 +64,7 @@
 /// "Region-bound pairs" tracks outlives relations that are known to
 /// be true, either because of explicit where-clauses like `T: 'a` or
 /// because of implied bounds.
-pub type RegionBoundPairs<'tcx> =
-    FxIndexSet<ty::OutlivesPredicate<GenericKind<'tcx>, Region<'tcx>>>;
+pub type RegionBoundPairs<'tcx> = FxIndexSet<ty::OutlivesPredicate<'tcx, GenericKind<'tcx>>>;
 
 impl<'tcx> OutlivesEnvironment<'tcx> {
     /// Create a builder using `ParamEnv` and add explicit outlives bounds into it.
diff --git a/compiler/rustc_infer/src/infer/outlives/verify.rs b/compiler/rustc_infer/src/infer/outlives/verify.rs
index bd981c2..7e977b9 100644
--- a/compiler/rustc_infer/src/infer/outlives/verify.rs
+++ b/compiler/rustc_infer/src/infer/outlives/verify.rs
@@ -94,7 +94,7 @@
     pub fn approx_declared_bounds_from_env(
         &self,
         alias_ty: ty::AliasTy<'tcx>,
-    ) -> Vec<ty::Binder<'tcx, ty::OutlivesPredicate<Ty<'tcx>, ty::Region<'tcx>>>> {
+    ) -> Vec<ty::PolyTypeOutlivesPredicate<'tcx>> {
         let erased_alias_ty = self.tcx.erase_regions(alias_ty.to_ty(self.tcx));
         self.declared_generic_bounds_from_env_for_erased_ty(erased_alias_ty)
     }
@@ -193,7 +193,7 @@
     fn declared_generic_bounds_from_env(
         &self,
         generic_ty: Ty<'tcx>,
-    ) -> Vec<ty::Binder<'tcx, ty::OutlivesPredicate<Ty<'tcx>, ty::Region<'tcx>>>> {
+    ) -> Vec<ty::PolyTypeOutlivesPredicate<'tcx>> {
         assert!(matches!(generic_ty.kind(), ty::Param(_) | ty::Placeholder(_)));
         self.declared_generic_bounds_from_env_for_erased_ty(generic_ty)
     }
@@ -213,7 +213,7 @@
     fn declared_generic_bounds_from_env_for_erased_ty(
         &self,
         erased_ty: Ty<'tcx>,
-    ) -> Vec<ty::Binder<'tcx, ty::OutlivesPredicate<Ty<'tcx>, ty::Region<'tcx>>>> {
+    ) -> Vec<ty::PolyTypeOutlivesPredicate<'tcx>> {
         let tcx = self.tcx;
 
         // To start, collect bounds from user environment. Note that
diff --git a/compiler/rustc_log/src/lib.rs b/compiler/rustc_log/src/lib.rs
index 81257f9..e11c45b 100644
--- a/compiler/rustc_log/src/lib.rs
+++ b/compiler/rustc_log/src/lib.rs
@@ -159,7 +159,9 @@
         if !target.contains(&self.backtrace_target) {
             return Ok(());
         }
-        let backtrace = std::backtrace::Backtrace::capture();
+        // Use Backtrace::force_capture because we don't want to depend on the
+        // RUST_BACKTRACE environment variable being set.
+        let backtrace = std::backtrace::Backtrace::force_capture();
         writeln!(writer, "stack backtrace: \n{backtrace:?}")
     }
 }
diff --git a/compiler/rustc_macros/src/serialize.rs b/compiler/rustc_macros/src/serialize.rs
index 5fa11d2..7b5dd16 100644
--- a/compiler/rustc_macros/src/serialize.rs
+++ b/compiler/rustc_macros/src/serialize.rs
@@ -13,7 +13,7 @@
         quote! {}
     };
 
-    s.add_impl_generic(parse_quote! {#decoder_ty: ::rustc_type_ir::codec::TyDecoder #bound });
+    s.add_impl_generic(parse_quote! { #decoder_ty: ::rustc_type_ir::codec::TyDecoder #bound });
     s.add_bounds(synstructure::AddBounds::Fields);
     s.underscore_const(true);
 
@@ -34,7 +34,7 @@
 
 pub fn decodable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::TokenStream {
     let decoder_ty = quote! { __D };
-    s.add_impl_generic(parse_quote! {#decoder_ty: ::rustc_span::SpanDecoder});
+    s.add_impl_generic(parse_quote! { #decoder_ty: ::rustc_span::SpanDecoder });
     s.add_bounds(synstructure::AddBounds::Generics);
     s.underscore_const(true);
 
@@ -43,7 +43,7 @@
 
 pub fn decodable_generic_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::TokenStream {
     let decoder_ty = quote! { __D };
-    s.add_impl_generic(parse_quote! {#decoder_ty: ::rustc_serialize::Decoder});
+    s.add_impl_generic(parse_quote! { #decoder_ty: ::rustc_serialize::Decoder });
     s.add_bounds(synstructure::AddBounds::Generics);
     s.underscore_const(true);
 
@@ -120,7 +120,7 @@
     let __decoder = quote! { __decoder };
     // Use the span of the field for the method call, so
     // that backtraces will point to the field.
-    quote_spanned! {field_span=> #decode_inner_method(#__decoder) }
+    quote_spanned! { field_span=> #decode_inner_method(#__decoder) }
 }
 
 pub fn type_encodable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::TokenStream {
@@ -133,7 +133,7 @@
     };
 
     let encoder_ty = quote! { __E };
-    s.add_impl_generic(parse_quote! {#encoder_ty: ::rustc_type_ir::codec::TyEncoder #bound });
+    s.add_impl_generic(parse_quote! { #encoder_ty: ::rustc_type_ir::codec::TyEncoder #bound });
     s.add_bounds(synstructure::AddBounds::Fields);
     s.underscore_const(true);
 
@@ -142,7 +142,7 @@
 
 pub fn meta_encodable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::TokenStream {
     if !s.ast().generics.lifetimes().any(|lt| lt.lifetime.ident == "tcx") {
-        s.add_impl_generic(parse_quote! {'tcx});
+        s.add_impl_generic(parse_quote! { 'tcx });
     }
     s.add_impl_generic(parse_quote! { '__a });
     let encoder_ty = quote! { EncodeContext<'__a, 'tcx> };
@@ -154,7 +154,7 @@
 
 pub fn encodable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::TokenStream {
     let encoder_ty = quote! { __E };
-    s.add_impl_generic(parse_quote! { #encoder_ty: ::rustc_span::SpanEncoder});
+    s.add_impl_generic(parse_quote! { #encoder_ty: ::rustc_span::SpanEncoder });
     s.add_bounds(synstructure::AddBounds::Generics);
     s.underscore_const(true);
 
@@ -163,7 +163,7 @@
 
 pub fn encodable_generic_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::TokenStream {
     let encoder_ty = quote! { __E };
-    s.add_impl_generic(parse_quote! { #encoder_ty: ::rustc_serialize::Encoder});
+    s.add_impl_generic(parse_quote! { #encoder_ty: ::rustc_serialize::Encoder });
     s.add_bounds(synstructure::AddBounds::Generics);
     s.underscore_const(true);
 
diff --git a/compiler/rustc_metadata/src/locator.rs b/compiler/rustc_metadata/src/locator.rs
index 7de03be..6ff1997 100644
--- a/compiler/rustc_metadata/src/locator.rs
+++ b/compiler/rustc_metadata/src/locator.rs
@@ -853,7 +853,12 @@
             slice_owned(mmap, Deref::deref)
         }
     };
-    let blob = MetadataBlob(raw_bytes);
+    let Ok(blob) = MetadataBlob::new(raw_bytes) else {
+        return Err(MetadataError::LoadFailure(format!(
+            "corrupt metadata encountered in {}",
+            filename.display()
+        )));
+    };
     match blob.check_compatibility(cfg_version) {
         Ok(()) => Ok(blob),
         Err(None) => Err(MetadataError::LoadFailure(format!(
diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs
index bb68c6e..f91e121 100644
--- a/compiler/rustc_metadata/src/rmeta/decoder.rs
+++ b/compiler/rustc_metadata/src/rmeta/decoder.rs
@@ -40,10 +40,9 @@
 mod cstore_impl;
 
 /// A reference to the raw binary version of crate metadata.
-/// A `MetadataBlob` internally is just a reference counted pointer to
-/// the actual data, so cloning it is cheap.
-#[derive(Clone)]
-pub(crate) struct MetadataBlob(pub(crate) OwnedSlice);
+/// This struct applies [`MemDecoder`]'s validation when constructed
+/// so that later constructions are guaranteed to succeed.
+pub(crate) struct MetadataBlob(OwnedSlice);
 
 impl std::ops::Deref for MetadataBlob {
     type Target = [u8];
@@ -54,6 +53,19 @@
     }
 }
 
+impl MetadataBlob {
+    /// Runs the [`MemDecoder`] validation and if it passes, constructs a new [`MetadataBlob`].
+    pub fn new(slice: OwnedSlice) -> Result<Self, ()> {
+        if MemDecoder::new(&slice, 0).is_ok() { Ok(Self(slice)) } else { Err(()) }
+    }
+
+    /// Since this has passed the validation of [`MetadataBlob::new`], this returns bytes which are
+    /// known to pass the [`MemDecoder`] validation.
+    pub fn bytes(&self) -> &OwnedSlice {
+        &self.0
+    }
+}
+
 /// A map from external crate numbers (as decoded from some crate file) to
 /// local crate numbers (as generated during this session). Each external
 /// crate may refer to types in other external crates, and each has their
@@ -165,7 +177,14 @@
     fn decoder(self, pos: usize) -> DecodeContext<'a, 'tcx> {
         let tcx = self.tcx();
         DecodeContext {
-            opaque: MemDecoder::new(self.blob(), pos),
+            // FIXME: This unwrap should never panic because we check that it won't when creating
+            // `MetadataBlob`. Ideally we'd just have a `MetadataDecoder` and hand out subslices of
+            // it as we do elsewhere in the compiler using `MetadataDecoder::split_at`. But we own
+            // the data for the decoder so holding onto the `MemDecoder` too would make us a
+            // self-referential struct which is downright goofy because `MetadataBlob` is already
+            // self-referential. Probably `MemDecoder` should contain an `OwnedSlice`, but that
+            // demands a significant refactoring due to our crate graph.
+            opaque: MemDecoder::new(self.blob(), pos).unwrap(),
             cdata: self.cdata(),
             blob: self.blob(),
             sess: self.sess().or(tcx.map(|tcx| tcx.sess)),
@@ -393,7 +412,7 @@
     where
         F: FnOnce(&mut Self) -> R,
     {
-        let new_opaque = MemDecoder::new(self.opaque.data(), pos);
+        let new_opaque = self.opaque.split_at(pos);
         let old_opaque = mem::replace(&mut self.opaque, new_opaque);
         let old_state = mem::replace(&mut self.lazy_state, LazyState::NoNode);
         let r = f(self);
diff --git a/compiler/rustc_metadata/src/rmeta/def_path_hash_map.rs b/compiler/rustc_metadata/src/rmeta/def_path_hash_map.rs
index 9950bc1..861bf6b 100644
--- a/compiler/rustc_metadata/src/rmeta/def_path_hash_map.rs
+++ b/compiler/rustc_metadata/src/rmeta/def_path_hash_map.rs
@@ -48,7 +48,7 @@
     fn decode(d: &mut DecodeContext<'a, 'tcx>) -> DefPathHashMapRef<'static> {
         let len = d.read_usize();
         let pos = d.position();
-        let o = d.blob().clone().0.slice(|blob| &blob[pos..pos + len]);
+        let o = d.blob().bytes().clone().slice(|blob| &blob[pos..pos + len]);
 
         // Although we already have the data we need via the `OwnedSlice`, we still need
         // to advance the `DecodeContext`'s position so it's in a valid state after
diff --git a/compiler/rustc_middle/src/infer/canonical.rs b/compiler/rustc_middle/src/infer/canonical.rs
index 49bf03e..dba71d8 100644
--- a/compiler/rustc_middle/src/infer/canonical.rs
+++ b/compiler/rustc_middle/src/infer/canonical.rs
@@ -32,7 +32,7 @@
 use crate::infer::MemberConstraint;
 use crate::mir::ConstraintCategory;
 use crate::ty::GenericArg;
-use crate::ty::{self, List, Region, Ty, TyCtxt, TypeFlags, TypeVisitableExt};
+use crate::ty::{self, List, Ty, TyCtxt, TypeFlags, TypeVisitableExt};
 
 pub type Canonical<'tcx, V> = ir::Canonical<TyCtxt<'tcx>, V>;
 pub type CanonicalVarInfo<'tcx> = ir::CanonicalVarInfo<TyCtxt<'tcx>>;
@@ -141,7 +141,7 @@
 }
 
 pub type QueryOutlivesConstraint<'tcx> =
-    (ty::OutlivesPredicate<GenericArg<'tcx>, Region<'tcx>>, ConstraintCategory<'tcx>);
+    (ty::OutlivesPredicate<'tcx, GenericArg<'tcx>>, ConstraintCategory<'tcx>);
 
 TrivialTypeTraversalImpls! {
     crate::infer::canonical::Certainty,
diff --git a/compiler/rustc_middle/src/query/on_disk_cache.rs b/compiler/rustc_middle/src/query/on_disk_cache.rs
index 2dcb587..941911c 100644
--- a/compiler/rustc_middle/src/query/on_disk_cache.rs
+++ b/compiler/rustc_middle/src/query/on_disk_cache.rs
@@ -154,24 +154,25 @@
 
 impl<'sess> OnDiskCache<'sess> {
     /// Creates a new `OnDiskCache` instance from the serialized data in `data`.
-    pub fn new(sess: &'sess Session, data: Mmap, start_pos: usize) -> Self {
-        debug_assert!(sess.opts.incremental.is_some());
+    ///
+    /// The serialized cache has some basic integrity checks, if those checks indicate that the
+    /// on-disk data is corrupt, an error is returned.
+    pub fn new(sess: &'sess Session, data: Mmap, start_pos: usize) -> Result<Self, ()> {
+        assert!(sess.opts.incremental.is_some());
 
-        // Wrap in a scope so we can borrow `data`.
-        let footer: Footer = {
-            let mut decoder = MemDecoder::new(&data, start_pos);
+        let mut decoder = MemDecoder::new(&data, start_pos)?;
 
-            // Decode the *position* of the footer, which can be found in the
-            // last 8 bytes of the file.
-            let footer_pos = decoder
-                .with_position(decoder.len() - IntEncodedWithFixedSize::ENCODED_SIZE, |decoder| {
-                    IntEncodedWithFixedSize::decode(decoder).0 as usize
-                });
-            // Decode the file footer, which contains all the lookup tables, etc.
-            decoder.with_position(footer_pos, |decoder| decode_tagged(decoder, TAG_FILE_FOOTER))
-        };
+        // Decode the *position* of the footer, which can be found in the
+        // last 8 bytes of the file.
+        let footer_pos = decoder
+            .with_position(decoder.len() - IntEncodedWithFixedSize::ENCODED_SIZE, |decoder| {
+                IntEncodedWithFixedSize::decode(decoder).0 as usize
+            });
+        // Decode the file footer, which contains all the lookup tables, etc.
+        let footer: Footer =
+            decoder.with_position(footer_pos, |decoder| decode_tagged(decoder, TAG_FILE_FOOTER));
 
-        Self {
+        Ok(Self {
             serialized_data: RwLock::new(Some(data)),
             file_index_to_stable_id: footer.file_index_to_stable_id,
             file_index_to_file: Default::default(),
@@ -184,7 +185,7 @@
             expn_data: footer.expn_data,
             foreign_expn_data: footer.foreign_expn_data,
             hygiene_context: Default::default(),
-        }
+        })
     }
 
     pub fn new_empty(source_map: &'sess SourceMap) -> Self {
@@ -437,7 +438,8 @@
         let serialized_data = self.serialized_data.read();
         let mut decoder = CacheDecoder {
             tcx,
-            opaque: MemDecoder::new(serialized_data.as_deref().unwrap_or(&[]), pos.to_usize()),
+            opaque: MemDecoder::new(serialized_data.as_deref().unwrap_or(&[]), pos.to_usize())
+                .unwrap(),
             source_map: self.source_map,
             file_index_to_file: &self.file_index_to_file,
             file_index_to_stable_id: &self.file_index_to_stable_id,
@@ -558,7 +560,7 @@
     {
         debug_assert!(pos < self.opaque.len());
 
-        let new_opaque = MemDecoder::new(self.opaque.data(), pos);
+        let new_opaque = self.opaque.split_at(pos);
         let old_opaque = mem::replace(&mut self.opaque, new_opaque);
         let r = f(self);
         self.opaque = old_opaque;
diff --git a/compiler/rustc_middle/src/ty/codec.rs b/compiler/rustc_middle/src/ty/codec.rs
index c0effe9..07652b4 100644
--- a/compiler/rustc_middle/src/ty/codec.rs
+++ b/compiler/rustc_middle/src/ty/codec.rs
@@ -115,18 +115,11 @@
     }
 }
 
-impl<'tcx, E: TyEncoder<I = TyCtxt<'tcx>>> Encodable<E>
-    for ty::Binder<'tcx, ty::PredicateKind<'tcx>>
-{
-    fn encode(&self, e: &mut E) {
-        self.bound_vars().encode(e);
-        encode_with_shorthand(e, &self.skip_binder(), TyEncoder::predicate_shorthands);
-    }
-}
-
 impl<'tcx, E: TyEncoder<I = TyCtxt<'tcx>>> Encodable<E> for ty::Predicate<'tcx> {
     fn encode(&self, e: &mut E) {
-        self.kind().encode(e);
+        let kind = self.kind();
+        kind.bound_vars().encode(e);
+        encode_with_shorthand(e, &kind.skip_binder(), TyEncoder::predicate_shorthands);
     }
 }
 
@@ -233,13 +226,11 @@
     }
 }
 
-impl<'tcx, D: TyDecoder<I = TyCtxt<'tcx>>> Decodable<D>
-    for ty::Binder<'tcx, ty::PredicateKind<'tcx>>
-{
-    fn decode(decoder: &mut D) -> ty::Binder<'tcx, ty::PredicateKind<'tcx>> {
+impl<'tcx, D: TyDecoder<I = TyCtxt<'tcx>>> Decodable<D> for ty::Predicate<'tcx> {
+    fn decode(decoder: &mut D) -> ty::Predicate<'tcx> {
         let bound_vars = Decodable::decode(decoder);
         // Handle shorthands first, if we have a usize > 0x80.
-        ty::Binder::bind_with_vars(
+        let predicate_kind = ty::Binder::bind_with_vars(
             if decoder.positioned_at_shorthand() {
                 let pos = decoder.read_usize();
                 assert!(pos >= SHORTHAND_OFFSET);
@@ -250,13 +241,7 @@
                 <ty::PredicateKind<'tcx> as Decodable<D>>::decode(decoder)
             },
             bound_vars,
-        )
-    }
-}
-
-impl<'tcx, D: TyDecoder<I = TyCtxt<'tcx>>> Decodable<D> for ty::Predicate<'tcx> {
-    fn decode(decoder: &mut D) -> ty::Predicate<'tcx> {
-        let predicate_kind = Decodable::decode(decoder);
+        );
         decoder.interner().mk_predicate(predicate_kind)
     }
 }
@@ -599,32 +584,3 @@
         }
     }
 }
-
-macro_rules! impl_binder_encode_decode {
-    ($($t:ty),+ $(,)?) => {
-        $(
-            impl<'tcx, E: TyEncoder<I = TyCtxt<'tcx>>> Encodable<E> for ty::Binder<'tcx, $t> {
-                fn encode(&self, e: &mut E) {
-                    self.bound_vars().encode(e);
-                    self.as_ref().skip_binder().encode(e);
-                }
-            }
-            impl<'tcx, D: TyDecoder<I = TyCtxt<'tcx>>> Decodable<D> for ty::Binder<'tcx, $t> {
-                fn decode(decoder: &mut D) -> Self {
-                    let bound_vars = Decodable::decode(decoder);
-                    ty::Binder::bind_with_vars(Decodable::decode(decoder), bound_vars)
-                }
-            }
-        )*
-    }
-}
-
-impl_binder_encode_decode! {
-    &'tcx ty::List<Ty<'tcx>>,
-    ty::FnSig<'tcx>,
-    ty::Predicate<'tcx>,
-    ty::TraitPredicate<'tcx>,
-    ty::ExistentialPredicate<'tcx>,
-    ty::TraitRef<'tcx>,
-    ty::ExistentialTraitRef<'tcx>,
-}
diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs
index 8185c99..896114e 100644
--- a/compiler/rustc_middle/src/ty/context.rs
+++ b/compiler/rustc_middle/src/ty/context.rs
@@ -31,8 +31,7 @@
     self, AdtDef, AdtDefData, AdtKind, Binder, Clause, Clauses, Const, ConstData,
     GenericParamDefKind, ImplPolarity, List, ListWithCachedTypeInfo, ParamConst, ParamTy, Pattern,
     PatternKind, PolyExistentialPredicate, PolyFnSig, Predicate, PredicateKind, PredicatePolarity,
-    Region, RegionKind, ReprOptions, TraitObjectVisitor, Ty, TyKind, TyVid, TypeVisitable,
-    Visibility,
+    Region, RegionKind, ReprOptions, TraitObjectVisitor, Ty, TyKind, TyVid, Visibility,
 };
 use crate::ty::{GenericArg, GenericArgs, GenericArgsRef};
 use rustc_ast::{self as ast, attr};
@@ -96,9 +95,8 @@
     type GenericArg = ty::GenericArg<'tcx>;
     type Term = ty::Term<'tcx>;
 
-    type Binder<T: TypeVisitable<TyCtxt<'tcx>>> = Binder<'tcx, T>;
-    type BoundVars = &'tcx List<ty::BoundVariableKind>;
-    type BoundVar = ty::BoundVariableKind;
+    type BoundVarKinds = &'tcx List<ty::BoundVariableKind>;
+    type BoundVarKind = ty::BoundVariableKind;
 
     type CanonicalVars = CanonicalVarInfos<'tcx>;
     type PredefinedOpaques = solve::PredefinedOpaques<'tcx>;
@@ -123,7 +121,6 @@
     type Abi = abi::Abi;
 
     type Const = ty::Const<'tcx>;
-    type AliasConst = ty::UnevaluatedConst<'tcx>;
     type PlaceholderConst = ty::PlaceholderConst;
     type ParamConst = ty::ParamConst;
     type BoundConst = ty::BoundVar;
@@ -138,15 +135,7 @@
 
     type ParamEnv = ty::ParamEnv<'tcx>;
     type Predicate = Predicate<'tcx>;
-    type TraitPredicate = ty::TraitPredicate<'tcx>;
-    type RegionOutlivesPredicate = ty::RegionOutlivesPredicate<'tcx>;
-    type TypeOutlivesPredicate = ty::TypeOutlivesPredicate<'tcx>;
-    type ProjectionPredicate = ty::ProjectionPredicate<'tcx>;
-    type NormalizesTo = ty::NormalizesTo<'tcx>;
-    type SubtypePredicate = ty::SubtypePredicate<'tcx>;
-
-    type CoercePredicate = ty::CoercePredicate<'tcx>;
-    type ClosureKind = ty::ClosureKind;
+    type Clause = Clause<'tcx>;
 
     type Clauses = ty::Clauses<'tcx>;
 
@@ -245,6 +234,10 @@
 }
 
 impl<'tcx> rustc_type_ir::inherent::Safety<TyCtxt<'tcx>> for hir::Safety {
+    fn is_safe(self) -> bool {
+        matches!(self, hir::Safety::Safe)
+    }
+
     fn prefix_str(self) -> &'static str {
         self.prefix_str()
     }
diff --git a/compiler/rustc_middle/src/ty/generic_args.rs b/compiler/rustc_middle/src/ty/generic_args.rs
index 3d263e6..cb11bb3 100644
--- a/compiler/rustc_middle/src/ty/generic_args.rs
+++ b/compiler/rustc_middle/src/ty/generic_args.rs
@@ -51,6 +51,14 @@
     fn identity_for_item(tcx: TyCtxt<'tcx>, def_id: DefId) -> ty::GenericArgsRef<'tcx> {
         GenericArgs::identity_for_item(tcx, def_id)
     }
+
+    fn extend_with_error(
+        tcx: TyCtxt<'tcx>,
+        def_id: DefId,
+        original_args: &[ty::GenericArg<'tcx>],
+    ) -> ty::GenericArgsRef<'tcx> {
+        ty::GenericArgs::extend_with_error(tcx, def_id, original_args)
+    }
 }
 
 impl<'tcx> rustc_type_ir::inherent::IntoKind for GenericArg<'tcx> {
diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs
index 8bb19bc..f274098 100644
--- a/compiler/rustc_middle/src/ty/mod.rs
+++ b/compiler/rustc_middle/src/ty/mod.rs
@@ -1814,6 +1814,11 @@
         self.get_attrs(did, attr).next().is_some()
     }
 
+    /// Determines whether an item is annotated with a multi-segement attribute
+    pub fn has_attrs_with_path(self, did: impl Into<DefId>, attrs: &[Symbol]) -> bool {
+        self.get_attrs_by_path(did.into(), attrs).next().is_some()
+    }
+
     /// Returns `true` if this is an `auto trait`.
     pub fn trait_is_auto(self, trait_def_id: DefId) -> bool {
         self.trait_def(trait_def_id).has_auto_impl
diff --git a/compiler/rustc_middle/src/ty/predicate.rs b/compiler/rustc_middle/src/ty/predicate.rs
index be91249..293cc0a 100644
--- a/compiler/rustc_middle/src/ty/predicate.rs
+++ b/compiler/rustc_middle/src/ty/predicate.rs
@@ -1,15 +1,13 @@
 use rustc_data_structures::captures::Captures;
 use rustc_data_structures::intern::Interned;
 use rustc_hir::def_id::DefId;
-use rustc_macros::{
-    extension, HashStable, Lift, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable,
-};
+use rustc_macros::{extension, HashStable};
 use rustc_type_ir as ir;
 use std::cmp::Ordering;
 
 use crate::ty::{
-    self, Binder, DebruijnIndex, EarlyBinder, PredicatePolarity, Term, Ty, TyCtxt, TypeFlags,
-    Upcast, UpcastFrom, WithCachedTypeInfo,
+    self, DebruijnIndex, EarlyBinder, PredicatePolarity, Ty, TyCtxt, TypeFlags, Upcast, UpcastFrom,
+    WithCachedTypeInfo,
 };
 
 pub type TraitRef<'tcx> = ir::TraitRef<TyCtxt<'tcx>>;
@@ -24,6 +22,15 @@
 pub type NormalizesTo<'tcx> = ir::NormalizesTo<TyCtxt<'tcx>>;
 pub type CoercePredicate<'tcx> = ir::CoercePredicate<TyCtxt<'tcx>>;
 pub type SubtypePredicate<'tcx> = ir::SubtypePredicate<TyCtxt<'tcx>>;
+pub type OutlivesPredicate<'tcx, T> = ir::OutlivesPredicate<TyCtxt<'tcx>, T>;
+pub type RegionOutlivesPredicate<'tcx> = OutlivesPredicate<'tcx, ty::Region<'tcx>>;
+pub type TypeOutlivesPredicate<'tcx> = OutlivesPredicate<'tcx, Ty<'tcx>>;
+pub type PolyTraitPredicate<'tcx> = ty::Binder<'tcx, TraitPredicate<'tcx>>;
+pub type PolyRegionOutlivesPredicate<'tcx> = ty::Binder<'tcx, RegionOutlivesPredicate<'tcx>>;
+pub type PolyTypeOutlivesPredicate<'tcx> = ty::Binder<'tcx, TypeOutlivesPredicate<'tcx>>;
+pub type PolySubtypePredicate<'tcx> = ty::Binder<'tcx, SubtypePredicate<'tcx>>;
+pub type PolyCoercePredicate<'tcx> = ty::Binder<'tcx, CoercePredicate<'tcx>>;
+pub type PolyProjectionPredicate<'tcx> = ty::Binder<'tcx, ProjectionPredicate<'tcx>>;
 
 /// A statement that can be proven by a trait solver. This includes things that may
 /// show up in where clauses, such as trait predicates and projection predicates,
@@ -155,6 +162,8 @@
     pub(super) Interned<'tcx, WithCachedTypeInfo<ty::Binder<'tcx, PredicateKind<'tcx>>>>,
 );
 
+impl<'tcx> rustc_type_ir::inherent::Clause<TyCtxt<'tcx>> for Clause<'tcx> {}
+
 impl<'tcx> Clause<'tcx> {
     pub fn as_predicate(self) -> Predicate<'tcx> {
         Predicate(self.0)
@@ -231,34 +240,6 @@
 
 pub type PolyExistentialPredicate<'tcx> = ty::Binder<'tcx, ExistentialPredicate<'tcx>>;
 
-impl<'tcx> PolyExistentialPredicate<'tcx> {
-    /// Given an existential predicate like `?Self: PartialEq<u32>` (e.g., derived from `dyn PartialEq<u32>`),
-    /// and a concrete type `self_ty`, returns a full predicate where the existentially quantified variable `?Self`
-    /// has been replaced with `self_ty` (e.g., `self_ty: PartialEq<u32>`, in our example).
-    pub fn with_self_ty(&self, tcx: TyCtxt<'tcx>, self_ty: Ty<'tcx>) -> ty::Clause<'tcx> {
-        match self.skip_binder() {
-            ExistentialPredicate::Trait(tr) => {
-                self.rebind(tr).with_self_ty(tcx, self_ty).upcast(tcx)
-            }
-            ExistentialPredicate::Projection(p) => {
-                self.rebind(p.with_self_ty(tcx, self_ty)).upcast(tcx)
-            }
-            ExistentialPredicate::AutoTrait(did) => {
-                let generics = tcx.generics_of(did);
-                let trait_ref = if generics.own_params.len() == 1 {
-                    ty::TraitRef::new(tcx, did, [self_ty])
-                } else {
-                    // If this is an ill-formed auto trait, then synthesize
-                    // new error args for the missing generics.
-                    let err_args = ty::GenericArgs::extend_with_error(tcx, did, &[self_ty.into()]);
-                    ty::TraitRef::new(tcx, did, err_args)
-                };
-                self.rebind(trait_ref).upcast(tcx)
-            }
-        }
-    }
-}
-
 impl<'tcx> ty::List<ty::PolyExistentialPredicate<'tcx>> {
     /// Returns the "principal `DefId`" of this set of existential predicates.
     ///
@@ -322,49 +303,9 @@
 }
 
 pub type PolyTraitRef<'tcx> = ty::Binder<'tcx, TraitRef<'tcx>>;
-
-impl<'tcx> PolyTraitRef<'tcx> {
-    pub fn self_ty(&self) -> ty::Binder<'tcx, Ty<'tcx>> {
-        self.map_bound_ref(|tr| tr.self_ty())
-    }
-
-    pub fn def_id(&self) -> DefId {
-        self.skip_binder().def_id
-    }
-}
-
 pub type PolyExistentialTraitRef<'tcx> = ty::Binder<'tcx, ExistentialTraitRef<'tcx>>;
-
-impl<'tcx> PolyExistentialTraitRef<'tcx> {
-    pub fn def_id(&self) -> DefId {
-        self.skip_binder().def_id
-    }
-
-    /// Object types don't have a self type specified. Therefore, when
-    /// we convert the principal trait-ref into a normal trait-ref,
-    /// you must give *some* self type. A common choice is `mk_err()`
-    /// or some placeholder type.
-    pub fn with_self_ty(&self, tcx: TyCtxt<'tcx>, self_ty: Ty<'tcx>) -> ty::PolyTraitRef<'tcx> {
-        self.map_bound(|trait_ref| trait_ref.with_self_ty(tcx, self_ty))
-    }
-}
-
 pub type PolyExistentialProjection<'tcx> = ty::Binder<'tcx, ExistentialProjection<'tcx>>;
 
-impl<'tcx> PolyExistentialProjection<'tcx> {
-    pub fn with_self_ty(
-        &self,
-        tcx: TyCtxt<'tcx>,
-        self_ty: Ty<'tcx>,
-    ) -> ty::PolyProjectionPredicate<'tcx> {
-        self.map_bound(|p| p.with_self_ty(tcx, self_ty))
-    }
-
-    pub fn item_def_id(&self) -> DefId {
-        self.skip_binder().def_id
-    }
-}
-
 impl<'tcx> Clause<'tcx> {
     /// Performs a instantiation suitable for going from a
     /// poly-trait-ref to supertraits that must hold if that
@@ -471,73 +412,6 @@
     }
 }
 
-pub type PolyTraitPredicate<'tcx> = ty::Binder<'tcx, TraitPredicate<'tcx>>;
-
-impl<'tcx> PolyTraitPredicate<'tcx> {
-    pub fn def_id(self) -> DefId {
-        // Ok to skip binder since trait `DefId` does not care about regions.
-        self.skip_binder().def_id()
-    }
-
-    pub fn self_ty(self) -> ty::Binder<'tcx, Ty<'tcx>> {
-        self.map_bound(|trait_ref| trait_ref.self_ty())
-    }
-
-    #[inline]
-    pub fn polarity(self) -> PredicatePolarity {
-        self.skip_binder().polarity
-    }
-}
-
-/// `A: B`
-#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, TyEncodable, TyDecodable)]
-#[derive(HashStable, TypeFoldable, TypeVisitable, Lift)]
-pub struct OutlivesPredicate<A, B>(pub A, pub B);
-pub type RegionOutlivesPredicate<'tcx> = OutlivesPredicate<ty::Region<'tcx>, ty::Region<'tcx>>;
-pub type TypeOutlivesPredicate<'tcx> = OutlivesPredicate<Ty<'tcx>, ty::Region<'tcx>>;
-pub type PolyRegionOutlivesPredicate<'tcx> = ty::Binder<'tcx, RegionOutlivesPredicate<'tcx>>;
-pub type PolyTypeOutlivesPredicate<'tcx> = ty::Binder<'tcx, TypeOutlivesPredicate<'tcx>>;
-
-pub type PolySubtypePredicate<'tcx> = ty::Binder<'tcx, SubtypePredicate<'tcx>>;
-
-pub type PolyCoercePredicate<'tcx> = ty::Binder<'tcx, CoercePredicate<'tcx>>;
-
-pub type PolyProjectionPredicate<'tcx> = Binder<'tcx, ProjectionPredicate<'tcx>>;
-
-impl<'tcx> PolyProjectionPredicate<'tcx> {
-    /// Returns the `DefId` of the trait of the associated item being projected.
-    #[inline]
-    pub fn trait_def_id(&self, tcx: TyCtxt<'tcx>) -> DefId {
-        self.skip_binder().projection_term.trait_def_id(tcx)
-    }
-
-    /// Get the [PolyTraitRef] required for this projection to be well formed.
-    /// Note that for generic associated types the predicates of the associated
-    /// type also need to be checked.
-    #[inline]
-    pub fn required_poly_trait_ref(&self, tcx: TyCtxt<'tcx>) -> PolyTraitRef<'tcx> {
-        // Note: unlike with `TraitRef::to_poly_trait_ref()`,
-        // `self.0.trait_ref` is permitted to have escaping regions.
-        // This is because here `self` has a `Binder` and so does our
-        // return value, so we are preserving the number of binding
-        // levels.
-        self.map_bound(|predicate| predicate.projection_term.trait_ref(tcx))
-    }
-
-    pub fn term(&self) -> Binder<'tcx, Term<'tcx>> {
-        self.map_bound(|predicate| predicate.term)
-    }
-
-    /// The `DefId` of the `TraitItem` for the associated type.
-    ///
-    /// Note that this is not the `DefId` of the `TraitRef` containing this
-    /// associated type, which is in `tcx.associated_item(projection_def_id()).container`.
-    pub fn projection_def_id(&self) -> DefId {
-        // Ok to skip binder since trait `DefId` does not care about regions.
-        self.skip_binder().projection_term.def_id
-    }
-}
-
 pub trait ToPolyTraitRef<'tcx> {
     fn to_poly_trait_ref(&self) -> PolyTraitRef<'tcx>;
 }
@@ -554,8 +428,8 @@
     }
 }
 
-impl<'tcx> UpcastFrom<TyCtxt<'tcx>, Binder<'tcx, PredicateKind<'tcx>>> for Predicate<'tcx> {
-    fn upcast_from(from: Binder<'tcx, PredicateKind<'tcx>>, tcx: TyCtxt<'tcx>) -> Self {
+impl<'tcx> UpcastFrom<TyCtxt<'tcx>, ty::Binder<'tcx, PredicateKind<'tcx>>> for Predicate<'tcx> {
+    fn upcast_from(from: ty::Binder<'tcx, PredicateKind<'tcx>>, tcx: TyCtxt<'tcx>) -> Self {
         tcx.mk_predicate(from)
     }
 }
@@ -566,8 +440,8 @@
     }
 }
 
-impl<'tcx> UpcastFrom<TyCtxt<'tcx>, Binder<'tcx, ClauseKind<'tcx>>> for Predicate<'tcx> {
-    fn upcast_from(from: Binder<'tcx, ClauseKind<'tcx>>, tcx: TyCtxt<'tcx>) -> Self {
+impl<'tcx> UpcastFrom<TyCtxt<'tcx>, ty::Binder<'tcx, ClauseKind<'tcx>>> for Predicate<'tcx> {
+    fn upcast_from(from: ty::Binder<'tcx, ClauseKind<'tcx>>, tcx: TyCtxt<'tcx>) -> Self {
         tcx.mk_predicate(from.map_bound(PredicateKind::Clause))
     }
 }
@@ -580,12 +454,12 @@
 
 impl<'tcx> UpcastFrom<TyCtxt<'tcx>, ClauseKind<'tcx>> for Clause<'tcx> {
     fn upcast_from(from: ClauseKind<'tcx>, tcx: TyCtxt<'tcx>) -> Self {
-        tcx.mk_predicate(Binder::dummy(PredicateKind::Clause(from))).expect_clause()
+        tcx.mk_predicate(ty::Binder::dummy(PredicateKind::Clause(from))).expect_clause()
     }
 }
 
-impl<'tcx> UpcastFrom<TyCtxt<'tcx>, Binder<'tcx, ClauseKind<'tcx>>> for Clause<'tcx> {
-    fn upcast_from(from: Binder<'tcx, ClauseKind<'tcx>>, tcx: TyCtxt<'tcx>) -> Self {
+impl<'tcx> UpcastFrom<TyCtxt<'tcx>, ty::Binder<'tcx, ClauseKind<'tcx>>> for Clause<'tcx> {
+    fn upcast_from(from: ty::Binder<'tcx, ClauseKind<'tcx>>, tcx: TyCtxt<'tcx>) -> Self {
         tcx.mk_predicate(from.map_bound(|clause| PredicateKind::Clause(clause))).expect_clause()
     }
 }
@@ -609,22 +483,22 @@
     }
 }
 
-impl<'tcx> UpcastFrom<TyCtxt<'tcx>, Binder<'tcx, TraitRef<'tcx>>> for Predicate<'tcx> {
-    fn upcast_from(from: Binder<'tcx, TraitRef<'tcx>>, tcx: TyCtxt<'tcx>) -> Self {
+impl<'tcx> UpcastFrom<TyCtxt<'tcx>, ty::Binder<'tcx, TraitRef<'tcx>>> for Predicate<'tcx> {
+    fn upcast_from(from: ty::Binder<'tcx, TraitRef<'tcx>>, tcx: TyCtxt<'tcx>) -> Self {
         let pred: PolyTraitPredicate<'tcx> = from.upcast(tcx);
         pred.upcast(tcx)
     }
 }
 
-impl<'tcx> UpcastFrom<TyCtxt<'tcx>, Binder<'tcx, TraitRef<'tcx>>> for Clause<'tcx> {
-    fn upcast_from(from: Binder<'tcx, TraitRef<'tcx>>, tcx: TyCtxt<'tcx>) -> Self {
+impl<'tcx> UpcastFrom<TyCtxt<'tcx>, ty::Binder<'tcx, TraitRef<'tcx>>> for Clause<'tcx> {
+    fn upcast_from(from: ty::Binder<'tcx, TraitRef<'tcx>>, tcx: TyCtxt<'tcx>) -> Self {
         let pred: PolyTraitPredicate<'tcx> = from.upcast(tcx);
         pred.upcast(tcx)
     }
 }
 
-impl<'tcx> UpcastFrom<TyCtxt<'tcx>, Binder<'tcx, TraitRef<'tcx>>> for PolyTraitPredicate<'tcx> {
-    fn upcast_from(from: Binder<'tcx, TraitRef<'tcx>>, _tcx: TyCtxt<'tcx>) -> Self {
+impl<'tcx> UpcastFrom<TyCtxt<'tcx>, ty::Binder<'tcx, TraitRef<'tcx>>> for PolyTraitPredicate<'tcx> {
+    fn upcast_from(from: ty::Binder<'tcx, TraitRef<'tcx>>, _tcx: TyCtxt<'tcx>) -> Self {
         from.map_bound(|trait_ref| TraitPredicate {
             trait_ref,
             polarity: PredicatePolarity::Positive,
@@ -664,10 +538,8 @@
     }
 }
 
-impl<'tcx> UpcastFrom<TyCtxt<'tcx>, OutlivesPredicate<Ty<'tcx>, ty::Region<'tcx>>>
-    for Predicate<'tcx>
-{
-    fn upcast_from(from: OutlivesPredicate<Ty<'tcx>, ty::Region<'tcx>>, tcx: TyCtxt<'tcx>) -> Self {
+impl<'tcx> UpcastFrom<TyCtxt<'tcx>, TypeOutlivesPredicate<'tcx>> for Predicate<'tcx> {
+    fn upcast_from(from: TypeOutlivesPredicate<'tcx>, tcx: TyCtxt<'tcx>) -> Self {
         ty::Binder::dummy(PredicateKind::Clause(ClauseKind::TypeOutlives(from))).upcast(tcx)
     }
 }
diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs
index 0dbb17e..a1ead1b 100644
--- a/compiler/rustc_middle/src/ty/print/pretty.rs
+++ b/compiler/rustc_middle/src/ty/print/pretty.rs
@@ -2860,10 +2860,9 @@
     }
 }
 
-impl<'tcx, T, U, P: PrettyPrinter<'tcx>> Print<'tcx, P> for ty::OutlivesPredicate<T, U>
+impl<'tcx, T, P: PrettyPrinter<'tcx>> Print<'tcx, P> for ty::OutlivesPredicate<'tcx, T>
 where
     T: Print<'tcx, P>,
-    U: Print<'tcx, P>,
 {
     fn print(&self, cx: &mut P) -> Result<(), PrintError> {
         define_scoped_cx!(cx);
@@ -2934,12 +2933,13 @@
     }
 }
 
+#[extension(pub trait PrintPolyTraitRefExt<'tcx>)]
 impl<'tcx> ty::Binder<'tcx, ty::TraitRef<'tcx>> {
-    pub fn print_only_trait_path(self) -> ty::Binder<'tcx, TraitRefPrintOnlyTraitPath<'tcx>> {
+    fn print_only_trait_path(self) -> ty::Binder<'tcx, TraitRefPrintOnlyTraitPath<'tcx>> {
         self.map_bound(|tr| tr.print_only_trait_path())
     }
 
-    pub fn print_trait_sugared(self) -> ty::Binder<'tcx, TraitRefPrintSugared<'tcx>> {
+    fn print_trait_sugared(self) -> ty::Binder<'tcx, TraitRefPrintSugared<'tcx>> {
         self.map_bound(|tr| tr.print_trait_sugared())
     }
 }
@@ -2960,8 +2960,9 @@
     }
 }
 
+#[extension(pub trait PrintPolyTraitPredicateExt<'tcx>)]
 impl<'tcx> ty::PolyTraitPredicate<'tcx> {
-    pub fn print_modifiers_and_trait_path(
+    fn print_modifiers_and_trait_path(
         self,
     ) -> ty::Binder<'tcx, TraitPredPrintModifiersAndPath<'tcx>> {
         self.map_bound(TraitPredPrintModifiersAndPath)
@@ -3014,21 +3015,7 @@
     ty::Region<'tcx>,
     Ty<'tcx>,
     &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
-    ty::Const<'tcx>,
-
-    // HACK(eddyb) these are exhaustive instead of generic,
-    // because `for<'tcx>` isn't possible yet.
-    ty::PolyExistentialProjection<'tcx>,
-    ty::PolyExistentialTraitRef<'tcx>,
-    ty::Binder<'tcx, ty::TraitRef<'tcx>>,
-    ty::Binder<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>,
-    ty::Binder<'tcx, TraitRefPrintSugared<'tcx>>,
-    ty::Binder<'tcx, ty::FnSig<'tcx>>,
-    ty::Binder<'tcx, ty::TraitPredicate<'tcx>>,
-    ty::Binder<'tcx, TraitPredPrintModifiersAndPath<'tcx>>,
-    ty::Binder<'tcx, ty::ProjectionPredicate<'tcx>>,
-    ty::OutlivesPredicate<Ty<'tcx>, ty::Region<'tcx>>,
-    ty::OutlivesPredicate<ty::Region<'tcx>, ty::Region<'tcx>>
+    ty::Const<'tcx>
 }
 
 define_print! {
diff --git a/compiler/rustc_middle/src/ty/region.rs b/compiler/rustc_middle/src/ty/region.rs
index 551e2ea..d7da373 100644
--- a/compiler/rustc_middle/src/ty/region.rs
+++ b/compiler/rustc_middle/src/ty/region.rs
@@ -384,6 +384,10 @@
     fn var(self) -> BoundVar {
         self.var
     }
+
+    fn assert_eq(self, var: ty::BoundVariableKind) {
+        assert_eq!(self.kind, var.expect_region())
+    }
 }
 
 impl core::fmt::Debug for BoundRegion {
diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs
index 81d92a2..af3aa3b 100644
--- a/compiler/rustc_middle/src/ty/structural_impls.rs
+++ b/compiler/rustc_middle/src/ty/structural_impls.rs
@@ -387,38 +387,6 @@
     }
 }
 
-impl<'tcx, T: TypeFoldable<TyCtxt<'tcx>>> TypeFoldable<TyCtxt<'tcx>> for ty::Binder<'tcx, T> {
-    fn try_fold_with<F: FallibleTypeFolder<TyCtxt<'tcx>>>(
-        self,
-        folder: &mut F,
-    ) -> Result<Self, F::Error> {
-        folder.try_fold_binder(self)
-    }
-}
-
-impl<'tcx, T: TypeVisitable<TyCtxt<'tcx>>> TypeVisitable<TyCtxt<'tcx>> for ty::Binder<'tcx, T> {
-    fn visit_with<V: TypeVisitor<TyCtxt<'tcx>>>(&self, visitor: &mut V) -> V::Result {
-        visitor.visit_binder(self)
-    }
-}
-
-impl<'tcx, T: TypeFoldable<TyCtxt<'tcx>>> TypeSuperFoldable<TyCtxt<'tcx>> for ty::Binder<'tcx, T> {
-    fn try_super_fold_with<F: FallibleTypeFolder<TyCtxt<'tcx>>>(
-        self,
-        folder: &mut F,
-    ) -> Result<Self, F::Error> {
-        self.try_map_bound(|ty| ty.try_fold_with(folder))
-    }
-}
-
-impl<'tcx, T: TypeVisitable<TyCtxt<'tcx>>> TypeSuperVisitable<TyCtxt<'tcx>>
-    for ty::Binder<'tcx, T>
-{
-    fn super_visit_with<V: TypeVisitor<TyCtxt<'tcx>>>(&self, visitor: &mut V) -> V::Result {
-        self.as_ref().skip_binder().visit_with(visitor)
-    }
-}
-
 impl<'tcx> TypeFoldable<TyCtxt<'tcx>> for &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>> {
     fn try_fold_with<F: FallibleTypeFolder<TyCtxt<'tcx>>>(
         self,
diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs
index 2d9d178..fc9a854 100644
--- a/compiler/rustc_middle/src/ty/sty.rs
+++ b/compiler/rustc_middle/src/ty/sty.rs
@@ -3,17 +3,16 @@
 #![allow(rustc::usage_of_ty_tykind)]
 
 use crate::infer::canonical::Canonical;
-use crate::ty::visit::ValidateBoundVars;
 use crate::ty::InferTy::*;
 use crate::ty::{
     self, AdtDef, BoundRegionKind, Discr, Region, Ty, TyCtxt, TypeFlags, TypeSuperVisitable,
-    TypeVisitable, TypeVisitableExt, TypeVisitor,
+    TypeVisitable, TypeVisitor,
 };
 use crate::ty::{GenericArg, GenericArgs, GenericArgsRef};
 use crate::ty::{List, ParamEnv};
 use hir::def::{CtorKind, DefKind};
 use rustc_data_structures::captures::Captures;
-use rustc_errors::{DiagArgValue, ErrorGuaranteed, IntoDiagArg, MultiSpan};
+use rustc_errors::{ErrorGuaranteed, MultiSpan};
 use rustc_hir as hir;
 use rustc_hir::def_id::DefId;
 use rustc_hir::LangItem;
@@ -21,11 +20,11 @@
 use rustc_span::symbol::{sym, Symbol};
 use rustc_span::{Span, DUMMY_SP};
 use rustc_target::abi::{FieldIdx, VariantIdx, FIRST_VARIANT};
-use rustc_target::spec::abi::{self, Abi};
+use rustc_target::spec::abi;
 use std::assert_matches::debug_assert_matches;
 use std::borrow::Cow;
 use std::iter;
-use std::ops::{ControlFlow, Deref, Range};
+use std::ops::{ControlFlow, Range};
 use ty::util::IntTypeExt;
 
 use rustc_type_ir::TyKind::*;
@@ -40,6 +39,7 @@
 pub type TypeAndMut<'tcx> = ir::TypeAndMut<TyCtxt<'tcx>>;
 pub type AliasTy<'tcx> = ir::AliasTy<TyCtxt<'tcx>>;
 pub type FnSig<'tcx> = ir::FnSig<TyCtxt<'tcx>>;
+pub type Binder<'tcx, T> = ir::Binder<TyCtxt<'tcx>, T>;
 
 pub trait Article {
     fn article(&self) -> &'static str;
@@ -373,7 +373,7 @@
         self.split().signature_parts_ty
     }
 
-    pub fn coroutine_closure_sig(self) -> ty::Binder<'tcx, CoroutineClosureSignature<'tcx>> {
+    pub fn coroutine_closure_sig(self) -> Binder<'tcx, CoroutineClosureSignature<'tcx>> {
         let interior = self.coroutine_witness_ty();
         let ty::FnPtr(sig) = self.signature_parts_ty().kind() else { bug!() };
         sig.map_bound(|sig| {
@@ -898,203 +898,6 @@
     }
 }
 
-/// Binder is a binder for higher-ranked lifetimes or types. It is part of the
-/// compiler's representation for things like `for<'a> Fn(&'a isize)`
-/// (which would be represented by the type `PolyTraitRef ==
-/// Binder<'tcx, TraitRef>`). Note that when we instantiate,
-/// erase, or otherwise "discharge" these bound vars, we change the
-/// type from `Binder<'tcx, T>` to just `T` (see
-/// e.g., `liberate_late_bound_regions`).
-///
-/// `Decodable` and `Encodable` are implemented for `Binder<T>` using the `impl_binder_encode_decode!` macro.
-#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
-#[derive(HashStable, Lift)]
-pub struct Binder<'tcx, T> {
-    value: T,
-    bound_vars: &'tcx List<BoundVariableKind>,
-}
-
-impl<'tcx, T> Binder<'tcx, T>
-where
-    T: TypeVisitable<TyCtxt<'tcx>>,
-{
-    /// Wraps `value` in a binder, asserting that `value` does not
-    /// contain any bound vars that would be bound by the
-    /// binder. This is commonly used to 'inject' a value T into a
-    /// different binding level.
-    #[track_caller]
-    pub fn dummy(value: T) -> Binder<'tcx, T> {
-        assert!(
-            !value.has_escaping_bound_vars(),
-            "`{value:?}` has escaping bound vars, so it cannot be wrapped in a dummy binder."
-        );
-        Binder { value, bound_vars: ty::List::empty() }
-    }
-
-    pub fn bind_with_vars(value: T, bound_vars: &'tcx List<BoundVariableKind>) -> Binder<'tcx, T> {
-        if cfg!(debug_assertions) {
-            let mut validator = ValidateBoundVars::new(bound_vars);
-            value.visit_with(&mut validator);
-        }
-        Binder { value, bound_vars }
-    }
-}
-
-impl<'tcx, T> rustc_type_ir::inherent::BoundVars<TyCtxt<'tcx>> for ty::Binder<'tcx, T> {
-    fn bound_vars(&self) -> &'tcx List<ty::BoundVariableKind> {
-        self.bound_vars
-    }
-
-    fn has_no_bound_vars(&self) -> bool {
-        self.bound_vars.is_empty()
-    }
-}
-
-impl<'tcx, T> Binder<'tcx, T> {
-    /// Skips the binder and returns the "bound" value. This is a
-    /// risky thing to do because it's easy to get confused about
-    /// De Bruijn indices and the like. It is usually better to
-    /// discharge the binder using `no_bound_vars` or
-    /// `instantiate_bound_regions` or something like
-    /// that. `skip_binder` is only valid when you are either
-    /// extracting data that has nothing to do with bound vars, you
-    /// are doing some sort of test that does not involve bound
-    /// regions, or you are being very careful about your depth
-    /// accounting.
-    ///
-    /// Some examples where `skip_binder` is reasonable:
-    ///
-    /// - extracting the `DefId` from a PolyTraitRef;
-    /// - comparing the self type of a PolyTraitRef to see if it is equal to
-    ///   a type parameter `X`, since the type `X` does not reference any regions
-    pub fn skip_binder(self) -> T {
-        self.value
-    }
-
-    pub fn bound_vars(&self) -> &'tcx List<BoundVariableKind> {
-        self.bound_vars
-    }
-
-    pub fn as_ref(&self) -> Binder<'tcx, &T> {
-        Binder { value: &self.value, bound_vars: self.bound_vars }
-    }
-
-    pub fn as_deref(&self) -> Binder<'tcx, &T::Target>
-    where
-        T: Deref,
-    {
-        Binder { value: &self.value, bound_vars: self.bound_vars }
-    }
-
-    pub fn map_bound_ref<F, U: TypeVisitable<TyCtxt<'tcx>>>(&self, f: F) -> Binder<'tcx, U>
-    where
-        F: FnOnce(&T) -> U,
-    {
-        self.as_ref().map_bound(f)
-    }
-
-    pub fn map_bound<F, U: TypeVisitable<TyCtxt<'tcx>>>(self, f: F) -> Binder<'tcx, U>
-    where
-        F: FnOnce(T) -> U,
-    {
-        let Binder { value, bound_vars } = self;
-        let value = f(value);
-        if cfg!(debug_assertions) {
-            let mut validator = ValidateBoundVars::new(bound_vars);
-            value.visit_with(&mut validator);
-        }
-        Binder { value, bound_vars }
-    }
-
-    pub fn try_map_bound<F, U: TypeVisitable<TyCtxt<'tcx>>, E>(
-        self,
-        f: F,
-    ) -> Result<Binder<'tcx, U>, E>
-    where
-        F: FnOnce(T) -> Result<U, E>,
-    {
-        let Binder { value, bound_vars } = self;
-        let value = f(value)?;
-        if cfg!(debug_assertions) {
-            let mut validator = ValidateBoundVars::new(bound_vars);
-            value.visit_with(&mut validator);
-        }
-        Ok(Binder { value, bound_vars })
-    }
-
-    /// Wraps a `value` in a binder, using the same bound variables as the
-    /// current `Binder`. This should not be used if the new value *changes*
-    /// the bound variables. Note: the (old or new) value itself does not
-    /// necessarily need to *name* all the bound variables.
-    ///
-    /// This currently doesn't do anything different than `bind`, because we
-    /// don't actually track bound vars. However, semantically, it is different
-    /// because bound vars aren't allowed to change here, whereas they are
-    /// in `bind`. This may be (debug) asserted in the future.
-    pub fn rebind<U>(&self, value: U) -> Binder<'tcx, U>
-    where
-        U: TypeVisitable<TyCtxt<'tcx>>,
-    {
-        Binder::bind_with_vars(value, self.bound_vars)
-    }
-
-    /// Unwraps and returns the value within, but only if it contains
-    /// no bound vars at all. (In other words, if this binder --
-    /// and indeed any enclosing binder -- doesn't bind anything at
-    /// all.) Otherwise, returns `None`.
-    ///
-    /// (One could imagine having a method that just unwraps a single
-    /// binder, but permits late-bound vars bound by enclosing
-    /// binders, but that would require adjusting the debruijn
-    /// indices, and given the shallow binding structure we often use,
-    /// would not be that useful.)
-    pub fn no_bound_vars(self) -> Option<T>
-    where
-        T: TypeVisitable<TyCtxt<'tcx>>,
-    {
-        // `self.value` is equivalent to `self.skip_binder()`
-        if self.value.has_escaping_bound_vars() { None } else { Some(self.skip_binder()) }
-    }
-
-    /// Splits the contents into two things that share the same binder
-    /// level as the original, returning two distinct binders.
-    ///
-    /// `f` should consider bound regions at depth 1 to be free, and
-    /// anything it produces with bound regions at depth 1 will be
-    /// bound in the resulting return values.
-    pub fn split<U, V, F>(self, f: F) -> (Binder<'tcx, U>, Binder<'tcx, V>)
-    where
-        F: FnOnce(T) -> (U, V),
-    {
-        let Binder { value, bound_vars } = self;
-        let (u, v) = f(value);
-        (Binder { value: u, bound_vars }, Binder { value: v, bound_vars })
-    }
-}
-
-impl<'tcx, T> Binder<'tcx, Option<T>> {
-    pub fn transpose(self) -> Option<Binder<'tcx, T>> {
-        let Binder { value, bound_vars } = self;
-        value.map(|value| Binder { value, bound_vars })
-    }
-}
-
-impl<'tcx, T: IntoIterator> Binder<'tcx, T> {
-    pub fn iter(self) -> impl Iterator<Item = ty::Binder<'tcx, T::Item>> {
-        let Binder { value, bound_vars } = self;
-        value.into_iter().map(|value| Binder { value, bound_vars })
-    }
-}
-
-impl<'tcx, T> IntoDiagArg for Binder<'tcx, T>
-where
-    T: IntoDiagArg,
-{
-    fn into_diag_arg(self) -> DiagArgValue {
-        self.value.into_diag_arg()
-    }
-}
-
 #[derive(Copy, Clone, Debug, TypeFoldable, TypeVisitable)]
 pub struct GenSig<'tcx> {
     pub resume_ty: Ty<'tcx>,
@@ -1103,48 +906,6 @@
 }
 
 pub type PolyFnSig<'tcx> = Binder<'tcx, FnSig<'tcx>>;
-
-impl<'tcx> PolyFnSig<'tcx> {
-    #[inline]
-    pub fn inputs(&self) -> Binder<'tcx, &'tcx [Ty<'tcx>]> {
-        self.map_bound_ref(|fn_sig| fn_sig.inputs())
-    }
-
-    #[inline]
-    #[track_caller]
-    pub fn input(&self, index: usize) -> ty::Binder<'tcx, Ty<'tcx>> {
-        self.map_bound_ref(|fn_sig| fn_sig.inputs()[index])
-    }
-
-    pub fn inputs_and_output(&self) -> ty::Binder<'tcx, &'tcx List<Ty<'tcx>>> {
-        self.map_bound_ref(|fn_sig| fn_sig.inputs_and_output)
-    }
-
-    #[inline]
-    pub fn output(&self) -> ty::Binder<'tcx, Ty<'tcx>> {
-        self.map_bound_ref(|fn_sig| fn_sig.output())
-    }
-
-    pub fn c_variadic(&self) -> bool {
-        self.skip_binder().c_variadic
-    }
-
-    pub fn safety(&self) -> hir::Safety {
-        self.skip_binder().safety
-    }
-
-    pub fn abi(&self) -> abi::Abi {
-        self.skip_binder().abi
-    }
-
-    pub fn is_fn_trait_compatible(&self) -> bool {
-        matches!(
-            self.skip_binder(),
-            ty::FnSig { safety: rustc_hir::Safety::Safe, abi: Abi::Rust, c_variadic: false, .. }
-        )
-    }
-}
-
 pub type CanonicalPolyFnSig<'tcx> = Canonical<'tcx, Binder<'tcx, FnSig<'tcx>>>;
 
 #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, TyEncodable, TyDecodable)]
@@ -1203,6 +964,10 @@
     fn var(self) -> BoundVar {
         self.var
     }
+
+    fn assert_eq(self, var: ty::BoundVariableKind) {
+        assert_eq!(self.kind, var.expect_ty())
+    }
 }
 
 #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, TyEncodable, TyDecodable)]
@@ -2001,7 +1766,7 @@
             FnPtr(f) => *f,
             Error(_) => {
                 // ignore errors (#54954)
-                ty::Binder::dummy(ty::FnSig {
+                Binder::dummy(ty::FnSig {
                     inputs_and_output: ty::List::empty(),
                     c_variadic: false,
                     safety: hir::Safety::Safe,
diff --git a/compiler/rustc_middle/src/ty/visit.rs b/compiler/rustc_middle/src/ty/visit.rs
index 218567b..b1bbfd4 100644
--- a/compiler/rustc_middle/src/ty/visit.rs
+++ b/compiler/rustc_middle/src/ty/visit.rs
@@ -1,7 +1,6 @@
 use crate::ty::{self, Binder, Ty, TyCtxt, TypeFlags};
 
 use rustc_data_structures::fx::FxHashSet;
-use rustc_data_structures::sso::SsoHashSet;
 use rustc_type_ir::fold::TypeFoldable;
 use std::ops::ControlFlow;
 
@@ -145,103 +144,6 @@
     }
 }
 
-pub struct ValidateBoundVars<'tcx> {
-    bound_vars: &'tcx ty::List<ty::BoundVariableKind>,
-    binder_index: ty::DebruijnIndex,
-    // We may encounter the same variable at different levels of binding, so
-    // this can't just be `Ty`
-    visited: SsoHashSet<(ty::DebruijnIndex, Ty<'tcx>)>,
-}
-
-impl<'tcx> ValidateBoundVars<'tcx> {
-    pub fn new(bound_vars: &'tcx ty::List<ty::BoundVariableKind>) -> Self {
-        ValidateBoundVars {
-            bound_vars,
-            binder_index: ty::INNERMOST,
-            visited: SsoHashSet::default(),
-        }
-    }
-}
-
-impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for ValidateBoundVars<'tcx> {
-    type Result = ControlFlow<()>;
-
-    fn visit_binder<T: TypeVisitable<TyCtxt<'tcx>>>(
-        &mut self,
-        t: &Binder<'tcx, T>,
-    ) -> Self::Result {
-        self.binder_index.shift_in(1);
-        let result = t.super_visit_with(self);
-        self.binder_index.shift_out(1);
-        result
-    }
-
-    fn visit_ty(&mut self, t: Ty<'tcx>) -> Self::Result {
-        if t.outer_exclusive_binder() < self.binder_index
-            || !self.visited.insert((self.binder_index, t))
-        {
-            return ControlFlow::Break(());
-        }
-        match *t.kind() {
-            ty::Bound(debruijn, bound_ty) if debruijn == self.binder_index => {
-                if self.bound_vars.len() <= bound_ty.var.as_usize() {
-                    bug!("Not enough bound vars: {:?} not found in {:?}", t, self.bound_vars);
-                }
-                let list_var = self.bound_vars[bound_ty.var.as_usize()];
-                match list_var {
-                    ty::BoundVariableKind::Ty(kind) => {
-                        if kind != bound_ty.kind {
-                            bug!(
-                                "Mismatched type kinds: {:?} doesn't var in list {:?}",
-                                bound_ty.kind,
-                                list_var
-                            );
-                        }
-                    }
-                    _ => {
-                        bug!("Mismatched bound variable kinds! Expected type, found {:?}", list_var)
-                    }
-                }
-            }
-
-            _ => (),
-        };
-
-        t.super_visit_with(self)
-    }
-
-    fn visit_region(&mut self, r: ty::Region<'tcx>) -> Self::Result {
-        match *r {
-            ty::ReBound(index, br) if index == self.binder_index => {
-                if self.bound_vars.len() <= br.var.as_usize() {
-                    bug!("Not enough bound vars: {:?} not found in {:?}", br, self.bound_vars);
-                }
-                let list_var = self.bound_vars[br.var.as_usize()];
-                match list_var {
-                    ty::BoundVariableKind::Region(kind) => {
-                        if kind != br.kind {
-                            bug!(
-                                "Mismatched region kinds: {:?} doesn't match var ({:?}) in list ({:?})",
-                                br.kind,
-                                list_var,
-                                self.bound_vars
-                            );
-                        }
-                    }
-                    _ => bug!(
-                        "Mismatched bound variable kinds! Expected region, found {:?}",
-                        list_var
-                    ),
-                }
-            }
-
-            _ => (),
-        };
-
-        ControlFlow::Continue(())
-    }
-}
-
 /// Collects all the late-bound regions at the innermost binding level
 /// into a hash set.
 struct LateBoundRegionsCollector {
diff --git a/compiler/rustc_next_trait_solver/src/canonicalizer.rs b/compiler/rustc_next_trait_solver/src/canonicalizer.rs
index 696639e..127ebde 100644
--- a/compiler/rustc_next_trait_solver/src/canonicalizer.rs
+++ b/compiler/rustc_next_trait_solver/src/canonicalizer.rs
@@ -217,10 +217,9 @@
         self.infcx.interner()
     }
 
-    fn fold_binder<T>(&mut self, t: I::Binder<T>) -> I::Binder<T>
+    fn fold_binder<T>(&mut self, t: ty::Binder<I, T>) -> ty::Binder<I, T>
     where
         T: TypeFoldable<I>,
-        I::Binder<T>: TypeSuperFoldable<I>,
     {
         self.binder_index.shift_in(1);
         let t = t.super_fold_with(self);
@@ -455,10 +454,9 @@
         self.interner
     }
 
-    fn fold_binder<T>(&mut self, t: I::Binder<T>) -> I::Binder<T>
+    fn fold_binder<T>(&mut self, t: ty::Binder<I, T>) -> ty::Binder<I, T>
     where
         T: TypeFoldable<I>,
-        I::Binder<T>: TypeSuperFoldable<I>,
     {
         self.binder.shift_in(1);
         let t = t.super_fold_with(self);
diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl
index 3deefca..d850644 100644
--- a/compiler/rustc_passes/messages.ftl
+++ b/compiler/rustc_passes/messages.ftl
@@ -341,7 +341,7 @@
     feature `{$implied_by}` implying `{$feature}` does not exist
 
 passes_incorrect_do_not_recommend_location =
-    `#[do_not_recommend]` can only be placed on trait implementations
+    `#[diagnostic::do_not_recommend]` can only be placed on trait implementations
 
 passes_incorrect_meta_item = expected a quoted string literal
 passes_incorrect_meta_item_suggestion = consider surrounding this with quotes
diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs
index b1438a5..1924533 100644
--- a/compiler/rustc_passes/src/check_attr.rs
+++ b/compiler/rustc_passes/src/check_attr.rs
@@ -113,92 +113,96 @@
         let mut seen = FxHashMap::default();
         let attrs = self.tcx.hir().attrs(hir_id);
         for attr in attrs {
-            if attr.path_matches(&[sym::diagnostic, sym::on_unimplemented]) {
-                self.check_diagnostic_on_unimplemented(attr.span, hir_id, target);
-            }
-            match attr.name_or_empty() {
-                sym::do_not_recommend => self.check_do_not_recommend(attr.span, target),
-                sym::inline => self.check_inline(hir_id, attr, span, target),
-                sym::coverage => self.check_coverage(hir_id, attr, span, target),
-                sym::non_exhaustive => self.check_non_exhaustive(hir_id, attr, span, target),
-                sym::marker => self.check_marker(hir_id, attr, span, target),
-                sym::target_feature => self.check_target_feature(hir_id, attr, span, target, attrs),
-                sym::thread_local => self.check_thread_local(attr, span, target),
-                sym::track_caller => {
+            match attr.path().as_slice() {
+                [sym::diagnostic, sym::do_not_recommend] => {
+                    self.check_do_not_recommend(attr.span, hir_id, target)
+                }
+                [sym::diagnostic, sym::on_unimplemented] => {
+                    self.check_diagnostic_on_unimplemented(attr.span, hir_id, target)
+                }
+                [sym::inline] => self.check_inline(hir_id, attr, span, target),
+                [sym::coverage] => self.check_coverage(hir_id, attr, span, target),
+                [sym::non_exhaustive] => self.check_non_exhaustive(hir_id, attr, span, target),
+                [sym::marker] => self.check_marker(hir_id, attr, span, target),
+                [sym::target_feature] => {
+                    self.check_target_feature(hir_id, attr, span, target, attrs)
+                }
+                [sym::thread_local] => self.check_thread_local(attr, span, target),
+                [sym::track_caller] => {
                     self.check_track_caller(hir_id, attr.span, attrs, span, target)
                 }
-                sym::doc => self.check_doc_attrs(
+                [sym::doc] => self.check_doc_attrs(
                     attr,
                     hir_id,
                     target,
                     &mut specified_inline,
                     &mut doc_aliases,
                 ),
-                sym::no_link => self.check_no_link(hir_id, attr, span, target),
-                sym::export_name => self.check_export_name(hir_id, attr, span, target),
-                sym::rustc_layout_scalar_valid_range_start
-                | sym::rustc_layout_scalar_valid_range_end => {
+                [sym::no_link] => self.check_no_link(hir_id, attr, span, target),
+                [sym::export_name] => self.check_export_name(hir_id, attr, span, target),
+                [sym::rustc_layout_scalar_valid_range_start]
+                | [sym::rustc_layout_scalar_valid_range_end] => {
                     self.check_rustc_layout_scalar_valid_range(attr, span, target)
                 }
-                sym::allow_internal_unstable => {
+                [sym::allow_internal_unstable] => {
                     self.check_allow_internal_unstable(hir_id, attr, span, target, attrs)
                 }
-                sym::debugger_visualizer => self.check_debugger_visualizer(attr, target),
-                sym::rustc_allow_const_fn_unstable => {
+                [sym::debugger_visualizer] => self.check_debugger_visualizer(attr, target),
+                [sym::rustc_allow_const_fn_unstable] => {
                     self.check_rustc_allow_const_fn_unstable(hir_id, attr, span, target)
                 }
-                sym::rustc_std_internal_symbol => {
+                [sym::rustc_std_internal_symbol] => {
                     self.check_rustc_std_internal_symbol(attr, span, target)
                 }
-                sym::naked => self.check_naked(hir_id, attr, span, target),
-                sym::rustc_never_returns_null_ptr => {
+                [sym::naked] => self.check_naked(hir_id, attr, span, target),
+                [sym::rustc_never_returns_null_ptr] => {
                     self.check_applied_to_fn_or_method(hir_id, attr, span, target)
                 }
-                sym::rustc_legacy_const_generics => {
+                [sym::rustc_legacy_const_generics] => {
                     self.check_rustc_legacy_const_generics(hir_id, attr, span, target, item)
                 }
-                sym::rustc_lint_query_instability => {
+                [sym::rustc_lint_query_instability] => {
                     self.check_rustc_lint_query_instability(hir_id, attr, span, target)
                 }
-                sym::rustc_lint_diagnostics => {
+                [sym::rustc_lint_diagnostics] => {
                     self.check_rustc_lint_diagnostics(hir_id, attr, span, target)
                 }
-                sym::rustc_lint_opt_ty => self.check_rustc_lint_opt_ty(attr, span, target),
-                sym::rustc_lint_opt_deny_field_access => {
+                [sym::rustc_lint_opt_ty] => self.check_rustc_lint_opt_ty(attr, span, target),
+                [sym::rustc_lint_opt_deny_field_access] => {
                     self.check_rustc_lint_opt_deny_field_access(attr, span, target)
                 }
-                sym::rustc_clean
-                | sym::rustc_dirty
-                | sym::rustc_if_this_changed
-                | sym::rustc_then_this_would_need => self.check_rustc_dirty_clean(attr),
-                sym::rustc_coinductive
-                | sym::rustc_must_implement_one_of
-                | sym::rustc_deny_explicit_impl
-                | sym::const_trait => self.check_must_be_applied_to_trait(attr, span, target),
-                sym::cmse_nonsecure_entry => {
+                [sym::rustc_clean]
+                | [sym::rustc_dirty]
+                | [sym::rustc_if_this_changed]
+                | [sym::rustc_then_this_would_need] => self.check_rustc_dirty_clean(attr),
+                [sym::rustc_coinductive]
+                | [sym::rustc_must_implement_one_of]
+                | [sym::rustc_deny_explicit_impl]
+                | [sym::const_trait] => self.check_must_be_applied_to_trait(attr, span, target),
+                [sym::cmse_nonsecure_entry] => {
                     self.check_cmse_nonsecure_entry(hir_id, attr, span, target)
                 }
-                sym::collapse_debuginfo => self.check_collapse_debuginfo(attr, span, target),
-                sym::must_not_suspend => self.check_must_not_suspend(attr, span, target),
-                sym::must_use => self.check_must_use(hir_id, attr, target),
-                sym::rustc_pass_by_value => self.check_pass_by_value(attr, span, target),
-                sym::rustc_allow_incoherent_impl => {
+                [sym::collapse_debuginfo] => self.check_collapse_debuginfo(attr, span, target),
+                [sym::must_not_suspend] => self.check_must_not_suspend(attr, span, target),
+                [sym::must_use] => self.check_must_use(hir_id, attr, target),
+                [sym::rustc_pass_by_value] => self.check_pass_by_value(attr, span, target),
+                [sym::rustc_allow_incoherent_impl] => {
                     self.check_allow_incoherent_impl(attr, span, target)
                 }
-                sym::rustc_has_incoherent_inherent_impls => {
+                [sym::rustc_has_incoherent_inherent_impls] => {
                     self.check_has_incoherent_inherent_impls(attr, span, target)
                 }
-                sym::ffi_pure => self.check_ffi_pure(attr.span, attrs, target),
-                sym::ffi_const => self.check_ffi_const(attr.span, target),
-                sym::rustc_const_unstable
-                | sym::rustc_const_stable
-                | sym::unstable
-                | sym::stable
-                | sym::rustc_allowed_through_unstable_modules
-                | sym::rustc_promotable => self.check_stability_promotable(attr, target),
-                sym::link_ordinal => self.check_link_ordinal(attr, span, target),
-                sym::rustc_confusables => self.check_confusables(attr, target),
-                sym::rustc_safe_intrinsic => {
+                [sym::ffi_pure] => self.check_ffi_pure(attr.span, attrs, target),
+                [sym::ffi_const] => self.check_ffi_const(attr.span, target),
+                [sym::rustc_const_unstable]
+                | [sym::rustc_const_stable]
+                | [sym::unstable]
+                | [sym::stable]
+                | [sym::rustc_allowed_through_unstable_modules]
+                | [sym::rustc_promotable] => self.check_stability_promotable(attr, target),
+                [sym::link_ordinal] => self.check_link_ordinal(attr, span, target),
+                [sym::rustc_confusables] => self.check_confusables(attr, target),
+                [sym::rustc_safe_intrinsic] => {
                     self.check_rustc_safe_intrinsic(hir_id, attr, span, target)
                 }
                 _ => true,
@@ -290,18 +294,26 @@
         );
     }
 
-    /// Checks if `#[do_not_recommend]` is applied on a trait impl.
-    fn check_do_not_recommend(&self, attr_span: Span, target: Target) -> bool {
-        if let Target::Impl = target {
-            true
-        } else {
-            self.dcx().emit_err(errors::IncorrectDoNotRecommendLocation { span: attr_span });
-            false
+    /// Checks if `#[diagnostic::do_not_recommend]` is applied on a trait impl.
+    fn check_do_not_recommend(&self, attr_span: Span, hir_id: HirId, target: Target) -> bool {
+        if !matches!(target, Target::Impl) {
+            self.tcx.emit_node_span_lint(
+                UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
+                hir_id,
+                attr_span,
+                errors::IncorrectDoNotRecommendLocation,
+            );
         }
+        true
     }
 
     /// Checks if `#[diagnostic::on_unimplemented]` is applied to a trait definition
-    fn check_diagnostic_on_unimplemented(&self, attr_span: Span, hir_id: HirId, target: Target) {
+    fn check_diagnostic_on_unimplemented(
+        &self,
+        attr_span: Span,
+        hir_id: HirId,
+        target: Target,
+    ) -> bool {
         if !matches!(target, Target::Trait) {
             self.tcx.emit_node_span_lint(
                 UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
@@ -310,6 +322,7 @@
                 DiagnosticOnUnimplementedOnlyForTraits,
             );
         }
+        true
     }
 
     /// Checks if an `#[inline]` is applied to a function or a closure. Returns `true` if valid.
diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs
index b8586e7..1805527 100644
--- a/compiler/rustc_passes/src/errors.rs
+++ b/compiler/rustc_passes/src/errors.rs
@@ -17,12 +17,9 @@
 use crate::check_attr::ProcMacroKind;
 use crate::lang_items::Duplicate;
 
-#[derive(Diagnostic)]
+#[derive(LintDiagnostic)]
 #[diag(passes_incorrect_do_not_recommend_location)]
-pub struct IncorrectDoNotRecommendLocation {
-    #[primary_span]
-    pub span: Span,
-}
+pub struct IncorrectDoNotRecommendLocation;
 
 #[derive(LintDiagnostic)]
 #[diag(passes_outer_crate_level_attr)]
diff --git a/compiler/rustc_query_system/src/dep_graph/serialized.rs b/compiler/rustc_query_system/src/dep_graph/serialized.rs
index b426bb8..8e91d9d 100644
--- a/compiler/rustc_query_system/src/dep_graph/serialized.rs
+++ b/compiler/rustc_query_system/src/dep_graph/serialized.rs
@@ -182,15 +182,13 @@
     pub fn decode<D: Deps>(d: &mut MemDecoder<'_>) -> Arc<SerializedDepGraph> {
         // The last 16 bytes are the node count and edge count.
         debug!("position: {:?}", d.position());
-        let (node_count, edge_count, graph_size) =
-            d.with_position(d.len() - 3 * IntEncodedWithFixedSize::ENCODED_SIZE, |d| {
+        let (node_count, edge_count) =
+            d.with_position(d.len() - 2 * IntEncodedWithFixedSize::ENCODED_SIZE, |d| {
                 debug!("position: {:?}", d.position());
                 let node_count = IntEncodedWithFixedSize::decode(d).0 as usize;
                 let edge_count = IntEncodedWithFixedSize::decode(d).0 as usize;
-                let graph_size = IntEncodedWithFixedSize::decode(d).0 as usize;
-                (node_count, edge_count, graph_size)
+                (node_count, edge_count)
             });
-        assert_eq!(d.len(), graph_size);
         debug!("position: {:?}", d.position());
 
         debug!(?node_count, ?edge_count);
@@ -606,8 +604,6 @@
         debug!("position: {:?}", encoder.position());
         IntEncodedWithFixedSize(node_count).encode(&mut encoder);
         IntEncodedWithFixedSize(edge_count).encode(&mut encoder);
-        let graph_size = encoder.position() + IntEncodedWithFixedSize::ENCODED_SIZE;
-        IntEncodedWithFixedSize(graph_size as u64).encode(&mut encoder);
         debug!("position: {:?}", encoder.position());
         // Drop the encoder so that nothing is written after the counts.
         let result = encoder.finish();
diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs
index 092eb1c..268e7f0 100644
--- a/compiler/rustc_resolve/src/macros.rs
+++ b/compiler/rustc_resolve/src/macros.rs
@@ -578,7 +578,9 @@
         if res == Res::NonMacroAttr(NonMacroAttrKind::Tool)
             && let [namespace, attribute, ..] = &*path.segments
             && namespace.ident.name == sym::diagnostic
-            && attribute.ident.name != sym::on_unimplemented
+            && !(attribute.ident.name == sym::on_unimplemented
+                || (attribute.ident.name == sym::do_not_recommend
+                    && self.tcx.features().do_not_recommend))
         {
             let distance =
                 edit_distance(attribute.ident.name.as_str(), sym::on_unimplemented.as_str(), 5);
diff --git a/compiler/rustc_serialize/src/opaque.rs b/compiler/rustc_serialize/src/opaque.rs
index eec83c02..1dcb699 100644
--- a/compiler/rustc_serialize/src/opaque.rs
+++ b/compiler/rustc_serialize/src/opaque.rs
@@ -17,6 +17,8 @@
 
 pub type FileEncodeResult = Result<usize, (PathBuf, io::Error)>;
 
+pub const MAGIC_END_BYTES: &[u8] = b"rust-end-file";
+
 /// The size of the buffer in `FileEncoder`.
 const BUF_SIZE: usize = 8192;
 
@@ -181,6 +183,7 @@
     }
 
     pub fn finish(&mut self) -> FileEncodeResult {
+        self.write_all(MAGIC_END_BYTES);
         self.flush();
         #[cfg(debug_assertions)]
         {
@@ -261,15 +264,18 @@
 
 impl<'a> MemDecoder<'a> {
     #[inline]
-    pub fn new(data: &'a [u8], position: usize) -> MemDecoder<'a> {
+    pub fn new(data: &'a [u8], position: usize) -> Result<MemDecoder<'a>, ()> {
+        let data = data.strip_suffix(MAGIC_END_BYTES).ok_or(())?;
         let Range { start, end } = data.as_ptr_range();
-        MemDecoder { start, current: data[position..].as_ptr(), end, _marker: PhantomData }
+        Ok(MemDecoder { start, current: data[position..].as_ptr(), end, _marker: PhantomData })
     }
 
     #[inline]
-    pub fn data(&self) -> &'a [u8] {
-        // SAFETY: This recovers the original slice, only using members we never modify.
-        unsafe { std::slice::from_raw_parts(self.start, self.len()) }
+    pub fn split_at(&self, position: usize) -> MemDecoder<'a> {
+        assert!(position <= self.len());
+        // SAFETY: We checked above that this offset is within the original slice
+        let current = unsafe { self.start.add(position) };
+        MemDecoder { start: self.start, current, end: self.end, _marker: PhantomData }
     }
 
     #[inline]
diff --git a/compiler/rustc_serialize/tests/leb128.rs b/compiler/rustc_serialize/tests/leb128.rs
index dc9b32a..fafe4b9 100644
--- a/compiler/rustc_serialize/tests/leb128.rs
+++ b/compiler/rustc_serialize/tests/leb128.rs
@@ -1,4 +1,6 @@
 use rustc_serialize::leb128::*;
+use rustc_serialize::opaque::MemDecoder;
+use rustc_serialize::opaque::MAGIC_END_BYTES;
 use rustc_serialize::Decoder;
 
 macro_rules! impl_test_unsigned_leb128 {
@@ -25,13 +27,15 @@
                 let n = $write_fn_name(&mut buf, x);
                 stream.extend(&buf[..n]);
             }
+            let stream_end = stream.len();
+            stream.extend(MAGIC_END_BYTES);
 
-            let mut decoder = rustc_serialize::opaque::MemDecoder::new(&stream, 0);
+            let mut decoder = MemDecoder::new(&stream, 0).unwrap();
             for &expected in &values {
                 let actual = $read_fn_name(&mut decoder);
                 assert_eq!(expected, actual);
             }
-            assert_eq!(stream.len(), decoder.position());
+            assert_eq!(stream_end, decoder.position());
         }
     };
 }
@@ -72,13 +76,15 @@
                 let n = $write_fn_name(&mut buf, x);
                 stream.extend(&buf[..n]);
             }
+            let stream_end = stream.len();
+            stream.extend(MAGIC_END_BYTES);
 
-            let mut decoder = rustc_serialize::opaque::MemDecoder::new(&stream, 0);
+            let mut decoder = MemDecoder::new(&stream, 0).unwrap();
             for &expected in &values {
                 let actual = $read_fn_name(&mut decoder);
                 assert_eq!(expected, actual);
             }
-            assert_eq!(stream.len(), decoder.position());
+            assert_eq!(stream_end, decoder.position());
         }
     };
 }
diff --git a/compiler/rustc_serialize/tests/opaque.rs b/compiler/rustc_serialize/tests/opaque.rs
index 45ff85f..833151d 100644
--- a/compiler/rustc_serialize/tests/opaque.rs
+++ b/compiler/rustc_serialize/tests/opaque.rs
@@ -42,7 +42,7 @@
     encoder.finish().unwrap();
 
     let data = fs::read(&tmpfile).unwrap();
-    let mut decoder = MemDecoder::new(&data[..], 0);
+    let mut decoder = MemDecoder::new(&data[..], 0).unwrap();
     for value in values {
         let decoded = Decodable::decode(&mut decoder);
         assert_eq!(value, decoded);
diff --git a/compiler/rustc_smir/src/rustc_smir/convert/ty.rs b/compiler/rustc_smir/src/rustc_smir/convert/ty.rs
index 1544798..be20924 100644
--- a/compiler/rustc_smir/src/rustc_smir/convert/ty.rs
+++ b/compiler/rustc_smir/src/rustc_smir/convert/ty.rs
@@ -707,12 +707,11 @@
     }
 }
 
-impl<'tcx, A, B, U, V> Stable<'tcx> for ty::OutlivesPredicate<A, B>
+impl<'tcx, T> Stable<'tcx> for ty::OutlivesPredicate<'tcx, T>
 where
-    A: Stable<'tcx, T = U>,
-    B: Stable<'tcx, T = V>,
+    T: Stable<'tcx>,
 {
-    type T = stable_mir::ty::OutlivesPredicate<U, V>;
+    type T = stable_mir::ty::OutlivesPredicate<T::T, Region>;
 
     fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
         let ty::OutlivesPredicate(a, b) = self;
diff --git a/compiler/rustc_span/src/hygiene.rs b/compiler/rustc_span/src/hygiene.rs
index aa4bcef..00ef17d 100644
--- a/compiler/rustc_span/src/hygiene.rs
+++ b/compiler/rustc_span/src/hygiene.rs
@@ -40,6 +40,7 @@
 use std::collections::hash_map::Entry;
 use std::fmt;
 use std::hash::Hash;
+use tracing::{debug, trace};
 
 /// A `SyntaxContext` represents a chain of pairs `(ExpnId, Transparency)` named "marks".
 #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs
index f83bacd..b2ca01f 100644
--- a/compiler/rustc_span/src/lib.rs
+++ b/compiler/rustc_span/src/lib.rs
@@ -33,15 +33,16 @@
 #![feature(rustdoc_internals)]
 // tidy-alphabetical-end
 
+// The code produced by the `Encodable`/`Decodable` derive macros refer to
+// `rustc_span::Span{Encoder,Decoder}`. That's fine outside this crate, but doesn't work inside
+// this crate without this line making `rustc_span` available.
 extern crate self as rustc_span;
 
-#[macro_use]
-extern crate tracing;
-
 use rustc_data_structures::{outline, AtomicRef};
 use rustc_macros::{Decodable, Encodable, HashStable_Generic};
 use rustc_serialize::opaque::{FileEncoder, MemDecoder};
 use rustc_serialize::{Decodable, Decoder, Encodable, Encoder};
+use tracing::debug;
 
 mod caching_source_map_view;
 pub mod source_map;
diff --git a/compiler/rustc_span/src/source_map.rs b/compiler/rustc_span/src/source_map.rs
index 2093dcf..fb212d6 100644
--- a/compiler/rustc_span/src/source_map.rs
+++ b/compiler/rustc_span/src/source_map.rs
@@ -16,6 +16,7 @@
 use std::fs;
 use std::io::{self, BorrowedBuf, Read};
 use std::path;
+use tracing::{debug, instrument, trace};
 
 #[cfg(test)]
 mod tests;
diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs
index 4933080..7291eb0 100644
--- a/compiler/rustc_trait_selection/src/solve/fulfill.rs
+++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs
@@ -380,7 +380,10 @@
             source: CandidateSource::Impl(impl_def_id),
             result: _,
         } = candidate.kind()
-            && goal.infcx().tcx.has_attr(impl_def_id, sym::do_not_recommend)
+            && goal
+                .infcx()
+                .tcx
+                .has_attrs_with_path(impl_def_id, &[sym::diagnostic, sym::do_not_recommend])
         {
             return ControlFlow::Break(self.obligation.clone());
         }
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 f9b6b28..6c56ebb 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
@@ -47,7 +47,8 @@
 use crate::traits::error_reporting::type_err_ctxt_ext::InferCtxtPrivExt;
 use crate::traits::query::evaluate_obligation::InferCtxtExt as _;
 use rustc_middle::ty::print::{
-    with_forced_trimmed_paths, with_no_trimmed_paths, PrintTraitPredicateExt as _,
+    with_forced_trimmed_paths, with_no_trimmed_paths, PrintPolyTraitPredicateExt as _,
+    PrintTraitPredicateExt as _,
 };
 
 use itertools::EitherOrBoth;
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs
index 20dfb5e..494fca0 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs
@@ -1012,7 +1012,10 @@
         let mut base_cause = obligation.cause.code().clone();
         loop {
             if let ObligationCauseCode::ImplDerived(ref c) = base_cause {
-                if self.tcx.has_attr(c.impl_or_alias_def_id, sym::do_not_recommend) {
+                if self.tcx.has_attrs_with_path(
+                    c.impl_or_alias_def_id,
+                    &[sym::diagnostic, sym::do_not_recommend],
+                ) {
                     let code = (*c.derived.parent_code).clone();
                     obligation.cause.map_code(|_| code);
                     obligation.predicate = c.derived.parent_trait_pred.upcast(self.tcx);
diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs
index f7e84a4..00cc77e 100644
--- a/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs
+++ b/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs
@@ -162,8 +162,7 @@
     let mut checked_wf_args = rustc_data_structures::fx::FxHashSet::default();
     let mut wf_args = vec![ty.into()];
 
-    let mut outlives_bounds: Vec<ty::OutlivesPredicate<ty::GenericArg<'tcx>, ty::Region<'tcx>>> =
-        vec![];
+    let mut outlives_bounds: Vec<ty::OutlivesPredicate<'tcx, ty::GenericArg<'tcx>>> = vec![];
 
     while let Some(arg) = wf_args.pop() {
         if !checked_wf_args.insert(arg) {
diff --git a/compiler/rustc_type_ir/src/binder.rs b/compiler/rustc_type_ir/src/binder.rs
new file mode 100644
index 0000000..5336b91
--- /dev/null
+++ b/compiler/rustc_type_ir/src/binder.rs
@@ -0,0 +1,340 @@
+use std::fmt::Debug;
+use std::hash::Hash;
+use std::ops::{ControlFlow, Deref};
+
+#[cfg(feature = "nightly")]
+use rustc_macros::HashStable_NoContext;
+use rustc_serialize::Decodable;
+
+use crate::fold::{FallibleTypeFolder, TypeFoldable, TypeSuperFoldable};
+use crate::inherent::*;
+use crate::lift::Lift;
+use crate::visit::{Flags, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor};
+use crate::{self as ty, Interner, SsoHashSet};
+
+/// Binder is a binder for higher-ranked lifetimes or types. It is part of the
+/// compiler's representation for things like `for<'a> Fn(&'a isize)`
+/// (which would be represented by the type `PolyTraitRef ==
+/// Binder<I, TraitRef>`). Note that when we instantiate,
+/// erase, or otherwise "discharge" these bound vars, we change the
+/// type from `Binder<I, T>` to just `T` (see
+/// e.g., `liberate_late_bound_regions`).
+///
+/// `Decodable` and `Encodable` are implemented for `Binder<T>` using the `impl_binder_encode_decode!` macro.
+#[derive(derivative::Derivative)]
+#[derivative(
+    Clone(bound = "T: Clone"),
+    Copy(bound = "T: Copy"),
+    Hash(bound = "T: Hash"),
+    PartialEq(bound = "T: PartialEq"),
+    Eq(bound = "T: Eq"),
+    Debug(bound = "T: Debug")
+)]
+#[cfg_attr(feature = "nightly", derive(HashStable_NoContext))]
+pub struct Binder<I: Interner, T> {
+    value: T,
+    bound_vars: I::BoundVarKinds,
+}
+
+// FIXME: We manually derive `Lift` because the `derive(Lift_Generic)` doesn't
+// understand how to turn `T` to `T::Lifted` in the output `type Lifted`.
+impl<I: Interner, U: Interner, T> Lift<U> for Binder<I, T>
+where
+    T: Lift<U>,
+    I::BoundVarKinds: Lift<U, Lifted = U::BoundVarKinds>,
+{
+    type Lifted = Binder<U, T::Lifted>;
+
+    fn lift_to_tcx(self, tcx: U) -> Option<Self::Lifted> {
+        Some(Binder {
+            value: self.value.lift_to_tcx(tcx)?,
+            bound_vars: self.bound_vars.lift_to_tcx(tcx)?,
+        })
+    }
+}
+
+macro_rules! impl_binder_encode_decode {
+    ($($t:ty),+ $(,)?) => {
+        $(
+            impl<I: Interner, E: crate::TyEncoder<I = I>> rustc_serialize::Encodable<E> for ty::Binder<I, $t>
+            where
+                $t: rustc_serialize::Encodable<E>,
+                I::BoundVarKinds: rustc_serialize::Encodable<E>,
+            {
+                fn encode(&self, e: &mut E) {
+                    self.bound_vars().encode(e);
+                    self.as_ref().skip_binder().encode(e);
+                }
+            }
+            impl<I: Interner, D: crate::TyDecoder<I = I>> Decodable<D> for ty::Binder<I, $t>
+            where
+                $t: TypeVisitable<I> + rustc_serialize::Decodable<D>,
+                I::BoundVarKinds: rustc_serialize::Decodable<D>,
+            {
+                fn decode(decoder: &mut D) -> Self {
+                    let bound_vars = Decodable::decode(decoder);
+                    ty::Binder::bind_with_vars(<$t>::decode(decoder), bound_vars)
+                }
+            }
+        )*
+    }
+}
+
+impl_binder_encode_decode! {
+    ty::FnSig<I>,
+    ty::TraitPredicate<I>,
+    ty::ExistentialPredicate<I>,
+    ty::TraitRef<I>,
+    ty::ExistentialTraitRef<I>,
+}
+
+impl<I: Interner, T> Binder<I, T>
+where
+    T: TypeVisitable<I>,
+{
+    /// Wraps `value` in a binder, asserting that `value` does not
+    /// contain any bound vars that would be bound by the
+    /// binder. This is commonly used to 'inject' a value T into a
+    /// different binding level.
+    #[track_caller]
+    pub fn dummy(value: T) -> Binder<I, T> {
+        assert!(
+            !value.has_escaping_bound_vars(),
+            "`{value:?}` has escaping bound vars, so it cannot be wrapped in a dummy binder."
+        );
+        Binder { value, bound_vars: Default::default() }
+    }
+
+    pub fn bind_with_vars(value: T, bound_vars: I::BoundVarKinds) -> Binder<I, T> {
+        if cfg!(debug_assertions) {
+            let mut validator = ValidateBoundVars::new(bound_vars);
+            value.visit_with(&mut validator);
+        }
+        Binder { value, bound_vars }
+    }
+}
+
+impl<I: Interner, T: TypeFoldable<I>> TypeFoldable<I> for Binder<I, T> {
+    fn try_fold_with<F: FallibleTypeFolder<I>>(self, folder: &mut F) -> Result<Self, F::Error> {
+        folder.try_fold_binder(self)
+    }
+}
+
+impl<I: Interner, T: TypeVisitable<I>> TypeVisitable<I> for Binder<I, T> {
+    fn visit_with<V: TypeVisitor<I>>(&self, visitor: &mut V) -> V::Result {
+        visitor.visit_binder(self)
+    }
+}
+
+impl<I: Interner, T: TypeFoldable<I>> TypeSuperFoldable<I> for Binder<I, T> {
+    fn try_super_fold_with<F: FallibleTypeFolder<I>>(
+        self,
+        folder: &mut F,
+    ) -> Result<Self, F::Error> {
+        self.try_map_bound(|ty| ty.try_fold_with(folder))
+    }
+}
+
+impl<I: Interner, T: TypeVisitable<I>> TypeSuperVisitable<I> for Binder<I, T> {
+    fn super_visit_with<V: TypeVisitor<I>>(&self, visitor: &mut V) -> V::Result {
+        self.as_ref().skip_binder().visit_with(visitor)
+    }
+}
+
+impl<I: Interner, T> Binder<I, T> {
+    /// Skips the binder and returns the "bound" value. This is a
+    /// risky thing to do because it's easy to get confused about
+    /// De Bruijn indices and the like. It is usually better to
+    /// discharge the binder using `no_bound_vars` or
+    /// `instantiate_bound_regions` or something like
+    /// that. `skip_binder` is only valid when you are either
+    /// extracting data that has nothing to do with bound vars, you
+    /// are doing some sort of test that does not involve bound
+    /// regions, or you are being very careful about your depth
+    /// accounting.
+    ///
+    /// Some examples where `skip_binder` is reasonable:
+    ///
+    /// - extracting the `DefId` from a PolyTraitRef;
+    /// - comparing the self type of a PolyTraitRef to see if it is equal to
+    ///   a type parameter `X`, since the type `X` does not reference any regions
+    pub fn skip_binder(self) -> T {
+        self.value
+    }
+
+    pub fn bound_vars(&self) -> I::BoundVarKinds {
+        self.bound_vars
+    }
+
+    pub fn as_ref(&self) -> Binder<I, &T> {
+        Binder { value: &self.value, bound_vars: self.bound_vars }
+    }
+
+    pub fn as_deref(&self) -> Binder<I, &T::Target>
+    where
+        T: Deref,
+    {
+        Binder { value: &self.value, bound_vars: self.bound_vars }
+    }
+
+    pub fn map_bound_ref<F, U: TypeVisitable<I>>(&self, f: F) -> Binder<I, U>
+    where
+        F: FnOnce(&T) -> U,
+    {
+        self.as_ref().map_bound(f)
+    }
+
+    pub fn map_bound<F, U: TypeVisitable<I>>(self, f: F) -> Binder<I, U>
+    where
+        F: FnOnce(T) -> U,
+    {
+        let Binder { value, bound_vars } = self;
+        let value = f(value);
+        if cfg!(debug_assertions) {
+            let mut validator = ValidateBoundVars::new(bound_vars);
+            value.visit_with(&mut validator);
+        }
+        Binder { value, bound_vars }
+    }
+
+    pub fn try_map_bound<F, U: TypeVisitable<I>, E>(self, f: F) -> Result<Binder<I, U>, E>
+    where
+        F: FnOnce(T) -> Result<U, E>,
+    {
+        let Binder { value, bound_vars } = self;
+        let value = f(value)?;
+        if cfg!(debug_assertions) {
+            let mut validator = ValidateBoundVars::new(bound_vars);
+            value.visit_with(&mut validator);
+        }
+        Ok(Binder { value, bound_vars })
+    }
+
+    /// Wraps a `value` in a binder, using the same bound variables as the
+    /// current `Binder`. This should not be used if the new value *changes*
+    /// the bound variables. Note: the (old or new) value itself does not
+    /// necessarily need to *name* all the bound variables.
+    ///
+    /// This currently doesn't do anything different than `bind`, because we
+    /// don't actually track bound vars. However, semantically, it is different
+    /// because bound vars aren't allowed to change here, whereas they are
+    /// in `bind`. This may be (debug) asserted in the future.
+    pub fn rebind<U>(&self, value: U) -> Binder<I, U>
+    where
+        U: TypeVisitable<I>,
+    {
+        Binder::bind_with_vars(value, self.bound_vars)
+    }
+
+    /// Unwraps and returns the value within, but only if it contains
+    /// no bound vars at all. (In other words, if this binder --
+    /// and indeed any enclosing binder -- doesn't bind anything at
+    /// all.) Otherwise, returns `None`.
+    ///
+    /// (One could imagine having a method that just unwraps a single
+    /// binder, but permits late-bound vars bound by enclosing
+    /// binders, but that would require adjusting the debruijn
+    /// indices, and given the shallow binding structure we often use,
+    /// would not be that useful.)
+    pub fn no_bound_vars(self) -> Option<T>
+    where
+        T: TypeVisitable<I>,
+    {
+        // `self.value` is equivalent to `self.skip_binder()`
+        if self.value.has_escaping_bound_vars() { None } else { Some(self.skip_binder()) }
+    }
+
+    /// Splits the contents into two things that share the same binder
+    /// level as the original, returning two distinct binders.
+    ///
+    /// `f` should consider bound regions at depth 1 to be free, and
+    /// anything it produces with bound regions at depth 1 will be
+    /// bound in the resulting return values.
+    pub fn split<U, V, F>(self, f: F) -> (Binder<I, U>, Binder<I, V>)
+    where
+        F: FnOnce(T) -> (U, V),
+    {
+        let Binder { value, bound_vars } = self;
+        let (u, v) = f(value);
+        (Binder { value: u, bound_vars }, Binder { value: v, bound_vars })
+    }
+}
+
+impl<I: Interner, T> Binder<I, Option<T>> {
+    pub fn transpose(self) -> Option<Binder<I, T>> {
+        let Binder { value, bound_vars } = self;
+        value.map(|value| Binder { value, bound_vars })
+    }
+}
+
+impl<I: Interner, T: IntoIterator> Binder<I, T> {
+    pub fn iter(self) -> impl Iterator<Item = Binder<I, T::Item>> {
+        let Binder { value, bound_vars } = self;
+        value.into_iter().map(move |value| Binder { value, bound_vars })
+    }
+}
+
+pub struct ValidateBoundVars<I: Interner> {
+    bound_vars: I::BoundVarKinds,
+    binder_index: ty::DebruijnIndex,
+    // We may encounter the same variable at different levels of binding, so
+    // this can't just be `Ty`
+    visited: SsoHashSet<(ty::DebruijnIndex, I::Ty)>,
+}
+
+impl<I: Interner> ValidateBoundVars<I> {
+    pub fn new(bound_vars: I::BoundVarKinds) -> Self {
+        ValidateBoundVars {
+            bound_vars,
+            binder_index: ty::INNERMOST,
+            visited: SsoHashSet::default(),
+        }
+    }
+}
+
+impl<I: Interner> TypeVisitor<I> for ValidateBoundVars<I> {
+    type Result = ControlFlow<()>;
+
+    fn visit_binder<T: TypeVisitable<I>>(&mut self, t: &Binder<I, T>) -> Self::Result {
+        self.binder_index.shift_in(1);
+        let result = t.super_visit_with(self);
+        self.binder_index.shift_out(1);
+        result
+    }
+
+    fn visit_ty(&mut self, t: I::Ty) -> Self::Result {
+        if t.outer_exclusive_binder() < self.binder_index
+            || !self.visited.insert((self.binder_index, t))
+        {
+            return ControlFlow::Break(());
+        }
+        match t.kind() {
+            ty::Bound(debruijn, bound_ty) if debruijn == self.binder_index => {
+                let idx = bound_ty.var().as_usize();
+                if self.bound_vars.len() <= idx {
+                    panic!("Not enough bound vars: {:?} not found in {:?}", t, self.bound_vars);
+                }
+                bound_ty.assert_eq(self.bound_vars[idx]);
+            }
+            _ => {}
+        };
+
+        t.super_visit_with(self)
+    }
+
+    fn visit_region(&mut self, r: I::Region) -> Self::Result {
+        match r.kind() {
+            ty::ReBound(index, br) if index == self.binder_index => {
+                let idx = br.var().as_usize();
+                if self.bound_vars.len() <= idx {
+                    panic!("Not enough bound vars: {:?} not found in {:?}", r, self.bound_vars);
+                }
+                br.assert_eq(self.bound_vars[idx]);
+            }
+
+            _ => (),
+        };
+
+        ControlFlow::Continue(())
+    }
+}
diff --git a/compiler/rustc_type_ir/src/const_kind.rs b/compiler/rustc_type_ir/src/const_kind.rs
index af07e9f..7076df2 100644
--- a/compiler/rustc_type_ir/src/const_kind.rs
+++ b/compiler/rustc_type_ir/src/const_kind.rs
@@ -5,7 +5,7 @@
 use rustc_type_ir_macros::{Lift_Generic, TypeFoldable_Generic, TypeVisitable_Generic};
 use std::fmt;
 
-use crate::{DebruijnIndex, DebugWithInfcx, InferCtxtLike, Interner, WithInfcx};
+use crate::{self as ty, DebruijnIndex, DebugWithInfcx, InferCtxtLike, Interner, WithInfcx};
 
 use self::ConstKind::*;
 
@@ -29,7 +29,7 @@
     /// An unnormalized const item such as an anon const or assoc const or free const item.
     /// Right now anything other than anon consts does not actually work properly but this
     /// should
-    Unevaluated(I::AliasConst),
+    Unevaluated(ty::UnevaluatedConst<I>),
 
     /// Used to hold computed value.
     Value(I::ValueConst),
diff --git a/compiler/rustc_type_ir/src/fold.rs b/compiler/rustc_type_ir/src/fold.rs
index 01bb3d7..405aba3 100644
--- a/compiler/rustc_type_ir/src/fold.rs
+++ b/compiler/rustc_type_ir/src/fold.rs
@@ -48,8 +48,8 @@
 use rustc_index::{Idx, IndexVec};
 use std::mem;
 
-use crate::Lrc;
-use crate::{visit::TypeVisitable, Interner};
+use crate::visit::TypeVisitable;
+use crate::{self as ty, Interner, Lrc};
 
 #[cfg(feature = "nightly")]
 type Never = !;
@@ -128,10 +128,9 @@
 pub trait TypeFolder<I: Interner>: FallibleTypeFolder<I, Error = Never> {
     fn interner(&self) -> I;
 
-    fn fold_binder<T>(&mut self, t: I::Binder<T>) -> I::Binder<T>
+    fn fold_binder<T>(&mut self, t: ty::Binder<I, T>) -> ty::Binder<I, T>
     where
         T: TypeFoldable<I>,
-        I::Binder<T>: TypeSuperFoldable<I>,
     {
         t.super_fold_with(self)
     }
@@ -167,10 +166,9 @@
 
     fn interner(&self) -> I;
 
-    fn try_fold_binder<T>(&mut self, t: I::Binder<T>) -> Result<I::Binder<T>, Self::Error>
+    fn try_fold_binder<T>(&mut self, t: ty::Binder<I, T>) -> Result<ty::Binder<I, T>, Self::Error>
     where
         T: TypeFoldable<I>,
-        I::Binder<T>: TypeSuperFoldable<I>,
     {
         t.try_super_fold_with(self)
     }
@@ -206,10 +204,9 @@
         TypeFolder::interner(self)
     }
 
-    fn try_fold_binder<T>(&mut self, t: I::Binder<T>) -> Result<I::Binder<T>, Never>
+    fn try_fold_binder<T>(&mut self, t: ty::Binder<I, T>) -> Result<ty::Binder<I, T>, Never>
     where
         T: TypeFoldable<I>,
-        I::Binder<T>: TypeSuperFoldable<I>,
     {
         Ok(self.fold_binder(t))
     }
diff --git a/compiler/rustc_type_ir/src/inherent.rs b/compiler/rustc_type_ir/src/inherent.rs
index f66c6e7..77fe30a 100644
--- a/compiler/rustc_type_ir/src/inherent.rs
+++ b/compiler/rustc_type_ir/src/inherent.rs
@@ -8,11 +8,8 @@
 use std::ops::Deref;
 
 use crate::fold::{TypeFoldable, TypeSuperFoldable};
-use crate::visit::{Flags, TypeSuperVisitable};
-use crate::{
-    AliasTy, AliasTyKind, BoundVar, ConstKind, ConstVid, DebruijnIndex, DebugWithInfcx, InferConst,
-    InferTy, Interner, RegionKind, TyKind, TyVid, UnevaluatedConst, UniverseIndex,
-};
+use crate::visit::{Flags, TypeSuperVisitable, TypeVisitable};
+use crate::{self as ty, DebugWithInfcx, Interner, UpcastFrom};
 
 pub trait Ty<I: Interner<Ty = Self>>:
     Copy
@@ -21,24 +18,30 @@
     + Eq
     + Into<I::GenericArg>
     + Into<I::Term>
-    + IntoKind<Kind = TyKind<I>>
+    + IntoKind<Kind = ty::TyKind<I>>
     + TypeSuperVisitable<I>
     + TypeSuperFoldable<I>
     + Flags
 {
     fn new_bool(interner: I) -> Self;
 
-    fn new_infer(interner: I, var: InferTy) -> Self;
+    fn new_infer(interner: I, var: ty::InferTy) -> Self;
 
-    fn new_var(interner: I, var: TyVid) -> Self;
+    fn new_var(interner: I, var: ty::TyVid) -> Self;
 
-    fn new_anon_bound(interner: I, debruijn: DebruijnIndex, var: BoundVar) -> Self;
+    fn new_anon_bound(interner: I, debruijn: ty::DebruijnIndex, var: ty::BoundVar) -> Self;
 
-    fn new_alias(interner: I, kind: AliasTyKind, alias_ty: AliasTy<I>) -> Self;
+    fn new_alias(interner: I, kind: ty::AliasTyKind, alias_ty: ty::AliasTy<I>) -> Self;
 }
 
 pub trait Tys<I: Interner<Tys = Self>>:
-    Copy + Debug + Hash + Eq + IntoIterator<Item = I::Ty> + Deref<Target: Deref<Target = [I::Ty]>>
+    Copy
+    + Debug
+    + Hash
+    + Eq
+    + IntoIterator<Item = I::Ty>
+    + Deref<Target: Deref<Target = [I::Ty]>>
+    + TypeVisitable<I>
 {
     fn split_inputs_and_output(self) -> (I::FnInputTys, I::Ty);
 }
@@ -49,13 +52,21 @@
 }
 
 pub trait Safety<I: Interner<Safety = Self>>: Copy + Debug + Hash + Eq {
+    fn is_safe(self) -> bool;
+
     fn prefix_str(self) -> &'static str;
 }
 
 pub trait Region<I: Interner<Region = Self>>:
-    Copy + DebugWithInfcx<I> + Hash + Eq + Into<I::GenericArg> + IntoKind<Kind = RegionKind<I>> + Flags
+    Copy
+    + DebugWithInfcx<I>
+    + Hash
+    + Eq
+    + Into<I::GenericArg>
+    + IntoKind<Kind = ty::RegionKind<I>>
+    + Flags
 {
-    fn new_anon_bound(interner: I, debruijn: DebruijnIndex, var: BoundVar) -> Self;
+    fn new_anon_bound(interner: I, debruijn: ty::DebruijnIndex, var: ty::BoundVar) -> Self;
 
     fn new_static(interner: I) -> Self;
 }
@@ -67,18 +78,23 @@
     + Eq
     + Into<I::GenericArg>
     + Into<I::Term>
-    + IntoKind<Kind = ConstKind<I>>
+    + IntoKind<Kind = ty::ConstKind<I>>
     + TypeSuperVisitable<I>
     + TypeSuperFoldable<I>
     + Flags
 {
-    fn new_infer(interner: I, var: InferConst, ty: I::Ty) -> Self;
+    fn new_infer(interner: I, var: ty::InferConst, ty: I::Ty) -> Self;
 
-    fn new_var(interner: I, var: ConstVid, ty: I::Ty) -> Self;
+    fn new_var(interner: I, var: ty::ConstVid, ty: I::Ty) -> Self;
 
-    fn new_anon_bound(interner: I, debruijn: DebruijnIndex, var: BoundVar, ty: I::Ty) -> Self;
+    fn new_anon_bound(
+        interner: I,
+        debruijn: ty::DebruijnIndex,
+        var: ty::BoundVar,
+        ty: I::Ty,
+    ) -> Self;
 
-    fn new_unevaluated(interner: I, uv: UnevaluatedConst<I>, ty: I::Ty) -> Self;
+    fn new_unevaluated(interner: I, uv: ty::UnevaluatedConst<I>, ty: I::Ty) -> Self;
 
     fn ty(self) -> I::Ty;
 }
@@ -100,6 +116,12 @@
     fn type_at(self, i: usize) -> I::Ty;
 
     fn identity_for_item(interner: I, def_id: I::DefId) -> I::GenericArgs;
+
+    fn extend_with_error(
+        tcx: I,
+        def_id: I::DefId,
+        original_args: &[I::GenericArg],
+    ) -> I::GenericArgs;
 }
 
 pub trait Predicate<I: Interner<Predicate = Self>>:
@@ -108,14 +130,25 @@
     fn is_coinductive(self, interner: I) -> bool;
 }
 
+pub trait Clause<I: Interner<Clause = Self>>:
+    Copy
+    + Debug
+    + Hash
+    + Eq
+    // FIXME: Remove these, uplift the `Upcast` impls.
+    + UpcastFrom<I, ty::Binder<I, ty::TraitRef<I>>>
+    + UpcastFrom<I, ty::Binder<I, ty::ProjectionPredicate<I>>>
+{
+}
+
 /// Common capabilities of placeholder kinds
 pub trait PlaceholderLike: Copy + Debug + Hash + Eq {
-    fn universe(self) -> UniverseIndex;
-    fn var(self) -> BoundVar;
+    fn universe(self) -> ty::UniverseIndex;
+    fn var(self) -> ty::BoundVar;
 
-    fn with_updated_universe(self, ui: UniverseIndex) -> Self;
+    fn with_updated_universe(self, ui: ty::UniverseIndex) -> Self;
 
-    fn new(ui: UniverseIndex, var: BoundVar) -> Self;
+    fn new(ui: ty::UniverseIndex, var: ty::BoundVar) -> Self;
 }
 
 pub trait IntoKind {
@@ -124,12 +157,8 @@
     fn kind(self) -> Self::Kind;
 }
 
-pub trait BoundVars<I: Interner> {
-    fn bound_vars(&self) -> I::BoundVars;
-
-    fn has_no_bound_vars(&self) -> bool;
-}
-
 pub trait BoundVarLike<I: Interner> {
-    fn var(self) -> BoundVar;
+    fn var(self) -> ty::BoundVar;
+
+    fn assert_eq(self, var: I::BoundVarKind);
 }
diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs
index 6516d5b..9b8bb21 100644
--- a/compiler/rustc_type_ir/src/interner.rs
+++ b/compiler/rustc_type_ir/src/interner.rs
@@ -28,19 +28,28 @@
     + IrPrint<CoercePredicate<Self>>
     + IrPrint<FnSig<Self>>
 {
-    type DefId: Copy + Debug + Hash + Eq;
+    type DefId: Copy + Debug + Hash + Eq + TypeVisitable<Self>;
     type AdtDef: Copy + Debug + Hash + Eq;
 
     type GenericArgs: GenericArgs<Self>;
     /// The slice of args for a specific item. For a GAT like `type Foo<'a>`, it will be `['a]`,
     /// not including the args from the parent item (trait or impl).
     type OwnItemArgs: Copy + Debug + Hash + Eq;
-    type GenericArg: Copy + DebugWithInfcx<Self> + Hash + Eq + IntoKind<Kind = GenericArgKind<Self>>;
-    type Term: Copy + Debug + Hash + Eq + IntoKind<Kind = TermKind<Self>>;
+    type GenericArg: Copy
+        + DebugWithInfcx<Self>
+        + Hash
+        + Eq
+        + IntoKind<Kind = GenericArgKind<Self>>
+        + TypeVisitable<Self>;
+    type Term: Copy + Debug + Hash + Eq + IntoKind<Kind = TermKind<Self>> + TypeVisitable<Self>;
 
-    type Binder<T: TypeVisitable<Self>>: BoundVars<Self> + TypeSuperVisitable<Self>;
-    type BoundVars: IntoIterator<Item = Self::BoundVar>;
-    type BoundVar;
+    type BoundVarKinds: Copy
+        + Debug
+        + Hash
+        + Eq
+        + Deref<Target: Deref<Target = [Self::BoundVarKind]>>
+        + Default;
+    type BoundVarKind: Copy + Debug + Hash + Eq;
 
     type CanonicalVars: Copy + Debug + Hash + Eq + IntoIterator<Item = CanonicalVarInfo<Self>>;
     type PredefinedOpaques: Copy + Debug + Hash + Eq;
@@ -51,7 +60,7 @@
     // Kinds of tys
     type Ty: Ty<Self>;
     type Tys: Tys<Self>;
-    type FnInputTys: Copy + Debug + Hash + Eq + Deref<Target = [Self::Ty]>;
+    type FnInputTys: Copy + Debug + Hash + Eq + Deref<Target = [Self::Ty]> + TypeVisitable<Self>;
     type ParamTy: Copy + Debug + Hash + Eq;
     type BoundTy: Copy + Debug + Hash + Eq + BoundVarLike<Self>;
     type PlaceholderTy: PlaceholderLike;
@@ -67,7 +76,6 @@
 
     // Kinds of consts
     type Const: Const<Self>;
-    type AliasConst: Copy + DebugWithInfcx<Self> + Hash + Eq;
     type PlaceholderConst: PlaceholderLike;
     type ParamConst: Copy + Debug + Hash + Eq;
     type BoundConst: Copy + Debug + Hash + Eq + BoundVarLike<Self>;
@@ -84,14 +92,7 @@
     // Predicates
     type ParamEnv: Copy + Debug + Hash + Eq;
     type Predicate: Predicate<Self>;
-    type TraitPredicate: Copy + Debug + Hash + Eq;
-    type RegionOutlivesPredicate: Copy + Debug + Hash + Eq;
-    type TypeOutlivesPredicate: Copy + Debug + Hash + Eq;
-    type ProjectionPredicate: Copy + Debug + Hash + Eq;
-    type NormalizesTo: Copy + Debug + Hash + Eq;
-    type SubtypePredicate: Copy + Debug + Hash + Eq;
-    type CoercePredicate: Copy + Debug + Hash + Eq;
-    type ClosureKind: Copy + Debug + Hash + Eq;
+    type Clause: Clause<Self>;
     type Clauses: Copy + Debug + Hash + Eq + TypeSuperVisitable<Self> + Flags;
 
     fn mk_canonical_var_infos(self, infos: &[CanonicalVarInfo<Self>]) -> Self::CanonicalVars;
diff --git a/compiler/rustc_type_ir/src/ir_print.rs b/compiler/rustc_type_ir/src/ir_print.rs
index af4b9ee..d57d081 100644
--- a/compiler/rustc_type_ir/src/ir_print.rs
+++ b/compiler/rustc_type_ir/src/ir_print.rs
@@ -1,8 +1,9 @@
 use std::fmt;
 
 use crate::{
-    AliasTerm, AliasTy, CoercePredicate, ExistentialProjection, ExistentialTraitRef, FnSig,
-    Interner, NormalizesTo, ProjectionPredicate, SubtypePredicate, TraitPredicate, TraitRef,
+    AliasTerm, AliasTy, Binder, CoercePredicate, ExistentialProjection, ExistentialTraitRef, FnSig,
+    Interner, NormalizesTo, OutlivesPredicate, ProjectionPredicate, SubtypePredicate,
+    TraitPredicate, TraitRef,
 };
 
 pub trait IrPrint<T> {
@@ -22,6 +23,15 @@
     }
 }
 
+impl<I: Interner, T> fmt::Display for Binder<I, T>
+where
+    I: IrPrint<Binder<I, T>>,
+{
+    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
+        <I as IrPrint<Binder<I, T>>>::print(self, fmt)
+    }
+}
+
 macro_rules! define_debug_via_print {
     ($($ty:ident),+ $(,)?) => {
         $(
@@ -49,3 +59,12 @@
 );
 
 define_debug_via_print!(TraitRef, ExistentialTraitRef, ExistentialProjection);
+
+impl<I: Interner, T> fmt::Display for OutlivesPredicate<I, T>
+where
+    I: IrPrint<OutlivesPredicate<I, T>>,
+{
+    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
+        <I as IrPrint<OutlivesPredicate<I, T>>>::print(self, fmt)
+    }
+}
diff --git a/compiler/rustc_type_ir/src/lib.rs b/compiler/rustc_type_ir/src/lib.rs
index fa9bda9..4a461b5 100644
--- a/compiler/rustc_type_ir/src/lib.rs
+++ b/compiler/rustc_type_ir/src/lib.rs
@@ -9,9 +9,13 @@
 extern crate self as rustc_type_ir;
 
 #[cfg(feature = "nightly")]
+use rustc_data_structures::sso::SsoHashSet;
+#[cfg(feature = "nightly")]
 use rustc_data_structures::sync::Lrc;
 #[cfg(feature = "nightly")]
 use rustc_macros::{Decodable, Encodable, HashStable_NoContext};
+#[cfg(not(feature = "nightly"))]
+use std::collections::HashSet as SsoHashSet;
 use std::fmt;
 use std::hash::Hash;
 #[cfg(not(feature = "nightly"))]
@@ -31,6 +35,7 @@
 
 #[macro_use]
 mod macros;
+mod binder;
 mod canonical;
 mod const_kind;
 mod debug;
@@ -43,6 +48,7 @@
 mod region_kind;
 mod upcast;
 
+pub use binder::*;
 pub use canonical::*;
 #[cfg(feature = "nightly")]
 pub use codec::*;
@@ -374,6 +380,10 @@
     fn var(self) -> BoundVar {
         self
     }
+
+    fn assert_eq(self, _var: I::BoundVarKind) {
+        unreachable!("FIXME: We really should have a separate `BoundConst` for consts")
+    }
 }
 
 /// Represents the various closure traits in the language. This
diff --git a/compiler/rustc_type_ir/src/predicate.rs b/compiler/rustc_type_ir/src/predicate.rs
index c0619d7..4e12c6b 100644
--- a/compiler/rustc_type_ir/src/predicate.rs
+++ b/compiler/rustc_type_ir/src/predicate.rs
@@ -6,10 +6,38 @@
 use rustc_type_ir_macros::{Lift_Generic, TypeFoldable_Generic, TypeVisitable_Generic};
 
 use crate::inherent::*;
+use crate::lift::Lift;
+use crate::upcast::Upcast;
 use crate::visit::TypeVisitableExt as _;
-use crate::{
-    AliasTy, AliasTyKind, DebugWithInfcx, InferCtxtLike, Interner, UnevaluatedConst, WithInfcx,
-};
+use crate::{self as ty, DebugWithInfcx, InferCtxtLike, Interner, WithInfcx};
+
+/// `A: 'region`
+#[derive(derivative::Derivative)]
+#[derivative(
+    Clone(bound = "A: Clone"),
+    Copy(bound = "A: Copy"),
+    Hash(bound = "A: Hash"),
+    PartialEq(bound = "A: PartialEq"),
+    Eq(bound = "A: Eq"),
+    Debug(bound = "A: fmt::Debug")
+)]
+#[derive(TypeVisitable_Generic, TypeFoldable_Generic)]
+#[cfg_attr(feature = "nightly", derive(TyDecodable, TyEncodable, HashStable_NoContext))]
+pub struct OutlivesPredicate<I: Interner, A>(pub A, pub I::Region);
+
+// FIXME: We manually derive `Lift` because the `derive(Lift_Generic)` doesn't
+// understand how to turn `A` to `A::Lifted` in the output `type Lifted`.
+impl<I: Interner, U: Interner, A> Lift<U> for OutlivesPredicate<I, A>
+where
+    A: Lift<U>,
+    I::Region: Lift<U, Lifted = U::Region>,
+{
+    type Lifted = OutlivesPredicate<U, A::Lifted>;
+
+    fn lift_to_tcx(self, tcx: U) -> Option<Self::Lifted> {
+        Some(OutlivesPredicate(self.0.lift_to_tcx(tcx)?, self.1.lift_to_tcx(tcx)?))
+    }
+}
 
 /// A complete reference to a trait. These take numerous guises in syntax,
 /// but perhaps the most recognizable form is in a where-clause:
@@ -75,6 +103,16 @@
     }
 }
 
+impl<I: Interner> ty::Binder<I, TraitRef<I>> {
+    pub fn self_ty(&self) -> ty::Binder<I, I::Ty> {
+        self.map_bound_ref(|tr| tr.self_ty())
+    }
+
+    pub fn def_id(&self) -> I::DefId {
+        self.skip_binder().def_id
+    }
+}
+
 #[derive(derivative::Derivative)]
 #[derivative(
     Clone(bound = ""),
@@ -112,6 +150,22 @@
     }
 }
 
+impl<I: Interner> ty::Binder<I, TraitPredicate<I>> {
+    pub fn def_id(self) -> I::DefId {
+        // Ok to skip binder since trait `DefId` does not care about regions.
+        self.skip_binder().def_id()
+    }
+
+    pub fn self_ty(self) -> ty::Binder<I, I::Ty> {
+        self.map_bound(|trait_ref| trait_ref.self_ty())
+    }
+
+    #[inline]
+    pub fn polarity(self) -> PredicatePolarity {
+        self.skip_binder().polarity
+    }
+}
+
 impl<I: Interner> fmt::Debug for TraitPredicate<I> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         // FIXME(effects) printing?
@@ -204,6 +258,34 @@
     }
 }
 
+impl<I: Interner> ty::Binder<I, ExistentialPredicate<I>> {
+    /// Given an existential predicate like `?Self: PartialEq<u32>` (e.g., derived from `dyn PartialEq<u32>`),
+    /// and a concrete type `self_ty`, returns a full predicate where the existentially quantified variable `?Self`
+    /// has been replaced with `self_ty` (e.g., `self_ty: PartialEq<u32>`, in our example).
+    pub fn with_self_ty(&self, tcx: I, self_ty: I::Ty) -> I::Clause {
+        match self.skip_binder() {
+            ExistentialPredicate::Trait(tr) => {
+                self.rebind(tr).with_self_ty(tcx, self_ty).upcast(tcx)
+            }
+            ExistentialPredicate::Projection(p) => {
+                self.rebind(p.with_self_ty(tcx, self_ty)).upcast(tcx)
+            }
+            ExistentialPredicate::AutoTrait(did) => {
+                let generics = tcx.generics_of(did);
+                let trait_ref = if generics.count() == 1 {
+                    ty::TraitRef::new(tcx, did, [self_ty])
+                } else {
+                    // If this is an ill-formed auto trait, then synthesize
+                    // new error args for the missing generics.
+                    let err_args = GenericArgs::extend_with_error(tcx, did, &[self_ty.into()]);
+                    ty::TraitRef::new(tcx, did, err_args)
+                };
+                self.rebind(trait_ref).upcast(tcx)
+            }
+        }
+    }
+}
+
 /// An existential reference to a trait, where `Self` is erased.
 /// For example, the trait object `Trait<'a, 'b, X, Y>` is:
 /// ```ignore (illustrative)
@@ -253,6 +335,20 @@
     }
 }
 
+impl<I: Interner> ty::Binder<I, ExistentialTraitRef<I>> {
+    pub fn def_id(&self) -> I::DefId {
+        self.skip_binder().def_id
+    }
+
+    /// Object types don't have a self type specified. Therefore, when
+    /// we convert the principal trait-ref into a normal trait-ref,
+    /// you must give *some* self type. A common choice is `mk_err()`
+    /// or some placeholder type.
+    pub fn with_self_ty(&self, tcx: I, self_ty: I::Ty) -> ty::Binder<I, TraitRef<I>> {
+        self.map_bound(|trait_ref| trait_ref.with_self_ty(tcx, self_ty))
+    }
+}
+
 /// A `ProjectionPredicate` for an `ExistentialTraitRef`.
 #[derive(derivative::Derivative)]
 #[derivative(
@@ -308,6 +404,16 @@
     }
 }
 
+impl<I: Interner> ty::Binder<I, ExistentialProjection<I>> {
+    pub fn with_self_ty(&self, tcx: I, self_ty: I::Ty) -> ty::Binder<I, ProjectionPredicate<I>> {
+        self.map_bound(|p| p.with_self_ty(tcx, self_ty))
+    }
+
+    pub fn item_def_id(&self) -> I::DefId {
+        self.skip_binder().def_id
+    }
+}
+
 #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
 #[cfg_attr(feature = "nightly", derive(Encodable, Decodable, HashStable_NoContext))]
 pub enum AliasTermKind {
@@ -414,7 +520,7 @@
         AliasTerm { def_id, args, _use_alias_term_new_instead: () }
     }
 
-    pub fn expect_ty(self, interner: I) -> AliasTy<I> {
+    pub fn expect_ty(self, interner: I) -> ty::AliasTy<I> {
         match self.kind(interner) {
             AliasTermKind::ProjectionTy
             | AliasTermKind::InherentTy
@@ -424,7 +530,7 @@
                 panic!("Cannot turn `UnevaluatedConst` into `AliasTy`")
             }
         }
-        AliasTy { def_id: self.def_id, args: self.args, _use_alias_ty_new_instead: () }
+        ty::AliasTy { def_id: self.def_id, args: self.args, _use_alias_ty_new_instead: () }
     }
 
     pub fn kind(self, interner: I) -> AliasTermKind {
@@ -435,32 +541,32 @@
         match self.kind(interner) {
             AliasTermKind::ProjectionTy => Ty::new_alias(
                 interner,
-                AliasTyKind::Projection,
-                AliasTy { def_id: self.def_id, args: self.args, _use_alias_ty_new_instead: () },
+                ty::AliasTyKind::Projection,
+                ty::AliasTy { def_id: self.def_id, args: self.args, _use_alias_ty_new_instead: () },
             )
             .into(),
             AliasTermKind::InherentTy => Ty::new_alias(
                 interner,
-                AliasTyKind::Inherent,
-                AliasTy { def_id: self.def_id, args: self.args, _use_alias_ty_new_instead: () },
+                ty::AliasTyKind::Inherent,
+                ty::AliasTy { def_id: self.def_id, args: self.args, _use_alias_ty_new_instead: () },
             )
             .into(),
             AliasTermKind::OpaqueTy => Ty::new_alias(
                 interner,
-                AliasTyKind::Opaque,
-                AliasTy { def_id: self.def_id, args: self.args, _use_alias_ty_new_instead: () },
+                ty::AliasTyKind::Opaque,
+                ty::AliasTy { def_id: self.def_id, args: self.args, _use_alias_ty_new_instead: () },
             )
             .into(),
             AliasTermKind::WeakTy => Ty::new_alias(
                 interner,
-                AliasTyKind::Weak,
-                AliasTy { def_id: self.def_id, args: self.args, _use_alias_ty_new_instead: () },
+                ty::AliasTyKind::Weak,
+                ty::AliasTy { def_id: self.def_id, args: self.args, _use_alias_ty_new_instead: () },
             )
             .into(),
             AliasTermKind::UnevaluatedConst | AliasTermKind::ProjectionConst => {
                 I::Const::new_unevaluated(
                     interner,
-                    UnevaluatedConst::new(self.def_id, self.args),
+                    ty::UnevaluatedConst::new(self.def_id, self.args),
                     interner.type_of_instantiated(self.def_id, self.args),
                 )
                 .into()
@@ -514,14 +620,14 @@
     }
 }
 
-impl<I: Interner> From<AliasTy<I>> for AliasTerm<I> {
-    fn from(ty: AliasTy<I>) -> Self {
+impl<I: Interner> From<ty::AliasTy<I>> for AliasTerm<I> {
+    fn from(ty: ty::AliasTy<I>) -> Self {
         AliasTerm { args: ty.args, def_id: ty.def_id, _use_alias_term_new_instead: () }
     }
 }
 
-impl<I: Interner> From<UnevaluatedConst<I>> for AliasTerm<I> {
-    fn from(ct: UnevaluatedConst<I>) -> Self {
+impl<I: Interner> From<ty::UnevaluatedConst<I>> for AliasTerm<I> {
+    fn from(ct: ty::UnevaluatedConst<I>) -> Self {
         AliasTerm { args: ct.args, def_id: ct.def, _use_alias_term_new_instead: () }
     }
 }
@@ -571,6 +677,40 @@
     }
 }
 
+impl<I: Interner> ty::Binder<I, ProjectionPredicate<I>> {
+    /// Returns the `DefId` of the trait of the associated item being projected.
+    #[inline]
+    pub fn trait_def_id(&self, tcx: I) -> I::DefId {
+        self.skip_binder().projection_term.trait_def_id(tcx)
+    }
+
+    /// Get the trait ref required for this projection to be well formed.
+    /// Note that for generic associated types the predicates of the associated
+    /// type also need to be checked.
+    #[inline]
+    pub fn required_poly_trait_ref(&self, tcx: I) -> ty::Binder<I, TraitRef<I>> {
+        // Note: unlike with `TraitRef::to_poly_trait_ref()`,
+        // `self.0.trait_ref` is permitted to have escaping regions.
+        // This is because here `self` has a `Binder` and so does our
+        // return value, so we are preserving the number of binding
+        // levels.
+        self.map_bound(|predicate| predicate.projection_term.trait_ref(tcx))
+    }
+
+    pub fn term(&self) -> ty::Binder<I, I::Term> {
+        self.map_bound(|predicate| predicate.term)
+    }
+
+    /// The `DefId` of the `TraitItem` for the associated type.
+    ///
+    /// Note that this is not the `DefId` of the `TraitRef` containing this
+    /// associated type, which is in `tcx.associated_item(projection_def_id()).container`.
+    pub fn projection_def_id(&self) -> I::DefId {
+        // Ok to skip binder since trait `DefId` does not care about regions.
+        self.skip_binder().projection_term.def_id
+    }
+}
+
 impl<I: Interner> fmt::Debug for ProjectionPredicate<I> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         write!(f, "ProjectionPredicate({:?}, {:?})", self.projection_term, self.term)
diff --git a/compiler/rustc_type_ir/src/predicate_kind.rs b/compiler/rustc_type_ir/src/predicate_kind.rs
index c477ab1..efe270e 100644
--- a/compiler/rustc_type_ir/src/predicate_kind.rs
+++ b/compiler/rustc_type_ir/src/predicate_kind.rs
@@ -3,7 +3,7 @@
 use rustc_type_ir_macros::{TypeFoldable_Generic, TypeVisitable_Generic};
 use std::fmt;
 
-use crate::Interner;
+use crate::{self as ty, Interner};
 
 /// A clause is something that can appear in where bounds or be inferred
 /// by implied bounds.
@@ -15,17 +15,17 @@
     /// Corresponds to `where Foo: Bar<A, B, C>`. `Foo` here would be
     /// the `Self` type of the trait reference and `A`, `B`, and `C`
     /// would be the type parameters.
-    Trait(I::TraitPredicate),
+    Trait(ty::TraitPredicate<I>),
 
-    /// `where 'a: 'b`
-    RegionOutlives(I::RegionOutlivesPredicate),
+    /// `where 'a: 'r`
+    RegionOutlives(ty::OutlivesPredicate<I, I::Region>),
 
-    /// `where T: 'a`
-    TypeOutlives(I::TypeOutlivesPredicate),
+    /// `where T: 'r`
+    TypeOutlives(ty::OutlivesPredicate<I, I::Ty>),
 
     /// `where <T as TraitRef>::Name == X`, approximately.
     /// See the `ProjectionPredicate` struct for details.
-    Projection(I::ProjectionPredicate),
+    Projection(ty::ProjectionPredicate<I>),
 
     /// Ensures that a const generic argument to a parameter `const N: u8`
     /// is of type `u8`.
@@ -75,7 +75,7 @@
     /// This obligation is created most often when we have two
     /// unresolved type variables and hence don't have enough
     /// information to process the subtyping obligation yet.
-    Subtype(I::SubtypePredicate),
+    Subtype(ty::SubtypePredicate<I>),
 
     /// `T1` coerced to `T2`
     ///
@@ -85,7 +85,7 @@
     /// obligation yet. At the moment, we actually process coercions
     /// very much like subtyping and don't handle the full coercion
     /// logic.
-    Coerce(I::CoercePredicate),
+    Coerce(ty::CoercePredicate<I>),
 
     /// Constants must be equal. The first component is the const that is expected.
     ConstEquate(I::Const, I::Const),
@@ -102,7 +102,7 @@
     /// `T as Trait>::Assoc`, `Projection(<T as Trait>::Assoc, ?x)` constrains `?x`
     /// to `<T as Trait>::Assoc` while `NormalizesTo(<T as Trait>::Assoc, ?x)`
     /// results in `NoSolution`.
-    NormalizesTo(I::NormalizesTo),
+    NormalizesTo(ty::NormalizesTo<I>),
 
     /// Separate from `ClauseKind::Projection` which is used for normalization in new solver.
     /// This predicate requires two terms to be equal to eachother.
diff --git a/compiler/rustc_type_ir/src/ty_kind.rs b/compiler/rustc_type_ir/src/ty_kind.rs
index 629ea9f..38082bf 100644
--- a/compiler/rustc_type_ir/src/ty_kind.rs
+++ b/compiler/rustc_type_ir/src/ty_kind.rs
@@ -8,7 +8,7 @@
 use std::fmt;
 
 use crate::inherent::*;
-use crate::{DebruijnIndex, DebugWithInfcx, InferCtxtLike, Interner, TraitRef, WithInfcx};
+use crate::{self as ty, DebruijnIndex, DebugWithInfcx, InferCtxtLike, Interner, WithInfcx};
 
 use self::TyKind::*;
 
@@ -514,7 +514,7 @@
     /// For example, if this is a projection of `<T as StreamingIterator>::Item<'a>`,
     /// then this function would return a `T: StreamingIterator` trait reference and
     /// `['a]` as the own args.
-    pub fn trait_ref_and_own_args(self, interner: I) -> (TraitRef<I>, I::OwnItemArgs) {
+    pub fn trait_ref_and_own_args(self, interner: I) -> (ty::TraitRef<I>, I::OwnItemArgs) {
         debug_assert_eq!(self.kind(interner), AliasTyKind::Projection);
         interner.trait_ref_and_own_args_for_alias(self.def_id, self.args)
     }
@@ -526,7 +526,7 @@
     /// WARNING: This will drop the args for generic associated types
     /// consider calling [Self::trait_ref_and_own_args] to get those
     /// as well.
-    pub fn trait_ref(self, interner: I) -> TraitRef<I> {
+    pub fn trait_ref(self, interner: I) -> ty::TraitRef<I> {
         self.trait_ref_and_own_args(interner).0
     }
 }
@@ -982,6 +982,49 @@
     pub fn output(self) -> I::Ty {
         self.split_inputs_and_output().1
     }
+
+    pub fn is_fn_trait_compatible(self) -> bool {
+        let FnSig { safety, abi, c_variadic, .. } = self;
+        !c_variadic && safety.is_safe() && abi.is_rust()
+    }
+}
+
+impl<I: Interner> ty::Binder<I, FnSig<I>> {
+    #[inline]
+    pub fn inputs(self) -> ty::Binder<I, I::FnInputTys> {
+        self.map_bound(|fn_sig| fn_sig.inputs())
+    }
+
+    #[inline]
+    #[track_caller]
+    pub fn input(self, index: usize) -> ty::Binder<I, I::Ty> {
+        self.map_bound(|fn_sig| fn_sig.inputs()[index])
+    }
+
+    pub fn inputs_and_output(self) -> ty::Binder<I, I::Tys> {
+        self.map_bound(|fn_sig| fn_sig.inputs_and_output)
+    }
+
+    #[inline]
+    pub fn output(self) -> ty::Binder<I, I::Ty> {
+        self.map_bound(|fn_sig| fn_sig.output())
+    }
+
+    pub fn c_variadic(self) -> bool {
+        self.skip_binder().c_variadic
+    }
+
+    pub fn safety(self) -> I::Safety {
+        self.skip_binder().safety
+    }
+
+    pub fn abi(self) -> I::Abi {
+        self.skip_binder().abi
+    }
+
+    pub fn is_fn_trait_compatible(&self) -> bool {
+        self.skip_binder().is_fn_trait_compatible()
+    }
 }
 
 impl<I: Interner> fmt::Debug for FnSig<I> {
diff --git a/compiler/rustc_type_ir/src/visit.rs b/compiler/rustc_type_ir/src/visit.rs
index 3d4125f..6880c7b 100644
--- a/compiler/rustc_type_ir/src/visit.rs
+++ b/compiler/rustc_type_ir/src/visit.rs
@@ -90,7 +90,7 @@
     #[cfg(not(feature = "nightly"))]
     type Result: VisitorResult;
 
-    fn visit_binder<T: TypeVisitable<I>>(&mut self, t: &I::Binder<T>) -> Self::Result {
+    fn visit_binder<T: TypeVisitable<I>>(&mut self, t: &ty::Binder<I, T>) -> Self::Result {
         t.super_visit_with(self)
     }
 
@@ -376,11 +376,11 @@
 impl<I: Interner> TypeVisitor<I> for HasTypeFlagsVisitor {
     type Result = ControlFlow<FoundFlags>;
 
-    fn visit_binder<T: TypeVisitable<I>>(&mut self, t: &I::Binder<T>) -> Self::Result {
+    fn visit_binder<T: TypeVisitable<I>>(&mut self, t: &ty::Binder<I, T>) -> Self::Result {
         // If we're looking for the HAS_BINDER_VARS flag, check if the
         // binder has vars. This won't be present in the binder's bound
         // value, so we need to check here too.
-        if self.flags.intersects(TypeFlags::HAS_BINDER_VARS) && !t.has_no_bound_vars() {
+        if self.flags.intersects(TypeFlags::HAS_BINDER_VARS) && !t.bound_vars().is_empty() {
             return ControlFlow::Break(FoundFlags);
         }
 
@@ -476,7 +476,7 @@
 impl<I: Interner> TypeVisitor<I> for HasEscapingVarsVisitor {
     type Result = ControlFlow<FoundEscapingVars>;
 
-    fn visit_binder<T: TypeVisitable<I>>(&mut self, t: &I::Binder<T>) -> Self::Result {
+    fn visit_binder<T: TypeVisitable<I>>(&mut self, t: &ty::Binder<I, T>) -> Self::Result {
         self.outer_index.shift_in(1);
         let result = t.super_visit_with(self);
         self.outer_index.shift_out(1);
diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs
index d58e1db..8e468a8 100644
--- a/library/core/src/intrinsics.rs
+++ b/library/core/src/intrinsics.rs
@@ -1483,10 +1483,10 @@
     ///
     /// # Safety
     ///
-    /// Both the starting and resulting pointer must be either in bounds or one
-    /// byte past the end of an allocated object. If either pointer is out of
-    /// bounds or arithmetic overflow occurs then any further use of the
-    /// returned value will result in undefined behavior.
+    /// If the computed offset is non-zero, then both the starting and resulting pointer must be
+    /// either in bounds or at the end of an allocated object. If either pointer is out
+    /// of bounds or arithmetic overflow occurs then any further use of the returned value will
+    /// result in undefined behavior.
     ///
     /// The stabilized version of this intrinsic is [`pointer::offset`].
     #[must_use = "returns a new pointer rather than modifying its argument"]
@@ -1502,7 +1502,7 @@
     /// # Safety
     ///
     /// Unlike the `offset` intrinsic, this intrinsic does not restrict the
-    /// resulting pointer to point into or one byte past the end of an allocated
+    /// resulting pointer to point into or at the end of an allocated
     /// object, and it wraps with two's complement arithmetic. The resulting
     /// value is not necessarily valid to be used to actually access memory.
     ///
diff --git a/library/core/src/primitive_docs.rs b/library/core/src/primitive_docs.rs
index 8283fdc..5989bcb 100644
--- a/library/core/src/primitive_docs.rs
+++ b/library/core/src/primitive_docs.rs
@@ -1476,14 +1476,17 @@
 ///
 /// For instance, this means that unsafe code in a safe function may assume these invariants are
 /// ensured of arguments passed by the caller, and it may assume that these invariants are ensured
-/// of return values from any safe functions it calls. In most cases, the inverse is also true:
-/// unsafe code must not violate these invariants when passing arguments to safe functions or
-/// returning values from safe functions; such violations may result in undefined behavior. Where
-/// exceptions to this latter requirement exist, they will be called out explicitly in documentation.
+/// of return values from any safe functions it calls.
+///
+/// For the other direction, things are more complicated: when unsafe code passes arguments
+/// to safe functions or returns values from safe functions, they generally must *at least*
+/// not violate these invariants. The full requirements are stronger, as the reference generally
+/// must point to data that is safe to use at type `T`.
 ///
 /// It is not decided yet whether unsafe code may violate these invariants temporarily on internal
 /// data. As a consequence, unsafe code which violates these invariants temporarily on internal data
-/// may become unsound in future versions of Rust depending on how this question is decided.
+/// may be unsound or become unsound in future versions of Rust depending on how this question is
+/// decided.
 ///
 /// [allocated object]: ptr#allocated-object
 #[stable(feature = "rust1", since = "1.0.0")]
diff --git a/library/core/src/ptr/const_ptr.rs b/library/core/src/ptr/const_ptr.rs
index 73bb256..c8065b2 100644
--- a/library/core/src/ptr/const_ptr.rs
+++ b/library/core/src/ptr/const_ptr.rs
@@ -465,8 +465,9 @@
     /// If any of the following conditions are violated, the result is Undefined
     /// Behavior:
     ///
-    /// * Both the starting and resulting pointer must be either in bounds or one
-    ///   byte past the end of the same [allocated object].
+    /// * If the computed offset, **in bytes**, is non-zero, then both the starting and resulting
+    ///   pointer must be either in bounds or at the end of the same [allocated object].
+    ///   (If it is zero, then the function is always well-defined.)
     ///
     /// * The computed offset, **in bytes**, cannot overflow an `isize`.
     ///
@@ -676,11 +677,11 @@
     /// If any of the following conditions are violated, the result is Undefined
     /// Behavior:
     ///
-    /// * Both `self` and `origin` must be either in bounds or one
-    ///   byte past the end of the same [allocated object].
+    /// * `self` and `origin` must either
     ///
-    /// * Both pointers must be *derived from* a pointer to the same object.
-    ///   (See below for an example.)
+    ///   * both be *derived from* a pointer to the same [allocated object], and the memory range between
+    ///     the two pointers must be either empty or in bounds of that object. (See below for an example.)
+    ///   * or both be derived from an integer literal/constant, and point to the same address.
     ///
     /// * The distance between the pointers, in bytes, must be an exact multiple
     ///   of the size of `T`.
@@ -951,8 +952,9 @@
     /// If any of the following conditions are violated, the result is Undefined
     /// Behavior:
     ///
-    /// * Both the starting and resulting pointer must be either in bounds or one
-    ///   byte past the end of the same [allocated object].
+    /// * If the computed offset, **in bytes**, is non-zero, then both the starting and resulting
+    ///   pointer must be either in bounds or at the end of the same [allocated object].
+    ///   (If it is zero, then the function is always well-defined.)
     ///
     /// * The computed offset, **in bytes**, cannot overflow an `isize`.
     ///
@@ -1035,8 +1037,9 @@
     /// If any of the following conditions are violated, the result is Undefined
     /// Behavior:
     ///
-    /// * Both the starting and resulting pointer must be either in bounds or one
-    ///   byte past the end of the same [allocated object].
+    /// * If the computed offset, **in bytes**, is non-zero, then both the starting and resulting
+    ///   pointer must be either in bounds or at the end of the same [allocated object].
+    ///   (If it is zero, then the function is always well-defined.)
     ///
     /// * The computed offset cannot exceed `isize::MAX` **bytes**.
     ///
diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs
index f87b6bb..d2bbdc8 100644
--- a/library/core/src/ptr/mod.rs
+++ b/library/core/src/ptr/mod.rs
@@ -15,18 +15,13 @@
 //! The precise rules for validity are not determined yet. The guarantees that are
 //! provided at this point are very minimal:
 //!
-//! * A [null] pointer is *never* valid, not even for accesses of [size zero][zst].
+//! * For operations of [size zero][zst], *every* pointer is valid, including the [null] pointer.
+//!   The following points are only concerned with non-zero-sized accesses.
+//! * A [null] pointer is *never* valid.
 //! * For a pointer to be valid, it is necessary, but not always sufficient, that the pointer
 //!   be *dereferenceable*: the memory range of the given size starting at the pointer must all be
 //!   within the bounds of a single allocated object. Note that in Rust,
 //!   every (stack-allocated) variable is considered a separate allocated object.
-//! * Even for operations of [size zero][zst], the pointer must not be pointing to deallocated
-//!   memory, i.e., deallocation makes pointers invalid even for zero-sized operations. However,
-//!   casting any non-zero integer *literal* to a pointer is valid for zero-sized accesses, even if
-//!   some memory happens to exist at that address and gets deallocated. This corresponds to writing
-//!   your own allocator: allocating zero-sized objects is not very hard. The canonical way to
-//!   obtain a pointer that is valid for zero-sized accesses is [`NonNull::dangling`].
-//FIXME: mention `ptr::dangling` above, once it is stable.
 //! * All accesses performed by functions in this module are *non-atomic* in the sense
 //!   of [atomic operations] used to synchronize between threads. This means it is
 //!   undefined behavior to perform two concurrent accesses to the same location from different
diff --git a/library/core/src/ptr/mut_ptr.rs b/library/core/src/ptr/mut_ptr.rs
index 7856a1d..c539534 100644
--- a/library/core/src/ptr/mut_ptr.rs
+++ b/library/core/src/ptr/mut_ptr.rs
@@ -480,8 +480,9 @@
     /// If any of the following conditions are violated, the result is Undefined
     /// Behavior:
     ///
-    /// * Both the starting and resulting pointer must be either in bounds or one
-    ///   byte past the end of the same [allocated object].
+    /// * If the computed offset, **in bytes**, is non-zero, then both the starting and resulting
+    ///   pointer must be either in bounds or at the end of the same [allocated object].
+    ///   (If it is zero, then the function is always well-defined.)
     ///
     /// * The computed offset, **in bytes**, cannot overflow an `isize`.
     ///
@@ -904,11 +905,11 @@
     /// If any of the following conditions are violated, the result is Undefined
     /// Behavior:
     ///
-    /// * Both `self` and `origin` must be either in bounds or one
-    ///   byte past the end of the same [allocated object].
+    /// * `self` and `origin` must either
     ///
-    /// * Both pointers must be *derived from* a pointer to the same object.
-    ///   (See below for an example.)
+    ///   * both be *derived from* a pointer to the same [allocated object], and the memory range between
+    ///     the two pointers must be either empty or in bounds of that object. (See below for an example.)
+    ///   * or both be derived from an integer literal/constant, and point to the same address.
     ///
     /// * The distance between the pointers, in bytes, must be an exact multiple
     ///   of the size of `T`.
@@ -1095,8 +1096,9 @@
     /// If any of the following conditions are violated, the result is Undefined
     /// Behavior:
     ///
-    /// * Both the starting and resulting pointer must be either in bounds or one
-    ///   byte past the end of the same [allocated object].
+    /// * If the computed offset, **in bytes**, is non-zero, then both the starting and resulting
+    ///   pointer must be either in bounds or at the end of the same [allocated object].
+    ///   (If it is zero, then the function is always well-defined.)
     ///
     /// * The computed offset, **in bytes**, cannot overflow an `isize`.
     ///
@@ -1179,8 +1181,9 @@
     /// If any of the following conditions are violated, the result is Undefined
     /// Behavior:
     ///
-    /// * Both the starting and resulting pointer must be either in bounds or one
-    ///   byte past the end of the same [allocated object].
+    /// * If the computed offset, **in bytes**, is non-zero, then both the starting and resulting
+    ///   pointer must be either in bounds or at the end of the same [allocated object].
+    ///   (If it is zero, then the function is always well-defined.)
     ///
     /// * The computed offset cannot exceed `isize::MAX` **bytes**.
     ///
diff --git a/src/librustdoc/scrape_examples.rs b/src/librustdoc/scrape_examples.rs
index 9c9b386..e9b380f 100644
--- a/src/librustdoc/scrape_examples.rs
+++ b/src/librustdoc/scrape_examples.rs
@@ -344,7 +344,9 @@
             Ok(bytes) => bytes,
             Err(e) => dcx.fatal(format!("failed to load examples: {e}")),
         };
-        let mut decoder = MemDecoder::new(&bytes, 0);
+        let Ok(mut decoder) = MemDecoder::new(&bytes, 0) else {
+            dcx.fatal(format!("Corrupt metadata encountered in {path}"))
+        };
         let calls = AllCallLocations::decode(&mut decoder);
 
         for (function, fn_calls) in calls.into_iter() {
diff --git a/src/tools/miri/tests/fail/dangling_pointers/dangling_zst_deref.rs b/src/tools/miri/tests/fail/dangling_pointers/dangling_zst_deref.rs
deleted file mode 100644
index a1fefe0..0000000
--- a/src/tools/miri/tests/fail/dangling_pointers/dangling_zst_deref.rs
+++ /dev/null
@@ -1,10 +0,0 @@
-// Make sure we find these even with many checks disabled.
-//@compile-flags: -Zmiri-disable-alignment-check -Zmiri-disable-stacked-borrows -Zmiri-disable-validation
-
-fn main() {
-    let p = {
-        let b = Box::new(42);
-        &*b as *const i32 as *const ()
-    };
-    let _x = unsafe { *p }; //~ ERROR: has been freed
-}
diff --git a/src/tools/miri/tests/fail/dangling_pointers/dangling_zst_deref.stderr b/src/tools/miri/tests/fail/dangling_pointers/dangling_zst_deref.stderr
deleted file mode 100644
index 72b9a4a..0000000
--- a/src/tools/miri/tests/fail/dangling_pointers/dangling_zst_deref.stderr
+++ /dev/null
@@ -1,25 +0,0 @@
-error: Undefined Behavior: memory access failed: ALLOC has been freed, so this pointer is dangling
-  --> $DIR/dangling_zst_deref.rs:LL:CC
-   |
-LL |     let _x = unsafe { *p };
-   |                       ^^ memory access failed: ALLOC has been freed, so this pointer is dangling
-   |
-   = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
-   = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
-help: ALLOC was allocated here:
-  --> $DIR/dangling_zst_deref.rs:LL:CC
-   |
-LL |         let b = Box::new(42);
-   |                 ^^^^^^^^^^^^
-help: ALLOC was deallocated here:
-  --> $DIR/dangling_zst_deref.rs:LL:CC
-   |
-LL |     };
-   |     ^
-   = note: BACKTRACE (of the first span):
-   = note: inside `main` at $DIR/dangling_zst_deref.rs:LL:CC
-
-note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
-
-error: aborting due to 1 previous error
-
diff --git a/src/tools/miri/tests/fail/dangling_pointers/maybe_null_pointer_deref_zst.rs b/src/tools/miri/tests/fail/dangling_pointers/maybe_null_pointer_deref_zst.rs
deleted file mode 100644
index 73d0b12..0000000
--- a/src/tools/miri/tests/fail/dangling_pointers/maybe_null_pointer_deref_zst.rs
+++ /dev/null
@@ -1,5 +0,0 @@
-fn main() {
-    // This pointer *could* be NULL so we cannot load from it, not even at ZST
-    let ptr = (&0u8 as *const u8).wrapping_sub(0x800) as *const ();
-    let _x: () = unsafe { *ptr }; //~ ERROR: out-of-bounds
-}
diff --git a/src/tools/miri/tests/fail/dangling_pointers/maybe_null_pointer_deref_zst.stderr b/src/tools/miri/tests/fail/dangling_pointers/maybe_null_pointer_deref_zst.stderr
deleted file mode 100644
index 13c53e2..0000000
--- a/src/tools/miri/tests/fail/dangling_pointers/maybe_null_pointer_deref_zst.stderr
+++ /dev/null
@@ -1,15 +0,0 @@
-error: Undefined Behavior: memory access failed: ALLOC has size 1, so pointer at offset -2048 is out-of-bounds
-  --> $DIR/maybe_null_pointer_deref_zst.rs:LL:CC
-   |
-LL |     let _x: () = unsafe { *ptr };
-   |                           ^^^^ memory access failed: ALLOC has size 1, so pointer at offset -2048 is out-of-bounds
-   |
-   = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
-   = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
-   = note: BACKTRACE:
-   = note: inside `main` at $DIR/maybe_null_pointer_deref_zst.rs:LL:CC
-
-note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
-
-error: aborting due to 1 previous error
-
diff --git a/src/tools/miri/tests/fail/dangling_pointers/maybe_null_pointer_write_zst.rs b/src/tools/miri/tests/fail/dangling_pointers/maybe_null_pointer_write_zst.rs
deleted file mode 100644
index 5537207..0000000
--- a/src/tools/miri/tests/fail/dangling_pointers/maybe_null_pointer_write_zst.rs
+++ /dev/null
@@ -1,8 +0,0 @@
-fn main() {
-    // This pointer *could* be NULL so we cannot load from it, not even at ZST.
-    // Not using the () type here, as writes of that type do not even have MIR generated.
-    // Also not assigning directly as that's array initialization, not assignment.
-    let zst_val = [1u8; 0];
-    let ptr = (&0u8 as *const u8).wrapping_sub(0x800) as *mut [u8; 0];
-    unsafe { *ptr = zst_val }; //~ ERROR: out-of-bounds
-}
diff --git a/src/tools/miri/tests/fail/dangling_pointers/maybe_null_pointer_write_zst.stderr b/src/tools/miri/tests/fail/dangling_pointers/maybe_null_pointer_write_zst.stderr
deleted file mode 100644
index e4e23e8..0000000
--- a/src/tools/miri/tests/fail/dangling_pointers/maybe_null_pointer_write_zst.stderr
+++ /dev/null
@@ -1,15 +0,0 @@
-error: Undefined Behavior: memory access failed: ALLOC has size 1, so pointer at offset -2048 is out-of-bounds
-  --> $DIR/maybe_null_pointer_write_zst.rs:LL:CC
-   |
-LL |     unsafe { *ptr = zst_val };
-   |              ^^^^^^^^^^^^^^ memory access failed: ALLOC has size 1, so pointer at offset -2048 is out-of-bounds
-   |
-   = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
-   = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
-   = note: BACKTRACE:
-   = note: inside `main` at $DIR/maybe_null_pointer_write_zst.rs:LL:CC
-
-note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
-
-error: aborting due to 1 previous error
-
diff --git a/src/tools/miri/tests/fail/dangling_pointers/null_pointer_deref_zst.rs b/src/tools/miri/tests/fail/dangling_pointers/null_pointer_deref_zst.rs
deleted file mode 100644
index f8af43f..0000000
--- a/src/tools/miri/tests/fail/dangling_pointers/null_pointer_deref_zst.rs
+++ /dev/null
@@ -1,5 +0,0 @@
-#[allow(deref_nullptr)]
-fn main() {
-    let x: () = unsafe { *std::ptr::null() }; //~ ERROR: memory access failed: null pointer is a dangling pointer
-    panic!("this should never print: {:?}", x);
-}
diff --git a/src/tools/miri/tests/fail/dangling_pointers/null_pointer_deref_zst.stderr b/src/tools/miri/tests/fail/dangling_pointers/null_pointer_deref_zst.stderr
deleted file mode 100644
index 1a8794f..0000000
--- a/src/tools/miri/tests/fail/dangling_pointers/null_pointer_deref_zst.stderr
+++ /dev/null
@@ -1,15 +0,0 @@
-error: Undefined Behavior: memory access failed: null pointer is a dangling pointer (it has no provenance)
-  --> $DIR/null_pointer_deref_zst.rs:LL:CC
-   |
-LL |     let x: () = unsafe { *std::ptr::null() };
-   |                          ^^^^^^^^^^^^^^^^^ memory access failed: null pointer is a dangling pointer (it has no provenance)
-   |
-   = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
-   = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
-   = note: BACKTRACE:
-   = note: inside `main` at $DIR/null_pointer_deref_zst.rs:LL:CC
-
-note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
-
-error: aborting due to 1 previous error
-
diff --git a/src/tools/miri/tests/fail/dangling_pointers/null_pointer_write_zst.rs b/src/tools/miri/tests/fail/dangling_pointers/null_pointer_write_zst.rs
deleted file mode 100644
index edd6c8f..0000000
--- a/src/tools/miri/tests/fail/dangling_pointers/null_pointer_write_zst.rs
+++ /dev/null
@@ -1,8 +0,0 @@
-#[allow(deref_nullptr)]
-fn main() {
-    // Not using the () type here, as writes of that type do not even have MIR generated.
-    // Also not assigning directly as that's array initialization, not assignment.
-    let zst_val = [1u8; 0];
-    unsafe { std::ptr::null_mut::<[u8; 0]>().write(zst_val) };
-    //~^ERROR: memory access failed: null pointer is a dangling pointer
-}
diff --git a/src/tools/miri/tests/fail/dangling_pointers/null_pointer_write_zst.stderr b/src/tools/miri/tests/fail/dangling_pointers/null_pointer_write_zst.stderr
deleted file mode 100644
index 1d4704e..0000000
--- a/src/tools/miri/tests/fail/dangling_pointers/null_pointer_write_zst.stderr
+++ /dev/null
@@ -1,15 +0,0 @@
-error: Undefined Behavior: memory access failed: null pointer is a dangling pointer (it has no provenance)
-  --> $DIR/null_pointer_write_zst.rs:LL:CC
-   |
-LL |     unsafe { std::ptr::null_mut::<[u8; 0]>().write(zst_val) };
-   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: null pointer is a dangling pointer (it has no provenance)
-   |
-   = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
-   = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
-   = note: BACKTRACE:
-   = note: inside `main` at $DIR/null_pointer_write_zst.rs:LL:CC
-
-note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
-
-error: aborting due to 1 previous error
-
diff --git a/src/tools/miri/tests/fail/intrinsics/copy_null.rs b/src/tools/miri/tests/fail/intrinsics/copy_null.rs
deleted file mode 100644
index 237e517..0000000
--- a/src/tools/miri/tests/fail/intrinsics/copy_null.rs
+++ /dev/null
@@ -1,15 +0,0 @@
-#![feature(intrinsics)]
-
-// Directly call intrinsic to avoid debug assertions in libstd
-extern "rust-intrinsic" {
-    fn copy_nonoverlapping<T>(src: *const T, dst: *mut T, count: usize);
-}
-
-fn main() {
-    let mut data = [0u16; 4];
-    let ptr = &mut data[0] as *mut u16;
-    // Even copying 0 elements from NULL should error.
-    unsafe {
-        copy_nonoverlapping(std::ptr::null(), ptr, 0); //~ ERROR: memory access failed: null pointer is a dangling pointer
-    }
-}
diff --git a/src/tools/miri/tests/fail/intrinsics/copy_null.stderr b/src/tools/miri/tests/fail/intrinsics/copy_null.stderr
deleted file mode 100644
index d73c034..0000000
--- a/src/tools/miri/tests/fail/intrinsics/copy_null.stderr
+++ /dev/null
@@ -1,15 +0,0 @@
-error: Undefined Behavior: memory access failed: null pointer is a dangling pointer (it has no provenance)
-  --> $DIR/copy_null.rs:LL:CC
-   |
-LL |         copy_nonoverlapping(std::ptr::null(), ptr, 0);
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: null pointer is a dangling pointer (it has no provenance)
-   |
-   = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
-   = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
-   = note: BACKTRACE:
-   = note: inside `main` at $DIR/copy_null.rs:LL:CC
-
-note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
-
-error: aborting due to 1 previous error
-
diff --git a/src/tools/miri/tests/fail/intrinsics/ptr_offset_0_plus_0.rs b/src/tools/miri/tests/fail/intrinsics/ptr_offset_0_plus_0.rs
deleted file mode 100644
index e2329c1..0000000
--- a/src/tools/miri/tests/fail/intrinsics/ptr_offset_0_plus_0.rs
+++ /dev/null
@@ -1,9 +0,0 @@
-//@compile-flags: -Zmiri-permissive-provenance
-
-#[rustfmt::skip] // fails with "left behind trailing whitespace"
-fn main() {
-    let x = 0 as *mut i32;
-    let _x = x.wrapping_offset(8); // ok, this has no inbounds tag
-    let _x = unsafe { x.offset(0) }; // UB despite offset 0, NULL is never inbounds
-    //~^ERROR: null pointer is a dangling pointer
-}
diff --git a/src/tools/miri/tests/fail/intrinsics/ptr_offset_0_plus_0.stderr b/src/tools/miri/tests/fail/intrinsics/ptr_offset_0_plus_0.stderr
deleted file mode 100644
index a8984c7..0000000
--- a/src/tools/miri/tests/fail/intrinsics/ptr_offset_0_plus_0.stderr
+++ /dev/null
@@ -1,15 +0,0 @@
-error: Undefined Behavior: out-of-bounds pointer arithmetic: null pointer is a dangling pointer (it has no provenance)
-  --> $DIR/ptr_offset_0_plus_0.rs:LL:CC
-   |
-LL |     let _x = unsafe { x.offset(0) }; // UB despite offset 0, NULL is never inbounds
-   |                       ^^^^^^^^^^^ out-of-bounds pointer arithmetic: null pointer is a dangling pointer (it has no provenance)
-   |
-   = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
-   = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
-   = note: BACKTRACE:
-   = note: inside `main` at $DIR/ptr_offset_0_plus_0.rs:LL:CC
-
-note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
-
-error: aborting due to 1 previous error
-
diff --git a/src/tools/miri/tests/fail/intrinsics/ptr_offset_from_oob.rs b/src/tools/miri/tests/fail/intrinsics/ptr_offset_from_oob.rs
deleted file mode 100644
index 0e5acf0..0000000
--- a/src/tools/miri/tests/fail/intrinsics/ptr_offset_from_oob.rs
+++ /dev/null
@@ -1,7 +0,0 @@
-fn main() {
-    let start_ptr = &4 as *const _ as *const u8;
-    let length = 10;
-    let end_ptr = start_ptr.wrapping_add(length);
-    // Even if the offset is 0, a dangling OOB pointer is not allowed.
-    unsafe { end_ptr.offset_from(end_ptr) }; //~ERROR: pointer at offset 10 is out-of-bounds
-}
diff --git a/src/tools/miri/tests/fail/intrinsics/ptr_offset_from_oob.stderr b/src/tools/miri/tests/fail/intrinsics/ptr_offset_from_oob.stderr
deleted file mode 100644
index 32a4461..0000000
--- a/src/tools/miri/tests/fail/intrinsics/ptr_offset_from_oob.stderr
+++ /dev/null
@@ -1,15 +0,0 @@
-error: Undefined Behavior: out-of-bounds `offset_from`: ALLOC has size 4, so pointer at offset 10 is out-of-bounds
-  --> $DIR/ptr_offset_from_oob.rs:LL:CC
-   |
-LL |     unsafe { end_ptr.offset_from(end_ptr) };
-   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds `offset_from`: ALLOC has size 4, so pointer at offset 10 is out-of-bounds
-   |
-   = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
-   = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
-   = note: BACKTRACE:
-   = note: inside `main` at $DIR/ptr_offset_from_oob.rs:LL:CC
-
-note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
-
-error: aborting due to 1 previous error
-
diff --git a/src/tools/miri/tests/fail/intrinsics/ptr_offset_ptr_plus_0.rs b/src/tools/miri/tests/fail/intrinsics/ptr_offset_ptr_plus_0.rs
deleted file mode 100644
index 575e288..0000000
--- a/src/tools/miri/tests/fail/intrinsics/ptr_offset_ptr_plus_0.rs
+++ /dev/null
@@ -1,7 +0,0 @@
-#[rustfmt::skip] // fails with "left behind trailing whitespace"
-fn main() {
-    let x = Box::into_raw(Box::new(0u32));
-    let x = x.wrapping_offset(8); // ok, this has no inbounds tag
-    let _x = unsafe { x.offset(0) }; // UB despite offset 0, the pointer is not inbounds of the only object it can point to
-    //~^ERROR: pointer at offset 32 is out-of-bounds
-}
diff --git a/src/tools/miri/tests/fail/intrinsics/ptr_offset_ptr_plus_0.stderr b/src/tools/miri/tests/fail/intrinsics/ptr_offset_ptr_plus_0.stderr
deleted file mode 100644
index 304d362..0000000
--- a/src/tools/miri/tests/fail/intrinsics/ptr_offset_ptr_plus_0.stderr
+++ /dev/null
@@ -1,20 +0,0 @@
-error: Undefined Behavior: out-of-bounds pointer arithmetic: ALLOC has size 4, so pointer at offset 32 is out-of-bounds
-  --> $DIR/ptr_offset_ptr_plus_0.rs:LL:CC
-   |
-LL |     let _x = unsafe { x.offset(0) }; // UB despite offset 0, the pointer is not inbounds of the only object it can point to
-   |                       ^^^^^^^^^^^ out-of-bounds pointer arithmetic: ALLOC has size 4, so pointer at offset 32 is out-of-bounds
-   |
-   = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
-   = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
-help: ALLOC was allocated here:
-  --> $DIR/ptr_offset_ptr_plus_0.rs:LL:CC
-   |
-LL |     let x = Box::into_raw(Box::new(0u32));
-   |                           ^^^^^^^^^^^^^^
-   = note: BACKTRACE (of the first span):
-   = note: inside `main` at $DIR/ptr_offset_ptr_plus_0.rs:LL:CC
-
-note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
-
-error: aborting due to 1 previous error
-
diff --git a/src/tools/miri/tests/fail/intrinsics/write_bytes_null.rs b/src/tools/miri/tests/fail/intrinsics/write_bytes_null.rs
deleted file mode 100644
index 2f46c82..0000000
--- a/src/tools/miri/tests/fail/intrinsics/write_bytes_null.rs
+++ /dev/null
@@ -1,10 +0,0 @@
-#![feature(intrinsics)]
-
-// Directly call intrinsic to avoid debug assertions in libstd
-extern "rust-intrinsic" {
-    fn write_bytes<T>(dst: *mut T, val: u8, count: usize);
-}
-
-fn main() {
-    unsafe { write_bytes::<u8>(std::ptr::null_mut(), 0, 0) }; //~ ERROR: memory access failed: null pointer is a dangling pointer
-}
diff --git a/src/tools/miri/tests/fail/intrinsics/write_bytes_null.stderr b/src/tools/miri/tests/fail/intrinsics/write_bytes_null.stderr
deleted file mode 100644
index def1809..0000000
--- a/src/tools/miri/tests/fail/intrinsics/write_bytes_null.stderr
+++ /dev/null
@@ -1,15 +0,0 @@
-error: Undefined Behavior: memory access failed: null pointer is a dangling pointer (it has no provenance)
-  --> $DIR/write_bytes_null.rs:LL:CC
-   |
-LL |     unsafe { write_bytes::<u8>(std::ptr::null_mut(), 0, 0) };
-   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: null pointer is a dangling pointer (it has no provenance)
-   |
-   = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
-   = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
-   = note: BACKTRACE:
-   = note: inside `main` at $DIR/write_bytes_null.rs:LL:CC
-
-note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
-
-error: aborting due to 1 previous error
-
diff --git a/src/tools/miri/tests/fail/zst2.rs b/src/tools/miri/tests/fail/zst2.rs
deleted file mode 100644
index 04218c2..0000000
--- a/src/tools/miri/tests/fail/zst2.rs
+++ /dev/null
@@ -1,12 +0,0 @@
-fn main() {
-    // Not using the () type here, as writes of that type do not even have MIR generated.
-    // Also not assigning directly as that's array initialization, not assignment.
-    let zst_val = [1u8; 0];
-
-    // make sure ZST accesses are checked against being "truly" dangling pointers
-    // (into deallocated allocations).
-    let mut x_box = Box::new(1u8);
-    let x = &mut *x_box as *mut _ as *mut [u8; 0];
-    drop(x_box);
-    unsafe { *x = zst_val }; //~ ERROR: has been freed
-}
diff --git a/src/tools/miri/tests/fail/zst2.stderr b/src/tools/miri/tests/fail/zst2.stderr
deleted file mode 100644
index b3f65e7..0000000
--- a/src/tools/miri/tests/fail/zst2.stderr
+++ /dev/null
@@ -1,25 +0,0 @@
-error: Undefined Behavior: memory access failed: ALLOC has been freed, so this pointer is dangling
-  --> $DIR/zst2.rs:LL:CC
-   |
-LL |     unsafe { *x = zst_val };
-   |              ^^^^^^^^^^^^ memory access failed: ALLOC has been freed, so this pointer is dangling
-   |
-   = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
-   = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
-help: ALLOC was allocated here:
-  --> $DIR/zst2.rs:LL:CC
-   |
-LL |     let mut x_box = Box::new(1u8);
-   |                     ^^^^^^^^^^^^^
-help: ALLOC was deallocated here:
-  --> $DIR/zst2.rs:LL:CC
-   |
-LL |     drop(x_box);
-   |     ^^^^^^^^^^^
-   = note: BACKTRACE (of the first span):
-   = note: inside `main` at $DIR/zst2.rs:LL:CC
-
-note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
-
-error: aborting due to 1 previous error
-
diff --git a/src/tools/miri/tests/fail/zst3.rs b/src/tools/miri/tests/fail/zst3.rs
deleted file mode 100644
index 454bef2..0000000
--- a/src/tools/miri/tests/fail/zst3.rs
+++ /dev/null
@@ -1,15 +0,0 @@
-fn main() {
-    // Not using the () type here, as writes of that type do not even have MIR generated.
-    // Also not assigning directly as that's array initialization, not assignment.
-    let zst_val = [1u8; 0];
-
-    // make sure ZST accesses are checked against being "truly" dangling pointers
-    // (that are out-of-bounds).
-    let mut x_box = Box::new(1u8);
-    let x = (&mut *x_box as *mut u8).wrapping_offset(1);
-    // This one is just "at the edge", but still okay
-    unsafe { *(x as *mut [u8; 0]) = zst_val };
-    // One byte further is OOB.
-    let x = x.wrapping_offset(1);
-    unsafe { *(x as *mut [u8; 0]) = zst_val }; //~ ERROR: out-of-bounds
-}
diff --git a/src/tools/miri/tests/fail/zst3.stderr b/src/tools/miri/tests/fail/zst3.stderr
deleted file mode 100644
index b9495fb..0000000
--- a/src/tools/miri/tests/fail/zst3.stderr
+++ /dev/null
@@ -1,20 +0,0 @@
-error: Undefined Behavior: memory access failed: ALLOC has size 1, so pointer at offset 2 is out-of-bounds
-  --> $DIR/zst3.rs:LL:CC
-   |
-LL |     unsafe { *(x as *mut [u8; 0]) = zst_val };
-   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: ALLOC has size 1, so pointer at offset 2 is out-of-bounds
-   |
-   = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
-   = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
-help: ALLOC was allocated here:
-  --> $DIR/zst3.rs:LL:CC
-   |
-LL |     let mut x_box = Box::new(1u8);
-   |                     ^^^^^^^^^^^^^
-   = note: BACKTRACE (of the first span):
-   = note: inside `main` at $DIR/zst3.rs:LL:CC
-
-note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
-
-error: aborting due to 1 previous error
-
diff --git a/src/tools/miri/tests/fail/zst1.rs b/src/tools/miri/tests/fail/zst_local_oob.rs
similarity index 100%
rename from src/tools/miri/tests/fail/zst1.rs
rename to src/tools/miri/tests/fail/zst_local_oob.rs
diff --git a/src/tools/miri/tests/fail/zst1.stderr b/src/tools/miri/tests/fail/zst_local_oob.stderr
similarity index 88%
rename from src/tools/miri/tests/fail/zst1.stderr
rename to src/tools/miri/tests/fail/zst_local_oob.stderr
index cda837d..ba1ccaa 100644
--- a/src/tools/miri/tests/fail/zst1.stderr
+++ b/src/tools/miri/tests/fail/zst_local_oob.stderr
@@ -1,5 +1,5 @@
 error: Undefined Behavior: memory access failed: ALLOC has size 0, so pointer to 1 byte starting at offset 0 is out-of-bounds
-  --> $DIR/zst1.rs:LL:CC
+  --> $DIR/zst_local_oob.rs:LL:CC
    |
 LL |     let _val = unsafe { *x };
    |                         ^^ memory access failed: ALLOC has size 0, so pointer to 1 byte starting at offset 0 is out-of-bounds
@@ -7,7 +7,7 @@
    = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
    = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
    = note: BACKTRACE:
-   = note: inside `main` at $DIR/zst1.rs:LL:CC
+   = note: inside `main` at $DIR/zst_local_oob.rs:LL:CC
 
 note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
 
diff --git a/src/tools/miri/tests/pass/align_offset_symbolic.rs b/src/tools/miri/tests/pass/align_offset_symbolic.rs
index dec3d77..9647277 100644
--- a/src/tools/miri/tests/pass/align_offset_symbolic.rs
+++ b/src/tools/miri/tests/pass/align_offset_symbolic.rs
@@ -118,10 +118,9 @@
     let parts: (*const (), *const u8) = unsafe { mem::transmute(ptr) };
     let vtable = parts.1;
     let offset = vtable.align_offset(mem::align_of::<TWOPTR>());
-    let _vtable_aligned = vtable.wrapping_add(offset) as *const [TWOPTR; 0];
-    // FIXME: we can't actually do the access since vtable pointers act like zero-sized allocations.
-    // Enable the next line once https://github.com/rust-lang/rust/issues/117945 is implemented.
-    //let _place = unsafe { &*vtable_aligned };
+    let vtable_aligned = vtable.wrapping_add(offset) as *const [TWOPTR; 0];
+    // Zero-sized deref, so no in-bounds requirement.
+    let _place = unsafe { &*vtable_aligned };
 }
 
 fn main() {
diff --git a/src/tools/miri/tests/pass/zero-sized-accesses-and-offsets.rs b/src/tools/miri/tests/pass/zero-sized-accesses-and-offsets.rs
new file mode 100644
index 0000000..2d142be
--- /dev/null
+++ b/src/tools/miri/tests/pass/zero-sized-accesses-and-offsets.rs
@@ -0,0 +1,59 @@
+//! Tests specific for <https://github.com/rust-lang/rust/issues/117945>: zero-sized operations.
+#![feature(strict_provenance)]
+
+use std::ptr;
+
+fn main() {
+    // Null.
+    test_ptr(ptr::null_mut::<()>());
+    // No provenance.
+    test_ptr(ptr::without_provenance_mut::<()>(1));
+    // Out-of-bounds.
+    let mut b = Box::new(0i32);
+    let ptr = ptr::addr_of_mut!(*b) as *mut ();
+    test_ptr(ptr.wrapping_byte_add(2));
+    // Dangling (use-after-free).
+    drop(b);
+    test_ptr(ptr);
+}
+
+fn test_ptr(ptr: *mut ()) {
+    unsafe {
+        // Reads and writes.
+        let mut val = *ptr;
+        *ptr = val;
+        ptr.read();
+        ptr.write(());
+        // Memory access intrinsics.
+        // - memcpy (1st and 2nd argument)
+        ptr.copy_from_nonoverlapping(&(), 1);
+        ptr.copy_to_nonoverlapping(&mut val, 1);
+        // - memmove (1st and 2nd argument)
+        ptr.copy_from(&(), 1);
+        ptr.copy_to(&mut val, 1);
+        // - memset
+        ptr.write_bytes(0u8, 1);
+        // Offset.
+        let _ = ptr.offset(0);
+        let _ = ptr.offset(1); // this is still 0 bytes
+        // Distance.
+        let ptr = ptr.cast::<i32>();
+        ptr.offset_from(ptr);
+        /*
+        FIXME: this is disabled for now as these cases are not yet allowed.
+        // Distance from other "bad" pointers that have the same address, but different provenance. Some
+        // of this is library UB, but we don't want it to be language UB since that would violate
+        // provenance monotonicity: if we allow computing the distance between two ptrs with no
+        // provenance, we have to allow computing it between two ptrs with arbitrary provenance.
+        // - Distance from "no provenance"
+        ptr.offset_from(ptr::without_provenance_mut(ptr.addr()));
+        // - Distance from out-of-bounds pointer
+        let mut b = Box::new(0i32);
+        let other_ptr = ptr::addr_of_mut!(*b);
+        ptr.offset_from(other_ptr.with_addr(ptr.addr()));
+        // - Distance from use-after-free pointer
+        drop(b);
+        ptr.offset_from(other_ptr.with_addr(ptr.addr()));
+        */
+    }
+}
diff --git a/tests/crashes/118185.rs b/tests/crashes/118185.rs
deleted file mode 100644
index c3a29c3..0000000
--- a/tests/crashes/118185.rs
+++ /dev/null
@@ -1,26 +0,0 @@
-//@ known-bug: #118185
-
-fn main() {
-    let target: Target = create_target();
-    target.get(0); // correct arguments work
-    target.get(10.0); // CRASH HERE
-}
-
-// must be generic
-fn create_target<T>() -> T {
-    unimplemented!()
-}
-
-// unimplemented trait, but contains function with the same name
-pub trait RandomTrait {
-    fn get(&mut self); // but less arguments
-}
-
-struct Target;
-
-impl Target {
-    // correct function with arguments
-    pub fn get(&self, data: i32) {
-        unimplemented!()
-    }
-}
diff --git a/tests/rustdoc/issue-95633.rs b/tests/rustdoc-ui/pub-use-primitive-document-private-items-95633.rs
similarity index 71%
rename from tests/rustdoc/issue-95633.rs
rename to tests/rustdoc-ui/pub-use-primitive-document-private-items-95633.rs
index 5695ef5..d53a67c 100644
--- a/tests/rustdoc/issue-95633.rs
+++ b/tests/rustdoc-ui/pub-use-primitive-document-private-items-95633.rs
@@ -1,6 +1,8 @@
+//@ check-pass
 //@ compile-flags: --document-private-items
 
 // This ensures that no ICE is triggered when rustdoc is run on this code.
+// https://github.com/rust-lang/rust/issues/95633
 
 mod stdlib {
     pub (crate) use std::i8;
diff --git a/tests/rustdoc/issue-82465-asref-for-and-of-local.rs b/tests/rustdoc/asref-for-and-of-local-82465.rs
similarity index 63%
rename from tests/rustdoc/issue-82465-asref-for-and-of-local.rs
rename to tests/rustdoc/asref-for-and-of-local-82465.rs
index adf4d11..e620468 100644
--- a/tests/rustdoc/issue-82465-asref-for-and-of-local.rs
+++ b/tests/rustdoc/asref-for-and-of-local-82465.rs
@@ -1,7 +1,10 @@
+// https://github.com/rust-lang/rust/issues/82465
+#![crate_name = "foo"]
+
 use std::convert::AsRef;
 pub struct Local;
 
-// @has issue_82465_asref_for_and_of_local/struct.Local.html '//h3[@class="code-header"]' 'impl AsRef<str> for Local'
+// @has foo/struct.Local.html '//h3[@class="code-header"]' 'impl AsRef<str> for Local'
 impl AsRef<str> for Local {
     fn as_ref(&self) -> &str {
         todo!()
diff --git a/tests/rustdoc/issue-88600.rs b/tests/rustdoc/enum-variant-doc-hidden-field-88600.rs
similarity index 93%
rename from tests/rustdoc/issue-88600.rs
rename to tests/rustdoc/enum-variant-doc-hidden-field-88600.rs
index f89af47..31d96e9 100644
--- a/tests/rustdoc/issue-88600.rs
+++ b/tests/rustdoc/enum-variant-doc-hidden-field-88600.rs
@@ -1,4 +1,6 @@
 // This test ensure that #[doc(hidden)] is applied correctly in enum variant fields.
+// https://github.com/rust-lang/rust/issues/88600
+#![crate_name = "foo"]
 
 // Denotes a field which should be hidden.
 pub struct H;
@@ -6,7 +8,7 @@
 // Denotes a field which should not be hidden (shown).
 pub struct S;
 
-// @has issue_88600/enum.FooEnum.html
+// @has foo/enum.FooEnum.html
 pub enum FooEnum {
     // @has - '//*[@id="variant.HiddenTupleItem"]//h3' 'HiddenTupleItem(/* private fields */)'
     // @count - '//*[@id="variant.HiddenTupleItem.field.0"]' 0
diff --git a/tests/rustdoc/issue-89309-heading-levels.rs b/tests/rustdoc/heading-levels-89309.rs
similarity index 92%
rename from tests/rustdoc/issue-89309-heading-levels.rs
rename to tests/rustdoc/heading-levels-89309.rs
index bb706c2..caa9942 100644
--- a/tests/rustdoc/issue-89309-heading-levels.rs
+++ b/tests/rustdoc/heading-levels-89309.rs
@@ -1,3 +1,4 @@
+// https://github.com/rust-lang/rust/issues/89309
 #![crate_name = "foo"]
 
 // @has foo/trait.Read.html
diff --git a/tests/rustdoc/auxiliary/issue-85454.rs b/tests/rustdoc/inline_cross/auxiliary/issue-85454.rs
similarity index 100%
rename from tests/rustdoc/auxiliary/issue-85454.rs
rename to tests/rustdoc/inline_cross/auxiliary/issue-85454.rs
diff --git a/tests/rustdoc/auxiliary/issue-98697-reexport-with-anonymous-lifetime.rs b/tests/rustdoc/inline_cross/auxiliary/reexport-with-anonymous-lifetime-98697.rs
similarity index 100%
rename from tests/rustdoc/auxiliary/issue-98697-reexport-with-anonymous-lifetime.rs
rename to tests/rustdoc/inline_cross/auxiliary/reexport-with-anonymous-lifetime-98697.rs
diff --git a/tests/rustdoc/issue-85454.rs b/tests/rustdoc/inline_cross/qpath-self-85454.rs
similarity index 94%
rename from tests/rustdoc/issue-85454.rs
rename to tests/rustdoc/inline_cross/qpath-self-85454.rs
index 790db0c..de806db 100644
--- a/tests/rustdoc/issue-85454.rs
+++ b/tests/rustdoc/inline_cross/qpath-self-85454.rs
@@ -1,6 +1,7 @@
 //@ aux-build:issue-85454.rs
 //@ build-aux-docs
 #![crate_name = "foo"]
+// https://github.com/rust-lang/rust/issues/85454
 
 extern crate issue_85454;
 
diff --git a/tests/rustdoc/inline_cross/reexport-with-anonymous-lifetime-98697.rs b/tests/rustdoc/inline_cross/reexport-with-anonymous-lifetime-98697.rs
new file mode 100644
index 0000000..fe6e5a3
--- /dev/null
+++ b/tests/rustdoc/inline_cross/reexport-with-anonymous-lifetime-98697.rs
@@ -0,0 +1,18 @@
+//@ aux-build:reexport-with-anonymous-lifetime-98697.rs
+//@ ignore-cross-compile
+#![crate_name = "foo"]
+
+// When reexporting a function with a HRTB with anonymous lifetimes,
+// make sure the anonymous lifetimes are not rendered.
+//
+// https://github.com/rust-lang/rust/issues/98697
+
+extern crate reexport_with_anonymous_lifetime_98697;
+
+// @has foo/fn.repro.html '//pre[@class="rust item-decl"]/code' 'fn repro<F>()where F: Fn(&str)'
+// @!has foo/fn.repro.html '//pre[@class="rust item-decl"]/code' 'for<'
+pub use reexport_with_anonymous_lifetime_98697::repro;
+
+// @has foo/struct.Extra.html '//div[@id="trait-implementations-list"]//h3[@class="code-header"]' 'impl MyTrait<&Extra> for Extra'
+// @!has foo/struct.Extra.html '//div[@id="trait-implementations-list"]//h3[@class="code-header"]' 'impl<'
+pub use reexport_with_anonymous_lifetime_98697::Extra;
diff --git a/tests/rustdoc/issue-94183-blanket-impl-reexported-trait.rs b/tests/rustdoc/inline_local/blanket-impl-reexported-trait-94183.rs
similarity index 94%
rename from tests/rustdoc/issue-94183-blanket-impl-reexported-trait.rs
rename to tests/rustdoc/inline_local/blanket-impl-reexported-trait-94183.rs
index 95ddd4c..343e030 100644
--- a/tests/rustdoc/issue-94183-blanket-impl-reexported-trait.rs
+++ b/tests/rustdoc/inline_local/blanket-impl-reexported-trait-94183.rs
@@ -2,6 +2,7 @@
 // This test ensures that a publicly re-exported private trait will
 // appear in the blanket impl list.
 
+// https://github.com/rust-lang/rust/issues/94183
 #![crate_name = "foo"]
 
 // @has 'foo/struct.S.html'
diff --git a/tests/rustdoc/issue-81141-private-reexport-in-public-api-2.rs b/tests/rustdoc/inline_local/private-reexport-in-public-api-81141-2.rs
similarity index 84%
rename from tests/rustdoc/issue-81141-private-reexport-in-public-api-2.rs
rename to tests/rustdoc/inline_local/private-reexport-in-public-api-81141-2.rs
index fba310c..c066f54 100644
--- a/tests/rustdoc/issue-81141-private-reexport-in-public-api-2.rs
+++ b/tests/rustdoc/inline_local/private-reexport-in-public-api-81141-2.rs
@@ -1,5 +1,6 @@
 //@ edition:2015
 
+// https://github.com/rust-lang/rust/issues/81141
 #![crate_name = "foo"]
 
 use external::Public as Private;
diff --git a/tests/rustdoc/issue-81141-private-reexport-in-public-api.rs b/tests/rustdoc/inline_local/private-reexport-in-public-api-81141.rs
similarity index 97%
rename from tests/rustdoc/issue-81141-private-reexport-in-public-api.rs
rename to tests/rustdoc/inline_local/private-reexport-in-public-api-81141.rs
index bd54d02..d695ed7 100644
--- a/tests/rustdoc/issue-81141-private-reexport-in-public-api.rs
+++ b/tests/rustdoc/inline_local/private-reexport-in-public-api-81141.rs
@@ -1,6 +1,7 @@
 // This test ensures that if a private re-export is present in a public API, it'll be
 // replaced by the first public item in the re-export chain or by the private item.
 
+// https://github.com/rust-lang/rust/issues/81141
 #![crate_name = "foo"]
 
 use crate::bar::Bar as Alias;
diff --git a/tests/rustdoc/issue-81141-private-reexport-in-public-api-generics.rs b/tests/rustdoc/inline_local/private-reexport-in-public-api-generics-81141.rs
similarity index 85%
rename from tests/rustdoc/issue-81141-private-reexport-in-public-api-generics.rs
rename to tests/rustdoc/inline_local/private-reexport-in-public-api-generics-81141.rs
index 7e28950..1c86c76 100644
--- a/tests/rustdoc/issue-81141-private-reexport-in-public-api-generics.rs
+++ b/tests/rustdoc/inline_local/private-reexport-in-public-api-generics-81141.rs
@@ -1,3 +1,4 @@
+// https://github.com/rust-lang/rust/issues/81141
 #![crate_name = "foo"]
 
 use crate::bar::Foo as Alias;
diff --git a/tests/rustdoc/issue-81141-private-reexport-in-public-api-hidden.rs b/tests/rustdoc/inline_local/private-reexport-in-public-api-hidden-81141.rs
similarity index 85%
rename from tests/rustdoc/issue-81141-private-reexport-in-public-api-hidden.rs
rename to tests/rustdoc/inline_local/private-reexport-in-public-api-hidden-81141.rs
index 388f69b..7d6fadf 100644
--- a/tests/rustdoc/issue-81141-private-reexport-in-public-api-hidden.rs
+++ b/tests/rustdoc/inline_local/private-reexport-in-public-api-hidden-81141.rs
@@ -1,5 +1,6 @@
 //@ compile-flags: -Z unstable-options --document-hidden-items
 
+// https://github.com/rust-lang/rust/issues/81141
 #![crate_name = "foo"]
 
 #[doc(hidden)]
diff --git a/tests/rustdoc/issue-81141-private-reexport-in-public-api-private.rs b/tests/rustdoc/inline_local/private-reexport-in-public-api-private-81141.rs
similarity index 93%
rename from tests/rustdoc/issue-81141-private-reexport-in-public-api-private.rs
rename to tests/rustdoc/inline_local/private-reexport-in-public-api-private-81141.rs
index 2633f98..6bf5078 100644
--- a/tests/rustdoc/issue-81141-private-reexport-in-public-api-private.rs
+++ b/tests/rustdoc/inline_local/private-reexport-in-public-api-private-81141.rs
@@ -1,5 +1,6 @@
 //@ compile-flags: --document-private-items
 
+// https://github.com/rust-lang/rust/issues/81141
 #![crate_name = "foo"]
 
 use crate::bar::Bar as Alias;
diff --git a/tests/rustdoc/inline_local/reexported-macro-and-macro-export-sidebar-89852.rs b/tests/rustdoc/inline_local/reexported-macro-and-macro-export-sidebar-89852.rs
new file mode 100644
index 0000000..cffe128
--- /dev/null
+++ b/tests/rustdoc/inline_local/reexported-macro-and-macro-export-sidebar-89852.rs
@@ -0,0 +1,16 @@
+//@ edition:2018
+
+// https://github.com/rust-lang/rust/issues/89852
+#![crate_name = "foo"]
+#![no_core]
+#![feature(no_core)]
+
+// @matchesraw 'foo/sidebar-items.js' '"repro"'
+// @!matchesraw 'foo/sidebar-items.js' '"repro".*"repro"'
+
+#[macro_export]
+macro_rules! repro {
+    () => {};
+}
+
+pub use crate::repro as repro2;
diff --git a/tests/rustdoc/issue-83375-multiple-mods-w-same-name-doc-inline.rs b/tests/rustdoc/issue-83375-multiple-mods-w-same-name-doc-inline.rs
deleted file mode 100644
index d0960df..0000000
--- a/tests/rustdoc/issue-83375-multiple-mods-w-same-name-doc-inline.rs
+++ /dev/null
@@ -1,16 +0,0 @@
-#![crate_name = "foo"]
-
-pub mod sub {
-    pub struct Item;
-
-    pub mod prelude {
-        pub use super::Item;
-    }
-}
-
-// @count foo/index.html '//a[@class="mod"][@title="mod foo::prelude"]' 1
-// @count foo/prelude/index.html '//div[@class="item-row"]' 0
-pub mod prelude {}
-
-#[doc(inline)]
-pub use sub::*;
diff --git a/tests/rustdoc/issue-86620.rs b/tests/rustdoc/issue-86620.rs
deleted file mode 100644
index a7ac0f1..0000000
--- a/tests/rustdoc/issue-86620.rs
+++ /dev/null
@@ -1,9 +0,0 @@
-//@ aux-build:issue-86620-1.rs
-
-extern crate issue_86620_1;
-
-use issue_86620_1::*;
-
-// @!has issue_86620/struct.S.html '//*[@id="method.vzip"]//a[@class="fnname"]/@href' #tymethod.vzip
-// @has issue_86620/struct.S.html '//*[@id="method.vzip"]//a[@class="anchor"]/@href' #method.vzip
-pub struct S;
diff --git a/tests/rustdoc/issue-89852.rs b/tests/rustdoc/issue-89852.rs
deleted file mode 100644
index e9b3d80..0000000
--- a/tests/rustdoc/issue-89852.rs
+++ /dev/null
@@ -1,14 +0,0 @@
-//@ edition:2018
-
-#![no_core]
-#![feature(no_core)]
-
-// @matchesraw 'issue_89852/sidebar-items.js' '"repro"'
-// @!matchesraw 'issue_89852/sidebar-items.js' '"repro".*"repro"'
-
-#[macro_export]
-macro_rules! repro {
-    () => {};
-}
-
-pub use crate::repro as repro2;
diff --git a/tests/rustdoc/issue-95873.rs b/tests/rustdoc/issue-95873.rs
deleted file mode 100644
index 83f1f2f..0000000
--- a/tests/rustdoc/issue-95873.rs
+++ /dev/null
@@ -1,2 +0,0 @@
-// @has issue_95873/index.html "//*[@class='item-name']" "pub use ::std as x;"
-pub use ::std as x;
diff --git a/tests/rustdoc/issue-98697.rs b/tests/rustdoc/issue-98697.rs
deleted file mode 100644
index df9f291..0000000
--- a/tests/rustdoc/issue-98697.rs
+++ /dev/null
@@ -1,17 +0,0 @@
-//@ aux-build:issue-98697-reexport-with-anonymous-lifetime.rs
-//@ ignore-cross-compile
-
-// When reexporting a function with a HRTB with anonymous lifetimes,
-// make sure the anonymous lifetimes are not rendered.
-//
-// https://github.com/rust-lang/rust/issues/98697
-
-extern crate issue_98697_reexport_with_anonymous_lifetime;
-
-// @has issue_98697/fn.repro.html '//pre[@class="rust item-decl"]/code' 'fn repro<F>()where F: Fn(&str)'
-// @!has issue_98697/fn.repro.html '//pre[@class="rust item-decl"]/code' 'for<'
-pub use issue_98697_reexport_with_anonymous_lifetime::repro;
-
-// @has issue_98697/struct.Extra.html '//div[@id="trait-implementations-list"]//h3[@class="code-header"]' 'impl MyTrait<&Extra> for Extra'
-// @!has issue_98697/struct.Extra.html '//div[@id="trait-implementations-list"]//h3[@class="code-header"]' 'impl<'
-pub use issue_98697_reexport_with_anonymous_lifetime::Extra;
diff --git a/tests/rustdoc/method-anchor-in-blanket-impl-86620.rs b/tests/rustdoc/method-anchor-in-blanket-impl-86620.rs
new file mode 100644
index 0000000..537dadd
--- /dev/null
+++ b/tests/rustdoc/method-anchor-in-blanket-impl-86620.rs
@@ -0,0 +1,11 @@
+//@ aux-build:issue-86620-1.rs
+#![crate_name = "foo"]
+// https://github.com/rust-lang/rust/issues/86620
+
+extern crate issue_86620_1;
+
+use issue_86620_1::*;
+
+// @!has foo/struct.S.html '//*[@id="method.vzip"]//a[@class="fnname"]/@href' #tymethod.vzip
+// @has foo/struct.S.html '//*[@id="method.vzip"]//a[@class="anchor"]/@href' #method.vzip
+pub struct S;
diff --git a/tests/rustdoc/issue-99221-multiple-macro-rules-w-same-name-submodule.rs b/tests/rustdoc/multiple-macro-rules-w-same-name-submodule-99221.rs
similarity index 85%
rename from tests/rustdoc/issue-99221-multiple-macro-rules-w-same-name-submodule.rs
rename to tests/rustdoc/multiple-macro-rules-w-same-name-submodule-99221.rs
index d3ccd1c..ed1e42c 100644
--- a/tests/rustdoc/issue-99221-multiple-macro-rules-w-same-name-submodule.rs
+++ b/tests/rustdoc/multiple-macro-rules-w-same-name-submodule-99221.rs
@@ -2,6 +2,7 @@
 //@ build-aux-docs
 //@ ignore-cross-compile
 
+// https://github.com/rust-lang/rust/issues/99221
 #![crate_name = "foo"]
 
 #[macro_use]
diff --git a/tests/rustdoc/issue-83375-multiple-mods-w-same-name-doc-inline-last-item.rs b/tests/rustdoc/multiple-mods-w-same-name-doc-inline-83375.rs
similarity index 85%
copy from tests/rustdoc/issue-83375-multiple-mods-w-same-name-doc-inline-last-item.rs
copy to tests/rustdoc/multiple-mods-w-same-name-doc-inline-83375.rs
index 9bce258..9b3dfd4 100644
--- a/tests/rustdoc/issue-83375-multiple-mods-w-same-name-doc-inline-last-item.rs
+++ b/tests/rustdoc/multiple-mods-w-same-name-doc-inline-83375.rs
@@ -1,3 +1,4 @@
+// https://github.com/rust-lang/rust/issues/83375
 #![crate_name = "foo"]
 
 pub mod sub {
@@ -8,9 +9,9 @@
     }
 }
 
-#[doc(inline)]
-pub use sub::*;
-
 // @count foo/index.html '//a[@class="mod"][@title="mod foo::prelude"]' 1
 // @count foo/prelude/index.html '//div[@class="item-row"]' 0
 pub mod prelude {}
+
+#[doc(inline)]
+pub use sub::*;
diff --git a/tests/rustdoc/issue-83375-multiple-mods-w-same-name-doc-inline-last-item.rs b/tests/rustdoc/multiple-mods-w-same-name-doc-inline-last-item-83375.rs
similarity index 85%
rename from tests/rustdoc/issue-83375-multiple-mods-w-same-name-doc-inline-last-item.rs
rename to tests/rustdoc/multiple-mods-w-same-name-doc-inline-last-item-83375.rs
index 9bce258..7bad825 100644
--- a/tests/rustdoc/issue-83375-multiple-mods-w-same-name-doc-inline-last-item.rs
+++ b/tests/rustdoc/multiple-mods-w-same-name-doc-inline-last-item-83375.rs
@@ -1,3 +1,4 @@
+// https://github.com/rust-lang/rust/issues/83375
 #![crate_name = "foo"]
 
 pub mod sub {
diff --git a/tests/rustdoc/pub-use-root-path-95873.rs b/tests/rustdoc/pub-use-root-path-95873.rs
new file mode 100644
index 0000000..5a817fb
--- /dev/null
+++ b/tests/rustdoc/pub-use-root-path-95873.rs
@@ -0,0 +1,5 @@
+// https://github.com/rust-lang/rust/issues/95873
+#![crate_name = "foo"]
+
+// @has foo/index.html "//*[@class='item-name']" "pub use ::std as x;"
+pub use ::std as x;
diff --git a/tests/rustdoc/issue-80233-normalize-auto-trait.rs b/tests/rustdoc/synthetic_auto/normalize-auto-trait-80233.rs
similarity index 85%
rename from tests/rustdoc/issue-80233-normalize-auto-trait.rs
rename to tests/rustdoc/synthetic_auto/normalize-auto-trait-80233.rs
index 62fbc24..0649801 100644
--- a/tests/rustdoc/issue-80233-normalize-auto-trait.rs
+++ b/tests/rustdoc/synthetic_auto/normalize-auto-trait-80233.rs
@@ -1,7 +1,9 @@
 // Regression test for issue #80233
 // Tests that we don't ICE when processing auto traits
+// https://github.com/rust-lang/rust/issues/80233
 
 #![crate_type = "lib"]
+#![crate_name = "foo"]
 pub trait Trait1 {}
 
 pub trait Trait2 {
@@ -30,7 +32,7 @@
 
 pub struct Struct1 {}
 
-// @has issue_80233_normalize_auto_trait/struct.Question.html
+// @has foo/struct.Question.html
 // @has - '//h3[@class="code-header"]' 'impl<T> Send for Question<T>'
 pub struct Question<T: Trait1> {
     pub ins: <<Vec<T> as Trait3>::Type3 as Trait2>::Type2,
diff --git a/tests/rustdoc/issue-96381.rs b/tests/rustdoc/underscore-type-in-trait-impl-96381.rs
similarity index 85%
rename from tests/rustdoc/issue-96381.rs
rename to tests/rustdoc/underscore-type-in-trait-impl-96381.rs
index 90875c0..6d6e5e0 100644
--- a/tests/rustdoc/issue-96381.rs
+++ b/tests/rustdoc/underscore-type-in-trait-impl-96381.rs
@@ -1,4 +1,5 @@
 //@ should-fail
+// https://github.com/rust-lang/rust/issues/96381
 
 #![allow(unused)]
 
diff --git a/tests/ui/async-await/async-closures/force-move-due-to-actually-fnonce.rs b/tests/ui/async-await/async-closures/force-move-due-to-actually-fnonce.rs
new file mode 100644
index 0000000..ce49f55
--- /dev/null
+++ b/tests/ui/async-await/async-closures/force-move-due-to-actually-fnonce.rs
@@ -0,0 +1,27 @@
+//@ aux-build:block-on.rs
+//@ edition:2021
+//@ check-pass
+
+#![feature(async_closure)]
+
+extern crate block_on;
+
+fn consume(_: String) {}
+
+fn main() {
+    block_on::block_on(async {
+        let x = 1i32;
+        let s = String::new();
+        // `consume(s)` pulls the closure's kind down to `FnOnce`,
+        // which means that we don't treat the borrow of `x` as a
+        // self-borrow (with `'env` lifetime). This leads to a lifetime
+        // error which is solved by forcing the inner coroutine to
+        // be `move` as well, so that it moves `x`.
+        let c = async move || {
+            println!("{x}");
+            // This makes the closure FnOnce...
+            consume(s);
+        };
+        c().await;
+    });
+}
diff --git a/tests/ui/async-await/async-closures/force-move-due-to-inferred-kind.rs b/tests/ui/async-await/async-closures/force-move-due-to-inferred-kind.rs
new file mode 100644
index 0000000..803c990
--- /dev/null
+++ b/tests/ui/async-await/async-closures/force-move-due-to-inferred-kind.rs
@@ -0,0 +1,24 @@
+//@ aux-build:block-on.rs
+//@ edition:2021
+//@ check-pass
+
+#![feature(async_closure)]
+
+extern crate block_on;
+
+fn force_fnonce<T: async FnOnce()>(t: T) -> T { t }
+
+fn main() {
+    block_on::block_on(async {
+        let x = 1i32;
+        // `force_fnonce` pulls the closure's kind down to `FnOnce`,
+        // which means that we don't treat the borrow of `x` as a
+        // self-borrow (with `'env` lifetime). This leads to a lifetime
+        // error which is solved by forcing the inner coroutine to
+        // be `move` as well, so that it moves `x`.
+        let c = force_fnonce(async move || {
+            println!("{x}");
+        });
+        c().await;
+    });
+}
diff --git a/tests/ui/const-ptr/forbidden_slices.rs b/tests/ui/const-ptr/forbidden_slices.rs
index 85baeed..2550a3a 100644
--- a/tests/ui/const-ptr/forbidden_slices.rs
+++ b/tests/ui/const-ptr/forbidden_slices.rs
@@ -12,7 +12,7 @@
     slice::{from_ptr_range, from_raw_parts},
 };
 
-// Null is never valid for reads
+// Null is never valid for references
 pub static S0: &[u32] = unsafe { from_raw_parts(ptr::null(), 0) };
 //~^ ERROR: it is undefined behavior to use this value
 pub static S1: &[()] = unsafe { from_raw_parts(ptr::null(), 0) };
@@ -46,10 +46,11 @@
 };
 
 pub static R0: &[u32] = unsafe { from_ptr_range(ptr::null()..ptr::null()) };
-pub static R1: &[()] = unsafe { from_ptr_range(ptr::null()..ptr::null()) };
+//~^ ERROR it is undefined behavior to use this value
+pub static R1: &[()] = unsafe { from_ptr_range(ptr::null()..ptr::null()) }; // errors inside libcore
 pub static R2: &[u32] = unsafe {
     let ptr = &D0 as *const u32;
-    from_ptr_range(ptr..ptr.add(2))
+    from_ptr_range(ptr..ptr.add(2)) // errors inside libcore
 };
 pub static R4: &[u8] = unsafe {
     //~^ ERROR: it is undefined behavior to use this value
diff --git a/tests/ui/const-ptr/forbidden_slices.stderr b/tests/ui/const-ptr/forbidden_slices.stderr
index 250366d..eb41a25 100644
--- a/tests/ui/const-ptr/forbidden_slices.stderr
+++ b/tests/ui/const-ptr/forbidden_slices.stderr
@@ -88,20 +88,16 @@
                HEX_DUMP
            }
 
-error[E0080]: could not evaluate static initializer
-  --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
-   |
-   = note: out-of-bounds `offset_from`: null pointer is a dangling pointer (it has no provenance)
-   |
-note: inside `std::ptr::const_ptr::<impl *const u32>::sub_ptr`
-  --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
-note: inside `from_ptr_range::<'_, u32>`
-  --> $SRC_DIR/core/src/slice/raw.rs:LL:COL
-note: inside `R0`
-  --> $DIR/forbidden_slices.rs:48:34
+error[E0080]: it is undefined behavior to use this value
+  --> $DIR/forbidden_slices.rs:48:1
    |
 LL | pub static R0: &[u32] = unsafe { from_ptr_range(ptr::null()..ptr::null()) };
-   |                                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   | ^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered a null reference
+   |
+   = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
+   = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) {
+               HEX_DUMP
+           }
 
 error[E0080]: could not evaluate static initializer
   --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
@@ -113,9 +109,9 @@
 note: inside `from_ptr_range::<'_, ()>`
   --> $SRC_DIR/core/src/slice/raw.rs:LL:COL
 note: inside `R1`
-  --> $DIR/forbidden_slices.rs:49:33
+  --> $DIR/forbidden_slices.rs:50:33
    |
-LL | pub static R1: &[()] = unsafe { from_ptr_range(ptr::null()..ptr::null()) };
+LL | pub static R1: &[()] = unsafe { from_ptr_range(ptr::null()..ptr::null()) }; // errors inside libcore
    |                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    = note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info)
 
@@ -127,13 +123,13 @@
 note: inside `std::ptr::const_ptr::<impl *const u32>::add`
   --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
 note: inside `R2`
-  --> $DIR/forbidden_slices.rs:52:25
+  --> $DIR/forbidden_slices.rs:53:25
    |
-LL |     from_ptr_range(ptr..ptr.add(2))
+LL |     from_ptr_range(ptr..ptr.add(2)) // errors inside libcore
    |                         ^^^^^^^^^^
 
 error[E0080]: it is undefined behavior to use this value
-  --> $DIR/forbidden_slices.rs:54:1
+  --> $DIR/forbidden_slices.rs:55:1
    |
 LL | pub static R4: &[u8] = unsafe {
    | ^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .<deref>[0]: encountered uninitialized memory, but expected an integer
@@ -144,7 +140,7 @@
            }
 
 error[E0080]: it is undefined behavior to use this value
-  --> $DIR/forbidden_slices.rs:59:1
+  --> $DIR/forbidden_slices.rs:60:1
    |
 LL | pub static R5: &[u8] = unsafe {
    | ^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .<deref>[0]: encountered a pointer, but expected an integer
@@ -157,7 +153,7 @@
    = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
 
 error[E0080]: it is undefined behavior to use this value
-  --> $DIR/forbidden_slices.rs:64:1
+  --> $DIR/forbidden_slices.rs:65:1
    |
 LL | pub static R6: &[bool] = unsafe {
    | ^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .<deref>[0]: encountered 0x11, but expected a boolean
@@ -168,7 +164,7 @@
            }
 
 error[E0080]: it is undefined behavior to use this value
-  --> $DIR/forbidden_slices.rs:69:1
+  --> $DIR/forbidden_slices.rs:70:1
    |
 LL | pub static R7: &[u16] = unsafe {
    | ^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered an unaligned reference (required 2 byte alignment but found 1)
@@ -186,7 +182,7 @@
 note: inside `std::ptr::const_ptr::<impl *const u64>::add`
   --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
 note: inside `R8`
-  --> $DIR/forbidden_slices.rs:76:25
+  --> $DIR/forbidden_slices.rs:77:25
    |
 LL |     from_ptr_range(ptr..ptr.add(1))
    |                         ^^^^^^^^^^
@@ -201,7 +197,7 @@
 note: inside `from_ptr_range::<'_, u32>`
   --> $SRC_DIR/core/src/slice/raw.rs:LL:COL
 note: inside `R9`
-  --> $DIR/forbidden_slices.rs:81:34
+  --> $DIR/forbidden_slices.rs:82:34
    |
 LL | pub static R9: &[u32] = unsafe { from_ptr_range(&D0..(&D0 as *const u32).add(1)) };
    |                                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -216,7 +212,7 @@
 note: inside `from_ptr_range::<'_, u32>`
   --> $SRC_DIR/core/src/slice/raw.rs:LL:COL
 note: inside `R10`
-  --> $DIR/forbidden_slices.rs:82:35
+  --> $DIR/forbidden_slices.rs:83:35
    |
 LL | pub static R10: &[u32] = unsafe { from_ptr_range(&D0..&D0) };
    |                                   ^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/tests/ui/consts/const-compare-bytes-ub.rs b/tests/ui/consts/const-compare-bytes-ub.rs
index b357bab..903ba15 100644
--- a/tests/ui/consts/const-compare-bytes-ub.rs
+++ b/tests/ui/consts/const-compare-bytes-ub.rs
@@ -7,11 +7,11 @@
 
 fn main() {
     const LHS_NULL: i32 = unsafe {
-        compare_bytes(0 as *const u8, 2 as *const u8, 0)
+        compare_bytes(0 as *const u8, 2 as *const u8, 1)
         //~^ ERROR evaluation of constant value failed
     };
     const RHS_NULL: i32 = unsafe {
-        compare_bytes(1 as *const u8, 0 as *const u8, 0)
+        compare_bytes(1 as *const u8, 0 as *const u8, 1)
         //~^ ERROR evaluation of constant value failed
     };
     const DANGLING_PTR_NON_ZERO_LENGTH: i32 = unsafe {
diff --git a/tests/ui/consts/const-compare-bytes-ub.stderr b/tests/ui/consts/const-compare-bytes-ub.stderr
index d8971eb..9e49706 100644
--- a/tests/ui/consts/const-compare-bytes-ub.stderr
+++ b/tests/ui/consts/const-compare-bytes-ub.stderr
@@ -1,14 +1,14 @@
 error[E0080]: evaluation of constant value failed
   --> $DIR/const-compare-bytes-ub.rs:10:9
    |
-LL |         compare_bytes(0 as *const u8, 2 as *const u8, 0)
+LL |         compare_bytes(0 as *const u8, 2 as *const u8, 1)
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: null pointer is a dangling pointer (it has no provenance)
 
 error[E0080]: evaluation of constant value failed
   --> $DIR/const-compare-bytes-ub.rs:14:9
    |
-LL |         compare_bytes(1 as *const u8, 0 as *const u8, 0)
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: null pointer is a dangling pointer (it has no provenance)
+LL |         compare_bytes(1 as *const u8, 0 as *const u8, 1)
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: 0x1[noalloc] is a dangling pointer (it has no provenance)
 
 error[E0080]: evaluation of constant value failed
   --> $DIR/const-compare-bytes-ub.rs:18:9
diff --git a/tests/ui/consts/copy-intrinsic.rs b/tests/ui/consts/copy-intrinsic.rs
index 94d7bdc..4183dc0 100644
--- a/tests/ui/consts/copy-intrinsic.rs
+++ b/tests/ui/consts/copy-intrinsic.rs
@@ -23,16 +23,20 @@
 const COPY_OOB_1: () = unsafe {
     let mut x = 0i32;
     let dangle = (&mut x as *mut i32).wrapping_add(10);
-    // Even if the first ptr is an int ptr and this is a ZST copy, we should detect dangling 2nd ptrs.
-    copy_nonoverlapping(0x100 as *const i32, dangle, 0); //~ ERROR evaluation of constant value failed [E0080]
-    //~| pointer at offset 40 is out-of-bounds
+    // Zero-sized copy is fine.
+    copy_nonoverlapping(0x100 as *const i32, dangle, 0);
+    // Non-zero-sized copy is not.
+    copy_nonoverlapping(0x100 as *const i32, dangle, 1); //~ ERROR evaluation of constant value failed [E0080]
+    //~| 0x100[noalloc] is a dangling pointer
 };
 const COPY_OOB_2: () = unsafe {
     let x = 0i32;
     let dangle = (&x as *const i32).wrapping_add(10);
-    // Even if the second ptr is an int ptr and this is a ZST copy, we should detect dangling 1st ptrs.
-    copy_nonoverlapping(dangle, 0x100 as *mut i32, 0); //~ ERROR evaluation of constant value failed [E0080]
-    //~| pointer at offset 40 is out-of-bounds
+    // Zero-sized copy is fine.
+    copy_nonoverlapping(dangle, 0x100 as *mut i32, 0);
+    // Non-zero-sized copy is not.
+    copy_nonoverlapping(dangle, 0x100 as *mut i32, 1); //~ ERROR evaluation of constant value failed [E0080]
+    //~| offset 40 is out-of-bounds
 };
 
 const COPY_SIZE_OVERFLOW: () = unsafe {
diff --git a/tests/ui/consts/copy-intrinsic.stderr b/tests/ui/consts/copy-intrinsic.stderr
index 0e4e6a6..d34e61c 100644
--- a/tests/ui/consts/copy-intrinsic.stderr
+++ b/tests/ui/consts/copy-intrinsic.stderr
@@ -1,23 +1,23 @@
 error[E0080]: evaluation of constant value failed
-  --> $DIR/copy-intrinsic.rs:27:5
+  --> $DIR/copy-intrinsic.rs:29:5
    |
-LL |     copy_nonoverlapping(0x100 as *const i32, dangle, 0);
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: ALLOC0 has size 4, so pointer at offset 40 is out-of-bounds
+LL |     copy_nonoverlapping(0x100 as *const i32, dangle, 1);
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: 0x100[noalloc] is a dangling pointer (it has no provenance)
 
 error[E0080]: evaluation of constant value failed
-  --> $DIR/copy-intrinsic.rs:34:5
+  --> $DIR/copy-intrinsic.rs:38:5
    |
-LL |     copy_nonoverlapping(dangle, 0x100 as *mut i32, 0);
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: ALLOC1 has size 4, so pointer at offset 40 is out-of-bounds
+LL |     copy_nonoverlapping(dangle, 0x100 as *mut i32, 1);
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: ALLOC0 has size 4, so pointer to 4 bytes starting at offset 40 is out-of-bounds
 
 error[E0080]: evaluation of constant value failed
-  --> $DIR/copy-intrinsic.rs:41:5
+  --> $DIR/copy-intrinsic.rs:45:5
    |
 LL |     copy(&x, &mut y, 1usize << (mem::size_of::<usize>() * 8 - 1));
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflow computing total size of `copy`
 
 error[E0080]: evaluation of constant value failed
-  --> $DIR/copy-intrinsic.rs:47:5
+  --> $DIR/copy-intrinsic.rs:51:5
    |
 LL |     copy_nonoverlapping(&x, &mut y, 1usize << (mem::size_of::<usize>() * 8 - 1));
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflow computing total size of `copy_nonoverlapping`
diff --git a/tests/ui/consts/dangling-alloc-id-ice.rs b/tests/ui/consts/dangling-alloc-id-ice.rs
index 76d6f33..6b07b8b 100644
--- a/tests/ui/consts/dangling-alloc-id-ice.rs
+++ b/tests/ui/consts/dangling-alloc-id-ice.rs
@@ -10,7 +10,7 @@
 }
 
 const FOO: &() = {
-    //~^ ERROR it is undefined behavior to use this value
+    //~^ ERROR encountered dangling pointer
     let y = ();
     unsafe { Foo { y: &y }.long_live_the_unit }
 };
diff --git a/tests/ui/consts/dangling-alloc-id-ice.stderr b/tests/ui/consts/dangling-alloc-id-ice.stderr
index 881c0b1..de31acf 100644
--- a/tests/ui/consts/dangling-alloc-id-ice.stderr
+++ b/tests/ui/consts/dangling-alloc-id-ice.stderr
@@ -1,14 +1,8 @@
-error[E0080]: it is undefined behavior to use this value
+error: encountered dangling pointer in final value of constant
   --> $DIR/dangling-alloc-id-ice.rs:12:1
    |
 LL | const FOO: &() = {
-   | ^^^^^^^^^^^^^^ constructing invalid value: encountered a dangling reference (use-after-free)
-   |
-   = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
-   = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) {
-               HEX_DUMP
-           }
+   | ^^^^^^^^^^^^^^
 
 error: aborting due to 1 previous error
 
-For more information about this error, try `rustc --explain E0080`.
diff --git a/tests/ui/consts/offset_from_ub.rs b/tests/ui/consts/offset_from_ub.rs
index e0dd270..57767e9 100644
--- a/tests/ui/consts/offset_from_ub.rs
+++ b/tests/ui/consts/offset_from_ub.rs
@@ -34,8 +34,8 @@
 
 pub const OFFSET_FROM_NULL: isize = {
     let ptr = 0 as *const u8;
-    unsafe { ptr_offset_from(ptr, ptr) } //~ERROR evaluation of constant value failed
-    //~| null pointer is a dangling pointer
+    // Null isn't special for zero-sized "accesses" (i.e., the range between the two pointers)
+    unsafe { ptr_offset_from(ptr, ptr) }
 };
 
 pub const DIFFERENT_INT: isize = { // offset_from with two different integers: like DIFFERENT_ALLOC
@@ -67,8 +67,8 @@
     let start_ptr = &4 as *const _ as *const u8;
     let length = 10;
     let end_ptr = (start_ptr).wrapping_add(length);
-    unsafe { ptr_offset_from(end_ptr, end_ptr) } //~ERROR evaluation of constant value failed
-    //~| pointer at offset 10 is out-of-bounds
+    // Out-of-bounds is fine as long as the range between the pointers is empty.
+    unsafe { ptr_offset_from(end_ptr, end_ptr) }
 };
 
 pub const DIFFERENT_ALLOC_UNSIGNED: usize = {
diff --git a/tests/ui/consts/offset_from_ub.stderr b/tests/ui/consts/offset_from_ub.stderr
index e3bac8d..65f75a6 100644
--- a/tests/ui/consts/offset_from_ub.stderr
+++ b/tests/ui/consts/offset_from_ub.stderr
@@ -24,12 +24,6 @@
    |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ exact_div: 1_isize cannot be divided by 2_isize without remainder
 
 error[E0080]: evaluation of constant value failed
-  --> $DIR/offset_from_ub.rs:37:14
-   |
-LL |     unsafe { ptr_offset_from(ptr, ptr) }
-   |              ^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds `offset_from`: null pointer is a dangling pointer (it has no provenance)
-
-error[E0080]: evaluation of constant value failed
   --> $DIR/offset_from_ub.rs:44:14
    |
 LL |     unsafe { ptr_offset_from(ptr2, ptr1) }
@@ -48,12 +42,6 @@
    |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds `offset_from`: ALLOC1 has size 4, so pointer to 10 bytes starting at offset 0 is out-of-bounds
 
 error[E0080]: evaluation of constant value failed
-  --> $DIR/offset_from_ub.rs:70:14
-   |
-LL |     unsafe { ptr_offset_from(end_ptr, end_ptr) }
-   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds `offset_from`: ALLOC2 has size 4, so pointer at offset 10 is out-of-bounds
-
-error[E0080]: evaluation of constant value failed
   --> $DIR/offset_from_ub.rs:79:14
    |
 LL |     unsafe { ptr_offset_from_unsigned(field_ptr, base_ptr) }
@@ -109,6 +97,6 @@
 LL |     unsafe { ptr1.offset_from(ptr2.wrapping_offset(1)) }
    |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: aborting due to 15 previous errors
+error: aborting due to 13 previous errors
 
 For more information about this error, try `rustc --explain E0080`.
diff --git a/tests/ui/consts/offset_ub.rs b/tests/ui/consts/offset_ub.rs
index 920ecb6..36e4ff1 100644
--- a/tests/ui/consts/offset_ub.rs
+++ b/tests/ui/consts/offset_ub.rs
@@ -17,10 +17,10 @@
 pub const ZERO_SIZED_ALLOC: *const u8 = unsafe { [0u8; 0].as_ptr().offset(1) }; //~NOTE
 pub const DANGLING: *const u8 = unsafe { ptr::NonNull::<u8>::dangling().as_ptr().offset(4) }; //~NOTE
 
-// Right now, a zero offset from null is UB
-pub const NULL_OFFSET_ZERO: *const u8 = unsafe { ptr::null::<u8>().offset(0) }; //~NOTE
-
 // Make sure that we don't panic when computing abs(offset*size_of::<T>())
 pub const UNDERFLOW_ABS: *const u8 = unsafe { (usize::MAX as *const u8).offset(isize::MIN) }; //~NOTE
 
+// Offset-by-zero is allowed.
+pub const NULL_OFFSET_ZERO: *const u8 = unsafe { ptr::null::<u8>().offset(0) };
+
 fn main() {}
diff --git a/tests/ui/consts/offset_ub.stderr b/tests/ui/consts/offset_ub.stderr
index b398b20..89371f0 100644
--- a/tests/ui/consts/offset_ub.stderr
+++ b/tests/ui/consts/offset_ub.stderr
@@ -131,29 +131,16 @@
 error[E0080]: evaluation of constant value failed
   --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
    |
-   = note: out-of-bounds pointer arithmetic: null pointer is a dangling pointer (it has no provenance)
-   |
-note: inside `std::ptr::const_ptr::<impl *const u8>::offset`
-  --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
-note: inside `NULL_OFFSET_ZERO`
-  --> $DIR/offset_ub.rs:21:50
-   |
-LL | pub const NULL_OFFSET_ZERO: *const u8 = unsafe { ptr::null::<u8>().offset(0) };
-   |                                                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error[E0080]: evaluation of constant value failed
-  --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
-   |
    = note: out-of-bounds pointer arithmetic: 0x7f..f[noalloc] is a dangling pointer (it has no provenance)
    |
 note: inside `std::ptr::const_ptr::<impl *const u8>::offset`
   --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
 note: inside `UNDERFLOW_ABS`
-  --> $DIR/offset_ub.rs:24:47
+  --> $DIR/offset_ub.rs:21:47
    |
 LL | pub const UNDERFLOW_ABS: *const u8 = unsafe { (usize::MAX as *const u8).offset(isize::MIN) };
    |                                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: aborting due to 12 previous errors
+error: aborting due to 11 previous errors
 
 For more information about this error, try `rustc --explain E0080`.
diff --git a/tests/ui/diagnostic_namespace/do_not_recommend/as_expression.rs b/tests/ui/diagnostic_namespace/do_not_recommend/as_expression.rs
index 9cdc193..5fd5cc5 100644
--- a/tests/ui/diagnostic_namespace/do_not_recommend/as_expression.rs
+++ b/tests/ui/diagnostic_namespace/do_not_recommend/as_expression.rs
@@ -26,7 +26,7 @@
     type SqlType = T;
 }
 
-#[do_not_recommend]
+#[diagnostic::do_not_recommend]
 impl<T, ST> AsExpression<ST> for T
 where
     T: Expression<SqlType = ST>,
diff --git a/tests/ui/diagnostic_namespace/do_not_recommend/feature-gate-do_not_recommend.rs b/tests/ui/diagnostic_namespace/do_not_recommend/feature-gate-do_not_recommend.rs
new file mode 100644
index 0000000..5a26d28
--- /dev/null
+++ b/tests/ui/diagnostic_namespace/do_not_recommend/feature-gate-do_not_recommend.rs
@@ -0,0 +1,17 @@
+#![feature(do_not_recommend)]
+
+pub trait Foo {}
+
+impl Foo for i32 {}
+
+pub trait Bar {}
+
+#[diagnostic::do_not_recommend]
+impl<T: Foo> Bar for T {}
+
+fn stuff<T: Bar>(_: T) {}
+
+fn main() {
+    stuff(1u8);
+    //~^ the trait bound `u8: Bar` is not satisfied
+}
diff --git a/tests/ui/rfcs/rfc-2397-do-not-recommend/feature-gate-do_not_recommend.stderr b/tests/ui/diagnostic_namespace/do_not_recommend/feature-gate-do_not_recommend.stderr
similarity index 79%
rename from tests/ui/rfcs/rfc-2397-do-not-recommend/feature-gate-do_not_recommend.stderr
rename to tests/ui/diagnostic_namespace/do_not_recommend/feature-gate-do_not_recommend.stderr
index e6f1994..3951231 100644
--- a/tests/ui/rfcs/rfc-2397-do-not-recommend/feature-gate-do_not_recommend.stderr
+++ b/tests/ui/diagnostic_namespace/do_not_recommend/feature-gate-do_not_recommend.stderr
@@ -1,11 +1,11 @@
 error[E0277]: the trait bound `u8: Bar` is not satisfied
-  --> $DIR/feature-gate-do_not_recommend.rs:19:11
+  --> $DIR/feature-gate-do_not_recommend.rs:15:11
    |
 LL |     stuff(1u8);
    |           ^^^ the trait `Bar` is not implemented for `u8`
    |
 note: required by a bound in `stuff`
-  --> $DIR/feature-gate-do_not_recommend.rs:16:13
+  --> $DIR/feature-gate-do_not_recommend.rs:12:13
    |
 LL | fn stuff<T: Bar>(_: T) {}
    |             ^^^ required by this bound in `stuff`
diff --git a/tests/ui/diagnostic_namespace/do_not_recommend/incorrect-locations.rs b/tests/ui/diagnostic_namespace/do_not_recommend/incorrect-locations.rs
new file mode 100644
index 0000000..400ef83
--- /dev/null
+++ b/tests/ui/diagnostic_namespace/do_not_recommend/incorrect-locations.rs
@@ -0,0 +1,39 @@
+//@ check-pass
+#![feature(do_not_recommend)]
+
+#[diagnostic::do_not_recommend]
+//~^WARN `#[diagnostic::do_not_recommend]` can only be placed
+const CONST: () = ();
+
+#[diagnostic::do_not_recommend]
+//~^WARN `#[diagnostic::do_not_recommend]` can only be placed
+static STATIC: () = ();
+
+#[diagnostic::do_not_recommend]
+//~^WARN `#[diagnostic::do_not_recommend]` can only be placed
+type Type = ();
+
+#[diagnostic::do_not_recommend]
+//~^WARN `#[diagnostic::do_not_recommend]` can only be placed
+enum Enum {}
+
+#[diagnostic::do_not_recommend]
+//~^WARN `#[diagnostic::do_not_recommend]` can only be placed
+extern "C" {}
+
+#[diagnostic::do_not_recommend]
+//~^WARN `#[diagnostic::do_not_recommend]` can only be placed
+fn fun() {}
+
+#[diagnostic::do_not_recommend]
+//~^WARN `#[diagnostic::do_not_recommend]` can only be placed
+struct Struct {}
+
+#[diagnostic::do_not_recommend]
+//~^WARN `#[diagnostic::do_not_recommend]` can only be placed
+trait Trait {}
+
+#[diagnostic::do_not_recommend]
+impl Trait for i32 {}
+
+fn main() {}
diff --git a/tests/ui/diagnostic_namespace/do_not_recommend/incorrect-locations.stderr b/tests/ui/diagnostic_namespace/do_not_recommend/incorrect-locations.stderr
new file mode 100644
index 0000000..c83fd46
--- /dev/null
+++ b/tests/ui/diagnostic_namespace/do_not_recommend/incorrect-locations.stderr
@@ -0,0 +1,52 @@
+warning: `#[diagnostic::do_not_recommend]` can only be placed on trait implementations
+  --> $DIR/incorrect-locations.rs:4:1
+   |
+LL | #[diagnostic::do_not_recommend]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: `#[warn(unknown_or_malformed_diagnostic_attributes)]` on by default
+
+warning: `#[diagnostic::do_not_recommend]` can only be placed on trait implementations
+  --> $DIR/incorrect-locations.rs:8:1
+   |
+LL | #[diagnostic::do_not_recommend]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+warning: `#[diagnostic::do_not_recommend]` can only be placed on trait implementations
+  --> $DIR/incorrect-locations.rs:12:1
+   |
+LL | #[diagnostic::do_not_recommend]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+warning: `#[diagnostic::do_not_recommend]` can only be placed on trait implementations
+  --> $DIR/incorrect-locations.rs:16:1
+   |
+LL | #[diagnostic::do_not_recommend]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+warning: `#[diagnostic::do_not_recommend]` can only be placed on trait implementations
+  --> $DIR/incorrect-locations.rs:20:1
+   |
+LL | #[diagnostic::do_not_recommend]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+warning: `#[diagnostic::do_not_recommend]` can only be placed on trait implementations
+  --> $DIR/incorrect-locations.rs:24:1
+   |
+LL | #[diagnostic::do_not_recommend]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+warning: `#[diagnostic::do_not_recommend]` can only be placed on trait implementations
+  --> $DIR/incorrect-locations.rs:28:1
+   |
+LL | #[diagnostic::do_not_recommend]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+warning: `#[diagnostic::do_not_recommend]` can only be placed on trait implementations
+  --> $DIR/incorrect-locations.rs:32:1
+   |
+LL | #[diagnostic::do_not_recommend]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+warning: 8 warnings emitted
+
diff --git a/tests/ui/diagnostic_namespace/do_not_recommend/simple.rs b/tests/ui/diagnostic_namespace/do_not_recommend/simple.rs
index 6fb15b9..780649b 100644
--- a/tests/ui/diagnostic_namespace/do_not_recommend/simple.rs
+++ b/tests/ui/diagnostic_namespace/do_not_recommend/simple.rs
@@ -6,7 +6,7 @@
 
 trait Foo {}
 
-#[do_not_recommend]
+#[diagnostic::do_not_recommend]
 impl<T> Foo for T where T: Send {}
 
 fn needs_foo<T: Foo>() {}
diff --git a/tests/ui/diagnostic_namespace/do_not_recommend/stacked.rs b/tests/ui/diagnostic_namespace/do_not_recommend/stacked.rs
index 695660d..fc355bd 100644
--- a/tests/ui/diagnostic_namespace/do_not_recommend/stacked.rs
+++ b/tests/ui/diagnostic_namespace/do_not_recommend/stacked.rs
@@ -8,7 +8,7 @@
 trait DontRecommend {}
 trait Other {}
 
-#[do_not_recommend]
+#[diagnostic::do_not_recommend]
 impl<T> Root for T where T: DontRecommend {}
 
 impl<T> DontRecommend for T where T: Other {}
diff --git a/tests/ui/diagnostic_namespace/do_not_recommend/unstable-feature.rs b/tests/ui/diagnostic_namespace/do_not_recommend/unstable-feature.rs
new file mode 100644
index 0000000..ccc687a
--- /dev/null
+++ b/tests/ui/diagnostic_namespace/do_not_recommend/unstable-feature.rs
@@ -0,0 +1,8 @@
+#![deny(unknown_or_malformed_diagnostic_attributes)]
+trait Foo {}
+
+#[diagnostic::do_not_recommend]
+//~^ ERROR unknown diagnostic attribute [unknown_or_malformed_diagnostic_attributes]
+impl Foo for i32 {}
+
+fn main() {}
diff --git a/tests/ui/diagnostic_namespace/do_not_recommend/unstable-feature.stderr b/tests/ui/diagnostic_namespace/do_not_recommend/unstable-feature.stderr
new file mode 100644
index 0000000..d833222
--- /dev/null
+++ b/tests/ui/diagnostic_namespace/do_not_recommend/unstable-feature.stderr
@@ -0,0 +1,14 @@
+error: unknown diagnostic attribute
+  --> $DIR/unstable-feature.rs:4:15
+   |
+LL | #[diagnostic::do_not_recommend]
+   |               ^^^^^^^^^^^^^^^^
+   |
+note: the lint level is defined here
+  --> $DIR/unstable-feature.rs:1:9
+   |
+LL | #![deny(unknown_or_malformed_diagnostic_attributes)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 1 previous error
+
diff --git a/tests/crashes/118185-2.rs b/tests/ui/mismatched_types/diagnostic-method-lookup-returns-sig-with-fewer-args.rs
similarity index 84%
rename from tests/crashes/118185-2.rs
rename to tests/ui/mismatched_types/diagnostic-method-lookup-returns-sig-with-fewer-args.rs
index c3a29c3..fd41bee 100644
--- a/tests/crashes/118185-2.rs
+++ b/tests/ui/mismatched_types/diagnostic-method-lookup-returns-sig-with-fewer-args.rs
@@ -1,9 +1,8 @@
-//@ known-bug: #118185
-
 fn main() {
     let target: Target = create_target();
     target.get(0); // correct arguments work
-    target.get(10.0); // CRASH HERE
+    target.get(10.0); // (used to crash here)
+    //~^ ERROR mismatched types
 }
 
 // must be generic
diff --git a/tests/ui/mismatched_types/diagnostic-method-lookup-returns-sig-with-fewer-args.stderr b/tests/ui/mismatched_types/diagnostic-method-lookup-returns-sig-with-fewer-args.stderr
new file mode 100644
index 0000000..0f86916
--- /dev/null
+++ b/tests/ui/mismatched_types/diagnostic-method-lookup-returns-sig-with-fewer-args.stderr
@@ -0,0 +1,17 @@
+error[E0308]: mismatched types
+  --> $DIR/diagnostic-method-lookup-returns-sig-with-fewer-args.rs:4:16
+   |
+LL |     target.get(10.0); // (used to crash here)
+   |            --- ^^^^ expected `i32`, found floating-point number
+   |            |
+   |            arguments to this method are incorrect
+   |
+note: method defined here
+  --> $DIR/diagnostic-method-lookup-returns-sig-with-fewer-args.rs:22:12
+   |
+LL |     pub fn get(&self, data: i32) {
+   |            ^^^        ---------
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/tests/ui/rfcs/rfc-2397-do-not-recommend/feature-gate-do_not_recommend.rs b/tests/ui/rfcs/rfc-2397-do-not-recommend/feature-gate-do_not_recommend.rs
deleted file mode 100644
index c9dc1c6..0000000
--- a/tests/ui/rfcs/rfc-2397-do-not-recommend/feature-gate-do_not_recommend.rs
+++ /dev/null
@@ -1,21 +0,0 @@
-#![feature(do_not_recommend)]
-
-pub trait Foo {
-}
-
-impl Foo for i32 {
-}
-
-pub trait Bar {
-}
-
-#[do_not_recommend]
-impl<T: Foo> Bar for T {
-}
-
-fn stuff<T: Bar>(_: T) {}
-
-fn main() {
-    stuff(1u8);
-    //~^ the trait bound `u8: Bar` is not satisfied
-}
diff --git a/tests/ui/rfcs/rfc-2397-do-not-recommend/incorrect-locations.rs b/tests/ui/rfcs/rfc-2397-do-not-recommend/incorrect-locations.rs
deleted file mode 100644
index 91863f5..0000000
--- a/tests/ui/rfcs/rfc-2397-do-not-recommend/incorrect-locations.rs
+++ /dev/null
@@ -1,45 +0,0 @@
-#![feature(do_not_recommend)]
-
-#[do_not_recommend]
-//~^ `#[do_not_recommend]` can only be placed
-const CONST: () = ();
-
-#[do_not_recommend]
-//~^ `#[do_not_recommend]` can only be placed
-static Static: () = ();
-
-#[do_not_recommend]
-//~^ `#[do_not_recommend]` can only be placed
-type Type = ();
-
-#[do_not_recommend]
-//~^ `#[do_not_recommend]` can only be placed
-enum Enum {
-}
-
-#[do_not_recommend]
-//~^ `#[do_not_recommend]` can only be placed
-extern {
-}
-
-#[do_not_recommend]
-//~^ `#[do_not_recommend]` can only be placed
-fn fun() {
-}
-
-#[do_not_recommend]
-//~^ `#[do_not_recommend]` can only be placed
-struct Struct {
-}
-
-#[do_not_recommend]
-//~^ `#[do_not_recommend]` can only be placed
-trait Trait {
-}
-
-#[do_not_recommend]
-impl Trait for i32 {
-}
-
-fn main() {
-}
diff --git a/tests/ui/rfcs/rfc-2397-do-not-recommend/incorrect-locations.stderr b/tests/ui/rfcs/rfc-2397-do-not-recommend/incorrect-locations.stderr
deleted file mode 100644
index 01ebc23..0000000
--- a/tests/ui/rfcs/rfc-2397-do-not-recommend/incorrect-locations.stderr
+++ /dev/null
@@ -1,50 +0,0 @@
-error: `#[do_not_recommend]` can only be placed on trait implementations
-  --> $DIR/incorrect-locations.rs:3:1
-   |
-LL | #[do_not_recommend]
-   | ^^^^^^^^^^^^^^^^^^^
-
-error: `#[do_not_recommend]` can only be placed on trait implementations
-  --> $DIR/incorrect-locations.rs:7:1
-   |
-LL | #[do_not_recommend]
-   | ^^^^^^^^^^^^^^^^^^^
-
-error: `#[do_not_recommend]` can only be placed on trait implementations
-  --> $DIR/incorrect-locations.rs:11:1
-   |
-LL | #[do_not_recommend]
-   | ^^^^^^^^^^^^^^^^^^^
-
-error: `#[do_not_recommend]` can only be placed on trait implementations
-  --> $DIR/incorrect-locations.rs:15:1
-   |
-LL | #[do_not_recommend]
-   | ^^^^^^^^^^^^^^^^^^^
-
-error: `#[do_not_recommend]` can only be placed on trait implementations
-  --> $DIR/incorrect-locations.rs:20:1
-   |
-LL | #[do_not_recommend]
-   | ^^^^^^^^^^^^^^^^^^^
-
-error: `#[do_not_recommend]` can only be placed on trait implementations
-  --> $DIR/incorrect-locations.rs:25:1
-   |
-LL | #[do_not_recommend]
-   | ^^^^^^^^^^^^^^^^^^^
-
-error: `#[do_not_recommend]` can only be placed on trait implementations
-  --> $DIR/incorrect-locations.rs:30:1
-   |
-LL | #[do_not_recommend]
-   | ^^^^^^^^^^^^^^^^^^^
-
-error: `#[do_not_recommend]` can only be placed on trait implementations
-  --> $DIR/incorrect-locations.rs:35:1
-   |
-LL | #[do_not_recommend]
-   | ^^^^^^^^^^^^^^^^^^^
-
-error: aborting due to 8 previous errors
-
diff --git a/tests/ui/rfcs/rfc-2397-do-not-recommend/unstable-feature.rs b/tests/ui/rfcs/rfc-2397-do-not-recommend/unstable-feature.rs
deleted file mode 100644
index f0c5c22..0000000
--- a/tests/ui/rfcs/rfc-2397-do-not-recommend/unstable-feature.rs
+++ /dev/null
@@ -1,10 +0,0 @@
-trait Foo {
-}
-
-#[do_not_recommend]
-//~^ ERROR the `#[do_not_recommend]` attribute is an experimental feature
-impl Foo for i32 {
-}
-
-fn main() {
-}
diff --git a/tests/ui/rfcs/rfc-2397-do-not-recommend/unstable-feature.stderr b/tests/ui/rfcs/rfc-2397-do-not-recommend/unstable-feature.stderr
deleted file mode 100644
index 02bc51c..0000000
--- a/tests/ui/rfcs/rfc-2397-do-not-recommend/unstable-feature.stderr
+++ /dev/null
@@ -1,13 +0,0 @@
-error[E0658]: the `#[do_not_recommend]` attribute is an experimental feature
-  --> $DIR/unstable-feature.rs:4:1
-   |
-LL | #[do_not_recommend]
-   | ^^^^^^^^^^^^^^^^^^^
-   |
-   = note: see issue #51992 <https://github.com/rust-lang/rust/issues/51992> for more information
-   = help: add `#![feature(do_not_recommend)]` to the crate attributes to enable
-   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
-
-error: aborting due to 1 previous error
-
-For more information about this error, try `rustc --explain E0658`.