diff --git a/library/core/src/alloc/layout.rs b/library/core/src/alloc/layout.rs
index 0b92767c932..e96a41422a2 100644
--- a/library/core/src/alloc/layout.rs
+++ b/library/core/src/alloc/layout.rs
@@ -183,6 +183,8 @@ impl Layout {
     ///     - a [slice], then the length of the slice tail must be an initialized
     ///       integer, and the size of the *entire value*
     ///       (dynamic tail length + statically sized prefix) must fit in `isize`.
+    ///       For the special case where the dynamic tail length is 0, this function
+    ///       is safe to call.
     ///     - a [trait object], then the vtable part of the pointer must point
     ///       to a valid vtable for the type `T` acquired by an unsizing coercion,
     ///       and the size of the *entire value*
diff --git a/library/core/src/mem/mod.rs b/library/core/src/mem/mod.rs
index dd4b6e82343..9bb4ba922cd 100644
--- a/library/core/src/mem/mod.rs
+++ b/library/core/src/mem/mod.rs
@@ -359,6 +359,12 @@ pub const fn size_of_val<T: ?Sized>(val: &T) -> usize {
 ///     - a [slice], then the length of the slice tail must be an initialized
 ///       integer, and the size of the *entire value*
 ///       (dynamic tail length + statically sized prefix) must fit in `isize`.
+///       For the special case where the dynamic tail length is 0, this function
+///       is safe to call.
+//        NOTE: the reason this is safe is that if an overflow were to occur already with size 0,
+//        then we would stop compilation as even the "statically known" part of the type would
+//        already be too big (or the call may be in dead code and optimized away, but then it
+//        doesn't matter).
 ///     - a [trait object], then the vtable part of the pointer must point
 ///       to a valid vtable acquired by an unsizing coercion, and the size
 ///       of the *entire value* (dynamic tail length + statically sized prefix)
@@ -506,6 +512,8 @@ pub const fn align_of_val<T: ?Sized>(val: &T) -> usize {
 ///     - a [slice], then the length of the slice tail must be an initialized
 ///       integer, and the size of the *entire value*
 ///       (dynamic tail length + statically sized prefix) must fit in `isize`.
+///       For the special case where the dynamic tail length is 0, this function
+///       is safe to call.
 ///     - a [trait object], then the vtable part of the pointer must point
 ///       to a valid vtable acquired by an unsizing coercion, and the size
 ///       of the *entire value* (dynamic tail length + statically sized prefix)
diff --git a/tests/ui/layout/size-of-val-raw-too-big.rs b/tests/ui/layout/size-of-val-raw-too-big.rs
new file mode 100644
index 00000000000..8d82c78d953
--- /dev/null
+++ b/tests/ui/layout/size-of-val-raw-too-big.rs
@@ -0,0 +1,18 @@
+//@ build-fail
+//@ compile-flags: --crate-type lib
+//@ only-32bit Layout computation rejects this layout for different reasons on 64-bit.
+//@ error-pattern: too big for the current architecture
+#![feature(core_intrinsics)]
+#![allow(internal_features)]
+
+// isize::MAX is fine, but with the padding for the unsized tail it is too big.
+#[repr(C)]
+pub struct Example([u8; isize::MAX as usize], [u16]);
+
+// We guarantee that with length 0, `size_of_val_raw` (which calls the `size_of_val` intrinsic)
+// is safe to call. The compiler aborts compilation if a length of 0 would overflow.
+// So let's construct a case where length 0 just barely overflows, and ensure that
+// does abort compilation.
+pub fn check(x: *const Example) -> usize {
+    unsafe { std::intrinsics::size_of_val(x) }
+}
diff --git a/tests/ui/layout/size-of-val-raw-too-big.stderr b/tests/ui/layout/size-of-val-raw-too-big.stderr
new file mode 100644
index 00000000000..aa9abd644fa
--- /dev/null
+++ b/tests/ui/layout/size-of-val-raw-too-big.stderr
@@ -0,0 +1,4 @@
+error: values of the type `Example` are too big for the current architecture
+
+error: aborting due to 1 previous error
+