diff --git a/library/core/src/ascii/ascii_char.rs b/library/core/src/ascii/ascii_char.rs
index ce09a0b444da3..48de4f17b1b3a 100644
--- a/library/core/src/ascii/ascii_char.rs
+++ b/library/core/src/ascii/ascii_char.rs
@@ -506,7 +506,7 @@ impl AsciiChar {
     pub const unsafe fn digit_unchecked(d: u8) -> Self {
         assert_unsafe_precondition!(
             check_language_ub,
-            "`AsciiChar::digit_unchecked` input cannot exceed 9.",
+            "`ascii::Char::digit_unchecked` input cannot exceed 9.",
             (d: u8 = d) => d < 10
         );
 
diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs
index d7a2f1909cabe..d76b7ca66cca4 100644
--- a/library/core/src/intrinsics.rs
+++ b/library/core/src/intrinsics.rs
@@ -64,6 +64,7 @@
 #![allow(missing_docs)]
 
 use crate::marker::{DiscriminantKind, Tuple};
+use crate::mem::SizedTypeProperties;
 use crate::{ptr, ub_checks};
 
 pub mod mir;
@@ -3311,10 +3312,12 @@ pub const unsafe fn copy_nonoverlapping<T>(src: *const T, dst: *mut T, count: us
             size: usize = size_of::<T>(),
             align: usize = align_of::<T>(),
             count: usize = 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)
+        ) => {
+            let zero_size = count == 0 || size == 0;
+            ub_checks::is_aligned_and_not_null(src, align, zero_size)
+                && ub_checks::is_aligned_and_not_null(dst, align, zero_size)
+                && ub_checks::is_nonoverlapping(src, dst, size, count)
+        }
     );
 
     // SAFETY: the safety contract for `copy_nonoverlapping` must be
@@ -3412,9 +3415,10 @@ pub const unsafe fn copy<T>(src: *const T, dst: *mut T, count: usize) {
                 src: *const () = src as *const (),
                 dst: *mut () = dst as *mut (),
                 align: usize = align_of::<T>(),
+                zero_size: bool = T::IS_ZST || count == 0,
             ) =>
-            ub_checks::is_aligned_and_not_null(src, align)
-                && ub_checks::is_aligned_and_not_null(dst, align)
+            ub_checks::is_aligned_and_not_null(src, align, zero_size)
+                && ub_checks::is_aligned_and_not_null(dst, align, zero_size)
         );
         copy(src, dst, count)
     }
@@ -3491,7 +3495,8 @@ pub const unsafe fn write_bytes<T>(dst: *mut T, val: u8, count: usize) {
             (
                 addr: *const () = dst as *const (),
                 align: usize = align_of::<T>(),
-            ) => ub_checks::is_aligned_and_not_null(addr, align)
+                zero_size: bool = T::IS_ZST || count == 0,
+            ) => ub_checks::is_aligned_and_not_null(addr, align, zero_size)
         );
         write_bytes(dst, val, count)
     }
diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs
index 67f1b0cd16de4..62160937083c5 100644
--- a/library/core/src/ptr/mod.rs
+++ b/library/core/src/ptr/mod.rs
@@ -448,7 +448,7 @@
 
 use crate::cmp::Ordering;
 use crate::marker::FnPtr;
-use crate::mem::{self, MaybeUninit};
+use crate::mem::{self, MaybeUninit, SizedTypeProperties};
 use crate::{fmt, hash, intrinsics, ub_checks};
 
 mod alignment;
@@ -1165,10 +1165,12 @@ pub const unsafe fn swap_nonoverlapping<T>(x: *mut T, y: *mut T, count: usize) {
             size: usize = size_of::<T>(),
             align: usize = align_of::<T>(),
             count: usize = 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)
+        ) => {
+            let zero_size = size == 0 || count == 0;
+            ub_checks::is_aligned_and_not_null(x, align, zero_size)
+                && ub_checks::is_aligned_and_not_null(y, align, zero_size)
+                && ub_checks::is_nonoverlapping(x, y, size, count)
+        }
     );
 
     // Split up the slice into small power-of-two-sized chunks that LLVM is able
@@ -1277,7 +1279,8 @@ pub const unsafe fn replace<T>(dst: *mut T, src: T) -> T {
             (
                 addr: *const () = dst as *const (),
                 align: usize = align_of::<T>(),
-            ) => ub_checks::is_aligned_and_not_null(addr, align)
+                is_zst: bool = T::IS_ZST,
+            ) => ub_checks::is_aligned_and_not_null(addr, align, is_zst)
         );
         mem::replace(&mut *dst, src)
     }
