Vendor dependencies for 0.3.0 release

This commit is contained in:
2025-09-27 10:29:08 -05:00
parent 0c8d39d483
commit 82ab7f317b
26803 changed files with 16134934 additions and 0 deletions

386
vendor/zerocopy-derive/src/enum.rs vendored Normal file
View File

@@ -0,0 +1,386 @@
// Copyright 2024 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 proc_macro2::{Span, TokenStream};
use quote::quote;
use syn::{parse_quote, DataEnum, Error, Fields, Generics, Ident, Path};
use crate::{derive_try_from_bytes_inner, repr::EnumRepr, Trait};
/// Generates a tag enum for the given enum. This generates an enum with the
/// same non-align `repr`s, variants, and corresponding discriminants, but none
/// of the fields.
pub(crate) fn generate_tag_enum(repr: &EnumRepr, data: &DataEnum) -> TokenStream {
let variants = data.variants.iter().map(|v| {
let ident = &v.ident;
if let Some((eq, discriminant)) = &v.discriminant {
quote! { #ident #eq #discriminant }
} else {
quote! { #ident }
}
});
// Don't include any `repr(align)` when generating the tag enum, as that
// could add padding after the tag but before any variants, which is not the
// correct behavior.
let repr = match repr {
EnumRepr::Transparent(span) => quote::quote_spanned! { *span => #[repr(transparent)] },
EnumRepr::Compound(c, _) => quote! { #c },
};
quote! {
#repr
#[allow(dead_code, non_camel_case_types)]
enum ___ZerocopyTag {
#(#variants,)*
}
}
}
fn tag_ident(variant_ident: &Ident) -> Ident {
Ident::new(&format!("___ZEROCOPY_TAG_{}", variant_ident), variant_ident.span())
}
/// Generates a constant for the tag associated with each variant of the enum.
/// When we match on the enum's tag, each arm matches one of these constants. We
/// have to use constants here because:
///
/// - The type that we're matching on is not the type of the tag, it's an
/// integer of the same size as the tag type and with the same bit patterns.
/// - We can't read the enum tag as an enum because the bytes may not represent
/// a valid variant.
/// - Patterns do not currently support const expressions, so we have to assign
/// these constants to names rather than use them inline in the `match`
/// statement.
fn generate_tag_consts(data: &DataEnum) -> TokenStream {
let tags = data.variants.iter().map(|v| {
let variant_ident = &v.ident;
let tag_ident = tag_ident(variant_ident);
quote! {
// This casts the enum variant to its discriminant, and then
// converts the discriminant to the target integral type via a
// numeric cast [1].
//
// Because these are the same size, this is defined to be a no-op
// and therefore is a lossless conversion [2].
//
// [1]: https://doc.rust-lang.org/stable/reference/expressions/operator-expr.html#enum-cast
// [2]: https://doc.rust-lang.org/stable/reference/expressions/operator-expr.html#numeric-cast
#[allow(non_upper_case_globals)]
const #tag_ident: ___ZerocopyTagPrimitive =
___ZerocopyTag::#variant_ident as ___ZerocopyTagPrimitive;
}
});
quote! {
#(#tags)*
}
}
fn variant_struct_ident(variant_ident: &Ident) -> Ident {
Ident::new(&format!("___ZerocopyVariantStruct_{}", variant_ident), variant_ident.span())
}
/// Generates variant structs for the given enum variant.
///
/// These are structs associated with each variant of an enum. They are
/// `repr(C)` tuple structs with the same fields as the variant after a
/// `MaybeUninit<___ZerocopyInnerTag>`.
///
/// In order to unify the generated types for `repr(C)` and `repr(int)` enums,
/// we use a "fused" representation with fields for both an inner tag and an
/// outer tag. Depending on the repr, we will set one of these tags to the tag
/// type and the other to `()`. This lets us generate the same code but put the
/// tags in different locations.
fn generate_variant_structs(
enum_name: &Ident,
generics: &Generics,
data: &DataEnum,
zerocopy_crate: &Path,
) -> TokenStream {
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
// All variant structs have a `PhantomData<MyEnum<...>>` field because we
// don't know which generic parameters each variant will use, and unused
// generic parameters are a compile error.
let phantom_ty = quote! {
core_reexport::marker::PhantomData<#enum_name #ty_generics>
};
let variant_structs = data.variants.iter().filter_map(|variant| {
// We don't generate variant structs for unit variants because we only
// need to check the tag. This helps cut down our generated code a bit.
if matches!(variant.fields, Fields::Unit) {
return None;
}
let variant_struct_ident = variant_struct_ident(&variant.ident);
let field_types = variant.fields.iter().map(|f| &f.ty);
let variant_struct = parse_quote! {
#[repr(C)]
#[allow(non_snake_case)]
struct #variant_struct_ident #impl_generics (
core_reexport::mem::MaybeUninit<___ZerocopyInnerTag>,
#(#field_types,)*
#phantom_ty,
) #where_clause;
};
// We do this rather than emitting `#[derive(::zerocopy::TryFromBytes)]`
// because that is not hygienic, and this is also more performant.
let try_from_bytes_impl =
derive_try_from_bytes_inner(&variant_struct, Trait::TryFromBytes, zerocopy_crate)
.expect("derive_try_from_bytes_inner should not fail on synthesized type");
Some(quote! {
#variant_struct
#try_from_bytes_impl
})
});
quote! {
#(#variant_structs)*
}
}
fn generate_variants_union(generics: &Generics, data: &DataEnum) -> TokenStream {
let (_, ty_generics, _) = generics.split_for_impl();
let fields = data.variants.iter().filter_map(|variant| {
// We don't generate variant structs for unit variants because we only
// need to check the tag. This helps cut down our generated code a bit.
if matches!(variant.fields, Fields::Unit) {
return None;
}
// Field names are prefixed with `__field_` to prevent name collision with
// the `__nonempty` field.
let field_name = Ident::new(&format!("__field_{}", &variant.ident), variant.ident.span());
let variant_struct_ident = variant_struct_ident(&variant.ident);
Some(quote! {
#field_name: core_reexport::mem::ManuallyDrop<
#variant_struct_ident #ty_generics
>,
})
});
quote! {
#[repr(C)]
#[allow(non_snake_case)]
union ___ZerocopyVariants #generics {
#(#fields)*
// Enums can have variants with no fields, but unions must
// have at least one field. So we just add a trailing unit
// to ensure that this union always has at least one field.
// Because this union is `repr(C)`, this unit type does not
// affect the layout.
__nonempty: (),
}
}
}
/// Generates an implementation of `is_bit_valid` for an arbitrary enum.
///
/// The general process is:
///
/// 1. Generate a tag enum. This is an enum with the same repr, variants, and
/// corresponding discriminants as the original enum, but without any fields
/// on the variants. This gives us access to an enum where the variants have
/// the same discriminants as the one we're writing `is_bit_valid` for.
/// 2. Make constants from the variants of the tag enum. We need these because
/// we can't put const exprs in match arms.
/// 3. Generate variant structs. These are structs which have the same fields as
/// each variant of the enum, and are `#[repr(C)]` with an optional "inner
/// tag".
/// 4. Generate a variants union, with one field for each variant struct type.
/// 5. And finally, our raw enum is a `#[repr(C)]` struct of an "outer tag" and
/// the variants union.
///
/// See these reference links for fully-worked example decompositions.
///
/// - `repr(C)`: <https://doc.rust-lang.org/reference/type-layout.html#reprc-enums-with-fields>
/// - `repr(int)`: <https://doc.rust-lang.org/reference/type-layout.html#primitive-representation-of-enums-with-fields>
/// - `repr(C, int)`: <https://doc.rust-lang.org/reference/type-layout.html#combining-primitive-representations-of-enums-with-fields-and-reprc>
pub(crate) fn derive_is_bit_valid(
enum_ident: &Ident,
repr: &EnumRepr,
generics: &Generics,
data: &DataEnum,
zerocopy_crate: &Path,
) -> Result<TokenStream, Error> {
let trait_path = Trait::TryFromBytes.crate_path(zerocopy_crate);
let tag_enum = generate_tag_enum(repr, data);
let tag_consts = generate_tag_consts(data);
let (outer_tag_type, inner_tag_type) = if repr.is_c() {
(quote! { ___ZerocopyTag }, quote! { () })
} else if repr.is_primitive() {
(quote! { () }, quote! { ___ZerocopyTag })
} else {
return Err(Error::new(
Span::call_site(),
"must have #[repr(C)] or #[repr(Int)] attribute in order to guarantee this type's memory layout",
));
};
let variant_structs = generate_variant_structs(enum_ident, generics, data, zerocopy_crate);
let variants_union = generate_variants_union(generics, data);
let (_, ty_generics, _) = generics.split_for_impl();
let match_arms = data.variants.iter().map(|variant| {
let tag_ident = tag_ident(&variant.ident);
let variant_struct_ident = variant_struct_ident(&variant.ident);
if matches!(variant.fields, Fields::Unit) {
// Unit variants don't need any further validation beyond checking
// the tag.
quote! {
#tag_ident => true
}
} else {
quote! {
#tag_ident => {
// SAFETY:
// - This cast is from a `repr(C)` union which has a field
// of type `variant_struct_ident` to that variant struct
// type itself. This addresses a subset of the bytes
// addressed by `variants`.
// - The returned pointer is cast from `p`, and so has the
// same provenance as `p`.
// - We checked that the tag of the enum matched the
// constant for this variant, so this cast preserves
// types and locations of all fields. Therefore, any
// `UnsafeCell`s will have the same location as in the
// original type.
let variant = unsafe {
variants.cast_unsized_unchecked(
|p: #zerocopy_crate::pointer::PtrInner<'_, ___ZerocopyVariants #ty_generics>| {
p.cast_sized::<#variant_struct_ident #ty_generics>()
}
)
};
// SAFETY: `cast_unsized_unchecked` removes the
// initialization invariant from `p`, so we re-assert that
// all of the bytes are initialized.
let variant = unsafe { variant.assume_initialized() };
<
#variant_struct_ident #ty_generics as #trait_path
>::is_bit_valid(variant)
}
}
}
});
Ok(quote! {
// SAFETY: We use `is_bit_valid` to validate that the bit pattern of the
// enum's tag corresponds to one of the enum's discriminants. Then, we
// check the bit validity of each field of the corresponding variant.
// Thus, this is a sound implementation of `is_bit_valid`.
fn is_bit_valid<___ZerocopyAliasing>(
mut candidate: #zerocopy_crate::Maybe<'_, Self, ___ZerocopyAliasing>,
) -> #zerocopy_crate::util::macro_util::core_reexport::primitive::bool
where
___ZerocopyAliasing: #zerocopy_crate::pointer::invariant::Reference,
{
use #zerocopy_crate::util::macro_util::core_reexport;
#tag_enum
type ___ZerocopyTagPrimitive = #zerocopy_crate::util::macro_util::SizeToTag<
{ core_reexport::mem::size_of::<___ZerocopyTag>() },
>;
#tag_consts
type ___ZerocopyOuterTag = #outer_tag_type;
type ___ZerocopyInnerTag = #inner_tag_type;
#variant_structs
#variants_union
#[repr(C)]
struct ___ZerocopyRawEnum #generics {
tag: ___ZerocopyOuterTag,
variants: ___ZerocopyVariants #ty_generics,
}
let tag = {
// SAFETY:
// - The provided cast addresses a subset of the bytes addressed
// by `candidate` because it addresses the starting tag of the
// enum.
// - Because the pointer is cast from `candidate`, it has the
// same provenance as it.
// - There are no `UnsafeCell`s in the tag because it is a
// primitive integer.
let tag_ptr = unsafe {
candidate.reborrow().cast_unsized_unchecked(|p: #zerocopy_crate::pointer::PtrInner<'_, Self>| {
p.cast_sized::<___ZerocopyTagPrimitive>()
})
};
// SAFETY: `tag_ptr` is casted from `candidate`, whose referent
// is `Initialized`. Since we have not written uninitialized
// bytes into the referent, `tag_ptr` is also `Initialized`.
let tag_ptr = unsafe { tag_ptr.assume_initialized() };
tag_ptr.recall_validity::<_, (_, (_, _))>().read_unaligned::<#zerocopy_crate::BecauseImmutable>()
};
// SAFETY:
// - The raw enum has the same fields in the same locations as the
// input enum, and may have a lower alignment. This guarantees
// that it addresses a subset of the bytes addressed by
// `candidate`.
// - The returned pointer is cast from `p`, and so has the same
// provenance as `p`.
// - The raw enum has the same types at the same locations as the
// original enum, and so preserves the locations of any
// `UnsafeCell`s.
let raw_enum = unsafe {
candidate.cast_unsized_unchecked(|p: #zerocopy_crate::pointer::PtrInner<'_, Self>| {
p.cast_sized::<___ZerocopyRawEnum #ty_generics>()
})
};
// SAFETY: `cast_unsized_unchecked` removes the initialization
// invariant from `p`, so we re-assert that all of the bytes are
// initialized.
let raw_enum = unsafe { raw_enum.assume_initialized() };
// SAFETY:
// - This projection returns a subfield of `this` using
// `addr_of_mut!`.
// - Because the subfield pointer is derived from `this`, it has the
// same provenance.
// - The locations of `UnsafeCell`s in the subfield match the
// locations of `UnsafeCell`s in `this`. This is because the
// subfield pointer just points to a smaller portion of the
// overall struct.
let variants = unsafe {
use #zerocopy_crate::pointer::PtrInner;
raw_enum.cast_unsized_unchecked(|p: PtrInner<'_, ___ZerocopyRawEnum #ty_generics>| {
let p = p.as_non_null().as_ptr();
let ptr = core_reexport::ptr::addr_of_mut!((*p).variants);
// SAFETY: `ptr` is a projection into `p`, which is
// `NonNull`, and guaranteed not to wrap around the address
// space. Thus, `ptr` cannot be null.
let ptr = unsafe { core_reexport::ptr::NonNull::new_unchecked(ptr) };
unsafe { PtrInner::new(ptr) }
})
};
#[allow(non_upper_case_globals)]
match tag {
#(#match_arms,)*
_ => false,
}
}
})
}

