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