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