zerocopy_derive/
repr.rs

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