Skip to content

Commit

Permalink
Implement TryFromBytes for Option<F> for fn types (#904)
Browse files Browse the repository at this point in the history
Makes progress on #5
  • Loading branch information
joshlf authored Feb 17, 2024
1 parent d52b2c4 commit 2933323
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 27 deletions.
62 changes: 45 additions & 17 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3462,12 +3462,20 @@ safety_comment! {
);
unsafe_impl!(T => FromZeros for Option<NonNull<T>>);
unsafe_impl_for_power_set!(A, B, C, D, E, F, G, H, I, J, K, L -> M => FromZeros for opt_fn!(...));
unsafe_impl_for_power_set!(
A, B, C, D, E, F, G, H, I, J, K, L -> M => TryFromBytes for opt_fn!(...);
|c: Maybe<Self>| pointer::is_zeroed(c)
);
unsafe_impl_for_power_set!(A, B, C, D, E, F, G, H, I, J, K, L -> M => FromZeros for opt_extern_c_fn!(...));
unsafe_impl_for_power_set!(
A, B, C, D, E, F, G, H, I, J, K, L -> M => TryFromBytes for opt_extern_c_fn!(...);
|c: Maybe<Self>| pointer::is_zeroed(c)
);
}

safety_comment! {
/// SAFETY:
/// TODO
/// TODO(#896): Write this safety proof before the next stable release.
unsafe_impl_for_power_set!(A, B, C, D, E, F, G, H, I, J, K, L -> M => NoCell for opt_fn!(...));
unsafe_impl_for_power_set!(A, B, C, D, E, F, G, H, I, J, K, L -> M => NoCell for opt_extern_c_fn!(...));
}
Expand Down Expand Up @@ -8040,6 +8048,27 @@ mod tests {
fn with_failing_test_cases<F: Fn(&mut [u8])>(_f: F) {}
}

macro_rules! impl_try_from_bytes_testable_for_null_pointer_optimization {
($($tys:ty),*) => {
$(
impl TryFromBytesTestable for Option<$tys> {
fn with_passing_test_cases<F: Fn(&Self)>(f: F) {
// Test with a zeroed value.
f(&None);
}

fn with_failing_test_cases<F: Fn(&mut [u8])>(f: F) {
for pos in 0..mem::size_of::<Self>() {
let mut bytes = [0u8; mem::size_of::<Self>()];
bytes[pos] = 0x01;
f(&mut bytes[..]);
}
}
}
)*
};
}

// Implements `TryFromBytesTestable`.
macro_rules! impl_try_from_bytes_testable {
// Base case for recursion (when the list of types has run out).
Expand Down Expand Up @@ -8081,6 +8110,17 @@ mod tests {
};
}

impl_try_from_bytes_testable_for_null_pointer_optimization!(
Box<UnsafeCell<NotZerocopy>>,
&'static UnsafeCell<NotZerocopy>,
&'static mut UnsafeCell<NotZerocopy>,
NonNull<UnsafeCell<NotZerocopy>>,
fn(),
FnManyArgs,
extern "C" fn(),
ECFnManyArgs
);

