Skip to content

Commit

Permalink
[derive] Support deriving NoCell (#667)
Browse files Browse the repository at this point in the history
Makes progress on #251
  • Loading branch information
joshlf committed Dec 6, 2023
1 parent b3b15e7 commit 949c7bb
Show file tree
Hide file tree
Showing 21 changed files with 838 additions and 301 deletions.
6 changes: 4 additions & 2 deletions src/byteorder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@ example of how it can be used for parsing UDP packets.
[`AsBytes`]: crate::AsBytes
[`Unaligned`]: crate::Unaligned"),
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
#[cfg_attr(any(feature = "derive", test), derive(KnownLayout, FromZeroes, FromBytes, AsBytes, Unaligned))]
#[cfg_attr(any(feature = "derive", test), derive(KnownLayout, NoCell, FromZeroes, FromBytes, AsBytes, Unaligned))]
#[repr(transparent)]
pub struct $name<O>([u8; $bytes], PhantomData<O>);
}
Expand All @@ -288,7 +288,9 @@ example of how it can be used for parsing UDP packets.
/// SAFETY:
/// `$name<O>` is `repr(transparent)`, and so it has the same layout
/// as its only non-zero field, which is a `u8` array. `u8` arrays
/// are `FromZeroes`, `FromBytes`, `AsBytes`, and `Unaligned`.
/// are `NoCell`, `FromZeroes`, `FromBytes`, `AsBytes`, and
/// `Unaligned`.
impl_or_verify!(O => NoCell for $name<O>);
impl_or_verify!(O => FromZeroes for $name<O>);
impl_or_verify!(O => FromBytes for $name<O>);
impl_or_verify!(O => AsBytes for $name<O>);
Expand Down
10 changes: 9 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,14 @@ pub use zerocopy_derive::Unaligned;
#[doc(hidden)]
pub use zerocopy_derive::KnownLayout;

// `pub use` separately here so that we can mark it `#[doc(hidden)]`.
//
// TODO(#251): Remove this or add a doc comment.
#[cfg(any(feature = "derive", test))]
#[cfg_attr(doc_cfg, doc(cfg(feature = "derive")))]
#[doc(hidden)]
pub use zerocopy_derive::NoCell;

use core::{
cell::{self, RefMut},
cmp::Ordering,
Expand Down Expand Up @@ -7966,7 +7974,7 @@ mod tests {
assert_impls!(Wrapping<NotZerocopy>: KnownLayout, !NoCell, !TryFromBytes, !FromZeroes, !FromBytes, !AsBytes, !Unaligned);
assert_impls!(Wrapping<UnsafeCell<()>>: KnownLayout, !NoCell, !TryFromBytes, !FromZeroes, !FromBytes, !AsBytes, !Unaligned);

assert_impls!(Unalign<u8>: KnownLayout, FromZeroes, FromBytes, AsBytes, Unaligned, !NoCell, !TryFromBytes);
assert_impls!(Unalign<u8>: KnownLayout, NoCell, FromZeroes, FromBytes, AsBytes, Unaligned, !TryFromBytes);
assert_impls!(Unalign<NotZerocopy>: Unaligned, !NoCell, !KnownLayout, !TryFromBytes, !FromZeroes, !FromBytes, !AsBytes);

assert_impls!([u8]: KnownLayout, NoCell, TryFromBytes, FromZeroes, FromBytes, AsBytes, Unaligned);
Expand Down
3 changes: 2 additions & 1 deletion src/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -697,6 +697,7 @@ pub(crate) mod testutil {
// By contrast, `AU64` is guaranteed to have alignment 8.
#[derive(
KnownLayout,
NoCell,
FromZeroes,
FromBytes,
AsBytes,
Expand Down Expand Up @@ -726,7 +727,7 @@ pub(crate) mod testutil {
}

#[derive(
FromZeroes, FromBytes, Eq, PartialEq, Ord, PartialOrd, Default, Debug, Copy, Clone,
NoCell, FromZeroes, FromBytes, Eq, PartialEq, Ord, PartialOrd, Default, Debug, Copy, Clone,
)]
#[repr(C)]
pub(crate) struct Nested<T, U: ?Sized> {
Expand Down
5 changes: 4 additions & 1 deletion src/wrappers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ use super::*;
#[derive(Default, Copy)]
#[cfg_attr(
any(feature = "derive", test),
derive(KnownLayout, FromZeroes, FromBytes, AsBytes, Unaligned)
derive(NoCell, KnownLayout, FromZeroes, FromBytes, AsBytes, Unaligned)
)]
#[repr(C, packed)]
pub struct Unalign<T>(T);
Expand All @@ -74,7 +74,10 @@ safety_comment! {
/// alignment of `T`, and so we don't require that `T: Unaligned`
/// - `Unalign<T>` has the same bit validity as `T`, and so it is
/// `FromZeroes`, `FromBytes`, or `AsBytes` exactly when `T` is as well.
/// - `NoCell`: `Unalign<T>` has the same fields as `T`, so it contains
/// `UnsafeCell`s exactly when `T` does.
impl_or_verify!(T => Unaligned for Unalign<T>);
impl_or_verify!(T: NoCell => NoCell for Unalign<T>);
impl_or_verify!(T: FromZeroes => FromZeroes for Unalign<T>);
impl_or_verify!(T: FromBytes => FromBytes for Unalign<T>);
impl_or_verify!(T: AsBytes => AsBytes for Unalign<T>);
Expand Down
18 changes: 18 additions & 0 deletions zerocopy-derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,23 @@ pub fn derive_known_layout(ts: proc_macro::TokenStream) -> proc_macro::TokenStre
.into()
}

