Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/third_party/rust/bytemuck_derive/src/   (Firefox Browser Version 153.0.1©)  Datei vom 27.6.2026 mit Größe 44 kB image not shown  

Quelle  traits.rs

  Sprache: Rust
 

#![allow(unused_imports)]
use std::{cmp, convert::TryFrom};

use proc_macro2::{Ident, Span, TokenStream, TokenTree};
use quote::{quote, ToTokens};
use syn::{
  parse::{Parse, ParseStream, Parser},
  punctuated::Punctuated,
  spanned::Spanned,
  Result, *,
};

macro_rules! bail {
  ($msg:expr $(,)?) => {
    return Err(Error::new(Span::call_site(), &$msg[..]))
  };

  ( $msg:expr => $span_to_blame:expr $(,)? ) => {
    return Err(Error::new_spanned(&$span_to_blame, $msg))
  };
}

pub trait Derivable {
  fn ident(input: &DeriveInput, crate_name: &TokenStream) -> Result<syn::Path>;
  fn implies_trait(_crate_name: &TokenStream) -> Option<TokenStream> {
    None
  }
  fn asserts(
    _input: &DeriveInput, _crate_name: &TokenStream,
  ) -> Result<TokenStream> {
    Ok(quote!())
  }
  fn check_attributes(_ty: &Data, _attributes: &[Attribute]) -> Result<()> {
    Ok(())
  }
  fn trait_impl(
    _input: &DeriveInput, _crate_name: &TokenStream,
  ) -> Result<(TokenStream, TokenStream)> {
    Ok((quote!(), quote!()))
  }
  fn requires_where_clause() -> bool {
    true
  }
  fn explicit_bounds_attribute_name() -> Option<&'static str> {
    None
  }

  /// If this trait has a custom meaning for "perfect derive", this function
  /// should be overridden to return `Some`.
  ///
  /// The default is "the fields of a struct; unions and enums not supported".
  fn perfect_derive_fields(_input: &DeriveInput) -> Option<Fields> {
    None
  }
}

pub struct Pod;