// Note that these impls are only for types which are not `FromBytes`.
// `FromBytes` types are covered by a preceding blanket impl.
impl_try_from_bytes_testable!(
Expand Down Expand Up @@ -8121,18 +8161,6 @@ mod tests {
Wrapping<bool>
=> @success Wrapping(false), Wrapping(true),
@failure 2u8, 0xFFu8;
Option<Box<UnsafeCell<NotZerocopy>>>
=> @success None,
@failure [0x01; mem::size_of::<Option<Box<UnsafeCell<NotZerocopy>>>>()];
Option<&'static UnsafeCell<NotZerocopy>>
=> @success None,
@failure [0x01; mem::size_of::<Option<&'static UnsafeCell<NotZerocopy>>>()];
Option<&'static mut UnsafeCell<NotZerocopy>>
=> @success None,
@failure [0x01; mem::size_of::<Option<&'static mut UnsafeCell<NotZerocopy>>>()];
Option<NonNull<UnsafeCell<NotZerocopy>>>
=> @success None,
@failure [0x01; mem::size_of::<Option<NonNull<UnsafeCell<NotZerocopy>>>>()];
*const NotZerocopy
=> @success ptr::null::<NotZerocopy>(),
@failure [0x01; mem::size_of::<*const NotZerocopy>()];
Expand Down Expand Up @@ -8512,10 +8540,10 @@ mod tests {
assert_impls!(Option<&'static mut [UnsafeCell<NotZerocopy>]>: KnownLayout, NoCell, !TryFromBytes, !FromZeros, !FromBytes, !IntoBytes, !Unaligned);
assert_impls!(Option<NonNull<UnsafeCell<NotZerocopy>>>: KnownLayout, TryFromBytes, FromZeros, NoCell, !FromBytes, !IntoBytes, !Unaligned);
assert_impls!(Option<NonNull<[UnsafeCell<NotZerocopy>]>>: KnownLayout, NoCell, !TryFromBytes, !FromZeros, !FromBytes, !IntoBytes, !Unaligned);
assert_impls!(Option<fn()>: KnownLayout, NoCell, FromZeros, !TryFromBytes, !FromBytes, !IntoBytes, !Unaligned);
assert_impls!(Option<FnManyArgs>: KnownLayout, NoCell, FromZeros, !TryFromBytes, !FromBytes, !IntoBytes, !Unaligned);
assert_impls!(Option<extern "C" fn()>: KnownLayout, NoCell, FromZeros, !TryFromBytes, !FromBytes, !IntoBytes, !Unaligned);
assert_impls!(Option<ECFnManyArgs>: KnownLayout, NoCell, FromZeros, !TryFromBytes, !FromBytes, !IntoBytes, !Unaligned);
assert_impls!(Option<fn()>: KnownLayout, NoCell, TryFromBytes, FromZeros, !FromBytes, !IntoBytes, !Unaligned);
assert_impls!(Option<FnManyArgs>: KnownLayout, NoCell, TryFromBytes, FromZeros, !FromBytes, !IntoBytes, !Unaligned);
assert_impls!(Option<extern "C" fn()>: KnownLayout, NoCell, TryFromBytes, FromZeros, !FromBytes, !IntoBytes, !Unaligned);
assert_impls!(Option<ECFnManyArgs>: KnownLayout, NoCell, TryFromBytes, FromZeros, !FromBytes, !IntoBytes, !Unaligned);

assert_impls!(PhantomData<NotZerocopy>: KnownLayout, NoCell, TryFromBytes, FromZeros, FromBytes, IntoBytes, Unaligned);
assert_impls!(PhantomData<UnsafeCell<()>>: KnownLayout, NoCell, TryFromBytes, FromZeros, FromBytes, IntoBytes, Unaligned);
Expand Down
38 changes: 28 additions & 10 deletions src/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -213,18 +213,36 @@ macro_rules! unsafe_impl {
/// unsafe impl<A, B> Foo for type!(A, B) { ... }
/// ```
macro_rules! unsafe_impl_for_power_set {
($first:ident $(, $rest:ident)* $(-> $ret:ident)? => $trait:ident for $macro:ident!(...)) => {
unsafe_impl_for_power_set!($($rest),* $(-> $ret)? => $trait for $macro!(...));
unsafe_impl_for_power_set!(@impl $first $(, $rest)* $(-> $ret)? => $trait for $macro!(...));
(
$first:ident $(, $rest:ident)* $(-> $ret:ident)? => $trait:ident for $macro:ident!(...)
$(; |$candidate:ident $(: MaybeAligned<$ref_repr:ty>)? $(: Maybe<$ptr_repr:ty>)?| $is_bit_valid:expr)?
) => {
unsafe_impl_for_power_set!(
$($rest),* $(-> $ret)? => $trait for $macro!(...)
$(; |$candidate $(: MaybeAligned<$ref_repr>)? $(: Maybe<$ptr_repr>)?| $is_bit_valid)?
);
unsafe_impl_for_power_set!(
@impl $first $(, $rest)* $(-> $ret)? => $trait for $macro!(...)
$(; |$candidate $(: MaybeAligned<$ref_repr>)? $(: Maybe<$ptr_repr>)?| $is_bit_valid)?
);
};
($(-> $ret:ident)? => $trait:ident for $macro:ident!(...)) => {
unsafe_impl_for_power_set!(@impl $(-> $ret)? => $trait for $macro!(...));
(
$(-> $ret:ident)? => $trait:ident for $macro:ident!(...)
$(; |$candidate:ident $(: MaybeAligned<$ref_repr:ty>)? $(: Maybe<$ptr_repr:ty>)?| $is_bit_valid:expr)?
) => {
unsafe_impl_for_power_set!(
@impl $(-> $ret)? => $trait for $macro!(...)
$(; |$candidate $(: MaybeAligned<$ref_repr>)? $(: Maybe<$ptr_repr>)?| $is_bit_valid)?
);
};
(@impl $($vars:ident),* $(-> $ret:ident)? => $trait:ident for $macro:ident!(...)) => {
unsafe impl<$($vars,)* $($ret)?> $trait for $macro!($($vars),* $(-> $ret)?) {
#[allow(clippy::missing_inline_in_public_items)]
fn only_derive_is_allowed_to_implement_this_trait() {}
}
(
@impl $($vars:ident),* $(-> $ret:ident)? => $trait:ident for $macro:ident!(...)
$(; |$candidate:ident $(: MaybeAligned<$ref_repr:ty>)? $(: Maybe<$ptr_repr:ty>)?| $is_bit_valid:expr)?
) => {
unsafe_impl!(
$($vars,)* $($ret)? => $trait for $macro!($($vars),* $(-> $ret)?)
$(; |$candidate $(: MaybeAligned<$ref_repr>)? $(: Maybe<$ptr_repr>)?| $is_bit_valid)?
);
};
}

Expand Down

0 comments on commit 2933323

Please sign in to comment.