#[proc_macro_derive(NoCell)]
pub fn derive_no_cell(ts: proc_macro::TokenStream) -> proc_macro::TokenStream {
let ast = syn::parse_macro_input!(ts as DeriveInput);
match &ast.data {
Data::Struct(strct) => {
impl_block(&ast, strct, Trait::NoCell, RequireBoundedFields::Yes, false, None, None)
}
Data::Enum(enm) => {
impl_block(&ast, enm, Trait::NoCell, RequireBoundedFields::Yes, false, None, None)
}
Data::Union(unn) => {
impl_block(&ast, unn, Trait::NoCell, RequireBoundedFields::Yes, false, None, None)
}
}
.into()
}

#[proc_macro_derive(FromZeroes)]
pub fn derive_from_zeroes(ts: proc_macro::TokenStream) -> proc_macro::TokenStream {
let ast = syn::parse_macro_input!(ts as DeriveInput);
Expand Down Expand Up @@ -637,6 +654,7 @@ impl PaddingCheck {
#[derive(Debug, Eq, PartialEq)]
enum Trait {
KnownLayout,
NoCell,
FromZeroes,
FromBytes,
AsBytes,
Expand Down
50 changes: 50 additions & 0 deletions zerocopy-derive/tests/enum_no_cell.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// Copyright 2022 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#![allow(warnings)]

mod util;

use {
core::cell::UnsafeCell, core::marker::PhantomData, static_assertions::assert_impl_all,
zerocopy::NoCell,
};

#[derive(NoCell)]
enum Foo {
A,
}

assert_impl_all!(Foo: NoCell);

#[derive(NoCell)]
enum Bar {
A = 0,
}

assert_impl_all!(Bar: NoCell);

#[derive(NoCell)]
enum Baz {
A = 1,
B = 0,
}

assert_impl_all!(Baz: NoCell);

// Deriving `NoCell` should work if the enum has bounded parameters.

#[derive(NoCell)]
#[repr(C)]
enum WithParams<'a: 'b, 'b: 'a, const N: usize, T: 'a + 'b + NoCell>
where
'a: 'b,
'b: 'a,
T: 'a + 'b + NoCell,
{
Variant([T; N], PhantomData<&'a &'b ()>),
UnsafeCell(PhantomData<UnsafeCell<()>>, &'a UnsafeCell<()>),
}

assert_impl_all!(WithParams<'static, 'static, 42, u8>: NoCell);
101 changes: 101 additions & 0 deletions zerocopy-derive/tests/struct_no_cell.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
// Copyright 2022 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#![allow(warnings)]

#[macro_use]
mod util;

use std::{cell::UnsafeCell, marker::PhantomData, option::IntoIter};

use {
static_assertions::{assert_impl_all, assert_not_impl_any},
zerocopy::NoCell,
};

use crate::util::AU16;

#[derive(NoCell)]
struct Zst;

assert_impl_all!(Zst: NoCell);

#[derive(NoCell)]
struct One {
a: bool,
}

assert_impl_all!(One: NoCell);

#[derive(NoCell)]
struct Two {
a: bool,
b: Zst,
}

assert_impl_all!(Two: NoCell);

#[derive(NoCell)]
struct Three {
a: [u8],
}

assert_impl_all!(Three: NoCell);

#[derive(NoCell)]
struct Four<'a> {
field: &'a UnsafeCell<u8>,
}

assert_impl_all!(Four<'static>: NoCell);

#[derive(NoCell)]
struct TypeParams<'a, T, U, I: Iterator> {
a: I::Item,
b: u8,
c: PhantomData<&'a [u8]>,
d: PhantomData<&'static str>,
e: PhantomData<String>,
f: PhantomData<U>,
g: T,
}

assert_impl_all!(TypeParams<'static, (), (), IntoIter<()>>: NoCell);
assert_impl_all!(TypeParams<'static, AU16, AU16, IntoIter<()>>: NoCell);
assert_impl_all!(TypeParams<'static, AU16, UnsafeCell<u8>, IntoIter<()>>: NoCell);
assert_not_impl_any!(TypeParams<'static, UnsafeCell<()>, (), IntoIter<()>>: NoCell);
assert_not_impl_any!(TypeParams<'static, [UnsafeCell<u8>; 0], (), IntoIter<()>>: NoCell);
assert_not_impl_any!(TypeParams<'static, (), (), IntoIter<UnsafeCell<()>>>: NoCell);

trait Trait {
type Assoc;
}

impl<T> Trait for UnsafeCell<T> {
type Assoc = T;
}

#[derive(NoCell)]
struct WithAssocType<T: Trait> {
field: <T as Trait>::Assoc,
}

assert_impl_all!(WithAssocType<UnsafeCell<u8>>: NoCell);

// Deriving `NoCell` should work if the struct has bounded parameters.

#[derive(NoCell)]
#[repr(C)]
struct WithParams<'a: 'b, 'b: 'a, const N: usize, T: 'a + 'b + NoCell>(
[T; N],
PhantomData<&'a &'b ()>,
PhantomData<UnsafeCell<()>>,
&'a UnsafeCell<()>,
)
where
'a: 'b,
'b: 'a,
T: 'a + 'b + NoCell;

assert_impl_all!(WithParams<'static, 'static, 42, u8>: NoCell);
Loading

0 comments on commit 949c7bb

Please sign in to comment.