Skip to content

Commit 3234ced

Browse files
committed
[derive] Support deriving NoCell
Makes progress on #251
1 parent b3b15e7 commit 3234ced

21 files changed

+838
-301
lines changed

src/byteorder.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,7 @@ example of how it can be used for parsing UDP packets.
276276
[`AsBytes`]: crate::AsBytes
277277
[`Unaligned`]: crate::Unaligned"),
278278
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
279-
#[cfg_attr(any(feature = "derive", test), derive(KnownLayout, FromZeroes, FromBytes, AsBytes, Unaligned))]
279+
#[cfg_attr(any(feature = "derive", test), derive(KnownLayout, NoCell, FromZeroes, FromBytes, AsBytes, Unaligned))]
280280
#[repr(transparent)]
281281
pub struct $name<O>([u8; $bytes], PhantomData<O>);
282282
}
@@ -288,7 +288,9 @@ example of how it can be used for parsing UDP packets.
288288
/// SAFETY:
289289
/// `$name<O>` is `repr(transparent)`, and so it has the same layout
290290
/// as its only non-zero field, which is a `u8` array. `u8` arrays
291-
/// are `FromZeroes`, `FromBytes`, `AsBytes`, and `Unaligned`.
291+
/// are `NoCell`, `FromZeroes`, `FromBytes`, `AsBytes`, and
292+
/// `Unaligned`.
293+
impl_or_verify!(O => NoCell for $name<O>);
292294
impl_or_verify!(O => FromZeroes for $name<O>);
293295
impl_or_verify!(O => FromBytes for $name<O>);
294296
impl_or_verify!(O => AsBytes for $name<O>);

src/lib.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,14 @@ pub use zerocopy_derive::Unaligned;
264264
#[doc(hidden)]
265265
pub use zerocopy_derive::KnownLayout;
266266

267+
// `pub use` separately here so that we can mark it `#[doc(hidden)]`.
268+
//
269+
// TODO(#251): Remove this or add a doc comment.
270+
#[cfg(any(feature = "derive", test))]
271+
#[cfg_attr(doc_cfg, doc(cfg(feature = "derive")))]
272+
#[doc(hidden)]
273+
pub use zerocopy_derive::NoCell;
274+
267275
use core::{
268276
cell::{self, RefMut},
269277
cmp::Ordering,
@@ -7966,7 +7974,7 @@ mod tests {
79667974
assert_impls!(Wrapping<NotZerocopy>: KnownLayout, !NoCell, !TryFromBytes, !FromZeroes, !FromBytes, !AsBytes, !Unaligned);
79677975
assert_impls!(Wrapping<UnsafeCell<()>>: KnownLayout, !NoCell, !TryFromBytes, !FromZeroes, !FromBytes, !AsBytes, !Unaligned);
79687976

7969-
assert_impls!(Unalign<u8>: KnownLayout, FromZeroes, FromBytes, AsBytes, Unaligned, !NoCell, !TryFromBytes);
7977+
assert_impls!(Unalign<u8>: KnownLayout, NoCell, FromZeroes, FromBytes, AsBytes, Unaligned, !TryFromBytes);
79707978
assert_impls!(Unalign<NotZerocopy>: Unaligned, !NoCell, !KnownLayout, !TryFromBytes, !FromZeroes, !FromBytes, !AsBytes);
79717979

79727980
assert_impls!([u8]: KnownLayout, NoCell, TryFromBytes, FromZeroes, FromBytes, AsBytes, Unaligned);

src/util.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -697,6 +697,7 @@ pub(crate) mod testutil {
697697
// By contrast, `AU64` is guaranteed to have alignment 8.
698698
#[derive(
699699
KnownLayout,
700+
NoCell,
700701
FromZeroes,
701702
FromBytes,
702703
AsBytes,
@@ -726,7 +727,7 @@ pub(crate) mod testutil {
726727
}
727728

728729
#[derive(
729-
FromZeroes, FromBytes, Eq, PartialEq, Ord, PartialOrd, Default, Debug, Copy, Clone,
730+
NoCell, FromZeroes, FromBytes, Eq, PartialEq, Ord, PartialOrd, Default, Debug, Copy, Clone,
730731
)]
731732
#[repr(C)]
732733
pub(crate) struct Nested<T, U: ?Sized> {

src/wrappers.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ use super::*;
6060
#[derive(Default, Copy)]
6161
#[cfg_attr(
6262
any(feature = "derive", test),
63-
derive(KnownLayout, FromZeroes, FromBytes, AsBytes, Unaligned)
63+
derive(NoCell, KnownLayout, FromZeroes, FromBytes, AsBytes, Unaligned)
6464
)]
6565
#[repr(C, packed)]
6666
pub struct Unalign<T>(T);
@@ -74,7 +74,10 @@ safety_comment! {
7474
/// alignment of `T`, and so we don't require that `T: Unaligned`
7575
/// - `Unalign<T>` has the same bit validity as `T`, and so it is
7676
/// `FromZeroes`, `FromBytes`, or `AsBytes` exactly when `T` is as well.
77+
/// - `NoCell`: `Unalign<T>` has the same fields as `T`, so it contains
78+
/// `UnsafeCell`s exactly when `T` does.
7779
impl_or_verify!(T => Unaligned for Unalign<T>);
80+
impl_or_verify!(T: NoCell => NoCell for Unalign<T>);
7881
impl_or_verify!(T: FromZeroes => FromZeroes for Unalign<T>);
7982
impl_or_verify!(T: FromBytes => FromBytes for Unalign<T>);
8083
impl_or_verify!(T: AsBytes => AsBytes for Unalign<T>);

zerocopy-derive/src/lib.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,23 @@ pub fn derive_known_layout(ts: proc_macro::TokenStream) -> proc_macro::TokenStre
243243
.into()
244244
}
245245