114
vendor/zerocopy-derive/src/ext.rs vendored Normal file
View File

@@ -0,0 +1,114 @@
// 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 proc_macro2::{Span, TokenStream};
use quote::ToTokens;
use syn::{Data, DataEnum, DataStruct, DataUnion, Field, Ident, Index, Type, Visibility};
pub(crate) trait DataExt {
/// Extracts the names and types of all fields. For enums, extracts the
/// names and types of fields from each variant. For tuple structs, the
/// names are the indices used to index into the struct (ie, `0`, `1`, etc).
///
/// FIXME: Extracting field names for enums doesn't really make sense. Types
/// makes sense because we don't care about where they live - we just care
/// about transitive ownership. But for field names, we'd only use them when
/// generating is_bit_valid, which cares about where they live.
fn fields(&self) -> Vec<(&Visibility, TokenStream, &Type)>;
fn variants(&self) -> Vec<Vec<(&Visibility, TokenStream, &Type)>>;
fn tag(&self) -> Option<Ident>;
}
impl DataExt for Data {
fn fields(&self) -> Vec<(&Visibility, TokenStream, &Type)> {
match self {
Data::Struct(strc) => strc.fields(),
Data::Enum(enm) => enm.fields(),
Data::Union(un) => un.fields(),
}
}
fn variants(&self) -> Vec<Vec<(&Visibility, TokenStream, &Type)>> {
match self {
Data::Struct(strc) => strc.variants(),
Data::Enum(enm) => enm.variants(),
Data::Union(un) => un.variants(),
}
}
fn tag(&self) -> Option<Ident> {
match self {
Data::Struct(strc) => strc.tag(),
Data::Enum(enm) => enm.tag(),
Data::Union(un) => un.tag(),
}
}
}
impl DataExt for DataStruct {
fn fields(&self) -> Vec<(&Visibility, TokenStream, &Type)> {
map_fields(&self.fields)
}
fn variants(&self) -> Vec<Vec<(&Visibility, TokenStream, &Type)>> {
vec![self.fields()]
}
fn tag(&self) -> Option<Ident> {
None
}
}
impl DataExt for DataEnum {
fn fields(&self) -> Vec<(&Visibility, TokenStream, &Type)> {
map_fields(self.variants.iter().flat_map(|var| &var.fields))
}
fn variants(&self) -> Vec<Vec<(&Visibility, TokenStream, &Type)>> {
self.variants.iter().map(|var| map_fields(&var.fields)).collect()
}
fn tag(&self) -> Option<Ident> {
Some(Ident::new("___ZerocopyTag", Span::call_site()))
}
}
impl DataExt for DataUnion {
fn fields(&self) -> Vec<(&Visibility, TokenStream, &Type)> {
map_fields(&self.fields.named)
}
fn variants(&self) -> Vec<Vec<(&Visibility, TokenStream, &Type)>> {
vec![self.fields()]
}
fn tag(&self) -> Option<Ident> {
None
}
}
fn map_fields<'a>(
fields: impl 'a + IntoIterator<Item = &'a Field>,
) -> Vec<(&'a Visibility, TokenStream, &'a Type)> {
fields
.into_iter()
.enumerate()
.map(|(idx, f)| {
(
&f.vis,
f.ident
.as_ref()
.map(ToTokens::to_token_stream)
.unwrap_or_else(|| Index::from(idx).to_token_stream()),
&f.ty,
)
})
.collect()
}

