src/bindgen/ir/repr.rs (153 lines of code) (raw):

/* 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 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 mut ids = Vec::new(); // We want only the `repr` attributes let iter = attrs.iter().filter(|attr| attr.path().is_ident("repr")); for repr_attr in iter { let reprs = repr_attr .parse_args_with( syn::punctuated::Punctuated::<syn::Meta, syn::Token![,]>::parse_terminated, ) .map_err(|err| format!("Invalid `#[repr]` attribute: {err}"))?; for meta in reprs { match meta { // #[repr(C)] and #[repr(transparent)] and #[repr(INT)] syn::Meta::Path(path) => { if let Some(ident) = path.get_ident() { let ident_s = ident.to_string(); ids.push((ident_s, None)) } else { return Err("Invalid `repr` attribute".to_string()); } } // #[repr(align(N))] syn::Meta::List(meta) if meta.path.is_ident("align") => { let lit: syn::LitInt = meta .parse_args() .map_err(|err| format!("Invalid align argument: {err}"))?; ids.push(("align".to_string(), Some(lit.to_string()))) } // #[repr(packed(N))] syn::Meta::List(meta) if meta.path.is_ident("packed") => { // no arguments if meta.tokens.is_empty() { ids.push(("packed".to_string(), None)) } else { let lit: syn::LitInt = meta .parse_args() .map_err(|err| format!("Invalid packed argument: {err}"))?; ids.push(("packed".to_string(), Some(lit.to_string()))) } } _ => return Err("Invalid `repr` attribute".to_string()), } } } 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(arg)) => { // Must be a positive integer. let align = match arg.parse::<u64>() { Ok(align) => align, Err(_) => return Err(format!("Non-unsigned #[repr(align({}))].", arg)), }; // 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, arg) => match arg { None => return Err(format!("Unsupported #[repr({})].", path)), Some(arg) => { return Err(format!("Unsupported #[repr({}({}))].", path, arg)); } }, }; 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) } }