/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use syn::ext::IdentExt; use crate::bindgen::ir::ty::{IntKind, PrimitiveType}; #[derive(Debug, Copy, Clone, PartialEq, Eq, Default)] pub enum ReprStyle { #[default] Rust, C, Transparent, } #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub struct ReprType { kind: IntKind, signed: bool, } impl ReprType { pub(crate) fn to_primitive(self) -> PrimitiveType { PrimitiveType::Integer { kind: self.kind, signed: self.signed, zeroable: true, } } } #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum ReprAlign { Packed, Align(u64), } #[derive(Debug, Copy, Clone, PartialEq, Eq, Default)] pub struct Repr { pub style: ReprStyle, pub ty: Option, pub align: Option, } impl Repr { pub fn load(attrs: &[syn::Attribute]) -> Result { let ids = attrs .iter() .filter_map(|attr| { if let syn::Meta::List(syn::MetaList { path, nested, .. }) = attr.parse_meta().ok()? { if path.is_ident("repr") { return Some(nested.into_iter().collect::>()); } } None }) .flatten() .filter_map(|meta| match meta { syn::NestedMeta::Meta(syn::Meta::Path(path)) => Some(( path.segments.first().unwrap().ident.unraw().to_string(), None, )), syn::NestedMeta::Meta(syn::Meta::List(syn::MetaList { path, nested, .. })) => { Some(( path.segments.first().unwrap().ident.unraw().to_string(), Some( nested .iter() .filter_map(|meta| match meta { // Only used for #[repr(align(...))]. syn::NestedMeta::Lit(syn::Lit::Int(literal)) => { Some(literal.base10_digits().to_string()) } // Only single levels of nesting supported at the moment. _ => None, }) .collect::>(), ), )) } _ => None, }); let mut repr = Repr::default(); for id in ids { let (int_kind, signed) = match (id.0.as_ref(), id.1) { ("u8", None) => (IntKind::B8, false), ("u16", None) => (IntKind::B16, false), ("u32", None) => (IntKind::B32, false), ("u64", None) => (IntKind::B64, false), ("usize", None) => (IntKind::Size, false), ("i8", None) => (IntKind::B8, true), ("i16", None) => (IntKind::B16, true), ("i32", None) => (IntKind::B32, true), ("i64", None) => (IntKind::B64, true), ("isize", None) => (IntKind::Size, true), ("C", None) => { repr.style = ReprStyle::C; continue; } ("transparent", None) => { repr.style = ReprStyle::Transparent; continue; } ("packed", args) => { // #[repr(packed(n))] not supported because of some open questions about how // to calculate the native alignment of types. See mozilla/cbindgen#433. if args.is_some() { return Err( "Not-yet-implemented #[repr(packed(...))] encountered.".to_string() ); } let align = ReprAlign::Packed; // Only permit a single alignment-setting repr. if let Some(old_align) = repr.align { return Err(format!( "Conflicting #[repr(align(...))] type hints {:?} and {:?}.", old_align, align )); } repr.align = Some(align); continue; } ("align", Some(args)) => { // #[repr(align(...))] only allows a single argument. if args.len() != 1 { return Err(format!( "Unsupported #[repr(align({}))], align must have exactly one argument.", args.join(", ") )); } // Must be a positive integer. let align = match args.first().unwrap().parse::() { Ok(align) => align, Err(_) => { return Err(format!("Non-numeric #[repr(align({}))].", args.join(", "))) } }; // Must be a power of 2. if !align.is_power_of_two() || align == 0 { return Err(format!("Invalid alignment to #[repr(align({}))].", align)); } // Only permit a single alignment-setting repr. if let Some(old_align) = repr.align { return Err(format!( "Conflicting #[repr(align(...))] type hints {:?} and {:?}.", old_align, ReprAlign::Align(align) )); } repr.align = Some(ReprAlign::Align(align)); continue; } (path, args) => match args { None => return Err(format!("Unsupported #[repr({})].", path)), Some(args) => { return Err(format!( "Unsupported #[repr({}({}))].", path, args.join(", ") )); } }, }; let ty = ReprType { kind: int_kind, signed, }; if let Some(old_ty) = repr.ty { return Err(format!( "Conflicting #[repr(...)] type hints {:?} and {:?}.", old_ty, ty )); } repr.ty = Some(ty); } Ok(repr) } }