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