Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 40 additions & 41 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1523,42 +1523,6 @@ pub unsafe trait Zeroable {
}
}

/// Marker trait for types that allow `Option<Self>` 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<Self> {}` is sound.
pub unsafe trait ZeroableOption {}

// SAFETY: by the safety requirement of `ZeroableOption`, this is valid.
unsafe impl<T: ZeroableOption> Zeroable for Option<T> {}

// SAFETY: `Option<&T>` is part of the option layout optimization guarantee:
// <https://doc.rust-lang.org/stable/std/option/index.html#representation>.
unsafe impl<T> ZeroableOption for &T {}
// SAFETY: `Option<&mut T>` is part of the option layout optimization guarantee:
// <https://doc.rust-lang.org/stable/std/option/index.html#representation>.
unsafe impl<T> ZeroableOption for &mut T {}
// SAFETY: `Option<NonNull<T>>` is part of the option layout optimization guarantee:
// <https://doc.rust-lang.org/stable/std/option/index.html#representation>.
unsafe impl<T> ZeroableOption for NonNull<T> {}

/// 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<T: Zeroable>() -> impl Init<T> {
// 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
Expand All @@ -1584,6 +1548,21 @@ pub const fn zeroed<T: Zeroable>() -> 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<T: Zeroable>() -> impl Init<T> {
// 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.
Expand Down Expand Up @@ -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<Self>` 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<Self> {}` is sound.
pub unsafe trait ZeroableOption {}

// SAFETY: by the safety requirement of `ZeroableOption`, this is valid.
unsafe impl<T: ZeroableOption> Zeroable for Option<T> {}

macro_rules! impl_fn_zeroable_option {
([$($abi:literal),* $(,)?] $args:tt) => {
$(impl_fn_zeroable_option!({extern $abi} $args);)*
Expand All @@ -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:
// <https://doc.rust-lang.org/stable/std/option/index.html#representation>.
{<T: ?Sized>} &T,
// SAFETY: `Option<&mut T>` is part of the option layout optimization guarantee:
// <https://doc.rust-lang.org/stable/std/option/index.html#representation>.
{<T: ?Sized>} &mut T,
// SAFETY: `Option<NonNull<T>>` is part of the option layout optimization guarantee:
// <https://doc.rust-lang.org/stable/std/option/index.html#representation>.
{<T: ?Sized>} NonNull<T>,
// SAFETY: All zeros is equivalent to `None` (option layout optimization guarantee:
// <https://doc.rust-lang.org/stable/std/option/index.html#representation>).
NonZeroU8, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU128, NonZeroUsize,
Expand Down
8 changes: 8 additions & 0 deletions tests/ui/compile-fail/zeroable/option_string_not_zeroable.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
use pin_init::*;

#[derive(Zeroable)]
struct Foo {
x: Option<String>,
}

fn main() {}
23 changes: 23 additions & 0 deletions tests/ui/compile-fail/zeroable/option_string_not_zeroable.stderr
Original file line number Diff line number Diff line change
@@ -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<String>,
| ^^^^^^^^^^^^^^ the trait `ZeroableOption` is not implemented for `String`
|
= help: the following other types implement trait `ZeroableOption`:
&T
&mut T
Box<T>
NonNull<T>
NonZero<i128>
NonZero<i16>
NonZero<i32>
NonZero<i64>
and $N others
= note: required for `Option<String>` 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)
31 changes: 31 additions & 0 deletions tests/zeroing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@

use std::marker::PhantomPinned;

use core::{
num::{NonZeroI32, NonZeroU8, NonZeroUsize},
ptr::NonNull,
};
use pin_init::*;

const MARKS: usize = 64;
Expand Down Expand Up @@ -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<NonNull<u8>> = zeroed();
let non_zero_unsigned: Option<NonZeroUsize> = zeroed();
let non_zero_signed: Option<NonZeroI32> = 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<T: ZeroableOption>() {
let _: Option<T> = zeroed();
}

#[test]
fn zeroed_option_generic_compile_check() {
assert_zeroable_option::<&u8>();
assert_zeroable_option::<&mut u8>();
assert_zeroable_option::<NonNull<u8>>();
assert_zeroable_option::<NonZeroU8>();
}