246+
#[proc_macro_derive(NoCell)]
247+
pub fn derive_no_cell(ts: proc_macro::TokenStream) -> proc_macro::TokenStream {
248+
let ast = syn::parse_macro_input!(ts as DeriveInput);
249+
match &ast.data {
250+
Data::Struct(strct) => {
251+
impl_block(&ast, strct, Trait::NoCell, RequireBoundedFields::Yes, false, None, None)
252+
}
253+
Data::Enum(enm) => {
254+
impl_block(&ast, enm, Trait::NoCell, RequireBoundedFields::Yes, false, None, None)
255+
}
256+
Data::Union(unn) => {
257+
impl_block(&ast, unn, Trait::NoCell, RequireBoundedFields::Yes, false, None, None)
258+
}
259+
}
260+
.into()
261+
}
262+
246263
#[proc_macro_derive(FromZeroes)]
247264
pub fn derive_from_zeroes(ts: proc_macro::TokenStream) -> proc_macro::TokenStream {
248265
let ast = syn::parse_macro_input!(ts as DeriveInput);
@@ -637,6 +654,7 @@ impl PaddingCheck {
637654
#[derive(Debug, Eq, PartialEq)]
638655
enum Trait {
639656
KnownLayout,
657+
NoCell,
640658
FromZeroes,
641659
FromBytes,
642660
AsBytes,

zerocopy-derive/tests/enum_no_cell.rs

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// Copyright 2022 The Fuchsia Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
#![allow(warnings)]
6+
7+
mod util;
8+
9+
use {
10+
core::cell::UnsafeCell, core::marker::PhantomData, static_assertions::assert_impl_all,
11+
zerocopy::NoCell,
12+
};
13+
14+
#[derive(NoCell)]
15+
enum Foo {
16+
A,
17+
}
18+
19+
assert_impl_all!(Foo: NoCell);
20+
21+
#[derive(NoCell)]
22+
enum Bar {
23+
A = 0,
24+
}
25+
26+
assert_impl_all!(Bar: NoCell);
27+
28+
#[derive(NoCell)]
29+
enum Baz {
30+
A = 1,
31+
B = 0,
32+
}
33+
34+
assert_impl_all!(Baz: NoCell);
35+
36+
// Deriving `NoCell` should work if the enum has bounded parameters.
37+
38+
#[derive(NoCell)]
39+
#[repr(C)]
40+
enum WithParams<'a: 'b, 'b: 'a, const N: usize, T: 'a + 'b + NoCell>
41+
where
42+
'a: 'b,
43+
'b: 'a,
44+
T: 'a + 'b + NoCell,
45+
{
46+
Variant([T; N], PhantomData<&'a &'b ()>),
47+
UnsafeCell(PhantomData<UnsafeCell<()>>, &'a UnsafeCell<()>),
48+
}
49+
50+
assert_impl_all!(WithParams<'static, 'static, 42, u8>: NoCell);
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
// Copyright 2022 The Fuchsia Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
#![allow(warnings)]
6+
7+
#[macro_use]
8+
mod util;
9+
10+
use std::{cell::UnsafeCell, marker::PhantomData, option::IntoIter};
11+
12+
use {
13+
static_assertions::{assert_impl_all, assert_not_impl_any},
14+
zerocopy::NoCell,
15+
};
16+
17+
use crate::util::AU16;
18+
19+
#[derive(NoCell)]
20+
struct Zst;
21+
22+
assert_impl_all!(Zst: NoCell);
23+
24+
#[derive(NoCell)]
25+
struct One {
26+
a: bool,
27+
}
28+
29+
assert_impl_all!(One: NoCell);
30+
31+
#[derive(NoCell)]
32+
struct Two {
33+
a: bool,
34+
b: Zst,
35+
}
36+
37+
assert_impl_all!(Two: NoCell);
38+
39+
#[derive(NoCell)]
40+
struct Three {
41+
a: [u8],
42+
}
43+
44+
assert_impl_all!(Three: NoCell);
45+
46+
#[derive(NoCell)]
47+
struct Four<'a> {
48+
field: &'a UnsafeCell<u8>,
49+
}
50+
51+
assert_impl_all!(Four<'static>: NoCell);
52+
53+
#[derive(NoCell)]
54+
struct TypeParams<'a, T, U, I: Iterator> {
55+
a: I::Item,
56+
b: u8,
57+
c: PhantomData<&'a [u8]>,
58+
d: PhantomData<&'static str>,
59+
e: PhantomData<String>,
60+
f: PhantomData<U>,
61+
g: T,
62+
}
63+
64+
assert_impl_all!(TypeParams<'static, (), (), IntoIter<()>>: NoCell);
65+
assert_impl_all!(TypeParams<'static, AU16, AU16, IntoIter<()>>: NoCell);
66+
assert_impl_all!(TypeParams<'static, AU16, UnsafeCell<u8>, IntoIter<()>>: NoCell);
67+
assert_not_impl_any!(TypeParams<'static, UnsafeCell<()>, (), IntoIter<()>>: NoCell);
68+
assert_not_impl_any!(TypeParams<'static, [UnsafeCell<u8>; 0], (), IntoIter<()>>: NoCell);
69+
assert_not_impl_any!(TypeParams<'static, (), (), IntoIter<UnsafeCell<()>>>: NoCell);
70+
71+
trait Trait {
72+
type Assoc;
73+
}
74+
75+
impl<T> Trait for UnsafeCell<T> {
76+
type Assoc = T;
77+
}
78+
79+
#[derive(NoCell)]
80+
struct WithAssocType<T: Trait> {
81+
field: <T as Trait>::Assoc,
82+
}
83+
84+
assert_impl_all!(WithAssocType<UnsafeCell<u8>>: NoCell);
85+
86+
// Deriving `NoCell` should work if the struct has bounded parameters.
87+
88+
#[derive(NoCell)]
89+
#[repr(C)]
90+
struct WithParams<'a: 'b, 'b: 'a, const N: usize, T: 'a + 'b + NoCell>(
91+
[T; N],
92+
PhantomData<&'a &'b ()>,
93+
PhantomData<UnsafeCell<()>>,
94+
&'a UnsafeCell<()>,
95+
)
96+
where
97+
'a: 'b,
98+
'b: 'a,
99+
T: 'a + 'b + NoCell;
100+
101+
assert_impl_all!(WithParams<'static, 'static, 42, u8>: NoCell);

0 commit comments

Comments
 (0)