about summary refs log tree commit diff
path: root/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'compiler')
-rw-r--r--compiler/rustc_ast_ir/src/visit.rs2
-rw-r--r--compiler/rustc_ast_passes/Cargo.toml2
-rw-r--r--compiler/rustc_ast_pretty/Cargo.toml2
-rw-r--r--compiler/rustc_borrowck/Cargo.toml2
-rw-r--r--compiler/rustc_borrowck/src/type_check/mod.rs2
-rw-r--r--compiler/rustc_codegen_cranelift/src/base.rs2
-rw-r--r--compiler/rustc_codegen_llvm/Cargo.toml2
-rw-r--r--compiler/rustc_codegen_ssa/Cargo.toml2
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/rvalue.rs3
-rw-r--r--compiler/rustc_const_eval/src/interpret/step.rs14
-rw-r--r--compiler/rustc_const_eval/src/interpret/validity.rs8
-rw-r--r--compiler/rustc_const_eval/src/interpret/visitor.rs10
-rw-r--r--compiler/rustc_const_eval/src/transform/check_consts/check.rs2
-rw-r--r--compiler/rustc_const_eval/src/transform/validate.rs2
-rw-r--r--compiler/rustc_driver_impl/src/lib.rs1
-rw-r--r--compiler/rustc_hir_analysis/Cargo.toml2
-rw-r--r--compiler/rustc_hir_analysis/src/check/intrinsic.rs9
-rw-r--r--compiler/rustc_hir_typeck/Cargo.toml2
-rw-r--r--compiler/rustc_hir_typeck/src/closure.rs17
-rw-r--r--compiler/rustc_interface/src/util.rs19
-rw-r--r--compiler/rustc_lint/Cargo.toml1
-rw-r--r--compiler/rustc_lint/src/non_local_def.rs34
-rw-r--r--compiler/rustc_lint_defs/src/builtin.rs31
-rw-r--r--compiler/rustc_metadata/src/locator.rs99
-rw-r--r--compiler/rustc_metadata/src/rmeta/decoder.rs24
-rw-r--r--compiler/rustc_middle/Cargo.toml1
-rw-r--r--compiler/rustc_middle/src/mir/pretty.rs2
-rw-r--r--compiler/rustc_middle/src/mir/syntax.rs12
-rw-r--r--compiler/rustc_middle/src/mir/tcx.rs2
-rw-r--r--compiler/rustc_middle/src/ty/instance.rs2
-rw-r--r--compiler/rustc_middle/src/ty/sty.rs7
-rw-r--r--compiler/rustc_mir_build/Cargo.toml2
-rw-r--r--compiler/rustc_mir_dataflow/src/move_paths/builder.rs2
-rw-r--r--compiler/rustc_mir_transform/Cargo.toml2
-rw-r--r--compiler/rustc_mir_transform/src/gvn.rs2
-rw-r--r--compiler/rustc_mir_transform/src/known_panics_lint.rs2
-rw-r--r--compiler/rustc_mir_transform/src/lower_intrinsics.rs21
-rw-r--r--compiler/rustc_mir_transform/src/promote_consts.rs2
-rw-r--r--compiler/rustc_parse/src/parser/diagnostics.rs2
-rw-r--r--compiler/rustc_passes/Cargo.toml1
-rw-r--r--compiler/rustc_pattern_analysis/messages.ftl8
-rw-r--r--compiler/rustc_pattern_analysis/src/constructor.rs35
-rw-r--r--compiler/rustc_pattern_analysis/src/errors.rs51
-rw-r--r--compiler/rustc_pattern_analysis/src/lib.rs23
-rw-r--r--compiler/rustc_pattern_analysis/src/rustc.rs72
-rw-r--r--compiler/rustc_pattern_analysis/src/usefulness.rs48
-rw-r--r--compiler/rustc_query_system/src/query/job.rs22
-rw-r--r--compiler/rustc_query_system/src/query/mod.rs2
-rw-r--r--compiler/rustc_smir/src/rustc_smir/convert/mir.rs8
-rw-r--r--compiler/rustc_span/Cargo.toml1
-rw-r--r--compiler/rustc_span/src/symbol.rs19
-rw-r--r--compiler/rustc_trait_selection/Cargo.toml2
-rw-r--r--compiler/rustc_trait_selection/src/traits/wf.rs601
-rw-r--r--compiler/rustc_transmute/Cargo.toml2
-rw-r--r--compiler/rustc_ty_utils/Cargo.toml2
-rw-r--r--compiler/rustc_ty_utils/src/instance.rs37
-rw-r--r--compiler/rustc_type_ir/src/lib.rs14
-rw-r--r--compiler/stable_mir/src/mir/body.rs10
58 files changed, 821 insertions, 492 deletions
diff --git a/compiler/rustc_ast_ir/src/visit.rs b/compiler/rustc_ast_ir/src/visit.rs
index dec9f7a47d0..f6d6bf3a3e3 100644
--- a/compiler/rustc_ast_ir/src/visit.rs
+++ b/compiler/rustc_ast_ir/src/visit.rs
@@ -14,7 +14,7 @@ impl VisitorResult for () {
     type Residual = !;
 
     #[cfg(not(feature = "nightly"))]
-    type Residual = core::ops::Infallible;
+    type Residual = core::convert::Infallible;
 
     fn output() -> Self {}
     fn from_residual(_: Self::Residual) -> Self {}
diff --git a/compiler/rustc_ast_passes/Cargo.toml b/compiler/rustc_ast_passes/Cargo.toml
index 99e79f65fb4..eace5ce8208 100644
--- a/compiler/rustc_ast_passes/Cargo.toml
+++ b/compiler/rustc_ast_passes/Cargo.toml
@@ -5,7 +5,7 @@ edition = "2021"
 
 [dependencies]
 # tidy-alphabetical-start
-itertools = "0.11"
+itertools = "0.12"
 rustc_ast = { path = "../rustc_ast" }
 rustc_ast_pretty = { path = "../rustc_ast_pretty" }
 rustc_attr = { path = "../rustc_attr" }
diff --git a/compiler/rustc_ast_pretty/Cargo.toml b/compiler/rustc_ast_pretty/Cargo.toml
index b38a2915a43..9ae5c9b3cec 100644
--- a/compiler/rustc_ast_pretty/Cargo.toml
+++ b/compiler/rustc_ast_pretty/Cargo.toml
@@ -5,7 +5,7 @@ edition = "2021"
 
 [dependencies]
 # tidy-alphabetical-start
-itertools = "0.11"
+itertools = "0.12"
 rustc_ast = { path = "../rustc_ast" }
 rustc_lexer = { path = "../rustc_lexer" }
 rustc_span = { path = "../rustc_span" }
diff --git a/compiler/rustc_borrowck/Cargo.toml b/compiler/rustc_borrowck/Cargo.toml
index 714f46270f9..bafc62c7318 100644
--- a/compiler/rustc_borrowck/Cargo.toml
+++ b/compiler/rustc_borrowck/Cargo.toml
@@ -6,7 +6,7 @@ edition = "2021"
 [dependencies]
 # tidy-alphabetical-start
 either = "1.5.0"
-itertools = "0.11"
+itertools = "0.12"
 polonius-engine = "0.13.0"
 rustc_data_structures = { path = "../rustc_data_structures" }
 rustc_errors = { path = "../rustc_errors" }
diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs
index 8d38b86fa34..412d50f493e 100644
--- a/compiler/rustc_borrowck/src/type_check/mod.rs
+++ b/compiler/rustc_borrowck/src/type_check/mod.rs
@@ -2000,7 +2000,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
                     ConstraintCategory::SizedBound,
                 );
             }
