xref: /linux/rust/zerocopy-derive/repr.rs (revision 7c306f00835a237a70024a8f968dbc3bc8db8fcf)
1 // SPDX-License-Identifier: (BSD-2-Clause OR Apache-2.0) OR MIT
2 //
3 // Copyright 2019 The Fuchsia Authors
4 //
5 // Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0
6 // <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT
7 // license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option.
8 // This file may not be copied, modified, or distributed except according to
9 // those terms.
10 
11 use core::{
12     convert::{Infallible, TryFrom},
13     num::NonZeroU32,
14 };
15 
16 use proc_macro2::{Span, TokenStream};
17 use quote::{quote_spanned, ToTokens, TokenStreamExt as _};
18 use syn::{
19     punctuated::Punctuated, spanned::Spanned as _, token::Comma, Attribute, Error, LitInt, Meta,
20     MetaList,
21 };
22 
23 use crate::util::{path_is_ident, to_ident_str};
24 
25 /// The computed representation of a type.
26 ///
27 /// This is the result of processing all `#[repr(...)]` attributes on a type, if
28 /// any. A `Repr` is only capable of representing legal combinations of
29 /// `#[repr(...)]` attributes.
30 #[cfg_attr(test, derive(Copy, Clone, Debug))]
31 pub(crate) enum Repr<Prim, Packed> {
32     /// `#[repr(transparent)]`
33     Transparent(Span),
34     /// A compound representation: `repr(C)`, `repr(Rust)`, or `repr(Int)`
35     /// optionally combined with `repr(packed(...))` or `repr(align(...))`
36     Compound(Spanned<CompoundRepr<Prim>>, Option<Spanned<AlignRepr<Packed>>>),
37 }
38 
39 /// A compound representation: `repr(C)`, `repr(Rust)`, or `repr(Int)`.
40 #[cfg_attr(test, derive(Copy, Clone, Debug, Eq, PartialEq))]
41 pub(crate) enum CompoundRepr<Prim> {
42     C,
43     Rust,
44     Primitive(Prim),
45 }
46 
47 /// `repr(Int)`
48 #[derive(Copy, Clone)]
49 #[cfg_attr(test, derive(Debug, Eq, PartialEq))]
50 pub(crate) enum PrimitiveRepr {
51     U8,
52     U16,
53     U32,
54     U64,
55     U128,
56     Usize,
57     I8,
58     I16,
59     I32,
60     I64,
61     I128,
62     Isize,
63 }
64 
65 /// `repr(packed(...))` or `repr(align(...))`
66 #[cfg_attr(test, derive(Copy, Clone, Debug, Eq, PartialEq))]
67 pub(crate) enum AlignRepr<Packed> {
68     Packed(Packed),
69     Align(NonZeroU32),
70 }
71 
72 /// The representations which can legally appear on a struct or union type.
73 pub(crate) type StructUnionRepr = Repr<Infallible, NonZeroU32>;
74 
75 /// The representations which can legally appear on an enum type.
76 pub(crate) type EnumRepr = Repr<PrimitiveRepr, Infallible>;
77 
78 impl<Prim, Packed> Repr<Prim, Packed> {
79     /// Gets the name of this "repr type" - the non-align `repr(X)` that is used
80     /// in prose to refer to this type.
81     ///
82     /// For example, we would refer to `#[repr(C, align(4))] struct Foo { ... }`
83     /// as a "`repr(C)` struct".
84     pub(crate) fn repr_type_name(&self) -> &str
85     where
86         Prim: Copy + With<PrimitiveRepr>,
87     {
88         use CompoundRepr::*;
89         use PrimitiveRepr::*;
90         use Repr::*;
91         match self {
92             Transparent(_span) => "repr(transparent)",
93             Compound(Spanned { t: repr, span: _ }, _align) => match repr {
94                 C => "repr(C)",
95                 Rust => "repr(Rust)",
96                 Primitive(prim) => prim.with(|prim| match prim {
97                     U8 => "repr(u8)",
98                     U16 => "repr(u16)",
99                     U32 => "repr(u32)",
100                     U64 => "repr(u64)",
101                     U128 => "repr(u128)",
102                     Usize => "repr(usize)",
103                     I8 => "repr(i8)",
104                     I16 => "repr(i16)",
105                     I32 => "repr(i32)",
106                     I64 => "repr(i64)",
107                     I128 => "repr(i128)",
108                     Isize => "repr(isize)",
109                 }),
110             },
111         }
112     }
113 
114     pub(crate) fn is_transparent(&self) -> bool {
115         matches!(self, Repr::Transparent(_))
116     }
117 
118     pub(crate) fn is_c(&self) -> bool {
119         use CompoundRepr::*;
120         matches!(self, Repr::Compound(Spanned { t: C, span: _ }, _align))
121     }
122 
123     pub(crate) fn is_primitive(&self) -> bool {
124         use CompoundRepr::*;
125         matches!(self, Repr::Compound(Spanned { t: Primitive(_), span: _ }, _align))
126     }
127 
128     pub(crate) fn get_packed(&self) -> Option<&Packed> {
129         use AlignRepr::*;
130         use Repr::*;
131         if let Compound(_, Some(Spanned { t: Packed(p), span: _ })) = self {
132             Some(p)
133         } else {
134             None
135         }
136     }
137 
138     pub(crate) fn get_align(&self) -> Option<Spanned<NonZeroU32>> {
139         use AlignRepr::*;
140         use Repr::*;
141         if let Compound(_, Some(Spanned { t: Align(n), span })) = self {
142             Some(Spanned::new(*n, *span))
143         } else {
144             None
145         }
146     }
147 
148     pub(crate) fn is_align_gt_1(&self) -> bool {
149         self.get_align().map(|n| n.t.get() > 1).unwrap_or(false)
150     }
151 
152     /// When deriving `Unaligned`, validate that the decorated type has no
153     /// `#[repr(align(N))]` attribute where `N > 1`. If no such attribute exists
154     /// (including if `N == 1`), this returns `Ok(())`, and otherwise it returns
155     /// a descriptive error.
156     pub(crate) fn unaligned_validate_no_align_gt_1(&self) -> Result<(), Error> {
157         if let Some(n) = self.get_align().filter(|n| n.t.get() > 1) {
158             Err(Error::new(
159                 n.span,
160                 "cannot derive `Unaligned` on type with alignment greater than 1",
161             ))
162         } else {
163             Ok(())
164         }
165     }
166 }
167 
168 impl<Prim> Repr<Prim, NonZeroU32> {
169     /// Does `self` describe a `#[repr(packed)]` or `#[repr(packed(1))]` type?
170     pub(crate) fn is_packed_1(&self) -> bool {
171         self.get_packed().map(|n| n.get() == 1).unwrap_or(false)
172     }
173 }
174 
175 impl<Packed> Repr<PrimitiveRepr, Packed> {
176     fn get_primitive(&self) -> Option<&PrimitiveRepr> {
177         use CompoundRepr::*;
178         use Repr::*;
179         if let Compound(Spanned { t: Primitive(p), span: _ }, _align) = self {
180             Some(p)
181         } else {
182             None
183         }
184     }
185 
186     /// Does `self` describe a `#[repr(u8)]` type?
187     pub(crate) fn is_u8(&self) -> bool {
188         matches!(self.get_primitive(), Some(PrimitiveRepr::U8))
189     }
190 
191     /// Does `self` describe a `#[repr(i8)]` type?
192     pub(crate) fn is_i8(&self) -> bool {
193         matches!(self.get_primitive(), Some(PrimitiveRepr::I8))
194     }
195 }
196 
197 impl<Prim, Packed> ToTokens for Repr<Prim, Packed>
198 where
199     Prim: With<PrimitiveRepr> + Copy,
200     Packed: With<NonZeroU32> + Copy,
201 {
202     fn to_tokens(&self, ts: &mut TokenStream) {
203         use Repr::*;
204         match self {
205             Transparent(span) => ts.append_all(quote_spanned! { *span=> #[repr(transparent)] }),
206             Compound(repr, align) => {
207                 repr.to_tokens(ts);
208                 if let Some(align) = align {
209                     align.to_tokens(ts);
210                 }
211             }
212         }
213     }
214 }
215 
216 impl<Prim: With<PrimitiveRepr> + Copy> ToTokens for Spanned<CompoundRepr<Prim>> {
217     fn to_tokens(&self, ts: &mut TokenStream) {
218         use CompoundRepr::*;
219         match &self.t {
220             C => ts.append_all(quote_spanned! { self.span=> #[repr(C)] }),
221             Rust => ts.append_all(quote_spanned! { self.span=> #[repr(Rust)] }),
222             Primitive(prim) => prim.with(|prim| Spanned::new(prim, self.span).to_tokens(ts)),
223         }
224     }
225 }
226 
227 impl ToTokens for Spanned<PrimitiveRepr> {
228     fn to_tokens(&self, ts: &mut TokenStream) {
229         use PrimitiveRepr::*;
230         match self.t {
231             U8 => ts.append_all(quote_spanned! { self.span => #[repr(u8)] }),
232             U16 => ts.append_all(quote_spanned! { self.span => #[repr(u16)] }),
233             U32 => ts.append_all(quote_spanned! { self.span => #[repr(u32)] }),
234             U64 => ts.append_all(quote_spanned! { self.span => #[repr(u64)] }),
235             U128 => ts.append_all(quote_spanned! { self.span => #[repr(u128)] }),
236             Usize => ts.append_all(quote_spanned! { self.span => #[repr(usize)] }),
237             I8 => ts.append_all(quote_spanned! { self.span => #[repr(i8)] }),
238             I16 => ts.append_all(quote_spanned! { self.span => #[repr(i16)] }),
239             I32 => ts.append_all(quote_spanned! { self.span => #[repr(i32)] }),
240             I64 => ts.append_all(quote_spanned! { self.span => #[repr(i64)] }),
241             I128 => ts.append_all(quote_spanned! { self.span => #[repr(i128)] }),
242             Isize => ts.append_all(quote_spanned! { self.span => #[repr(isize)] }),
243         }
244     }
245 }
246 
247 impl<Packed: With<NonZeroU32> + Copy> ToTokens for Spanned<AlignRepr<Packed>> {
248     fn to_tokens(&self, ts: &mut TokenStream) {
249         use AlignRepr::*;
250         // We use `syn::Index` instead of `u32` because `quote_spanned!`
251         // serializes `u32` literals as `123u32`, not just `123`. Rust doesn't
252         // recognize that as a valid argument to `#[repr(align(...))]` or
253         // `#[repr(packed(...))]`.
254         let to_index = |n: NonZeroU32| syn::Index { index: n.get(), span: self.span };
255         match self.t {
256             Packed(n) => n.with(|n| {
257                 let n = to_index(n);
258                 ts.append_all(quote_spanned! { self.span => #[repr(packed(#n))] })
259             }),
260             Align(n) => {
261                 let n = to_index(n);
262                 ts.append_all(quote_spanned! { self.span => #[repr(align(#n))] })
263             }
264         }
265     }
266 }
267 
268 /// The result of parsing a single `#[repr(...)]` attribute or a single
269 /// directive inside a compound `#[repr(..., ...)]` attribute.
270 #[derive(Copy, Clone, PartialEq, Eq)]
271 #[cfg_attr(test, derive(Debug))]
272 pub(crate) enum RawRepr {
273     Transparent,
274     C,
275     Rust,
276     U8,
277     U16,
278     U32,
279     U64,
280     U128,
281     Usize,
282     I8,
283     I16,
284     I32,
285     I64,
286     I128,
287     Isize,
288     Align(NonZeroU32),
289     PackedN(NonZeroU32),
290     Packed,
291 }
292 
293 /// The error from converting from a `RawRepr`.
294 #[cfg_attr(test, derive(Debug, Eq, PartialEq))]
295 pub(crate) enum FromRawReprError<E> {
296     /// The `RawRepr` doesn't affect the high-level repr we're parsing (e.g.
297     /// it's `align(...)` and we're parsing a `CompoundRepr`).
298     None,
299     /// The `RawRepr` is invalid for the high-level repr we're parsing (e.g.
300     /// it's `packed` repr and we're parsing an `AlignRepr` for an enum type).
301     Err(E),
302 }
303 
304 /// The representation hint is not supported for the decorated type.
305 #[cfg_attr(test, derive(Copy, Clone, Debug, Eq, PartialEq))]
306 pub(crate) struct UnsupportedReprError;
307 
308 impl<Prim: With<PrimitiveRepr>> TryFrom<RawRepr> for CompoundRepr<Prim> {
309     type Error = FromRawReprError<UnsupportedReprError>;
310     fn try_from(
311         raw: RawRepr,
312     ) -> Result<CompoundRepr<Prim>, FromRawReprError<UnsupportedReprError>> {
313         use RawRepr::*;
314         match raw {
315             C => Ok(CompoundRepr::C),
316             Rust => Ok(CompoundRepr::Rust),
317             raw @ (U8 | U16 | U32 | U64 | U128 | Usize | I8 | I16 | I32 | I64 | I128 | Isize) => {
318                 Prim::try_with_or(
319                     || match raw {
320                         U8 => Ok(PrimitiveRepr::U8),
321                         U16 => Ok(PrimitiveRepr::U16),
322                         U32 => Ok(PrimitiveRepr::U32),
323                         U64 => Ok(PrimitiveRepr::U64),
324                         U128 => Ok(PrimitiveRepr::U128),
325                         Usize => Ok(PrimitiveRepr::Usize),
326                         I8 => Ok(PrimitiveRepr::I8),
327                         I16 => Ok(PrimitiveRepr::I16),
328                         I32 => Ok(PrimitiveRepr::I32),
329                         I64 => Ok(PrimitiveRepr::I64),
330                         I128 => Ok(PrimitiveRepr::I128),
331                         Isize => Ok(PrimitiveRepr::Isize),
332                         Transparent | C | Rust | Align(_) | PackedN(_) | Packed => {
333                             Err(UnsupportedReprError)
334                         }
335                     },
336                     UnsupportedReprError,
337                 )
338                 .map(CompoundRepr::Primitive)
339                 .map_err(FromRawReprError::Err)
340             }
341             Transparent | Align(_) | PackedN(_) | Packed => Err(FromRawReprError::None),
342         }
343     }
344 }
345 
346 impl<Pcked: With<NonZeroU32>> TryFrom<RawRepr> for AlignRepr<Pcked> {
347     type Error = FromRawReprError<UnsupportedReprError>;
348     fn try_from(raw: RawRepr) -> Result<AlignRepr<Pcked>, FromRawReprError<UnsupportedReprError>> {
349         use RawRepr::*;
350         match raw {
351             Packed | PackedN(_) => Pcked::try_with_or(
352                 || match raw {
353                     Packed => Ok(NonZeroU32::new(1).unwrap()),
354                     PackedN(n) => Ok(n),
355                     U8 | U16 | U32 | U64 | U128 | Usize | I8 | I16 | I32 | I64 | I128 | Isize
356                     | Transparent | C | Rust | Align(_) => Err(UnsupportedReprError),
357                 },
358                 UnsupportedReprError,
359             )
360             .map(AlignRepr::Packed)
361             .map_err(FromRawReprError::Err),
362             Align(n) => Ok(AlignRepr::Align(n)),
363             U8 | U16 | U32 | U64 | U128 | Usize | I8 | I16 | I32 | I64 | I128 | Isize
364             | Transparent | C | Rust => Err(FromRawReprError::None),
365         }
366     }
367 }
368 
369 /// The error from extracting a high-level repr type from a list of `RawRepr`s.
370 #[cfg_attr(test, derive(Copy, Clone, Debug, Eq, PartialEq))]
371 enum FromRawReprsError<E> {
372     /// One of the `RawRepr`s is invalid for the high-level repr we're parsing
373     /// (e.g. there's a `packed` repr and we're parsing an `AlignRepr` for an
374     /// enum type).
375     Single(E),
376     /// Two `RawRepr`s appear which both affect the high-level repr we're
377     /// parsing (e.g., the list is `#[repr(align(2), packed)]`). Note that we
378     /// conservatively treat redundant reprs as conflicting (e.g.
379     /// `#[repr(packed, packed)]`).
380     Conflict,
381 }
382 
383 /// Tries to extract a high-level repr from a list of `RawRepr`s.
384 fn try_from_raw_reprs<'a, E, R: TryFrom<RawRepr, Error = FromRawReprError<E>>>(
385     r: impl IntoIterator<Item = &'a Spanned<RawRepr>>,
386 ) -> Result<Option<Spanned<R>>, Spanned<FromRawReprsError<E>>> {
387     // Walk the list of `RawRepr`s and attempt to convert each to an `R`. Bail
388     // if we find any errors. If we find more than one which converts to an `R`,
389     // bail with a `Conflict` error.
390     r.into_iter().try_fold(None, |found: Option<Spanned<R>>, raw| {
391         let new = match Spanned::<R>::try_from(*raw) {
392             Ok(r) => r,
393             // This `RawRepr` doesn't convert to an `R`, so keep the current
394             // found `R`, if any.
395             Err(FromRawReprError::None) => return Ok(found),
396             // This repr is unsupported for the decorated type (e.g.
397             // `repr(packed)` on an enum).
398             Err(FromRawReprError::Err(Spanned { t: err, span })) => {
399                 return Err(Spanned::new(FromRawReprsError::Single(err), span))
400             }
401         };
402 
403         if let Some(found) = found {
404             // We already found an `R`, but this `RawRepr` also converts to an
405             // `R`, so that's a conflict.
406             //
407             // `Span::join` returns `None` if the two spans are from different
408             // files or if we're not on the nightly compiler. In that case, just
409             // use `new`'s span.
410             let span = found.span.join(new.span).unwrap_or(new.span);
411             Err(Spanned::new(FromRawReprsError::Conflict, span))
412         } else {
413             Ok(Some(new))
414         }
415     })
416 }
417 
418 /// The error returned from [`Repr::from_attrs`].
419 #[cfg_attr(test, derive(Copy, Clone, Debug, Eq, PartialEq))]
420 enum FromAttrsError {
421     FromRawReprs(FromRawReprsError<UnsupportedReprError>),
422     Unrecognized,
423 }
424 
425 impl From<FromRawReprsError<UnsupportedReprError>> for FromAttrsError {
426     fn from(err: FromRawReprsError<UnsupportedReprError>) -> FromAttrsError {
427         FromAttrsError::FromRawReprs(err)
428     }
429 }
430 
431 impl From<UnrecognizedReprError> for FromAttrsError {
432     fn from(_err: UnrecognizedReprError) -> FromAttrsError {
433         FromAttrsError::Unrecognized
434     }
435 }
436 
437 impl From<Spanned<FromAttrsError>> for Error {
438     fn from(err: Spanned<FromAttrsError>) -> Error {
439         let Spanned { t: err, span } = err;
440         match err {
441             FromAttrsError::FromRawReprs(FromRawReprsError::Single(
442                 _err @ UnsupportedReprError,
443             )) => Error::new(span, "unsupported representation hint for the decorated type"),
444             FromAttrsError::FromRawReprs(FromRawReprsError::Conflict) => {
445                 // NOTE: This says "another" rather than "a preceding" because
446                 // when one of the reprs involved is `transparent`, we detect
447                 // that condition in `Repr::from_attrs`, and at that point we
448                 // can't tell which repr came first, so we might report this on
449                 // the first involved repr rather than the second, third, etc.
450                 Error::new(span, "this conflicts with another representation hint")
451             }
452             FromAttrsError::Unrecognized => Error::new(span, "unrecognized representation hint"),
453         }
454     }
455 }
456 
457 impl<Prim, Packed> Repr<Prim, Packed> {
458     fn from_attrs_inner(attrs: &[Attribute]) -> Result<Repr<Prim, Packed>, Spanned<FromAttrsError>>
459     where
460         Prim: With<PrimitiveRepr>,
461         Packed: With<NonZeroU32>,
462     {
463         let raw_reprs = RawRepr::from_attrs(attrs).map_err(Spanned::from)?;
464 
465         let transparent = {
466             let mut transparents = raw_reprs.iter().filter_map(|Spanned { t, span }| match t {
467                 RawRepr::Transparent => Some(span),
468                 _ => None,
469             });
470             let first = transparents.next();
471             let second = transparents.next();
472             match (first, second) {
473                 (None, None) => None,
474                 (Some(span), None) => Some(*span),
475                 (Some(_), Some(second)) => {
476                     return Err(Spanned::new(
477                         FromAttrsError::FromRawReprs(FromRawReprsError::Conflict),
478                         *second,
479                     ))
480                 }
481                 // An iterator can't produce a value only on the second call to
482                 // `.next()`.
483                 (None, Some(_)) => unreachable!(),
484             }
485         };
486 
487         let compound: Option<Spanned<CompoundRepr<Prim>>> =
488             try_from_raw_reprs(raw_reprs.iter()).map_err(Spanned::from)?;
489         let align: Option<Spanned<AlignRepr<Packed>>> =
490             try_from_raw_reprs(raw_reprs.iter()).map_err(Spanned::from)?;
491 
492         if let Some(span) = transparent {
493             if compound.is_some() || align.is_some() {
494                 // Arbitrarily report the problem on the `transparent` span. Any
495                 // span will do.
496                 return Err(Spanned::new(FromRawReprsError::Conflict.into(), span));
497             }
498 
499             Ok(Repr::Transparent(span))
500         } else {
501             Ok(Repr::Compound(
502                 compound.unwrap_or(Spanned::new(CompoundRepr::Rust, Span::call_site())),
503                 align,
504             ))
505         }
506     }
507 }
508 
509 impl<Prim, Packed> Repr<Prim, Packed> {
510     pub(crate) fn from_attrs(attrs: &[Attribute]) -> Result<Repr<Prim, Packed>, Error>
511     where
512         Prim: With<PrimitiveRepr>,
513         Packed: With<NonZeroU32>,
514     {
515         Repr::from_attrs_inner(attrs).map_err(Into::into)
516     }
517 }
518 
519 /// The representation hint could not be parsed or was unrecognized.
520 struct UnrecognizedReprError;
521 
522 impl RawRepr {
523     fn from_attrs(
524         attrs: &[Attribute],
525     ) -> Result<Vec<Spanned<RawRepr>>, Spanned<UnrecognizedReprError>> {
526         let mut reprs = Vec::new();
527         for attr in attrs {
528             // Ignore documentation attributes.
529             if path_is_ident(attr.path(), "doc") {
530                 continue;
531             }
532             if let Meta::List(ref meta_list) = attr.meta {
533                 if path_is_ident(&meta_list.path, "repr") {
534                     let parsed: Punctuated<Meta, Comma> =
535                         match meta_list.parse_args_with(Punctuated::parse_terminated) {
536                             Ok(parsed) => parsed,
537                             Err(_) => {
538                                 return Err(Spanned::new(
539                                     UnrecognizedReprError,
540                                     meta_list.tokens.span(),
541                                 ))
542                             }
543                         };
544                     for meta in parsed {
545                         let s = meta.span();
546                         reprs.push(
547                             RawRepr::from_meta(&meta)
548                                 .map(|r| Spanned::new(r, s))
549                                 .map_err(|e| Spanned::new(e, s))?,
550                         );
551                     }
552                 }
553             }
554         }
555 
556         Ok(reprs)
557     }
558 
559     fn from_meta(meta: &Meta) -> Result<RawRepr, UnrecognizedReprError> {
560         let (path, list) = match meta {
561             Meta::Path(path) => (path, None),
562             Meta::List(list) => (&list.path, Some(list)),
563             _ => return Err(UnrecognizedReprError),
564         };
565 
566         let ident = path.get_ident().ok_or(UnrecognizedReprError)?;
567 
568         // Only returns `Ok` for non-zero power-of-two values.
569         // `NonZeroU32::is_power_of_two` is unsupported on our MSRV.
570         #[allow(clippy::needless_nonzero_get)]
571         let parse_nzu64 = |list: &MetaList| {
572             list.parse_args::<LitInt>()
573                 .and_then(|int| int.base10_parse::<NonZeroU32>())
574                 .map_err(|_| UnrecognizedReprError)
575                 .and_then(|nz| {
576                     if nz.get().is_power_of_two() {
577                         Ok(nz)
578                     } else {
579                         Err(UnrecognizedReprError)
580                     }
581                 })
582         };
583 
584         use RawRepr::*;
585         let ident = to_ident_str(ident);
586         Ok(match (ident.as_str(), list) {
587             ("u8", None) => U8,
588             ("u16", None) => U16,
589             ("u32", None) => U32,
590             ("u64", None) => U64,
591             ("u128", None) => U128,
592             ("usize", None) => Usize,
593             ("i8", None) => I8,
594             ("i16", None) => I16,
595             ("i32", None) => I32,
596             ("i64", None) => I64,
597             ("i128", None) => I128,
598             ("isize", None) => Isize,
599             ("C", None) => C,
600             ("transparent", None) => Transparent,
601             ("Rust", None) => Rust,
602             ("packed", None) => Packed,
603             ("packed", Some(list)) => PackedN(parse_nzu64(list)?),
604             ("align", Some(list)) => Align(parse_nzu64(list)?),
605             _ => return Err(UnrecognizedReprError),
606         })
607     }
608 }
609 
610 pub(crate) use util::*;
611 mod util {
612     use super::*;
613     /// A value with an associated span.
614     #[derive(Copy, Clone)]
615     #[cfg_attr(test, derive(Debug))]
616     pub(crate) struct Spanned<T> {
617         pub(crate) t: T,
618         pub(crate) span: Span,
619     }
620 
621     impl<T> Spanned<T> {
622         pub(super) fn new(t: T, span: Span) -> Spanned<T> {
623             Spanned { t, span }
624         }
625 
626         pub(super) fn from<U>(s: Spanned<U>) -> Spanned<T>
627         where
628             T: From<U>,
629         {
630             let Spanned { t: u, span } = s;
631             Spanned::new(u.into(), span)
632         }
633 
634         /// Delegates to `T: TryFrom`, preserving span information in both the
635         /// success and error cases.
636         pub(super) fn try_from<E, U>(
637             u: Spanned<U>,
638         ) -> Result<Spanned<T>, FromRawReprError<Spanned<E>>>
639         where
640             T: TryFrom<U, Error = FromRawReprError<E>>,
641         {
642             let Spanned { t: u, span } = u;
643             T::try_from(u).map(|t| Spanned { t, span }).map_err(|err| match err {
644                 FromRawReprError::None => FromRawReprError::None,
645                 FromRawReprError::Err(e) => FromRawReprError::Err(Spanned::new(e, span)),
646             })
647         }
648     }
649 
650     // Used to permit implementing `With<T> for T: Inhabited` and for
651     // `Infallible` without a blanket impl conflict.
652     pub(crate) trait Inhabited {}
653     impl Inhabited for PrimitiveRepr {}
654     impl Inhabited for NonZeroU32 {}
655 
656     pub(crate) trait With<T> {
657         fn with<O, F: FnOnce(T) -> O>(self, f: F) -> O;
658         fn try_with_or<E, F: FnOnce() -> Result<T, E>>(f: F, err: E) -> Result<Self, E>
659         where
660             Self: Sized;
661     }
662 
663     impl<T: Inhabited> With<T> for T {
664         fn with<O, F: FnOnce(T) -> O>(self, f: F) -> O {
665             f(self)
666         }
667 
668         fn try_with_or<E, F: FnOnce() -> Result<T, E>>(f: F, _err: E) -> Result<Self, E> {
669             f()
670         }
671     }
672 
673     impl<T> With<T> for Infallible {
674         fn with<O, F: FnOnce(T) -> O>(self, _f: F) -> O {
675             match self {}
676         }
677 
678         fn try_with_or<E, F: FnOnce() -> Result<T, E>>(_f: F, err: E) -> Result<Self, E> {
679             Err(err)
680         }
681     }
682 }
683 
684 #[cfg(test)]
685 mod tests {
686     use syn::parse_quote;
687 
688     use super::*;
689 
690     impl<T> From<T> for Spanned<T> {
691         fn from(t: T) -> Spanned<T> {
692             Spanned::new(t, Span::call_site())
693         }
694     }
695 
696     // We ignore spans for equality in testing since real spans are hard to
697     // synthesize and don't implement `PartialEq`.
698     impl<T: PartialEq> PartialEq for Spanned<T> {
699         fn eq(&self, other: &Spanned<T>) -> bool {
700             self.t.eq(&other.t)
701         }
702     }
703 
704     impl<T: Eq> Eq for Spanned<T> {}
705 
706     impl<Prim: PartialEq, Packed: PartialEq> PartialEq for Repr<Prim, Packed> {
707         fn eq(&self, other: &Repr<Prim, Packed>) -> bool {
708             match (self, other) {
709                 (Repr::Transparent(_), Repr::Transparent(_)) => true,
710                 (Repr::Compound(sc, sa), Repr::Compound(oc, oa)) => (sc, sa) == (oc, oa),
711                 _ => false,
712             }
713         }
714     }
715 
716     fn s() -> Span {
717         Span::call_site()
718     }
719 
720     #[test]
721     fn test() {
722         // Test that a given `#[repr(...)]` attribute parses and returns the
723         // given `Repr` or error.
724         macro_rules! test {
725             ($(#[$attr:meta])* => $repr:expr) => {
726                 test!(@inner $(#[$attr])* => Repr => Ok($repr));
727             };
728             // In the error case, the caller must explicitly provide the name of
729             // the `Repr` type to assist in type inference.
730             (@error $(#[$attr:meta])* => $typ:ident => $repr:expr) => {
731                 test!(@inner $(#[$attr])* => $typ => Err($repr));
732             };
733             (@inner $(#[$attr:meta])* => $typ:ident => $repr:expr) => {
734                 let attr: Attribute = parse_quote!($(#[$attr])*);
735                 let mut got = $typ::from_attrs_inner(&[attr]);
736                 let expect: Result<Repr<_, _>, _> = $repr;
737                 if false {
738                     // Force Rust to infer `got` as having the same type as
739                     // `expect`.
740                     got = expect;
741                 }
742                 assert_eq!(got, expect, stringify!($(#[$attr])*));
743             };
744         }
745 
746         use AlignRepr::*;
747         use CompoundRepr::*;
748         use PrimitiveRepr::*;
749         let nz = |n: u32| NonZeroU32::new(n).unwrap();
750 
751         // Rust treats raw identifiers and their ordinary spellings as the
752         // same representation hints. Exercise every spelling recognized by
753         // `RawRepr::from_meta` so that adding a hint cannot accidentally
754         // reintroduce raw-identifier-sensitive parsing.
755         macro_rules! test_raw_repr {
756             ($ordinary:meta, $raw:meta => $expected:expr) => {{
757                 let ordinary: Meta = parse_quote!($ordinary);
758                 let raw: Meta = parse_quote!($raw);
759                 assert_eq!(RawRepr::from_meta(&ordinary).ok().unwrap(), $expected);
760                 assert_eq!(RawRepr::from_meta(&raw).ok().unwrap(), $expected);
761             }};
762         }
763 
764         test_raw_repr!(transparent, r#transparent => RawRepr::Transparent);
765         test_raw_repr!(C, r#C => RawRepr::C);
766         test_raw_repr!(Rust, r#Rust => RawRepr::Rust);
767         test_raw_repr!(u8, r#u8 => RawRepr::U8);
768         test_raw_repr!(u16, r#u16 => RawRepr::U16);
769         test_raw_repr!(u32, r#u32 => RawRepr::U32);
770         test_raw_repr!(u64, r#u64 => RawRepr::U64);
771         test_raw_repr!(u128, r#u128 => RawRepr::U128);
772         test_raw_repr!(usize, r#usize => RawRepr::Usize);
773         test_raw_repr!(i8, r#i8 => RawRepr::I8);
774         test_raw_repr!(i16, r#i16 => RawRepr::I16);
775         test_raw_repr!(i32, r#i32 => RawRepr::I32);
776         test_raw_repr!(i64, r#i64 => RawRepr::I64);
777         test_raw_repr!(i128, r#i128 => RawRepr::I128);
778         test_raw_repr!(isize, r#isize => RawRepr::Isize);
779         test_raw_repr!(align(2), r#align(2) => RawRepr::Align(nz(2)));
780         test_raw_repr!(packed, r#packed => RawRepr::Packed);
781         test_raw_repr!(packed(2), r#packed(2) => RawRepr::PackedN(nz(2)));
782 
783         test!(#[repr(transparent)] => StructUnionRepr::Transparent(s()));
784         test!(#[r#repr(r#transparent)] => StructUnionRepr::Transparent(s()));
785         test!(#[repr()] => StructUnionRepr::Compound(Rust.into(), None));
786         test!(#[repr(packed)] => StructUnionRepr::Compound(Rust.into(), Some(Packed(nz(1)).into())));
787         test!(#[repr(packed(2))] => StructUnionRepr::Compound(Rust.into(), Some(Packed(nz(2)).into())));
788         test!(#[repr(align(1))] => StructUnionRepr::Compound(Rust.into(), Some(Align(nz(1)).into())));
789         test!(#[repr(align(2))] => StructUnionRepr::Compound(Rust.into(), Some(Align(nz(2)).into())));
790         test!(#[repr(C)] => StructUnionRepr::Compound(C.into(), None));
791         test!(#[repr(C, packed)] => StructUnionRepr::Compound(C.into(), Some(Packed(nz(1)).into())));
792         test!(#[repr(C, packed(2))] => StructUnionRepr::Compound(C.into(), Some(Packed(nz(2)).into())));
793         test!(#[repr(C, align(1))] => StructUnionRepr::Compound(C.into(), Some(Align(nz(1)).into())));
794         test!(#[repr(C, align(2))] => StructUnionRepr::Compound(C.into(), Some(Align(nz(2)).into())));
795         test!(#[r#repr(r#C, r#align(2))] => StructUnionRepr::Compound(C.into(), Some(Align(nz(2)).into())));
796         test!(#[r#repr(r#C, r#packed(2))] => StructUnionRepr::Compound(C.into(), Some(Packed(nz(2)).into())));
797 
798         test!(#[repr(transparent)] => EnumRepr::Transparent(s()));
799         test!(#[repr()] => EnumRepr::Compound(Rust.into(), None));
800         test!(#[repr(align(1))] => EnumRepr::Compound(Rust.into(), Some(Align(nz(1)).into())));
801         test!(#[repr(align(2))] => EnumRepr::Compound(Rust.into(), Some(Align(nz(2)).into())));
802 
803         macro_rules! for_each_compound_repr {
804             ($($r:tt => $var:expr),*) => {
805                 $(
806                     test!(#[repr($r)] => EnumRepr::Compound($var.into(), None));
807                     test!(#[repr($r, align(1))] => EnumRepr::Compound($var.into(), Some(Align(nz(1)).into())));
808                     test!(#[repr($r, align(2))] => EnumRepr::Compound($var.into(), Some(Align(nz(2)).into())));
809                 )*
810             }
811         }
812 
813         for_each_compound_repr!(
814             C => C,
815             u8 => Primitive(U8),
816             u16 => Primitive(U16),
817             u32 => Primitive(U32),
818             u64 => Primitive(U64),
819             usize => Primitive(Usize),
820             i8 => Primitive(I8),
821             i16 => Primitive(I16),
822             i32 => Primitive(I32),
823             i64 => Primitive(I64),
824             isize => Primitive(Isize)
825         );
826 
827         use FromAttrsError::*;
828         use FromRawReprsError::*;
829 
830         // Run failure tests which are valid for both `StructUnionRepr` and
831         // `EnumRepr`.
832         macro_rules! for_each_repr_type {
833             ($($repr:ident),*) => {
834                 $(
835                     // Invalid packed or align attributes
836                     test!(@error #[repr(packed(0))] => $repr => Unrecognized.into());
837                     test!(@error #[repr(packed(3))] => $repr => Unrecognized.into());
838                     test!(@error #[repr(align(0))] => $repr => Unrecognized.into());
839                     test!(@error #[repr(align(3))] => $repr => Unrecognized.into());
840 
841                     // Conflicts
842                     test!(@error #[repr(transparent, transparent)] => $repr => FromRawReprs(Conflict).into());
843                     test!(@error #[repr(transparent, C)] => $repr => FromRawReprs(Conflict).into());
844                     test!(@error #[repr(transparent, Rust)] => $repr => FromRawReprs(Conflict).into());
845 
846                     test!(@error #[repr(C, transparent)] => $repr => FromRawReprs(Conflict).into());
847                     test!(@error #[repr(C, C)] => $repr => FromRawReprs(Conflict).into());
848                     test!(@error #[repr(C, Rust)] => $repr => FromRawReprs(Conflict).into());
849 
850                     test!(@error #[repr(Rust, transparent)] => $repr => FromRawReprs(Conflict).into());
851                     test!(@error #[repr(Rust, C)] => $repr => FromRawReprs(Conflict).into());
852                     test!(@error #[repr(Rust, Rust)] => $repr => FromRawReprs(Conflict).into());
853                 )*
854             }
855         }
856 
857         for_each_repr_type!(StructUnionRepr, EnumRepr);
858 
859         // Enum-specific conflicts.
860         //
861         // We don't bother to test every combination since that would be a huge
862         // number (enums can have primitive reprs u8, u16, u32, u64, usize, i8,
863         // i16, i32, i64, and isize). Instead, since the conflict logic doesn't
864         // care what specific value of `PrimitiveRepr` is present, we assume
865         // that testing against u8 alone is fine.
866         test!(@error #[repr(transparent, u8)] => EnumRepr => FromRawReprs(Conflict).into());
867         test!(@error #[repr(u8, transparent)] => EnumRepr => FromRawReprs(Conflict).into());
868         test!(@error #[repr(C, u8)] => EnumRepr => FromRawReprs(Conflict).into());
869         test!(@error #[repr(u8, C)] => EnumRepr => FromRawReprs(Conflict).into());
870         test!(@error #[repr(Rust, u8)] => EnumRepr => FromRawReprs(Conflict).into());
871         test!(@error #[repr(u8, Rust)] => EnumRepr => FromRawReprs(Conflict).into());
872         test!(@error #[repr(u8, u8)] => EnumRepr => FromRawReprs(Conflict).into());
873 
874         // Illegal struct/union reprs
875         test!(@error #[repr(u8)] => StructUnionRepr => FromRawReprs(Single(UnsupportedReprError)).into());
876         test!(@error #[repr(u16)] => StructUnionRepr => FromRawReprs(Single(UnsupportedReprError)).into());
877         test!(@error #[repr(u32)] => StructUnionRepr => FromRawReprs(Single(UnsupportedReprError)).into());
878         test!(@error #[repr(u64)] => StructUnionRepr => FromRawReprs(Single(UnsupportedReprError)).into());
879         test!(@error #[repr(usize)] => StructUnionRepr => FromRawReprs(Single(UnsupportedReprError)).into());
880         test!(@error #[repr(i8)] => StructUnionRepr => FromRawReprs(Single(UnsupportedReprError)).into());
881         test!(@error #[repr(i16)] => StructUnionRepr => FromRawReprs(Single(UnsupportedReprError)).into());
882         test!(@error #[repr(i32)] => StructUnionRepr => FromRawReprs(Single(UnsupportedReprError)).into());
883         test!(@error #[repr(i64)] => StructUnionRepr => FromRawReprs(Single(UnsupportedReprError)).into());
884         test!(@error #[repr(isize)] => StructUnionRepr => FromRawReprs(Single(UnsupportedReprError)).into());
885 
886         // Illegal enum reprs
887         test!(@error #[repr(packed)] => EnumRepr => FromRawReprs(Single(UnsupportedReprError)).into());
888         test!(@error #[repr(packed(1))] => EnumRepr => FromRawReprs(Single(UnsupportedReprError)).into());
889         test!(@error #[repr(packed(2))] => EnumRepr => FromRawReprs(Single(UnsupportedReprError)).into());
890     }
891 }
892