blob: 71c4b5845aa7dc9a49f84ac50e5305c5c0aad277 [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 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)
}
}