@@ -1429,7 +1432,8 @@ pub const unsafe fn read<T>(src: *const T) -> T {
             (
                 addr: *const () = src as *const (),
                 align: usize = align_of::<T>(),
-            ) => ub_checks::is_aligned_and_not_null(addr, align)
+                is_zst: bool = T::IS_ZST,
+            ) => ub_checks::is_aligned_and_not_null(addr, align, is_zst)
         );
         crate::intrinsics::read_via_copy(src)
     }
@@ -1634,7 +1638,8 @@ pub const unsafe fn write<T>(dst: *mut T, src: T) {
             (
                 addr: *mut () = dst as *mut (),
                 align: usize = align_of::<T>(),
-            ) => ub_checks::is_aligned_and_not_null(addr, align)
+                is_zst: bool = T::IS_ZST,
+            ) => ub_checks::is_aligned_and_not_null(addr, align, is_zst)
         );
         intrinsics::write_via_move(dst, src)
     }
@@ -1806,7 +1811,8 @@ pub unsafe fn read_volatile<T>(src: *const T) -> T {
             (
                 addr: *const () = src as *const (),
                 align: usize = align_of::<T>(),
-            ) => ub_checks::is_aligned_and_not_null(addr, align)
+                is_zst: bool = T::IS_ZST,
+            ) => ub_checks::is_aligned_and_not_null(addr, align, is_zst)
         );
         intrinsics::volatile_load(src)
     }
@@ -1885,7 +1891,8 @@ pub unsafe fn write_volatile<T>(dst: *mut T, src: T) {
             (
                 addr: *mut () = dst as *mut (),
                 align: usize = align_of::<T>(),
-            ) => ub_checks::is_aligned_and_not_null(addr, align)
+                is_zst: bool = T::IS_ZST,
+            ) => ub_checks::is_aligned_and_not_null(addr, align, is_zst)
         );
         intrinsics::volatile_store(dst, src);
     }
diff --git a/library/core/src/slice/raw.rs b/library/core/src/slice/raw.rs
index 84e916b9a84e7..27bbadf7c89de 100644
--- a/library/core/src/slice/raw.rs
+++ b/library/core/src/slice/raw.rs
@@ -132,7 +132,7 @@ pub const unsafe fn from_raw_parts<'a, T>(data: *const T, len: usize) -> &'a [T]
                 align: usize = align_of::<T>(),
                 len: usize = len,
             ) =>
-            ub_checks::is_aligned_and_not_null(data, align)
+            ub_checks::is_aligned_and_not_null(data, align, false)
                 && ub_checks::is_valid_allocation_size(size, len)
         );
         &*ptr::slice_from_raw_parts(data, len)
@@ -187,7 +187,7 @@ 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,
             ) =>
-            ub_checks::is_aligned_and_not_null(data, align)
+            ub_checks::is_aligned_and_not_null(data, align, false)
                 && ub_checks::is_valid_allocation_size(size, len)
         );
         &mut *ptr::slice_from_raw_parts_mut(data, len)
diff --git a/library/core/src/ub_checks.rs b/library/core/src/ub_checks.rs
index c1a8c34539e6c..daaaf5a71953e 100644
--- a/library/core/src/ub_checks.rs
+++ b/library/core/src/ub_checks.rs
@@ -109,15 +109,15 @@ pub(crate) const fn check_language_ub() -> bool {
     intrinsics::ub_checks() && const_eval_select((), comptime, runtime)
 }
 
-/// Checks whether `ptr` is properly aligned with respect to
-/// `align_of::<T>()`.
+/// Checks whether `ptr` is properly aligned with respect to the given alignment, and
+/// if `is_zst == false`, that `ptr` is not null.
 ///
 /// 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)
+pub(crate) const fn is_aligned_and_not_null(ptr: *const (), align: usize, is_zst: bool) -> bool {
+    ptr.is_aligned_to(align) && (is_zst || !ptr.is_null())
 }
 
 #[inline]
