Skip to content

[derive] Support deriving NoCell #667

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Dec 6, 2023
Merged
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
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