diff options
Diffstat (limited to 'src/bindgen/ir/repr.rs')
-rw-r--r-- | src/bindgen/ir/repr.rs | 182 |
1 files changed, 182 insertions, 0 deletions
diff --git a/src/bindgen/ir/repr.rs b/src/bindgen/ir/repr.rs new file mode 100644 index 0000000..c40cd7f --- /dev/null +++ b/src/bindgen/ir/repr.rs @@ -0,0 +1,182 @@ +/* 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<ReprType>, + pub align: Option<ReprAlign>, +} + +impl Repr { + pub fn load(attrs: &[syn::Attribute]) -> Result<Repr, String> { + 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::<Vec<_>>()); + } + } + 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::<Vec<_>>(), + ), + )) + } + _ => 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::<u64>() { + 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) + } +} |