Skip to main content

zerocopy_derive/
repr.rs

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
11use core::{
12    convert::{Infallible, TryFrom},
13    num::NonZeroU32,
14};
15
16use proc_macro2::{Span, TokenStream};
17use quote::{quote_spanned, ToTokens, TokenStreamExt as _};
18use syn::{
19    punctuated::Punctuated, spanned::Spanned as _, token::Comma, Attribute, Error, LitInt, Meta,
20    MetaList,
21};
22
23use 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))]
31pub(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))]
41pub(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))]
50pub(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))]
67pub(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.
73pub(crate) type StructUnionRepr = Repr<Infallible, NonZeroU32>;
74
75/// The representations which can legally appear on an enum type.
76pub(crate) type EnumRepr = Repr<PrimitiveRepr, Infallible>;
77
78impl<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
168impl<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
175impl<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
197impl<Prim, Packed> ToTokens for Repr<Prim, Packed>
198where
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
216impl<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
227impl 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
247impl<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))]
272pub(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))]
295pub(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))]
306pub(crate) struct UnsupportedReprError;
307
308impl<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
346impl<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))]
371enum 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.
384fn 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))]
420enum FromAttrsError {
421    FromRawReprs(FromRawReprsError<UnsupportedReprError>),
422    Unrecognized,
423}
424
425impl From<FromRawReprsError<UnsupportedReprError>> for FromAttrsError {
426    fn from(err: FromRawReprsError<UnsupportedReprError>) -> FromAttrsError {
427        FromAttrsError::FromRawReprs(err)
428    }
429}
430
431impl From<UnrecognizedReprError> for FromAttrsError {
432    fn from(_err: UnrecognizedReprError) -> FromAttrsError {
433        FromAttrsError::Unrecognized
434    }
435}
436
437impl 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
457impl<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
509impl<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.
520struct UnrecognizedReprError;
521
522impl 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
610pub(crate) use util::*;
611mod 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)]
685mod 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}