use rucc_ast::{AlignSpec, AttrArg, AttrList};
use rucc_diag::{Diagnostic, Span};
use rucc_types::layout;
use crate::check::Checker;
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub(in crate::check) struct Packing {
pub(in crate::check) packed: bool,
pub(in crate::check) align: Option<u32>,
}
const BIGGEST_ALIGNMENT: u32 = 16;
impl Checker<'_> {
pub(in crate::check) fn packing(&mut self, attrs: AttrList) -> Packing {
let mut packing = Packing::default();
let written = self.ast[attrs].to_vec();
for attr in written {
if attr.namespace.is_some_and(|ns| self.text(ns) != "gnu") {
continue;
}
match self.text(attr.name) {
"packed" => packing.packed = true,
"aligned" => {
if let Some(align) = self.aligned_argument(attr) {
packing.align = Some(packing.align.unwrap_or(1).max(align));
}
}
_ => {}
}
}
packing
}
pub(in crate::check) fn member_alignas(
&mut self,
align: Option<AlignSpec>,
span: Span,
) -> Option<u32> {
let requested = match align? {
AlignSpec::Type(named) => {
let named = self.type_name(named);
i128::from(layout(&self.types, named, self.cx.target).ok()?.align)
}
AlignSpec::Expr(expr) => {
let value = self.expr(expr);
match self.eval_integer(value) {
Ok(value) => value,
Err(failed) => {
if !failed.poisoned {
let at = self.tast.expr_span(failed.at);
let what = "requested alignment is not an integer constant";
self.report(Diagnostic::error(what, at).with_code("E0606"));
}
return None;
}
}
}
};
if requested == 0 {
return None;
}
if requested < 0 || requested & (requested - 1) != 0 {
let what = format!("requested alignment '{requested}' is not a positive power of 2");
self.report(Diagnostic::error(what, span).with_code("E0607"));
return None;
}
u32::try_from(requested).ok()
}
fn aligned_argument(&mut self, attr: rucc_ast::Attribute) -> Option<u32> {
let args = self.ast[attr.args].to_vec();
let requested = match args.first() {
None => return Some(BIGGEST_ALIGNMENT),
Some(AttrArg::Expr(expr)) => {
let value = self.expr(*expr);
match self.eval_integer(value) {
Ok(value) => value,
Err(failed) => {
if !failed.poisoned {
let at = self.tast.expr_span(failed.at);
let what = "requested alignment is not an integer constant";
self.report(Diagnostic::error(what, at).with_code("E0606"));
}
return None;
}
}
}
Some(AttrArg::Ident(_)) => {
let what = "requested alignment is not an integer constant";
self.report(Diagnostic::error(what, attr.span).with_code("E0606"));
return None;
}
};
if requested <= 0 || requested & (requested - 1) != 0 {
let what = format!("requested alignment '{requested}' is not a positive power of 2");
self.report(Diagnostic::error(what, attr.span).with_code("E0607"));
return None;
}
u32::try_from(requested).ok()
}
}