• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #![allow(unused_imports)]
2 use proc_macro2::{Ident, Span, TokenStream, TokenTree};
3 use quote::{quote, quote_spanned, ToTokens};
4 use syn::{
5   parse::{Parse, ParseStream, Parser},
6   punctuated::Punctuated,
7   spanned::Spanned,
8   Result, *,
9 };
10 
11 macro_rules! bail {
12   ($msg:expr $(,)?) => {
13     return Err(Error::new(Span::call_site(), &$msg[..]))
14   };
15 
16   ( $msg:expr => $span_to_blame:expr $(,)? ) => {
17     return Err(Error::new_spanned(&$span_to_blame, $msg))
18   };
19 }
20 
21 pub trait Derivable {
ident(input: &DeriveInput) -> Result<syn::Path>22   fn ident(input: &DeriveInput) -> Result<syn::Path>;
implies_trait() -> Option<TokenStream>23   fn implies_trait() -> Option<TokenStream> {
24     None
25   }
asserts(_input: &DeriveInput) -> Result<TokenStream>26   fn asserts(_input: &DeriveInput) -> Result<TokenStream> {
27     Ok(quote!())
28   }
check_attributes(_ty: &Data, _attributes: &[Attribute]) -> Result<()>29   fn check_attributes(_ty: &Data, _attributes: &[Attribute]) -> Result<()> {
30     Ok(())
31   }
trait_impl(_input: &DeriveInput) -> Result<(TokenStream, TokenStream)>32   fn trait_impl(_input: &DeriveInput) -> Result<(TokenStream, TokenStream)> {
33     Ok((quote!(), quote!()))
34   }
requires_where_clause() -> bool35   fn requires_where_clause() -> bool {
36     true
37   }
explicit_bounds_attribute_name() -> Option<&'static str>38   fn explicit_bounds_attribute_name() -> Option<&'static str> {
39     None
40   }
41 }
42 
43 pub struct Pod;
44 
45 impl Derivable for Pod {
ident(_: &DeriveInput) -> Result<syn::Path>46   fn ident(_: &DeriveInput) -> Result<syn::Path> {
47     Ok(syn::parse_quote!(::bytemuck::Pod))
48   }
49 
asserts(input: &DeriveInput) -> Result<TokenStream>50   fn asserts(input: &DeriveInput) -> Result<TokenStream> {
51     let repr = get_repr(&input.attrs)?;
52 
53     let completly_packed =
54       repr.packed == Some(1) || repr.repr == Repr::Transparent;
55 
56     if !completly_packed && !input.generics.params.is_empty() {
57       bail!("\
58         Pod requires cannot be derived for non-packed types containing \
59         generic parameters because the padding requirements can't be verified \
60         for generic non-packed structs\
61       " => input.generics.params.first().unwrap());
62     }
63 
64     match &input.data {
65       Data::Struct(_) => {
66         let assert_no_padding = if !completly_packed {
67           Some(generate_assert_no_padding(input)?)
68         } else {
69           None
70         };
71         let assert_fields_are_pod =
72           generate_fields_are_trait(input, Self::ident(input)?)?;
73 
74         Ok(quote!(
75           #assert_no_padding
76           #assert_fields_are_pod
77         ))
78       }
79       Data::Enum(_) => bail!("Deriving Pod is not supported for enums"),
80       Data::Union(_) => bail!("Deriving Pod is not supported for unions"),
81     }
82   }
83 
check_attributes(_ty: &Data, attributes: &[Attribute]) -> Result<()>84   fn check_attributes(_ty: &Data, attributes: &[Attribute]) -> Result<()> {
85     let repr = get_repr(attributes)?;
86     match repr.repr {
87       Repr::C => Ok(()),
88       Repr::Transparent => Ok(()),
89       _ => {
90         bail!("Pod requires the type to be #[repr(C)] or #[repr(transparent)]")
91       }
92     }
93   }
94 }
95 
96 pub struct AnyBitPattern;
97 
98 impl Derivable for AnyBitPattern {
ident(_: &DeriveInput) -> Result<syn::Path>99   fn ident(_: &DeriveInput) -> Result<syn::Path> {
100     Ok(syn::parse_quote!(::bytemuck::AnyBitPattern))
101   }
102 
implies_trait() -> Option<TokenStream>103   fn implies_trait() -> Option<TokenStream> {
104     Some(quote!(::bytemuck::Zeroable))
105   }
106 
asserts(input: &DeriveInput) -> Result<TokenStream>107   fn asserts(input: &DeriveInput) -> Result<TokenStream> {
108     match &input.data {
109       Data::Union(_) => Ok(quote!()), // unions are always `AnyBitPattern`
110       Data::Struct(_) => generate_fields_are_trait(input, Self::ident(input)?),
111       Data::Enum(_) => {
112         bail!("Deriving AnyBitPattern is not supported for enums")
113       }
114     }
115   }
116 }
117 
118 pub struct Zeroable;
119 
120 impl Derivable for Zeroable {
ident(_: &DeriveInput) -> Result<syn::Path>121   fn ident(_: &DeriveInput) -> Result<syn::Path> {
122     Ok(syn::parse_quote!(::bytemuck::Zeroable))
123   }
124 
asserts(input: &DeriveInput) -> Result<TokenStream>125   fn asserts(input: &DeriveInput) -> Result<TokenStream> {
126     match &input.data {
127       Data::Union(_) => Ok(quote!()), // unions are always `Zeroable`
128       Data::Struct(_) => generate_fields_are_trait(input, Self::ident(input)?),
129       Data::Enum(_) => bail!("Deriving Zeroable is not supported for enums"),
130     }
131   }
132 
explicit_bounds_attribute_name() -> Option<&'static str>133   fn explicit_bounds_attribute_name() -> Option<&'static str> {
134     Some("zeroable")
135   }
136 }
137 
138 pub struct NoUninit;
139 
140 impl Derivable for NoUninit {
ident(_: &DeriveInput) -> Result<syn::Path>141   fn ident(_: &DeriveInput) -> Result<syn::Path> {
142     Ok(syn::parse_quote!(::bytemuck::NoUninit))
143   }
144 
check_attributes(ty: &Data, attributes: &[Attribute]) -> Result<()>145   fn check_attributes(ty: &Data, attributes: &[Attribute]) -> Result<()> {
146     let repr = get_repr(attributes)?;
147     match ty {
148       Data::Struct(_) => match repr.repr {
149         Repr::C | Repr::Transparent => Ok(()),
150         _ => bail!("NoUninit requires the struct to be #[repr(C)] or #[repr(transparent)]"),
151       },
152       Data::Enum(_) => if repr.repr.is_integer() {
153         Ok(())
154       } else {
155         bail!("NoUninit requires the enum to be an explicit #[repr(Int)]")
156       },
157       Data::Union(_) => bail!("NoUninit can only be derived on enums and structs")
158     }
159   }
160 
asserts(input: &DeriveInput) -> Result<TokenStream>161   fn asserts(input: &DeriveInput) -> Result<TokenStream> {
162     if !input.generics.params.is_empty() {
163       bail!("NoUninit cannot be derived for structs containing generic parameters because the padding requirements can't be verified for generic structs");
164     }
165 
166     match &input.data {
167       Data::Struct(DataStruct { .. }) => {
168         let assert_no_padding = generate_assert_no_padding(&input)?;
169         let assert_fields_are_no_padding =
170           generate_fields_are_trait(&input, Self::ident(input)?)?;
171 
172         Ok(quote!(
173             #assert_no_padding
174             #assert_fields_are_no_padding
175         ))
176       }
177       Data::Enum(DataEnum { variants, .. }) => {
178         if variants.iter().any(|variant| !variant.fields.is_empty()) {
179           bail!("Only fieldless enums are supported for NoUninit")
180         } else {
181           Ok(quote!())
182         }
183       }
184       Data::Union(_) => bail!("NoUninit cannot be derived for unions"), /* shouldn't be possible since we already error in attribute check for this case */
185     }
186   }
187 
trait_impl(_input: &DeriveInput) -> Result<(TokenStream, TokenStream)>188   fn trait_impl(_input: &DeriveInput) -> Result<(TokenStream, TokenStream)> {
189     Ok((quote!(), quote!()))
190   }
191 }
192 
193 pub struct CheckedBitPattern;
194 
195 impl Derivable for CheckedBitPattern {
ident(_: &DeriveInput) -> Result<syn::Path>196   fn ident(_: &DeriveInput) -> Result<syn::Path> {
197     Ok(syn::parse_quote!(::bytemuck::CheckedBitPattern))
198   }
199 
check_attributes(ty: &Data, attributes: &[Attribute]) -> Result<()>200   fn check_attributes(ty: &Data, attributes: &[Attribute]) -> Result<()> {
201     let repr = get_repr(attributes)?;
202     match ty {
203       Data::Struct(_) => match repr.repr {
204         Repr::C | Repr::Transparent => Ok(()),
205         _ => bail!("CheckedBitPattern derive requires the struct to be #[repr(C)] or #[repr(transparent)]"),
206       },
207       Data::Enum(_) => if repr.repr.is_integer() {
208         Ok(())
209       } else {
210         bail!("CheckedBitPattern requires the enum to be an explicit #[repr(Int)]")
211       },
212       Data::Union(_) => bail!("CheckedBitPattern can only be derived on enums and structs")
213     }
214   }
215 
asserts(input: &DeriveInput) -> Result<TokenStream>216   fn asserts(input: &DeriveInput) -> Result<TokenStream> {
217     if !input.generics.params.is_empty() {
218       bail!("CheckedBitPattern cannot be derived for structs containing generic parameters");
219     }
220 
221     match &input.data {
222       Data::Struct(DataStruct { .. }) => {
223         let assert_fields_are_maybe_pod =
224           generate_fields_are_trait(&input, Self::ident(input)?)?;
225 
226         Ok(assert_fields_are_maybe_pod)
227       }
228       Data::Enum(_) => Ok(quote!()), /* nothing needed, already guaranteed OK by NoUninit */
229       Data::Union(_) => bail!("Internal error in CheckedBitPattern derive"), /* shouldn't be possible since we already error in attribute check for this case */
230     }
231   }
232 
trait_impl(input: &DeriveInput) -> Result<(TokenStream, TokenStream)>233   fn trait_impl(input: &DeriveInput) -> Result<(TokenStream, TokenStream)> {
234     match &input.data {
235       Data::Struct(DataStruct { fields, .. }) => {
236         generate_checked_bit_pattern_struct(&input.ident, fields, &input.attrs)
237       }
238       Data::Enum(_) => generate_checked_bit_pattern_enum(input),
239       Data::Union(_) => bail!("Internal error in CheckedBitPattern derive"), /* shouldn't be possible since we already error in attribute check for this case */
240     }
241   }
242 }
243 
244 pub struct TransparentWrapper;
245 
246 impl TransparentWrapper {
get_wrapper_type( attributes: &[Attribute], fields: &Fields, ) -> Option<TokenStream>247   fn get_wrapper_type(
248     attributes: &[Attribute], fields: &Fields,
249   ) -> Option<TokenStream> {
250     let transparent_param = get_simple_attr(attributes, "transparent");
251     transparent_param.map(|ident| ident.to_token_stream()).or_else(|| {
252       let mut types = get_field_types(&fields);
253       let first_type = types.next();
254       if let Some(_) = types.next() {
255         // can't guess param type if there is more than one field
256         return None;
257       } else {
258         first_type.map(|ty| ty.to_token_stream())
259       }
260     })
261   }
262 }
263 
264 impl Derivable for TransparentWrapper {
ident(input: &DeriveInput) -> Result<syn::Path>265   fn ident(input: &DeriveInput) -> Result<syn::Path> {
266     let fields = get_struct_fields(input)?;
267 
268     let ty = match Self::get_wrapper_type(&input.attrs, &fields) {
269       Some(ty) => ty,
270       None => bail!(
271         "\
272         when deriving TransparentWrapper for a struct with more than one field \
273         you need to specify the transparent field using #[transparent(T)]\
274       "
275       ),
276     };
277 
278     Ok(syn::parse_quote!(::bytemuck::TransparentWrapper<#ty>))
279   }
280 
asserts(input: &DeriveInput) -> Result<TokenStream>281   fn asserts(input: &DeriveInput) -> Result<TokenStream> {
282     let (impl_generics, _ty_generics, where_clause) =
283       input.generics.split_for_impl();
284     let fields = get_struct_fields(input)?;
285     let wrapped_type = match Self::get_wrapper_type(&input.attrs, &fields) {
286       Some(wrapped_type) => wrapped_type.to_string(),
287       None => unreachable!(), /* other code will already reject this derive */
288     };
289     let mut wrapped_field_ty = None;
290     let mut nonwrapped_field_tys = vec![];
291     for field in fields.iter() {
292       let field_ty = &field.ty;
293       if field_ty.to_token_stream().to_string() == wrapped_type {
294         if wrapped_field_ty.is_some() {
295           bail!(
296             "TransparentWrapper can only have one field of the wrapped type"
297           );
298         }
299         wrapped_field_ty = Some(field_ty);
300       } else {
301         nonwrapped_field_tys.push(field_ty);
302       }
303     }
304     if let Some(wrapped_field_ty) = wrapped_field_ty {
305       Ok(quote!(
306         const _: () = {
307           #[repr(transparent)]
308           struct AssertWrappedIsWrapped #impl_generics((u8, ::core::marker::PhantomData<#wrapped_field_ty>), #(#nonwrapped_field_tys),*) #where_clause;
309           fn assert_zeroable<Z: ::bytemuck::Zeroable>() {}
310           fn check #impl_generics () #where_clause {
311             #(
312               assert_zeroable::<#nonwrapped_field_tys>();
313             )*
314           }
315         };
316       ))
317     } else {
318       bail!("TransparentWrapper must have one field of the wrapped type")
319     }
320   }
321 
check_attributes(_ty: &Data, attributes: &[Attribute]) -> Result<()>322   fn check_attributes(_ty: &Data, attributes: &[Attribute]) -> Result<()> {
323     let repr = get_repr(attributes)?;
324 
325     match repr.repr {
326       Repr::Transparent => Ok(()),
327       _ => {
328         bail!(
329           "TransparentWrapper requires the struct to be #[repr(transparent)]"
330         )
331       }
332     }
333   }
334 
requires_where_clause() -> bool335   fn requires_where_clause() -> bool {
336     false
337   }
338 }
339 
340 pub struct Contiguous;
341 
342 impl Derivable for Contiguous {
ident(_: &DeriveInput) -> Result<syn::Path>343   fn ident(_: &DeriveInput) -> Result<syn::Path> {
344     Ok(syn::parse_quote!(::bytemuck::Contiguous))
345   }
346 
trait_impl(input: &DeriveInput) -> Result<(TokenStream, TokenStream)>347   fn trait_impl(input: &DeriveInput) -> Result<(TokenStream, TokenStream)> {
348     let repr = get_repr(&input.attrs)?;
349 
350     let integer_ty = if let Some(integer_ty) = repr.repr.as_integer_type() {
351       integer_ty
352     } else {
353       bail!("Contiguous requires the enum to be #[repr(Int)]");
354     };
355 
356     let variants = get_enum_variants(input)?;
357     let mut variants_with_discriminator =
358       VariantDiscriminantIterator::new(variants);
359 
360     let (min, max, count) = variants_with_discriminator.try_fold(
361       (i64::max_value(), i64::min_value(), 0),
362       |(min, max, count), res| {
363         let discriminator = res?;
364         Ok::<_, Error>((
365           i64::min(min, discriminator),
366           i64::max(max, discriminator),
367           count + 1,
368         ))
369       },
370     )?;
371 
372     if max - min != count - 1 {
373       bail! {
374         "Contiguous requires the enum discriminants to be contiguous",
375       }
376     }
377 
378     let min_lit = LitInt::new(&format!("{}", min), input.span());
379     let max_lit = LitInt::new(&format!("{}", max), input.span());
380 
381     // `from_integer` and `into_integer` are usually provided by the trait's default implementation.
382     // We override this implementation because it goes through `transmute_copy`, which can lead to
383     // inefficient assembly as seen in https://github.com/Lokathor/bytemuck/issues/175 .
384 
385     Ok((
386       quote!(),
387       quote! {
388           type Int = #integer_ty;
389           const MIN_VALUE: #integer_ty = #min_lit;
390           const MAX_VALUE: #integer_ty = #max_lit;
391 
392           #[inline]
393           fn from_integer(value: Self::Int) -> Option<Self> {
394             #[allow(clippy::manual_range_contains)]
395             if Self::MIN_VALUE <= value && value <= Self::MAX_VALUE {
396               Some(unsafe { ::core::mem::transmute(value) })
397             } else {
398               None
399             }
400           }
401 
402           #[inline]
403           fn into_integer(self) -> Self::Int {
404               self as #integer_ty
405           }
406       },
407     ))
408   }
409 }
410 
get_struct_fields(input: &DeriveInput) -> Result<&Fields>411 fn get_struct_fields(input: &DeriveInput) -> Result<&Fields> {
412   if let Data::Struct(DataStruct { fields, .. }) = &input.data {
413     Ok(fields)
414   } else {
415     bail!("deriving this trait is only supported for structs")
416   }
417 }
418 
get_fields(input: &DeriveInput) -> Result<Fields>419 fn get_fields(input: &DeriveInput) -> Result<Fields> {
420   match &input.data {
421     Data::Struct(DataStruct { fields, .. }) => Ok(fields.clone()),
422     Data::Union(DataUnion { fields, .. }) => Ok(Fields::Named(fields.clone())),
423     Data::Enum(_) => bail!("deriving this trait is not supported for enums"),
424   }
425 }
426 
get_enum_variants<'a>( input: &'a DeriveInput, ) -> Result<impl Iterator<Item = &'a Variant> + 'a>427 fn get_enum_variants<'a>(
428   input: &'a DeriveInput,
429 ) -> Result<impl Iterator<Item = &'a Variant> + 'a> {
430   if let Data::Enum(DataEnum { variants, .. }) = &input.data {
431     Ok(variants.iter())
432   } else {
433     bail!("deriving this trait is only supported for enums")
434   }
435 }
436 
get_field_types<'a>( fields: &'a Fields, ) -> impl Iterator<Item = &'a Type> + 'a437 fn get_field_types<'a>(
438   fields: &'a Fields,
439 ) -> impl Iterator<Item = &'a Type> + 'a {
440   fields.iter().map(|field| &field.ty)
441 }
442 
generate_checked_bit_pattern_struct( input_ident: &Ident, fields: &Fields, attrs: &[Attribute], ) -> Result<(TokenStream, TokenStream)>443 fn generate_checked_bit_pattern_struct(
444   input_ident: &Ident, fields: &Fields, attrs: &[Attribute],
445 ) -> Result<(TokenStream, TokenStream)> {
446   let bits_ty = Ident::new(&format!("{}Bits", input_ident), input_ident.span());
447 
448   let repr = get_repr(attrs)?;
449 
450   let field_names = fields
451     .iter()
452     .enumerate()
453     .map(|(i, field)| {
454       field.ident.clone().unwrap_or_else(|| {
455         Ident::new(&format!("field{}", i), input_ident.span())
456       })
457     })
458     .collect::<Vec<_>>();
459   let field_tys = fields.iter().map(|field| &field.ty).collect::<Vec<_>>();
460 
461   let field_name = &field_names[..];
462   let field_ty = &field_tys[..];
463 
464   let derive_dbg =
465     quote!(#[cfg_attr(not(target_arch = "spirv"), derive(Debug))]);
466 
467   Ok((
468     quote! {
469         #repr
470         #[derive(Clone, Copy, ::bytemuck::AnyBitPattern)]
471         #derive_dbg
472         pub struct #bits_ty {
473             #(#field_name: <#field_ty as ::bytemuck::CheckedBitPattern>::Bits,)*
474         }
475     },
476     quote! {
477         type Bits = #bits_ty;
478 
479         #[inline]
480         #[allow(clippy::double_comparisons)]
481         fn is_valid_bit_pattern(bits: &#bits_ty) -> bool {
482             #(<#field_ty as ::bytemuck::CheckedBitPattern>::is_valid_bit_pattern(&{ bits.#field_name }) && )* true
483         }
484     },
485   ))
486 }
487 
generate_checked_bit_pattern_enum( input: &DeriveInput, ) -> Result<(TokenStream, TokenStream)>488 fn generate_checked_bit_pattern_enum(
489   input: &DeriveInput,
490 ) -> Result<(TokenStream, TokenStream)> {
491   let span = input.span();
492   let mut variants_with_discriminant =
493     VariantDiscriminantIterator::new(get_enum_variants(input)?);
494 
495   let (min, max, count) = variants_with_discriminant.try_fold(
496     (i64::max_value(), i64::min_value(), 0),
497     |(min, max, count), res| {
498       let discriminant = res?;
499       Ok::<_, Error>((
500         i64::min(min, discriminant),
501         i64::max(max, discriminant),
502         count + 1,
503       ))
504     },
505   )?;
506 
507   let check = if count == 0 {
508     quote_spanned!(span => false)
509   } else if max - min == count - 1 {
510     // contiguous range
511     let min_lit = LitInt::new(&format!("{}", min), span);
512     let max_lit = LitInt::new(&format!("{}", max), span);
513 
514     quote!(*bits >= #min_lit && *bits <= #max_lit)
515   } else {
516     // not contiguous range, check for each
517     let variant_lits =
518       VariantDiscriminantIterator::new(get_enum_variants(input)?)
519         .map(|res| {
520           let variant = res?;
521           Ok(LitInt::new(&format!("{}", variant), span))
522         })
523         .collect::<Result<Vec<_>>>()?;
524 
525     // count is at least 1
526     let first = &variant_lits[0];
527     let rest = &variant_lits[1..];
528 
529     quote!(matches!(*bits, #first #(| #rest )*))
530   };
531 
532   let repr = get_repr(&input.attrs)?;
533   let integer_ty = repr.repr.as_integer_type().unwrap(); // should be checked in attr check already
534   Ok((
535     quote!(),
536     quote! {
537         type Bits = #integer_ty;
538 
539         #[inline]
540         #[allow(clippy::double_comparisons)]
541         fn is_valid_bit_pattern(bits: &Self::Bits) -> bool {
542             #check
543         }
544     },
545   ))
546 }
547 
548 /// Check that a struct has no padding by asserting that the size of the struct
549 /// is equal to the sum of the size of it's fields
generate_assert_no_padding(input: &DeriveInput) -> Result<TokenStream>550 fn generate_assert_no_padding(input: &DeriveInput) -> Result<TokenStream> {
551   let struct_type = &input.ident;
552   let span = input.ident.span();
553   let fields = get_fields(input)?;
554 
555   let mut field_types = get_field_types(&fields);
556   let size_sum = if let Some(first) = field_types.next() {
557     let size_first = quote_spanned!(span => ::core::mem::size_of::<#first>());
558     let size_rest =
559       quote_spanned!(span => #( + ::core::mem::size_of::<#field_types>() )*);
560 
561     quote_spanned!(span => #size_first #size_rest)
562   } else {
563     quote_spanned!(span => 0)
564   };
565 
566   Ok(quote_spanned! {span => const _: fn() = || {
567     #[doc(hidden)]
568     struct TypeWithoutPadding([u8; #size_sum]);
569     let _ = ::core::mem::transmute::<#struct_type, TypeWithoutPadding>;
570   };})
571 }
572 
573 /// Check that all fields implement a given trait
generate_fields_are_trait( input: &DeriveInput, trait_: syn::Path, ) -> Result<TokenStream>574 fn generate_fields_are_trait(
575   input: &DeriveInput, trait_: syn::Path,
576 ) -> Result<TokenStream> {
577   let (impl_generics, _ty_generics, where_clause) =
578     input.generics.split_for_impl();
579   let fields = get_fields(input)?;
580   let span = input.span();
581   let field_types = get_field_types(&fields);
582   Ok(quote_spanned! {span => #(const _: fn() = || {
583       #[allow(clippy::missing_const_for_fn)]
584       #[doc(hidden)]
585       fn check #impl_generics () #where_clause {
586         fn assert_impl<T: #trait_>() {}
587         assert_impl::<#field_types>();
588       }
589     };)*
590   })
591 }
592 
get_ident_from_stream(tokens: TokenStream) -> Option<Ident>593 fn get_ident_from_stream(tokens: TokenStream) -> Option<Ident> {
594   match tokens.into_iter().next() {
595     Some(TokenTree::Group(group)) => get_ident_from_stream(group.stream()),
596     Some(TokenTree::Ident(ident)) => Some(ident),
597     _ => None,
598   }
599 }
600 
601 /// get a simple #[foo(bar)] attribute, returning "bar"
get_simple_attr(attributes: &[Attribute], attr_name: &str) -> Option<Ident>602 fn get_simple_attr(attributes: &[Attribute], attr_name: &str) -> Option<Ident> {
603   for attr in attributes {
604     if let (AttrStyle::Outer, Meta::List(list)) = (&attr.style, &attr.meta) {
605       if list.path.is_ident(attr_name) {
606         if let Some(ident) = get_ident_from_stream(list.tokens.clone()) {
607           return Some(ident);
608         }
609       }
610     }
611   }
612 
613   None
614 }
615 
get_repr(attributes: &[Attribute]) -> Result<Representation>616 fn get_repr(attributes: &[Attribute]) -> Result<Representation> {
617   attributes
618     .iter()
619     .filter_map(|attr| {
620       if attr.path().is_ident("repr") {
621         Some(attr.parse_args::<Representation>())
622       } else {
623         None
624       }
625     })
626     .try_fold(Representation::default(), |a, b| {
627       let b = b?;
628       Ok(Representation {
629         repr: match (a.repr, b.repr) {
630           (a, Repr::Rust) => a,
631           (Repr::Rust, b) => b,
632           _ => bail!("conflicting representation hints"),
633         },
634         packed: match (a.packed, b.packed) {
635           (a, None) => a,
636           (None, b) => b,
637           _ => bail!("conflicting representation hints"),
638         },
639         align: match (a.align, b.align) {
640           (a, None) => a,
641           (None, b) => b,
642           _ => bail!("conflicting representation hints"),
643         },
644       })
645     })
646 }
647 
648 mk_repr! {
649   U8 => u8,
650   I8 => i8,
651   U16 => u16,
652   I16 => i16,
653   U32 => u32,
654   I32 => i32,
655   U64 => u64,
656   I64 => i64,
657   I128 => i128,
658   U128 => u128,
659   Usize => usize,
660   Isize => isize,
661 }
662 // where
663 macro_rules! mk_repr {(
664   $(
665     $Xn:ident => $xn:ident
666   ),* $(,)?
667 ) => (
668   #[derive(Clone, Copy, PartialEq)]
669   enum Repr {
670     Rust,
671     C,
672     Transparent,
673     $($Xn),*
674   }
675 
676   impl Repr {
677     fn is_integer(self) -> bool {
678       match self {
679         Repr::Rust | Repr::C | Repr::Transparent => false,
680         _ => true,
681       }
682     }
683 
684     fn as_integer_type(self) -> Option<TokenStream> {
685       match self {
686         Repr::Rust | Repr::C | Repr::Transparent => None,
687         $(
688           Repr::$Xn => Some(quote! { ::core::primitive::$xn }),
689         )*
690       }
691     }
692   }
693 
694   #[derive(Clone, Copy)]
695   struct Representation {
696     packed: Option<u32>,
697     align: Option<u32>,
698     repr: Repr,
699   }
700 
701   impl Default for Representation {
702     fn default() -> Self {
703       Self { packed: None, align: None, repr: Repr::Rust }
704     }
705   }
706 
707   impl Parse for Representation {
708     fn parse(input: ParseStream<'_>) -> Result<Representation> {
709       let mut ret = Representation::default();
710       while !input.is_empty() {
711         let keyword = input.parse::<Ident>()?;
712         // preëmptively call `.to_string()` *once* (rather than on `is_ident()`)
713         let keyword_str = keyword.to_string();
714         let new_repr = match keyword_str.as_str() {
715           "C" => Repr::C,
716           "transparent" => Repr::Transparent,
717           "packed" => {
718             ret.packed = Some(if input.peek(token::Paren) {
719               let contents; parenthesized!(contents in input);
720               LitInt::base10_parse::<u32>(&contents.parse()?)?
721             } else {
722               1
723             });
724             let _: Option<Token![,]> = input.parse()?;
725             continue;
726           },
727           "align" => {
728             let contents; parenthesized!(contents in input);
729             ret.align = Some(LitInt::base10_parse::<u32>(&contents.parse()?)?);
730             let _: Option<Token![,]> = input.parse()?;
731             continue;
732           },
733         $(
734           stringify!($xn) => Repr::$Xn,
735         )*
736           _ => return Err(input.error("unrecognized representation hint"))
737         };
738         if ::core::mem::replace(&mut ret.repr, new_repr) != Repr::Rust {
739           input.error("duplicate representation hint");
740         }
741         let _: Option<Token![,]> = input.parse()?;
742       }
743       Ok(ret)
744     }
745   }
746 
747   impl ToTokens for Representation {
748     fn to_tokens(&self, tokens: &mut TokenStream) {
749       let repr = match self.repr {
750         Repr::Rust => None,
751         Repr::C => Some(quote!(C)),
752         Repr::Transparent => Some(quote!(transparent)),
753         $(
754           Repr::$Xn => Some(quote!($xn)),
755         )*
756       };
757       let packed = self.packed.map(|p| {
758         let lit = LitInt::new(&p.to_string(), Span::call_site());
759         quote!(packed(#lit))
760       });
761       let comma = if packed.is_some() && repr.is_some() {
762         Some(quote!(,))
763       } else {
764         None
765       };
766       tokens.extend(quote!(
767         #[repr( #repr #comma #packed )]
768       ));
769     }
770   }
771 )}
772 use mk_repr;
773 
774 struct VariantDiscriminantIterator<'a, I: Iterator<Item = &'a Variant> + 'a> {
775   inner: I,
776   last_value: i64,
777 }
778 
779 impl<'a, I: Iterator<Item = &'a Variant> + 'a>
780   VariantDiscriminantIterator<'a, I>
781 {
new(inner: I) -> Self782   fn new(inner: I) -> Self {
783     VariantDiscriminantIterator { inner, last_value: -1 }
784   }
785 }
786 
787 impl<'a, I: Iterator<Item = &'a Variant> + 'a> Iterator
788   for VariantDiscriminantIterator<'a, I>
789 {
790   type Item = Result<i64>;
791 
next(&mut self) -> Option<Self::Item>792   fn next(&mut self) -> Option<Self::Item> {
793     let variant = self.inner.next()?;
794     if !variant.fields.is_empty() {
795       return Some(Err(Error::new_spanned(
796         &variant.fields,
797         "Only fieldless enums are supported",
798       )));
799     }
800 
801     if let Some((_, discriminant)) = &variant.discriminant {
802       let discriminant_value = match parse_int_expr(discriminant) {
803         Ok(value) => value,
804         Err(e) => return Some(Err(e)),
805       };
806       self.last_value = discriminant_value;
807     } else {
808       self.last_value += 1;
809     }
810 
811     Some(Ok(self.last_value))
812   }
813 }
814 
parse_int_expr(expr: &Expr) -> Result<i64>815 fn parse_int_expr(expr: &Expr) -> Result<i64> {
816   match expr {
817     Expr::Unary(ExprUnary { op: UnOp::Neg(_), expr, .. }) => {
818       parse_int_expr(expr).map(|int| -int)
819     }
820     Expr::Lit(ExprLit { lit: Lit::Int(int), .. }) => int.base10_parse(),
821     Expr::Lit(ExprLit { lit: Lit::Byte(byte), .. }) => Ok(byte.value().into()),
822     _ => bail!("Not an integer expression"),
823   }
824 }
825