Skip to content

Commit cb7c1ea

Browse files
committed
Introduce derive(most_traits) and rename unstable linux cfg
Renames `zerocopy_unstable_derive_on_error` to `zerocopy_unstable_linux`. Adds unstable `derive(most_traits)` which attempts to quietly derive all zerocopy traits, but excludes traits like `Hash` for which zerocopy provides optimized derives. gherrit-pr-id: G713b9f3eb6365b11e4e1777ee69e75a6fae758d3
1 parent c97143c commit cb7c1ea

14 files changed

Lines changed: 194 additions & 68 deletions

File tree

tools/cargo-zerocopy/src/main.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -267,7 +267,7 @@ fn install_targets_or_exit(version: &str, targets: &[String]) -> Result<(), Erro
267267
fn get_rustflags(name: &str) -> String {
268268
// See #1792 for context on zerocopy_derive_union_into_bytes.
269269
let mut flags =
270-
"--cfg zerocopy_unstable_derive_on_error --cfg zerocopy_derive_union_into_bytes --cfg __ZEROCOPY_INTERNAL_USE_ONLY_DEV_MODE"
270+
"--cfg zerocopy_unstable_linux --cfg zerocopy_derive_union_into_bytes --cfg __ZEROCOPY_INTERNAL_USE_ONLY_DEV_MODE"
271271
.to_string();
272272
flags += &format!(" --cfg __ZEROCOPY_INTERNAL_USE_ONLY_TOOLCHAIN=\"{name}\"");
273273

zerocopy/build.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ fn main() {
9595
println!("cargo:rustc-check-cfg=cfg(coverage_nightly)");
9696
println!("cargo:rustc-check-cfg=cfg(zerocopy_inline_always)");
9797
println!("cargo:rustc-check-cfg=cfg(zerocopy_unstable_ptr)");
98+
println!("cargo:rustc-check-cfg=cfg(zerocopy_unstable_linux)");
9899
println!("cargo:rustc-check-cfg=cfg(no_fp_fmt_parse)");
99100
}
100101

zerocopy/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -433,6 +433,9 @@ const _: () = {
433433
WARNING
434434
};
435435

436+
#[doc(hidden)]
437+
#[cfg(all(any(feature = "derive", test), zerocopy_unstable_linux))]
438+
pub use zerocopy_derive::most_traits;
436439
/// Implements [`KnownLayout`].
437440
///
438441
/// This derive analyzes various aspects of a type's layout that are needed for

zerocopy/testutil/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -127,13 +127,13 @@ impl UiTestRunner {
127127
let mut command = Command::new("cargo");
128128
command.current_dir(workspace_root.clone());
129129
// We strip `--cfg zerocopy_derive_union_into_bytes` and `--cfg
130-
// zerocopy_unstable_derive_on_error` from `RUSTFLAGS` so that the
130+
// zerocopy_unstable_linux` from `RUSTFLAGS` so that the
131131
// `zerocopy-derive` proc macro is built without them. This ensures it
132132
// generates the feature-gate checks into the UI tests, which we can
133133
// then explicitly enable or disable via `rustc_args`.
134134
let mut rustflags = env::var("RUSTFLAGS").unwrap_or_default();
135135
let cfgs_to_strip =
136-
["--cfg zerocopy_derive_union_into_bytes", "--cfg zerocopy_unstable_derive_on_error"];
136+
["--cfg zerocopy_derive_union_into_bytes", "--cfg zerocopy_unstable_linux"];
137137
for &cfg in &cfgs_to_strip {
138138
rustflags = rustflags.replace(cfg, "");
139139
}

zerocopy/zerocopy-derive/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ exclude = [".*", "tests/enum_from_bytes.rs", "tests/ui-nightly/enum_from_bytes_u
2626
# See #1792 for more context.
2727
unexpected_cfgs = { level = "warn", check-cfg = [
2828
'cfg(zerocopy_derive_union_into_bytes)',
29-
'cfg(zerocopy_unstable_derive_on_error)',
29+
'cfg(zerocopy_unstable_linux)',
3030
] }
3131

3232
[lib]

zerocopy/zerocopy-derive/src/derive/mod.rs

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ use crate::{
1313
util::{Ctx, DataExt, FieldBounds, ImplBlockBuilder, Trait},
1414
};
1515

16-
pub(crate) fn derive_immutable(ctx: &Ctx, _top_level: Trait) -> TokenStream {
17-
match &ctx.ast.data {
16+
pub(crate) fn derive_immutable(ctx: &Ctx, _top_level: Trait) -> Result<TokenStream, Error> {
17+
Ok(match &ctx.ast.data {
1818
Data::Struct(strct) => {
1919
ImplBlockBuilder::new(ctx, strct, Trait::Immutable, FieldBounds::ALL_SELF).build()
2020
}
@@ -24,7 +24,7 @@ pub(crate) fn derive_immutable(ctx: &Ctx, _top_level: Trait) -> TokenStream {
2424
Data::Union(unn) => {
2525
ImplBlockBuilder::new(ctx, unn, Trait::Immutable, FieldBounds::ALL_SELF).build()
2626
}
27-
}
27+
})
2828
}
2929

3030
pub(crate) fn derive_hash(ctx: &Ctx, _top_level: Trait) -> Result<TokenStream, Error> {
@@ -95,16 +95,20 @@ pub(crate) fn derive_split_at(ctx: &Ctx, _top_level: Trait) -> Result<TokenStrea
9595
match &ctx.ast.data {
9696
Data::Struct(_) => {}
9797
Data::Enum(_) | Data::Union(_) => {
98-
return Err(Error::new(Span::call_site(), "can only be applied to structs"));
98+
return ctx
99+
.error_or_skip(Error::new(Span::call_site(), "can only be applied to structs"));
99100
}
100101
};
101102

102103
if repr.get_packed().is_some() {
103-
return Err(Error::new(Span::call_site(), "must not have #[repr(packed)] attribute"));
104+
return ctx.error_or_skip(Error::new(
105+
Span::call_site(),
106+
"must not have #[repr(packed)] attribute",
107+
));
104108
}
105109

106110
if !(repr.is_c() || repr.is_transparent()) {
107-
return Err(Error::new(
111+
return ctx.error_or_skip(Error::new(
108112
Span::call_site(),
109113
"must have #[repr(C)] or #[repr(transparent)] in order to guarantee this type's layout is splitable",
110114
));
@@ -114,7 +118,7 @@ pub(crate) fn derive_split_at(ctx: &Ctx, _top_level: Trait) -> Result<TokenStrea
114118
let trailing_field = if let Some(((_, _, trailing_field), _)) = fields.split_last() {
115119
trailing_field
116120
} else {
117-
return Err(Error::new(Span::call_site(), "must at least one field"));
121+
return ctx.error_or_skip(Error::new(Span::call_site(), "must at least one field"));
118122
};
119123

120124
let zerocopy_crate = &ctx.zerocopy_crate;

zerocopy/zerocopy-derive/src/lib.rs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,40 @@ derive!(ByteHash => derive_hash => crate::derive::derive_hash);
127127
derive!(ByteEq => derive_eq => crate::derive::derive_eq);
128128
derive!(SplitAt => derive_split_at => crate::derive::derive_split_at);
129129

130+
#[cfg_attr(zerocopy_unstable_linux, doc(hidden))]
131+
#[proc_macro_derive(most_traits, attributes(zerocopy))]
132+
pub fn most_traits(ts: proc_macro::TokenStream) -> proc_macro::TokenStream {
133+
let ast = syn::parse_macro_input!(ts as DeriveInput);
134+
let ctx = match Ctx::try_from_derive_input(ast) {
135+
Ok(ctx) => ctx,
136+
Err(e) => return e.into_compile_error().into(),
137+
}
138+
.skip_on_error();
139+
140+
// top-level traits for which to attempt a derive
141+
let derives: [(fn(&Ctx, Trait) -> _, _); 6] = [
142+
(crate::derive::known_layout::derive, Trait::KnownLayout),
143+
(crate::derive::derive_immutable, Trait::Immutable),
144+
(crate::derive::from_bytes::derive_from_bytes, Trait::FromBytes),
145+
(crate::derive::into_bytes::derive_into_bytes, Trait::IntoBytes),
146+
(crate::derive::derive_split_at, Trait::SplitAt),
147+
(crate::derive::unaligned::derive_unaligned, Trait::Unaligned),
148+
];
149+
150+
let mut tokens = proc_macro2::TokenStream::new();
151+
for (derive, t) in derives {
152+
tokens.extend(derive(&ctx, t))
153+
}
154+
155+
// We wrap in `const_block` as a backstop in case any derive fails
156+
// to wrap its output in `const_block` (and thus fails to annotate)
157+
// with the full set of `#[allow(...)]` attributes).
158+
let ts = const_block([Some(tokens)]);
159+
#[cfg(test)]
160+
crate::util::testutil::check_hygiene(ts.clone());
161+
ts.into()
162+
}
163+
130164
/// Deprecated: prefer [`FromZeros`] instead.
131165
#[deprecated(since = "0.8.0", note = "`FromZeroes` was renamed to `FromZeros`")]
132166
#[doc(hidden)]

zerocopy/zerocopy-derive/src/util.rs

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,11 @@ impl Ctx {
9696
}
9797
}
9898

99+
pub(crate) fn skip_on_error(mut self) -> Self {
100+
self.skip_on_error = true;
101+
self
102+
}
103+
99104
pub(crate) fn core_path(&self) -> TokenStream {
100105
let zerocopy_crate = &self.zerocopy_crate;
101106
quote!(#zerocopy_crate::util::macro_util::core_reexport)
@@ -104,20 +109,21 @@ impl Ctx {
104109
pub(crate) fn cfg_compile_error(&self) -> TokenStream {
105110
// By checking both during the compilation of the proc macro *and* in
106111
// the generated code, we ensure that `--cfg
107-
// zerocopy_unstable_derive_on_error` need only be passed *either* when
112+
// zerocopy_unstable_linux` need only be passed *either* when
108113
// compiling this crate *or* when compiling the user's crate. The former
109114
// is preferable, but in some situations (such as when cross-compiling
110115
// using `cargo build --target`), it doesn't get propagated to this
111116
// crate's build by default.
112-
if cfg!(zerocopy_unstable_derive_on_error) {
117+
if cfg!(zerocopy_unstable_linux) {
113118
quote!()
114119
} else if let Some(span) = self.on_error_span {
115120
let core = self.core_path();
116-
let error_message = "`on_error` is experimental; pass '--cfg zerocopy_unstable_derive_on_error' to enable";
121+
let error_message =
122+
"`on_error` is experimental; pass '--cfg zerocopy_unstable_linux' to enable";
117123
quote::quote_spanned! {span=>
118124
#[allow(unused_attributes, unexpected_cfgs)]
119125
const _: () = {
120-
#[cfg(not(zerocopy_unstable_derive_on_error))]
126+
#[cfg(not(zerocopy_unstable_linux))]
121127
#core::compile_error!(#error_message);
122128
};
123129
}
@@ -610,6 +616,20 @@ impl<'a> ImplBlockBuilder<'a> {
610616
}
611617
};
612618

619+
let zerocopy_bounds =
620+
field_type_bounds
621+
.into_iter()
622+
.chain(padding_check_bound.into_iter())
623+
.chain(self_bounds.into_iter())
624+
.map(|bound| {
625+
if self.ctx.skip_on_error {
626+
parse_quote!(for<'zc> #bound)
627+
} else {
628+
bound.clone()
629+
}
630+
})
631+
.collect::<Vec<_>>();
632+
613633
let bounds = self
614634
.ctx
615635
.ast
@@ -619,9 +639,7 @@ impl<'a> ImplBlockBuilder<'a> {
619639
.map(|where_clause| where_clause.predicates.iter())
620640
.into_iter()
621641
.flatten()
622-
.chain(field_type_bounds.iter())
623-
.chain(padding_check_bound.iter())
624-
.chain(self_bounds.iter());
642+
.chain(zerocopy_bounds.iter());
625643

626644
// The parameters with trait bounds, but without type defaults.
627645
let mut params: Vec<_> = self

zerocopy/zerocopy-derive/tests/on_error.rs

Lines changed: 110 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -144,26 +144,118 @@ union BadIntoBytesUnionGeneric<T: imp::Copy> {
144144

145145
util_assert_not_impl_any!(BadIntoBytesUnionGeneric<u8>: imp::IntoBytes);
146146

147-
#[cfg(__ZEROCOPY_INTERNAL_USE_ONLY_NIGHTLY_FEATURES_IN_TESTS)]
148-
mod trivial_bounds {
149-
use super::*;
147+
#[derive(imp::FromBytes)]
148+
#[zerocopy(on_error = "skip")]
149+
#[zerocopy(crate = "zerocopy_renamed")]
150+
#[repr(transparent)]
151+
struct TrivialBounds(bool);
152+
153+
util_assert_not_impl_any!(TrivialBounds: imp::FromBytes);
154+
155+
#[derive(imp::IntoBytes)]
156+
#[zerocopy(on_error = "skip")]
157+
#[zerocopy(crate = "zerocopy_renamed")]
158+
#[repr(C)]
159+
struct BadIntoBytesStructPadding {
160+
a: u8,
161+
b: u16,
162+
}
163+
164+
util_assert_not_impl_any!(BadIntoBytesStructPadding: imp::IntoBytes);
165+
166+
#[derive(imp::most_traits)]
167+
#[zerocopy(crate = "zerocopy_renamed")]
168+
#[repr(C)]
169+
struct NotFromBytes {
170+
a: [bool],
171+
}
172+
173+
util_assert_impl_all!(NotFromBytes:
174+
imp::SplitAt,
175+
imp::IntoBytes,
176+
imp::KnownLayout,
177+
imp::Unaligned,
178+
imp::Immutable,
179+
);
180+
util_assert_not_impl_any!(NotFromBytes: imp::FromBytes);
181+
182+
#[derive(imp::most_traits)]
183+
#[zerocopy(crate = "zerocopy_renamed")]
184+
#[repr(C)]
185+
struct NotFromZeros {
186+
a: [imp::core::num::NonZeroU8],
187+
}
150188

151-
#[derive(imp::FromBytes)]
152-
#[zerocopy(on_error = "skip")]
153-
#[zerocopy(crate = "zerocopy_renamed")]
154-
#[repr(transparent)]
155-
struct TrivialBounds(bool);
189+
util_assert_impl_all!(NotFromZeros:
190+
imp::SplitAt,
191+
imp::IntoBytes,
192+
imp::KnownLayout,
193+
imp::Unaligned,
194+
imp::Immutable,
195+
);
196+
util_assert_not_impl_any!(NotFromZeros: imp::FromZeros);
156197

157-
util_assert_not_impl_any!(TrivialBounds: imp::FromBytes);
198+
#[derive(imp::most_traits)]
199+
#[zerocopy(crate = "zerocopy_renamed")]
200+
#[repr(C)]
201+
struct NotUnaligned {
202+
a: [u16],
203+
}
158204

159-
#[derive(imp::IntoBytes)]
160-
#[zerocopy(on_error = "skip")]
161-
#[zerocopy(crate = "zerocopy_renamed")]
162-
#[repr(C)]
163-
struct BadIntoBytesStructPadding {
164-
a: u8,
165-
b: u16,
166-
}
205+
util_assert_impl_all!(NotUnaligned:
206+
imp::FromBytes,
207+
imp::IntoBytes,
208+
imp::KnownLayout,
209+
imp::Immutable,
210+
imp::SplitAt,
211+
);
212+
util_assert_not_impl_any!(NotUnaligned: imp::Unaligned);
167213

168-
util_assert_not_impl_any!(BadIntoBytesStructPadding: imp::IntoBytes);
214+
#[derive(imp::most_traits)]
215+
#[zerocopy(crate = "zerocopy_renamed")]
216+
#[repr(C)]
217+
struct NotIntoBytes {
218+
a: [imp::core::mem::MaybeUninit<u8>],
169219
}
220+
221+
util_assert_impl_all!(NotIntoBytes:
222+
imp::FromBytes,
223+
imp::KnownLayout,
224+
imp::SplitAt,
225+
imp::Unaligned,
226+
imp::Immutable,
227+
);
228+
util_assert_not_impl_any!(NotIntoBytes: imp::IntoBytes);
229+
230+
#[derive(imp::most_traits)]
231+
#[zerocopy(crate = "zerocopy_renamed")]
232+
#[repr(C)]
233+
struct NotImmutable {
234+
a: [imp::core::cell::UnsafeCell<u8>],
235+
}
236+
237+
util_assert_impl_all!(NotImmutable:
238+
imp::FromBytes,
239+
imp::IntoBytes,
240+
imp::KnownLayout,
241+
imp::SplitAt,
242+
imp::Unaligned,
243+
);
244+
util_assert_not_impl_any!(NotImmutable: imp::Immutable);
245+
246+
#[derive(imp::most_traits)]
247+
#[zerocopy(crate = "zerocopy_renamed")]
248+
#[repr(C)]
249+
struct NotSplit {
250+
a: u8,
251+
b: u8,
252+
}
253+
254+
util_assert_impl_all!(NotSplit:
255+
imp::FromBytes,
256+
imp::IntoBytes,
257+
imp::KnownLayout,
258+
imp::Unaligned,
259+
imp::Immutable,
260+
);
261+
util_assert_not_impl_any!(NotSplit: imp::SplitAt);

zerocopy/zerocopy-derive/tests/ui.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ fn ui() {
1515
// present.
1616
UiTestRunner::new()
1717
.rustc_arg("--cfg=zerocopy_derive_union_into_bytes")
18-
.rustc_arg("--cfg=zerocopy_unstable_derive_on_error")
18+
.rustc_arg("--cfg=zerocopy_unstable_linux")
1919
.rustc_arg("-Wwarnings") // To ensure .stderr files reflect typical user encounter
2020
.run();
2121

0 commit comments

Comments
 (0)