-            &Rvalue::NullaryOp(NullOp::DebugAssertions, _) => {}
+            &Rvalue::NullaryOp(NullOp::UbCheck(_), _) => {}
 
             Rvalue::ShallowInitBox(operand, ty) => {
                 self.check_operand(operand, location);
diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs
index 1ce920f3bdb..2415c2c90b2 100644
--- a/compiler/rustc_codegen_cranelift/src/base.rs
+++ b/compiler/rustc_codegen_cranelift/src/base.rs
@@ -779,7 +779,7 @@ fn codegen_stmt<'tcx>(
                         NullOp::OffsetOf(fields) => {
                             layout.offset_of_subfield(fx, fields.iter()).bytes()
                         }
-                        NullOp::DebugAssertions => {
+                        NullOp::UbCheck(_) => {
                             let val = fx.tcx.sess.opts.debug_assertions;
                             let val = CValue::by_val(
                                 fx.bcx.ins().iconst(types::I8, i64::try_from(val).unwrap()),
diff --git a/compiler/rustc_codegen_llvm/Cargo.toml b/compiler/rustc_codegen_llvm/Cargo.toml
index 3948a49ee2a..3fda59e8b52 100644
--- a/compiler/rustc_codegen_llvm/Cargo.toml
+++ b/compiler/rustc_codegen_llvm/Cargo.toml
@@ -9,7 +9,7 @@ test = false
 [dependencies]
 # tidy-alphabetical-start
 bitflags = "2.4.1"
-itertools = "0.11"
+itertools = "0.12"
 libc = "0.2"
 measureme = "11"
 object = { version = "0.32.0", default-features = false, features = ["std", "read"] }
diff --git a/compiler/rustc_codegen_ssa/Cargo.toml b/compiler/rustc_codegen_ssa/Cargo.toml
index 81ca42e1ad8..7851b9e8e03 100644
--- a/compiler/rustc_codegen_ssa/Cargo.toml
+++ b/compiler/rustc_codegen_ssa/Cargo.toml
@@ -8,7 +8,7 @@ edition = "2021"
 ar_archive_writer = "0.1.5"
 bitflags = "2.4.1"
 cc = "1.0.90"
-itertools = "0.11"
+itertools = "0.12"
 jobserver = "0.1.28"
 pathdiff = "0.2.0"
 regex = "1.4"
diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs
index 9ae82d4845e..8c6b9faf39d 100644
--- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs
@@ -685,7 +685,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                         let val = layout.offset_of_subfield(bx.cx(), fields.iter()).bytes();
                         bx.cx().const_usize(val)
                     }
-                    mir::NullOp::DebugAssertions => {
+                    mir::NullOp::UbCheck(_) => {
+                        // In codegen, we want to check for language UB and library UB
                         let val = bx.tcx().sess.opts.debug_assertions;
                         bx.cx().const_bool(val)
                     }
diff --git a/compiler/rustc_const_eval/src/interpret/step.rs b/compiler/rustc_const_eval/src/interpret/step.rs
index d4c96f4573d..54bac70da38 100644
--- a/compiler/rustc_const_eval/src/interpret/step.rs
+++ b/compiler/rustc_const_eval/src/interpret/step.rs
@@ -258,10 +258,16 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                         let val = layout.offset_of_subfield(self, fields.iter()).bytes();
                         Scalar::from_target_usize(val, self)
                     }
-                    mir::NullOp::DebugAssertions => {
-                        // The checks hidden behind this are always better done by the interpreter
-                        // itself, because it knows the runtime state better.
-                        Scalar::from_bool(false)
+                    mir::NullOp::UbCheck(kind) => {
+                        // We want to enable checks for library UB, because the interpreter doesn't
+                        // know about those on its own.
+                        // But we want to disable checks for language UB, because the interpreter
+                        // has its own better checks for that.
+                        let should_check = match kind {
+                            mir::UbKind::LibraryUb => self.tcx.sess.opts.debug_assertions,
+                            mir::UbKind::LanguageUb => false,
+                        };
+                        Scalar::from_bool(should_check)
                     }
                 };
                 self.write_scalar(val, &dest)?;
diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs
index ff1cb43db86..c568f9acfd3 100644
--- a/compiler/rustc_const_eval/src/interpret/validity.rs
+++ b/compiler/rustc_const_eval/src/interpret/validity.rs
@@ -17,8 +17,8 @@ use rustc_middle::mir::interpret::{
     ExpectedKind, InterpError, InvalidMetaKind, Misalignment, PointerKind, Provenance,
     ValidationErrorInfo, ValidationErrorKind, ValidationErrorKind::*,
 };
-use rustc_middle::ty;
 use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
+use rustc_middle::ty::{self, Ty};
 use rustc_span::symbol::{sym, Symbol};
 use rustc_target::abi::{
     Abi, FieldIdx, Scalar as ScalarAbi, Size, VariantIdx, Variants, WrappingRange,
@@ -783,7 +783,11 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
     }
 
     #[inline]
-    fn visit_box(&mut self, op: &OpTy<'tcx, M::Provenance>) -> InterpResult<'tcx> {
+    fn visit_box(
+        &mut self,
+        _box_ty: Ty<'tcx>,
+        op: &OpTy<'tcx, M::Provenance>,
+    ) -> InterpResult<'tcx> {
         self.check_safe_pointer(op, PointerKind::Box)?;
         Ok(())
     }
diff --git a/compiler/rustc_const_eval/src/interpret/visitor.rs b/compiler/rustc_const_eval/src/interpret/visitor.rs
index b200ecbf73a..0e824f3f592 100644
--- a/compiler/rustc_const_eval/src/interpret/visitor.rs
+++ b/compiler/rustc_const_eval/src/interpret/visitor.rs
@@ -3,7 +3,7 @@
 
 use rustc_index::IndexVec;
 use rustc_middle::mir::interpret::InterpResult;
-use rustc_middle::ty;
+use rustc_middle::ty::{self, Ty};
 use rustc_target::abi::FieldIdx;
 use rustc_target::abi::{FieldsShape, VariantIdx, Variants};
 
@@ -47,10 +47,10 @@ pub trait ValueVisitor<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>>: Sized {
         Ok(())
     }
     /// Visits the given value as the pointer of a `Box`. There is nothing to recurse into.
-    /// The type of `v` will be a raw pointer, but this is a field of `Box<T>` and the
-    /// pointee type is the actual `T`.
+    /// The type of `v` will be a raw pointer to `T`, but this is a field of `Box<T>` and the
+    /// pointee type is the actual `T`. `box_ty` provides the full type of the `Box` itself.
     #[inline(always)]
-    fn visit_box(&mut self, _v: &Self::V) -> InterpResult<'tcx> {
+    fn visit_box(&mut self, _box_ty: Ty<'tcx>, _v: &Self::V) -> InterpResult<'tcx> {
         Ok(())
     }
 
@@ -144,7 +144,7 @@ pub trait ValueVisitor<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>>: Sized {
                 assert_eq!(nonnull_ptr.layout().fields.count(), 1);
                 let raw_ptr = self.ecx().project_field(&nonnull_ptr, 0)?; // the actual raw ptr
                 // ... whose only field finally is a raw ptr we can dereference.
-                self.visit_box(&raw_ptr)?;
+                self.visit_box(ty, &raw_ptr)?;
 
                 // The second `Box` field is the allocator, which we recursively check for validity
                 // like in regular structs.
diff --git a/compiler/rustc_const_eval/src/transform/check_consts/check.rs b/compiler/rustc_const_eval/src/transform/check_consts/check.rs
index 7ec78e7b9cf..53308cd7f90 100644
--- a/compiler/rustc_const_eval/src/transform/check_consts/check.rs
+++ b/compiler/rustc_const_eval/src/transform/check_consts/check.rs
@@ -558,7 +558,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
             Rvalue::Cast(_, _, _) => {}
 
             Rvalue::NullaryOp(
-                NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(_) | NullOp::DebugAssertions,
+                NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(_) | NullOp::UbCheck(_),
                 _,
             ) => {}
             Rvalue::ShallowInitBox(_, _) => {}
diff --git a/compiler/rustc_const_eval/src/transform/validate.rs b/compiler/rustc_const_eval/src/transform/validate.rs
index 74ba2f6039e..f26c8f8592d 100644
--- a/compiler/rustc_const_eval/src/transform/validate.rs
+++ b/compiler/rustc_const_eval/src/transform/validate.rs
@@ -1157,7 +1157,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
             Rvalue::Repeat(_, _)
             | Rvalue::ThreadLocalRef(_)
             | Rvalue::AddressOf(_, _)
-            | Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::DebugAssertions, _)
+            | Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::UbCheck(_), _)
             | Rvalue::Discriminant(_) => {}
         }
         self.super_rvalue(rvalue, location);
diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs
index e06d1d245b2..2802f44cda7 100644
--- a/compiler/rustc_driver_impl/src/lib.rs
+++ b/compiler/rustc_driver_impl/src/lib.rs
@@ -676,6 +676,7 @@ fn list_metadata(early_dcx: &EarlyDiagCtxt, sess: &Session, metadata_loader: &dy
                 metadata_loader,
                 &mut v,
                 &sess.opts.unstable_opts.ls,
+                sess.cfg_version,
             )
             .unwrap();
             safe_println!("{}", String::from_utf8(v).unwrap());
diff --git a/compiler/rustc_hir_analysis/Cargo.toml b/compiler/rustc_hir_analysis/Cargo.toml
index 648b569a217..04ca7f123d3 100644
--- a/compiler/rustc_hir_analysis/Cargo.toml
+++ b/compiler/rustc_hir_analysis/Cargo.toml
@@ -9,7 +9,7 @@ doctest = false
 
 [dependencies]
 # tidy-alphabetical-start
-itertools = "0.11"
+itertools = "0.12"
 rustc_arena = { path = "../rustc_arena" }
 rustc_ast = { path = "../rustc_ast" }
 rustc_attr = { path = "../rustc_attr" }
diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs
index 95c51cc0486..bf5838143d9 100644
--- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs
+++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs
@@ -127,7 +127,8 @@ pub fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -
         | sym::variant_count
         | sym::is_val_statically_known
         | sym::ptr_mask
-        | sym::debug_assertions
+        | sym::check_language_ub
+        | sym::check_library_ub
         | sym::fadd_algebraic
         | sym::fsub_algebraic
         | sym::fmul_algebraic
@@ -584,7 +585,7 @@ pub fn check_intrinsic_type(
                 (0, 0, vec![Ty::new_imm_ptr(tcx, Ty::new_unit(tcx))], tcx.types.usize)
             }
 
-            sym::debug_assertions => (0, 1, Vec::new(), tcx.types.bool),
+            sym::check_language_ub | sym::check_library_ub => (0, 1, Vec::new(), tcx.types.bool),
 
             sym::simd_eq
             | sym::simd_ne
@@ -657,6 +658,10 @@ pub fn check_intrinsic_type(
             sym::simd_shuffle => (3, 0, vec![param(0), param(0), param(1)], param(2)),
             sym::simd_shuffle_generic => (2, 1, vec![param(0), param(0)], param(1)),
 
+            sym::retag_box_to_raw => {
+                (2, 0, vec![Ty::new_mut_ptr(tcx, param(0))], Ty::new_mut_ptr(tcx, param(0)))
+            }
+
             other => {
                 tcx.dcx().emit_err(UnrecognizedIntrinsicFunction { span, name: other });
                 return;
diff --git a/compiler/rustc_hir_typeck/Cargo.toml b/compiler/rustc_hir_typeck/Cargo.toml
index 0a5fa37ed04..9e7f0776b60 100644
--- a/compiler/rustc_hir_typeck/Cargo.toml
+++ b/compiler/rustc_hir_typeck/Cargo.toml
@@ -5,7 +5,7 @@ edition = "2021"
 
 [dependencies]
 # tidy-alphabetical-start
-itertools = "0.11"
+itertools = "0.12"
 rustc_ast = { path = "../rustc_ast" }
 rustc_ast_ir = { path = "../rustc_ast_ir" }
 rustc_attr = { path = "../rustc_attr" }
diff --git a/compiler/rustc_hir_typeck/src/closure.rs b/compiler/rustc_hir_typeck/src/closure.rs
index 1ff961a9089..c17af666eb9 100644
--- a/compiler/rustc_hir_typeck/src/closure.rs
+++ b/compiler/rustc_hir_typeck/src/closure.rs
@@ -19,7 +19,7 @@ use rustc_target::spec::abi::Abi;
 use rustc_trait_selection::traits;
 use rustc_trait_selection::traits::error_reporting::ArgKind;
 use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _;
-use std::cmp;
+use rustc_type_ir::ClosureKind;
 use std::iter;
 use std::ops::ControlFlow;
 
@@ -437,10 +437,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 };
 
                 if let Some(found_kind) = found_kind {
-                    expected_kind = Some(
-                        expected_kind
-                            .map_or_else(|| found_kind, |current| cmp::min(current, found_kind)),
-                    );
+                    // always use the closure kind that is more permissive.
+                    match (expected_kind, found_kind) {
+                        (None, _) => expected_kind = Some(found_kind),
+                        (Some(ClosureKind::FnMut), ClosureKind::Fn) => {
+                            expected_kind = Some(ClosureKind::Fn)
+                        }
+                        (Some(ClosureKind::FnOnce), ClosureKind::Fn | ClosureKind::FnMut) => {
+                            expected_kind = Some(found_kind)
+                        }
+                        _ => {}
+                    }
                 }
             }
         }
diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs
index 0d50200133c..23bd2dac57e 100644
--- a/compiler/rustc_interface/src/util.rs
+++ b/compiler/rustc_interface/src/util.rs
@@ -101,10 +101,11 @@ pub(crate) fn run_in_thread_pool_with_globals<F: FnOnce() -> R + Send, R: Send>(
     threads: usize,
     f: F,
 ) -> R {
-    use rustc_data_structures::{jobserver, sync::FromDyn};
+    use rustc_data_structures::{defer, jobserver, sync::FromDyn};
     use rustc_middle::ty::tls;
     use rustc_query_impl::QueryCtxt;
-    use rustc_query_system::query::{deadlock, QueryContext};
+    use rustc_query_system::query::{break_query_cycles, QueryContext};
+    use std::process;
 
     let registry = sync::Registry::new(std::num::NonZero::new(threads).unwrap());
 
@@ -128,7 +129,19 @@ pub(crate) fn run_in_thread_pool_with_globals<F: FnOnce() -> R + Send, R: Send>(
             let query_map =
                 FromDyn::from(tls::with(|tcx| QueryCtxt::new(tcx).collect_active_jobs()));
             let registry = rayon_core::Registry::current();
-            thread::spawn(move || deadlock(query_map.into_inner(), &registry));
+            thread::Builder::new()
+                .name("rustc query cycle handler".to_string())
+                .spawn(move || {
+                    let on_panic = defer(|| {
+                        eprintln!("query cycle handler thread panicked, aborting process");
+                        // We need to abort here as we failed to resolve the deadlock,
+                        // otherwise the compiler could just hang,
+                        process::abort();
+                    });
+                    break_query_cycles(query_map.into_inner(), &registry);
+                    on_panic.disable();
+                })
+                .unwrap();
         });
     if let Some(size) = get_stack_size() {
         builder = builder.stack_size(size);
diff --git a/compiler/rustc_lint/Cargo.toml b/compiler/rustc_lint/Cargo.toml
index 2271321b8bf..fa1133e7780 100644
--- a/compiler/rustc_lint/Cargo.toml
+++ b/compiler/rustc_lint/Cargo.toml
@@ -23,7 +23,6 @@ rustc_span = { path = "../rustc_span" }
 rustc_target = { path = "../rustc_target" }
 rustc_trait_selection = { path = "../rustc_trait_selection" }
 rustc_type_ir = { path = "../rustc_type_ir" }
-smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
 tracing = "0.1"
 unicode-security = "0.1.0"
 # tidy-alphabetical-end
diff --git a/compiler/rustc_lint/src/non_local_def.rs b/compiler/rustc_lint/src/non_local_def.rs
index a4fd5a7c45f..7c4d92d3ce0 100644
--- a/compiler/rustc_lint/src/non_local_def.rs
+++ b/compiler/rustc_lint/src/non_local_def.rs
@@ -2,8 +2,6 @@ use rustc_hir::{def::DefKind, Body, Item, ItemKind, Node, Path, QPath, TyKind};
 use rustc_span::def_id::{DefId, LOCAL_CRATE};
 use rustc_span::{sym, symbol::kw, ExpnKind, MacroKind};
 
-use smallvec::{smallvec, SmallVec};
-
 use crate::lints::{NonLocalDefinitionsCargoUpdateNote, NonLocalDefinitionsDiag};
 use crate::{LateContext, LateLintPass, LintContext};
 
@@ -85,7 +83,7 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions {
             if let Some(def_id) = oexpn.macro_def_id
                 && let ExpnKind::Macro(macro_kind, macro_name) = oexpn.kind
                 && def_id.krate != LOCAL_CRATE
-                && std::env::var_os("CARGO").is_some()
+                && rustc_session::utils::was_invoked_from_cargo()
             {
                 Some(NonLocalDefinitionsCargoUpdateNote {
                     macro_kind: macro_kind.descr(),
@@ -114,25 +112,25 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions {
                 // is using local items and so we don't lint on it.
 
                 // We also ignore anon-const in item by including the anon-const
-                // parent as well; and since it's quite uncommon, we use smallvec
-                // to avoid unnecessary heap allocations.
-                let local_parents: SmallVec<[DefId; 1]> = if parent_def_kind == DefKind::Const
+                // parent as well.
+                let parent_parent = if parent_def_kind == DefKind::Const
                     && parent_opt_item_name == Some(kw::Underscore)
                 {
-                    smallvec![parent, cx.tcx.parent(parent)]
+                    Some(cx.tcx.parent(parent))
                 } else {
-                    smallvec![parent]
+                    None
                 };
 
                 let self_ty_has_local_parent = match impl_.self_ty.kind {
                     TyKind::Path(QPath::Resolved(_, ty_path)) => {
-                        path_has_local_parent(ty_path, cx, &*local_parents)
+                        path_has_local_parent(ty_path, cx, parent, parent_parent)
                     }
                     TyKind::TraitObject([principle_poly_trait_ref, ..], _, _) => {
                         path_has_local_parent(
                             principle_poly_trait_ref.trait_ref.path,
                             cx,
-                            &*local_parents,
+                            parent,
+                            parent_parent,
                         )
                     }
                     TyKind::TraitObject([], _, _)
@@ -154,7 +152,7 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions {
 
                 let of_trait_has_local_parent = impl_
                     .of_trait
-                    .map(|of_trait| path_has_local_parent(of_trait.path, cx, &*local_parents))
+                    .map(|of_trait| path_has_local_parent(of_trait.path, cx, parent, parent_parent))
                     .unwrap_or(false);
 
                 // If none of them have a local parent (LOGICAL NOR) this means that
@@ -218,6 +216,16 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions {
 ///    std::convert::PartialEq<Foo<Bar>>
 ///    ^^^^^^^^^^^^^^^^^^^^^^^
 /// ```
-fn path_has_local_parent(path: &Path<'_>, cx: &LateContext<'_>, local_parents: &[DefId]) -> bool {
-    path.res.opt_def_id().is_some_and(|did| local_parents.contains(&cx.tcx.parent(did)))
+fn path_has_local_parent(
+    path: &Path<'_>,
+    cx: &LateContext<'_>,
+    impl_parent: DefId,
+    impl_parent_parent: Option<DefId>,
+) -> bool {
+    path.res.opt_def_id().is_some_and(|did| {
+        did.is_local() && {
+            let res_parent = cx.tcx.parent(did);
+            res_parent == impl_parent || Some(res_parent) == impl_parent_parent
+        }
+    })
 }
diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs
index 94f8bbe2437..f2560b35aa2 100644
--- a/compiler/rustc_lint_defs/src/builtin.rs
+++ b/compiler/rustc_lint_defs/src/builtin.rs
@@ -67,6 +67,7 @@ declare_lint_pass! {
         MISSING_FRAGMENT_SPECIFIER,
         MUST_NOT_SUSPEND,
         NAMED_ARGUMENTS_USED_POSITIONALLY,
+        NON_CONTIGUOUS_RANGE_ENDPOINTS,
         NON_EXHAUSTIVE_OMITTED_PATTERNS,
         ORDER_DEPENDENT_TRAIT_OBJECTS,
         OVERLAPPING_RANGE_ENDPOINTS,
@@ -813,6 +814,36 @@ declare_lint! {
 }
 
 declare_lint! {
+    /// The `non_contiguous_range_endpoints` lint detects likely off-by-one errors when using
+    /// exclusive [range patterns].
+    ///
+    /// [range patterns]: https://doc.rust-lang.org/nightly/reference/patterns.html#range-patterns
+    ///
+    /// ### Example
+    ///
+    /// ```rust
+    /// # #![feature(exclusive_range_pattern)]
+    /// let x = 123u32;
+    /// match x {
+    ///     0..100 => { println!("small"); }
+    ///     101..1000 => { println!("large"); }
+    ///     _ => { println!("larger"); }
+    /// }
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// It is likely a mistake to have range patterns in a match expression that miss out a single
+    /// number. Check that the beginning and end values are what you expect, and keep in mind that
+    /// with `..=` the right bound is inclusive, and with `..` it is exclusive.
+    pub NON_CONTIGUOUS_RANGE_ENDPOINTS,
+    Warn,
+    "detects off-by-one errors with exclusive range patterns"
+}
+
+declare_lint! {
     /// The `bindings_with_variant_name` lint detects pattern bindings with
     /// the same name as one of the matched variants.
     ///
diff --git a/compiler/rustc_metadata/src/locator.rs b/compiler/rustc_metadata/src/locator.rs
index dcccace12b0..10bbebb3871 100644
--- a/compiler/rustc_metadata/src/locator.rs
+++ b/compiler/rustc_metadata/src/locator.rs
@@ -569,31 +569,47 @@ impl<'a> CrateLocator<'a> {
                 debug!("skipping empty file");
                 continue;
             }
-            let (hash, metadata) =
-                match get_metadata_section(self.target, flavor, &lib, self.metadata_loader) {
-                    Ok(blob) => {
-                        if let Some(h) = self.crate_matches(&blob, &lib) {
-                            (h, blob)
-                        } else {
-                            info!("metadata mismatch");
-                            continue;
-                        }
-                    }
-                    Err(MetadataError::LoadFailure(err)) => {
-                        info!("no metadata found: {}", err);
-                        // The file was present and created by the same compiler version, but we
-                        // couldn't load it for some reason. Give a hard error instead of silently
-                        // ignoring it, but only if we would have given an error anyway.
-                        self.crate_rejections
-                            .via_invalid
-                            .push(CrateMismatch { path: lib, got: err });
-                        continue;
-                    }
-                    Err(err @ MetadataError::NotPresent(_)) => {
-                        info!("no metadata found: {}", err);
+            let (hash, metadata) = match get_metadata_section(
+                self.target,
+                flavor,
+                &lib,
+                self.metadata_loader,
+                self.cfg_version,
+            ) {
+                Ok(blob) => {
+                    if let Some(h) = self.crate_matches(&blob, &lib) {
+                        (h, blob)
+                    } else {
+                        info!("metadata mismatch");
                         continue;
                     }
-                };
+                }
+                Err(MetadataError::VersionMismatch { expected_version, found_version }) => {
+                    // The file was present and created by the same compiler version, but we
+                    // couldn't load it for some reason. Give a hard error instead of silently
+                    // ignoring it, but only if we would have given an error anyway.
+                    info!(
+                        "Rejecting via version: expected {} got {}",
+                        expected_version, found_version
+                    );
+                    self.crate_rejections
+                        .via_version
+                        .push(CrateMismatch { path: lib, got: found_version });
+                    continue;
+                }
+                Err(MetadataError::LoadFailure(err)) => {
+                    info!("no metadata found: {}", err);
+                    // The file was present and created by the same compiler version, but we
+                    // couldn't load it for some reason. Give a hard error instead of silently
+                    // ignoring it, but only if we would have given an error anyway.
+                    self.crate_rejections.via_invalid.push(CrateMismatch { path: lib, got: err });
+                    continue;
+                }
+                Err(err @ MetadataError::NotPresent(_)) => {
+                    info!("no metadata found: {}", err);
+                    continue;
+                }
+            };
             // If we see multiple hashes, emit an error about duplicate candidates.
             if slot.as_ref().is_some_and(|s| s.0 != hash) {
                 if let Some(candidates) = err_data {
@@ -648,16 +664,6 @@ impl<'a> CrateLocator<'a> {
     }
 
     fn crate_matches(&mut self, metadata: &MetadataBlob, libpath: &Path) -> Option<Svh> {
-        let rustc_version = rustc_version(self.cfg_version);
-        let found_version = metadata.get_rustc_version();
-        if found_version != rustc_version {
-            info!("Rejecting via version: expected {} got {}", rustc_version, found_version);
-            self.crate_rejections
-                .via_version
-                .push(CrateMismatch { path: libpath.to_path_buf(), got: found_version });
-            return None;
-        }
-
         let header = metadata.get_header();
         if header.is_proc_macro_crate != self.is_proc_macro {
             info!(
@@ -770,6 +776,7 @@ fn get_metadata_section<'p>(
     flavor: CrateFlavor,
     filename: &'p Path,
     loader: &dyn MetadataLoader,
+    cfg_version: &'static str,
 ) -> Result<MetadataBlob, MetadataError<'p>> {
     if !filename.exists() {
         return Err(MetadataError::NotPresent(filename));
@@ -847,13 +854,18 @@ fn get_metadata_section<'p>(
         }
     };
     let blob = MetadataBlob(raw_bytes);
-    if blob.is_compatible() {
-        Ok(blob)
-    } else {
-        Err(MetadataError::LoadFailure(format!(
+    match blob.check_compatibility(cfg_version) {
+        Ok(()) => Ok(blob),
+        Err(None) => Err(MetadataError::LoadFailure(format!(
             "invalid metadata version found: {}",
             filename.display()
-        )))
+        ))),
+        Err(Some(found_version)) => {
+            return Err(MetadataError::VersionMismatch {
+                expected_version: rustc_version(cfg_version),
+                found_version,
+            });
+        }
     }
 }
 
@@ -864,9 +876,10 @@ pub fn list_file_metadata(
     metadata_loader: &dyn MetadataLoader,
     out: &mut dyn Write,
     ls_kinds: &[String],
+    cfg_version: &'static str,
 ) -> IoResult<()> {
     let flavor = get_flavor_from_path(path);
-    match get_metadata_section(target, flavor, path, metadata_loader) {
+    match get_metadata_section(target, flavor, path, metadata_loader, cfg_version) {
         Ok(metadata) => metadata.list_crate_metadata(out, ls_kinds),
         Err(msg) => write!(out, "{msg}\n"),
     }
@@ -932,6 +945,8 @@ enum MetadataError<'a> {
     NotPresent(&'a Path),
     /// The file was present and invalid.
     LoadFailure(String),
+    /// The file was present, but compiled with a different rustc version.
+    VersionMismatch { expected_version: String, found_version: String },
 }
 
 impl fmt::Display for MetadataError<'_> {
@@ -941,6 +956,12 @@ impl fmt::Display for MetadataError<'_> {
                 f.write_str(&format!("no such file: '{}'", filename.display()))
             }
             MetadataError::LoadFailure(msg) => f.write_str(msg),
+            MetadataError::VersionMismatch { expected_version, found_version } => {
+                f.write_str(&format!(
+                    "rustc version mismatch. expected {}, found {}",
+                    expected_version, found_version,
+                ))
+            }
         }
     }
 }
diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs
index da384007c22..0467cf2969f 100644
--- a/compiler/rustc_metadata/src/rmeta/decoder.rs
+++ b/compiler/rustc_metadata/src/rmeta/decoder.rs
@@ -684,13 +684,25 @@ impl<'a, 'tcx, I: Idx, T> Decodable<DecodeContext<'a, 'tcx>> for LazyTable<I, T>
 implement_ty_decoder!(DecodeContext<'a, 'tcx>);
 
 impl MetadataBlob {
-    pub(crate) fn is_compatible(&self) -> bool {
-        self.blob().starts_with(METADATA_HEADER)
-    }
+    pub(crate) fn check_compatibility(
+        &self,
+        cfg_version: &'static str,
+    ) -> Result<(), Option<String>> {
+        if !self.blob().starts_with(METADATA_HEADER) {
+            if self.blob().starts_with(b"rust") {
+                return Err(Some("<unknown rustc version>".to_owned()));
+            }
+            return Err(None);
+        }
 
-    pub(crate) fn get_rustc_version(&self) -> String {
-        LazyValue::<String>::from_position(NonZero::new(METADATA_HEADER.len() + 8).unwrap())
-            .decode(self)
+        let found_version =
+            LazyValue::<String>::from_position(NonZero::new(METADATA_HEADER.len() + 8).unwrap())
+                .decode(self);
+        if rustc_version(cfg_version) != found_version {
+            return Err(Some(found_version));
+        }
+
+        Ok(())
     }
 
     fn root_pos(&self) -> NonZero<usize> {
diff --git a/compiler/rustc_middle/Cargo.toml b/compiler/rustc_middle/Cargo.toml
index 5a24a7ab0bd..96c58ef411b 100644
--- a/compiler/rustc_middle/Cargo.toml
+++ b/compiler/rustc_middle/Cargo.toml
@@ -6,7 +6,6 @@ edition = "2021"
 [dependencies]
 # tidy-alphabetical-start
 bitflags = "2.4.1"
-derive_more = "0.99.17"
 either = "1.5.0"
 field-offset = "0.3.5"
 gsgdt = "0.1.2"
diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs
index e058302af31..18069547a7e 100644
--- a/compiler/rustc_middle/src/mir/pretty.rs
+++ b/compiler/rustc_middle/src/mir/pretty.rs
@@ -915,7 +915,7 @@ impl<'tcx> Debug for Rvalue<'tcx> {
                     NullOp::SizeOf => write!(fmt, "SizeOf({t})"),
                     NullOp::AlignOf => write!(fmt, "AlignOf({t})"),
                     NullOp::OffsetOf(fields) => write!(fmt, "OffsetOf({t}, {fields:?})"),
-                    NullOp::DebugAssertions => write!(fmt, "cfg!(debug_assertions)"),
+                    NullOp::UbCheck(kind) => write!(fmt, "UbCheck({kind:?})"),
                 }
             }
             ThreadLocalRef(did) => ty::tls::with(|tcx| {
diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs
index f188923f876..0ab797134c0 100644
--- a/compiler/rustc_middle/src/mir/syntax.rs
+++ b/compiler/rustc_middle/src/mir/syntax.rs
@@ -1366,8 +1366,16 @@ pub enum NullOp<'tcx> {
     AlignOf,
     /// Returns the offset of a field
     OffsetOf(&'tcx List<(VariantIdx, FieldIdx)>),
-    /// cfg!(debug_assertions), but expanded in codegen
-    DebugAssertions,
+    /// Returns whether we want to check for library UB or language UB at monomorphization time.
+    /// Both kinds of UB evaluate to `true` in codegen, and only library UB evalutes to `true` in
+    /// const-eval/Miri, because the interpreter has its own better checks for language UB.
+    UbCheck(UbKind),
+}
+
+#[derive(Clone, Copy, Debug, PartialEq, Eq, TyEncodable, TyDecodable, Hash, HashStable)]
+pub enum UbKind {
+    LanguageUb,
+    LibraryUb,
 }
 
 #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
diff --git a/compiler/rustc_middle/src/mir/tcx.rs b/compiler/rustc_middle/src/mir/tcx.rs
index 5bc151de659..0c29fe57d4f 100644
--- a/compiler/rustc_middle/src/mir/tcx.rs
+++ b/compiler/rustc_middle/src/mir/tcx.rs
@@ -194,7 +194,7 @@ impl<'tcx> Rvalue<'tcx> {
             Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(..), _) => {
                 tcx.types.usize
             }
-            Rvalue::NullaryOp(NullOp::DebugAssertions, _) => tcx.types.bool,
+            Rvalue::NullaryOp(NullOp::UbCheck(_), _) => tcx.types.bool,
             Rvalue::Aggregate(ref ak, ref ops) => match **ak {
                 AggregateKind::Array(ty) => Ty::new_array(tcx, ty, ops.len() as u64),
                 AggregateKind::Tuple => {
diff --git a/compiler/rustc_middle/src/ty/instance.rs b/compiler/rustc_middle/src/ty/instance.rs
index 9fc8d418f5b..814c3629b08 100644
--- a/compiler/rustc_middle/src/ty/instance.rs
+++ b/compiler/rustc_middle/src/ty/instance.rs
@@ -30,7 +30,7 @@ pub struct Instance<'tcx> {
     pub args: GenericArgsRef<'tcx>,
 }
 
-#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
+#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
 #[derive(TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable, Lift)]
 pub enum InstanceDef<'tcx> {
     /// A user-defined callable item.
diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs
index 427c0f04bd1..39b5d3b6ea7 100644
--- a/compiler/rustc_middle/src/ty/sty.rs
+++ b/compiler/rustc_middle/src/ty/sty.rs
@@ -2008,13 +2008,10 @@ impl<'tcx> Ty<'tcx> {
                     // Single-argument Box is always global. (for "minicore" tests)
                     return true;
                 };
-                if let Some(alloc_adt) = alloc.expect_ty().ty_adt_def() {
+                alloc.expect_ty().ty_adt_def().is_some_and(|alloc_adt| {
                     let global_alloc = tcx.require_lang_item(LangItem::GlobalAlloc, None);
                     alloc_adt.did() == global_alloc
-                } else {
-                    // Allocator is not an ADT...
-                    false
-                }
+                })
             }
             _ => false,
         }
diff --git a/compiler/rustc_mir_build/Cargo.toml b/compiler/rustc_mir_build/Cargo.toml
index d71f7121322..6618e4f5a00 100644
--- a/compiler/rustc_mir_build/Cargo.toml
+++ b/compiler/rustc_mir_build/Cargo.toml
@@ -6,7 +6,7 @@ edition = "2021"
 [dependencies]
 # tidy-alphabetical-start
 either = "1"
-itertools = "0.11"
+itertools = "0.12"
 rustc_apfloat = "0.2.0"
 rustc_arena = { path = "../rustc_arena" }
 rustc_ast = { path = "../rustc_ast" }
diff --git a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs
index db48ecd702b..0f900e6a557 100644
--- a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs
+++ b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs
@@ -433,7 +433,7 @@ impl<'b, 'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> Gatherer<'b, 'a, 'tcx, F> {
             | Rvalue::Discriminant(..)
             | Rvalue::Len(..)
             | Rvalue::NullaryOp(
-                NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(..) | NullOp::DebugAssertions,
+                NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(..) | NullOp::UbCheck(_),
                 _,
             ) => {}
         }
diff --git a/compiler/rustc_mir_transform/Cargo.toml b/compiler/rustc_mir_transform/Cargo.toml
index 9cc083edb44..bd0a54ef363 100644
--- a/compiler/rustc_mir_transform/Cargo.toml
+++ b/compiler/rustc_mir_transform/Cargo.toml
@@ -6,7 +6,7 @@ edition = "2021"
 [dependencies]
 # tidy-alphabetical-start
 either = "1"
-itertools = "0.11"
+itertools = "0.12"
 rustc_arena = { path = "../rustc_arena" }
 rustc_ast = { path = "../rustc_ast" }
 rustc_attr = { path = "../rustc_attr" }
diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs
index a080e2423d4..a3a2108787a 100644
--- a/compiler/rustc_mir_transform/src/gvn.rs
+++ b/compiler/rustc_mir_transform/src/gvn.rs
@@ -488,7 +488,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
                     NullOp::OffsetOf(fields) => {
                         layout.offset_of_subfield(&self.ecx, fields.iter()).bytes()
                     }
-                    NullOp::DebugAssertions => return None,
+                    NullOp::UbCheck(_) => return None,
                 };
                 let usize_layout = self.ecx.layout_of(self.tcx.types.usize).unwrap();
                 let imm = ImmTy::try_from_uint(val, usize_layout)?;
diff --git a/compiler/rustc_mir_transform/src/known_panics_lint.rs b/compiler/rustc_mir_transform/src/known_panics_lint.rs
index 27477769cef..4bca437ea6f 100644
--- a/compiler/rustc_mir_transform/src/known_panics_lint.rs
+++ b/compiler/rustc_mir_transform/src/known_panics_lint.rs
@@ -639,7 +639,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
                     NullOp::OffsetOf(fields) => {
                         op_layout.offset_of_subfield(self, fields.iter()).bytes()
                     }
-                    NullOp::DebugAssertions => return None,
+                    NullOp::UbCheck(_) => return None,
                 };
                 ImmTy::from_scalar(Scalar::from_target_usize(val, self), layout).into()
             }
diff --git a/compiler/rustc_mir_transform/src/lower_intrinsics.rs b/compiler/rustc_mir_transform/src/lower_intrinsics.rs
index f317c025e96..1bab240ef50 100644
--- a/compiler/rustc_mir_transform/src/lower_intrinsics.rs
+++ b/compiler/rustc_mir_transform/src/lower_intrinsics.rs
@@ -20,13 +20,30 @@ impl<'tcx> MirPass<'tcx> for LowerIntrinsics {
                     sym::unreachable => {
                         terminator.kind = TerminatorKind::Unreachable;
                     }
-                    sym::debug_assertions => {
+                    sym::check_language_ub => {
                         let target = target.unwrap();
                         block.statements.push(Statement {
                             source_info: terminator.source_info,
                             kind: StatementKind::Assign(Box::new((
                                 *destination,
-                                Rvalue::NullaryOp(NullOp::DebugAssertions, tcx.types.bool),
+                                Rvalue::NullaryOp(
+                                    NullOp::UbCheck(UbKind::LanguageUb),
+                                    tcx.types.bool,
+                                ),
+                            ))),
+                        });
+                        terminator.kind = TerminatorKind::Goto { target };
+                    }
+                    sym::check_library_ub => {
+                        let target = target.unwrap();
+                        block.statements.push(Statement {
+                            source_info: terminator.source_info,
+                            kind: StatementKind::Assign(Box::new((
+                                *destination,
+                                Rvalue::NullaryOp(
+                                    NullOp::UbCheck(UbKind::LibraryUb),
+                                    tcx.types.bool,
+                                ),
                             ))),
                         });
                         terminator.kind = TerminatorKind::Goto { target };
diff --git a/compiler/rustc_mir_transform/src/promote_consts.rs b/compiler/rustc_mir_transform/src/promote_consts.rs
index 2e11da4d585..9fe8c34a8bf 100644
--- a/compiler/rustc_mir_transform/src/promote_consts.rs
+++ b/compiler/rustc_mir_transform/src/promote_consts.rs
@@ -446,7 +446,7 @@ impl<'tcx> Validator<'_, 'tcx> {
                 NullOp::SizeOf => {}
                 NullOp::AlignOf => {}
                 NullOp::OffsetOf(_) => {}
-                NullOp::DebugAssertions => {}
+                NullOp::UbCheck(_) => {}
             },
 
             Rvalue::ShallowInitBox(_, _) => return Err(Unpromotable),
diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs
index 2f7ac7d3a12..de088b9364b 100644
--- a/compiler/rustc_parse/src/parser/diagnostics.rs
+++ b/compiler/rustc_parse/src/parser/diagnostics.rs
@@ -667,7 +667,7 @@ impl<'a> Parser<'a> {
         {
             err.note("you may be trying to write a c-string literal");
             err.note("c-string literals require Rust 2021 or later");
-            HelpUseLatestEdition::new().add_to_diagnostic(&mut err);
+            err.subdiagnostic(self.dcx(), HelpUseLatestEdition::new());
         }
 
         // `pub` may be used for an item or `pub(crate)`
diff --git a/compiler/rustc_passes/Cargo.toml b/compiler/rustc_passes/Cargo.toml
index 80e6c104bd4..6abfa08c530 100644
--- a/compiler/rustc_passes/Cargo.toml
+++ b/compiler/rustc_passes/Cargo.toml
@@ -5,7 +5,6 @@ edition = "2021"
 
 [dependencies]
 # tidy-alphabetical-start
-itertools = "0.11"
 rustc_ast = { path = "../rustc_ast" }
 rustc_ast_pretty = { path = "../rustc_ast_pretty" }
 rustc_attr = { path = "../rustc_attr" }
diff --git a/compiler/rustc_pattern_analysis/messages.ftl b/compiler/rustc_pattern_analysis/messages.ftl
index 827928f97d7..41a1d958f10 100644
--- a/compiler/rustc_pattern_analysis/messages.ftl
+++ b/compiler/rustc_pattern_analysis/messages.ftl
@@ -1,3 +1,11 @@
+pattern_analysis_excluside_range_missing_gap = multiple ranges are one apart
+    .label = this range doesn't match `{$gap}` because `..` is an exclusive range
+    .suggestion = use an inclusive range instead
+
+pattern_analysis_excluside_range_missing_max = exclusive range missing `{$max}`
+    .label = this range doesn't match `{$max}` because `..` is an exclusive range
+    .suggestion = use an inclusive range instead
+
 pattern_analysis_non_exhaustive_omitted_pattern = some variants are not matched explicitly
     .help = ensure that all variants are matched explicitly by adding the suggested match arms
     .note = the matched value is of type `{$scrut_ty}` and the `non_exhaustive_omitted_patterns` attribute was found
diff --git a/compiler/rustc_pattern_analysis/src/constructor.rs b/compiler/rustc_pattern_analysis/src/constructor.rs
index 767227619b0..69e294e47a5 100644
--- a/compiler/rustc_pattern_analysis/src/constructor.rs
+++ b/compiler/rustc_pattern_analysis/src/constructor.rs
@@ -192,7 +192,7 @@ impl fmt::Display for RangeEnd {
 #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
 pub enum MaybeInfiniteInt {
     NegInfinity,
-    /// Encoded value. DO NOT CONSTRUCT BY HAND; use `new_finite`.
+    /// Encoded value. DO NOT CONSTRUCT BY HAND; use `new_finite_{int,uint}`.
     #[non_exhaustive]
     Finite(u128),
     /// The integer after `u128::MAX`. We need it to represent `x..=u128::MAX` as an exclusive range.
@@ -229,25 +229,22 @@ impl MaybeInfiniteInt {
     }
 
     /// Note: this will not turn a finite value into an infinite one or vice-versa.
-    pub fn minus_one(self) -> Self {
+    pub fn minus_one(self) -> Option<Self> {
         match self {
-            Finite(n) => match n.checked_sub(1) {
-                Some(m) => Finite(m),
-                None => panic!("Called `MaybeInfiniteInt::minus_one` on 0"),
-            },
-            JustAfterMax => Finite(u128::MAX),
-            x => x,
+            Finite(n) => n.checked_sub(1).map(Finite),
+            JustAfterMax => Some(Finite(u128::MAX)),
+            x => Some(x),
         }
     }
     /// Note: this will not turn a finite value into an infinite one or vice-versa.
-    pub fn plus_one(self) -> Self {
+    pub fn plus_one(self) -> Option<Self> {
         match self {
             Finite(n) => match n.checked_add(1) {
-                Some(m) => Finite(m),
-                None => JustAfterMax,
+                Some(m) => Some(Finite(m)),
+                None => Some(JustAfterMax),
             },
-            JustAfterMax => panic!("Called `MaybeInfiniteInt::plus_one` on u128::MAX+1"),
-            x => x,
+            JustAfterMax => None,
+            x => Some(x),
         }
     }
 }
@@ -268,18 +265,24 @@ impl IntRange {
     pub fn is_singleton(&self) -> bool {
         // Since `lo` and `hi` can't be the same `Infinity` and `plus_one` never changes from finite
         // to infinite, this correctly only detects ranges that contain exacly one `Finite(x)`.
-        self.lo.plus_one() == self.hi
+        self.lo.plus_one() == Some(self.hi)
     }
 
+    /// Construct a singleton range.
+    /// `x` must be a `Finite(_)` value.
     #[inline]
     pub fn from_singleton(x: MaybeInfiniteInt) -> IntRange {
-        IntRange { lo: x, hi: x.plus_one() }
+        // `unwrap()` is ok on a finite value
+        IntRange { lo: x, hi: x.plus_one().unwrap() }
     }
 
+    /// Construct a range with these boundaries.
+    /// `lo` must not be `PosInfinity` or `JustAfterMax`. `hi` must not be `NegInfinity`.
+    /// If `end` is `Included`, `hi` must also not be `JustAfterMax`.
     #[inline]
     pub fn from_range(lo: MaybeInfiniteInt, mut hi: MaybeInfiniteInt, end: RangeEnd) -> IntRange {
         if end == RangeEnd::Included {
-            hi = hi.plus_one();
+            hi = hi.plus_one().unwrap();
         }
         if lo >= hi {
             // This should have been caught earlier by E0030.
diff --git a/compiler/rustc_pattern_analysis/src/errors.rs b/compiler/rustc_pattern_analysis/src/errors.rs
index d0f56f0268d..e471b8abd73 100644
--- a/compiler/rustc_pattern_analysis/src/errors.rs
+++ b/compiler/rustc_pattern_analysis/src/errors.rs
@@ -77,6 +77,57 @@ impl<'tcx> AddToDiagnostic for Overlap<'tcx> {
 }
 
 #[derive(LintDiagnostic)]
+#[diag(pattern_analysis_excluside_range_missing_max)]
+pub struct ExclusiveRangeMissingMax<'tcx> {
+    #[label]
+    #[suggestion(code = "{suggestion}", applicability = "maybe-incorrect")]
+    /// This is an exclusive range that looks like `lo..max` (i.e. doesn't match `max`).
+    pub first_range: Span,
+    /// Suggest `lo..=max` instead.
+    pub suggestion: String,
+    pub max: Pat<'tcx>,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(pattern_analysis_excluside_range_missing_gap)]
+pub struct ExclusiveRangeMissingGap<'tcx> {
+    #[label]
+    #[suggestion(code = "{suggestion}", applicability = "maybe-incorrect")]
+    /// This is an exclusive range that looks like `lo..gap` (i.e. doesn't match `gap`).
+    pub first_range: Span,
+    pub gap: Pat<'tcx>,
+    /// Suggest `lo..=gap` instead.
+    pub suggestion: String,
+    #[subdiagnostic]
+    /// All these ranges skipped over `gap` which we think is probably a mistake.
+    pub gap_with: Vec<GappedRange<'tcx>>,
+}
+
+pub struct GappedRange<'tcx> {
+    pub span: Span,
+    pub gap: Pat<'tcx>,
+    pub first_range: Pat<'tcx>,
+}
+
+impl<'tcx> AddToDiagnostic for GappedRange<'tcx> {
+    fn add_to_diagnostic_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>(
+        self,
+        diag: &mut Diag<'_, G>,
+        _: F,
+    ) {
+        let GappedRange { span, gap, first_range } = self;
+
+        // FIXME(mejrs) unfortunately `#[derive(LintDiagnostic)]`
+        // does not support `#[subdiagnostic(eager)]`...
+        let message = format!(
+            "this could appear to continue range `{first_range}`, but `{gap}` isn't matched by \
+            either of them"
+        );
+        diag.span_label(span, message);
+    }
+}
+
+#[derive(LintDiagnostic)]
 #[diag(pattern_analysis_non_exhaustive_omitted_pattern)]
 #[help]
 #[note]
diff --git a/compiler/rustc_pattern_analysis/src/lib.rs b/compiler/rustc_pattern_analysis/src/lib.rs
index 4b0955699fc..f632eaf7ea4 100644
--- a/compiler/rustc_pattern_analysis/src/lib.rs
+++ b/compiler/rustc_pattern_analysis/src/lib.rs
@@ -70,14 +70,8 @@ use rustc_middle::ty::Ty;
 use rustc_span::ErrorGuaranteed;
 
 use crate::constructor::{Constructor, ConstructorSet, IntRange};
-#[cfg(feature = "rustc")]
-use crate::lints::lint_nonexhaustive_missing_variants;
 use crate::pat::DeconstructedPat;
 use crate::pat_column::PatternColumn;
-#[cfg(feature = "rustc")]
-use crate::rustc::RustcMatchCheckCtxt;
-#[cfg(feature = "rustc")]
-use crate::usefulness::{compute_match_usefulness, ValidityConstraint};
 
 pub trait Captures<'a> {}
 impl<'a, T: ?Sized> Captures<'a> for T {}
@@ -145,6 +139,18 @@ pub trait TypeCx: Sized + fmt::Debug {
 
     /// The maximum pattern complexity limit was reached.
     fn complexity_exceeded(&self) -> Result<(), Self::Error>;
+
+    /// Lint that there is a gap `gap` between `pat` and all of `gapped_with` such that the gap is
+    /// not matched by another range. If `gapped_with` is empty, then `gap` is `T::MAX`. We only
+    /// detect singleton gaps.
+    /// The default implementation does nothing.
+    fn lint_non_contiguous_range_endpoints(
+        &self,
+        _pat: &DeconstructedPat<Self>,
+        _gap: IntRange,
+        _gapped_with: &[&DeconstructedPat<Self>],
+    ) {
+    }
 }
 
 /// The arm of a match expression.
@@ -167,11 +173,14 @@ impl<'p, Cx: TypeCx> Copy for MatchArm<'p, Cx> {}
 /// useful, and runs some lints.
 #[cfg(feature = "rustc")]
 pub fn analyze_match<'p, 'tcx>(
-    tycx: &RustcMatchCheckCtxt<'p, 'tcx>,
+    tycx: &rustc::RustcMatchCheckCtxt<'p, 'tcx>,
     arms: &[rustc::MatchArm<'p, 'tcx>],
     scrut_ty: Ty<'tcx>,
     pattern_complexity_limit: Option<usize>,
 ) -> Result<rustc::UsefulnessReport<'p, 'tcx>, ErrorGuaranteed> {
+    use lints::lint_nonexhaustive_missing_variants;
+    use usefulness::{compute_match_usefulness, ValidityConstraint};
+
     let scrut_ty = tycx.reveal_opaque_ty(scrut_ty);
     let scrut_validity = ValidityConstraint::from_bool(tycx.known_valid_scrutinee);
     let report =
diff --git a/compiler/rustc_pattern_analysis/src/rustc.rs b/compiler/rustc_pattern_analysis/src/rustc.rs
index 5f5bfa7154a..0085f0ab656 100644
--- a/compiler/rustc_pattern_analysis/src/rustc.rs
+++ b/compiler/rustc_pattern_analysis/src/rustc.rs
@@ -8,7 +8,7 @@ use rustc_index::{Idx, IndexVec};
 use rustc_middle::middle::stability::EvalResult;
 use rustc_middle::mir::interpret::Scalar;
 use rustc_middle::mir::{self, Const};
-use rustc_middle::thir::{FieldPat, Pat, PatKind, PatRange, PatRangeBoundary};
+use rustc_middle::thir::{self, FieldPat, Pat, PatKind, PatRange, PatRangeBoundary};
 use rustc_middle::ty::layout::IntegerExt;
 use rustc_middle::ty::{self, FieldDef, OpaqueTypeKey, Ty, TyCtxt, TypeVisitableExt, VariantDef};
 use rustc_session::lint;
@@ -718,12 +718,12 @@ impl<'p, 'tcx: 'p> RustcMatchCheckCtxt<'p, 'tcx> {
                 let value = mir::Const::from_ty_const(c, cx.tcx);
                 lo = PatRangeBoundary::Finite(value);
             }
-            let hi = if matches!(range.hi, Finite(0)) {
+            let hi = if let Some(hi) = range.hi.minus_one() {
+                hi
+            } else {
                 // The range encodes `..ty::MIN`, so we can't convert it to an inclusive range.
                 end = rustc_hir::RangeEnd::Excluded;
                 range.hi
-            } else {
-                range.hi.minus_one()
             };
             let hi = cx.hoist_pat_range_bdy(hi, ty);
             PatKind::Range(Box::new(PatRange { lo, hi, end, ty: ty.inner() }))
@@ -900,6 +900,70 @@ impl<'p, 'tcx: 'p> TypeCx for RustcMatchCheckCtxt<'p, 'tcx> {
         let span = self.whole_match_span.unwrap_or(self.scrut_span);
         Err(self.tcx.dcx().span_err(span, "reached pattern complexity limit"))
     }
+
+    fn lint_non_contiguous_range_endpoints(
+        &self,
+        pat: &crate::pat::DeconstructedPat<Self>,
+        gap: IntRange,
+        gapped_with: &[&crate::pat::DeconstructedPat<Self>],
+    ) {
+        let Some(&thir_pat) = pat.data() else { return };
+        let thir::PatKind::Range(range) = &thir_pat.kind else { return };
+        // Only lint when the left range is an exclusive range.
+        if range.end != rustc_hir::RangeEnd::Excluded {
+            return;
+        }
+        // `pat` is an exclusive range like `lo..gap`. `gapped_with` contains ranges that start with
+        // `gap+1`.
+        let suggested_range: thir::Pat<'_> = {
+            // Suggest `lo..=gap` instead.
+            let mut suggested_range = thir_pat.clone();
+            let thir::PatKind::Range(range) = &mut suggested_range.kind else { unreachable!() };
+            range.end = rustc_hir::RangeEnd::Included;
+            suggested_range
+        };
+        let gap_as_pat = self.hoist_pat_range(&gap, *pat.ty());
+        if gapped_with.is_empty() {
+            // If `gapped_with` is empty, `gap == T::MAX`.
+            self.tcx.emit_node_span_lint(
+                lint::builtin::NON_CONTIGUOUS_RANGE_ENDPOINTS,
+                self.match_lint_level,
+                thir_pat.span,
+                errors::ExclusiveRangeMissingMax {
+                    // Point at this range.
+                    first_range: thir_pat.span,
+                    // That's the gap that isn't covered.
+                    max: gap_as_pat.clone(),
+                    // Suggest `lo..=max` instead.
+                    suggestion: suggested_range.to_string(),
+                },
+            );
+        } else {
+            self.tcx.emit_node_span_lint(
+                lint::builtin::NON_CONTIGUOUS_RANGE_ENDPOINTS,
+                self.match_lint_level,
+                thir_pat.span,
+                errors::ExclusiveRangeMissingGap {
+                    // Point at this range.
+                    first_range: thir_pat.span,
+                    // That's the gap that isn't covered.
+                    gap: gap_as_pat.clone(),
+                    // Suggest `lo..=gap` instead.
+                    suggestion: suggested_range.to_string(),
+                    // All these ranges skipped over `gap` which we think is probably a
+                    // mistake.
+                    gap_with: gapped_with
+                        .iter()
+                        .map(|pat| errors::GappedRange {
+                            span: pat.data().unwrap().span,
+                            gap: gap_as_pat.clone(),
+                            first_range: thir_pat.clone(),
+                        })
+                        .collect(),
+                },
+            );
+        }
+    }
 }
 
 /// Recursively expand this pattern into its subpatterns. Only useful for or-patterns.
diff --git a/compiler/rustc_pattern_analysis/src/usefulness.rs b/compiler/rustc_pattern_analysis/src/usefulness.rs
index c518844cc5e..a067bf1f0c2 100644
--- a/compiler/rustc_pattern_analysis/src/usefulness.rs
+++ b/compiler/rustc_pattern_analysis/src/usefulness.rs
@@ -1489,7 +1489,7 @@ impl<Cx: TypeCx> WitnessMatrix<Cx> {
 /// We can however get false negatives because exhaustiveness does not explore all cases. See the
 /// section on relevancy at the top of the file.
 fn collect_overlapping_range_endpoints<'p, Cx: TypeCx>(
-    mcx: &mut UsefulnessCtxt<'_, Cx>,
+    cx: &Cx,
     overlap_range: IntRange,
     matrix: &Matrix<'p, Cx>,
     specialized_matrix: &Matrix<'p, Cx>,
@@ -1522,11 +1522,11 @@ fn collect_overlapping_range_endpoints<'p, Cx: TypeCx>(
                     .map(|&(_, pat)| pat)
                     .collect();
                 if !overlaps_with.is_empty() {
-                    mcx.tycx.lint_overlapping_range_endpoints(pat, overlap_range, &overlaps_with);
+                    cx.lint_overlapping_range_endpoints(pat, overlap_range, &overlaps_with);
                 }
             }
             suffixes.push((child_row_id, pat))
-        } else if this_range.hi == overlap.plus_one() {
+        } else if Some(this_range.hi) == overlap.plus_one() {
             // `this_range` looks like `this_range.lo..=overlap`; it overlaps with any
             // ranges that look like `overlap..=hi`.
             if !suffixes.is_empty() {
@@ -1538,7 +1538,7 @@ fn collect_overlapping_range_endpoints<'p, Cx: TypeCx>(
                     .map(|&(_, pat)| pat)
                     .collect();
                 if !overlaps_with.is_empty() {
-                    mcx.tycx.lint_overlapping_range_endpoints(pat, overlap_range, &overlaps_with);
+                    cx.lint_overlapping_range_endpoints(pat, overlap_range, &overlaps_with);
                 }
             }
             prefixes.push((child_row_id, pat))
@@ -1546,6 +1546,33 @@ fn collect_overlapping_range_endpoints<'p, Cx: TypeCx>(
     }
 }
 
+/// Collect ranges that have a singleton gap between them.
+fn collect_non_contiguous_range_endpoints<'p, Cx: TypeCx>(
+    cx: &Cx,
+    gap_range: &IntRange,
+    matrix: &Matrix<'p, Cx>,
+) {
+    let gap = gap_range.lo;
+    // Ranges that look like `lo..gap`.
+    let mut onebefore: SmallVec<[_; 1]> = Default::default();
+    // Ranges that start on `gap+1` or singletons `gap+1`.
+    let mut oneafter: SmallVec<[_; 1]> = Default::default();
+    // Look through the column for ranges near the gap.
+    for pat in matrix.heads() {
+        let PatOrWild::Pat(pat) = pat else { continue };
+        let Constructor::IntRange(this_range) = pat.ctor() else { continue };
+        if gap == this_range.hi {
+            onebefore.push(pat)
+        } else if gap.plus_one() == Some(this_range.lo) {
+            oneafter.push(pat)
+        }
+    }
+
+    for pat_before in onebefore {
+        cx.lint_non_contiguous_range_endpoints(pat_before, *gap_range, oneafter.as_slice());
+    }
+}
+
 /// The core of the algorithm.
 ///
 /// This recursively computes witnesses of the non-exhaustiveness of `matrix` (if any). Also tracks
@@ -1626,13 +1653,24 @@ fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: TypeCx>(
                 && spec_matrix.rows.len() >= 2
                 && spec_matrix.rows.iter().any(|row| !row.intersects.is_empty())
             {
-                collect_overlapping_range_endpoints(mcx, overlap_range, matrix, &spec_matrix);
+                collect_overlapping_range_endpoints(mcx.tycx, overlap_range, matrix, &spec_matrix);
             }
         }
 
         matrix.unspecialize(spec_matrix);
     }
 
+    // Detect singleton gaps between ranges.
+    if missing_ctors.iter().any(|c| matches!(c, Constructor::IntRange(..))) {
+        for missing in &missing_ctors {
+            if let Constructor::IntRange(gap) = missing {
+                if gap.is_singleton() {
+                    collect_non_contiguous_range_endpoints(mcx.tycx, gap, matrix);
+                }
+            }
+        }
+    }
+
     // Record usefulness in the patterns.
     for row in matrix.rows() {
         if row.useful {
diff --git a/compiler/rustc_query_system/src/query/job.rs b/compiler/rustc_query_system/src/query/job.rs
index 1a54a229357..248a741af90 100644
--- a/compiler/rustc_query_system/src/query/job.rs
+++ b/compiler/rustc_query_system/src/query/job.rs
@@ -17,10 +17,9 @@ use std::num::NonZero;
 use {
     parking_lot::{Condvar, Mutex},
     rustc_data_structures::fx::FxHashSet,
-    rustc_data_structures::{defer, jobserver},
+    rustc_data_structures::jobserver,
     rustc_span::DUMMY_SP,
     std::iter,
-    std::process,
     std::sync::Arc,
 };
 
@@ -514,12 +513,7 @@ fn remove_cycle(
 /// There may be multiple cycles involved in a deadlock, so this searches
 /// all active queries for cycles before finally resuming all the waiters at once.
 #[cfg(parallel_compiler)]
-pub fn deadlock(query_map: QueryMap, registry: &rayon_core::Registry) {
-    let on_panic = defer(|| {
-        eprintln!("deadlock handler panicked, aborting process");
-        process::abort();
-    });
-
+pub fn break_query_cycles(query_map: QueryMap, registry: &rayon_core::Registry) {
     let mut wakelist = Vec::new();
     let mut jobs: Vec<QueryJobId> = query_map.keys().cloned().collect();
 
@@ -539,19 +533,17 @@ pub fn deadlock(query_map: QueryMap, registry: &rayon_core::Registry) {
     // X to Y due to Rayon waiting and a true dependency from Y to X. The algorithm here
     // only considers the true dependency and won't detect a cycle.
     if !found_cycle {
-        if query_map.len() == 0 {
-            panic!("deadlock detected without any query!")
-        } else {
-            panic!("deadlock detected! current query map:\n{:#?}", query_map);
-        }
+        panic!(
+            "deadlock detected as we're unable to find a query cycle to break\n\
+            current query map:\n{:#?}",
+            query_map
+        );
     }
 
     // FIXME: Ensure this won't cause a deadlock before we return
     for waiter in wakelist.into_iter() {
         waiter.notify(registry);
     }
-
-    on_panic.disable();
 }
 
 #[inline(never)]
diff --git a/compiler/rustc_query_system/src/query/mod.rs b/compiler/rustc_query_system/src/query/mod.rs
index 0aefe553a34..01b9d458f1e 100644
--- a/compiler/rustc_query_system/src/query/mod.rs
+++ b/compiler/rustc_query_system/src/query/mod.rs
@@ -3,7 +3,7 @@ pub use self::plumbing::*;
 
 mod job;
 #[cfg(parallel_compiler)]
-pub use self::job::deadlock;
+pub use self::job::break_query_cycles;
 pub use self::job::{
     print_query_stack, report_cycle, QueryInfo, QueryJob, QueryJobId, QueryJobInfo, QueryMap,
 };
diff --git a/compiler/rustc_smir/src/rustc_smir/convert/mir.rs b/compiler/rustc_smir/src/rustc_smir/convert/mir.rs
index 003a9a59200..c0876adf905 100644
--- a/compiler/rustc_smir/src/rustc_smir/convert/mir.rs
+++ b/compiler/rustc_smir/src/rustc_smir/convert/mir.rs
@@ -251,13 +251,19 @@ impl<'tcx> Stable<'tcx> for mir::NullOp<'tcx> {
     type T = stable_mir::mir::NullOp;
     fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
         use rustc_middle::mir::NullOp::*;
+        use rustc_middle::mir::UbKind;
         match self {
             SizeOf => stable_mir::mir::NullOp::SizeOf,
             AlignOf => stable_mir::mir::NullOp::AlignOf,
             OffsetOf(indices) => stable_mir::mir::NullOp::OffsetOf(
                 indices.iter().map(|idx| idx.stable(tables)).collect(),
             ),
-            DebugAssertions => stable_mir::mir::NullOp::DebugAssertions,
+            UbCheck(UbKind::LanguageUb) => {
+                stable_mir::mir::NullOp::UbCheck(stable_mir::mir::UbKind::LanguageUb)
+            }
+            UbCheck(UbKind::LibraryUb) => {
+                stable_mir::mir::NullOp::UbCheck(stable_mir::mir::UbKind::LibraryUb)
+            }
         }
     }
 }
diff --git a/compiler/rustc_span/Cargo.toml b/compiler/rustc_span/Cargo.toml
index 99de91a068a..98ed985738a 100644
--- a/compiler/rustc_span/Cargo.toml
+++ b/compiler/rustc_span/Cargo.toml
@@ -6,6 +6,7 @@ edition = "2021"
 [dependencies]
 # tidy-alphabetical-start
 indexmap = { version = "2.0.0" }
+itoa = "1.0"
 md5 = { package = "md-5", version = "0.10.0" }
 rustc_arena = { path = "../rustc_arena" }
 rustc_data_structures = { path = "../rustc_data_structures" }
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index 9dadd471247..708349e85ae 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -518,6 +518,8 @@ symbols! {
         cfi,
         cfi_encoding,
         char,
+        check_language_ub,
+        check_library_ub,
         client,
         clippy,
         clobber_abi,
@@ -998,6 +1000,11 @@ symbols! {
         is_val_statically_known,
         isa_attribute,
         isize,
+        isize_legacy_const_max,
+        isize_legacy_const_min,
+        isize_legacy_fn_max_value,
+        isize_legacy_fn_min_value,
+        isize_legacy_mod,
         issue,
         issue_5723_bootstrap,
         issue_tracker_base_url,
@@ -1450,6 +1457,7 @@ symbols! {
         residual,
         result,
         resume,
+        retag_box_to_raw,
         return_position_impl_trait_in_trait,
         return_type_notation,
         rhs,
@@ -1907,6 +1915,11 @@ symbols! {
         used_with_arg,
         using,
         usize,
+        usize_legacy_const_max,
+        usize_legacy_const_min,
+        usize_legacy_fn_max_value,
+        usize_legacy_fn_min_value,
+        usize_legacy_mod,
         va_arg,
         va_copy,
         va_end,
@@ -2327,13 +2340,15 @@ pub mod sym {
     ///
     /// The first few non-negative integers each have a static symbol and therefore
     /// are fast.
-    pub fn integer<N: TryInto<usize> + Copy + ToString>(n: N) -> Symbol {
+    pub fn integer<N: TryInto<usize> + Copy + itoa::Integer>(n: N) -> Symbol {
         if let Result::Ok(idx) = n.try_into() {
             if idx < 10 {
                 return Symbol::new(super::SYMBOL_DIGITS_BASE + idx as u32);
             }
         }
-        Symbol::intern(&n.to_string())
+        let mut buffer = itoa::Buffer::new();
+        let printed = buffer.format(n);
+        Symbol::intern(printed)
     }
 }
 
diff --git a/compiler/rustc_trait_selection/Cargo.toml b/compiler/rustc_trait_selection/Cargo.toml
index 3a58d41fcd0..811eb4c9810 100644
--- a/compiler/rustc_trait_selection/Cargo.toml
+++ b/compiler/rustc_trait_selection/Cargo.toml
@@ -6,7 +6,7 @@ edition = "2021"
 [dependencies]
 # tidy-alphabetical-start
 bitflags = "2.4.1"
-itertools = "0.11.0"
+itertools = "0.12"
 rustc_ast = { path = "../rustc_ast" }
 rustc_ast_ir = { path = "../rustc_ast_ir" }
 rustc_attr = { path = "../rustc_attr" }
diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs
index b09a803e856..f89daf033f6 100644
--- a/compiler/rustc_trait_selection/src/traits/wf.rs
+++ b/compiler/rustc_trait_selection/src/traits/wf.rs
@@ -2,7 +2,9 @@ use crate::infer::InferCtxt;
 use crate::traits;
 use rustc_hir as hir;
 use rustc_hir::lang_items::LangItem;
-use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt};
+use rustc_middle::ty::{
+    self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor,
+};
 use rustc_middle::ty::{GenericArg, GenericArgKind, GenericArgsRef};
 use rustc_span::def_id::{DefId, LocalDefId, CRATE_DEF_ID};
 use rustc_span::{Span, DUMMY_SP};
@@ -535,305 +537,8 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
     /// Pushes all the predicates needed to validate that `ty` is WF into `out`.
     #[instrument(level = "debug", skip(self))]
     fn compute(&mut self, arg: GenericArg<'tcx>) {
-        let mut walker = arg.walk();
-        let param_env = self.param_env;
-        let depth = self.recursion_depth;
-        while let Some(arg) = walker.next() {
-            debug!(?arg, ?self.out);
-            let ty = match arg.unpack() {
-                GenericArgKind::Type(ty) => ty,
-
-                // No WF constraints for lifetimes being present, any outlives
-                // obligations are handled by the parent (e.g. `ty::Ref`).
-                GenericArgKind::Lifetime(_) => continue,
-
-                GenericArgKind::Const(ct) => {
-                    match ct.kind() {
-                        ty::ConstKind::Unevaluated(uv) => {
-                            if !ct.has_escaping_bound_vars() {
-                                let obligations = self.nominal_obligations(uv.def, uv.args);
-                                self.out.extend(obligations);
-
-                                let predicate = ty::Binder::dummy(ty::PredicateKind::Clause(
-                                    ty::ClauseKind::ConstEvaluatable(ct),
-                                ));
-                                let cause = self.cause(traits::WellFormed(None));
-                                self.out.push(traits::Obligation::with_depth(
-                                    self.tcx(),
-                                    cause,
-                                    self.recursion_depth,
-                                    self.param_env,
-                                    predicate,
-                                ));
-                            }
-                        }
-                        ty::ConstKind::Infer(_) => {
-                            let cause = self.cause(traits::WellFormed(None));
-
-                            self.out.push(traits::Obligation::with_depth(
-                                self.tcx(),
-                                cause,
-                                self.recursion_depth,
-                                self.param_env,
-                                ty::Binder::dummy(ty::PredicateKind::Clause(
-                                    ty::ClauseKind::WellFormed(ct.into()),
-                                )),
-                            ));
-                        }
-                        ty::ConstKind::Expr(_) => {
-                            // FIXME(generic_const_exprs): this doesn't verify that given `Expr(N + 1)` the
-                            // trait bound `typeof(N): Add<typeof(1)>` holds. This is currently unnecessary
-                            // as `ConstKind::Expr` is only produced via normalization of `ConstKind::Unevaluated`
-                            // which means that the `DefId` would have been typeck'd elsewhere. However in
-                            // the future we may allow directly lowering to `ConstKind::Expr` in which case
-                            // we would not be proving bounds we should.
-
-                            let predicate = ty::Binder::dummy(ty::PredicateKind::Clause(
-                                ty::ClauseKind::ConstEvaluatable(ct),
-                            ));
-                            let cause = self.cause(traits::WellFormed(None));
-                            self.out.push(traits::Obligation::with_depth(
-                                self.tcx(),
-                                cause,
-                                self.recursion_depth,
-                                self.param_env,
-                                predicate,
-                            ));
-                        }
-
-                        ty::ConstKind::Error(_)
-                        | ty::ConstKind::Param(_)
-                        | ty::ConstKind::Bound(..)
-                        | ty::ConstKind::Placeholder(..) => {
-                            // These variants are trivially WF, so nothing to do here.
-                        }
-                        ty::ConstKind::Value(..) => {
-                            // FIXME: Enforce that values are structurally-matchable.
-                        }
-                    }
-                    continue;
-                }
-            };
-
-            debug!("wf bounds for ty={:?} ty.kind={:#?}", ty, ty.kind());
-
-            match *ty.kind() {
-                ty::Bool
-                | ty::Char
-                | ty::Int(..)
-                | ty::Uint(..)
-                | ty::Float(..)
-                | ty::Error(_)
-                | ty::Str
-                | ty::CoroutineWitness(..)
-                | ty::Never
-                | ty::Param(_)
-                | ty::Bound(..)
-                | ty::Placeholder(..)
-                | ty::Foreign(..) => {
-                    // WfScalar, WfParameter, etc
-                }
-
-                // Can only infer to `ty::Int(_) | ty::Uint(_)`.
-                ty::Infer(ty::IntVar(_)) => {}
-
-                // Can only infer to `ty::Float(_)`.
-                ty::Infer(ty::FloatVar(_)) => {}
-
-                ty::Slice(subty) => {
-                    self.require_sized(subty, traits::SliceOrArrayElem);
-                }
-
-                ty::Array(subty, _) => {
-                    self.require_sized(subty, traits::SliceOrArrayElem);
-                    // Note that we handle the len is implicitly checked while walking `arg`.
-                }
-
-                ty::Tuple(tys) => {
-                    if let Some((_last, rest)) = tys.split_last() {
-                        for &elem in rest {
-                            self.require_sized(elem, traits::TupleElem);
-                        }
-                    }
-                }
-
-                ty::RawPtr(_) => {
-                    // Simple cases that are WF if their type args are WF.
-                }
-
-                ty::Alias(ty::Projection, data) => {
-                    walker.skip_current_subtree(); // Subtree handled by compute_projection.
-                    self.compute_projection(data);
-                }
-                ty::Alias(ty::Inherent, data) => {
-                    walker.skip_current_subtree(); // Subtree handled by compute_inherent_projection.
-                    self.compute_inherent_projection(data);
-                }
-
-                ty::Adt(def, args) => {
-                    // WfNominalType
-                    let obligations = self.nominal_obligations(def.did(), args);
-                    self.out.extend(obligations);
-                }
-
-                ty::FnDef(did, args) => {
-                    let obligations = self.nominal_obligations(did, args);
-                    self.out.extend(obligations);
-                }
-
-                ty::Ref(r, rty, _) => {
-                    // WfReference
-                    if !r.has_escaping_bound_vars() && !rty.has_escaping_bound_vars() {
-                        let cause = self.cause(traits::ReferenceOutlivesReferent(ty));
-                        self.out.push(traits::Obligation::with_depth(
-                            self.tcx(),
-                            cause,
-                            depth,
-                            param_env,
-                            ty::Binder::dummy(ty::PredicateKind::Clause(
-                                ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(rty, r)),
-                            )),
-                        ));
-                    }
-                }
-
-                ty::Coroutine(did, args, ..) => {
-                    // Walk ALL the types in the coroutine: this will
-                    // include the upvar types as well as the yield
-                    // type. Note that this is mildly distinct from
-                    // the closure case, where we have to be careful
-                    // about the signature of the closure. We don't
-                    // have the problem of implied bounds here since
-                    // coroutines don't take arguments.
-                    let obligations = self.nominal_obligations(did, args);
-                    self.out.extend(obligations);
-                }
-
-                ty::Closure(did, args) => {
-                    // Only check the upvar types for WF, not the rest
-                    // of the types within. This is needed because we
-                    // capture the signature and it may not be WF
-                    // without the implied bounds. Consider a closure
-                    // like `|x: &'a T|` -- it may be that `T: 'a` is
-                    // not known to hold in the creator's context (and
-                    // indeed the closure may not be invoked by its
-                    // creator, but rather turned to someone who *can*
-                    // verify that).
-                    //
-                    // The special treatment of closures here really
-                    // ought not to be necessary either; the problem
-                    // is related to #25860 -- there is no way for us
-                    // to express a fn type complete with the implied
-                    // bounds that it is assuming. I think in reality
-                    // the WF rules around fn are a bit messed up, and
-                    // that is the rot problem: `fn(&'a T)` should
-                    // probably always be WF, because it should be
-                    // shorthand for something like `where(T: 'a) {
-                    // fn(&'a T) }`, as discussed in #25860.
-                    walker.skip_current_subtree(); // subtree handled below
-                    // FIXME(eddyb) add the type to `walker` instead of recursing.
-                    self.compute(args.as_closure().tupled_upvars_ty().into());
-                    // Note that we cannot skip the generic types
-                    // types. Normally, within the fn
-                    // body where they are created, the generics will
-                    // always be WF, and outside of that fn body we
-                    // are not directly inspecting closure types
-                    // anyway, except via auto trait matching (which
-                    // only inspects the upvar types).
-                    // But when a closure is part of a type-alias-impl-trait
-                    // then the function that created the defining site may
-                    // have had more bounds available than the type alias
-                    // specifies. This may cause us to have a closure in the
-                    // hidden type that is not actually well formed and
-                    // can cause compiler crashes when the user abuses unsafe
-                    // code to procure such a closure.
-                    // See tests/ui/type-alias-impl-trait/wf_check_closures.rs
-                    let obligations = self.nominal_obligations(did, args);
-                    self.out.extend(obligations);
-                }
-
-                ty::CoroutineClosure(did, args) => {
-                    // See the above comments. The same apply to coroutine-closures.
-                    walker.skip_current_subtree();
-                    self.compute(args.as_coroutine_closure().tupled_upvars_ty().into());
-                    let obligations = self.nominal_obligations(did, args);
-                    self.out.extend(obligations);
-                }
-
-                ty::FnPtr(_) => {
-                    // let the loop iterate into the argument/return
-                    // types appearing in the fn signature
-                }
-
-                ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) => {
-                    // All of the requirements on type parameters
-                    // have already been checked for `impl Trait` in
-                    // return position. We do need to check type-alias-impl-trait though.
-                    if self.tcx().is_type_alias_impl_trait(def_id) {
-                        let obligations = self.nominal_obligations(def_id, args);
-                        self.out.extend(obligations);
-                    }
-                }
-
-                ty::Alias(ty::Weak, ty::AliasTy { def_id, args, .. }) => {
-                    let obligations = self.nominal_obligations(def_id, args);
-                    self.out.extend(obligations);
-                }
-
-                ty::Dynamic(data, r, _) => {
-                    // WfObject
-                    //
-                    // Here, we defer WF checking due to higher-ranked
-                    // regions. This is perhaps not ideal.
-                    self.from_object_ty(ty, data, r);
-
-                    // FIXME(#27579) RFC also considers adding trait
-                    // obligations that don't refer to Self and
-                    // checking those
-
-                    let defer_to_coercion = self.tcx().features().object_safe_for_dispatch;
-
-                    if !defer_to_coercion {
-                        if let Some(principal) = data.principal_def_id() {
-                            self.out.push(traits::Obligation::with_depth(
-                                self.tcx(),
-                                self.cause(traits::WellFormed(None)),
-                                depth,
-                                param_env,
-                                ty::Binder::dummy(ty::PredicateKind::ObjectSafe(principal)),
-                            ));
-                        }
-                    }
-                }
-
-                // Inference variables are the complicated case, since we don't
-                // know what type they are. We do two things:
-                //
-                // 1. Check if they have been resolved, and if so proceed with
-                //    THAT type.
-                // 2. If not, we've at least simplified things (e.g., we went
-                //    from `Vec<$0>: WF` to `$0: WF`), so we can
-                //    register a pending obligation and keep
-                //    moving. (Goal is that an "inductive hypothesis"
-                //    is satisfied to ensure termination.)
-                // See also the comment on `fn obligations`, describing "livelock"
-                // prevention, which happens before this can be reached.
-                ty::Infer(_) => {
-                    let cause = self.cause(traits::WellFormed(None));
-                    self.out.push(traits::Obligation::with_depth(
-                        self.tcx(),
-                        cause,
-                        self.recursion_depth,
-                        param_env,
-                        ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(
-                            ty.into(),
-                        ))),
-                    ));
-                }
-            }
-
-            debug!(?self.out);
-        }
+        arg.visit_with(self);
+        debug!(?self.out);
     }
 
     #[instrument(level = "debug", skip(self))]
@@ -933,6 +638,302 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
     }
 }
 
+impl<'a, 'tcx> TypeVisitor<TyCtxt<'tcx>> for WfPredicates<'a, 'tcx> {
+    type Result = ();
+
+    fn visit_ty(&mut self, t: <TyCtxt<'tcx> as ty::Interner>::Ty) -> Self::Result {
+        debug!("wf bounds for t={:?} t.kind={:#?}", t, t.kind());
+
+        match *t.kind() {
+            ty::Bool
+            | ty::Char
+            | ty::Int(..)
+            | ty::Uint(..)
+            | ty::Float(..)
+            | ty::Error(_)
+            | ty::Str
+            | ty::CoroutineWitness(..)
+            | ty::Never
+            | ty::Param(_)
+            | ty::Bound(..)
+            | ty::Placeholder(..)
+            | ty::Foreign(..) => {
+                // WfScalar, WfParameter, etc
+            }
+
+            // Can only infer to `ty::Int(_) | ty::Uint(_)`.
+            ty::Infer(ty::IntVar(_)) => {}
+
+            // Can only infer to `ty::Float(_)`.
+            ty::Infer(ty::FloatVar(_)) => {}
+
+            ty::Slice(subty) => {
+                self.require_sized(subty, traits::SliceOrArrayElem);
+            }
+
+            ty::Array(subty, _) => {
+                self.require_sized(subty, traits::SliceOrArrayElem);
+                // Note that we handle the len is implicitly checked while walking `arg`.
+            }
+
+            ty::Tuple(tys) => {
+                if let Some((_last, rest)) = tys.split_last() {
+                    for &elem in rest {
+                        self.require_sized(elem, traits::TupleElem);
+                    }
+                }
+            }
+
+            ty::RawPtr(_) => {
+                // Simple cases that are WF if their type args are WF.
+            }
+
+            ty::Alias(ty::Projection, data) => {
+                self.compute_projection(data);
+                return; // Subtree handled by compute_projection.
+            }
+            ty::Alias(ty::Inherent, data) => {
+                self.compute_inherent_projection(data);
+                return; // Subtree handled by compute_inherent_projection.
+            }
+
+            ty::Adt(def, args) => {
+                // WfNominalType
+                let obligations = self.nominal_obligations(def.did(), args);
+                self.out.extend(obligations);
+            }
+
+            ty::FnDef(did, args) => {
+                let obligations = self.nominal_obligations(did, args);
+                self.out.extend(obligations);
+            }
+
+            ty::Ref(r, rty, _) => {
+                // WfReference
+                if !r.has_escaping_bound_vars() && !rty.has_escaping_bound_vars() {
+                    let cause = self.cause(traits::ReferenceOutlivesReferent(t));
+                    self.out.push(traits::Obligation::with_depth(
+                        self.tcx(),
+                        cause,
+                        self.recursion_depth,
+                        self.param_env,
+                        ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(
+                            ty::OutlivesPredicate(rty, r),
+                        ))),
+                    ));
+                }
+            }
+
+            ty::Coroutine(did, args, ..) => {
+                // Walk ALL the types in the coroutine: this will
+                // include the upvar types as well as the yield
+                // type. Note that this is mildly distinct from
+                // the closure case, where we have to be careful
+                // about the signature of the closure. We don't
+                // have the problem of implied bounds here since
+                // coroutines don't take arguments.
+                let obligations = self.nominal_obligations(did, args);
+                self.out.extend(obligations);
+            }
+
+            ty::Closure(did, args) => {
+                // Note that we cannot skip the generic types
+                // types. Normally, within the fn
+                // body where they are created, the generics will
+                // always be WF, and outside of that fn body we
+                // are not directly inspecting closure types
+                // anyway, except via auto trait matching (which
+                // only inspects the upvar types).
+                // But when a closure is part of a type-alias-impl-trait
+                // then the function that created the defining site may
+                // have had more bounds available than the type alias
+                // specifies. This may cause us to have a closure in the
+                // hidden type that is not actually well formed and
+                // can cause compiler crashes when the user abuses unsafe
+                // code to procure such a closure.
+                // See tests/ui/type-alias-impl-trait/wf_check_closures.rs
+                let obligations = self.nominal_obligations(did, args);
+                self.out.extend(obligations);
+                // Only check the upvar types for WF, not the rest
+                // of the types within. This is needed because we
+                // capture the signature and it may not be WF
+                // without the implied bounds. Consider a closure
+                // like `|x: &'a T|` -- it may be that `T: 'a` is
+                // not known to hold in the creator's context (and
+                // indeed the closure may not be invoked by its
+                // creator, but rather turned to someone who *can*
+                // verify that).
+                //
+                // The special treatment of closures here really
+                // ought not to be necessary either; the problem
+                // is related to #25860 -- there is no way for us
+                // to express a fn type complete with the implied
+                // bounds that it is assuming. I think in reality
+                // the WF rules around fn are a bit messed up, and
+                // that is the rot problem: `fn(&'a T)` should
+                // probably always be WF, because it should be
+                // shorthand for something like `where(T: 'a) {
+                // fn(&'a T) }`, as discussed in #25860.
+                let upvars = args.as_closure().tupled_upvars_ty();
+                return upvars.visit_with(self);
+            }
+
+            ty::CoroutineClosure(did, args) => {
+                // See the above comments. The same apply to coroutine-closures.
+                let obligations = self.nominal_obligations(did, args);
+                self.out.extend(obligations);
+                let upvars = args.as_coroutine_closure().tupled_upvars_ty();
+                return upvars.visit_with(self);
+            }
+
+            ty::FnPtr(_) => {
+                // Let the visitor iterate into the argument/return
+                // types appearing in the fn signature.
+            }
+
+            ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) => {
+                // All of the requirements on type parameters
+                // have already been checked for `impl Trait` in
+                // return position. We do need to check type-alias-impl-trait though.
+                if self.tcx().is_type_alias_impl_trait(def_id) {
+                    let obligations = self.nominal_obligations(def_id, args);
+                    self.out.extend(obligations);
+                }
+            }
+
+            ty::Alias(ty::Weak, ty::AliasTy { def_id, args, .. }) => {
+                let obligations = self.nominal_obligations(def_id, args);
+                self.out.extend(obligations);
+            }
+
+            ty::Dynamic(data, r, _) => {
+                // WfObject
+                //
+                // Here, we defer WF checking due to higher-ranked
+                // regions. This is perhaps not ideal.
+                self.from_object_ty(t, data, r);
+
+                // FIXME(#27579) RFC also considers adding trait
+                // obligations that don't refer to Self and
+                // checking those
+
+                let defer_to_coercion = self.tcx().features().object_safe_for_dispatch;
+
+                if !defer_to_coercion {
+                    if let Some(principal) = data.principal_def_id() {
+                        self.out.push(traits::Obligation::with_depth(
+                            self.tcx(),
+                            self.cause(traits::WellFormed(None)),
+                            self.recursion_depth,
+                            self.param_env,
+                            ty::Binder::dummy(ty::PredicateKind::ObjectSafe(principal)),
+                        ));
+                    }
+                }
+            }
+
+            // Inference variables are the complicated case, since we don't
+            // know what type they are. We do two things:
+            //
+            // 1. Check if they have been resolved, and if so proceed with
+            //    THAT type.
+            // 2. If not, we've at least simplified things (e.g., we went
+            //    from `Vec<$0>: WF` to `$0: WF`), so we can
+            //    register a pending obligation and keep
+            //    moving. (Goal is that an "inductive hypothesis"
+            //    is satisfied to ensure termination.)
+            // See also the comment on `fn obligations`, describing "livelock"
+            // prevention, which happens before this can be reached.
+            ty::Infer(_) => {
+                let cause = self.cause(traits::WellFormed(None));
+                self.out.push(traits::Obligation::with_depth(
+                    self.tcx(),
+                    cause,
+                    self.recursion_depth,
+                    self.param_env,
+                    ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(
+                        t.into(),
+                    ))),
+                ));
+            }
+        }
+
+        t.super_visit_with(self)
+    }
+
+    fn visit_const(&mut self, c: <TyCtxt<'tcx> as ty::Interner>::Const) -> Self::Result {
+        match c.kind() {
+            ty::ConstKind::Unevaluated(uv) => {
+                if !c.has_escaping_bound_vars() {
+                    let obligations = self.nominal_obligations(uv.def, uv.args);
+                    self.out.extend(obligations);
+
+                    let predicate = ty::Binder::dummy(ty::PredicateKind::Clause(
+                        ty::ClauseKind::ConstEvaluatable(c),
+                    ));
+                    let cause = self.cause(traits::WellFormed(None));
+                    self.out.push(traits::Obligation::with_depth(
+                        self.tcx(),
+                        cause,
+                        self.recursion_depth,
+                        self.param_env,
+                        predicate,
+                    ));
+                }
+            }
+            ty::ConstKind::Infer(_) => {
+                let cause = self.cause(traits::WellFormed(None));
+
+                self.out.push(traits::Obligation::with_depth(
+                    self.tcx(),
+                    cause,
+                    self.recursion_depth,
+                    self.param_env,
+                    ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(
+                        c.into(),
+                    ))),
+                ));
+            }
+            ty::ConstKind::Expr(_) => {
+                // FIXME(generic_const_exprs): this doesn't verify that given `Expr(N + 1)` the
+                // trait bound `typeof(N): Add<typeof(1)>` holds. This is currently unnecessary
+                // as `ConstKind::Expr` is only produced via normalization of `ConstKind::Unevaluated`
+                // which means that the `DefId` would have been typeck'd elsewhere. However in
+                // the future we may allow directly lowering to `ConstKind::Expr` in which case
+                // we would not be proving bounds we should.
+
+                let predicate = ty::Binder::dummy(ty::PredicateKind::Clause(
+                    ty::ClauseKind::ConstEvaluatable(c),
+                ));
+                let cause = self.cause(traits::WellFormed(None));
+                self.out.push(traits::Obligation::with_depth(
+                    self.tcx(),
+                    cause,
+                    self.recursion_depth,
+                    self.param_env,
+                    predicate,
+                ));
+            }
+
+            ty::ConstKind::Error(_)
+            | ty::ConstKind::Param(_)
+            | ty::ConstKind::Bound(..)
+            | ty::ConstKind::Placeholder(..) => {
+                // These variants are trivially WF, so nothing to do here.
+            }
+            ty::ConstKind::Value(..) => {
+                // FIXME: Enforce that values are structurally-matchable.
+            }
+        }
+
+        c.super_visit_with(self)
+    }
+
+    fn visit_predicate(&mut self, _p: <TyCtxt<'tcx> as ty::Interner>::Predicate) -> Self::Result {
+        bug!("predicate should not be checked for well-formedness");
+    }
+}
+
 /// Given an object type like `SomeTrait + Send`, computes the lifetime
 /// bounds that must hold on the elided self type. These are derived
 /// from the declarations of `SomeTrait`, `Send`, and friends -- if
diff --git a/compiler/rustc_transmute/Cargo.toml b/compiler/rustc_transmute/Cargo.toml
index 3d0ad31747f..79939d62a51 100644
--- a/compiler/rustc_transmute/Cargo.toml
+++ b/compiler/rustc_transmute/Cargo.toml
@@ -29,5 +29,5 @@ rustc = [
 
 [dev-dependencies]
 # tidy-alphabetical-start
-itertools = "0.11"
+itertools = "0.12"
 # tidy-alphabetical-end
diff --git a/compiler/rustc_ty_utils/Cargo.toml b/compiler/rustc_ty_utils/Cargo.toml
index 2a30bd5d539..01d5251bfa0 100644
--- a/compiler/rustc_ty_utils/Cargo.toml
+++ b/compiler/rustc_ty_utils/Cargo.toml
@@ -5,7 +5,7 @@ edition = "2021"
 
 [dependencies]
 # tidy-alphabetical-start
-itertools = "0.11"
+itertools = "0.12"
 rustc_ast_ir = { path = "../rustc_ast_ir" }
 rustc_data_structures = { path = "../rustc_data_structures" }
 rustc_errors = { path = "../rustc_errors" }
diff --git a/compiler/rustc_ty_utils/src/instance.rs b/compiler/rustc_ty_utils/src/instance.rs
index 381681fb1f4..2816bcc888b 100644
--- a/compiler/rustc_ty_utils/src/instance.rs
+++ b/compiler/rustc_ty_utils/src/instance.rs
@@ -7,6 +7,7 @@ use rustc_middle::ty::GenericArgsRef;
 use rustc_middle::ty::{self, Instance, TyCtxt, TypeVisitableExt};
 use rustc_span::sym;
 use rustc_trait_selection::traits;
+use rustc_type_ir::ClosureKind;
 use traits::{translate_args, Reveal};
 
 use crate::errors::UnexpectedFnPtrAssociatedItem;
@@ -296,23 +297,25 @@ fn resolve_associated_item<'tcx>(
             {
                 match *rcvr_args.type_at(0).kind() {
                     ty::CoroutineClosure(coroutine_closure_def_id, args) => {
-                        // If we're computing `AsyncFnOnce`/`AsyncFnMut` for a by-ref closure,
-                        // or `AsyncFnOnce` for a by-mut closure, then construct a new body that
-                        // has the right return types.
-                        //
-                        // Specifically, `AsyncFnMut` for a by-ref coroutine-closure just needs
-                        // to have its input and output types fixed (`&mut self` and returning
-                        // `i16` coroutine kind).
-                        if target_kind > args.as_coroutine_closure().kind() {
-                            Some(Instance {
-                                def: ty::InstanceDef::ConstructCoroutineInClosureShim {
-                                    coroutine_closure_def_id,
-                                    target_kind,
-                                },
-                                args,
-                            })
-                        } else {
-                            Some(Instance::new(coroutine_closure_def_id, args))
+                        match (target_kind, args.as_coroutine_closure().kind()) {
+                            (ClosureKind::FnOnce | ClosureKind::FnMut, ClosureKind::Fn)
+                            | (ClosureKind::FnOnce, ClosureKind::FnMut) => {
+                                // If we're computing `AsyncFnOnce`/`AsyncFnMut` for a by-ref closure,
+                                // or `AsyncFnOnce` for a by-mut closure, then construct a new body that
+                                // has the right return types.
+                                //
+                                // Specifically, `AsyncFnMut` for a by-ref coroutine-closure just needs
+                                // to have its input and output types fixed (`&mut self` and returning
+                                // `i16` coroutine kind).
+                                Some(Instance {
+                                    def: ty::InstanceDef::ConstructCoroutineInClosureShim {
+                                        coroutine_closure_def_id,
+                                        target_kind,
+                                    },
+                                    args,
+                                })
+                            }
+                            _ => Some(Instance::new(coroutine_closure_def_id, args)),
                         }
                     }
                     ty::Closure(closure_def_id, args) => {
diff --git a/compiler/rustc_type_ir/src/lib.rs b/compiler/rustc_type_ir/src/lib.rs
index 2ded1b956e5..c01baa58ae7 100644
--- a/compiler/rustc_type_ir/src/lib.rs
+++ b/compiler/rustc_type_ir/src/lib.rs
@@ -369,12 +369,9 @@ rustc_index::newtype_index! {
 ///
 /// You can get the environment type of a closure using
 /// `tcx.closure_env_ty()`.
-#[derive(Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Hash, Debug)]
+#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
 #[cfg_attr(feature = "nightly", derive(Encodable, Decodable, HashStable_NoContext))]
 pub enum ClosureKind {
-    // Warning: Ordering is significant here! The ordering is chosen
-    // because the trait Fn is a subtrait of FnMut and so in turn, and
-    // hence we order it so that Fn < FnMut < FnOnce.
     Fn,
     FnMut,
     FnOnce,
@@ -394,8 +391,15 @@ impl ClosureKind {
 
     /// Returns `true` if a type that impls this closure kind
     /// must also implement `other`.
+    #[rustfmt::skip]
     pub fn extends(self, other: ClosureKind) -> bool {
-        self <= other
+        use ClosureKind::*;
+        match (self, other) {
+              (Fn, Fn | FnMut | FnOnce)
+            | (FnMut,   FnMut | FnOnce)
+            | (FnOnce,          FnOnce) => true,
+            _ => false,
+        }
     }
 }
 
diff --git a/compiler/stable_mir/src/mir/body.rs b/compiler/stable_mir/src/mir/body.rs
index be727f024c6..ae8e71bb950 100644
--- a/compiler/stable_mir/src/mir/body.rs
+++ b/compiler/stable_mir/src/mir/body.rs
@@ -639,7 +639,7 @@ impl Rvalue {
             Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(..), _) => {
                 Ok(Ty::usize_ty())
             }
-            Rvalue::NullaryOp(NullOp::DebugAssertions, _) => Ok(Ty::bool_ty()),
+            Rvalue::NullaryOp(NullOp::UbCheck(_), _) => Ok(Ty::bool_ty()),
             Rvalue::Aggregate(ak, ops) => match *ak {
                 AggregateKind::Array(ty) => Ty::try_new_array(ty, ops.len() as u64),
                 AggregateKind::Tuple => Ok(Ty::new_tuple(
@@ -1007,7 +1007,13 @@ pub enum NullOp {
     /// Returns the offset of a field.
     OffsetOf(Vec<(VariantIdx, FieldIdx)>),
     /// cfg!(debug_assertions), but at codegen time
-    DebugAssertions,
+    UbCheck(UbKind),
+}
+
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub enum UbKind {
+    LanguageUb,
+    LibraryUb,
 }
 
 impl Operand {