impl Derivable for Pod {
  fn ident(_: &DeriveInput, crate_name: &TokenStream) -> Result<syn::Path> {
    Ok(syn::parse_quote!(#crate_name::Pod))
  }

  fn asserts(
    input: &DeriveInput, crate_name: &TokenStream,
  ) -> Result<TokenStream> {
    let repr = get_repr(&input.attrs)?;

    let completly_packed =
      repr.packed == Some(1) || repr.repr == Repr::Transparent;

    if !completly_packed && !input.generics.params.is_empty() {
      bail!("\
        Pod requires cannot be derived for non-packed types containing \
        generic parameters because the padding requirements can't be verified \
        for generic non-packed structs\
      " => input.generics.params.first().unwrap());
    }

    match &input.data {
      Data::Struct(_) => {
        let assert_no_padding = if !completly_packed {
          Some(generate_assert_no_padding(input)?)
        } else {
          None
        };
        let assert_fields_are_pod = generate_fields_are_trait(
          input,
          None,
          Self::ident(input, crate_name)?,
        )?;

        Ok(quote!(
          #assert_no_padding
          #assert_fields_are_pod
        ))
      }
      Data::Enum(_) => bail!("Deriving Pod is not supported for enums"),
      Data::Union(_) => bail!("Deriving Pod is not supported for unions"),
    }
  }

  fn check_attributes(_ty: &Data, attributes: &[Attribute]) -> Result<()> {
    let repr = get_repr(attributes)?;
    match repr.repr {
      Repr::C => Ok(()),
      Repr::Transparent => Ok(()),
      _ => {
        bail!("Pod requires the type to be #[repr(C)] or #[repr(transparent)]")
      }
    }
  }
}

pub struct AnyBitPattern;

impl Derivable for AnyBitPattern {
  fn ident(_: &DeriveInput, crate_name: &TokenStream) -> Result<syn::Path> {
    Ok(syn::parse_quote!(#crate_name::AnyBitPattern))
  }

  fn implies_trait(crate_name: &TokenStream) -> Option<TokenStream> {
    Some(quote!(#crate_name::Zeroable))
  }

  fn asserts(
    input: &DeriveInput, crate_name: &TokenStream,
  ) -> Result<TokenStream> {
    match &input.data {
      Data::Union(_) => Ok(quote!()), // unions are always `AnyBitPattern`
      Data::Struct(_) => {
        generate_fields_are_trait(input, None, Self::ident(input, crate_name)?)
      }
      Data::Enum(_) => {
        bail!("Deriving AnyBitPattern is not supported for enums")
      }
    }
  }
}

pub struct Zeroable;

/// Helper function to get the variant with discriminant zero (implicit or
/// explicit).
fn get_zero_variant(enum_: &DataEnum) -> Result<Option<&Variant>> {
  let iter = VariantDiscriminantIterator::new(enum_.variants.iter());
  let mut zero_variant = None;
  for res in iter {
    let (discriminant, variant) = res?;
    if discriminant == 0 {
      zero_variant = Some(variant);
      break;
    }
  }
  Ok(zero_variant)
}

impl Derivable for Zeroable {
  fn ident(_: &DeriveInput, crate_name: &TokenStream) -> Result<syn::Path> {
    Ok(syn::parse_quote!(#crate_name::Zeroable))
  }

  fn check_attributes(ty: &Data, attributes: &[Attribute]) -> Result<()> {
    let repr = get_repr(attributes)?;
    match ty {
      Data::Struct(_) => Ok(()),
      Data::Enum(_) => {
        if !matches!(
          repr.repr,
          Repr::C | Repr::Integer(_) | Repr::CWithDiscriminant(_)
        ) {
          bail!("Zeroable requires the enum to be an explicit #[repr(Int)] and/or #[repr(C)]")
        }

        // We ensure there is a zero variant in `asserts`, since it is needed
        // there anyway.

        Ok(())
      }
      Data::Union(_) => Ok(()),
    }
  }

  fn asserts(
    input: &DeriveInput, crate_name: &TokenStream,
  ) -> Result<TokenStream> {
    match &input.data {
      Data::Union(_) => Ok(quote!()), // unions are always `Zeroable`
      Data::Struct(_) => {
        generate_fields_are_trait(input, None, Self::ident(input, crate_name)?)
      }
      Data::Enum(enum_) => {
        let zero_variant = get_zero_variant(enum_)?;

        if zero_variant.is_none() {
          bail!("No variant's discriminant is 0")
        };

        generate_fields_are_trait(
          input,
          zero_variant,
          Self::ident(input, crate_name)?,
        )
      }
    }
  }

  fn explicit_bounds_attribute_name() -> Option<&'static str> {
    Some("zeroable")
  }

  fn perfect_derive_fields(input: &DeriveInput) -> Option<Fields> {
    match &input.data {
      Data::Struct(struct_) => Some(struct_.fields.clone()),
      Data::Enum(enum_) => {
        // We handle `Err` returns from `get_zero_variant` in `asserts`, so it's
        // fine to just ignore them here and return `None`.
        // Otherwise, we clone the `fields` of the zero variant (if any).
        Some(get_zero_variant(enum_).ok()??.fields.clone())
      }
      Data::Union(_) => None,
    }
  }
}

pub struct NoUninit;

impl Derivable for NoUninit {
  fn ident(_: &DeriveInput, crate_name: &TokenStream) -> Result<syn::Path> {
    Ok(syn::parse_quote!(#crate_name::NoUninit))
  }

  fn check_attributes(ty: &Data, attributes: &[Attribute]) -> Result<()> {
    let repr = get_repr(attributes)?;
    match ty {
      Data::Struct(_) => match repr.repr {
        Repr::C | Repr::Transparent => Ok(()),
        _ => bail!("NoUninit requires the struct to be #[repr(C)] or #[repr(transparent)]"),
      },
      Data::Enum(_) => if repr.repr.is_integer() {
        Ok(())
      } else {
        bail!("NoUninit requires the enum to be an explicit #[repr(Int)]")
      },
      Data::Union(_) => bail!("NoUninit can only be derived on enums and structs")
    }
  }

  fn asserts(
    input: &DeriveInput, crate_name: &TokenStream,
  ) -> Result<TokenStream> {
    if !input.generics.params.is_empty() {
      bail!("NoUninit cannot be derived for structs containing generic parameters because the padding requirements can't be verified for generic structs");
    }

    match &input.data {
      Data::Struct(DataStruct { .. }) => {
        let assert_no_padding = generate_assert_no_padding(&input)?;
        let assert_fields_are_no_padding = generate_fields_are_trait(
          &input,
          None,
          Self::ident(input, crate_name)?,
        )?;

        Ok(quote!(
            #assert_no_padding
            #assert_fields_are_no_padding
        ))
      }
      Data::Enum(DataEnum { variants, .. }) => {
        if variants.iter().any(|variant| !variant.fields.is_empty()) {
          bail!("Only fieldless enums are supported for NoUninit")
        } else {
          Ok(quote!())
        }
      }
      Data::Union(_) => bail!("NoUninit cannot be derived for unions"), /* shouldn't be possible since we already error in attribute check for this case */
    }
  }

  fn trait_impl(
    _input: &DeriveInput, _crate_name: &TokenStream,
  ) -> Result<(TokenStream, TokenStream)> {
    Ok((quote!(), quote!()))
  }
}

pub struct CheckedBitPattern;

impl Derivable for CheckedBitPattern {
  fn ident(_: &DeriveInput, crate_name: &TokenStream) -> Result<syn::Path> {
    Ok(syn::parse_quote!(#crate_name::CheckedBitPattern))
  }

  fn check_attributes(ty: &Data, attributes: &[Attribute]) -> Result<()> {
    let repr = get_repr(attributes)?;
    match ty {
      Data::Struct(_) => match repr.repr {
        Repr::C | Repr::Transparent => Ok(()),
        _ => bail!("CheckedBitPattern derive requires the struct to be #[repr(C)] or #[repr(transparent)]"),
      },
      Data::Enum(DataEnum { variants,.. }) => {
        if !enum_has_fields(variants.iter()){
          if repr.repr.is_integer() {
            Ok(())
          } else {
            bail!("CheckedBitPattern requires the enum to be an explicit #[repr(Int)]")
          }
        } else if matches!(repr.repr, Repr::Rust) {
          bail!("CheckedBitPattern requires an explicit repr annotation because `repr(Rust)` doesn't have a specified type layout")
        } else {
          Ok(())
        }
      }
      Data::Union(_) => bail!("CheckedBitPattern can only be derived on enums and structs")
    }
  }

  fn asserts(
    input: &DeriveInput, crate_name: &TokenStream,
  ) -> Result<TokenStream> {
    if !input.generics.params.is_empty() {
      bail!("CheckedBitPattern cannot be derived for structs containing generic parameters");
    }

    match &input.data {
      Data::Struct(DataStruct { .. }) => {
        let assert_fields_are_maybe_pod = generate_fields_are_trait(
          &input,
          None,
          Self::ident(input, crate_name)?,
        )?;

        Ok(assert_fields_are_maybe_pod)
      }
      // nothing needed, already guaranteed OK by NoUninit.
      Data::Enum(_) => Ok(quote!()),
      Data::Union(_) => bail!("Internal error in CheckedBitPattern derive"), /* shouldn't be possible since we already error in attribute check for this case */
    }
  }

  fn trait_impl(
    input: &DeriveInput, crate_name: &TokenStream,
  ) -> Result<(TokenStream, TokenStream)> {
    match &input.data {
      Data::Struct(DataStruct { fields, .. }) => {
        generate_checked_bit_pattern_struct(
          &input.ident,
          fields,
          &input.attrs,
          crate_name,
        )
      }
      Data::Enum(DataEnum { variants, .. }) => {
        generate_checked_bit_pattern_enum(input, variants, crate_name)
      }
      Data::Union(_) => bail!("Internal error in CheckedBitPattern derive"), /* shouldn't be possible since we already error in attribute check for this case */
    }
  }
}

pub struct TransparentWrapper;

impl TransparentWrapper {
  fn get_wrapper_type(
    attributes: &[Attribute], fields: &Fields,
  ) -> Option<TokenStream> {
    let transparent_param = get_simple_attr(attributes, "transparent");
    transparent_param.map(|ident| ident.to_token_stream()).or_else(|| {
      let mut types = get_field_types(&fields);
      let first_type = types.next();
      if let Some(_) = types.next() {
        // can't guess param type if there is more than one field
        return None;
      } else {
        first_type.map(|ty| ty.to_token_stream())
      }
    })
  }
}

impl Derivable for TransparentWrapper {
  fn ident(input: &DeriveInput, crate_name: &TokenStream) -> Result<syn::Path> {
    let fields = get_struct_fields(input)?;

    let ty = match Self::get_wrapper_type(&input.attrs, &fields) {
      Some(ty) => ty,
      None => bail!(
        "\
        when deriving TransparentWrapper for a struct with more than one field \
        you need to specify the transparent field using #[transparent(T)]\
      "
      ),
    };

    Ok(syn::parse_quote!(#crate_name::TransparentWrapper<#ty>))
  }

  fn asserts(
    input: &DeriveInput, crate_name: &TokenStream,
  ) -> Result<TokenStream> {
    let (impl_generics, _ty_generics, where_clause) =
      input.generics.split_for_impl();
    let fields = get_struct_fields(input)?;
    let wrapped_type = match Self::get_wrapper_type(&input.attrs, &fields) {
      Some(wrapped_type) => wrapped_type.to_string(),
      None => unreachable!(), /* other code will already reject this derive */
    };
    let mut wrapped_field_ty = None;
    let mut nonwrapped_field_tys = vec![];
    for field in fields.iter() {
      let field_ty = &field.ty;
      if field_ty.to_token_stream().to_string() == wrapped_type {
        if wrapped_field_ty.is_some() {
          bail!(
            "TransparentWrapper can only have one field of the wrapped type"
          );
        }
        wrapped_field_ty = Some(field_ty);
      } else {
        nonwrapped_field_tys.push(field_ty);
      }
    }
    if let Some(wrapped_field_ty) = wrapped_field_ty {
      Ok(quote!(
        const _: () = {
          #[repr(transparent)]
          #[allow(clippy::multiple_bound_locations)]
          struct AssertWrappedIsWrapped #impl_generics((u8, ::core::marker::PhantomData<#wrapped_field_ty>), #(#nonwrapped_field_tys),*) #where_clause;
          fn assert_zeroable<Z: #crate_name::Zeroable>() {}
          #[allow(clippy::multiple_bound_locations)]
          fn check #impl_generics () #where_clause {
            #(
              assert_zeroable::<#nonwrapped_field_tys>();
            )*
          }
        };
      ))
    } else {
      bail!("TransparentWrapper must have one field of the wrapped type")
    }
  }

  fn check_attributes(_ty: &Data, attributes: &[Attribute]) -> Result<()> {
    let repr = get_repr(attributes)?;

    match repr.repr {
      Repr::Transparent => Ok(()),
      _ => {
        bail!(
          "TransparentWrapper requires the struct to be #[repr(transparent)]"
        )
      }
    }
  }

  fn requires_where_clause() -> bool {
    false
  }
}

pub struct Contiguous;

impl Derivable for Contiguous {
  fn ident(_: &DeriveInput, crate_name: &TokenStream) -> Result<syn::Path> {
    Ok(syn::parse_quote!(#crate_name::Contiguous))
  }

  fn trait_impl(
    input: &DeriveInput, _crate_name: &TokenStream,
  ) -> Result<(TokenStream, TokenStream)> {
    let repr = get_repr(&input.attrs)?;

    let integer_ty = if let Some(integer_ty) = repr.repr.as_integer() {
      integer_ty
    } else {
      bail!("Contiguous requires the enum to be #[repr(Int)]");
    };

    let variants = get_enum_variants(input)?;
    if enum_has_fields(variants.clone()) {
      return Err(Error::new_spanned(
        &input,
        "Only fieldless enums are supported",
      ));
    }

    let mut variants_with_discriminant =
      VariantDiscriminantIterator::new(variants);

    let (min, max, count) = variants_with_discriminant.try_fold(
      (i128::MAX, i128::MIN, 0),
      |(min, max, count), res| {
        let (discriminant, _variant) = res?;
        Ok::<_, Error>((
          i128::min(min, discriminant),
          i128::max(max, discriminant),
          count + 1,
        ))
      },
    )?;

    if max - min != count - 1 {
      bail! {
        "Contiguous requires the enum discriminants to be contiguous",
      }
    }

    let min_lit = LitInt::new(&format!("{}", min), input.span());
    let max_lit = LitInt::new(&format!("{}", max), input.span());

    // `from_integer` and `into_integer` are usually provided by the trait's
    // default implementation. We override this implementation because it
    // goes through `transmute_copy`, which can lead to inefficient assembly as seen in https://github.com/Lokathor/bytemuck/issues/175 .

    Ok((
      quote!(),
      quote! {
          type Int = #integer_ty;

          #[allow(clippy::missing_docs_in_private_items)]
          const MIN_VALUE: #integer_ty = #min_lit;

          #[allow(clippy::missing_docs_in_private_items)]
          const MAX_VALUE: #integer_ty = #max_lit;

          #[inline]
          fn from_integer(value: Self::Int) -> Option<Self> {
            #[allow(clippy::manual_range_contains)]
            if Self::MIN_VALUE <= value && value <= Self::MAX_VALUE {
              Some(unsafe { ::core::mem::transmute(value) })
            } else {
              None
            }
          }

          #[inline]
          fn into_integer(self) -> Self::Int {
              self as #integer_ty
          }
      },
    ))
  }
}

fn get_struct_fields(input: &DeriveInput) -> Result<&Fields> {
  if let Data::Struct(DataStruct { fields, .. }) = &input.data {
    Ok(fields)
  } else {
    bail!("deriving this trait is only supported for structs")
  }
}

/// Extract the `Fields` off a `DeriveInput`, or, in the `enum` case, off
/// those of the `enum_variant`, when provided (e.g., for `Zeroable`).
///
/// We purposely allow not providing an `enum_variant` for cases where
/// the caller wants to reject supporting `enum`s (e.g., `NoPadding`).
fn get_fields(
  input: &DeriveInput, enum_variant: Option<&Variant>,
) -> Result<Fields> {
  match &input.data {
    Data::Struct(DataStruct { fields, .. }) => Ok(fields.clone()),
    Data::Union(DataUnion { fields, .. }) => Ok(Fields::Named(fields.clone())),
    Data::Enum(_) => match enum_variant {
      Some(variant) => Ok(variant.fields.clone()),
      None => bail!("deriving this trait is not supported for enums"),
    },
  }
}

fn get_enum_variants<'a>(
  input: &'a DeriveInput,
) -> Result<impl Iterator<Item = &'a Variant> + Clone + 'a> {
  if let Data::Enum(DataEnum { variants, .. }) = &input.data {
    Ok(variants.iter())
  } else {
    bail!("deriving this trait is only supported for enums")
  }
}

fn get_field_types<'a>(
  fields: &'a Fields,
) -> impl Iterator<Item = &'a Type> + 'a {
  fields.iter().map(|field| &field.ty)
}

fn generate_checked_bit_pattern_struct(
  input_ident: &Ident, fields: &Fields, attrs: &[Attribute],
  crate_name: &TokenStream,
) -> Result<(TokenStream, TokenStream)> {
  let bits_ty = Ident::new(&format!("{}Bits", input_ident), input_ident.span());

  let repr = get_repr(attrs)?;

  let field_names = fields
    .iter()
    .enumerate()
    .map(|(i, field)| {
      field.ident.clone().unwrap_or_else(|| {
        Ident::new(&format!("field{}", i), input_ident.span())
      })
    })
    .collect::<Vec<_>>();
  let field_tys = fields.iter().map(|field| &field.ty).collect::<Vec<_>>();

  let field_name = &field_names[..];
  let field_ty = &field_tys[..];

  Ok((
    quote! {
        #[doc = #GENERATED_TYPE_DOCUMENTATION]
        #repr
        #[derive(Clone, Copy, #crate_name::AnyBitPattern)]
        #[allow(missing_docs)]
        pub struct #bits_ty {
            #(#field_name: <#field_ty as #crate_name::CheckedBitPattern>::Bits,)*
        }

        #[allow(unexpected_cfgs)]
        const _: () = {
          #[cfg(not(target_arch = "spirv"))]
          impl ::core::fmt::Debug for #bits_ty {
            fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
              let mut debug_struct = ::core::fmt::Formatter::debug_struct(f, ::core::stringify!(#bits_ty));
              #(::core::fmt::DebugStruct::field(&mut debug_struct, ::core::stringify!(#field_name), &{ self.#field_name });)*
              ::core::fmt::DebugStruct::finish(&mut debug_struct)
            }
          }
        };
    },
    quote! {
        type Bits = #bits_ty;

        #[inline]
        #[allow(clippy::double_comparisons, unused)]
        fn is_valid_bit_pattern(bits: &#bits_ty) -> bool {
            #(<#field_ty as #crate_name::CheckedBitPattern>::is_valid_bit_pattern(&{ bits.#field_name }) && )* true
        }
    },
  ))
}

fn generate_checked_bit_pattern_enum(
  input: &DeriveInput, variants: &Punctuated<Variant, Token![,]>,
  crate_name: &TokenStream,
) -> Result<(TokenStream, TokenStream)> {
  if enum_has_fields(variants.iter()) {
    generate_checked_bit_pattern_enum_with_fields(input, variants, crate_name)
  } else {
    generate_checked_bit_pattern_enum_without_fields(input, variants)
  }
}

fn generate_checked_bit_pattern_enum_without_fields(
  input: &DeriveInput, variants: &Punctuated<Variant, Token![,]>,
) -> Result<(TokenStream, TokenStream)> {
  let span = input.span();
  let mut variants_with_discriminant =
    VariantDiscriminantIterator::new(variants.iter());

  let (min, max, count) = variants_with_discriminant.try_fold(
    (i128::MAX, i128::MIN, 0),
    |(min, max, count), res| {
      let (discriminant, _variant) = res?;
      Ok::<_, Error>((
        i128::min(min, discriminant),
        i128::max(max, discriminant),
        count + 1,
      ))
    },
  )?;

  let check = if count == 0 {
    quote!(false)
  } else if max - min == count - 1 {
    // contiguous range
    let min_lit = LitInt::new(&format!("{}", min), span);
    let max_lit = LitInt::new(&format!("{}", max), span);

    quote!(*bits >= #min_lit && *bits <= #max_lit)
  } else {
    // not contiguous range, check for each
    let variant_discriminant_lits =
      VariantDiscriminantIterator::new(variants.iter())
        .map(|res| {
          let (discriminant, _variant) = res?;
          Ok(LitInt::new(&format!("{}", discriminant), span))
        })
        .collect::<Result<Vec<_>>>()?;

    // count is at least 1
    let first = &variant_discriminant_lits[0];
    let rest = &variant_discriminant_lits[1..];

    quote!(matches!(*bits, #first #(#rest )*))
  };

  let repr = get_repr(&input.attrs)?;
  let integer = repr.repr.as_integer().unwrap(); // should be checked in attr check already
  Ok((
    quote!(),
    quote! {
        type Bits = #integer;

        #[inline]
        #[allow(clippy::double_comparisons)]
        fn is_valid_bit_pattern(bits: &Self::Bits) -> bool {
            #check
        }
    },
  ))
}

fn generate_checked_bit_pattern_enum_with_fields(
  input: &DeriveInput, variants: &Punctuated<Variant, Token![,]>,
  crate_name: &TokenStream,
) -> Result<(TokenStream, TokenStream)> {
  let representation = get_repr(&input.attrs)?;
  let vis = &input.vis;

  match representation.repr {
    Repr::Rust => unreachable!(),
    repr @ (Repr::C | Repr::CWithDiscriminant(_)) => {
      let integer = match repr {
        Repr::C => quote!(::core::ffi::c_int),
        Repr::CWithDiscriminant(integer) => quote!(#integer),
        _ => unreachable!(),
      };
      let input_ident = &input.ident;

      let bits_repr = Representation { repr: Repr::C, ..representation };

      // the enum manually re-configured as the actual tagged union it
      // represents, thus circumventing the requirements rust imposes on
      // the tag even when using #[repr(C)] enum layout
      // see: https://doc.rust-lang.org/reference/type-layout.html#reprc-enums-with-fields
      let bits_ty_ident =
        Ident::new(&format!("{input_ident}Bits"), input.span());

      // the variants union part of the tagged union. These get put into a union
      // which gets the AnyBitPattern derive applied to it, thus checking
      // that the fields of the union obey the requriements of AnyBitPattern.
      // The types that actually go in the union are one more level of
      // indirection deep: we generate new structs for each variant
      // (`variant_struct_definitions`) which themselves have the
      // `CheckedBitPattern` derive applied, thus generating
      // `{variant_struct_ident}Bits` structs, which are the ones that go
      // into this union.
      let variants_union_ident =
        Ident::new(&format!("{}Variants", input.ident), input.span());

      let variant_struct_idents = variants.iter().map(|v| {
        Ident::new(&format!("{input_ident}Variant{}", v.ident), v.span())
      });

      let variant_struct_definitions =
        variant_struct_idents.clone().zip(variants.iter()).map(|(variant_struct_ident, v)| {
          let fields = v.fields.iter().map(|v| &v.ty);

          quote! {
            #[derive(::core::clone::Clone, ::core::marker::Copy, #crate_name::CheckedBitPattern)]
            #[repr(C)]
            #vis struct #variant_struct_ident(#(#fields),*);
          }
        });

      let union_fields = variant_struct_idents
        .clone()
        .zip(variants.iter())
        .map(|(variant_struct_ident, v)| {
          let variant_struct_bits_ident =
            Ident::new(&format!("{variant_struct_ident}Bits"), input.span());
          let field_ident = &v.ident;
          quote! {
            #field_ident#variant_struct_bits_ident
          }
        });

      let variant_checks = variant_struct_idents
        .clone()
        .zip(VariantDiscriminantIterator::new(variants.iter()))
        .zip(variants.iter())
        .map(|((variant_struct_ident, discriminant), v)| -> Result<_> {
          let (discriminant, _variant) = discriminant?;
          let discriminant = LitInt::new(&discriminant.to_string(), v.span());
          let ident = &v.ident;
          Ok(quote! {
            #discriminant => {
              let payload = unsafe { &bits.payload.#ident };
              <#variant_struct_ident as #crate_name::CheckedBitPattern>::is_valid_bit_pattern(payload)
            }
          })
        })
        .collect::<Result<Vec<_>>>()?;

      Ok((
        quote! {
          #[doc = #GENERATED_TYPE_DOCUMENTATION]
          #[derive(::core::clone::Clone, ::core::marker::Copy, #crate_name::AnyBitPattern)]
          #bits_repr
          #vis struct #bits_ty_ident {
            tag: #integer,
            payload: #variants_union_ident,
          }

          #[allow(unexpected_cfgs)]
          const _: () = {
            #[cfg(not(target_arch = "spirv"))]
            impl ::core::fmt::Debug for #bits_ty_ident {
              fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
                let mut debug_struct = ::core::fmt::Formatter::debug_struct(f, ::core::stringify!(#bits_ty_ident));
                ::core::fmt::DebugStruct::field(&mut debug_struct, "tag", &n style='color:red'>self.tag);
                ::core::fmt::DebugStruct::field(&mut debug_struct, "payload", &self.payload);
                ::core::fmt::DebugStruct::finish(&mut debug_struct)
              }
            }
          };

          #[derive(::core::clone::Clone, ::core::marker::Copy, #crate_name::AnyBitPattern)]
          #[repr(C)]
          #[allow(non_snake_case)]
          #vis union #variants_union_ident {
            #(#union_fields,)*
          }

          #[allow(unexpected_cfgs)]
          const _: () = {
            #[cfg(not(target_arch = "spirv"))]
            impl ::core::fmt::Debug for #variants_union_ident {
              fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
                let mut debug_struct = ::core::fmt::Formatter::debug_struct(f, ::core::stringify!(#variants_union_ident));
                ::core::fmt::DebugStruct::finish_non_exhaustive(&mut debug_struct)
              }
            }
          };

          #(#variant_struct_definitions)*
        },
        quote! {
          type Bits = #bits_ty_ident;

          #[inline]
          #[allow(clippy::double_comparisons)]
          fn is_valid_bit_pattern(bits: &Self::Bits) -> bool {
            match bits.tag {
              #(#variant_checks)*
              _ => false,
            }
          }
        },
      ))
    }
    Repr::Transparent => {
      if variants.len() != 1 {
        bail!("enums with more than one variant cannot be transparent")
      }

      let variant = &variants[0];

      let bits_ty = Ident::new(&format!("{}Bits", input.ident), input.span());
      let fields = variant.fields.iter().map(|v| &v.ty);

      Ok((
        quote! {
          #[doc = #GENERATED_TYPE_DOCUMENTATION]
          #[derive(::core::clone::Clone, ::core::marker::Copy, #crate_name::CheckedBitPattern)]
          #[repr(C)]
          #vis struct #bits_ty(#(#fields),*);
        },
        quote! {
          type Bits = <#bits_ty as #crate_name::CheckedBitPattern>::Bits;

          #[inline]
          #[allow(clippy::double_comparisons)]
          fn is_valid_bit_pattern(bits: &Self::Bits) -> bool {
            <#bits_ty as #crate_name::CheckedBitPattern>::is_valid_bit_pattern(bits)
          }
        },
      ))
    }
    Repr::Integer(integer) => {
      let bits_repr = Representation { repr: Repr::C, ..representation };
      let input_ident = &input.ident;

      // the enum manually re-configured as the union it represents. such a
      // union is the union of variants as a repr(c) struct with the
      // discriminator type inserted at the beginning. in our case we
      // union the `Bits` representation of each variant rather than the variant
      // itself, which we generate via a nested `CheckedBitPattern` derive
      // on the `variant_struct_definitions` generated below.
      //
      // see: https://doc.rust-lang.org/reference/type-layout.html#primitive-representation-of-enums-with-fields
      let bits_ty_ident =
        Ident::new(&format!("{input_ident}Bits"), input.span());

      let variant_struct_idents = variants.iter().map(|v| {
        Ident::new(&format!("{input_ident}Variant{}", v.ident), v.span())
      });

      let variant_struct_definitions =
        variant_struct_idents.clone().zip(variants.iter()).map(|(variant_struct_ident, v)| {
          let fields = v.fields.iter().map(|v| &v.ty);

          // adding the discriminant repr integer as first field, as described above
          quote! {
            #[derive(::core::clone::Clone, ::core::marker::Copy, #crate_name::CheckedBitPattern)]
            #[repr(C)]
            #vis struct #variant_struct_ident(#integer#(#fields),*);
          }
        });

      let union_fields = variant_struct_idents
        .clone()
        .zip(variants.iter())
        .map(|(variant_struct_ident, v)| {
          let variant_struct_bits_ident =
            Ident::new(&format!("{variant_struct_ident}Bits"), input.span());
          let field_ident = &v.ident;
          quote! {
            #field_ident#variant_struct_bits_ident
          }
        });

      let variant_checks = variant_struct_idents
        .clone()
        .zip(VariantDiscriminantIterator::new(variants.iter()))
        .zip(variants.iter())
        .map(|((variant_struct_ident, discriminant), v)| -> Result<_> {
          let (discriminant, _variant) = discriminant?;
          let discriminant = LitInt::new(&discriminant.to_string(), v.span());
          let ident = &v.ident;
          Ok(quote! {
            #discriminant => {
              let payload = unsafe { &bits.#ident };
              <#variant_struct_ident as #crate_name::CheckedBitPattern>::is_valid_bit_pattern(payload)
            }
          })
        })
        .collect::<Result<Vec<_>>>()?;

      Ok((
        quote! {
          #[doc = #GENERATED_TYPE_DOCUMENTATION]
          #[derive(::core::clone::Clone, ::core::marker::Copy, #crate_name::AnyBitPattern)]
          #bits_repr
          #[allow(non_snake_case)]
          #vis union #bits_ty_ident {
            __tag: #integer,
            #(#union_fields,)*
          }

          #[allow(unexpected_cfgs)]
          const _: () = {
            #[cfg(not(target_arch = "spirv"))]
            impl ::core::fmt::Debug for #bits_ty_ident {
              fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
                let mut debug_struct = ::core::fmt::Formatter::debug_struct(f, ::core::stringify!(#bits_ty_ident));
                ::core::fmt::DebugStruct::field(&mut debug_struct, "tag"unsafe { &self.__tag });
                ::core::fmt::DebugStruct::finish_non_exhaustive(&mut debug_struct)
              }
            }
          };

          #(#variant_struct_definitions)*
        },
        quote! {
          type Bits = #bits_ty_ident;

          #[inline]
          #[allow(clippy::double_comparisons)]
          fn is_valid_bit_pattern(bits: &Self::Bits) -> bool {
            match unsafe { bits.__tag } {
              #(#variant_checks)*
              _ => false,
            }
          }
        },
      ))
    }
  }
}

/// Check that a struct has no padding by asserting that the size of the struct
/// is equal to the sum of the size of it's fields
fn generate_assert_no_padding(input: &DeriveInput) -> Result<TokenStream> {
  let struct_type = &input.ident;
  let enum_variant = None; // `no padding` check is not supported for `enum`s yet.
  let fields = get_fields(input, enum_variant)?;

  let mut field_types = get_field_types(&fields);
  let size_sum = if let Some(first) = field_types.next() {
    let size_first = quote!(::core::mem::size_of::<#first>());
    let size_rest = quote!(#( + ::core::mem::size_of::<#field_types>() )*);

    quote!(#size_first #size_rest)
  } else {
    quote!(0)
  };

  Ok(quote! {const _: fn() = || {
    #[doc(hidden)]
    struct TypeWithoutPadding([u8; #size_sum]);
    let _ = ::core::mem::transmute::<#struct_type, TypeWithoutPadding>;
  };})
}

/// Check that all fields implement a given trait
fn generate_fields_are_trait(
  input: &DeriveInput, enum_variant: Option<&Variant>, trait_: syn::Path,
) -> Result<TokenStream> {
  let (impl_generics, _ty_generics, where_clause) =
    input.generics.split_for_impl();
  let fields = get_fields(input, enum_variant)?;
  let field_types = get_field_types(&fields);
  Ok(quote! {#(const _: fn() = || {
      #[allow(clippy::missing_const_for_fn)]
      #[doc(hidden)]
      fn check #impl_generics () #where_clause {
        fn assert_impl<T: #trait_>() {}
        assert_impl::<#field_types>();
      }
    };)*
  })
}

fn get_ident_from_stream(tokens: TokenStream) -> Option<Ident> {
  match tokens.into_iter().next() {
    Some(TokenTree::Group(group)) => get_ident_from_stream(group.stream()),
    Some(TokenTree::Ident(ident)) => Some(ident),
    _ => None,
  }
}

/// get a simple #[foo(bar)] attribute, returning "bar"
fn get_simple_attr(attributes: &[Attribute], attr_name: &str) -> Option<Ident> {
  for attr in attributes {
    if let (AttrStyle::Outer, Meta::List(list)) = (&attr.style, &attr.meta) {
      if list.path.is_ident(attr_name) {
        if let Some(ident) = get_ident_from_stream(list.tokens.clone()) {
          return Some(ident);
        }
      }
    }
  }

  None
}

fn get_repr(attributes: &[Attribute]) -> Result<Representation> {
  attributes
    .iter()
    .filter_map(|attr| {
      if attr.path().is_ident("repr") {
        Some(attr.parse_args::<Representation>())
      } else {
        None
      }
    })
    .try_fold(Representation::default(), |a, b| {
      let b = b?;
      Ok(Representation {
        repr: match (a.repr, b.repr) {
          (a, Repr::Rust) => a,
          (Repr::Rust, b) => b,
          _ => bail!("conflicting representation hints"),
        },
        packed: match (a.packed, b.packed) {
          (a, None) => a,
          (None, b) => b,
          _ => bail!("conflicting representation hints"),
        },
        align: match (a.align, b.align) {
          (Some(a), Some(b)) => Some(cmp::max(a, b)),
          (a, None) => a,
          (None, b) => b,
        },
      })
    })
}

mk_repr! {
  U8 => u8,
  I8 => i8,
  U16 => u16,
  I16 => i16,
  U32 => u32,
  I32 => i32,
  U64 => u64,
  I64 => i64,
  I128 => i128,
  U128 => u128,
  Usize => usize,
  Isize => isize,
}
// where
macro_rules! mk_repr {(
  $(
    $Xn:ident => $xn:ident
  ),* $(,)?
) => (
  #[derive(Debug, Clone, Copy, PartialEq, Eq)]
  enum IntegerRepr {
    $($Xn),*
  }

  impl<'a> TryFrom<&'a str> for IntegerRepr {
    type Error = &'a str;

    fn try_from(value: &'a str) -> std::result::Result<Self, &'a str> {
      match value {
        $(
          stringify!($xn) => Ok(Self::$Xn),
        )*
        _ => Err(value),
      }
    }
  }

  impl ToTokens for IntegerRepr {
    fn to_tokens(&self, tokens: &mut TokenStream) {
      match self {
        $(
          Self::$Xn => tokens.extend(quote!($xn)),
        )*
      }
    }
  }
)}
use mk_repr;

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum Repr {
  Rust,
  C,
  Transparent,
  Integer(IntegerRepr),
  CWithDiscriminant(IntegerRepr),
}

impl Repr {
  fn is_integer(&self) -> bool {
    matches!(selfSelf::Integer(..))
  }

  fn as_integer(&self) -> Option<IntegerRepr> {
    if let Self::Integer(v) = self {
      Some(*v)
    } else {
      None
    }
  }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
struct Representation {
  packed: Option<u32>,
  align: Option<u32>,
  repr: Repr,
}

impl Default for Representation {
  fn default() -> Self {
    Self { packed: None, align: None, repr: Repr::Rust }
  }
}

impl Parse for Representation {
  fn parse(input: ParseStream<'_>) -> Result<Representation> {
    let mut ret = Representation::default();
    while !input.is_empty() {
      let keyword = input.parse::<Ident>()?;
      // preëmptively call `.to_string()` *once* (rather than on `is_ident()`)
      let keyword_str = keyword.to_string();
      let new_repr = match keyword_str.as_str() {
        "C" => Repr::C,
        "transparent" => Repr::Transparent,
        "packed" => {
          ret.packed = Some(if input.peek(token::Paren) {
            let contents;
            parenthesized!(contents in input);
            LitInt::base10_parse::<u32>(&contents.parse()?)?
          } else {
            1
          });
          let _: Option<Token![,]> = input.parse()?;
          continue;
        }
        "align" => {
          let contents;
          parenthesized!(contents in input);
          let new_align = LitInt::base10_parse::<u32>(&contents.parse()?)?;
          ret.align = Some(
            ret
              .align
              .map_or(new_align, |old_align| cmp::max(old_align, new_align)),
          );
          let _: Option<Token![,]> = input.parse()?;
          continue;
        }
        ident => {
          let primitive = IntegerRepr::try_from(ident)
            .map_err(|_| input.error("unrecognized representation hint"))?;
          Repr::Integer(primitive)
        }
      };
      ret.repr = match (ret.repr, new_repr) {
        (Repr::Rust, new_repr) => {
          // This is the first explicit repr.
          new_repr
        }
        (Repr::C, Repr::Integer(integer))
        | (Repr::Integer(integer), Repr::C) => {
          // Both the C repr and an integer repr have been specified
          // -> merge into a C wit discriminant.
          Repr::CWithDiscriminant(integer)
        }
        (_, _) => {
          return Err(input.error("duplicate representation hint"));
        }
      };
      let _: Option<Token![,]> = input.parse()?;
    }
    Ok(ret)
  }
}

impl ToTokens for Representation {
  fn to_tokens(&self, tokens: &mut TokenStream) {
    let mut meta = Punctuated::<_, Token![,]>::new();

    match self.repr {
      Repr::Rust => {}
      Repr::C => meta.push(quote!(C)),
      Repr::Transparent => meta.push(quote!(transparent)),
      Repr::Integer(primitive) => meta.push(quote!(#primitive)),
      Repr::CWithDiscriminant(primitive) => {
        meta.push(quote!(C));
        meta.push(quote!(#primitive));
      }
    }

    if let Some(packed) = self.packed.as_ref() {
      let lit = LitInt::new(&packed.to_string(), Span::call_site());
      meta.push(quote!(packed(#lit)));
    }

    if let Some(align) = self.align.as_ref() {
      let lit = LitInt::new(&align.to_string(), Span::call_site());
      meta.push(quote!(align(#lit)));
    }

    tokens.extend(quote!(
      #[repr(#meta)]
    ));
  }
}

fn enum_has_fields<'a>(
  mut variants: impl Iterator<Item = &'a Variant>,
) -> bool {
  variants.any(|v| matches!(v.fields, Fields::Named(_) | Fields::Unnamed(_)))
}

struct VariantDiscriminantIterator<'a, I: Iterator<Item = &'a Variant> + 'a> {
  inner: I,
  last_value: i128,
}

impl<'a, I: Iterator<Item = &'a Variant> + 'a>
  VariantDiscriminantIterator<'a, I>
{
  fn new(inner: I) -> Self {
    VariantDiscriminantIterator { inner, last_value: -1 }
  }
}

impl<'a, I: Iterator<Item = &'a Variant> + 'a> Iterator
  for VariantDiscriminantIterator<'a, I>
{
  type Item = Result<(i128, &'a Variant)>;

  fn next(&mut self) -> Option<Self::Item> {
    let variant = self.inner.next()?;

    if let Some((_, discriminant)) = &variant.discriminant {
      let discriminant_value = match parse_int_expr(discriminant) {
        Ok(value) => value,
        Err(e) => return Some(Err(e)),
      };
      self.last_value = discriminant_value;
    } else {
      // If this wraps, then either:
      // 1. the enum is using repr(u128), so wrapping is correct
      // 2. the enum is using repr(i<=128 or u<128), so the compiler will
      //    already emit a "wrapping discriminant" E0370 error.
      self.last_value = self.last_value.wrapping_add(1);
      // Static assert that there is no integer repr > 128 bits. If that
      // changes, the above comment is inaccurate and needs to be updated!
      // FIXME(zachs18): maybe should also do something to ensure `isize::BITS
      // <= 128`?
      if let Some(repr) = None::<IntegerRepr> {
        match repr {
          IntegerRepr::U8
          | IntegerRepr::I8
          | IntegerRepr::U16
          | IntegerRepr::I16
          | IntegerRepr::U32
          | IntegerRepr::I32
          | IntegerRepr::U64
          | IntegerRepr::I64
          | IntegerRepr::I128
          | IntegerRepr::U128
          | IntegerRepr::Usize
          | IntegerRepr::Isize => (),
        }
      }
    }

    Some(Ok((self.last_value, variant)))
  }
}

fn parse_int_expr(expr: &Expr) -> Result<i128> {
  match expr {
    Expr::Unary(ExprUnary { op: UnOp::Neg(_), expr, .. }) => {
      parse_int_expr(expr).map(|int| -int)
    }
    Expr::Lit(ExprLit { lit: Lit::Int(int), .. }) => int.base10_parse(),
    Expr::Lit(ExprLit { lit: Lit::Byte(byte), .. }) => Ok(byte.value().into()),
    _ => bail!("Not an integer expression"),
  }
}

#[cfg(test)]
mod tests {
  use syn::parse_quote;

  use super::{get_repr, IntegerRepr, Repr, Representation};

  #[test]
  fn parse_basic_repr() {
    let attr = parse_quote!(#[repr(C)]);
    let repr = get_repr(&[attr]).unwrap();
    assert_eq!(repr, Representation { repr: Repr::C, ..Default::default() });

    let attr = parse_quote!(#[repr(transparent)]);
    let repr = get_repr(&[attr]).unwrap();
    assert_eq!(
      repr,
      Representation { repr: Repr::Transparent, ..Default::default() }
    );

    let attr = parse_quote!(#[repr(u8)]);
    let repr = get_repr(&[attr]).unwrap();
    assert_eq!(
      repr,
      Representation {
        repr: Repr::Integer(IntegerRepr::U8),
        ..Default::default()
      }
    );

    let attr = parse_quote!(#[repr(packed)]);
    let repr = get_repr(&[attr]).unwrap();
    assert_eq!(repr, Representation { packed: Some(1), ..Default::default() });

    let attr = parse_quote!(#[repr(packed(1))]);
    let repr = get_repr(&[attr]).unwrap();
    assert_eq!(repr, Representation { packed: Some(1), ..Default::default() });

    let attr = parse_quote!(#[repr(packed(2))]);
    let repr = get_repr(&[attr]).unwrap();
    assert_eq!(repr, Representation { packed: Some(2), ..Default::default() });

    let attr = parse_quote!(#[repr(align(2))]);
    let repr = get_repr(&[attr]).unwrap();
    assert_eq!(repr, Representation { align: Some(2), ..Default::default() });
  }

  #[test]
  fn parse_advanced_repr() {
    let attr = parse_quote!(#[repr(align(4), align(2))]);
    let repr = get_repr(&[attr]).unwrap();
    assert_eq!(repr, Representation { align: Some(4), ..Default::default() });

    let attr1 = parse_quote!(#[repr(align(1))]);
    let attr2 = parse_quote!(#[repr(align(4))]);
    let attr3 = parse_quote!(#[repr(align(2))]);
    let repr = get_repr(&[attr1, attr2, attr3]).unwrap();
    assert_eq!(repr, Representation { align: Some(4), ..Default::default() });

    let attr = parse_quote!(#[repr(C, u8)]);
    let repr = get_repr(&[attr]).unwrap();
    assert_eq!(
      repr,
      Representation {
        repr: Repr::CWithDiscriminant(IntegerRepr::U8),
        ..Default::default()
      }
    );

    let attr = parse_quote!(#[repr(u8, C)]);
    let repr = get_repr(&[attr]).unwrap();
    assert_eq!(
      repr,
      Representation {
        repr: Repr::CWithDiscriminant(IntegerRepr::U8),
        ..Default::default()
      }
    );
  }
}

pub fn bytemuck_crate_name(input: &DeriveInput) -> TokenStream {
  const ATTR_NAME: &'static str = "crate";

  let mut crate_name = quote!(::bytemuck);
  for attr in &input.attrs {
    if !attr.path().is_ident("bytemuck") {
      continue;
    }

    attr.parse_nested_meta(|meta| {
      if meta.path.is_ident(ATTR_NAME) {
        let expr: syn::Expr = meta.value()?.parse()?;
        let mut value = &expr;
        while let syn::Expr::Group(e) = value {
          value = &e.expr;
        }
        if let syn::Expr::Lit(syn::ExprLit {
          lit: syn::Lit::Str(lit), ..
        }) = value
        {
          let suffix = lit.suffix();
          if !suffix.is_empty() {
            bail!(format!("Unexpected suffix `{}` on string literal", suffix))
          }
          let path: syn::Path = match lit.parse() {
            Ok(path) => path,
            Err(_) => {
              bail!(format!("Failed to parse path: {:?}", lit.value()))
            }
          };
          crate_name = path.into_token_stream();
        } else {
          bail!(
            "Expected bytemuck `crate` attribute to be a string: `crate = \"...\"`",
          )
        }
      }
      Ok(())
    }).unwrap();
  }

  return crate_name;
}

const GENERATED_TYPE_DOCUMENTATION: &str =
  " `bytemuck`-generated type for internal purposes only.";

Messung V0.5 in Prozent
C=76 H=91 G=83

¤ Dauer der Verarbeitung: 0.39 Sekunden  (vorverarbeitet am  2026-08-25) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.