diff --git a/src/lib.rs b/src/lib.rs index 9bc6f742..9019eaaf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1523,42 +1523,6 @@ pub unsafe trait Zeroable { } } -/// Marker trait for types that allow `Option` to be set to all zeroes in order to write -/// `None` to that location. -/// -/// # Safety -/// -/// The implementer needs to ensure that `unsafe impl Zeroable for Option {}` is sound. -pub unsafe trait ZeroableOption {} - -// SAFETY: by the safety requirement of `ZeroableOption`, this is valid. -unsafe impl Zeroable for Option {} - -// SAFETY: `Option<&T>` is part of the option layout optimization guarantee: -// . -unsafe impl ZeroableOption for &T {} -// SAFETY: `Option<&mut T>` is part of the option layout optimization guarantee: -// . -unsafe impl ZeroableOption for &mut T {} -// SAFETY: `Option>` is part of the option layout optimization guarantee: -// . -unsafe impl ZeroableOption for NonNull {} - -/// Create an initializer for a zeroed `T`. -/// -/// The returned initializer will write `0x00` to every byte of the given `slot`. -#[inline] -pub fn init_zeroed() -> impl Init { - // SAFETY: Because `T: Zeroable`, all bytes zero is a valid bit pattern for `T` - // and because we write all zeroes, the memory is initialized. - unsafe { - init_from_closure(|slot: *mut T| { - slot.write_bytes(0, 1); - Ok(()) - }) - } -} - /// Create a `T` consisting of all zeroes. /// /// Whenever a type implements [`Zeroable`], this function should be preferred over @@ -1584,6 +1548,21 @@ pub const fn zeroed() -> T { unsafe { core::mem::zeroed() } } +/// Create an initializer for a zeroed `T`. +/// +/// The returned initializer will write `0x00` to every byte of the given `slot`. +#[inline] +pub fn init_zeroed() -> impl Init { + // SAFETY: Because `T: Zeroable`, all bytes zero is a valid bit pattern for `T` + // and because we write all zeroes, the memory is initialized. + unsafe { + init_from_closure(|slot: *mut T| { + slot.write_bytes(0, 1); + Ok(()) + }) + } +} + macro_rules! impl_zeroable { ($($({$($generics:tt)*})? $t:ty, )*) => { // SAFETY: Safety comments written in the macro invocation. @@ -1649,6 +1628,17 @@ macro_rules! impl_tuple_zeroable { impl_tuple_zeroable!(A, B, C, D, E, F, G, H, I, J); +/// Marker trait for types that allow `Option` to be set to all zeroes in order to write +/// `None` to that location. +/// +/// # Safety +/// +/// The implementer needs to ensure that `unsafe impl Zeroable for Option {}` is sound. +pub unsafe trait ZeroableOption {} + +// SAFETY: by the safety requirement of `ZeroableOption`, this is valid. +unsafe impl Zeroable for Option {} + macro_rules! impl_fn_zeroable_option { ([$($abi:literal),* $(,)?] $args:tt) => { $(impl_fn_zeroable_option!({extern $abi} $args);)* @@ -1674,14 +1664,23 @@ macro_rules! impl_fn_zeroable_option { impl_fn_zeroable_option!(["Rust", "C"] { A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U }); -macro_rules! impl_non_zero_int_zeroable_option { - ($($int:ty),* $(,)?) => { - // SAFETY: Safety comment written in the macro invocation. - $(unsafe impl ZeroableOption for $int {})* +macro_rules! impl_zeroable_option { + ($($({$($generics:tt)*})? $t:ty, )*) => { + // SAFETY: Safety comments written in the macro invocation. + $(unsafe impl$($($generics)*)? ZeroableOption for $t {})* }; } -impl_non_zero_int_zeroable_option! { +impl_zeroable_option! { + // SAFETY: `Option<&T>` is part of the option layout optimization guarantee: + // . + {} &T, + // SAFETY: `Option<&mut T>` is part of the option layout optimization guarantee: + // . + {} &mut T, + // SAFETY: `Option>` is part of the option layout optimization guarantee: + // . + {} NonNull, // SAFETY: All zeros is equivalent to `None` (option layout optimization guarantee: // ). NonZeroU8, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU128, NonZeroUsize, diff --git a/tests/ui/compile-fail/zeroable/option_string_not_zeroable.rs b/tests/ui/compile-fail/zeroable/option_string_not_zeroable.rs new file mode 100644 index 00000000..76ee220e --- /dev/null +++ b/tests/ui/compile-fail/zeroable/option_string_not_zeroable.rs @@ -0,0 +1,8 @@ +use pin_init::*; + +#[derive(Zeroable)] +struct Foo { + x: Option, +} + +fn main() {} diff --git a/tests/ui/compile-fail/zeroable/option_string_not_zeroable.stderr b/tests/ui/compile-fail/zeroable/option_string_not_zeroable.stderr new file mode 100644 index 00000000..ea3fb763 --- /dev/null +++ b/tests/ui/compile-fail/zeroable/option_string_not_zeroable.stderr @@ -0,0 +1,23 @@ +error[E0277]: the trait bound `String: ZeroableOption` is not satisfied + --> tests/ui/compile-fail/zeroable/option_string_not_zeroable.rs:5:8 + | +5 | x: Option, + | ^^^^^^^^^^^^^^ the trait `ZeroableOption` is not implemented for `String` + | + = help: the following other types implement trait `ZeroableOption`: + &T + &mut T + Box + NonNull + NonZero + NonZero + NonZero + NonZero + and $N others + = note: required for `Option` to implement `pin_init::Zeroable` +note: required by a bound in `assert_zeroable` + --> tests/ui/compile-fail/zeroable/option_string_not_zeroable.rs:3:10 + | +3 | #[derive(Zeroable)] + | ^^^^^^^^ required by this bound in `assert_zeroable` + = note: this error originates in the derive macro `Zeroable` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/zeroing.rs b/tests/zeroing.rs index e411e4ba..b731a16e 100644 --- a/tests/zeroing.rs +++ b/tests/zeroing.rs @@ -3,6 +3,10 @@ use std::marker::PhantomPinned; +use core::{ + num::{NonZeroI32, NonZeroU8, NonZeroUsize}, + ptr::NonNull, +}; use pin_init::*; const MARKS: usize = 64; @@ -35,3 +39,30 @@ impl Foo { fn test() { let _ = Box::pin_init(Foo::new()).unwrap(); } + +#[test] +fn zeroed_option_runtime_values() { + let ref_opt: Option<&u8> = zeroed(); + let mut_ref_opt: Option<&mut u8> = zeroed(); + let non_null_opt: Option> = zeroed(); + let non_zero_unsigned: Option = zeroed(); + let non_zero_signed: Option = zeroed(); + + assert!(ref_opt.is_none()); + assert!(mut_ref_opt.is_none()); + assert!(non_null_opt.is_none()); + assert!(non_zero_unsigned.is_none()); + assert!(non_zero_signed.is_none()); +} + +fn assert_zeroable_option() { + let _: Option = zeroed(); +} + +#[test] +fn zeroed_option_generic_compile_check() { + assert_zeroable_option::<&u8>(); + assert_zeroable_option::<&mut u8>(); + assert_zeroable_option::>(); + assert_zeroable_option::(); +}