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