diff --git a/library/core/src/mem/mod.rs b/library/core/src/mem/mod.rs index 9054ade2d7968..23ece3c57982d 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(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(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 0000000000000..3ea510d4c1db1 --- /dev/null +++ b/tests/ui/layout/size-of-val-raw-too-big.rs @@ -0,0 +1,29 @@ +//@ build-fail +//@ compile-flags: --target i686-unknown-linux-gnu --crate-type lib +//@ needs-llvm-components: x86 +//@ error-pattern: too big for the current architecture +#![feature(no_core, lang_items, intrinsics)] +#![allow(internal_features)] +#![no_std] +#![no_core] + +#[lang = "sized"] +pub trait Sized {} +#[lang = "copy"] +pub trait Copy: Sized {} + +// 0x7fffffff is fine, but with the padding for the unsized tail it is too big. +#[repr(C)] +pub struct Example([u8; 0x7fffffff], [u16]); + +extern "rust-intrinsic" { + pub fn size_of_val(_: *const T) -> usize; +} + +// We guarantee that with length 0, `size_of_val_raw` (which calls the `size_of_val` intrinsic) +// is safe to call. The compiler aborts execution if a length of 0 would overflow. +// So let's construct a case where length 0 just barely overflows, and ensure that +// does abort execution. +pub fn check(x: *const Example) { + unsafe { 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 0000000000000..aa9abd644faaf --- /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 +