blob: 971320187674a704cd6e4a4ed0f9e0603c8373ae [file] [log] [blame]
/* 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)]
pub enum ReprStyle {
Rust,
C,
Transparent,
}
impl Default for ReprStyle {
fn default() -> Self {
ReprStyle::Rust
}
}
#[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 eqrion/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)
}
}