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".
repr_type_name(&self) -> &str where Prim: Copy + With<PrimitiveRepr>,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
is_transparent(&self) -> bool112 pub(crate) fn is_transparent(&self) -> bool {
113 matches!(self, Repr::Transparent(_))
114 }
115
is_c(&self) -> bool116 pub(crate) fn is_c(&self) -> bool {
117 use CompoundRepr::*;
118 matches!(self, Repr::Compound(Spanned { t: C, span: _ }, _align))
119 }
120
is_primitive(&self) -> bool121 pub(crate) fn is_primitive(&self) -> bool {
122 use CompoundRepr::*;
123 matches!(self, Repr::Compound(Spanned { t: Primitive(_), span: _ }, _align))
124 }
125
get_packed(&self) -> Option<&Packed>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
get_align(&self) -> Option<Spanned<NonZeroU32>>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
is_align_gt_1(&self) -> bool146 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.
unaligned_validate_no_align_gt_1(&self) -> Result<(), 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?
is_packed_1(&self) -> bool168 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> {
get_primitive(&self) -> Option<&PrimitiveRepr>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?
is_u8(&self) -> bool185 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?
is_i8(&self) -> bool190 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 {
to_tokens(&self, ts: &mut TokenStream)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>> {
to_tokens(&self, ts: &mut TokenStream)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> {
to_tokens(&self, ts: &mut TokenStream)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>> {
to_tokens(&self, ts: &mut TokenStream)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>;
try_from( raw: RawRepr, ) -> Result<CompoundRepr<Prim>, 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>;
try_from(raw: RawRepr) -> Result<AlignRepr<Pcked>, 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.
try_from_raw_reprs<'a, E, R: TryFrom<RawRepr, Error = FromRawReprError<E>>>( r: impl IntoIterator<Item = &'a Spanned<RawRepr>>, ) -> Result<Option<Spanned<R>>, Spanned<FromRawReprsError<E>>>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 {
from(err: FromRawReprsError<UnsupportedReprError>) -> FromAttrsError424 fn from(err: FromRawReprsError<UnsupportedReprError>) -> FromAttrsError {
425 FromAttrsError::FromRawReprs(err)
426 }
427 }
428
429 impl From<UnrecognizedReprError> for FromAttrsError {
from(_err: UnrecognizedReprError) -> FromAttrsError430 fn from(_err: UnrecognizedReprError) -> FromAttrsError {
431 FromAttrsError::Unrecognized
432 }
433 }
434
435 impl From<Spanned<FromAttrsError>> for Error {
from(err: Spanned<FromAttrsError>) -> Error436 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> {
from_attrs_inner(attrs: &[Attribute]) -> Result<Repr<Prim, Packed>, Spanned<FromAttrsError>> where Prim: With<PrimitiveRepr>, Packed: With<NonZeroU32>,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> {
from_attrs(attrs: &[Attribute]) -> Result<Repr<Prim, Packed>, Error> where Prim: With<PrimitiveRepr>, Packed: With<NonZeroU32>,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 {
from_attrs( attrs: &[Attribute], ) -> Result<Vec<Spanned<RawRepr>>, Spanned<UnrecognizedReprError>>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
from_meta(meta: &Meta) -> Result<RawRepr, UnrecognizedReprError>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> {
new(t: T, span: Span) -> Spanned<T>617 pub(super) fn new(t: T, span: Span) -> Spanned<T> {
618 Spanned { t, span }
619 }
620
from<U>(s: Spanned<U>) -> Spanned<T> where T: From<U>,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.
try_from<E, U>( u: Spanned<U>, ) -> Result<Spanned<T>, FromRawReprError<Spanned<E>>> where T: TryFrom<U, Error = FromRawReprError<E>>,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> {
with<O, F: FnOnce(T) -> O>(self, f: F) -> O652 fn with<O, F: FnOnce(T) -> O>(self, f: F) -> O;
try_with_or<E, F: FnOnce() -> Result<T, E>>(f: F, err: E) -> Result<Self, E> where Self: Sized653 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 {
with<O, F: FnOnce(T) -> O>(self, f: F) -> O659 fn with<O, F: FnOnce(T) -> O>(self, f: F) -> O {
660 f(self)
661 }
662
try_with_or<E, F: FnOnce() -> Result<T, E>>(f: F, _err: E) -> Result<Self, E>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 {
with<O, F: FnOnce(T) -> O>(self, _f: F) -> O669 fn with<O, F: FnOnce(T) -> O>(self, _f: F) -> O {
670 match self {}
671 }
672
try_with_or<E, F: FnOnce() -> Result<T, E>>(_f: F, err: E) -> Result<Self, E>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> {
from(t: T) -> 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> {
eq(&self, other: &Spanned<T>) -> bool694 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> {
eq(&self, other: &Repr<Prim, Packed>) -> bool702 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
s() -> Span711 fn s() -> Span {
712 Span::call_site()
713 }
714
715 #[test]
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