// Copyright 2019 The Fuchsia Authors // // Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0 // <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT // license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. // This file may not be copied, modified, or distributed except according to // those terms.
use core::{
convert::{Infallible, TryFrom},
num::NonZeroU32,
};
use proc_macro2::{Span, TokenStream}; use quote::{quote_spanned, ToTokens, TokenStreamExt as _}; use syn::{
punctuated::Punctuated, spanned::Spanned as _, token::Comma, Attribute, Error, LitInt, Meta,
MetaList,
};
/// The computed representation of a type. /// /// This is the result of processing all `#[repr(...)]` attributes on a type, if /// any. A `Repr` is only capable of representing legal combinations of /// `#[repr(...)]` attributes. #[cfg_attr(test, derive(Copy, Clone, Debug))] pub(crate) enum Repr<Prim, Packed> { /// `#[repr(transparent)]`
Transparent(Span), /// A compound representation: `repr(C)`, `repr(Rust)`, or `repr(Int)` /// optionally combined with `repr(packed(...))` or `repr(align(...))`
Compound(Spanned<CompoundRepr<Prim>>, Option<Spanned<AlignRepr<Packed>>>),
}
/// The representations which can legally appear on a struct or union type. pub(crate) type StructUnionRepr = Repr<Infallible, NonZeroU32>;
/// The representations which can legally appear on an enum type. pub(crate) type EnumRepr = Repr<PrimitiveRepr, Infallible>;
impl<Prim, Packed> Repr<Prim, Packed> { /// Gets the name of this "repr type" - the non-align `repr(X)` that is used /// in prose to refer to this type. /// /// For example, we would refer to `#[repr(C, align(4))] struct Foo { ... }` /// as a "`repr(C)` struct". pub(crate) fn repr_type_name(&self) -> &str where
Prim: Copy + With<PrimitiveRepr>,
{ use CompoundRepr::*; use PrimitiveRepr::*; use Repr::*; matchself {
Transparent(_span) => "repr(transparent)",
Compound(Spanned { t: repr, span: _ }, _align) => match repr {
C => "repr(C)",
Rust => "repr(Rust)",
Primitive(prim) => prim.with(|prim| match prim {
U8 => "repr(u8)",
U16 => "repr(u16)",
U32 => "repr(u32)",
U64 => "repr(u64)",
U128 => "repr(u128)",
Usize => "repr(usize)",
I8 => "repr(i8)",
I16 => "repr(i16)",
I32 => "repr(i32)",
I64 => "repr(i64)",
I128 => "repr(i128)",
Isize => "repr(isize)",
}),
},
}
}
/// When deriving `Unaligned`, validate that the decorated type has no /// `#[repr(align(N))]` attribute where `N > 1`. If no such attribute exists /// (including if `N == 1`), this returns `Ok(())`, and otherwise it returns /// a descriptive error. pub(crate) fn unaligned_validate_no_align_gt_1(&self) -> Result<(), Error> { iflet Some(n) = self.get_align().filter(|n| n.t.get() > 1) {
Err(Error::new(
n.span, "cannot derive `Unaligned` on type with alignment greater than 1",
))
} else {
Ok(())
}
}
}
impl<Prim> Repr<Prim, NonZeroU32> { /// Does `self` describe a `#[repr(packed)]` or `#[repr(packed(1))]` type? pub(crate) fn is_packed_1(&self) -> bool { self.get_packed().map(|n| n.get() == 1).unwrap_or(false)
}
}
impl<Packed: With<NonZeroU32> + Copy> ToTokens for Spanned<AlignRepr<Packed>> { fn to_tokens(&self, ts: &mut TokenStream) { use AlignRepr::*; // We use `syn::Index` instead of `u32` because `quote_spanned!` // serializes `u32` literals as `123u32`, not just `123`. Rust doesn't // recognize that as a valid argument to `#[repr(align(...))]` or // `#[repr(packed(...))]`. let to_index = |n: NonZeroU32| syn::Index { index: n.get(), span: self.span }; matchself.t {
Packed(n) => n.with(|n| { let n = to_index(n);
ts.append_all(quote_spanned! { self.span => #[repr(packed(#n))] })
}),
Align(n) => { let n = to_index(n);
ts.append_all(quote_spanned! { self.span => #[repr(align(#n))] })
}
}
}
}
/// The result of parsing a single `#[repr(...)]` attribute or a single /// directive inside a compound `#[repr(..., ...)]` attribute. #[derive(Copy, Clone, PartialEq, Eq)] #[cfg_attr(test, derive(Debug))] pub(crate) enum RawRepr {
Transparent,
C,
Rust,
U8,
U16,
U32,
U64,
U128,
Usize,
I8,
I16,
I32,
I64,
I128,
Isize,
Align(NonZeroU32),
PackedN(NonZeroU32),
Packed,
}
/// The error from converting from a `RawRepr`. #[cfg_attr(test, derive(Debug, Eq, PartialEq))] pub(crate) enum FromRawReprError<E> { /// The `RawRepr` doesn't affect the high-level repr we're parsing (e.g. /// it's `align(...)` and we're parsing a `CompoundRepr`).
None, /// The `RawRepr` is invalid for the high-level repr we're parsing (e.g. /// it's `packed` repr and we're parsing an `AlignRepr` for an enum type).
Err(E),
}
/// The representation hint is not supported for the decorated type. #[cfg_attr(test, derive(Copy, Clone, Debug, Eq, PartialEq))] pub(crate) struct UnsupportedReprError;
/// The error from extracting a high-level repr type from a list of `RawRepr`s. #[cfg_attr(test, derive(Copy, Clone, Debug, Eq, PartialEq))] enum FromRawReprsError<E> { /// One of the `RawRepr`s is invalid for the high-level repr we're parsing /// (e.g. there's a `packed` repr and we're parsing an `AlignRepr` for an /// enum type).
Single(E), /// Two `RawRepr`s appear which both affect the high-level repr we're /// parsing (e.g., the list is `#[repr(align(2), packed)]`). Note that we /// conservatively treat redundant reprs as conflicting (e.g. /// `#[repr(packed, packed)]`).
Conflict,
}
/// Tries to extract a high-level repr from a list of `RawRepr`s. fn 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>>> { // Walk the list of `RawRepr`s and attempt to convert each to an `R`. Bail // if we find any errors. If we find more than one which converts to an `R`, // bail with a `Conflict` error.
r.into_iter().try_fold(None, |found: Option<Spanned<R>>, raw| { let new = match Spanned::<R>::try_from(*raw) {
Ok(r) => r, // This `RawRepr` doesn't convert to an `R`, so keep the current // found `R`, if any.
Err(FromRawReprError::None) => return Ok(found), // This repr is unsupported for the decorated type (e.g. // `repr(packed)` on an enum).
Err(FromRawReprError::Err(Spanned { t: err, span })) => { return Err(Spanned::new(FromRawReprsError::Single(err), span))
}
};
iflet Some(found) = found { // We already found an `R`, but this `RawRepr` also converts to an // `R`, so that's a conflict. // // `Span::join` returns `None` if the two spans are from different // files or if we're not on the nightly compiler. In that case, just // use `new`'s span. let span = found.span.join(new.span).unwrap_or(new.span);
Err(Spanned::new(FromRawReprsError::Conflict, span))
} else {
Ok(Some(new))
}
})
}
/// The error returned from [`Repr::from_attrs`]. #[cfg_attr(test, derive(Copy, Clone, Debug, Eq, PartialEq))] enum FromAttrsError {
FromRawReprs(FromRawReprsError<UnsupportedReprError>),
Unrecognized,
}
impl From<Spanned<FromAttrsError>> for Error { fn from(err: Spanned<FromAttrsError>) -> Error { let Spanned { t: err, span } = err; match err {
FromAttrsError::FromRawReprs(FromRawReprsError::Single(
_err @ UnsupportedReprError,
)) => Error::new(span, "unsupported representation hint for the decorated type"),
FromAttrsError::FromRawReprs(FromRawReprsError::Conflict) => { // NOTE: This says "another" rather than "a preceding" because // when one of the reprs involved is `transparent`, we detect // that condition in `Repr::from_attrs`, and at that point we // can't tell which repr came first, so we might report this on // the first involved repr rather than the second, third, etc.
Error::new(span, "this conflicts with another representation hint")
}
FromAttrsError::Unrecognized => Error::new(span, "unrecognized representation hint"),
}
}
}
let transparent = { letmut transparents = raw_reprs.iter().filter_map(|Spanned { t, span }| match t {
RawRepr::Transparent => Some(span),
_ => None,
}); let first = transparents.next(); let second = transparents.next(); match (first, second) {
(None, None) => None,
(Some(span), None) => Some(*span),
(Some(_), Some(second)) => { return Err(Spanned::new(
FromAttrsError::FromRawReprs(FromRawReprsError::Conflict),
*second,
))
} // An iterator can't produce a value only on the second call to // `.next()`.
(None, Some(_)) => unreachable!(),
}
};
let compound: Option<Spanned<CompoundRepr<Prim>>> =
try_from_raw_reprs(raw_reprs.iter()).map_err(Spanned::from)?; let align: Option<Spanned<AlignRepr<Packed>>> =
try_from_raw_reprs(raw_reprs.iter()).map_err(Spanned::from)?;
iflet Some(span) = transparent { if compound.is_some() || align.is_some() { // Arbitrarily report the problem on the `transparent` span. Any // span will do. return Err(Spanned::new(FromRawReprsError::Conflict.into(), span));
}
pub(crate) use util::*; mod util { usesuper::*; /// A value with an associated span. #[derive(Copy, Clone)] #[cfg_attr(test, derive(Debug))] pub(crate) struct Spanned<T> { pub(crate) t: T, pub(crate) span: Span,
}
pub(super) fn from<U>(s: Spanned<U>) -> Spanned<T> where
T: From<U>,
{ let Spanned { t: u, span } = s;
Spanned::new(u.into(), span)
}
/// Delegates to `T: TryFrom`, preserving span information in both the /// success and error cases. pub(super) fn try_from<E, U>(
u: Spanned<U>,
) -> Result<Spanned<T>, FromRawReprError<Spanned<E>>> where
T: TryFrom<U, Error = FromRawReprError<E>>,
{ let Spanned { t: u, span } = u;
T::try_from(u).map(|t| Spanned { t, span }).map_err(|err| match err {
FromRawReprError::None => FromRawReprError::None,
FromRawReprError::Err(e) => FromRawReprError::Err(Spanned::new(e, span)),
})
}
}
// Used to permit implementing `With<T> for T: Inhabited` and for // `Infallible` without a blanket impl conflict. pub(crate) trait Inhabited {} impl Inhabited for PrimitiveRepr {} impl Inhabited for NonZeroU32 {}
// We ignore spans for equality in testing since real spans are hard to // synthesize and don't implement `PartialEq`. impl<T: PartialEq> PartialEq for Spanned<T> { fn eq(&self, other: &Spanned<T>) -> bool { self.t.eq(&other.t)
}
}
#[test] fn test() { // Test that a given `#[repr(...)]` attribute parses and returns the // given `Repr` or error.
macro_rules! test {
($(#[$attr:meta])* => $repr:expr) => {
test!(@inner $(#[$attr])* => Repr => Ok($repr));
}; // In the error case, the caller must explicitly provide the name of // the `Repr` type to assist in type inference.
(@error $(#[$attr:meta])* => $typ:ident => $repr:expr) => {
test!(@inner $(#[$attr])* => $typ => Err($repr));
};
(@inner $(#[$attr:meta])* => $typ:ident => $repr:expr) => { let attr: Attribute = parse_quote!($(#[$attr])*); letmut got = $typ::from_attrs_inner(&[attr]); let expect: Result<Repr<_, _>, _> = $repr; iffalse { // Force Rust to infer `got` as having the same type as // `expect`.
got = expect;
}
assert_eq!(got, expect, stringify!($(#[$attr])*));
};
}
use AlignRepr::*; use CompoundRepr::*; use PrimitiveRepr::*; let nz = |n: u32| NonZeroU32::new(n).unwrap();
// Enum-specific conflicts. // // We don't bother to test every combination since that would be a huge // number (enums can have primitive reprs u8, u16, u32, u64, usize, i8, // i16, i32, i64, and isize). Instead, since the conflict logic doesn't // care what specific value of `PrimitiveRepr` is present, we assume // that testing against u8 alone is fine.
test!(@error #[repr(transparent, u8)] => EnumRepr => FromRawReprs(Conflict).into());
test!(@error #[repr(u8, transparent)] => EnumRepr => FromRawReprs(Conflict).into());
test!(@error #[repr(C, u8)] => EnumRepr => FromRawReprs(Conflict).into());
test!(@error #[repr(u8, C)] => EnumRepr => FromRawReprs(Conflict).into());
test!(@error #[repr(Rust, u8)] => EnumRepr => FromRawReprs(Conflict).into());
test!(@error #[repr(u8, Rust)] => EnumRepr => FromRawReprs(Conflict).into());
test!(@error #[repr(u8, u8)] => EnumRepr => FromRawReprs(Conflict).into());
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.