| #![allow(unused_imports)] |
| use proc_macro2::{Ident, Span, TokenStream, TokenTree}; |
| use quote::{quote, quote_spanned, 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) -> Result<syn::Path>; |
| fn implies_trait() -> Option<TokenStream> { |
| None |
| } |
| fn asserts(_input: &DeriveInput) -> Result<TokenStream> { |
| Ok(quote!()) |
| } |
| fn check_attributes(_ty: &Data, _attributes: &[Attribute]) -> Result<()> { |
| Ok(()) |
| } |
| fn trait_impl(_input: &DeriveInput) -> Result<(TokenStream, TokenStream)> { |
| Ok((quote!(), quote!())) |
| } |
| fn requires_where_clause() -> bool { |
| true |
| } |
| } |
| |
| pub struct Pod; |
| |
| impl Derivable for Pod { |
| fn ident(_: &DeriveInput) -> Result<syn::Path> { |
| Ok(syn::parse_quote!(::bytemuck::Pod)) |
| } |
| |
| fn asserts(input: &DeriveInput) -> 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, Self::ident(input)?)?; |
| |
| 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) -> Result<syn::Path> { |
| Ok(syn::parse_quote!(::bytemuck::AnyBitPattern)) |
| } |
| |
| fn implies_trait() -> Option<TokenStream> { |
| Some(quote!(::bytemuck::Zeroable)) |
| } |
| |
| fn asserts(input: &DeriveInput) -> Result<TokenStream> { |
| match &input.data { |
| Data::Union(_) => Ok(quote!()), // unions are always `AnyBitPattern` |
| Data::Struct(_) => generate_fields_are_trait(input, Self::ident(input)?), |
| Data::Enum(_) => { |
| bail!("Deriving AnyBitPattern is not supported for enums") |
| } |
| } |
| } |
| } |
| |
| pub struct Zeroable; |
| |
| impl Derivable for Zeroable { |
| fn ident(_: &DeriveInput) -> Result<syn::Path> { |
| Ok(syn::parse_quote!(::bytemuck::Zeroable)) |
| } |
| |
| fn asserts(input: &DeriveInput) -> Result<TokenStream> { |
| match &input.data { |
| Data::Union(_) => Ok(quote!()), // unions are always `Zeroable` |
| Data::Struct(_) => generate_fields_are_trait(input, Self::ident(input)?), |
| Data::Enum(_) => bail!("Deriving Zeroable is not supported for enums"), |
| } |
| } |
| } |
| |
| pub struct NoUninit; |
| |
| impl Derivable for NoUninit { |
| fn ident(_: &DeriveInput) -> Result<syn::Path> { |
| Ok(syn::parse_quote!(::bytemuck::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) -> 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, Self::ident(input)?)?; |
| |
| 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) -> Result<(TokenStream, TokenStream)> { |
| Ok((quote!(), quote!())) |
| } |
| } |
| |
| pub struct CheckedBitPattern; |
| |
| impl Derivable for CheckedBitPattern { |
| fn ident(_: &DeriveInput) -> Result<syn::Path> { |
| Ok(syn::parse_quote!(::bytemuck::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(_) => if repr.repr.is_integer() { |
| Ok(()) |
| } else { |
| bail!("CheckedBitPattern requires the enum to be an explicit #[repr(Int)]") |
| }, |
| Data::Union(_) => bail!("CheckedBitPattern can only be derived on enums and structs") |
| } |
| } |
| |
| fn asserts(input: &DeriveInput) -> 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, Self::ident(input)?)?; |
| |
| Ok(assert_fields_are_maybe_pod) |
| } |
| Data::Enum(_) => Ok(quote!()), /* nothing needed, already guaranteed OK by NoUninit */ |
| 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) -> Result<(TokenStream, TokenStream)> { |
| match &input.data { |
| Data::Struct(DataStruct { fields, .. }) => { |
| generate_checked_bit_pattern_struct(&input.ident, fields, &input.attrs) |
| } |
| Data::Enum(_) => generate_checked_bit_pattern_enum(input), |
| 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) -> 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!(::bytemuck::TransparentWrapper<#ty>)) |
| } |
| |
| fn asserts(input: &DeriveInput) -> 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)] |
| struct AssertWrappedIsWrapped #impl_generics((u8, ::core::marker::PhantomData<#wrapped_field_ty>), #(#nonwrapped_field_tys),*) #where_clause; |
| fn assert_zeroable<Z: ::bytemuck::Zeroable>() {} |
| 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) -> Result<syn::Path> { |
| Ok(syn::parse_quote!(::bytemuck::Contiguous)) |
| } |
| |
| fn trait_impl(input: &DeriveInput) -> Result<(TokenStream, TokenStream)> { |
| let repr = get_repr(&input.attrs)?; |
| |
| let integer_ty = if let Some(integer_ty) = repr.repr.as_integer_type() { |
| integer_ty |
| } else { |
| bail!("Contiguous requires the enum to be #[repr(Int)]"); |
| }; |
| |
| let variants = get_enum_variants(input)?; |
| let mut variants_with_discriminator = |
| VariantDiscriminantIterator::new(variants); |
| |
| let (min, max, count) = variants_with_discriminator.try_fold( |
| (i64::max_value(), i64::min_value(), 0), |
| |(min, max, count), res| { |
| let discriminator = res?; |
| Ok::<_, Error>(( |
| i64::min(min, discriminator), |
| i64::max(max, discriminator), |
| 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()); |
| |
| Ok(( |
| quote!(), |
| quote! { |
| type Int = #integer_ty; |
| const MIN_VALUE: #integer_ty = #min_lit; |
| const MAX_VALUE: #integer_ty = #max_lit; |
| }, |
| )) |
| } |
| } |
| |
| 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") |
| } |
| } |
| |
| fn get_fields(input: &DeriveInput) -> Result<Fields> { |
| match &input.data { |
| Data::Struct(DataStruct { fields, .. }) => Ok(fields.clone()), |
| Data::Union(DataUnion { fields, .. }) => Ok(Fields::Named(fields.clone())), |
| Data::Enum(_) => bail!("deriving this trait is not supported for enums"), |
| } |
| } |
| |
| fn get_enum_variants<'a>( |
| input: &'a DeriveInput, |
| ) -> Result<impl Iterator<Item = &'a Variant> + '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], |
| ) -> 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[..]; |
| |
| let derive_dbg = |
| quote!(#[cfg_attr(not(target_arch = "spirv"), derive(Debug))]); |
| |
| Ok(( |
| quote! { |
| #repr |
| #[derive(Clone, Copy, ::bytemuck::AnyBitPattern)] |
| #derive_dbg |
| pub struct #bits_ty { |
| #(#field_name: <#field_ty as ::bytemuck::CheckedBitPattern>::Bits,)* |
| } |
| }, |
| quote! { |
| type Bits = #bits_ty; |
| |
| #[inline] |
| #[allow(clippy::double_comparisons)] |
| fn is_valid_bit_pattern(bits: &#bits_ty) -> bool { |
| #(<#field_ty as ::bytemuck::CheckedBitPattern>::is_valid_bit_pattern(&{ bits.#field_name }) && )* true |
| } |
| }, |
| )) |
| } |
| |
| fn generate_checked_bit_pattern_enum( |
| input: &DeriveInput, |
| ) -> Result<(TokenStream, TokenStream)> { |
| let span = input.span(); |
| let mut variants_with_discriminant = |
| VariantDiscriminantIterator::new(get_enum_variants(input)?); |
| |
| let (min, max, count) = variants_with_discriminant.try_fold( |
| (i64::max_value(), i64::min_value(), 0), |
| |(min, max, count), res| { |
| let discriminant = res?; |
| Ok::<_, Error>(( |
| i64::min(min, discriminant), |
| i64::max(max, discriminant), |
| count + 1, |
| )) |
| }, |
| )?; |
| |
| let check = if count == 0 { |
| quote_spanned!(span => 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_lits = |
| VariantDiscriminantIterator::new(get_enum_variants(input)?) |
| .map(|res| { |
| let variant = res?; |
| Ok(LitInt::new(&format!("{}", variant), span)) |
| }) |
| .collect::<Result<Vec<_>>>()?; |
| |
| // count is at least 1 |
| let first = &variant_lits[0]; |
| let rest = &variant_lits[1..]; |
| |
| quote!(matches!(*bits, #first #(| #rest )*)) |
| }; |
| |
| let repr = get_repr(&input.attrs)?; |
| let integer_ty = repr.repr.as_integer_type().unwrap(); // should be checked in attr check already |
| Ok(( |
| quote!(), |
| quote! { |
| type Bits = #integer_ty; |
| |
| #[inline] |
| #[allow(clippy::double_comparisons)] |
| fn is_valid_bit_pattern(bits: &Self::Bits) -> bool { |
| #check |
| } |
| }, |
| )) |
| } |
| |
| /// 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 span = input.ident.span(); |
| let fields = get_fields(input)?; |
| |
| let mut field_types = get_field_types(&fields); |
| let size_sum = if let Some(first) = field_types.next() { |
| let size_first = quote_spanned!(span => ::core::mem::size_of::<#first>()); |
| let size_rest = |
| quote_spanned!(span => #( + ::core::mem::size_of::<#field_types>() )*); |
| |
| quote_spanned!(span => #size_first#size_rest) |
| } else { |
| quote_spanned!(span => 0) |
| }; |
| |
| Ok(quote_spanned! {span => const _: fn() = || { |
| 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, trait_: syn::Path, |
| ) -> Result<TokenStream> { |
| let (impl_generics, _ty_generics, where_clause) = |
| input.generics.split_for_impl(); |
| let fields = get_fields(input)?; |
| let span = input.span(); |
| let field_types = get_field_types(&fields); |
| Ok(quote_spanned! {span => #(const _: fn() = || { |
| #[allow(clippy::missing_const_for_fn)] |
| 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) { |
| (a, None) => a, |
| (None, b) => b, |
| _ => bail!("conflicting representation hints"), |
| }, |
| }) |
| }) |
| } |
| |
| 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(Clone, Copy, PartialEq)] |
| enum Repr { |
| Rust, |
| C, |
| Transparent, |
| $($Xn),* |
| } |
| |
| impl Repr { |
| fn is_integer(self) -> bool { |
| match self { |
| Repr::Rust | Repr::C | Repr::Transparent => false, |
| _ => true, |
| } |
| } |
| |
| fn as_integer_type(self) -> Option<TokenStream> { |
| match self { |
| Repr::Rust | Repr::C | Repr::Transparent => None, |
| $( |
| Repr::$Xn => Some(quote! { ::core::primitive::$xn }), |
| )* |
| } |
| } |
| } |
| |
| #[derive(Clone, Copy)] |
| 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); |
| ret.align = Some(LitInt::base10_parse::<u32>(&contents.parse()?)?); |
| let _: Option<Token![,]> = input.parse()?; |
| continue; |
| }, |
| $( |
| stringify!($xn) => Repr::$Xn, |
| )* |
| _ => return Err(input.error("unrecognized representation hint")) |
| }; |
| if ::core::mem::replace(&mut ret.repr, new_repr) != Repr::Rust { |
| input.error("duplicate representation hint"); |
| } |
| let _: Option<Token![,]> = input.parse()?; |
| } |
| Ok(ret) |
| } |
| } |
| |
| impl ToTokens for Representation { |
| fn to_tokens(&self, tokens: &mut TokenStream) { |
| let repr = match self.repr { |
| Repr::Rust => None, |
| Repr::C => Some(quote!(C)), |
| Repr::Transparent => Some(quote!(transparent)), |
| $( |
| Repr::$Xn => Some(quote!($xn)), |
| )* |
| }; |
| let packed = self.packed.map(|p| { |
| let lit = LitInt::new(&p.to_string(), Span::call_site()); |
| quote!(packed(#lit)) |
| }); |
| let comma = if packed.is_some() && repr.is_some() { |
| Some(quote!(,)) |
| } else { |
| None |
| }; |
| tokens.extend(quote!( |
| #[repr( #repr #comma #packed )] |
| )); |
| } |
| } |
| )} |
| use mk_repr; |
| |
| struct VariantDiscriminantIterator<'a, I: Iterator<Item = &'a Variant> + 'a> { |
| inner: I, |
| last_value: i64, |
| } |
| |
| 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<i64>; |
| |
| fn next(&mut self) -> Option<Self::Item> { |
| let variant = self.inner.next()?; |
| if !variant.fields.is_empty() { |
| return Some(Err(Error::new_spanned( |
| &variant.fields, |
| "Only fieldless enums are supported", |
| ))); |
| } |
| |
| 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 { |
| self.last_value += 1; |
| } |
| |
| Some(Ok(self.last_value)) |
| } |
| } |
| |
| fn parse_int_expr(expr: &Expr) -> Result<i64> { |
| 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"), |
| } |
| } |