summaryrefslogtreecommitdiffstats
path: root/third_party/rust/zerocopy-derive/src/repr.rs
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/rust/zerocopy-derive/src/repr.rs')
-rw-r--r--third_party/rust/zerocopy-derive/src/repr.rs311
1 files changed, 311 insertions, 0 deletions
diff --git a/third_party/rust/zerocopy-derive/src/repr.rs b/third_party/rust/zerocopy-derive/src/repr.rs
new file mode 100644
index 0000000000..f4f2788685
--- /dev/null
+++ b/third_party/rust/zerocopy-derive/src/repr.rs
@@ -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)
+}