Vendor things
This commit is contained in:
parent
5deceec006
commit
977e3c17e5
19434 changed files with 10682014 additions and 0 deletions
53
third-party/vendor/zerocopy-derive/src/ext.rs
vendored
Normal file
53
third-party/vendor/zerocopy-derive/src/ext.rs
vendored
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
// Copyright 2019 The Fuchsia Authors
|
||||
//
|
||||
// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0
|
||||
// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT
|
||||
// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option.
|
||||
// This file may not be copied, modified, or distributed except according to
|
||||
// those terms.
|
||||
|
||||
use syn::{Data, DataEnum, DataStruct, DataUnion, Type};
|
||||
|
||||
pub trait DataExt {
|
||||
/// Extract the types of all fields. For enums, extract the types of fields
|
||||
/// from each variant.
|
||||
fn field_types(&self) -> Vec<&Type>;
|
||||
}
|
||||
|
||||
impl DataExt for Data {
|
||||
fn field_types(&self) -> Vec<&Type> {
|
||||
match self {
|
||||
Data::Struct(strc) => strc.field_types(),
|
||||
Data::Enum(enm) => enm.field_types(),
|
||||
Data::Union(un) => un.field_types(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl DataExt for DataStruct {
|
||||
fn field_types(&self) -> Vec<&Type> {
|
||||
self.fields.iter().map(|f| &f.ty).collect()
|
||||
}
|
||||
}
|
||||
|
||||
impl DataExt for DataEnum {
|
||||
fn field_types(&self) -> Vec<&Type> {
|
||||
self.variants.iter().flat_map(|var| &var.fields).map(|f| &f.ty).collect()
|
||||
}
|
||||
}
|
||||
|
||||
impl DataExt for DataUnion {
|
||||
fn field_types(&self) -> Vec<&Type> {
|
||||
self.fields.named.iter().map(|f| &f.ty).collect()
|
||||
}
|
||||
}
|
||||
|
||||
pub trait EnumExt {
|
||||
fn is_c_like(&self) -> bool;
|
||||
}
|
||||
|
||||
impl EnumExt for DataEnum {
|
||||
fn is_c_like(&self) -> bool {
|
||||
self.field_types().is_empty()
|
||||
}
|
||||
}
|
||||
882
third-party/vendor/zerocopy-derive/src/lib.rs
vendored
Normal file
882
third-party/vendor/zerocopy-derive/src/lib.rs
vendored
Normal file
|
|
@ -0,0 +1,882 @@
|
|||
// Copyright 2019 The Fuchsia Authors
|
||||
//
|
||||
// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0
|
||||
// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT
|
||||
// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option.
|
||||
// This file may not be copied, modified, or distributed except according to
|
||||
// those terms.
|
||||
|
||||
//! Derive macros for [zerocopy]'s traits.
|
||||
//!
|
||||
//! [zerocopy]: https://docs.rs/zerocopy
|
||||
|
||||
// Sometimes we want to use lints which were added after our MSRV.
|
||||
// `unknown_lints` is `warn` by default and we deny warnings in CI, so without
|
||||
// this attribute, any unknown lint would cause a CI failure when testing with
|
||||
// our MSRV.
|
||||
#![allow(unknown_lints)]
|
||||
#![deny(renamed_and_removed_lints)]
|
||||
#![deny(clippy::all, clippy::missing_safety_doc, clippy::undocumented_unsafe_blocks)]
|
||||
#![deny(
|
||||
rustdoc::bare_urls,
|
||||
rustdoc::broken_intra_doc_links,
|
||||
rustdoc::invalid_codeblock_attributes,
|
||||
rustdoc::invalid_html_tags,
|
||||
rustdoc::invalid_rust_codeblocks,
|
||||
rustdoc::missing_crate_level_docs,
|
||||
rustdoc::private_intra_doc_links
|
||||
)]
|
||||
#![recursion_limit = "128"]
|
||||
|
||||
mod ext;
|
||||
mod repr;
|
||||
|
||||
use {
|
||||
proc_macro2::Span,
|
||||
quote::quote,
|
||||
syn::{
|
||||
parse_quote, Data, DataEnum, DataStruct, DataUnion, DeriveInput, Error, Expr, ExprLit,
|
||||
GenericParam, Ident, Lit,
|
||||
},
|
||||
};
|
||||
|
||||
use {crate::ext::*, crate::repr::*};
|
||||
|
||||
// Unwraps a `Result<_, Vec<Error>>`, converting any `Err` value into a
|
||||
// `TokenStream` and returning it.
|
||||
macro_rules! try_or_print {
|
||||
($e:expr) => {
|
||||
match $e {
|
||||
Ok(x) => x,
|
||||
Err(errors) => return print_all_errors(errors).into(),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// TODO(https://github.com/rust-lang/rust/issues/54140): Some errors could be
|
||||
// made better if we could add multiple lines of error output like this:
|
||||
//
|
||||
// error: unsupported representation
|
||||
// --> enum.rs:28:8
|
||||
// |
|
||||
// 28 | #[repr(transparent)]
|
||||
// |
|
||||
// help: required by the derive of FromBytes
|
||||
//
|
||||
// Instead, we have more verbose error messages like "unsupported representation
|
||||
// for deriving FromZeroes, FromBytes, AsBytes, or Unaligned on an enum"
|
||||
//
|
||||
// This will probably require Span::error
|
||||
// (https://doc.rust-lang.org/nightly/proc_macro/struct.Span.html#method.error),
|
||||
// which is currently unstable. Revisit this once it's stable.
|
||||
|
||||
#[proc_macro_derive(KnownLayout)]
|
||||
pub fn derive_known_layout(ts: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||
let ast = syn::parse_macro_input!(ts as DeriveInput);
|
||||
|
||||
let is_repr_c_struct = match &ast.data {
|
||||
Data::Struct(..) => {
|
||||
let reprs = try_or_print!(repr::reprs::<Repr>(&ast.attrs));
|
||||
if reprs.iter().any(|(_meta, repr)| repr == &Repr::C) {
|
||||
Some(reprs)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
Data::Enum(..) | Data::Union(..) => None,
|
||||
};
|
||||
|
||||
let fields = ast.data.field_types();
|
||||
|
||||
let (require_self_sized, extras) = if let (
|
||||
Some(reprs),
|
||||
Some((trailing_field, leading_fields)),
|
||||
) = (is_repr_c_struct, fields.split_last())
|
||||
{
|
||||
let repr_align = reprs
|
||||
.iter()
|
||||
.find_map(
|
||||
|(_meta, repr)| {
|
||||
if let Repr::Align(repr_align) = repr {
|
||||
Some(repr_align)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
},
|
||||
)
|
||||
.map(|repr_align| quote!(NonZeroUsize::new(#repr_align as usize)))
|
||||
.unwrap_or(quote!(None));
|
||||
|
||||
let repr_packed = reprs
|
||||
.iter()
|
||||
.find_map(|(_meta, repr)| match repr {
|
||||
Repr::Packed => Some(1),
|
||||
Repr::PackedN(repr_packed) => Some(*repr_packed),
|
||||
_ => None,
|
||||
})
|
||||
.map(|repr_packed| quote!(NonZeroUsize::new(#repr_packed as usize)))
|
||||
.unwrap_or(quote!(None));
|
||||
|
||||
(
|
||||
false,
|
||||
quote!(
|
||||
// SAFETY: `LAYOUT` accurately describes the layout of `Self`.
|
||||
// The layout of `Self` is reflected using a sequence of
|
||||
// invocations of `DstLayout::{new_zst,extend,pad_to_align}`.
|
||||
// The documentation of these items vows that invocations in
|
||||
// this manner will acurately describe a type, so long as:
|
||||
//
|
||||
// - that type is `repr(C)`,
|
||||
// - its fields are enumerated in the order they appear,
|
||||
// - the presence of `repr_align` and `repr_packed` are correctly accounted for.
|
||||
//
|
||||
// We respect all three of these preconditions here. This
|
||||
// expansion is only used if `is_repr_c_struct`, we enumerate
|
||||
// the fields in order, and we extract the values of `align(N)`
|
||||
// and `packed(N)`.
|
||||
const LAYOUT: ::zerocopy::DstLayout = {
|
||||
use ::zerocopy::macro_util::core_reexport::num::NonZeroUsize;
|
||||
use ::zerocopy::{DstLayout, KnownLayout};
|
||||
|
||||
let repr_align = #repr_align;
|
||||
let repr_packed = #repr_packed;
|
||||
|
||||
DstLayout::new_zst(repr_align)
|
||||
#(.extend(DstLayout::for_type::<#leading_fields>(), repr_packed))*
|
||||
.extend(<#trailing_field as KnownLayout>::LAYOUT, repr_packed)
|
||||
.pad_to_align()
|
||||
};
|
||||
|
||||
// SAFETY:
|
||||
// - The recursive call to `raw_from_ptr_len` preserves both address and provenance.
|
||||
// - The `as` cast preserves both address and provenance.
|
||||
// - `NonNull::new_unchecked` preserves both address and provenance.
|
||||
#[inline(always)]
|
||||
fn raw_from_ptr_len(
|
||||
bytes: ::zerocopy::macro_util::core_reexport::ptr::NonNull<u8>,
|
||||
elems: usize,
|
||||
) -> ::zerocopy::macro_util::core_reexport::ptr::NonNull<Self> {
|
||||
use ::zerocopy::{KnownLayout};
|
||||
let trailing = <#trailing_field as KnownLayout>::raw_from_ptr_len(bytes, elems);
|
||||
let slf = trailing.as_ptr() as *mut Self;
|
||||
// SAFETY: Constructed from `trailing`, which is non-null.
|
||||
unsafe { ::zerocopy::macro_util::core_reexport::ptr::NonNull::new_unchecked(slf) }
|
||||
}
|
||||
),
|
||||
)
|
||||
} else {
|
||||
// For enums, unions, and non-`repr(C)` structs, we require that
|
||||
// `Self` is sized, and as a result don't need to reason about the
|
||||
// internals of the type.
|
||||
(
|
||||
true,
|
||||
quote!(
|
||||
// SAFETY: `LAYOUT` is guaranteed to accurately describe the
|
||||
// layout of `Self`, because that is the documented safety
|
||||
// contract of `DstLayout::for_type`.
|
||||
const LAYOUT: ::zerocopy::DstLayout = ::zerocopy::DstLayout::for_type::<Self>();
|
||||
|
||||
// SAFETY: `.cast` preserves address and provenance.
|
||||
//
|
||||
// TODO(#429): Add documentation to `.cast` that promises that
|
||||
// it preserves provenance.
|
||||
#[inline(always)]
|
||||
fn raw_from_ptr_len(
|
||||
bytes: ::zerocopy::macro_util::core_reexport::ptr::NonNull<u8>,
|
||||
_elems: usize,
|
||||
) -> ::zerocopy::macro_util::core_reexport::ptr::NonNull<Self> {
|
||||
bytes.cast::<Self>()
|
||||
}
|
||||
),
|
||||
)
|
||||
};
|
||||
|
||||
match &ast.data {
|
||||
Data::Struct(strct) => {
|
||||
let require_trait_bound_on_field_types = if require_self_sized {
|
||||
RequireBoundedFields::No
|
||||
} else {
|
||||
RequireBoundedFields::Trailing
|
||||
};
|
||||
|
||||
// A bound on the trailing field is required, since structs are
|
||||
// unsized if their trailing field is unsized. Reflecting the layout
|
||||
// of an usized trailing field requires that the field is
|
||||
// `KnownLayout`.
|
||||
impl_block(
|
||||
&ast,
|
||||
strct,
|
||||
Trait::KnownLayout,
|
||||
require_trait_bound_on_field_types,
|
||||
require_self_sized,
|
||||
None,
|
||||
Some(extras),
|
||||
)
|
||||
}
|
||||
Data::Enum(enm) => {
|
||||
// A bound on the trailing field is not required, since enums cannot
|
||||
// currently be unsized.
|
||||
impl_block(
|
||||
&ast,
|
||||
enm,
|
||||
Trait::KnownLayout,
|
||||
RequireBoundedFields::No,
|
||||
true,
|
||||
None,
|
||||
Some(extras),
|
||||
)
|
||||
}
|
||||
Data::Union(unn) => {
|
||||
// A bound on the trailing field is not required, since unions
|
||||
// cannot currently be unsized.
|
||||
impl_block(
|
||||
&ast,
|
||||
unn,
|
||||
Trait::KnownLayout,
|
||||
RequireBoundedFields::No,
|
||||
true,
|
||||
None,
|
||||
Some(extras),
|
||||
)
|
||||
}
|
||||
}
|
||||
.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);
|
||||
match &ast.data {
|
||||
Data::Struct(strct) => derive_from_zeroes_struct(&ast, strct),
|
||||
Data::Enum(enm) => derive_from_zeroes_enum(&ast, enm),
|
||||
Data::Union(unn) => derive_from_zeroes_union(&ast, unn),
|
||||
}
|
||||
.into()
|
||||
}
|
||||
|
||||
#[proc_macro_derive(FromBytes)]
|
||||
pub fn derive_from_bytes(ts: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||
let ast = syn::parse_macro_input!(ts as DeriveInput);
|
||||
match &ast.data {
|
||||
Data::Struct(strct) => derive_from_bytes_struct(&ast, strct),
|
||||
Data::Enum(enm) => derive_from_bytes_enum(&ast, enm),
|
||||
Data::Union(unn) => derive_from_bytes_union(&ast, unn),
|
||||
}
|
||||
.into()
|
||||
}
|
||||
|
||||
#[proc_macro_derive(AsBytes)]
|
||||
pub fn derive_as_bytes(ts: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||
let ast = syn::parse_macro_input!(ts as DeriveInput);
|
||||
match &ast.data {
|
||||
Data::Struct(strct) => derive_as_bytes_struct(&ast, strct),
|
||||
Data::Enum(enm) => derive_as_bytes_enum(&ast, enm),
|
||||
Data::Union(unn) => derive_as_bytes_union(&ast, unn),
|
||||
}
|
||||
.into()
|
||||
}
|
||||
|
||||
#[proc_macro_derive(Unaligned)]
|
||||
pub fn derive_unaligned(ts: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||
let ast = syn::parse_macro_input!(ts as DeriveInput);
|
||||
match &ast.data {
|
||||
Data::Struct(strct) => derive_unaligned_struct(&ast, strct),
|
||||
Data::Enum(enm) => derive_unaligned_enum(&ast, enm),
|
||||
Data::Union(unn) => derive_unaligned_union(&ast, unn),
|
||||
}
|
||||
.into()
|
||||
}
|
||||
|
||||
const STRUCT_UNION_ALLOWED_REPR_COMBINATIONS: &[&[StructRepr]] = &[
|
||||
&[StructRepr::C],
|
||||
&[StructRepr::Transparent],
|
||||
&[StructRepr::Packed],
|
||||
&[StructRepr::C, StructRepr::Packed],
|
||||
];
|
||||
|
||||
// A struct is `FromZeroes` if:
|
||||
// - all fields are `FromZeroes`
|
||||
|
||||
fn derive_from_zeroes_struct(ast: &DeriveInput, strct: &DataStruct) -> proc_macro2::TokenStream {
|
||||
impl_block(ast, strct, Trait::FromZeroes, RequireBoundedFields::Yes, false, None, None)
|
||||
}
|
||||
|
||||
// An enum is `FromZeroes` if:
|
||||
// - all of its variants are fieldless
|
||||
// - one of the variants has a discriminant of `0`
|
||||
|
||||
fn derive_from_zeroes_enum(ast: &DeriveInput, enm: &DataEnum) -> proc_macro2::TokenStream {
|
||||
if !enm.is_c_like() {
|
||||
return Error::new_spanned(ast, "only C-like enums can implement FromZeroes")
|
||||
.to_compile_error();
|
||||
}
|
||||
|
||||
let has_explicit_zero_discriminant =
|
||||
enm.variants.iter().filter_map(|v| v.discriminant.as_ref()).any(|(_, e)| {
|
||||
if let Expr::Lit(ExprLit { lit: Lit::Int(i), .. }) = e {
|
||||
i.base10_parse::<usize>().ok() == Some(0)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
});
|
||||
// If the first variant of an enum does not specify its discriminant, it is set to zero:
|
||||
// https://doc.rust-lang.org/reference/items/enumerations.html#custom-discriminant-values-for-fieldless-enumerations
|
||||
let has_implicit_zero_discriminant =
|
||||
enm.variants.iter().next().map(|v| v.discriminant.is_none()) == Some(true);
|
||||
|
||||
if !has_explicit_zero_discriminant && !has_implicit_zero_discriminant {
|
||||
return Error::new_spanned(
|
||||
ast,
|
||||
"FromZeroes only supported on enums with a variant that has a discriminant of `0`",
|
||||
)
|
||||
.to_compile_error();
|
||||
}
|
||||
|
||||
impl_block(ast, enm, Trait::FromZeroes, RequireBoundedFields::Yes, false, None, None)
|
||||
}
|
||||
|
||||
// Like structs, unions are `FromZeroes` if
|
||||
// - all fields are `FromZeroes`
|
||||
|
||||
fn derive_from_zeroes_union(ast: &DeriveInput, unn: &DataUnion) -> proc_macro2::TokenStream {
|
||||
impl_block(ast, unn, Trait::FromZeroes, RequireBoundedFields::Yes, false, None, None)
|
||||
}
|
||||
|
||||
// A struct is `FromBytes` if:
|
||||
// - all fields are `FromBytes`
|
||||
|
||||
fn derive_from_bytes_struct(ast: &DeriveInput, strct: &DataStruct) -> proc_macro2::TokenStream {
|
||||
impl_block(ast, strct, Trait::FromBytes, RequireBoundedFields::Yes, false, None, None)
|
||||
}
|
||||
|
||||
// An enum is `FromBytes` if:
|
||||
// - Every possible bit pattern must be valid, which means that every bit
|
||||
// pattern must correspond to a different enum variant. Thus, for an enum
|
||||
// whose layout takes up N bytes, there must be 2^N variants.
|
||||
// - Since we must know N, only representations which guarantee the layout's
|
||||
// size are allowed. These are `repr(uN)` and `repr(iN)` (`repr(C)` implies an
|
||||
// implementation-defined size). `usize` and `isize` technically guarantee the
|
||||
// layout's size, but would require us to know how large those are on the
|
||||
// target platform. This isn't terribly difficult - we could emit a const
|
||||
// expression that could call `core::mem::size_of` in order to determine the
|
||||
// size and check against the number of enum variants, but a) this would be
|
||||
// platform-specific and, b) even on Rust's smallest bit width platform (32),
|
||||
// this would require ~4 billion enum variants, which obviously isn't a thing.
|
||||
|
||||
fn derive_from_bytes_enum(ast: &DeriveInput, enm: &DataEnum) -> proc_macro2::TokenStream {
|
||||
if !enm.is_c_like() {
|
||||
return Error::new_spanned(ast, "only C-like enums can implement FromBytes")
|
||||
.to_compile_error();
|
||||
}
|
||||
|
||||
let reprs = try_or_print!(ENUM_FROM_BYTES_CFG.validate_reprs(ast));
|
||||
|
||||
let variants_required = match reprs.as_slice() {
|
||||
[EnumRepr::U8] | [EnumRepr::I8] => 1usize << 8,
|
||||
[EnumRepr::U16] | [EnumRepr::I16] => 1usize << 16,
|
||||
// `validate_reprs` has already validated that it's one of the preceding
|
||||
// patterns.
|
||||
_ => unreachable!(),
|
||||
};
|
||||
if enm.variants.len() != variants_required {
|
||||
return Error::new_spanned(
|
||||
ast,
|
||||
format!(
|
||||
"FromBytes only supported on {} enum with {} variants",
|
||||
reprs[0], variants_required
|
||||
),
|
||||
)
|
||||
.to_compile_error();
|
||||
}
|
||||
|
||||
impl_block(ast, enm, Trait::FromBytes, RequireBoundedFields::Yes, false, None, None)
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
const ENUM_FROM_BYTES_CFG: Config<EnumRepr> = {
|
||||
use EnumRepr::*;
|
||||
Config {
|
||||
allowed_combinations_message: r#"FromBytes requires repr of "u8", "u16", "i8", or "i16""#,
|
||||
derive_unaligned: false,
|
||||
allowed_combinations: &[
|
||||
&[U8],
|
||||
&[U16],
|
||||
&[I8],
|
||||
&[I16],
|
||||
],
|
||||
disallowed_but_legal_combinations: &[
|
||||
&[C],
|
||||
&[U32],
|
||||
&[I32],
|
||||
&[U64],
|
||||
&[I64],
|
||||
&[Usize],
|
||||
&[Isize],
|
||||
],
|
||||
}
|
||||
};
|
||||
|
||||
// Like structs, unions are `FromBytes` if
|
||||
// - all fields are `FromBytes`
|
||||
|
||||
fn derive_from_bytes_union(ast: &DeriveInput, unn: &DataUnion) -> proc_macro2::TokenStream {
|
||||
impl_block(ast, unn, Trait::FromBytes, RequireBoundedFields::Yes, false, None, None)
|
||||
}
|
||||
|
||||
// A struct is `AsBytes` if:
|
||||
// - all fields are `AsBytes`
|
||||
// - `repr(C)` or `repr(transparent)` and
|
||||
// - no padding (size of struct equals sum of size of field types)
|
||||
// - `repr(packed)`
|
||||
|
||||
fn derive_as_bytes_struct(ast: &DeriveInput, strct: &DataStruct) -> proc_macro2::TokenStream {
|
||||
let reprs = try_or_print!(STRUCT_UNION_AS_BYTES_CFG.validate_reprs(ast));
|
||||
let is_transparent = reprs.contains(&StructRepr::Transparent);
|
||||
let is_packed = reprs.contains(&StructRepr::Packed);
|
||||
|
||||
// TODO(#10): Support type parameters for non-transparent, non-packed
|
||||
// structs.
|
||||
if !ast.generics.params.is_empty() && !is_transparent && !is_packed {
|
||||
return Error::new(
|
||||
Span::call_site(),
|
||||
"unsupported on generic structs that are not repr(transparent) or repr(packed)",
|
||||
)
|
||||
.to_compile_error();
|
||||
}
|
||||
|
||||
// We don't need a padding check if the struct is repr(transparent) or
|
||||
// repr(packed).
|
||||
// - repr(transparent): The layout and ABI of the whole struct is the same
|
||||
// as its only non-ZST field (meaning there's no padding outside of that
|
||||
// field) and we require that field to be `AsBytes` (meaning there's no
|
||||
// padding in that field).
|
||||
// - repr(packed): Any inter-field padding bytes are removed, meaning that
|
||||
// any padding bytes would need to come from the fields, all of which
|
||||
// we require to be `AsBytes` (meaning they don't have any padding).
|
||||
let padding_check = if is_transparent || is_packed { None } else { Some(PaddingCheck::Struct) };
|
||||
impl_block(ast, strct, Trait::AsBytes, RequireBoundedFields::Yes, false, padding_check, None)
|
||||
}
|
||||
|
||||
const STRUCT_UNION_AS_BYTES_CFG: Config<StructRepr> = Config {
|
||||
// Since `disallowed_but_legal_combinations` is empty, this message will
|
||||
// never actually be emitted.
|
||||
allowed_combinations_message: r#"AsBytes requires either a) repr "C" or "transparent" with all fields implementing AsBytes or, b) repr "packed""#,
|
||||
derive_unaligned: false,
|
||||
allowed_combinations: STRUCT_UNION_ALLOWED_REPR_COMBINATIONS,
|
||||
disallowed_but_legal_combinations: &[],
|
||||
};
|
||||
|
||||
// An enum is `AsBytes` if it is C-like and has a defined repr.
|
||||
|
||||
fn derive_as_bytes_enum(ast: &DeriveInput, enm: &DataEnum) -> proc_macro2::TokenStream {
|
||||
if !enm.is_c_like() {
|
||||
return Error::new_spanned(ast, "only C-like enums can implement AsBytes")
|
||||
.to_compile_error();
|
||||
}
|
||||
|
||||
// We don't care what the repr is; we only care that it is one of the
|
||||
// allowed ones.
|
||||
let _: Vec<repr::EnumRepr> = try_or_print!(ENUM_AS_BYTES_CFG.validate_reprs(ast));
|
||||
impl_block(ast, enm, Trait::AsBytes, RequireBoundedFields::No, false, None, None)
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
const ENUM_AS_BYTES_CFG: Config<EnumRepr> = {
|
||||
use EnumRepr::*;
|
||||
Config {
|
||||
// Since `disallowed_but_legal_combinations` is empty, this message will
|
||||
// never actually be emitted.
|
||||
allowed_combinations_message: r#"AsBytes requires repr of "C", "u8", "u16", "u32", "u64", "usize", "i8", "i16", "i32", "i64", or "isize""#,
|
||||
derive_unaligned: false,
|
||||
allowed_combinations: &[
|
||||
&[C],
|
||||
&[U8],
|
||||
&[U16],
|
||||
&[I8],
|
||||
&[I16],
|
||||
&[U32],
|
||||
&[I32],
|
||||
&[U64],
|
||||
&[I64],
|
||||
&[Usize],
|
||||
&[Isize],
|
||||
],
|
||||
disallowed_but_legal_combinations: &[],
|
||||
}
|
||||
};
|
||||
|
||||
// A union is `AsBytes` if:
|
||||
// - all fields are `AsBytes`
|
||||
// - `repr(C)`, `repr(transparent)`, or `repr(packed)`
|
||||
// - no padding (size of union equals size of each field type)
|
||||
|
||||
fn derive_as_bytes_union(ast: &DeriveInput, unn: &DataUnion) -> proc_macro2::TokenStream {
|
||||
// TODO(#10): Support type parameters.
|
||||
if !ast.generics.params.is_empty() {
|
||||
return Error::new(Span::call_site(), "unsupported on types with type parameters")
|
||||
.to_compile_error();
|
||||
}
|
||||
|
||||
try_or_print!(STRUCT_UNION_AS_BYTES_CFG.validate_reprs(ast));
|
||||
|
||||
impl_block(
|
||||
ast,
|
||||
unn,
|
||||
Trait::AsBytes,
|
||||
RequireBoundedFields::Yes,
|
||||
false,
|
||||
Some(PaddingCheck::Union),
|
||||
None,
|
||||
)
|
||||
}
|
||||
|
||||
// A struct is `Unaligned` if:
|
||||
// - `repr(align)` is no more than 1 and either
|
||||
// - `repr(C)` or `repr(transparent)` and
|
||||
// - all fields `Unaligned`
|
||||
// - `repr(packed)`
|
||||
|
||||
fn derive_unaligned_struct(ast: &DeriveInput, strct: &DataStruct) -> proc_macro2::TokenStream {
|
||||
let reprs = try_or_print!(STRUCT_UNION_UNALIGNED_CFG.validate_reprs(ast));
|
||||
let require_trait_bounds_on_field_types = (!reprs.contains(&StructRepr::Packed)).into();
|
||||
|
||||
impl_block(ast, strct, Trait::Unaligned, require_trait_bounds_on_field_types, false, None, None)
|
||||
}
|
||||
|
||||
const STRUCT_UNION_UNALIGNED_CFG: Config<StructRepr> = Config {
|
||||
// Since `disallowed_but_legal_combinations` is empty, this message will
|
||||
// never actually be emitted.
|
||||
allowed_combinations_message: r#"Unaligned requires either a) repr "C" or "transparent" with all fields implementing Unaligned or, b) repr "packed""#,
|
||||
derive_unaligned: true,
|
||||
allowed_combinations: STRUCT_UNION_ALLOWED_REPR_COMBINATIONS,
|
||||
disallowed_but_legal_combinations: &[],
|
||||
};
|
||||
|
||||
// An enum is `Unaligned` if:
|
||||
// - No `repr(align(N > 1))`
|
||||
// - `repr(u8)` or `repr(i8)`
|
||||
|
||||
fn derive_unaligned_enum(ast: &DeriveInput, enm: &DataEnum) -> proc_macro2::TokenStream {
|
||||
if !enm.is_c_like() {
|
||||
return Error::new_spanned(ast, "only C-like enums can implement Unaligned")
|
||||
.to_compile_error();
|
||||
}
|
||||
|
||||
// The only valid reprs are `u8` and `i8`, and optionally `align(1)`. We
|
||||
// don't actually care what the reprs are so long as they satisfy that
|
||||
// requirement.
|
||||
let _: Vec<repr::EnumRepr> = try_or_print!(ENUM_UNALIGNED_CFG.validate_reprs(ast));
|
||||
|
||||
// C-like enums cannot currently have type parameters, so this value of true
|
||||
// for `require_trait_bound_on_field_types` doesn't really do anything. But
|
||||
// it's marginally more future-proof in case that restriction is lifted in
|
||||
// the future.
|
||||
impl_block(ast, enm, Trait::Unaligned, RequireBoundedFields::Yes, false, None, None)
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
const ENUM_UNALIGNED_CFG: Config<EnumRepr> = {
|
||||
use EnumRepr::*;
|
||||
Config {
|
||||
allowed_combinations_message:
|
||||
r#"Unaligned requires repr of "u8" or "i8", and no alignment (i.e., repr(align(N > 1)))"#,
|
||||
derive_unaligned: true,
|
||||
allowed_combinations: &[
|
||||
&[U8],
|
||||
&[I8],
|
||||
],
|
||||
disallowed_but_legal_combinations: &[
|
||||
&[C],
|
||||
&[U16],
|
||||
&[U32],
|
||||
&[U64],
|
||||
&[Usize],
|
||||
&[I16],
|
||||
&[I32],
|
||||
&[I64],
|
||||
&[Isize],
|
||||
],
|
||||
}
|
||||
};
|
||||
|
||||
// Like structs, a union is `Unaligned` if:
|
||||
// - `repr(align)` is no more than 1 and either
|
||||
// - `repr(C)` or `repr(transparent)` and
|
||||
// - all fields `Unaligned`
|
||||
// - `repr(packed)`
|
||||
|
||||
fn derive_unaligned_union(ast: &DeriveInput, unn: &DataUnion) -> proc_macro2::TokenStream {
|
||||
let reprs = try_or_print!(STRUCT_UNION_UNALIGNED_CFG.validate_reprs(ast));
|
||||
let require_trait_bound_on_field_types = (!reprs.contains(&StructRepr::Packed)).into();
|
||||
|
||||
impl_block(ast, unn, Trait::Unaligned, require_trait_bound_on_field_types, false, None, None)
|
||||
}
|
||||
|
||||
// This enum describes what kind of padding check needs to be generated for the
|
||||
// associated impl.
|
||||
enum PaddingCheck {
|
||||
// Check that the sum of the fields' sizes exactly equals the struct's size.
|
||||
Struct,
|
||||
// Check that the size of each field exactly equals the union's size.
|
||||
Union,
|
||||
}
|
||||
|
||||
impl PaddingCheck {
|
||||
/// Returns the ident of the macro to call in order to validate that a type
|
||||
/// passes the padding check encoded by `PaddingCheck`.
|
||||
fn validator_macro_ident(&self) -> Ident {
|
||||
let s = match self {
|
||||
PaddingCheck::Struct => "struct_has_padding",
|
||||
PaddingCheck::Union => "union_has_padding",
|
||||
};
|
||||
|
||||
Ident::new(s, Span::call_site())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
enum Trait {
|
||||
KnownLayout,
|
||||
FromZeroes,
|
||||
FromBytes,
|
||||
AsBytes,
|
||||
Unaligned,
|
||||
}
|
||||
|
||||
impl Trait {
|
||||
fn ident(&self) -> Ident {
|
||||
Ident::new(format!("{:?}", self).as_str(), Span::call_site())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
enum RequireBoundedFields {
|
||||
No,
|
||||
Yes,
|
||||
Trailing,
|
||||
}
|
||||
|
||||
impl From<bool> for RequireBoundedFields {
|
||||
fn from(do_require: bool) -> Self {
|
||||
match do_require {
|
||||
true => Self::Yes,
|
||||
false => Self::No,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn impl_block<D: DataExt>(
|
||||
input: &DeriveInput,
|
||||
data: &D,
|
||||
trt: Trait,
|
||||
require_trait_bound_on_field_types: RequireBoundedFields,
|
||||
require_self_sized: bool,
|
||||
padding_check: Option<PaddingCheck>,
|
||||
extras: Option<proc_macro2::TokenStream>,
|
||||
) -> proc_macro2::TokenStream {
|
||||
// In this documentation, we will refer to this hypothetical struct:
|
||||
//
|
||||
// #[derive(FromBytes)]
|
||||
// struct Foo<T, I: Iterator>
|
||||
// where
|
||||
// T: Copy,
|
||||
// I: Clone,
|
||||
// I::Item: Clone,
|
||||
// {
|
||||
// a: u8,
|
||||
// b: T,
|
||||
// c: I::Item,
|
||||
// }
|
||||
//
|
||||
// We extract the field types, which in this case are `u8`, `T`, and
|
||||
// `I::Item`. We re-use the existing parameters and where clauses. If
|
||||
// `require_trait_bound == true` (as it is for `FromBytes), we add where
|
||||
// bounds for each field's type:
|
||||
//
|
||||
// impl<T, I: Iterator> FromBytes for Foo<T, I>
|
||||
// where
|
||||
// T: Copy,
|
||||
// I: Clone,
|
||||
// I::Item: Clone,
|
||||
// T: FromBytes,
|
||||
// I::Item: FromBytes,
|
||||
// {
|
||||
// }
|
||||
//
|
||||
// NOTE: It is standard practice to only emit bounds for the type parameters
|
||||
// themselves, not for field types based on those parameters (e.g., `T` vs
|
||||
// `T::Foo`). For a discussion of why this is standard practice, see
|
||||
// https://github.com/rust-lang/rust/issues/26925.
|
||||
//
|
||||
// The reason we diverge from this standard is that doing it that way for us
|
||||
// would be unsound. E.g., consider a type, `T` where `T: FromBytes` but
|
||||
// `T::Foo: !FromBytes`. It would not be sound for us to accept a type with
|
||||
// a `T::Foo` field as `FromBytes` simply because `T: FromBytes`.
|
||||
//
|
||||
// While there's no getting around this requirement for us, it does have the
|
||||
// pretty serious downside that, when lifetimes are involved, the trait
|
||||
// solver ties itself in knots:
|
||||
//
|
||||
// #[derive(Unaligned)]
|
||||
// #[repr(C)]
|
||||
// struct Dup<'a, 'b> {
|
||||
// a: PhantomData<&'a u8>,
|
||||
// b: PhantomData<&'b u8>,
|
||||
// }
|
||||
//
|
||||
// error[E0283]: type annotations required: cannot resolve `core::marker::PhantomData<&'a u8>: zerocopy::Unaligned`
|
||||
// --> src/main.rs:6:10
|
||||
// |
|
||||
// 6 | #[derive(Unaligned)]
|
||||
// | ^^^^^^^^^
|
||||
// |
|
||||
// = note: required by `zerocopy::Unaligned`
|
||||
|
||||
let type_ident = &input.ident;
|
||||
let trait_ident = trt.ident();
|
||||
let field_types = data.field_types();
|
||||
|
||||
let bound_tt = |ty| parse_quote!(#ty: ::zerocopy::#trait_ident);
|
||||
let field_type_bounds: Vec<_> = match (require_trait_bound_on_field_types, &field_types[..]) {
|
||||
(RequireBoundedFields::Yes, _) => field_types.iter().map(bound_tt).collect(),
|
||||
(RequireBoundedFields::No, _) | (RequireBoundedFields::Trailing, []) => vec![],
|
||||
(RequireBoundedFields::Trailing, [.., last]) => vec![bound_tt(last)],
|
||||
};
|
||||
|
||||
// Don't bother emitting a padding check if there are no fields.
|
||||
#[allow(unstable_name_collisions)] // See `BoolExt` below
|
||||
let padding_check_bound = padding_check.and_then(|check| (!field_types.is_empty()).then_some(check)).map(|check| {
|
||||
let fields = field_types.iter();
|
||||
let validator_macro = check.validator_macro_ident();
|
||||
parse_quote!(
|
||||
::zerocopy::macro_util::HasPadding<#type_ident, {::zerocopy::#validator_macro!(#type_ident, #(#fields),*)}>:
|
||||
::zerocopy::macro_util::ShouldBe<false>
|
||||
)
|
||||
});
|
||||
|
||||
let self_sized_bound = if require_self_sized { Some(parse_quote!(Self: Sized)) } else { None };
|
||||
|
||||
let bounds = input
|
||||
.generics
|
||||
.where_clause
|
||||
.as_ref()
|
||||
.map(|where_clause| where_clause.predicates.iter())
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.chain(field_type_bounds.iter())
|
||||
.chain(padding_check_bound.iter())
|
||||
.chain(self_sized_bound.iter());
|
||||
|
||||
// The parameters with trait bounds, but without type defaults.
|
||||
let params = input.generics.params.clone().into_iter().map(|mut param| {
|
||||
match &mut param {
|
||||
GenericParam::Type(ty) => ty.default = None,
|
||||
GenericParam::Const(cnst) => cnst.default = None,
|
||||
GenericParam::Lifetime(_) => {}
|
||||
}
|
||||
quote!(#param)
|
||||
});
|
||||
|
||||
// The identifiers of the parameters without trait bounds or type defaults.
|
||||
let param_idents = input.generics.params.iter().map(|param| match param {
|
||||
GenericParam::Type(ty) => {
|
||||
let ident = &ty.ident;
|
||||
quote!(#ident)
|
||||
}
|
||||
GenericParam::Lifetime(l) => {
|
||||
let ident = &l.lifetime;
|
||||
quote!(#ident)
|
||||
}
|
||||
GenericParam::Const(cnst) => {
|
||||
let ident = &cnst.ident;
|
||||
quote!({#ident})
|
||||
}
|
||||
});
|
||||
|
||||
quote! {
|
||||
// TODO(#553): Add a test that generates a warning when
|
||||
// `#[allow(deprecated)]` isn't present.
|
||||
#[allow(deprecated)]
|
||||
unsafe impl < #(#params),* > ::zerocopy::#trait_ident for #type_ident < #(#param_idents),* >
|
||||
where
|
||||
#(#bounds,)*
|
||||
{
|
||||
fn only_derive_is_allowed_to_implement_this_trait() {}
|
||||
|
||||
#extras
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn print_all_errors(errors: Vec<Error>) -> proc_macro2::TokenStream {
|
||||
errors.iter().map(Error::to_compile_error).collect()
|
||||
}
|
||||
|
||||
// A polyfill for `Option::then_some`, which was added after our MSRV.
|
||||
//
|
||||
// TODO(#67): Remove this once our MSRV is >= 1.62.
|
||||
trait BoolExt {
|
||||
fn then_some<T>(self, t: T) -> Option<T>;
|
||||
}
|
||||
|
||||
impl BoolExt for bool {
|
||||
fn then_some<T>(self, t: T) -> Option<T> {
|
||||
if self {
|
||||
Some(t)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_config_repr_orderings() {
|
||||
// Validate that the repr lists in the various configs are in the
|
||||
// canonical order. If they aren't, then our algorithm to look up in
|
||||
// those lists won't work.
|
||||
|
||||
// TODO(https://github.com/rust-lang/rust/issues/53485): Remove once
|
||||
// `Vec::is_sorted` is stabilized.
|
||||
fn is_sorted_and_deduped<T: Clone + Ord>(ts: &[T]) -> bool {
|
||||
let mut sorted = ts.to_vec();
|
||||
sorted.sort();
|
||||
sorted.dedup();
|
||||
ts == sorted.as_slice()
|
||||
}
|
||||
|
||||
fn elements_are_sorted_and_deduped<T: Clone + Ord>(lists: &[&[T]]) -> bool {
|
||||
lists.iter().all(|list| is_sorted_and_deduped(list))
|
||||
}
|
||||
|
||||
fn config_is_sorted<T: KindRepr + Clone>(config: &Config<T>) -> bool {
|
||||
elements_are_sorted_and_deduped(config.allowed_combinations)
|
||||
&& elements_are_sorted_and_deduped(config.disallowed_but_legal_combinations)
|
||||
}
|
||||
|
||||
assert!(config_is_sorted(&STRUCT_UNION_UNALIGNED_CFG));
|
||||
assert!(config_is_sorted(&ENUM_FROM_BYTES_CFG));
|
||||
assert!(config_is_sorted(&ENUM_UNALIGNED_CFG));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_config_repr_no_overlap() {
|
||||
// Validate that no set of reprs appears in both the
|
||||
// `allowed_combinations` and `disallowed_but_legal_combinations` lists.
|
||||
|
||||
fn overlap<T: Eq>(a: &[T], b: &[T]) -> bool {
|
||||
a.iter().any(|elem| b.contains(elem))
|
||||
}
|
||||
|
||||
fn config_overlaps<T: KindRepr + Eq>(config: &Config<T>) -> bool {
|
||||
overlap(config.allowed_combinations, config.disallowed_but_legal_combinations)
|
||||
}
|
||||
|
||||
assert!(!config_overlaps(&STRUCT_UNION_UNALIGNED_CFG));
|
||||
assert!(!config_overlaps(&ENUM_FROM_BYTES_CFG));
|
||||
assert!(!config_overlaps(&ENUM_UNALIGNED_CFG));
|
||||
}
|
||||
}
|
||||
311
third-party/vendor/zerocopy-derive/src/repr.rs
vendored
Normal file
311
third-party/vendor/zerocopy-derive/src/repr.rs
vendored
Normal file
|
|
@ -0,0 +1,311 @@
|
|||
// Copyright 2019 The Fuchsia Authors
|
||||
//
|
||||
// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0
|
||||
// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT
|
||||
// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option.
|
||||
// This file may not be copied, modified, or distributed except according to
|
||||
// those terms.
|
||||
|
||||
use core::fmt::{self, Display, Formatter};
|
||||
|
||||
use {
|
||||
proc_macro2::Span,
|
||||
syn::punctuated::Punctuated,
|
||||
syn::spanned::Spanned,
|
||||
syn::token::Comma,
|
||||
syn::{Attribute, DeriveInput, Error, LitInt, Meta},
|
||||
};
|
||||
|
||||
pub struct Config<Repr: KindRepr> {
|
||||
// A human-readable message describing what combinations of representations
|
||||
// are allowed. This will be printed to the user if they use an invalid
|
||||
// combination.
|
||||
pub allowed_combinations_message: &'static str,
|
||||
// Whether we're checking as part of `derive(Unaligned)`. If not, we can
|
||||
// ignore `repr(align)`, which makes the code (and the list of valid repr
|
||||
// combinations we have to enumerate) somewhat simpler. If we're checking
|
||||
// for `Unaligned`, then in addition to checking against illegal
|
||||
// combinations, we also check to see if there exists a `repr(align(N > 1))`
|
||||
// attribute.
|
||||
pub derive_unaligned: bool,
|
||||
// Combinations which are valid for the trait.
|
||||
pub allowed_combinations: &'static [&'static [Repr]],
|
||||
// Combinations which are not valid for the trait, but are legal according
|
||||
// to Rust. Any combination not in this or `allowed_combinations` is either
|
||||
// illegal according to Rust or the behavior is unspecified. If the behavior
|
||||
// is unspecified, it might become specified in the future, and that
|
||||
// specification might not play nicely with our requirements. Thus, we
|
||||
// reject combinations with unspecified behavior in addition to illegal
|
||||
// combinations.
|
||||
pub disallowed_but_legal_combinations: &'static [&'static [Repr]],
|
||||
}
|
||||
|
||||
impl<R: KindRepr> Config<R> {
|
||||
/// Validate that `input`'s representation attributes conform to the
|
||||
/// requirements specified by this `Config`.
|
||||
///
|
||||
/// `validate_reprs` extracts the `repr` attributes, validates that they
|
||||
/// conform to the requirements of `self`, and returns them. Regardless of
|
||||
/// whether `align` attributes are considered during validation, they are
|
||||
/// stripped out of the returned value since no callers care about them.
|
||||
pub fn validate_reprs(&self, input: &DeriveInput) -> Result<Vec<R>, Vec<Error>> {
|
||||
let mut metas_reprs = reprs(&input.attrs)?;
|
||||
metas_reprs.sort_by(|a: &(_, R), b| a.1.partial_cmp(&b.1).unwrap());
|
||||
|
||||
if self.derive_unaligned {
|
||||
if let Some((meta, _)) =
|
||||
metas_reprs.iter().find(|&repr: &&(_, R)| repr.1.is_align_gt_one())
|
||||
{
|
||||
return Err(vec![Error::new_spanned(
|
||||
meta,
|
||||
"cannot derive Unaligned with repr(align(N > 1))",
|
||||
)]);
|
||||
}
|
||||
}
|
||||
|
||||
let mut metas = Vec::new();
|
||||
let mut reprs = Vec::new();
|
||||
metas_reprs.into_iter().filter(|(_, repr)| !repr.is_align()).for_each(|(meta, repr)| {
|
||||
metas.push(meta);
|
||||
reprs.push(repr)
|
||||
});
|
||||
|
||||
if reprs.is_empty() {
|
||||
// Use `Span::call_site` to report this error on the
|
||||
// `#[derive(...)]` itself.
|
||||
return Err(vec![Error::new(Span::call_site(), "must have a non-align #[repr(...)] attribute in order to guarantee this type's memory layout")]);
|
||||
}
|
||||
|
||||
let initial_sp = metas[0].span();
|
||||
let err_span = metas.iter().skip(1).try_fold(initial_sp, |sp, meta| sp.join(meta.span()));
|
||||
|
||||
if self.allowed_combinations.contains(&reprs.as_slice()) {
|
||||
Ok(reprs)
|
||||
} else if self.disallowed_but_legal_combinations.contains(&reprs.as_slice()) {
|
||||
Err(vec![Error::new(
|
||||
err_span.unwrap_or_else(|| input.span()),
|
||||
self.allowed_combinations_message,
|
||||
)])
|
||||
} else {
|
||||
Err(vec![Error::new(
|
||||
err_span.unwrap_or_else(|| input.span()),
|
||||
"conflicting representation hints",
|
||||
)])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The type of valid reprs for a particular kind (enum, struct, union).
|
||||
pub trait KindRepr: 'static + Sized + Ord {
|
||||
fn is_align(&self) -> bool;
|
||||
fn is_align_gt_one(&self) -> bool;
|
||||
fn parse(meta: &Meta) -> syn::Result<Self>;
|
||||
}
|
||||
|
||||
// Defines an enum for reprs which are valid for a given kind (structs, enums,
|
||||
// etc), and provide implementations of `KindRepr`, `Ord`, and `Display`, and
|
||||
// those traits' super-traits.
|
||||
macro_rules! define_kind_specific_repr {
|
||||
($type_name:expr, $repr_name:ident, [ $($repr_variant:ident),* ] , [ $($repr_variant_aligned:ident),* ]) => {
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub enum $repr_name {
|
||||
$($repr_variant,)*
|
||||
$($repr_variant_aligned(u64),)*
|
||||
}
|
||||
|
||||
impl KindRepr for $repr_name {
|
||||
fn is_align(&self) -> bool {
|
||||
match self {
|
||||
$($repr_name::$repr_variant_aligned(_) => true,)*
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn is_align_gt_one(&self) -> bool {
|
||||
match self {
|
||||
// `packed(n)` only lowers alignment
|
||||
$repr_name::Align(n) => n > &1,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn parse(meta: &Meta) -> syn::Result<$repr_name> {
|
||||
match Repr::from_meta(meta)? {
|
||||
$(Repr::$repr_variant => Ok($repr_name::$repr_variant),)*
|
||||
$(Repr::$repr_variant_aligned(u) => Ok($repr_name::$repr_variant_aligned(u)),)*
|
||||
_ => Err(Error::new_spanned(meta, concat!("unsupported representation for deriving FromBytes, AsBytes, or Unaligned on ", $type_name)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Define a stable ordering so we can canonicalize lists of reprs. The
|
||||
// ordering itself doesn't matter so long as it's stable.
|
||||
impl PartialOrd for $repr_name {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for $repr_name {
|
||||
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
|
||||
format!("{:?}", self).cmp(&format!("{:?}", other))
|
||||
}
|
||||
}
|
||||
|
||||
impl core::fmt::Display for $repr_name {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
match self {
|
||||
$($repr_name::$repr_variant => Repr::$repr_variant,)*
|
||||
$($repr_name::$repr_variant_aligned(u) => Repr::$repr_variant_aligned(*u),)*
|
||||
}.fmt(f)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
define_kind_specific_repr!("a struct", StructRepr, [C, Transparent, Packed], [Align, PackedN]);
|
||||
define_kind_specific_repr!(
|
||||
"an enum",
|
||||
EnumRepr,
|
||||
[C, U8, U16, U32, U64, Usize, I8, I16, I32, I64, Isize],
|
||||
[Align]
|
||||
);
|
||||
|
||||
// All representations known to Rust.
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
|
||||
pub enum Repr {
|
||||
U8,
|
||||
U16,
|
||||
U32,
|
||||
U64,
|
||||
Usize,
|
||||
I8,
|
||||
I16,
|
||||
I32,
|
||||
I64,
|
||||
Isize,
|
||||
C,
|
||||
Transparent,
|
||||
Packed,
|
||||
PackedN(u64),
|
||||
Align(u64),
|
||||
}
|
||||
|
||||
impl Repr {
|
||||
fn from_meta(meta: &Meta) -> Result<Repr, Error> {
|
||||
let (path, list) = match meta {
|
||||
Meta::Path(path) => (path, None),
|
||||
Meta::List(list) => (&list.path, Some(list)),
|
||||
_ => return Err(Error::new_spanned(meta, "unrecognized representation hint")),
|
||||
};
|
||||
|
||||
let ident = path
|
||||
.get_ident()
|
||||
.ok_or_else(|| Error::new_spanned(meta, "unrecognized representation hint"))?;
|
||||
|
||||
Ok(match (ident.to_string().as_str(), list) {
|
||||
("u8", None) => Repr::U8,
|
||||
("u16", None) => Repr::U16,
|
||||
("u32", None) => Repr::U32,
|
||||
("u64", None) => Repr::U64,
|
||||
("usize", None) => Repr::Usize,
|
||||
("i8", None) => Repr::I8,
|
||||
("i16", None) => Repr::I16,
|
||||
("i32", None) => Repr::I32,
|
||||
("i64", None) => Repr::I64,
|
||||
("isize", None) => Repr::Isize,
|
||||
("C", None) => Repr::C,
|
||||
("transparent", None) => Repr::Transparent,
|
||||
("packed", None) => Repr::Packed,
|
||||
("packed", Some(list)) => {
|
||||
Repr::PackedN(list.parse_args::<LitInt>()?.base10_parse::<u64>()?)
|
||||
}
|
||||
("align", Some(list)) => {
|
||||
Repr::Align(list.parse_args::<LitInt>()?.base10_parse::<u64>()?)
|
||||
}
|
||||
_ => return Err(Error::new_spanned(meta, "unrecognized representation hint")),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl KindRepr for Repr {
|
||||
fn is_align(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn is_align_gt_one(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn parse(meta: &Meta) -> syn::Result<Self> {
|
||||
Self::from_meta(meta)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Repr {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
|
||||
if let Repr::Align(n) = self {
|
||||
return write!(f, "repr(align({}))", n);
|
||||
}
|
||||
if let Repr::PackedN(n) = self {
|
||||
return write!(f, "repr(packed({}))", n);
|
||||
}
|
||||
write!(
|
||||
f,
|
||||
"repr({})",
|
||||
match self {
|
||||
Repr::U8 => "u8",
|
||||
Repr::U16 => "u16",
|
||||
Repr::U32 => "u32",
|
||||
Repr::U64 => "u64",
|
||||
Repr::Usize => "usize",
|
||||
Repr::I8 => "i8",
|
||||
Repr::I16 => "i16",
|
||||
Repr::I32 => "i32",
|
||||
Repr::I64 => "i64",
|
||||
Repr::Isize => "isize",
|
||||
Repr::C => "C",
|
||||
Repr::Transparent => "transparent",
|
||||
Repr::Packed => "packed",
|
||||
_ => unreachable!(),
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn reprs<R: KindRepr>(attrs: &[Attribute]) -> Result<Vec<(Meta, R)>, Vec<Error>> {
|
||||
let mut reprs = Vec::new();
|
||||
let mut errors = Vec::new();
|
||||
for attr in attrs {
|
||||
// Ignore documentation attributes.
|
||||
if attr.path().is_ident("doc") {
|
||||
continue;
|
||||
}
|
||||
if let Meta::List(ref meta_list) = attr.meta {
|
||||
if meta_list.path.is_ident("repr") {
|
||||
let parsed: Punctuated<Meta, Comma> =
|
||||
match meta_list.parse_args_with(Punctuated::parse_terminated) {
|
||||
Ok(parsed) => parsed,
|
||||
Err(_) => {
|
||||
errors.push(Error::new_spanned(
|
||||
&meta_list.tokens,
|
||||
"unrecognized representation hint",
|
||||
));
|
||||
continue;
|
||||
}
|
||||
};
|
||||
for meta in parsed {
|
||||
match R::parse(&meta) {
|
||||
Ok(repr) => reprs.push((meta, repr)),
|
||||
Err(err) => errors.push(err),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !errors.is_empty() {
|
||||
return Err(errors);
|
||||
}
|
||||
Ok(reprs)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue