about summary refs log tree commit diff
path: root/library/core
diff options
context:
space:
mode:
authorRalf Jung <post@ralfj.de>2024-03-17 10:12:25 +0100
committerRalf Jung <post@ralfj.de>2024-03-23 18:44:17 +0100
commit987ef4c9221e792614a015a39b78f7d6a156c2f2 (patch)
tree44985e29db29ff6466fa01ca440416892cf7c49b /library/core
parentd6eb0f5a09247ff8443e68b4d17e0350c74bafb1 (diff)
downloadrust-987ef4c9221e792614a015a39b78f7d6a156c2f2.tar.gz
rust-987ef4c9221e792614a015a39b78f7d6a156c2f2.zip
move assert_unsafe_preconditions to its own file
These macros and functions are not intrinsics, after all.
Diffstat (limited to 'library/core')
-rw-r--r--library/core/src/char/convert.rs2
-rw-r--r--library/core/src/hint.rs5
-rw-r--r--library/core/src/intrinsics.rs145
-rw-r--r--library/core/src/lib.rs1
-rw-r--r--library/core/src/num/nonzero.rs5
-rw-r--r--library/core/src/ops/index_range.rs5
-rw-r--r--library/core/src/ptr/alignment.rs4
-rw-r--r--library/core/src/ptr/const_ptr.rs2
-rw-r--r--library/core/src/ptr/mod.rs33
-rw-r--r--library/core/src/ptr/non_null.rs2
-rw-r--r--library/core/src/slice/index.rs2
-rw-r--r--library/core/src/slice/mod.rs2
-rw-r--r--library/core/src/slice/raw.rs16
-rw-r--r--library/core/src/str/traits.rs2
-rw-r--r--library/core/src/ub_checks.rs131
15 files changed, 182 insertions, 175 deletions
diff --git a/library/core/src/char/convert.rs b/library/core/src/char/convert.rs
index 70b9e89f9ea..8f612929110 100644
--- a/library/core/src/char/convert.rs
+++ b/library/core/src/char/convert.rs
@@ -4,9 +4,9 @@ use crate::char::TryFromCharError;
 use crate::convert::TryFrom;
 use crate::error::Error;
 use crate::fmt;
-use crate::intrinsics::assert_unsafe_precondition;
 use crate::mem::transmute;
 use crate::str::FromStr;
+use crate::ub_checks::assert_unsafe_precondition;
 
 /// Converts a `u32` to a `char`. See [`char::from_u32`].
 #[must_use]
diff --git a/library/core/src/hint.rs b/library/core/src/hint.rs
index ffe059bf65c..b27d0db4619 100644
--- a/library/core/src/hint.rs
+++ b/library/core/src/hint.rs
@@ -4,6 +4,7 @@
 //! Hints may be compile time or runtime.
 
 use crate::intrinsics;
+use crate::ub_checks;
 
 /// Informs the compiler that the site which is calling this function is not
 /// reachable, possibly enabling further optimizations.
