mirror of
https://github.com/torvalds/linux.git
synced 2026-07-27 09:36:22 +02:00
Update our vendored copy of `zerocopy` (and `zerocopy-derive`) to v0.8.52.
Most SPDX identifiers have been added upstream at our request [1]
(without parentheses -- supporting them is an issue on the kernel side,
but it does already reduce our differences). The CSS one for `rustdoc`
was added too [2], but will be picked up in a later version.
For `zerocopy`, enable `--cfg no_fp_fmt_parse`, which was added at our
request to avoid our local workaround [3]. This means one less difference,
thus indicate so in our `README.md`.
For `zerocopy-derive`, enable `--cfg zerocopy_unstable_linux`. This
allows us to use `#[derive(zerocopy_derive::most_traits)]`, a new feature
upstream added for us [4]. We noticed a minor doc render bug [5], which
will be fixed for a future version too.
The following script may be used to check for the remaining differences:
for path in $(cd rust/zerocopy-derive/ && find . -type f ! -name README.md); do
curl --silent --show-error --location \
https://github.com/google/zerocopy/raw/v0.8.52/zerocopy/zerocopy-derive/src/$path |
git diff --no-index - rust/zerocopy-derive/$path &&
echo $path: OK
done
for path in $(cd rust/zerocopy/ && find . -type f ! -name README.md); do
curl --silent --show-error --location \
https://github.com/google/zerocopy/raw/v0.8.52/zerocopy/$path |
git diff --no-index - rust/zerocopy/$path &&
echo $path: OK
done
Cc: Joshua Liebow-Feeser <joshlf@google.com>
Cc: Jack Wrenn <jswrenn@google.com>
Link: https://github.com/google/zerocopy/issues/3428 [1]
Link: https://github.com/google/zerocopy/issues/3457 [2]
Link: https://github.com/google/zerocopy/issues/3426 [3]
Link: https://github.com/google/zerocopy/pull/3416 [4]
Link: https://github.com/google/zerocopy/issues/3466 [5]
Acked-by: Nicolas Schier <n.schier@fritz.com>
Reviewed-by: Alice Ryhl <aliceryhl@google.com>
Link: https://patch.msgid.link/20260625231919.692444-1-ojeda@kernel.org
Signed-off-by: Miguel Ojeda <ojeda@kernel.org>
193 lines
8.5 KiB
Rust
193 lines
8.5 KiB
Rust
// SPDX-License-Identifier: (BSD-2-Clause OR Apache-2.0) OR MIT
|
|
//
|
|
use proc_macro2::{Span, TokenStream};
|
|
use syn::{
|
|
parse_quote, Data, DataEnum, DataStruct, DataUnion, Error, Expr, ExprLit, ExprUnary, Lit, UnOp,
|
|
WherePredicate,
|
|
};
|
|
|
|
use crate::{
|
|
derive::try_from_bytes::derive_try_from_bytes,
|
|
repr::{CompoundRepr, EnumRepr, Repr, Spanned},
|
|
util::{enum_size_from_repr, Ctx, FieldBounds, ImplBlockBuilder, Trait, TraitBound},
|
|
};
|
|
/// Returns `Ok(index)` if variant `index` of the enum has a discriminant of
|
|
/// zero. If `Err(bool)` is returned, the boolean is true if the enum has
|
|
/// unknown discriminants (e.g. discriminants set to const expressions which we
|
|
/// can't evaluate in a proc macro). If the enum has unknown discriminants, then
|
|
/// it might have a zero variant that we just can't detect.
|
|
pub(crate) fn find_zero_variant(enm: &DataEnum) -> Result<usize, bool> {
|
|
// Discriminants can be anywhere in the range [i128::MIN, u128::MAX] because
|
|
// the discriminant type may be signed or unsigned. Since we only care about
|
|
// tracking the discriminant when it's less than or equal to zero, we can
|
|
// avoid u128 -> i128 conversions and bounds checking by making the "next
|
|
// discriminant" value implicitly negative.
|
|
// Technically 64 bits is enough, but 128 is better for future compatibility
|
|
// with https://github.com/rust-lang/rust/issues/56071
|
|
let mut next_negative_discriminant = Some(0);
|
|
|
|
// Sometimes we encounter explicit discriminants that we can't know the
|
|
// value of (e.g. a constant expression that requires evaluation). These
|
|
// could evaluate to zero or a negative number, but we can't assume that
|
|
// they do (no false positives allowed!). So we treat them like strictly-
|
|
// positive values that can't result in any zero variants, and track whether
|
|
// we've encountered any unknown discriminants.
|
|
let mut has_unknown_discriminants = false;
|
|
|
|
for (i, v) in enm.variants.iter().enumerate() {
|
|
match v.discriminant.as_ref() {
|
|
// Implicit discriminant
|
|
None => {
|
|
match next_negative_discriminant.as_mut() {
|
|
Some(0) => return Ok(i),
|
|
// n is nonzero so subtraction is always safe
|
|
Some(n) => *n -= 1,
|
|
None => (),
|
|
}
|
|
}
|
|
// Explicit positive discriminant
|
|
Some((_, Expr::Lit(ExprLit { lit: Lit::Int(int), .. }))) => {
|
|
match int.base10_parse::<u128>().ok() {
|
|
Some(0) => return Ok(i),
|
|
Some(_) => next_negative_discriminant = None,
|
|
None => {
|
|
// Numbers should never fail to parse, but just in case:
|
|
has_unknown_discriminants = true;
|
|
next_negative_discriminant = None;
|
|
}
|
|
}
|
|
}
|
|
// Explicit negative discriminant
|
|
Some((_, Expr::Unary(ExprUnary { op: UnOp::Neg(_), expr, .. }))) => match &**expr {
|
|
Expr::Lit(ExprLit { lit: Lit::Int(int), .. }) => {
|
|
match int.base10_parse::<u128>().ok() {
|
|
Some(0) => return Ok(i),
|
|
// x is nonzero so subtraction is always safe
|
|
Some(x) => next_negative_discriminant = Some(x - 1),
|
|
None => {
|
|
// Numbers should never fail to parse, but just in
|
|
// case:
|
|
has_unknown_discriminants = true;
|
|
next_negative_discriminant = None;
|
|
}
|
|
}
|
|
}
|
|
// Unknown negative discriminant (e.g. const repr)
|
|
_ => {
|
|
has_unknown_discriminants = true;
|
|
next_negative_discriminant = None;
|
|
}
|
|
},
|
|
// Unknown discriminant (e.g. const expr)
|
|
_ => {
|
|
has_unknown_discriminants = true;
|
|
next_negative_discriminant = None;
|
|
}
|
|
}
|
|
}
|
|
|
|
Err(has_unknown_discriminants)
|
|
}
|
|
pub(crate) fn derive_from_zeros(ctx: &Ctx, top_level: Trait) -> Result<TokenStream, Error> {
|
|
let try_from_bytes = derive_try_from_bytes(ctx, top_level)?;
|
|
let from_zeros = match &ctx.ast.data {
|
|
Data::Struct(strct) => derive_from_zeros_struct(ctx, strct),
|
|
Data::Enum(enm) => derive_from_zeros_enum(ctx, enm)?,
|
|
Data::Union(unn) => derive_from_zeros_union(ctx, unn),
|
|
};
|
|
Ok(IntoIterator::into_iter([try_from_bytes, from_zeros]).collect())
|
|
}
|
|
pub(crate) fn derive_from_bytes(ctx: &Ctx, top_level: Trait) -> Result<TokenStream, Error> {
|
|
let from_zeros = derive_from_zeros(ctx, top_level)?;
|
|
let from_bytes = match &ctx.ast.data {
|
|
Data::Struct(strct) => derive_from_bytes_struct(ctx, strct),
|
|
Data::Enum(enm) => derive_from_bytes_enum(ctx, enm)?,
|
|
Data::Union(unn) => derive_from_bytes_union(ctx, unn),
|
|
};
|
|
|
|
Ok(IntoIterator::into_iter([from_zeros, from_bytes]).collect())
|
|
}
|
|
fn derive_from_zeros_struct(ctx: &Ctx, strct: &DataStruct) -> TokenStream {
|
|
ImplBlockBuilder::new(ctx, strct, Trait::FromZeros, FieldBounds::ALL_SELF).build()
|
|
}
|
|
fn derive_from_zeros_enum(ctx: &Ctx, enm: &DataEnum) -> Result<TokenStream, Error> {
|
|
let repr = EnumRepr::from_attrs(&ctx.ast.attrs)?;
|
|
|
|
// We don't actually care what the repr is; we just care that it's one of
|
|
// the allowed ones.
|
|
match repr {
|
|
Repr::Compound(Spanned { t: CompoundRepr::C | CompoundRepr::Primitive(_), span: _ }, _) => {
|
|
}
|
|
Repr::Transparent(_) | Repr::Compound(Spanned { t: CompoundRepr::Rust, span: _ }, _) => {
|
|
return ctx.error_or_skip(
|
|
Error::new(
|
|
Span::call_site(),
|
|
"must have #[repr(C)] or #[repr(Int)] attribute in order to guarantee this type's memory layout",
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
let zero_variant = match find_zero_variant(enm) {
|
|
Ok(index) => enm.variants.iter().nth(index).unwrap(),
|
|
// Has unknown variants
|
|
Err(true) => {
|
|
return ctx.error_or_skip(Error::new_spanned(
|
|
&ctx.ast,
|
|
"FromZeros only supported on enums with a variant that has a discriminant of `0`\n\
|
|
help: This enum has discriminants which are not literal integers. One of those may \
|
|
define or imply which variant has a discriminant of zero. Use a literal integer to \
|
|
define or imply the variant with a discriminant of zero.",
|
|
));
|
|
}
|
|
// Does not have unknown variants
|
|
Err(false) => {
|
|
return ctx.error_or_skip(Error::new_spanned(
|
|
&ctx.ast,
|
|
"FromZeros only supported on enums with a variant that has a discriminant of `0`",
|
|
));
|
|
}
|
|
};
|
|
|
|
let zerocopy_crate = &ctx.zerocopy_crate;
|
|
let explicit_bounds = zero_variant
|
|
.fields
|
|
.iter()
|
|
.map(|field| {
|
|
let ty = &field.ty;
|
|
parse_quote! { #ty: #zerocopy_crate::FromZeros }
|
|
})
|
|
.collect::<Vec<WherePredicate>>();
|
|
|
|
Ok(ImplBlockBuilder::new(ctx, enm, Trait::FromZeros, FieldBounds::Explicit(explicit_bounds))
|
|
.build())
|
|
}
|
|
fn derive_from_zeros_union(ctx: &Ctx, unn: &DataUnion) -> TokenStream {
|
|
let field_type_trait_bounds = FieldBounds::All(&[TraitBound::Slf]);
|
|
ImplBlockBuilder::new(ctx, unn, Trait::FromZeros, field_type_trait_bounds).build()
|
|
}
|
|
fn derive_from_bytes_struct(ctx: &Ctx, strct: &DataStruct) -> TokenStream {
|
|
ImplBlockBuilder::new(ctx, strct, Trait::FromBytes, FieldBounds::ALL_SELF).build()
|
|
}
|
|
fn derive_from_bytes_enum(ctx: &Ctx, enm: &DataEnum) -> Result<TokenStream, Error> {
|
|
let repr = EnumRepr::from_attrs(&ctx.ast.attrs)?;
|
|
|
|
let variants_required = 1usize << enum_size_from_repr(&repr)?;
|
|
if enm.variants.len() != variants_required {
|
|
return ctx.error_or_skip(Error::new_spanned(
|
|
&ctx.ast,
|
|
format!(
|
|
"FromBytes only supported on {} enum with {} variants",
|
|
repr.repr_type_name(),
|
|
variants_required
|
|
),
|
|
));
|
|
}
|
|
|
|
Ok(ImplBlockBuilder::new(ctx, enm, Trait::FromBytes, FieldBounds::ALL_SELF).build())
|
|
}
|
|
fn derive_from_bytes_union(ctx: &Ctx, unn: &DataUnion) -> TokenStream {
|
|
let field_type_trait_bounds = FieldBounds::All(&[TraitBound::Slf]);
|
|
ImplBlockBuilder::new(ctx, unn, Trait::FromBytes, field_type_trait_bounds).build()
|
|
}
|