diff --git a/tests/ui/precondition-checks/alignment.rs b/tests/ui/precondition-checks/alignment.rs
new file mode 100644
index 0000000000000..92400528fa07f
--- /dev/null
+++ b/tests/ui/precondition-checks/alignment.rs
@@ -0,0 +1,11 @@
+//@ run-fail
+//@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes
+//@ error-pattern: unsafe precondition(s) violated: Alignment::new_unchecked requires
+
+#![feature(ptr_alignment_type)]
+
+fn main() {
+    unsafe {
+        std::ptr::Alignment::new_unchecked(0);
+    }
+}
diff --git a/tests/ui/precondition-checks/ascii-char-digit_unchecked.rs b/tests/ui/precondition-checks/ascii-char-digit_unchecked.rs
new file mode 100644
index 0000000000000..30c6f79fb08f0
--- /dev/null
+++ b/tests/ui/precondition-checks/ascii-char-digit_unchecked.rs
@@ -0,0 +1,11 @@
+//@ run-fail
+//@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes
+//@ error-pattern: unsafe precondition(s) violated: `ascii::Char::digit_unchecked` input cannot exceed 9
+
+#![feature(ascii_char)]
+
+fn main() {
+    unsafe {
+        std::ascii::Char::digit_unchecked(b'a');
+    }
+}
diff --git a/tests/ui/precondition-checks/assert_unchecked.rs b/tests/ui/precondition-checks/assert_unchecked.rs
new file mode 100644
index 0000000000000..22b2b41455021
--- /dev/null
+++ b/tests/ui/precondition-checks/assert_unchecked.rs
@@ -0,0 +1,9 @@
+//@ run-fail
+//@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes
+//@ error-pattern: unsafe precondition(s) violated: hint::assert_unchecked must never be called when the condition is false
+
+fn main() {
+    unsafe {
+        std::hint::assert_unchecked(false);
+    }
+}
diff --git a/tests/ui/precondition-checks/char-from_u32_unchecked.rs b/tests/ui/precondition-checks/char-from_u32_unchecked.rs
new file mode 100644
index 0000000000000..d950f20c77208
--- /dev/null
+++ b/tests/ui/precondition-checks/char-from_u32_unchecked.rs
@@ -0,0 +1,9 @@
+//@ run-fail
+//@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes
+//@ error-pattern: unsafe precondition(s) violated: invalid value for `char`
+
+fn main() {
+    unsafe {
+        char::from_u32_unchecked(0xD801);
+    }
+}
diff --git a/tests/ui/precondition-checks/copy-nonoverlapping.rs b/tests/ui/precondition-checks/copy-nonoverlapping.rs
new file mode 100644
index 0000000000000..81018e4bff3e5
--- /dev/null
+++ b/tests/ui/precondition-checks/copy-nonoverlapping.rs
@@ -0,0 +1,25 @@
+//@ run-fail
+//@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes
+//@ error-pattern: unsafe precondition(s) violated: ptr::copy_nonoverlapping requires
+//@ revisions: null_src null_dst misaligned_src misaligned_dst overlapping
+
+use std::ptr;
+
+fn main() {
+    let src = [0u16; 3];
+    let mut dst = [0u16; 3];
+    let src = src.as_ptr();
+    let dst = dst.as_mut_ptr();
+    unsafe {
+        #[cfg(null_src)]
+        ptr::copy_nonoverlapping(ptr::null(), dst, 1);
+        #[cfg(null_dst)]
+        ptr::copy_nonoverlapping(src, ptr::null_mut(), 1);
+        #[cfg(misaligned_src)]
+        ptr::copy_nonoverlapping(src.byte_add(1), dst, 1);
+        #[cfg(misaligned_dst)]
+        ptr::copy_nonoverlapping(src, dst.byte_add(1), 1);
+        #[cfg(overlapping)]
+        ptr::copy_nonoverlapping(dst, dst.add(1), 2);
+    }
+}
diff --git a/tests/ui/precondition-checks/copy.rs b/tests/ui/precondition-checks/copy.rs
new file mode 100644
index 0000000000000..694853f950ab5
--- /dev/null
+++ b/tests/ui/precondition-checks/copy.rs
@@ -0,0 +1,23 @@
+//@ run-fail
+//@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes
+//@ error-pattern: unsafe precondition(s) violated: ptr::copy requires
+//@ revisions: null_src null_dst misaligned_src misaligned_dst
+
+use std::ptr;
+
+fn main() {
+    let src = [0u16; 3];
+    let mut dst = [0u16; 3];
+    let src = src.as_ptr();
+    let dst = dst.as_mut_ptr();
+    unsafe {
+        #[cfg(null_src)]
+        ptr::copy(ptr::null(), dst, 1);
+        #[cfg(null_dst)]
+        ptr::copy(src, ptr::null_mut(), 1);
+        #[cfg(misaligned_src)]
+        ptr::copy(src.byte_add(1), dst, 1);
+        #[cfg(misaligned_dst)]
+        ptr::copy(src, dst.byte_add(1), 1);
+    }
+}
diff --git a/tests/ui/precondition-checks/layout.rs b/tests/ui/precondition-checks/layout.rs
new file mode 100644
index 0000000000000..4fd1bbc4a994b
--- /dev/null
+++ b/tests/ui/precondition-checks/layout.rs
@@ -0,0 +1,15 @@
+//@ run-fail
+//@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes
+//@ error-pattern: unsafe precondition(s) violated: Layout::from_size_align_unchecked requires
+//@ revisions: toolarge badalign
+//@[toolarge] compile-flags: --cfg toolarge
+//@[badalign] compile-flags: --cfg badalign
+
+fn main() {
+    unsafe {
+        #[cfg(toolarge)]
+        std::alloc::Layout::from_size_align_unchecked(isize::MAX as usize, 2);
+        #[cfg(badalign)]
+        std::alloc::Layout::from_size_align_unchecked(1, 3);
+    }
+}
diff --git a/tests/ui/precondition-checks/misaligned-slice.rs b/tests/ui/precondition-checks/misaligned-slice.rs
deleted file mode 100644
index 2963a0b5e6310..0000000000000
--- a/tests/ui/precondition-checks/misaligned-slice.rs
+++ /dev/null
@@ -1,10 +0,0 @@
-//@ run-fail
-//@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes
-//@ error-pattern: unsafe precondition(s) violated: slice::from_raw_parts
-//@ ignore-debug
-
-fn main() {
-    unsafe {
-        let _s: &[u64] = std::slice::from_raw_parts(1usize as *const u64, 0);
-    }
-}
diff --git a/tests/ui/precondition-checks/nonnull.rs b/tests/ui/precondition-checks/nonnull.rs
new file mode 100644
index 0000000000000..6b8edd4e5825e
--- /dev/null
+++ b/tests/ui/precondition-checks/nonnull.rs
@@ -0,0 +1,9 @@
+//@ run-fail
+//@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes
+//@ error-pattern: unsafe precondition(s) violated: NonNull::new_unchecked requires
+
+fn main() {
+    unsafe {
+        std::ptr::NonNull::new_unchecked(std::ptr::null_mut::<u8>());
+    }
+}
diff --git a/tests/ui/precondition-checks/nonzero-from_mut_unchecked.rs b/tests/ui/precondition-checks/nonzero-from_mut_unchecked.rs
new file mode 100644
index 0000000000000..46ce7dc356fe2
--- /dev/null
+++ b/tests/ui/precondition-checks/nonzero-from_mut_unchecked.rs
@@ -0,0 +1,12 @@
+//@ run-fail
+//@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes
+//@ error-pattern: unsafe precondition(s) violated: NonZero::from_mut_unchecked requires
+
+#![feature(nonzero_from_mut)]
+
+fn main() {
+    unsafe {
+        let mut num = 0u8;
+        std::num::NonZeroU8::from_mut_unchecked(&mut num);
+    }
+}
diff --git a/tests/ui/precondition-checks/nonzero-new_unchecked.rs b/tests/ui/precondition-checks/nonzero-new_unchecked.rs
new file mode 100644
index 0000000000000..7827a42844fd4
--- /dev/null
+++ b/tests/ui/precondition-checks/nonzero-new_unchecked.rs
@@ -0,0 +1,9 @@
+//@ run-fail
+//@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes
+//@ error-pattern: unsafe precondition(s) violated: NonZero::new_unchecked requires
+
+fn main() {
+    unsafe {
+        std::num::NonZeroU8::new_unchecked(0);
+    }
+}
diff --git a/tests/ui/precondition-checks/null-slice.rs b/tests/ui/precondition-checks/null-slice.rs
deleted file mode 100644
index 280960358b7b3..0000000000000
--- a/tests/ui/precondition-checks/null-slice.rs
+++ /dev/null
@@ -1,10 +0,0 @@
-//@ run-fail
-//@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes
-//@ error-pattern: unsafe precondition(s) violated: slice::from_raw_parts
-//@ ignore-debug
-
-fn main() {
-    unsafe {
-        let _s: &[u8] = std::slice::from_raw_parts(std::ptr::null(), 0);
-    }
-}
diff --git a/tests/ui/precondition-checks/out-of-bounds-get-unchecked.rs b/tests/ui/precondition-checks/out-of-bounds-get-unchecked.rs
deleted file mode 100644
index 011e92183fa47..0000000000000
--- a/tests/ui/precondition-checks/out-of-bounds-get-unchecked.rs
+++ /dev/null
@@ -1,11 +0,0 @@
-//@ run-fail
-//@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes
-//@ error-pattern: slice::get_unchecked requires
-//@ ignore-debug
-
-fn main() {
-    unsafe {
-        let sli: &[u8] = &[0];
-        sli.get_unchecked(1);
-    }
-}
diff --git a/tests/ui/precondition-checks/read.rs b/tests/ui/precondition-checks/read.rs
new file mode 100644
index 0000000000000..ab9921a0ceebe
--- /dev/null
+++ b/tests/ui/precondition-checks/read.rs
@@ -0,0 +1,18 @@
+//@ run-fail
+//@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes
+//@ error-pattern: unsafe precondition(s) violated: ptr::read requires
+//@ revisions: null misaligned
+//@ ignore-test
+
+use std::ptr;
+
+fn main() {
+    let src = [0u16; 2];
+    let src = src.as_ptr();
+    unsafe {
+        #[cfg(null)]
+        ptr::read(ptr::null::<u8>());
+        #[cfg(misaligned)]
+        ptr::read(src.byte_add(1));
+    }
+}
diff --git a/tests/ui/precondition-checks/read_volatile.rs b/tests/ui/precondition-checks/read_volatile.rs
new file mode 100644
index 0000000000000..e14881d029037
--- /dev/null
+++ b/tests/ui/precondition-checks/read_volatile.rs
@@ -0,0 +1,17 @@
+//@ run-fail
+//@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes
+//@ error-pattern: unsafe precondition(s) violated: ptr::read_volatile requires
+//@ revisions: null misaligned
+
+use std::ptr;
+
+fn main() {
+    let src = [0u16; 2];
+    let src = src.as_ptr();
+    unsafe {
+        #[cfg(null)]
+        ptr::read_volatile(ptr::null::<u8>());
+        #[cfg(misaligned)]
+        ptr::read_volatile(src.byte_add(1));
+    }
+}
diff --git a/tests/ui/precondition-checks/replace.rs b/tests/ui/precondition-checks/replace.rs
new file mode 100644
index 0000000000000..2808cee7b64b1
--- /dev/null
+++ b/tests/ui/precondition-checks/replace.rs
@@ -0,0 +1,17 @@
+//@ run-fail
+//@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes
+//@ error-pattern: unsafe precondition(s) violated: ptr::replace requires
+//@ revisions: null misaligned
+
+use std::ptr;
+
+fn main() {
+    let mut dst = [0u16; 2];
+    let dst = dst.as_mut_ptr();
+    unsafe {
+        #[cfg(null)]
+        ptr::replace(ptr::null_mut::<u8>(), 1);
+        #[cfg(misaligned)]
+        ptr::replace(dst.byte_add(1), 1u16);
+    }
+}
diff --git a/tests/ui/precondition-checks/slice-from-raw-parts-mut.rs b/tests/ui/precondition-checks/slice-from-raw-parts-mut.rs
new file mode 100644
index 0000000000000..3801639e2551b
--- /dev/null
+++ b/tests/ui/precondition-checks/slice-from-raw-parts-mut.rs
@@ -0,0 +1,16 @@
+//@ run-fail
+//@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes
+//@ error-pattern: unsafe precondition(s) violated: slice::from_raw_parts_mut requires
+//@ revisions: null misaligned toolarge
+
+fn main() {
+    unsafe {
+        #[cfg(null)]
+        let _s: &mut [u8] = std::slice::from_raw_parts_mut(std::ptr::null_mut(), 0);
+        #[cfg(misaligned)]
+        let _s: &mut [u16] = std::slice::from_raw_parts_mut(1usize as *mut u16, 0);
+        #[cfg(toolarge)]
+        let _s: &mut [u16] =
+            std::slice::from_raw_parts_mut(2usize as *mut u16, isize::MAX as usize);
+    }
+}
diff --git a/tests/ui/precondition-checks/slice-from-raw-parts.rs b/tests/ui/precondition-checks/slice-from-raw-parts.rs
new file mode 100644
index 0000000000000..a3690fa045eb7
--- /dev/null
+++ b/tests/ui/precondition-checks/slice-from-raw-parts.rs
@@ -0,0 +1,15 @@
+//@ run-fail
+//@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes
+//@ error-pattern: unsafe precondition(s) violated: slice::from_raw_parts requires
+//@ revisions: null misaligned toolarge
+
+fn main() {
+    unsafe {
+        #[cfg(null)]
+        let _s: &[u8] = std::slice::from_raw_parts(std::ptr::null(), 0);
+        #[cfg(misaligned)]
+        let _s: &[u16] = std::slice::from_raw_parts(1usize as *const u16, 0);
+        #[cfg(toolarge)]
+        let _s: &[u16] = std::slice::from_raw_parts(2usize as *const u16, isize::MAX as usize);
+    }
+}
diff --git a/tests/ui/precondition-checks/slice-get_unchecked.rs b/tests/ui/precondition-checks/slice-get_unchecked.rs
new file mode 100644
index 0000000000000..1d8188fb9531a
--- /dev/null
+++ b/tests/ui/precondition-checks/slice-get_unchecked.rs
@@ -0,0 +1,20 @@
+//@ run-fail
+//@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes
+//@ error-pattern: unsafe precondition(s) violated: slice::get_unchecked requires
+//@ revisions: usize range range_to range_from backwards_range
+
+fn main() {
+    unsafe {
+        let s = &[0];
+        #[cfg(usize)]
+        s.get_unchecked(1);
+        #[cfg(range)]
+        s.get_unchecked(1..2);
+        #[cfg(range_to)]
+        s.get_unchecked(..2);
+        #[cfg(range_from)]
+        s.get_unchecked(2..);
+        #[cfg(backwards_range)]
+        s.get_unchecked(1..0);
+     }
+}
diff --git a/tests/ui/precondition-checks/slice-get_unchecked_mut.rs b/tests/ui/precondition-checks/slice-get_unchecked_mut.rs
new file mode 100644
index 0000000000000..34c1454af438d
--- /dev/null
+++ b/tests/ui/precondition-checks/slice-get_unchecked_mut.rs
@@ -0,0 +1,20 @@
+//@ run-fail
+//@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes
+//@ error-pattern: unsafe precondition(s) violated: slice::get_unchecked_mut requires
+//@ revisions: usize range range_to range_from backwards_range
+
+fn main() {
+    unsafe {
+        let mut s = &mut [0];
+        #[cfg(usize)]
+        s.get_unchecked_mut(1);
+        #[cfg(range)]
+        s.get_unchecked_mut(1..2);
+        #[cfg(range_to)]
+        s.get_unchecked_mut(..2);
+        #[cfg(range_from)]
+        s.get_unchecked_mut(2..);
+        #[cfg(backwards_range)]
+        s.get_unchecked_mut(1..0);
+     }
+}
diff --git a/tests/ui/precondition-checks/slice-swap_unchecked.rs b/tests/ui/precondition-checks/slice-swap_unchecked.rs
new file mode 100644
index 0000000000000..227a49091ec22
--- /dev/null
+++ b/tests/ui/precondition-checks/slice-swap_unchecked.rs
@@ -0,0 +1,14 @@
+//@ run-fail
+//@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes
+//@ error-pattern: index out of bounds: the len is 2 but the index is 2
+//@ revisions: oob_a oob_b
+
+fn main() {
+    let mut pair = [0u8; 2];
+    unsafe {
+        #[cfg(oob_a)]
+        pair.swap(0, 2);
+        #[cfg(oob_b)]
+        pair.swap(2, 0);
+    }
+}
diff --git a/tests/ui/precondition-checks/str-get_unchecked.rs b/tests/ui/precondition-checks/str-get_unchecked.rs
new file mode 100644
index 0000000000000..14d17f997ec9b
--- /dev/null
+++ b/tests/ui/precondition-checks/str-get_unchecked.rs
@@ -0,0 +1,18 @@
+//@ run-fail
+//@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes
+//@ error-pattern: unsafe precondition(s) violated: str::get_unchecked requires
+//@ revisions: range range_to range_from backwards_range
+
+fn main() {
+    unsafe {
+        let s = "💅";
+        #[cfg(range)]
+        s.get_unchecked(4..5);
+        #[cfg(range_to)]
+        s.get_unchecked(..5);
+        #[cfg(range_from)]
+        s.get_unchecked(5..);
+        #[cfg(backwards_range)]
+        s.get_unchecked(1..0);
+    }
+}
diff --git a/tests/ui/precondition-checks/str-get_unchecked_mut.rs b/tests/ui/precondition-checks/str-get_unchecked_mut.rs
new file mode 100644
index 0000000000000..ca1b169005559
--- /dev/null
+++ b/tests/ui/precondition-checks/str-get_unchecked_mut.rs
@@ -0,0 +1,19 @@
+//@ run-fail
+//@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes
+//@ error-pattern: unsafe precondition(s) violated: str::get_unchecked_mut requires
+//@ revisions: range range_to range_from backwards_range
+
+fn main() {
+    unsafe {
+        let mut s: String = "💅".chars().collect();
+        let mut s: &mut str = &mut s;
+        #[cfg(range)]
+        s.get_unchecked_mut(4..5);
+        #[cfg(range_to)]
+        s.get_unchecked_mut(..5);
+        #[cfg(range_from)]
+        s.get_unchecked_mut(5..);
+        #[cfg(backwards_range)]
+        s.get_unchecked_mut(1..0);
+    }
+}
diff --git a/tests/ui/precondition-checks/swap-nonoverlapping.rs b/tests/ui/precondition-checks/swap-nonoverlapping.rs
new file mode 100644
index 0000000000000..52e4a3c870be5
--- /dev/null
+++ b/tests/ui/precondition-checks/swap-nonoverlapping.rs
@@ -0,0 +1,25 @@
+//@ run-fail
+//@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes
+//@ error-pattern: unsafe precondition(s) violated: ptr::swap_nonoverlapping requires
+//@ revisions: null_src null_dst misaligned_src misaligned_dst overlapping
+
+use std::ptr;
+
+fn main() {
+    let mut src = [0u16; 3];
+    let mut dst = [0u16; 3];
+    let src = src.as_mut_ptr();
+    let dst = dst.as_mut_ptr();
+    unsafe {
+        #[cfg(null_src)]
+        ptr::swap_nonoverlapping(ptr::null_mut(), dst, 1);
+        #[cfg(null_dst)]
+        ptr::swap_nonoverlapping(src, ptr::null_mut(), 1);
+        #[cfg(misaligned_src)]
+        ptr::swap_nonoverlapping(src.byte_add(1), dst, 1);
+        #[cfg(misaligned_dst)]
+        ptr::swap_nonoverlapping(src, dst.byte_add(1), 1);
+        #[cfg(overlapping)]
+        ptr::swap_nonoverlapping(dst, dst.add(1), 2);
+    }
+}
diff --git a/tests/ui/precondition-checks/unchecked_add.rs b/tests/ui/precondition-checks/unchecked_add.rs
new file mode 100644
index 0000000000000..f44a6ea32ad8f
--- /dev/null
+++ b/tests/ui/precondition-checks/unchecked_add.rs
@@ -0,0 +1,9 @@
+//@ run-fail
+//@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes
+//@ error-pattern: unsafe precondition(s) violated: u8::unchecked_add cannot overflow
+
+fn main() {
+    unsafe {
+        1u8.unchecked_add(u8::MAX);
+    }
+}
diff --git a/tests/ui/precondition-checks/unchecked_mul.rs b/tests/ui/precondition-checks/unchecked_mul.rs
new file mode 100644
index 0000000000000..66655dda136e9
--- /dev/null
+++ b/tests/ui/precondition-checks/unchecked_mul.rs
@@ -0,0 +1,9 @@
+//@ run-fail
+//@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes
+//@ error-pattern: unsafe precondition(s) violated: u8::unchecked_add cannot overflow
+
+fn main() {
+    unsafe {
+        2u8.unchecked_add(u8::MAX);
+    }
+}
diff --git a/tests/ui/precondition-checks/unchecked_shl.rs b/tests/ui/precondition-checks/unchecked_shl.rs
new file mode 100644
index 0000000000000..1c96db0b1ec71
--- /dev/null
+++ b/tests/ui/precondition-checks/unchecked_shl.rs
@@ -0,0 +1,11 @@
+//@ run-fail
+//@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes
+//@ error-pattern: unsafe precondition(s) violated: u8::unchecked_shl cannot overflow
+
+#![feature(unchecked_shifts)]
+
+fn main() {
+    unsafe {
+        0u8.unchecked_shl(u8::BITS);
+    }
+}
diff --git a/tests/ui/precondition-checks/unchecked_shr.rs b/tests/ui/precondition-checks/unchecked_shr.rs
new file mode 100644
index 0000000000000..4a6d9ffb1d35b
--- /dev/null
+++ b/tests/ui/precondition-checks/unchecked_shr.rs
@@ -0,0 +1,11 @@
+//@ run-fail
+//@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes
+//@ error-pattern: unsafe precondition(s) violated: u8::unchecked_shr cannot overflow
+
+#![feature(unchecked_shifts)]
+
+fn main() {
+    unsafe {
+        0u8.unchecked_shr(u8::BITS);
+    }
+}
diff --git a/tests/ui/precondition-checks/unchecked_sub.rs b/tests/ui/precondition-checks/unchecked_sub.rs
new file mode 100644
index 0000000000000..545dde0e27809
--- /dev/null
+++ b/tests/ui/precondition-checks/unchecked_sub.rs
@@ -0,0 +1,9 @@
+//@ run-fail
+//@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes
+//@ error-pattern: unsafe precondition(s) violated: u8::unchecked_sub cannot overflow
+
+fn main() {
+    unsafe {
+        0u8.unchecked_sub(1u8);
+    }
+}
diff --git a/tests/ui/precondition-checks/unreachable_unchecked.rs b/tests/ui/precondition-checks/unreachable_unchecked.rs
new file mode 100644
index 0000000000000..2435450c4b5a1
--- /dev/null
+++ b/tests/ui/precondition-checks/unreachable_unchecked.rs
@@ -0,0 +1,9 @@
+//@ run-fail
+//@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes
+//@ error-pattern: unsafe precondition(s) violated: hint::unreachable_unchecked must never be reached
+
+fn main() {
+    unsafe {
+        std::hint::unreachable_unchecked();
+    }
+}
diff --git a/tests/ui/precondition-checks/write.rs b/tests/ui/precondition-checks/write.rs
new file mode 100644
index 0000000000000..f76e776fcf35d
--- /dev/null
+++ b/tests/ui/precondition-checks/write.rs
@@ -0,0 +1,18 @@
+//@ run-fail
+//@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes
+//@ error-pattern: unsafe precondition(s) violated: ptr::write requires
+//@ revisions: null misaligned
+//@ ignore-test
+
+use std::ptr;
+
+fn main() {
+    let mut dst = [0u16; 2];
+    let mut dst = dst.as_mut_ptr();
+    unsafe {
+        #[cfg(null)]
+        ptr::write(ptr::null_mut::<u8>(), 1u8);
+        #[cfg(misaligned)]
+        ptr::write(dst.byte_add(1), 1u16);
+    }
+}
diff --git a/tests/ui/precondition-checks/write_bytes.rs b/tests/ui/precondition-checks/write_bytes.rs
new file mode 100644
index 0000000000000..3f64be9d1ee14
--- /dev/null
+++ b/tests/ui/precondition-checks/write_bytes.rs
@@ -0,0 +1,18 @@
+//@ run-fail
+//@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes
+//@ error-pattern: unsafe precondition(s) violated: ptr::write requires
+//@ revisions: null misaligned
+//@ ignore-test
+
+use std::ptr;
+
+fn main() {
+    let mut dst = [0u16; 2];
+    let mut dst = dst.as_mut_ptr();
+    unsafe {
+        #[cfg(null)]
+        ptr::write_bytes(ptr::null_mut::<u8>(), 1u8, 2);
+        #[cfg(misaligned)]
+        ptr::write_bytes(dst.byte_add(1), 1u8, 2);
+    }
+}
diff --git a/tests/ui/precondition-checks/write_volatile.rs b/tests/ui/precondition-checks/write_volatile.rs
new file mode 100644
index 0000000000000..ac0b89b5ecf2a
--- /dev/null
+++ b/tests/ui/precondition-checks/write_volatile.rs
@@ -0,0 +1,17 @@
+//@ run-fail
+//@ compile-flags: -Copt-level=3 -Cdebug-assertions=no -Zub-checks=yes
+//@ error-pattern: unsafe precondition(s) violated: ptr::write_volatile requires
+//@ revisions: null misaligned
+
+use std::ptr;
+
+fn main() {
+    let mut dst = [0u16; 2];
+    let mut dst = dst.as_mut_ptr();
+    unsafe {
+        #[cfg(null)]
+        ptr::write_volatile(ptr::null_mut::<u8>(), 1u8);
+        #[cfg(misaligned)]
+        ptr::write_volatile(dst.byte_add(1), 1u16);
+    }
+}
diff --git a/tests/ui/precondition-checks/zero-size-null.rs b/tests/ui/precondition-checks/zero-size-null.rs
new file mode 100644
index 0000000000000..43a81175f9448
--- /dev/null
+++ b/tests/ui/precondition-checks/zero-size-null.rs
@@ -0,0 +1,21 @@
+// Test that none of the precondition checks panic on zero-sized reads or writes through null.
+
+//@ run-pass
+//@ compile-flags: -Zmir-opt-level=0 -Copt-level=0 -Cdebug-assertions=yes
+
+use std::ptr;
+
+fn main() {
+    unsafe {
+        ptr::copy_nonoverlapping::<u8>(ptr::null(), ptr::null_mut(), 0);
+        ptr::copy_nonoverlapping::<()>(ptr::null(), ptr::null_mut(), 123);
+        ptr::copy::<u8>(ptr::null(), ptr::null_mut(), 0);
+        ptr::copy::<()>(ptr::null(), ptr::null_mut(), 123);
+        ptr::swap::<()>(ptr::null_mut(), ptr::null_mut());
+        ptr::replace::<()>(ptr::null_mut(), ());
+        ptr::read::<()>(ptr::null());
+        ptr::write::<()>(ptr::null_mut(), ());
+        ptr::read_volatile::<()>(ptr::null());
+        ptr::write_volatile::<()>(ptr::null_mut(), ());
+    }
+}