1883
vendor/zerocopy-derive/src/lib.rs vendored Normal file

File diff suppressed because it is too large Load Diff

2362
vendor/zerocopy-derive/src/output_tests.rs vendored Normal file

File diff suppressed because it is too large Load Diff

849
vendor/zerocopy-derive/src/repr.rs vendored Normal file
View File

@@ -0,0 +1,849 @@
// 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>>>),
}
/// A compound representation: `repr(C)`, `repr(Rust)`, or `repr(Int)`.
#[cfg_attr(test, derive(Copy, Clone, Debug, Eq, PartialEq))]
pub(crate) enum CompoundRepr<Prim> {
C,
Rust,
Primitive(Prim),
}
/// `repr(Int)`
#[derive(Copy, Clone)]
#[cfg_attr(test, derive(Debug, Eq, PartialEq))]
pub(crate) enum PrimitiveRepr {
U8,
U16,
U32,
U64,
U128,
Usize,
I8,
I16,
I32,
I64,
I128,
Isize,
}
/// `repr(packed(...))` or `repr(align(...))`
#[cfg_attr(test, derive(Copy, Clone, Debug, Eq, PartialEq))]
pub(crate) enum AlignRepr<Packed> {
Packed(Packed),
Align(NonZeroU32),
}
/// 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::*;
match self {
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)",
}),
},
}
}
pub(crate) fn is_transparent(&self) -> bool {
matches!(self, Repr::Transparent(_))
}
pub(crate) fn is_c(&self) -> bool {
use CompoundRepr::*;
matches!(self, Repr::Compound(Spanned { t: C, span: _ }, _align))
}
pub(crate) fn is_primitive(&self) -> bool {
use CompoundRepr::*;
matches!(self, Repr::Compound(Spanned { t: Primitive(_), span: _ }, _align))
}
pub(crate) fn get_packed(&self) -> Option<&Packed> {
use AlignRepr::*;
use Repr::*;
if let Compound(_, Some(Spanned { t: Packed(p), span: _ })) = self {
Some(p)
} else {
None
}
}
pub(crate) fn get_align(&self) -> Option<Spanned<NonZeroU32>> {
use AlignRepr::*;
use Repr::*;
if let Compound(_, Some(Spanned { t: Align(n), span })) = self {
Some(Spanned::new(*n, *span))
} else {
None
}
}
pub(crate) fn is_align_gt_1(&self) -> bool {
self.get_align().map(|n| n.t.get() > 1).unwrap_or(false)
}
/// 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> {
if let 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> Repr<PrimitiveRepr, Packed> {
fn get_primitive(&self) -> Option<&PrimitiveRepr> {
use CompoundRepr::*;
use Repr::*;
if let Compound(Spanned { t: Primitive(p), span: _ }, _align) = self {
Some(p)
} else {
None
}
}
/// Does `self` describe a `#[repr(u8)]` type?
pub(crate) fn is_u8(&self) -> bool {
matches!(self.get_primitive(), Some(PrimitiveRepr::U8))
}
/// Does `self` describe a `#[repr(i8)]` type?
pub(crate) fn is_i8(&self) -> bool {
matches!(self.get_primitive(), Some(PrimitiveRepr::I8))
}
}
impl<Prim, Packed> ToTokens for Repr<Prim, Packed>
where
Prim: With<PrimitiveRepr> + Copy,
Packed: With<NonZeroU32> + Copy,
{
fn to_tokens(&self, ts: &mut TokenStream) {
use Repr::*;
match self {
Transparent(span) => ts.append_all(quote_spanned! { *span=> #[repr(transparent)] }),
Compound(repr, align) => {
repr.to_tokens(ts);
if let Some(align) = align {
align.to_tokens(ts);
}
}
}
}
}
impl<Prim: With<PrimitiveRepr> + Copy> ToTokens for Spanned<CompoundRepr<Prim>> {
fn to_tokens(&self, ts: &mut TokenStream) {
use CompoundRepr::*;
match &self.t {
C => ts.append_all(quote_spanned! { self.span=> #[repr(C)] }),
Rust => ts.append_all(quote_spanned! { self.span=> #[repr(Rust)] }),
Primitive(prim) => prim.with(|prim| Spanned::new(prim, self.span).to_tokens(ts)),
}
}
}
impl ToTokens for Spanned<PrimitiveRepr> {
fn to_tokens(&self, ts: &mut TokenStream) {
use PrimitiveRepr::*;
match self.t {
U8 => ts.append_all(quote_spanned! { self.span => #[repr(u8)] }),
U16 => ts.append_all(quote_spanned! { self.span => #[repr(u16)] }),
U32 => ts.append_all(quote_spanned! { self.span => #[repr(u32)] }),
U64 => ts.append_all(quote_spanned! { self.span => #[repr(u64)] }),
U128 => ts.append_all(quote_spanned! { self.span => #[repr(u128)] }),
Usize => ts.append_all(quote_spanned! { self.span => #[repr(usize)] }),
I8 => ts.append_all(quote_spanned! { self.span => #[repr(i8)] }),
I16 => ts.append_all(quote_spanned! { self.span => #[repr(i16)] }),
I32 => ts.append_all(quote_spanned! { self.span => #[repr(i32)] }),
I64 => ts.append_all(quote_spanned! { self.span => #[repr(i64)] }),
I128 => ts.append_all(quote_spanned! { self.span => #[repr(i128)] }),
Isize => ts.append_all(quote_spanned! { self.span => #[repr(isize)] }),
}
}
}
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 };
match self.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;
impl<Prim: With<PrimitiveRepr>> TryFrom<RawRepr> for CompoundRepr<Prim> {
type Error = FromRawReprError<UnsupportedReprError>;
fn try_from(
raw: RawRepr,
) -> Result<CompoundRepr<Prim>, FromRawReprError<UnsupportedReprError>> {
use RawRepr::*;
match raw {
C => Ok(CompoundRepr::C),
Rust => Ok(CompoundRepr::Rust),
raw @ (U8 | U16 | U32 | U64 | U128 | Usize | I8 | I16 | I32 | I64 | I128 | Isize) => {
Prim::try_with_or(
|| match raw {
U8 => Ok(PrimitiveRepr::U8),
U16 => Ok(PrimitiveRepr::U16),
U32 => Ok(PrimitiveRepr::U32),
U64 => Ok(PrimitiveRepr::U64),
U128 => Ok(PrimitiveRepr::U128),
Usize => Ok(PrimitiveRepr::Usize),
I8 => Ok(PrimitiveRepr::I8),
I16 => Ok(PrimitiveRepr::I16),
I32 => Ok(PrimitiveRepr::I32),
I64 => Ok(PrimitiveRepr::I64),
I128 => Ok(PrimitiveRepr::I128),
Isize => Ok(PrimitiveRepr::Isize),
Transparent | C | Rust | Align(_) | PackedN(_) | Packed => {
Err(UnsupportedReprError)
}
},
UnsupportedReprError,
)
.map(CompoundRepr::Primitive)
.map_err(FromRawReprError::Err)
}
Transparent | Align(_) | PackedN(_) | Packed => Err(FromRawReprError::None),
}
}
}
impl<Pcked: With<NonZeroU32>> TryFrom<RawRepr> for AlignRepr<Pcked> {
type Error = FromRawReprError<UnsupportedReprError>;
fn try_from(raw: RawRepr) -> Result<AlignRepr<Pcked>, FromRawReprError<UnsupportedReprError>> {
use RawRepr::*;
match raw {
Packed | PackedN(_) => Pcked::try_with_or(
|| match raw {
Packed => Ok(NonZeroU32::new(1).unwrap()),
PackedN(n) => Ok(n),
U8 | U16 | U32 | U64 | U128 | Usize | I8 | I16 | I32 | I64 | I128 | Isize
| Transparent | C | Rust | Align(_) => Err(UnsupportedReprError),
},
UnsupportedReprError,
)
.map(AlignRepr::Packed)
.map_err(FromRawReprError::Err),
Align(n) => Ok(AlignRepr::Align(n)),
U8 | U16 | U32 | U64 | U128 | Usize | I8 | I16 | I32 | I64 | I128 | Isize
| Transparent | C | Rust => Err(FromRawReprError::None),
}
}
}
/// 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))
}
};
if let 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<FromRawReprsError<UnsupportedReprError>> for FromAttrsError {
fn from(err: FromRawReprsError<UnsupportedReprError>) -> FromAttrsError {
FromAttrsError::FromRawReprs(err)
}
}
impl From<UnrecognizedReprError> for FromAttrsError {
fn from(_err: UnrecognizedReprError) -> FromAttrsError {
FromAttrsError::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"),
}
}
}
impl<Prim, Packed> Repr<Prim, Packed> {
fn from_attrs_inner(attrs: &[Attribute]) -> Result<Repr<Prim, Packed>, Spanned<FromAttrsError>>
where
Prim: With<PrimitiveRepr>,
Packed: With<NonZeroU32>,
{
let raw_reprs = RawRepr::from_attrs(attrs).map_err(Spanned::from)?;
let transparent = {
let mut 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)?;
if let 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));
}
Ok(Repr::Transparent(span))
} else {
Ok(Repr::Compound(
compound.unwrap_or(Spanned::new(CompoundRepr::Rust, Span::call_site())),
align,
))
}
}
}
impl<Prim, Packed> Repr<Prim, Packed> {
pub(crate) fn from_attrs(attrs: &[Attribute]) -> Result<Repr<Prim, Packed>, Error>
where
Prim: With<PrimitiveRepr>,
Packed: With<NonZeroU32>,
{
Repr::from_attrs_inner(attrs).map_err(Into::into)
}
}
/// The representation hint could not be parsed or was unrecognized.
struct UnrecognizedReprError;
impl RawRepr {
fn from_attrs(
attrs: &[Attribute],
) -> Result<Vec<Spanned<RawRepr>>, Spanned<UnrecognizedReprError>> {
let mut reprs = Vec::new();
for attr in attrs {
// Ignore documentation attributes.
if attr.path().is_ident("doc") {
continue;
}
if let Meta::List(ref meta_list) = attr.meta {
if meta_list.path.is_ident("repr") {
let parsed: Punctuated<Meta, Comma> =
match meta_list.parse_args_with(Punctuated::parse_terminated) {
Ok(parsed) => parsed,
Err(_) => {
return Err(Spanned::new(
UnrecognizedReprError,
meta_list.tokens.span(),
))
}
};
for meta in parsed {
let s = meta.span();
reprs.push(
RawRepr::from_meta(&meta)
.map(|r| Spanned::new(r, s))
.map_err(|e| Spanned::new(e, s))?,
);
}
}
}
}
Ok(reprs)
}
fn from_meta(meta: &Meta) -> Result<RawRepr, UnrecognizedReprError> {
let (path, list) = match meta {
Meta::Path(path) => (path, None),
Meta::List(list) => (&list.path, Some(list)),
_ => return Err(UnrecognizedReprError),
};
let ident = path.get_ident().ok_or(UnrecognizedReprError)?;
// Only returns `Ok` for non-zero power-of-two values.
let parse_nzu64 = |list: &MetaList| {
list.parse_args::<LitInt>()
.and_then(|int| int.base10_parse::<NonZeroU32>())
.map_err(|_| UnrecognizedReprError)
.and_then(|nz| {
if nz.get().is_power_of_two() {
Ok(nz)
} else {
Err(UnrecognizedReprError)
}
})
};
use RawRepr::*;
Ok(match (ident.to_string().as_str(), list) {
("u8", None) => U8,
("u16", None) => U16,
("u32", None) => U32,
("u64", None) => U64,
("u128", None) => U128,
("usize", None) => Usize,
("i8", None) => I8,
("i16", None) => I16,
("i32", None) => I32,
("i64", None) => I64,
("i128", None) => I128,
("isize", None) => Isize,
("C", None) => C,
("transparent", None) => Transparent,
("Rust", None) => Rust,
("packed", None) => Packed,
("packed", Some(list)) => PackedN(parse_nzu64(list)?),
("align", Some(list)) => Align(parse_nzu64(list)?),
_ => return Err(UnrecognizedReprError),
})
}
}
pub(crate) use util::*;
mod util {
use super::*;
/// 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,
}
impl<T> Spanned<T> {
pub(super) fn new(t: T, span: Span) -> Spanned<T> {
Spanned { t, 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 {}
pub(crate) trait With<T> {
fn with<O, F: FnOnce(T) -> O>(self, f: F) -> O;
fn try_with_or<E, F: FnOnce() -> Result<T, E>>(f: F, err: E) -> Result<Self, E>
where
Self: Sized;
}
impl<T: Inhabited> With<T> for T {
fn with<O, F: FnOnce(T) -> O>(self, f: F) -> O {
f(self)
}
fn try_with_or<E, F: FnOnce() -> Result<T, E>>(f: F, _err: E) -> Result<Self, E> {
f()
}
}
impl<T> With<T> for Infallible {
fn with<O, F: FnOnce(T) -> O>(self, _f: F) -> O {
match self {}
}
fn try_with_or<E, F: FnOnce() -> Result<T, E>>(_f: F, err: E) -> Result<Self, E> {
Err(err)
}
}
}
#[cfg(test)]
mod tests {
use syn::parse_quote;
use super::*;
impl<T> From<T> for Spanned<T> {
fn from(t: T) -> Spanned<T> {
Spanned::new(t, Span::call_site())
}
}
// 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)
}
}
impl<T: Eq> Eq for Spanned<T> {}
impl<Prim: PartialEq, Packed: PartialEq> PartialEq for Repr<Prim, Packed> {
fn eq(&self, other: &Repr<Prim, Packed>) -> bool {
match (self, other) {
(Repr::Transparent(_), Repr::Transparent(_)) => true,
(Repr::Compound(sc, sa), Repr::Compound(oc, oa)) => (sc, sa) == (oc, oa),
_ => false,
}
}
}
fn s() -> Span {
Span::call_site()
}
#[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])*);
let mut got = $typ::from_attrs_inner(&[attr]);
let expect: Result<Repr<_, _>, _> = $repr;
if false {
// 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();
test!(#[repr(transparent)] => StructUnionRepr::Transparent(s()));
test!(#[repr()] => StructUnionRepr::Compound(Rust.into(), None));
test!(#[repr(packed)] => StructUnionRepr::Compound(Rust.into(), Some(Packed(nz(1)).into())));
test!(#[repr(packed(2))] => StructUnionRepr::Compound(Rust.into(), Some(Packed(nz(2)).into())));
test!(#[repr(align(1))] => StructUnionRepr::Compound(Rust.into(), Some(Align(nz(1)).into())));
test!(#[repr(align(2))] => StructUnionRepr::Compound(Rust.into(), Some(Align(nz(2)).into())));
test!(#[repr(C)] => StructUnionRepr::Compound(C.into(), None));
test!(#[repr(C, packed)] => StructUnionRepr::Compound(C.into(), Some(Packed(nz(1)).into())));
test!(#[repr(C, packed(2))] => StructUnionRepr::Compound(C.into(), Some(Packed(nz(2)).into())));
test!(#[repr(C, align(1))] => StructUnionRepr::Compound(C.into(), Some(Align(nz(1)).into())));
test!(#[repr(C, align(2))] => StructUnionRepr::Compound(C.into(), Some(Align(nz(2)).into())));
test!(#[repr(transparent)] => EnumRepr::Transparent(s()));
test!(#[repr()] => EnumRepr::Compound(Rust.into(), None));
test!(#[repr(align(1))] => EnumRepr::Compound(Rust.into(), Some(Align(nz(1)).into())));
test!(#[repr(align(2))] => EnumRepr::Compound(Rust.into(), Some(Align(nz(2)).into())));
macro_rules! for_each_compound_repr {
($($r:tt => $var:expr),*) => {
$(
test!(#[repr($r)] => EnumRepr::Compound($var.into(), None));
test!(#[repr($r, align(1))] => EnumRepr::Compound($var.into(), Some(Align(nz(1)).into())));
test!(#[repr($r, align(2))] => EnumRepr::Compound($var.into(), Some(Align(nz(2)).into())));
)*
}
}
for_each_compound_repr!(
C => C,
u8 => Primitive(U8),
u16 => Primitive(U16),
u32 => Primitive(U32),
u64 => Primitive(U64),
usize => Primitive(Usize),
i8 => Primitive(I8),
i16 => Primitive(I16),
i32 => Primitive(I32),
i64 => Primitive(I64),
isize => Primitive(Isize)
);
use FromAttrsError::*;
use FromRawReprsError::*;
// Run failure tests which are valid for both `StructUnionRepr` and
// `EnumRepr`.
macro_rules! for_each_repr_type {
($($repr:ident),*) => {
$(
// Invalid packed or align attributes
test!(@error #[repr(packed(0))] => $repr => Unrecognized.into());
test!(@error #[repr(packed(3))] => $repr => Unrecognized.into());
test!(@error #[repr(align(0))] => $repr => Unrecognized.into());
test!(@error #[repr(align(3))] => $repr => Unrecognized.into());
// Conflicts
test!(@error #[repr(transparent, transparent)] => $repr => FromRawReprs(Conflict).into());
test!(@error #[repr(transparent, C)] => $repr => FromRawReprs(Conflict).into());
test!(@error #[repr(transparent, Rust)] => $repr => FromRawReprs(Conflict).into());
test!(@error #[repr(C, transparent)] => $repr => FromRawReprs(Conflict).into());
test!(@error #[repr(C, C)] => $repr => FromRawReprs(Conflict).into());
test!(@error #[repr(C, Rust)] => $repr => FromRawReprs(Conflict).into());
test!(@error #[repr(Rust, transparent)] => $repr => FromRawReprs(Conflict).into());
test!(@error #[repr(Rust, C)] => $repr => FromRawReprs(Conflict).into());
test!(@error #[repr(Rust, Rust)] => $repr => FromRawReprs(Conflict).into());
)*
}
}
for_each_repr_type!(StructUnionRepr, EnumRepr);
// 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());
// Illegal struct/union reprs
test!(@error #[repr(u8)] => StructUnionRepr => FromRawReprs(Single(UnsupportedReprError)).into());
test!(@error #[repr(u16)] => StructUnionRepr => FromRawReprs(Single(UnsupportedReprError)).into());
test!(@error #[repr(u32)] => StructUnionRepr => FromRawReprs(Single(UnsupportedReprError)).into());
test!(@error #[repr(u64)] => StructUnionRepr => FromRawReprs(Single(UnsupportedReprError)).into());
test!(@error #[repr(usize)] => StructUnionRepr => FromRawReprs(Single(UnsupportedReprError)).into());
test!(@error #[repr(i8)] => StructUnionRepr => FromRawReprs(Single(UnsupportedReprError)).into());
test!(@error #[repr(i16)] => StructUnionRepr => FromRawReprs(Single(UnsupportedReprError)).into());
test!(@error #[repr(i32)] => StructUnionRepr => FromRawReprs(Single(UnsupportedReprError)).into());
test!(@error #[repr(i64)] => StructUnionRepr => FromRawReprs(Single(UnsupportedReprError)).into());
test!(@error #[repr(isize)] => StructUnionRepr => FromRawReprs(Single(UnsupportedReprError)).into());
// Illegal enum reprs
test!(@error #[repr(packed)] => EnumRepr => FromRawReprs(Single(UnsupportedReprError)).into());
test!(@error #[repr(packed(1))] => EnumRepr => FromRawReprs(Single(UnsupportedReprError)).into());
test!(@error #[repr(packed(2))] => EnumRepr => FromRawReprs(Single(UnsupportedReprError)).into());
}
}