1 #![allow(unused_imports)]
2 use std::{cmp, convert::TryFrom};
3
4 use proc_macro2::{Ident, Span, TokenStream, TokenTree};
5 use quote::{quote, quote_spanned, ToTokens};
6 use syn::{
7 parse::{Parse, ParseStream, Parser},
8 punctuated::Punctuated,
9 spanned::Spanned,
10 Result, *,
11 };
12
13 macro_rules! bail {
14 ($msg:expr $(,)?) => {
15 return Err(Error::new(Span::call_site(), &$msg[..]))
16 };
17
18 ( $msg:expr => $span_to_blame:expr $(,)? ) => {
19 return Err(Error::new_spanned(&$span_to_blame, $msg))
20 };
21 }
22
23 pub trait Derivable {
ident(input: &DeriveInput, crate_name: &TokenStream) -> Result<syn::Path>24 fn ident(input: &DeriveInput, crate_name: &TokenStream) -> Result<syn::Path>;
implies_trait(_crate_name: &TokenStream) -> Option<TokenStream>25 fn implies_trait(_crate_name: &TokenStream) -> Option<TokenStream> {
26 None
27 }
asserts( _input: &DeriveInput, _crate_name: &TokenStream, ) -> Result<TokenStream>28 fn asserts(
29 _input: &DeriveInput, _crate_name: &TokenStream,
30 ) -> Result<TokenStream> {
31 Ok(quote!())
32 }
check_attributes(_ty: &Data, _attributes: &[Attribute]) -> Result<()>33 fn check_attributes(_ty: &Data, _attributes: &[Attribute]) -> Result<()> {
34 Ok(())
35 }
trait_impl( _input: &DeriveInput, _crate_name: &TokenStream, ) -> Result<(TokenStream, TokenStream)>36 fn trait_impl(
37 _input: &DeriveInput, _crate_name: &TokenStream,
38 ) -> Result<(TokenStream, TokenStream)> {
39 Ok((quote!(), quote!()))
40 }
requires_where_clause() -> bool41 fn requires_where_clause() -> bool {
42 true
43 }
explicit_bounds_attribute_name() -> Option<&'static str>44 fn explicit_bounds_attribute_name() -> Option<&'static str> {
45 None
46 }
47 }
48
49 pub struct Pod;
50
51 impl Derivable for Pod {
ident(_: &DeriveInput, crate_name: &TokenStream) -> Result<syn::Path>52 fn ident(_: &DeriveInput, crate_name: &TokenStream) -> Result<syn::Path> {
53 Ok(syn::parse_quote!(#crate_name::Pod))
54 }
55
asserts( input: &DeriveInput, crate_name: &TokenStream, ) -> Result<TokenStream>56 fn asserts(
57 input: &DeriveInput, crate_name: &TokenStream,
58 ) -> Result<TokenStream> {
59 let repr = get_repr(&input.attrs)?;
60
61 let completly_packed =
62 repr.packed == Some(1) || repr.repr == Repr::Transparent;
63
64 if !completly_packed && !input.generics.params.is_empty() {
65 bail!("\
66 Pod requires cannot be derived for non-packed types containing \
67 generic parameters because the padding requirements can't be verified \
68 for generic non-packed structs\
69 " => input.generics.params.first().unwrap());
70 }
71
72 match &input.data {
73 Data::Struct(_) => {
74 let assert_no_padding = if !completly_packed {
75 Some(generate_assert_no_padding(input)?)
76 } else {
77 None
78 };
79 let assert_fields_are_pod =
80 generate_fields_are_trait(input, Self::ident(input, crate_name)?)?;
81
82 Ok(quote!(
83 #assert_no_padding
84 #assert_fields_are_pod
85 ))
86 }
87 Data::Enum(_) => bail!("Deriving Pod is not supported for enums"),
88 Data::Union(_) => bail!("Deriving Pod is not supported for unions"),
89 }
90 }
91
check_attributes(_ty: &Data, attributes: &[Attribute]) -> Result<()>92 fn check_attributes(_ty: &Data, attributes: &[Attribute]) -> Result<()> {
93 let repr = get_repr(attributes)?;
94 match repr.repr {
95 Repr::C => Ok(()),
96 Repr::Transparent => Ok(()),
97 _ => {
98 bail!("Pod requires the type to be #[repr(C)] or #[repr(transparent)]")
99 }
100 }
101 }
102 }
103
104 pub struct AnyBitPattern;
105
106 impl Derivable for AnyBitPattern {
ident(_: &DeriveInput, crate_name: &TokenStream) -> Result<syn::Path>107 fn ident(_: &DeriveInput, crate_name: &TokenStream) -> Result<syn::Path> {
108 Ok(syn::parse_quote!(#crate_name::AnyBitPattern))
109 }
110
implies_trait(crate_name: &TokenStream) -> Option<TokenStream>111 fn implies_trait(crate_name: &TokenStream) -> Option<TokenStream> {
112 Some(quote!(#crate_name::Zeroable))
113 }
114
asserts( input: &DeriveInput, crate_name: &TokenStream, ) -> Result<TokenStream>115 fn asserts(
116 input: &DeriveInput, crate_name: &TokenStream,
117 ) -> Result<TokenStream> {
118 match &input.data {
119 Data::Union(_) => Ok(quote!()), // unions are always `AnyBitPattern`
120 Data::Struct(_) => {
121 generate_fields_are_trait(input, Self::ident(input, crate_name)?)
122 }
123 Data::Enum(_) => {
124 bail!("Deriving AnyBitPattern is not supported for enums")
125 }
126 }
127 }
128 }
129
130 pub struct Zeroable;
131
132 impl Derivable for Zeroable {
ident(_: &DeriveInput, crate_name: &TokenStream) -> Result<syn::Path>133 fn ident(_: &DeriveInput, crate_name: &TokenStream) -> Result<syn::Path> {
134 Ok(syn::parse_quote!(#crate_name::Zeroable))
135 }
136
check_attributes(ty: &Data, attributes: &[Attribute]) -> Result<()>137 fn check_attributes(ty: &Data, attributes: &[Attribute]) -> Result<()> {
138 let repr = get_repr(attributes)?;
139 match ty {
140 Data::Struct(_) => Ok(()),
141 Data::Enum(DataEnum { variants, .. }) => {
142 if !repr.repr.is_integer() {
143 bail!("Zeroable requires the enum to be an explicit #[repr(Int)]")
144 }
145
146 if variants.iter().any(|variant| !variant.fields.is_empty()) {
147 bail!("Only fieldless enums are supported for Zeroable")
148 }
149
150 let iter = VariantDiscriminantIterator::new(variants.iter());
151 let mut has_zero_variant = false;
152 for res in iter {
153 let discriminant = res?;
154 if discriminant == 0 {
155 has_zero_variant = true;
156 break;
157 }
158 }
159 if !has_zero_variant {
160 bail!("No variant's discriminant is 0")
161 }
162
163 Ok(())
164 }
165 Data::Union(_) => Ok(()),
166 }
167 }
168
asserts( input: &DeriveInput, crate_name: &TokenStream, ) -> Result<TokenStream>169 fn asserts(
170 input: &DeriveInput, crate_name: &TokenStream,
171 ) -> Result<TokenStream> {
172 match &input.data {
173 Data::Union(_) => Ok(quote!()), // unions are always `Zeroable`
174 Data::Struct(_) => {
175 generate_fields_are_trait(input, Self::ident(input, crate_name)?)
176 }
177 Data::Enum(_) => Ok(quote!()),
178 }
179 }
180
explicit_bounds_attribute_name() -> Option<&'static str>181 fn explicit_bounds_attribute_name() -> Option<&'static str> {
182 Some("zeroable")
183 }
184 }
185
186 pub struct NoUninit;
187
188 impl Derivable for NoUninit {
ident(_: &DeriveInput, crate_name: &TokenStream) -> Result<syn::Path>189 fn ident(_: &DeriveInput, crate_name: &TokenStream) -> Result<syn::Path> {
190 Ok(syn::parse_quote!(#crate_name::NoUninit))
191 }
192
check_attributes(ty: &Data, attributes: &[Attribute]) -> Result<()>193 fn check_attributes(ty: &Data, attributes: &[Attribute]) -> Result<()> {
194 let repr = get_repr(attributes)?;
195 match ty {
196 Data::Struct(_) => match repr.repr {
197 Repr::C | Repr::Transparent => Ok(()),
198 _ => bail!("NoUninit requires the struct to be #[repr(C)] or #[repr(transparent)]"),
199 },
200 Data::Enum(_) => if repr.repr.is_integer() {
201 Ok(())
202 } else {
203 bail!("NoUninit requires the enum to be an explicit #[repr(Int)]")
204 },
205 Data::Union(_) => bail!("NoUninit can only be derived on enums and structs")
206 }
207 }
208
asserts( input: &DeriveInput, crate_name: &TokenStream, ) -> Result<TokenStream>209 fn asserts(
210 input: &DeriveInput, crate_name: &TokenStream,
211 ) -> Result<TokenStream> {
212 if !input.generics.params.is_empty() {
213 bail!("NoUninit cannot be derived for structs containing generic parameters because the padding requirements can't be verified for generic structs");
214 }
215
216 match &input.data {
217 Data::Struct(DataStruct { .. }) => {
218 let assert_no_padding = generate_assert_no_padding(&input)?;
219 let assert_fields_are_no_padding =
220 generate_fields_are_trait(&input, Self::ident(input, crate_name)?)?;
221
222 Ok(quote!(
223 #assert_no_padding
224 #assert_fields_are_no_padding
225 ))
226 }
227 Data::Enum(DataEnum { variants, .. }) => {
228 if variants.iter().any(|variant| !variant.fields.is_empty()) {
229 bail!("Only fieldless enums are supported for NoUninit")
230 } else {
231 Ok(quote!())
232 }
233 }
234 Data::Union(_) => bail!("NoUninit cannot be derived for unions"), /* shouldn't be possible since we already error in attribute check for this case */
235 }
236 }
237
trait_impl( _input: &DeriveInput, _crate_name: &TokenStream, ) -> Result<(TokenStream, TokenStream)>238 fn trait_impl(
239 _input: &DeriveInput, _crate_name: &TokenStream,
240 ) -> Result<(TokenStream, TokenStream)> {
241 Ok((quote!(), quote!()))
242 }
243 }
244
245 pub struct CheckedBitPattern;
246
247 impl Derivable for CheckedBitPattern {
ident(_: &DeriveInput, crate_name: &TokenStream) -> Result<syn::Path>248 fn ident(_: &DeriveInput, crate_name: &TokenStream) -> Result<syn::Path> {
249 Ok(syn::parse_quote!(#crate_name::CheckedBitPattern))
250 }
251
check_attributes(ty: &Data, attributes: &[Attribute]) -> Result<()>252 fn check_attributes(ty: &Data, attributes: &[Attribute]) -> Result<()> {
253 let repr = get_repr(attributes)?;
254 match ty {
255 Data::Struct(_) => match repr.repr {
256 Repr::C | Repr::Transparent => Ok(()),
257 _ => bail!("CheckedBitPattern derive requires the struct to be #[repr(C)] or #[repr(transparent)]"),
258 },
259 Data::Enum(DataEnum { variants,.. }) => {
260 if !enum_has_fields(variants.iter()){
261 if repr.repr.is_integer() {
262 Ok(())
263 } else {
264 bail!("CheckedBitPattern requires the enum to be an explicit #[repr(Int)]")
265 }
266 } else if matches!(repr.repr, Repr::Rust) {
267 bail!("CheckedBitPattern requires an explicit repr annotation because `repr(Rust)` doesn't have a specified type layout")
268 } else {
269 Ok(())
270 }
271 }
272 Data::Union(_) => bail!("CheckedBitPattern can only be derived on enums and structs")
273 }
274 }
275
asserts( input: &DeriveInput, crate_name: &TokenStream, ) -> Result<TokenStream>276 fn asserts(
277 input: &DeriveInput, crate_name: &TokenStream,
278 ) -> Result<TokenStream> {
279 if !input.generics.params.is_empty() {
280 bail!("CheckedBitPattern cannot be derived for structs containing generic parameters");
281 }
282
283 match &input.data {
284 Data::Struct(DataStruct { .. }) => {
285 let assert_fields_are_maybe_pod =
286 generate_fields_are_trait(&input, Self::ident(input, crate_name)?)?;
287
288 Ok(assert_fields_are_maybe_pod)
289 }
290 Data::Enum(_) => Ok(quote!()), /* nothing needed, already guaranteed
291 * OK by NoUninit */
292 Data::Union(_) => bail!("Internal error in CheckedBitPattern derive"), /* shouldn't be possible since we already error in attribute check for this case */
293 }
294 }
295
trait_impl( input: &DeriveInput, crate_name: &TokenStream, ) -> Result<(TokenStream, TokenStream)>296 fn trait_impl(
297 input: &DeriveInput, crate_name: &TokenStream,
298 ) -> Result<(TokenStream, TokenStream)> {
299 match &input.data {
300 Data::Struct(DataStruct { fields, .. }) => {
301 generate_checked_bit_pattern_struct(
302 &input.ident,
303 fields,
304 &input.attrs,
305 crate_name,
306 )
307 }
308 Data::Enum(DataEnum { variants, .. }) => {
309 generate_checked_bit_pattern_enum(input, variants, crate_name)
310 }
311 Data::Union(_) => bail!("Internal error in CheckedBitPattern derive"), /* shouldn't be possible since we already error in attribute check for this case */
312 }
313 }
314 }
315
316 pub struct TransparentWrapper;
317
318 impl TransparentWrapper {
get_wrapper_type( attributes: &[Attribute], fields: &Fields, ) -> Option<TokenStream>319 fn get_wrapper_type(
320 attributes: &[Attribute], fields: &Fields,
321 ) -> Option<TokenStream> {
322 let transparent_param = get_simple_attr(attributes, "transparent");
323 transparent_param.map(|ident| ident.to_token_stream()).or_else(|| {
324 let mut types = get_field_types(&fields);
325 let first_type = types.next();
326 if let Some(_) = types.next() {
327 // can't guess param type if there is more than one field
328 return None;
329 } else {
330 first_type.map(|ty| ty.to_token_stream())
331 }
332 })
333 }
334 }
335
336 impl Derivable for TransparentWrapper {
ident(input: &DeriveInput, crate_name: &TokenStream) -> Result<syn::Path>337 fn ident(input: &DeriveInput, crate_name: &TokenStream) -> Result<syn::Path> {
338 let fields = get_struct_fields(input)?;
339
340 let ty = match Self::get_wrapper_type(&input.attrs, &fields) {
341 Some(ty) => ty,
342 None => bail!(
343 "\
344 when deriving TransparentWrapper for a struct with more than one field \
345 you need to specify the transparent field using #[transparent(T)]\
346 "
347 ),
348 };
349
350 Ok(syn::parse_quote!(#crate_name::TransparentWrapper<#ty>))
351 }
352
asserts( input: &DeriveInput, crate_name: &TokenStream, ) -> Result<TokenStream>353 fn asserts(
354 input: &DeriveInput, crate_name: &TokenStream,
355 ) -> Result<TokenStream> {
356 let (impl_generics, _ty_generics, where_clause) =
357 input.generics.split_for_impl();
358 let fields = get_struct_fields(input)?;
359 let wrapped_type = match Self::get_wrapper_type(&input.attrs, &fields) {
360 Some(wrapped_type) => wrapped_type.to_string(),
361 None => unreachable!(), /* other code will already reject this derive */
362 };
363 let mut wrapped_field_ty = None;
364 let mut nonwrapped_field_tys = vec![];
365 for field in fields.iter() {
366 let field_ty = &field.ty;
367 if field_ty.to_token_stream().to_string() == wrapped_type {
368 if wrapped_field_ty.is_some() {
369 bail!(
370 "TransparentWrapper can only have one field of the wrapped type"
371 );
372 }
373 wrapped_field_ty = Some(field_ty);
374 } else {
375 nonwrapped_field_tys.push(field_ty);
376 }
377 }
378 if let Some(wrapped_field_ty) = wrapped_field_ty {
379 Ok(quote!(
380 const _: () = {
381 #[repr(transparent)]
382 #[allow(clippy::multiple_bound_locations)]
383 struct AssertWrappedIsWrapped #impl_generics((u8, ::core::marker::PhantomData<#wrapped_field_ty>), #(#nonwrapped_field_tys),*) #where_clause;
384 fn assert_zeroable<Z: #crate_name::Zeroable>() {}
385 #[allow(clippy::multiple_bound_locations)]
386 fn check #impl_generics () #where_clause {
387 #(
388 assert_zeroable::<#nonwrapped_field_tys>();
389 )*
390 }
391 };
392 ))
393 } else {
394 bail!("TransparentWrapper must have one field of the wrapped type")
395 }
396 }
397
check_attributes(_ty: &Data, attributes: &[Attribute]) -> Result<()>398 fn check_attributes(_ty: &Data, attributes: &[Attribute]) -> Result<()> {
399 let repr = get_repr(attributes)?;
400
401 match repr.repr {
402 Repr::Transparent => Ok(()),
403 _ => {
404 bail!(
405 "TransparentWrapper requires the struct to be #[repr(transparent)]"
406 )
407 }
408 }
409 }
410
requires_where_clause() -> bool411 fn requires_where_clause() -> bool {
412 false
413 }
414 }
415
416 pub struct Contiguous;
417
418 impl Derivable for Contiguous {
ident(_: &DeriveInput, crate_name: &TokenStream) -> Result<syn::Path>419 fn ident(_: &DeriveInput, crate_name: &TokenStream) -> Result<syn::Path> {
420 Ok(syn::parse_quote!(#crate_name::Contiguous))
421 }
422
trait_impl( input: &DeriveInput, _crate_name: &TokenStream, ) -> Result<(TokenStream, TokenStream)>423 fn trait_impl(
424 input: &DeriveInput, _crate_name: &TokenStream,
425 ) -> Result<(TokenStream, TokenStream)> {
426 let repr = get_repr(&input.attrs)?;
427
428 let integer_ty = if let Some(integer_ty) = repr.repr.as_integer() {
429 integer_ty
430 } else {
431 bail!("Contiguous requires the enum to be #[repr(Int)]");
432 };
433
434 let variants = get_enum_variants(input)?;
435 if enum_has_fields(variants.clone()) {
436 return Err(Error::new_spanned(
437 &input,
438 "Only fieldless enums are supported",
439 ));
440 }
441
442 let mut variants_with_discriminator =
443 VariantDiscriminantIterator::new(variants);
444
445 let (min, max, count) = variants_with_discriminator.try_fold(
446 (i64::max_value(), i64::min_value(), 0),
447 |(min, max, count), res| {
448 let discriminator = res?;
449 Ok::<_, Error>((
450 i64::min(min, discriminator),
451 i64::max(max, discriminator),
452 count + 1,
453 ))
454 },
455 )?;
456
457 if max - min != count - 1 {
458 bail! {
459 "Contiguous requires the enum discriminants to be contiguous",
460 }
461 }
462
463 let min_lit = LitInt::new(&format!("{}", min), input.span());
464 let max_lit = LitInt::new(&format!("{}", max), input.span());
465
466 // `from_integer` and `into_integer` are usually provided by the trait's
467 // default implementation. We override this implementation because it
468 // goes through `transmute_copy`, which can lead to inefficient assembly as seen in https://github.com/Lokathor/bytemuck/issues/175 .
469
470 Ok((
471 quote!(),
472 quote! {
473 type Int = #integer_ty;
474
475 #[allow(clippy::missing_docs_in_private_items)]
476 const MIN_VALUE: #integer_ty = #min_lit;
477
478 #[allow(clippy::missing_docs_in_private_items)]
479 const MAX_VALUE: #integer_ty = #max_lit;
480
481 #[inline]
482 fn from_integer(value: Self::Int) -> Option<Self> {
483 #[allow(clippy::manual_range_contains)]
484 if Self::MIN_VALUE <= value && value <= Self::MAX_VALUE {
485 Some(unsafe { ::core::mem::transmute(value) })
486 } else {
487 None
488 }
489 }
490
491 #[inline]
492 fn into_integer(self) -> Self::Int {
493 self as #integer_ty
494 }
495 },
496 ))
497 }
498 }
499
get_struct_fields(input: &DeriveInput) -> Result<&Fields>500 fn get_struct_fields(input: &DeriveInput) -> Result<&Fields> {
501 if let Data::Struct(DataStruct { fields, .. }) = &input.data {
502 Ok(fields)
503 } else {
504 bail!("deriving this trait is only supported for structs")
505 }
506 }
507
get_fields(input: &DeriveInput) -> Result<Fields>508 fn get_fields(input: &DeriveInput) -> Result<Fields> {
509 match &input.data {
510 Data::Struct(DataStruct { fields, .. }) => Ok(fields.clone()),
511 Data::Union(DataUnion { fields, .. }) => Ok(Fields::Named(fields.clone())),
512 Data::Enum(_) => bail!("deriving this trait is not supported for enums"),
513 }
514 }
515
get_enum_variants<'a>( input: &'a DeriveInput, ) -> Result<impl Iterator<Item = &'a Variant> + Clone + 'a>516 fn get_enum_variants<'a>(
517 input: &'a DeriveInput,
518 ) -> Result<impl Iterator<Item = &'a Variant> + Clone + 'a> {
519 if let Data::Enum(DataEnum { variants, .. }) = &input.data {
520 Ok(variants.iter())
521 } else {
522 bail!("deriving this trait is only supported for enums")
523 }
524 }
525
get_field_types<'a>( fields: &'a Fields, ) -> impl Iterator<Item = &'a Type> + 'a526 fn get_field_types<'a>(
527 fields: &'a Fields,
528 ) -> impl Iterator<Item = &'a Type> + 'a {
529 fields.iter().map(|field| &field.ty)
530 }
531
generate_checked_bit_pattern_struct( input_ident: &Ident, fields: &Fields, attrs: &[Attribute], crate_name: &TokenStream, ) -> Result<(TokenStream, TokenStream)>532 fn generate_checked_bit_pattern_struct(
533 input_ident: &Ident, fields: &Fields, attrs: &[Attribute],
534 crate_name: &TokenStream,
535 ) -> Result<(TokenStream, TokenStream)> {
536 let bits_ty = Ident::new(&format!("{}Bits", input_ident), input_ident.span());
537
538 let repr = get_repr(attrs)?;
539
540 let field_names = fields
541 .iter()
542 .enumerate()
543 .map(|(i, field)| {
544 field.ident.clone().unwrap_or_else(|| {
545 Ident::new(&format!("field{}", i), input_ident.span())
546 })
547 })
548 .collect::<Vec<_>>();
549 let field_tys = fields.iter().map(|field| &field.ty).collect::<Vec<_>>();
550
551 let field_name = &field_names[..];
552 let field_ty = &field_tys[..];
553
554 let derive_dbg =
555 quote!(#[cfg_attr(not(target_arch = "spirv"), derive(Debug))]);
556
557 Ok((
558 quote! {
559 #[doc = #GENERATED_TYPE_DOCUMENTATION]
560 #repr
561 #[derive(Clone, Copy, #crate_name::AnyBitPattern)]
562 #derive_dbg
563 #[allow(missing_docs)]
564 pub struct #bits_ty {
565 #(#field_name: <#field_ty as #crate_name::CheckedBitPattern>::Bits,)*
566 }
567 },
568 quote! {
569 type Bits = #bits_ty;
570
571 #[inline]
572 #[allow(clippy::double_comparisons, unused)]
573 fn is_valid_bit_pattern(bits: &#bits_ty) -> bool {
574 #(<#field_ty as #crate_name::CheckedBitPattern>::is_valid_bit_pattern(&{ bits.#field_name }) && )* true
575 }
576 },
577 ))
578 }
579
generate_checked_bit_pattern_enum( input: &DeriveInput, variants: &Punctuated<Variant, Token![,]>, crate_name: &TokenStream, ) -> Result<(TokenStream, TokenStream)>580 fn generate_checked_bit_pattern_enum(
581 input: &DeriveInput, variants: &Punctuated<Variant, Token![,]>,
582 crate_name: &TokenStream,
583 ) -> Result<(TokenStream, TokenStream)> {
584 if enum_has_fields(variants.iter()) {
585 generate_checked_bit_pattern_enum_with_fields(input, variants, crate_name)
586 } else {
587 generate_checked_bit_pattern_enum_without_fields(input, variants)
588 }
589 }
590
generate_checked_bit_pattern_enum_without_fields( input: &DeriveInput, variants: &Punctuated<Variant, Token![,]>, ) -> Result<(TokenStream, TokenStream)>591 fn generate_checked_bit_pattern_enum_without_fields(
592 input: &DeriveInput, variants: &Punctuated<Variant, Token![,]>,
593 ) -> Result<(TokenStream, TokenStream)> {
594 let span = input.span();
595 let mut variants_with_discriminant =
596 VariantDiscriminantIterator::new(variants.iter());
597
598 let (min, max, count) = variants_with_discriminant.try_fold(
599 (i64::max_value(), i64::min_value(), 0),
600 |(min, max, count), res| {
601 let discriminant = res?;
602 Ok::<_, Error>((
603 i64::min(min, discriminant),
604 i64::max(max, discriminant),
605 count + 1,
606 ))
607 },
608 )?;
609
610 let check = if count == 0 {
611 quote_spanned!(span => false)
612 } else if max - min == count - 1 {
613 // contiguous range
614 let min_lit = LitInt::new(&format!("{}", min), span);
615 let max_lit = LitInt::new(&format!("{}", max), span);
616
617 quote!(*bits >= #min_lit && *bits <= #max_lit)
618 } else {
619 // not contiguous range, check for each
620 let variant_lits = VariantDiscriminantIterator::new(variants.iter())
621 .map(|res| {
622 let variant = res?;
623 Ok(LitInt::new(&format!("{}", variant), span))
624 })
625 .collect::<Result<Vec<_>>>()?;
626
627 // count is at least 1
628 let first = &variant_lits[0];
629 let rest = &variant_lits[1..];
630
631 quote!(matches!(*bits, #first #(| #rest )*))
632 };
633
634 let repr = get_repr(&input.attrs)?;
635 let integer = repr.repr.as_integer().unwrap(); // should be checked in attr check already
636 Ok((
637 quote!(),
638 quote! {
639 type Bits = #integer;
640
641 #[inline]
642 #[allow(clippy::double_comparisons)]
643 fn is_valid_bit_pattern(bits: &Self::Bits) -> bool {
644 #check
645 }
646 },
647 ))
648 }
649
generate_checked_bit_pattern_enum_with_fields( input: &DeriveInput, variants: &Punctuated<Variant, Token![,]>, crate_name: &TokenStream, ) -> Result<(TokenStream, TokenStream)>650 fn generate_checked_bit_pattern_enum_with_fields(
651 input: &DeriveInput, variants: &Punctuated<Variant, Token![,]>,
652 crate_name: &TokenStream,
653 ) -> Result<(TokenStream, TokenStream)> {
654 let representation = get_repr(&input.attrs)?;
655 let vis = &input.vis;
656
657 let derive_dbg =
658 quote!(#[cfg_attr(not(target_arch = "spirv"), derive(Debug))]);
659
660 match representation.repr {
661 Repr::Rust => unreachable!(),
662 repr @ (Repr::C | Repr::CWithDiscriminant(_)) => {
663 let integer = match repr {
664 Repr::C => quote!(::core::ffi::c_int),
665 Repr::CWithDiscriminant(integer) => quote!(#integer),
666 _ => unreachable!(),
667 };
668 let input_ident = &input.ident;
669
670 let bits_repr = Representation { repr: Repr::C, ..representation };
671
672 // the enum manually re-configured as the actual tagged union it
673 // represents, thus circumventing the requirements rust imposes on
674 // the tag even when using #[repr(C)] enum layout
675 // see: https://doc.rust-lang.org/reference/type-layout.html#reprc-enums-with-fields
676 let bits_ty_ident =
677 Ident::new(&format!("{input_ident}Bits"), input.span());
678
679 // the variants union part of the tagged union. These get put into a union
680 // which gets the AnyBitPattern derive applied to it, thus checking
681 // that the fields of the union obey the requriements of AnyBitPattern.
682 // The types that actually go in the union are one more level of
683 // indirection deep: we generate new structs for each variant
684 // (`variant_struct_definitions`) which themselves have the
685 // `CheckedBitPattern` derive applied, thus generating
686 // `{variant_struct_ident}Bits` structs, which are the ones that go
687 // into this union.
688 let variants_union_ident =
689 Ident::new(&format!("{}Variants", input.ident), input.span());
690
691 let variant_struct_idents = variants.iter().map(|v| {
692 Ident::new(&format!("{input_ident}Variant{}", v.ident), v.span())
693 });
694
695 let variant_struct_definitions =
696 variant_struct_idents.clone().zip(variants.iter()).map(|(variant_struct_ident, v)| {
697 let fields = v.fields.iter().map(|v| &v.ty);
698
699 quote! {
700 #[derive(::core::clone::Clone, ::core::marker::Copy, #crate_name::CheckedBitPattern)]
701 #[repr(C)]
702 #vis struct #variant_struct_ident(#(#fields),*);
703 }
704 });
705
706 let union_fields = variant_struct_idents
707 .clone()
708 .zip(variants.iter())
709 .map(|(variant_struct_ident, v)| {
710 let variant_struct_bits_ident =
711 Ident::new(&format!("{variant_struct_ident}Bits"), input.span());
712 let field_ident = &v.ident;
713 quote! {
714 #field_ident: #variant_struct_bits_ident
715 }
716 });
717
718 let variant_checks = variant_struct_idents
719 .clone()
720 .zip(VariantDiscriminantIterator::new(variants.iter()))
721 .zip(variants.iter())
722 .map(|((variant_struct_ident, discriminant), v)| -> Result<_> {
723 let discriminant = discriminant?;
724 let discriminant = LitInt::new(&discriminant.to_string(), v.span());
725 let ident = &v.ident;
726 Ok(quote! {
727 #discriminant => {
728 let payload = unsafe { &bits.payload.#ident };
729 <#variant_struct_ident as #crate_name::CheckedBitPattern>::is_valid_bit_pattern(payload)
730 }
731 })
732 })
733 .collect::<Result<Vec<_>>>()?;
734
735 Ok((
736 quote! {
737 #[doc = #GENERATED_TYPE_DOCUMENTATION]
738 #[derive(::core::clone::Clone, ::core::marker::Copy, #crate_name::AnyBitPattern)]
739 #derive_dbg
740 #bits_repr
741 #vis struct #bits_ty_ident {
742 tag: #integer,
743 payload: #variants_union_ident,
744 }
745
746 #[derive(::core::clone::Clone, ::core::marker::Copy, #crate_name::AnyBitPattern)]
747 #[repr(C)]
748 #[allow(non_snake_case)]
749 #vis union #variants_union_ident {
750 #(#union_fields,)*
751 }
752
753 #[cfg(not(target_arch = "spirv"))]
754 impl ::core::fmt::Debug for #variants_union_ident {
755 fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
756 let mut debug_struct = ::core::fmt::Formatter::debug_struct(f, ::core::stringify!(#variants_union_ident));
757 ::core::fmt::DebugStruct::finish_non_exhaustive(&mut debug_struct)
758 }
759 }
760
761 #(#variant_struct_definitions)*
762 },
763 quote! {
764 type Bits = #bits_ty_ident;
765
766 #[inline]
767 #[allow(clippy::double_comparisons)]
768 fn is_valid_bit_pattern(bits: &Self::Bits) -> bool {
769 match bits.tag {
770 #(#variant_checks)*
771 _ => false,
772 }
773 }
774 },
775 ))
776 }
777 Repr::Transparent => {
778 if variants.len() != 1 {
779 bail!("enums with more than one variant cannot be transparent")
780 }
781
782 let variant = &variants[0];
783
784 let bits_ty = Ident::new(&format!("{}Bits", input.ident), input.span());
785 let fields = variant.fields.iter().map(|v| &v.ty);
786
787 Ok((
788 quote! {
789 #[doc = #GENERATED_TYPE_DOCUMENTATION]
790 #[derive(::core::clone::Clone, ::core::marker::Copy, #crate_name::CheckedBitPattern)]
791 #[repr(C)]
792 #vis struct #bits_ty(#(#fields),*);
793 },
794 quote! {
795 type Bits = <#bits_ty as #crate_name::CheckedBitPattern>::Bits;
796
797 #[inline]
798 #[allow(clippy::double_comparisons)]
799 fn is_valid_bit_pattern(bits: &Self::Bits) -> bool {
800 <#bits_ty as #crate_name::CheckedBitPattern>::is_valid_bit_pattern(bits)
801 }
802 },
803 ))
804 }
805 Repr::Integer(integer) => {
806 let bits_repr = Representation { repr: Repr::C, ..representation };
807 let input_ident = &input.ident;
808
809 // the enum manually re-configured as the union it represents. such a
810 // union is the union of variants as a repr(c) struct with the
811 // discriminator type inserted at the beginning. in our case we
812 // union the `Bits` representation of each variant rather than the variant
813 // itself, which we generate via a nested `CheckedBitPattern` derive
814 // on the `variant_struct_definitions` generated below.
815 //
816 // see: https://doc.rust-lang.org/reference/type-layout.html#primitive-representation-of-enums-with-fields
817 let bits_ty_ident =
818 Ident::new(&format!("{input_ident}Bits"), input.span());
819
820 let variant_struct_idents = variants.iter().map(|v| {
821 Ident::new(&format!("{input_ident}Variant{}", v.ident), v.span())
822 });
823
824 let variant_struct_definitions =
825 variant_struct_idents.clone().zip(variants.iter()).map(|(variant_struct_ident, v)| {
826 let fields = v.fields.iter().map(|v| &v.ty);
827
828 // adding the discriminant repr integer as first field, as described above
829 quote! {
830 #[derive(::core::clone::Clone, ::core::marker::Copy, #crate_name::CheckedBitPattern)]
831 #[repr(C)]
832 #vis struct #variant_struct_ident(#integer, #(#fields),*);
833 }
834 });
835
836 let union_fields = variant_struct_idents
837 .clone()
838 .zip(variants.iter())
839 .map(|(variant_struct_ident, v)| {
840 let variant_struct_bits_ident =
841 Ident::new(&format!("{variant_struct_ident}Bits"), input.span());
842 let field_ident = &v.ident;
843 quote! {
844 #field_ident: #variant_struct_bits_ident
845 }
846 });
847
848 let variant_checks = variant_struct_idents
849 .clone()
850 .zip(VariantDiscriminantIterator::new(variants.iter()))
851 .zip(variants.iter())
852 .map(|((variant_struct_ident, discriminant), v)| -> Result<_> {
853 let discriminant = discriminant?;
854 let discriminant = LitInt::new(&discriminant.to_string(), v.span());
855 let ident = &v.ident;
856 Ok(quote! {
857 #discriminant => {
858 let payload = unsafe { &bits.#ident };
859 <#variant_struct_ident as #crate_name::CheckedBitPattern>::is_valid_bit_pattern(payload)
860 }
861 })
862 })
863 .collect::<Result<Vec<_>>>()?;
864
865 Ok((
866 quote! {
867 #[doc = #GENERATED_TYPE_DOCUMENTATION]
868 #[derive(::core::clone::Clone, ::core::marker::Copy, #crate_name::AnyBitPattern)]
869 #bits_repr
870 #[allow(non_snake_case)]
871 #vis union #bits_ty_ident {
872 __tag: #integer,
873 #(#union_fields,)*
874 }
875
876 #[cfg(not(target_arch = "spirv"))]
877 impl ::core::fmt::Debug for #bits_ty_ident {
878 fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
879 let mut debug_struct = ::core::fmt::Formatter::debug_struct(f, ::core::stringify!(#bits_ty_ident));
880 ::core::fmt::DebugStruct::field(&mut debug_struct, "tag", unsafe { &self.__tag });
881 ::core::fmt::DebugStruct::finish_non_exhaustive(&mut debug_struct)
882 }
883 }
884
885 #(#variant_struct_definitions)*
886 },
887 quote! {
888 type Bits = #bits_ty_ident;
889
890 #[inline]
891 #[allow(clippy::double_comparisons)]
892 fn is_valid_bit_pattern(bits: &Self::Bits) -> bool {
893 match unsafe { bits.__tag } {
894 #(#variant_checks)*
895 _ => false,
896 }
897 }
898 },
899 ))
900 }
901 }
902 }
903
904 /// Check that a struct has no padding by asserting that the size of the struct
905 /// is equal to the sum of the size of it's fields
generate_assert_no_padding(input: &DeriveInput) -> Result<TokenStream>906 fn generate_assert_no_padding(input: &DeriveInput) -> Result<TokenStream> {
907 let struct_type = &input.ident;
908 let span = input.ident.span();
909 let fields = get_fields(input)?;
910
911 let mut field_types = get_field_types(&fields);
912 let size_sum = if let Some(first) = field_types.next() {
913 let size_first = quote_spanned!(span => ::core::mem::size_of::<#first>());
914 let size_rest =
915 quote_spanned!(span => #( + ::core::mem::size_of::<#field_types>() )*);
916
917 quote_spanned!(span => #size_first #size_rest)
918 } else {
919 quote_spanned!(span => 0)
920 };
921
922 Ok(quote_spanned! {span => const _: fn() = || {
923 #[doc(hidden)]
924 struct TypeWithoutPadding([u8; #size_sum]);
925 let _ = ::core::mem::transmute::<#struct_type, TypeWithoutPadding>;
926 };})
927 }
928
929 /// Check that all fields implement a given trait
generate_fields_are_trait( input: &DeriveInput, trait_: syn::Path, ) -> Result<TokenStream>930 fn generate_fields_are_trait(
931 input: &DeriveInput, trait_: syn::Path,
932 ) -> Result<TokenStream> {
933 let (impl_generics, _ty_generics, where_clause) =
934 input.generics.split_for_impl();
935 let fields = get_fields(input)?;
936 let span = input.span();
937 let field_types = get_field_types(&fields);
938 Ok(quote_spanned! {span => #(const _: fn() = || {
939 #[allow(clippy::missing_const_for_fn)]
940 #[doc(hidden)]
941 fn check #impl_generics () #where_clause {
942 fn assert_impl<T: #trait_>() {}
943 assert_impl::<#field_types>();
944 }
945 };)*
946 })
947 }
948
get_ident_from_stream(tokens: TokenStream) -> Option<Ident>949 fn get_ident_from_stream(tokens: TokenStream) -> Option<Ident> {
950 match tokens.into_iter().next() {
951 Some(TokenTree::Group(group)) => get_ident_from_stream(group.stream()),
952 Some(TokenTree::Ident(ident)) => Some(ident),
953 _ => None,
954 }
955 }
956
957 /// get a simple #[foo(bar)] attribute, returning "bar"
get_simple_attr(attributes: &[Attribute], attr_name: &str) -> Option<Ident>958 fn get_simple_attr(attributes: &[Attribute], attr_name: &str) -> Option<Ident> {
959 for attr in attributes {
960 if let (AttrStyle::Outer, Meta::List(list)) = (&attr.style, &attr.meta) {
961 if list.path.is_ident(attr_name) {
962 if let Some(ident) = get_ident_from_stream(list.tokens.clone()) {
963 return Some(ident);
964 }
965 }
966 }
967 }
968
969 None
970 }
971
get_repr(attributes: &[Attribute]) -> Result<Representation>972 fn get_repr(attributes: &[Attribute]) -> Result<Representation> {
973 attributes
974 .iter()
975 .filter_map(|attr| {
976 if attr.path().is_ident("repr") {
977 Some(attr.parse_args::<Representation>())
978 } else {
979 None
980 }
981 })
982 .try_fold(Representation::default(), |a, b| {
983 let b = b?;
984 Ok(Representation {
985 repr: match (a.repr, b.repr) {
986 (a, Repr::Rust) => a,
987 (Repr::Rust, b) => b,
988 _ => bail!("conflicting representation hints"),
989 },
990 packed: match (a.packed, b.packed) {
991 (a, None) => a,
992 (None, b) => b,
993 _ => bail!("conflicting representation hints"),
994 },
995 align: match (a.align, b.align) {
996 (Some(a), Some(b)) => Some(cmp::max(a, b)),
997 (a, None) => a,
998 (None, b) => b,
999 },
1000 })
1001 })
1002 }
1003
1004 mk_repr! {
1005 U8 => u8,
1006 I8 => i8,
1007 U16 => u16,
1008 I16 => i16,
1009 U32 => u32,
1010 I32 => i32,
1011 U64 => u64,
1012 I64 => i64,
1013 I128 => i128,
1014 U128 => u128,
1015 Usize => usize,
1016 Isize => isize,
1017 }
1018 // where
1019 macro_rules! mk_repr {(
1020 $(
1021 $Xn:ident => $xn:ident
1022 ),* $(,)?
1023 ) => (
1024 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
1025 enum IntegerRepr {
1026 $($Xn),*
1027 }
1028
1029 impl<'a> TryFrom<&'a str> for IntegerRepr {
1030 type Error = &'a str;
1031
1032 fn try_from(value: &'a str) -> std::result::Result<Self, &'a str> {
1033 match value {
1034 $(
1035 stringify!($xn) => Ok(Self::$Xn),
1036 )*
1037 _ => Err(value),
1038 }
1039 }
1040 }
1041
1042 impl ToTokens for IntegerRepr {
1043 fn to_tokens(&self, tokens: &mut TokenStream) {
1044 match self {
1045 $(
1046 Self::$Xn => tokens.extend(quote!($xn)),
1047 )*
1048 }
1049 }
1050 }
1051 )}
1052 use mk_repr;
1053
1054 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
1055 enum Repr {
1056 Rust,
1057 C,
1058 Transparent,
1059 Integer(IntegerRepr),
1060 CWithDiscriminant(IntegerRepr),
1061 }
1062
1063 impl Repr {
is_integer(&self) -> bool1064 fn is_integer(&self) -> bool {
1065 matches!(self, Self::Integer(..))
1066 }
1067
as_integer(&self) -> Option<IntegerRepr>1068 fn as_integer(&self) -> Option<IntegerRepr> {
1069 if let Self::Integer(v) = self {
1070 Some(*v)
1071 } else {
1072 None
1073 }
1074 }
1075 }
1076
1077 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
1078 struct Representation {
1079 packed: Option<u32>,
1080 align: Option<u32>,
1081 repr: Repr,
1082 }
1083
1084 impl Default for Representation {
default() -> Self1085 fn default() -> Self {
1086 Self { packed: None, align: None, repr: Repr::Rust }
1087 }
1088 }
1089
1090 impl Parse for Representation {
parse(input: ParseStream<'_>) -> Result<Representation>1091 fn parse(input: ParseStream<'_>) -> Result<Representation> {
1092 let mut ret = Representation::default();
1093 while !input.is_empty() {
1094 let keyword = input.parse::<Ident>()?;
1095 // preëmptively call `.to_string()` *once* (rather than on `is_ident()`)
1096 let keyword_str = keyword.to_string();
1097 let new_repr = match keyword_str.as_str() {
1098 "C" => Repr::C,
1099 "transparent" => Repr::Transparent,
1100 "packed" => {
1101 ret.packed = Some(if input.peek(token::Paren) {
1102 let contents;
1103 parenthesized!(contents in input);
1104 LitInt::base10_parse::<u32>(&contents.parse()?)?
1105 } else {
1106 1
1107 });
1108 let _: Option<Token![,]> = input.parse()?;
1109 continue;
1110 }
1111 "align" => {
1112 let contents;
1113 parenthesized!(contents in input);
1114 let new_align = LitInt::base10_parse::<u32>(&contents.parse()?)?;
1115 ret.align = Some(
1116 ret
1117 .align
1118 .map_or(new_align, |old_align| cmp::max(old_align, new_align)),
1119 );
1120 let _: Option<Token![,]> = input.parse()?;
1121 continue;
1122 }
1123 ident => {
1124 let primitive = IntegerRepr::try_from(ident)
1125 .map_err(|_| input.error("unrecognized representation hint"))?;
1126 Repr::Integer(primitive)
1127 }
1128 };
1129 ret.repr = match (ret.repr, new_repr) {
1130 (Repr::Rust, new_repr) => {
1131 // This is the first explicit repr.
1132 new_repr
1133 }
1134 (Repr::C, Repr::Integer(integer))
1135 | (Repr::Integer(integer), Repr::C) => {
1136 // Both the C repr and an integer repr have been specified
1137 // -> merge into a C wit discriminant.
1138 Repr::CWithDiscriminant(integer)
1139 }
1140 (_, _) => {
1141 return Err(input.error("duplicate representation hint"));
1142 }
1143 };
1144 let _: Option<Token![,]> = input.parse()?;
1145 }
1146 Ok(ret)
1147 }
1148 }
1149
1150 impl ToTokens for Representation {
to_tokens(&self, tokens: &mut TokenStream)1151 fn to_tokens(&self, tokens: &mut TokenStream) {
1152 let mut meta = Punctuated::<_, Token![,]>::new();
1153
1154 match self.repr {
1155 Repr::Rust => {}
1156 Repr::C => meta.push(quote!(C)),
1157 Repr::Transparent => meta.push(quote!(transparent)),
1158 Repr::Integer(primitive) => meta.push(quote!(#primitive)),
1159 Repr::CWithDiscriminant(primitive) => {
1160 meta.push(quote!(C));
1161 meta.push(quote!(#primitive));
1162 }
1163 }
1164
1165 if let Some(packed) = self.packed.as_ref() {
1166 let lit = LitInt::new(&packed.to_string(), Span::call_site());
1167 meta.push(quote!(packed(#lit)));
1168 }
1169
1170 if let Some(align) = self.align.as_ref() {
1171 let lit = LitInt::new(&align.to_string(), Span::call_site());
1172 meta.push(quote!(align(#lit)));
1173 }
1174
1175 tokens.extend(quote!(
1176 #[repr(#meta)]
1177 ));
1178 }
1179 }
1180
enum_has_fields<'a>( mut variants: impl Iterator<Item = &'a Variant>, ) -> bool1181 fn enum_has_fields<'a>(
1182 mut variants: impl Iterator<Item = &'a Variant>,
1183 ) -> bool {
1184 variants.any(|v| matches!(v.fields, Fields::Named(_) | Fields::Unnamed(_)))
1185 }
1186
1187 struct VariantDiscriminantIterator<'a, I: Iterator<Item = &'a Variant> + 'a> {
1188 inner: I,
1189 last_value: i64,
1190 }
1191
1192 impl<'a, I: Iterator<Item = &'a Variant> + 'a>
1193 VariantDiscriminantIterator<'a, I>
1194 {
new(inner: I) -> Self1195 fn new(inner: I) -> Self {
1196 VariantDiscriminantIterator { inner, last_value: -1 }
1197 }
1198 }
1199
1200 impl<'a, I: Iterator<Item = &'a Variant> + 'a> Iterator
1201 for VariantDiscriminantIterator<'a, I>
1202 {
1203 type Item = Result<i64>;
1204
next(&mut self) -> Option<Self::Item>1205 fn next(&mut self) -> Option<Self::Item> {
1206 let variant = self.inner.next()?;
1207
1208 if let Some((_, discriminant)) = &variant.discriminant {
1209 let discriminant_value = match parse_int_expr(discriminant) {
1210 Ok(value) => value,
1211 Err(e) => return Some(Err(e)),
1212 };
1213 self.last_value = discriminant_value;
1214 } else {
1215 self.last_value += 1;
1216 }
1217
1218 Some(Ok(self.last_value))
1219 }
1220 }
1221
parse_int_expr(expr: &Expr) -> Result<i64>1222 fn parse_int_expr(expr: &Expr) -> Result<i64> {
1223 match expr {
1224 Expr::Unary(ExprUnary { op: UnOp::Neg(_), expr, .. }) => {
1225 parse_int_expr(expr).map(|int| -int)
1226 }
1227 Expr::Lit(ExprLit { lit: Lit::Int(int), .. }) => int.base10_parse(),
1228 Expr::Lit(ExprLit { lit: Lit::Byte(byte), .. }) => Ok(byte.value().into()),
1229 _ => bail!("Not an integer expression"),
1230 }
1231 }
1232
1233 #[cfg(test)]
1234 mod tests {
1235 use syn::parse_quote;
1236
1237 use super::{get_repr, IntegerRepr, Repr, Representation};
1238
1239 #[test]
parse_basic_repr()1240 fn parse_basic_repr() {
1241 let attr = parse_quote!(#[repr(C)]);
1242 let repr = get_repr(&[attr]).unwrap();
1243 assert_eq!(repr, Representation { repr: Repr::C, ..Default::default() });
1244
1245 let attr = parse_quote!(#[repr(transparent)]);
1246 let repr = get_repr(&[attr]).unwrap();
1247 assert_eq!(
1248 repr,
1249 Representation { repr: Repr::Transparent, ..Default::default() }
1250 );
1251
1252 let attr = parse_quote!(#[repr(u8)]);
1253 let repr = get_repr(&[attr]).unwrap();
1254 assert_eq!(
1255 repr,
1256 Representation {
1257 repr: Repr::Integer(IntegerRepr::U8),
1258 ..Default::default()
1259 }
1260 );
1261
1262 let attr = parse_quote!(#[repr(packed)]);
1263 let repr = get_repr(&[attr]).unwrap();
1264 assert_eq!(repr, Representation { packed: Some(1), ..Default::default() });
1265
1266 let attr = parse_quote!(#[repr(packed(1))]);
1267 let repr = get_repr(&[attr]).unwrap();
1268 assert_eq!(repr, Representation { packed: Some(1), ..Default::default() });
1269
1270 let attr = parse_quote!(#[repr(packed(2))]);
1271 let repr = get_repr(&[attr]).unwrap();
1272 assert_eq!(repr, Representation { packed: Some(2), ..Default::default() });
1273
1274 let attr = parse_quote!(#[repr(align(2))]);
1275 let repr = get_repr(&[attr]).unwrap();
1276 assert_eq!(repr, Representation { align: Some(2), ..Default::default() });
1277 }
1278
1279 #[test]
parse_advanced_repr()1280 fn parse_advanced_repr() {
1281 let attr = parse_quote!(#[repr(align(4), align(2))]);
1282 let repr = get_repr(&[attr]).unwrap();
1283 assert_eq!(repr, Representation { align: Some(4), ..Default::default() });
1284
1285 let attr1 = parse_quote!(#[repr(align(1))]);
1286 let attr2 = parse_quote!(#[repr(align(4))]);
1287 let attr3 = parse_quote!(#[repr(align(2))]);
1288 let repr = get_repr(&[attr1, attr2, attr3]).unwrap();
1289 assert_eq!(repr, Representation { align: Some(4), ..Default::default() });
1290
1291 let attr = parse_quote!(#[repr(C, u8)]);
1292 let repr = get_repr(&[attr]).unwrap();
1293 assert_eq!(
1294 repr,
1295 Representation {
1296 repr: Repr::CWithDiscriminant(IntegerRepr::U8),
1297 ..Default::default()
1298 }
1299 );
1300
1301 let attr = parse_quote!(#[repr(u8, C)]);
1302 let repr = get_repr(&[attr]).unwrap();
1303 assert_eq!(
1304 repr,
1305 Representation {
1306 repr: Repr::CWithDiscriminant(IntegerRepr::U8),
1307 ..Default::default()
1308 }
1309 );
1310 }
1311 }
1312
bytemuck_crate_name(input: &DeriveInput) -> TokenStream1313 pub fn bytemuck_crate_name(input: &DeriveInput) -> TokenStream {
1314 const ATTR_NAME: &'static str = "crate";
1315
1316 let mut crate_name = quote!(::bytemuck);
1317 for attr in &input.attrs {
1318 if !attr.path().is_ident("bytemuck") {
1319 continue;
1320 }
1321
1322 attr.parse_nested_meta(|meta| {
1323 if meta.path.is_ident(ATTR_NAME) {
1324 let expr: syn::Expr = meta.value()?.parse()?;
1325 let mut value = &expr;
1326 while let syn::Expr::Group(e) = value {
1327 value = &e.expr;
1328 }
1329 if let syn::Expr::Lit(syn::ExprLit {
1330 lit: syn::Lit::Str(lit), ..
1331 }) = value
1332 {
1333 let suffix = lit.suffix();
1334 if !suffix.is_empty() {
1335 bail!(format!("Unexpected suffix `{}` on string literal", suffix))
1336 }
1337 let path: syn::Path = match lit.parse() {
1338 Ok(path) => path,
1339 Err(_) => {
1340 bail!(format!("Failed to parse path: {:?}", lit.value()))
1341 }
1342 };
1343 crate_name = path.into_token_stream();
1344 } else {
1345 bail!(
1346 "Expected bytemuck `crate` attribute to be a string: `crate = \"...\"`",
1347 )
1348 }
1349 }
1350 Ok(())
1351 }).unwrap();
1352 }
1353
1354 return crate_name;
1355 }
1356
1357 const GENERATED_TYPE_DOCUMENTATION: &str =
1358 " `bytemuck`-generated type for internal purposes only.";
1359