@@ -98,7 +99,7 @@ use crate::intrinsics;
 #[rustc_const_stable(feature = "const_unreachable_unchecked", since = "1.57.0")]
 #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
 pub const unsafe fn unreachable_unchecked() -> ! {
-    intrinsics::assert_unsafe_precondition!(
+    ub_checks::assert_unsafe_precondition!(
         check_language_ub,
         "hint::unreachable_unchecked must never be reached",
         () => false
@@ -148,7 +149,7 @@ pub const unsafe fn unreachable_unchecked() -> ! {
 pub const unsafe fn assert_unchecked(cond: bool) {
     // SAFETY: The caller promised `cond` is true.
     unsafe {
-        intrinsics::assert_unsafe_precondition!(
+        ub_checks::assert_unsafe_precondition!(
             check_language_ub,
             "hint::assert_unchecked must never be called when the condition is false",
             (cond: bool = cond) => cond,
diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs
index a0d5e220b86..af83a8d8fa4 100644
--- a/library/core/src/intrinsics.rs
+++ b/library/core/src/intrinsics.rs
@@ -67,6 +67,7 @@ use crate::marker::DiscriminantKind;
 use crate::marker::Tuple;
 use crate::mem::align_of;
 use crate::ptr;
+use crate::ub_checks;
 
 pub mod mir;
 pub mod simd;
@@ -2755,132 +2756,6 @@ pub unsafe fn vtable_align(_ptr: *const ()) -> usize {
 // (`transmute` also falls into this category, but it cannot be wrapped due to the
 // check that `T` and `U` have the same size.)
 
-/// Check that the preconditions of an unsafe function are followed. The check is enabled at
-/// runtime if debug assertions are enabled when the caller is monomorphized. In const-eval/Miri
-/// checks implemented with this macro for language UB are always ignored.
-///
-/// This macro should be called as
-/// `assert_unsafe_precondition!(check_{library,lang}_ub, "message", (ident: type = expr, ident: type = expr) => check_expr)`
-/// where each `expr` will be evaluated and passed in as function argument `ident: type`. Then all
-/// those arguments are passed to a function with the body `check_expr`.
-/// Pick `check_language_ub` when this is guarding a violation of language UB, i.e., immediate UB
-/// according to the Rust Abstract Machine. Pick `check_library_ub` when this is guarding a violation
-/// of a documented library precondition that does not *immediately* lead to language UB.
-///
-/// If `check_library_ub` is used but the check is actually guarding language UB, the check will
-/// slow down const-eval/Miri and we'll get the panic message instead of the interpreter's nice
-/// diagnostic, but our ability to detect UB is unchanged.
-/// But if `check_language_ub` is used when the check is actually for library UB, the check is
-/// omitted in const-eval/Miri and thus if we eventually execute language UB which relies on the
-/// library UB, the backtrace Miri reports may be far removed from original cause.
-///
-/// These checks are behind a condition which is evaluated at codegen time, not expansion time like
-/// [`debug_assert`]. This means that a standard library built with optimizations and debug
-/// assertions disabled will have these checks optimized out of its monomorphizations, but if a
-/// caller of the standard library has debug assertions enabled and monomorphizes an expansion of
-/// this macro, that monomorphization will contain the check.
-///
-/// Since these checks cannot be optimized out in MIR, some care must be taken in both call and
-/// implementation to mitigate their compile-time overhead. Calls to this macro always expand to
-/// this structure:
-/// ```ignore (pseudocode)
-/// if ::core::intrinsics::check_language_ub() {
-///     precondition_check(args)
-/// }
-/// ```
-/// where `precondition_check` is monomorphic with the attributes `#[rustc_nounwind]`, `#[inline]` and
-/// `#[rustc_no_mir_inline]`. This combination of attributes ensures that the actual check logic is
-/// compiled only once and generates a minimal amount of IR because the check cannot be inlined in
-/// MIR, but *can* be inlined and fully optimized by a codegen backend.
-///
-/// Callers should avoid introducing any other `let` bindings or any code outside this macro in
-/// order to call it. Since the precompiled standard library is built with full debuginfo and these
-/// variables cannot be optimized out in MIR, an innocent-looking `let` can produce enough
-/// debuginfo to have a measurable compile-time impact on debug builds.
-#[allow_internal_unstable(ub_checks)] // permit this to be called in stably-const fn
-macro_rules! assert_unsafe_precondition {
-    ($kind:ident, $message:expr, ($($name:ident:$ty:ty = $arg:expr),*$(,)?) => $e:expr $(,)?) => {
-        {
-            // This check is inlineable, but not by the MIR inliner.
-            // The reason for this is that the MIR inliner is in an exceptionally bad position
-            // to think about whether or not to inline this. In MIR, this call is gated behind `debug_assertions`,
-            // which will codegen to `false` in release builds. Inlining the check would be wasted work in that case and
-            // would be bad for compile times.
-            //
-            // LLVM on the other hand sees the constant branch, so if it's `false`, it can immediately delete it without
-            // inlining the check. If it's `true`, it can inline it and get significantly better performance.
-            #[rustc_no_mir_inline]
-            #[inline]
-            #[rustc_nounwind]
-            #[rustc_const_unstable(feature = "ub_checks", issue = "none")]
-            const fn precondition_check($($name:$ty),*) {
-                if !$e {
-                    ::core::panicking::panic_nounwind(
-                        concat!("unsafe precondition(s) violated: ", $message)
-                    );
-                }
-            }
-
-            if ::core::intrinsics::$kind() {
-                precondition_check($($arg,)*);
-            }
-        }
-    };
-}
-pub(crate) use assert_unsafe_precondition;
-
-/// Checks whether `ptr` is properly aligned with respect to
-/// `align_of::<T>()`.
-///
-/// In `const` this is approximate and can fail spuriously. It is primarily intended
-/// for `assert_unsafe_precondition!` with `check_language_ub`, in which case the
-/// check is anyway not executed in `const`.
-#[inline]
-pub(crate) const fn is_aligned_and_not_null(ptr: *const (), align: usize) -> bool {
-    !ptr.is_null() && ptr.is_aligned_to(align)
-}
-
-#[inline]
-pub(crate) const fn is_valid_allocation_size(size: usize, len: usize) -> bool {
-    let max_len = if size == 0 { usize::MAX } else { isize::MAX as usize / size };
-    len <= max_len
-}
-
-/// Checks whether the regions of memory starting at `src` and `dst` of size
-/// `count * size` do *not* overlap.
-///
-/// Note that in const-eval this function just returns `true` and therefore must
-/// only be used with `assert_unsafe_precondition!`, similar to `is_aligned_and_not_null`.
-#[inline]
-pub(crate) const fn is_nonoverlapping(
-    src: *const (),
-    dst: *const (),
-    size: usize,
-    count: usize,
-) -> bool {
-    #[inline]
-    fn runtime(src: *const (), dst: *const (), size: usize, count: usize) -> bool {
-        let src_usize = src.addr();
-        let dst_usize = dst.addr();
-        let Some(size) = size.checked_mul(count) else {
-            crate::panicking::panic_nounwind(
-                "is_nonoverlapping: `size_of::<T>() * count` overflows a usize",
-            )
-        };
-        let diff = src_usize.abs_diff(dst_usize);
-        // If the absolute distance between the ptrs is at least as big as the size of the buffer,
-        // they do not overlap.
-        diff >= size
-    }
-
-    #[inline]
-    const fn comptime(_: *const (), _: *const (), _: usize, _: usize) -> bool {
-        true
-    }
-
-    const_eval_select((src, dst, size, count), comptime, runtime)
-}
-
 /// Copies `count * size_of::<T>()` bytes from `src` to `dst`. The source
 /// and destination must *not* overlap.
 ///
@@ -2979,7 +2854,7 @@ pub const unsafe fn copy_nonoverlapping<T>(src: *const T, dst: *mut T, count: us
         pub fn copy_nonoverlapping<T>(src: *const T, dst: *mut T, count: usize);
     }
 
-    assert_unsafe_precondition!(
+    ub_checks::assert_unsafe_precondition!(
         check_language_ub,
         "ptr::copy_nonoverlapping requires that both pointer arguments are aligned and non-null \
         and the specified memory ranges do not overlap",
@@ -2990,9 +2865,9 @@ pub const unsafe fn copy_nonoverlapping<T>(src: *const T, dst: *mut T, count: us
             align: usize = align_of::<T>(),
             count: usize = count,
         ) =>
-        is_aligned_and_not_null(src, align)
-            && is_aligned_and_not_null(dst, align)
-            && is_nonoverlapping(src, dst, size, count)
+        ub_checks::is_aligned_and_not_null(src, align)
+            && ub_checks::is_aligned_and_not_null(dst, align)
+            && ub_checks::is_nonoverlapping(src, dst, size, count)
     );
 
     // SAFETY: the safety contract for `copy_nonoverlapping` must be
@@ -3083,7 +2958,7 @@ pub const unsafe fn copy<T>(src: *const T, dst: *mut T, count: usize) {
 
     // SAFETY: the safety contract for `copy` must be upheld by the caller.
     unsafe {
-        assert_unsafe_precondition!(
+        ub_checks::assert_unsafe_precondition!(
             check_language_ub,
             "ptr::copy_nonoverlapping requires that both pointer arguments are aligned and non-null \
             and the specified memory ranges do not overlap",
@@ -3092,8 +2967,8 @@ pub const unsafe fn copy<T>(src: *const T, dst: *mut T, count: usize) {
                 dst: *mut () = dst as *mut (),
                 align: usize = align_of::<T>(),
             ) =>
-            is_aligned_and_not_null(src, align)
-                && is_aligned_and_not_null(dst, align)
+            ub_checks::is_aligned_and_not_null(src, align)
+                && ub_checks::is_aligned_and_not_null(dst, align)
         );
         copy(src, dst, count)
     }
@@ -3164,13 +3039,13 @@ pub const unsafe fn write_bytes<T>(dst: *mut T, val: u8, count: usize) {
 
     // SAFETY: the safety contract for `write_bytes` must be upheld by the caller.
     unsafe {
-        assert_unsafe_precondition!(
+        ub_checks::assert_unsafe_precondition!(
             check_language_ub,
             "ptr::write_bytes requires that the destination pointer is aligned and non-null",
             (
                 addr: *const () = dst as *const (),
                 align: usize = align_of::<T>(),
-            ) => is_aligned_and_not_null(addr, align)
+            ) => ub_checks::is_aligned_and_not_null(addr, align)
         );
         write_bytes(dst, val, count)
     }
diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs
index ded8f4ba841..8f292bf1551 100644
--- a/library/core/src/lib.rs
+++ b/library/core/src/lib.rs
@@ -367,6 +367,7 @@ pub mod hint;
 pub mod intrinsics;
 pub mod mem;
 pub mod ptr;
+mod ub_checks;
 
 /* Core language traits */
 
diff --git a/library/core/src/num/nonzero.rs b/library/core/src/num/nonzero.rs
index a8f637280df..c65ffbb98f2 100644
--- a/library/core/src/num/nonzero.rs
+++ b/library/core/src/num/nonzero.rs
@@ -9,6 +9,7 @@ use crate::ops::{BitOr, BitOrAssign, Div, DivAssign, Neg, Rem, RemAssign};
 use crate::panic::{RefUnwindSafe, UnwindSafe};
 use crate::ptr;
 use crate::str::FromStr;
+use crate::ub_checks;
 
 use super::from_str_radix;
 use super::{IntErrorKind, ParseIntError};
@@ -369,7 +370,7 @@ where
             None => {
                 // SAFETY: The caller guarantees that `n` is non-zero, so this is unreachable.
                 unsafe {
-                    intrinsics::assert_unsafe_precondition!(
+                    ub_checks::assert_unsafe_precondition!(
                         check_language_ub,
                         "NonZero::new_unchecked requires the argument to be non-zero",
                         () => false,
@@ -409,7 +410,7 @@ where
             None => {
                 // SAFETY: The caller guarantees that `n` references a value that is non-zero, so this is unreachable.
                 unsafe {
-                    intrinsics::assert_unsafe_precondition!(
+                    ub_checks::assert_unsafe_precondition!(
                         check_library_ub,
                         "NonZero::from_mut_unchecked requires the argument to dereference as non-zero",
                         () => false,
diff --git a/library/core/src/ops/index_range.rs b/library/core/src/ops/index_range.rs
index b28d88fa5bf..65bda9177c7 100644
--- a/library/core/src/ops/index_range.rs
+++ b/library/core/src/ops/index_range.rs
@@ -1,6 +1,7 @@
-use crate::intrinsics::{assert_unsafe_precondition, unchecked_add, unchecked_sub};
+use crate::intrinsics::{unchecked_add, unchecked_sub};
 use crate::iter::{FusedIterator, TrustedLen};
 use crate::num::NonZero;
+use crate::ub_checks;
 
 /// Like a `Range<usize>`, but with a safety invariant that `start <= end`.
 ///
@@ -19,7 +20,7 @@ impl IndexRange {
     /// - `start <= end`
     #[inline]
     pub const unsafe fn new_unchecked(start: usize, end: usize) -> Self {
-        assert_unsafe_precondition!(
+        ub_checks::assert_unsafe_precondition!(
             check_library_ub,
             "IndexRange::new_unchecked requires `start <= end`",
             (start: usize = start, end: usize = end) => start <= end,
diff --git a/library/core/src/ptr/alignment.rs b/library/core/src/ptr/alignment.rs
index 8f44b7eb7c2..bc84fb5ccb0 100644
--- a/library/core/src/ptr/alignment.rs
+++ b/library/core/src/ptr/alignment.rs
@@ -1,7 +1,7 @@
 use crate::convert::{TryFrom, TryInto};
-#[cfg(debug_assertions)]
-use crate::intrinsics::assert_unsafe_precondition;
 use crate::num::NonZero;
+#[cfg(debug_assertions)]
+use crate::ub_checks::assert_unsafe_precondition;
 use crate::{cmp, fmt, hash, mem, num};
 
 /// A type storing a `usize` which is a power of two, and thus
diff --git a/library/core/src/ptr/const_ptr.rs b/library/core/src/ptr/const_ptr.rs
index 69c61602073..a6c00ff28d4 100644
--- a/library/core/src/ptr/const_ptr.rs
+++ b/library/core/src/ptr/const_ptr.rs
@@ -818,7 +818,7 @@ impl<T: ?Sized> *const T {
             intrinsics::const_eval_select((this, origin), comptime, runtime)
         }
 
-        assert_unsafe_precondition!(
+        ub_checks::assert_unsafe_precondition!(
             check_language_ub,
             "ptr::sub_ptr requires `self >= origin`",
             (
diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs
index 0662dfe9a15..56378b437e7 100644
--- a/library/core/src/ptr/mod.rs
+++ b/library/core/src/ptr/mod.rs
@@ -388,10 +388,9 @@
 use crate::cmp::Ordering;
 use crate::fmt;
 use crate::hash;
-use crate::intrinsics::{
-    self, assert_unsafe_precondition, is_aligned_and_not_null, is_nonoverlapping,
-};
+use crate::intrinsics;
 use crate::marker::FnPtr;
+use crate::ub_checks;
 
 use crate::mem::{self, align_of, size_of, MaybeUninit};
 
@@ -1019,7 +1018,7 @@ pub const unsafe fn swap_nonoverlapping<T>(x: *mut T, y: *mut T, count: usize) {
         };
     }
 
-    assert_unsafe_precondition!(
+    ub_checks::assert_unsafe_precondition!(
         check_language_ub,
         "ptr::swap_nonoverlapping requires that both pointer arguments are aligned and non-null \
         and the specified memory ranges do not overlap",
@@ -1030,9 +1029,9 @@ pub const unsafe fn swap_nonoverlapping<T>(x: *mut T, y: *mut T, count: usize) {
             align: usize = align_of::<T>(),
             count: usize = count,
         ) =>
-        is_aligned_and_not_null(x, align)
-            && is_aligned_and_not_null(y, align)
-            && is_nonoverlapping(x, y, size, count)
+        ub_checks::is_aligned_and_not_null(x, align)
+            && ub_checks::is_aligned_and_not_null(y, align)
+            && ub_checks::is_nonoverlapping(x, y, size, count)
     );
 
     // Split up the slice into small power-of-two-sized chunks that LLVM is able
@@ -1135,13 +1134,13 @@ pub const unsafe fn replace<T>(dst: *mut T, src: T) -> T {
     // and cannot overlap `src` since `dst` must point to a distinct
     // allocated object.
     unsafe {
-        assert_unsafe_precondition!(
+        ub_checks::assert_unsafe_precondition!(
             check_language_ub,
             "ptr::replace requires that the pointer argument is aligned and non-null",
             (
                 addr: *const () = dst as *const (),
                 align: usize = align_of::<T>(),
-            ) => is_aligned_and_not_null(addr, align)
+            ) => ub_checks::is_aligned_and_not_null(addr, align)
         );
         mem::replace(&mut *dst, src)
     }
@@ -1287,13 +1286,13 @@ pub const unsafe fn read<T>(src: *const T) -> T {
     // SAFETY: the caller must guarantee that `src` is valid for reads.
     unsafe {
         #[cfg(debug_assertions)] // Too expensive to always enable (for now?)
-        assert_unsafe_precondition!(
+        ub_checks::assert_unsafe_precondition!(
             check_language_ub,
             "ptr::read requires that the pointer argument is aligned and non-null",
             (
                 addr: *const () = src as *const (),
                 align: usize = align_of::<T>(),
-            ) => is_aligned_and_not_null(addr, align)
+            ) => ub_checks::is_aligned_and_not_null(addr, align)
         );
         crate::intrinsics::read_via_copy(src)
     }
@@ -1496,13 +1495,13 @@ pub const unsafe fn write<T>(dst: *mut T, src: T) {
     // to `dst` while `src` is owned by this function.
     unsafe {
         #[cfg(debug_assertions)] // Too expensive to always enable (for now?)
-        assert_unsafe_precondition!(
+        ub_checks::assert_unsafe_precondition!(
             check_language_ub,
             "ptr::write requires that the pointer argument is aligned and non-null",
             (
                 addr: *mut () = dst as *mut (),
                 align: usize = align_of::<T>(),
-            ) => is_aligned_and_not_null(addr, align)
+            ) => ub_checks::is_aligned_and_not_null(addr, align)
         );
         intrinsics::write_via_move(dst, src)
     }
@@ -1668,13 +1667,13 @@ pub const unsafe fn write_unaligned<T>(dst: *mut T, src: T) {
 pub unsafe fn read_volatile<T>(src: *const T) -> T {
     // SAFETY: the caller must uphold the safety contract for `volatile_load`.
     unsafe {
-        assert_unsafe_precondition!(
+        ub_checks::assert_unsafe_precondition!(
             check_language_ub,
             "ptr::read_volatile requires that the pointer argument is aligned and non-null",
             (
                 addr: *const () = src as *const (),
                 align: usize = align_of::<T>(),
-            ) => is_aligned_and_not_null(addr, align)
+            ) => ub_checks::is_aligned_and_not_null(addr, align)
         );
         intrinsics::volatile_load(src)
     }
@@ -1747,13 +1746,13 @@ pub unsafe fn read_volatile<T>(src: *const T) -> T {
 pub unsafe fn write_volatile<T>(dst: *mut T, src: T) {
     // SAFETY: the caller must uphold the safety contract for `volatile_store`.
     unsafe {
-        assert_unsafe_precondition!(
+        ub_checks::assert_unsafe_precondition!(
             check_language_ub,
             "ptr::write_volatile requires that the pointer argument is aligned and non-null",
             (
                 addr: *mut () = dst as *mut (),
                 align: usize = align_of::<T>(),
-            ) => is_aligned_and_not_null(addr, align)
+            ) => ub_checks::is_aligned_and_not_null(addr, align)
         );
         intrinsics::volatile_store(dst, src);
     }
diff --git a/library/core/src/ptr/non_null.rs b/library/core/src/ptr/non_null.rs
index 2ac42e20d43..e9488917acc 100644
--- a/library/core/src/ptr/non_null.rs
+++ b/library/core/src/ptr/non_null.rs
@@ -2,7 +2,6 @@ use crate::cmp::Ordering;
 use crate::fmt;
 use crate::hash;
 use crate::intrinsics;
-use crate::intrinsics::assert_unsafe_precondition;
 use crate::marker::Unsize;
 use crate::mem::{MaybeUninit, SizedTypeProperties};
 use crate::num::NonZero;
@@ -10,6 +9,7 @@ use crate::ops::{CoerceUnsized, DispatchFromDyn};
 use crate::ptr;
 use crate::ptr::Unique;
 use crate::slice::{self, SliceIndex};
+use crate::ub_checks::assert_unsafe_precondition;
 
 /// `*mut T` but non-zero and [covariant].
 ///
diff --git a/library/core/src/slice/index.rs b/library/core/src/slice/index.rs
index 210118817ab..127a407dae5 100644
--- a/library/core/src/slice/index.rs
+++ b/library/core/src/slice/index.rs
@@ -1,10 +1,10 @@
 //! Indexing implementations for `[T]`.
 
-use crate::intrinsics::assert_unsafe_precondition;
 use crate::intrinsics::const_eval_select;
 use crate::intrinsics::unchecked_sub;
 use crate::ops;
 use crate::ptr;
+use crate::ub_checks::assert_unsafe_precondition;
 
 #[stable(feature = "rust1", since = "1.0.0")]
 impl<T, I> ops::Index<I> for [T]
diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs
index 4a574bf0347..92080c5020a 100644
--- a/library/core/src/slice/mod.rs
+++ b/library/core/src/slice/mod.rs
@@ -9,7 +9,6 @@
 use crate::cmp::Ordering::{self, Equal, Greater, Less};
 use crate::fmt;
 use crate::hint;
-use crate::intrinsics::assert_unsafe_precondition;
 use crate::intrinsics::exact_div;
 use crate::mem::{self, SizedTypeProperties};
 use crate::num::NonZero;
@@ -17,6 +16,7 @@ use crate::ops::{Bound, OneSidedRange, Range, RangeBounds};
 use crate::ptr;
 use crate::simd::{self, Simd};
 use crate::slice;
+use crate::ub_checks::assert_unsafe_precondition;
 
 #[unstable(
     feature = "slice_internals",
diff --git a/library/core/src/slice/raw.rs b/library/core/src/slice/raw.rs
index 2199614ce27..29a12f106c5 100644
--- a/library/core/src/slice/raw.rs
+++ b/library/core/src/slice/raw.rs
@@ -1,12 +1,10 @@
 //! Free functions to create `&[T]` and `&mut [T]`.
 
 use crate::array;
-use crate::intrinsics::{
-    assert_unsafe_precondition, is_aligned_and_not_null, is_valid_allocation_size,
-};
 use crate::mem::{align_of, size_of};
 use crate::ops::Range;
 use crate::ptr;
+use crate::ub_checks;
 
 /// Forms a slice from a pointer and a length.
 ///
@@ -95,7 +93,7 @@ use crate::ptr;
 pub const unsafe fn from_raw_parts<'a, T>(data: *const T, len: usize) -> &'a [T] {
     // SAFETY: the caller must uphold the safety contract for `from_raw_parts`.
     unsafe {
-        assert_unsafe_precondition!(
+        ub_checks::assert_unsafe_precondition!(
             check_language_ub,
             "slice::from_raw_parts requires the pointer to be aligned and non-null, and the total size of the slice not to exceed `isize::MAX`",
             (
@@ -104,8 +102,8 @@ pub const unsafe fn from_raw_parts<'a, T>(data: *const T, len: usize) -> &'a [T]
                 align: usize = align_of::<T>(),
                 len: usize = len,
             ) =>
-                is_aligned_and_not_null(data, align)
-                && is_valid_allocation_size(size, len)
+            ub_checks::is_aligned_and_not_null(data, align)
+                && ub_checks::is_valid_allocation_size(size, len)
         );
         &*ptr::slice_from_raw_parts(data, len)
     }
@@ -149,7 +147,7 @@ pub const unsafe fn from_raw_parts<'a, T>(data: *const T, len: usize) -> &'a [T]
 pub const unsafe fn from_raw_parts_mut<'a, T>(data: *mut T, len: usize) -> &'a mut [T] {
     // SAFETY: the caller must uphold the safety contract for `from_raw_parts_mut`.
     unsafe {
-        assert_unsafe_precondition!(
+        ub_checks::assert_unsafe_precondition!(
             check_language_ub,
             "slice::from_raw_parts_mut requires the pointer to be aligned and non-null, and the total size of the slice not to exceed `isize::MAX`",
             (
@@ -158,8 +156,8 @@ pub const unsafe fn from_raw_parts_mut<'a, T>(data: *mut T, len: usize) -> &'a m
                 align: usize = align_of::<T>(),
                 len: usize = len,
             ) =>
-                is_aligned_and_not_null(data, align)
-                && is_valid_allocation_size(size, len)
+            ub_checks::is_aligned_and_not_null(data, align)
+                && ub_checks::is_valid_allocation_size(size, len)
         );
         &mut *ptr::slice_from_raw_parts_mut(data, len)
     }
diff --git a/library/core/src/str/traits.rs b/library/core/src/str/traits.rs
index ec81fd095d5..672af752149 100644
--- a/library/core/src/str/traits.rs
+++ b/library/core/src/str/traits.rs
@@ -1,10 +1,10 @@
 //! Trait implementations for `str`.
 
 use crate::cmp::Ordering;
-use crate::intrinsics::assert_unsafe_precondition;
 use crate::ops;
 use crate::ptr;
 use crate::slice::SliceIndex;
+use crate::ub_checks::assert_unsafe_precondition;
 
 use super::ParseBoolError;
 
diff --git a/library/core/src/ub_checks.rs b/library/core/src/ub_checks.rs
new file mode 100644
index 00000000000..2c5b4699806
--- /dev/null
+++ b/library/core/src/ub_checks.rs
@@ -0,0 +1,131 @@
+//! Provides the [`assert_unsafe_precondition`] macro as well as some utility functions that cover
+//! common preconditions.
+
+use crate::intrinsics::const_eval_select;
+
+/// Check that the preconditions of an unsafe function are followed. The check is enabled at
+/// runtime if debug assertions are enabled when the caller is monomorphized. In const-eval/Miri
+/// checks implemented with this macro for language UB are always ignored.
+///
+/// This macro should be called as
+/// `assert_unsafe_precondition!(check_{library,lang}_ub, "message", (ident: type = expr, ident: type = expr) => check_expr)`
+/// where each `expr` will be evaluated and passed in as function argument `ident: type`. Then all
+/// those arguments are passed to a function with the body `check_expr`.
+/// Pick `check_language_ub` when this is guarding a violation of language UB, i.e., immediate UB
+/// according to the Rust Abstract Machine. Pick `check_library_ub` when this is guarding a violation
+/// of a documented library precondition that does not *immediately* lead to language UB.
+///
+/// If `check_library_ub` is used but the check is actually guarding language UB, the check will
+/// slow down const-eval/Miri and we'll get the panic message instead of the interpreter's nice
+/// diagnostic, but our ability to detect UB is unchanged.
+/// But if `check_language_ub` is used when the check is actually for library UB, the check is
+/// omitted in const-eval/Miri and thus if we eventually execute language UB which relies on the
+/// library UB, the backtrace Miri reports may be far removed from original cause.
+///
+/// These checks are behind a condition which is evaluated at codegen time, not expansion time like
+/// [`debug_assert`]. This means that a standard library built with optimizations and debug
+/// assertions disabled will have these checks optimized out of its monomorphizations, but if a
+/// caller of the standard library has debug assertions enabled and monomorphizes an expansion of
+/// this macro, that monomorphization will contain the check.
+///
+/// Since these checks cannot be optimized out in MIR, some care must be taken in both call and
+/// implementation to mitigate their compile-time overhead. Calls to this macro always expand to
+/// this structure:
+/// ```ignore (pseudocode)
+/// if ::core::intrinsics::check_language_ub() {
+///     precondition_check(args)
+/// }
+/// ```
+/// where `precondition_check` is monomorphic with the attributes `#[rustc_nounwind]`, `#[inline]` and
+/// `#[rustc_no_mir_inline]`. This combination of attributes ensures that the actual check logic is
+/// compiled only once and generates a minimal amount of IR because the check cannot be inlined in
+/// MIR, but *can* be inlined and fully optimized by a codegen backend.
+///
+/// Callers should avoid introducing any other `let` bindings or any code outside this macro in
+/// order to call it. Since the precompiled standard library is built with full debuginfo and these
+/// variables cannot be optimized out in MIR, an innocent-looking `let` can produce enough
+/// debuginfo to have a measurable compile-time impact on debug builds.
+#[allow_internal_unstable(ub_checks)] // permit this to be called in stably-const fn
+macro_rules! assert_unsafe_precondition {
+    ($kind:ident, $message:expr, ($($name:ident:$ty:ty = $arg:expr),*$(,)?) => $e:expr $(,)?) => {
+        {
+            // This check is inlineable, but not by the MIR inliner.
+            // The reason for this is that the MIR inliner is in an exceptionally bad position
+            // to think about whether or not to inline this. In MIR, this call is gated behind `debug_assertions`,
+            // which will codegen to `false` in release builds. Inlining the check would be wasted work in that case and
+            // would be bad for compile times.
+            //
+            // LLVM on the other hand sees the constant branch, so if it's `false`, it can immediately delete it without
+            // inlining the check. If it's `true`, it can inline it and get significantly better performance.
+            #[rustc_no_mir_inline]
+            #[inline]
+            #[rustc_nounwind]
+            #[rustc_const_unstable(feature = "ub_checks", issue = "none")]
+            const fn precondition_check($($name:$ty),*) {
+                if !$e {
+                    ::core::panicking::panic_nounwind(
+                        concat!("unsafe precondition(s) violated: ", $message)
+                    );
+                }
+            }
+
+            if ::core::intrinsics::$kind() {
+                precondition_check($($arg,)*);
+            }
+        }
+    };
+}
+pub(crate) use assert_unsafe_precondition;
+
+/// Checks whether `ptr` is properly aligned with respect to
+/// `align_of::<T>()`.
+///
+/// In `const` this is approximate and can fail spuriously. It is primarily intended
+/// for `assert_unsafe_precondition!` with `check_language_ub`, in which case the
+/// check is anyway not executed in `const`.
+#[inline]
+pub(crate) const fn is_aligned_and_not_null(ptr: *const (), align: usize) -> bool {
+    !ptr.is_null() && ptr.is_aligned_to(align)
+}
+
+#[inline]
+pub(crate) const fn is_valid_allocation_size(size: usize, len: usize) -> bool {
+    let max_len = if size == 0 { usize::MAX } else { isize::MAX as usize / size };
+    len <= max_len
+}
+
+/// Checks whether the regions of memory starting at `src` and `dst` of size
+/// `count * size` do *not* overlap.
+///
+/// Note that in const-eval this function just returns `true` and therefore must
+/// only be used with `assert_unsafe_precondition!`, similar to `is_aligned_and_not_null`.
+#[inline]
+pub(crate) const fn is_nonoverlapping(
+    src: *const (),
+    dst: *const (),
+    size: usize,
+    count: usize,
+) -> bool {
+    #[inline]
+    fn runtime(src: *const (), dst: *const (), size: usize, count: usize) -> bool {
+        let src_usize = src.addr();
+        let dst_usize = dst.addr();
+        let Some(size) = size.checked_mul(count) else {
+            crate::panicking::panic_nounwind(
+                "is_nonoverlapping: `size_of::<T>() * count` overflows a usize",
+            )
+        };
+        let diff = src_usize.abs_diff(dst_usize);
+        // If the absolute distance between the ptrs is at least as big as the size of the buffer,
+        // they do not overlap.
+        diff >= size
+    }
+
+    #[inline]
+    const fn comptime(_: *const (), _: *const (), _: usize, _: usize) -> bool {
+        true
+    }
+
+    // This is just for safety checks so we can const_eval_select.
+    const_eval_select((src, dst, size, count), comptime, runtime)
+}