use rucc_base::Symbol;
use rucc_diag::Span;
use rucc_types::IntKind;
use crate::check::Checker;
use crate::expr::{Category, Expr, ExprId, ExprKind};
#[derive(Debug, Clone, Copy)]
struct Row {
name: &'static str,
builtin: &'static str,
at: IntKind,
}
const FAMILY: &[Row] = &[
Row { name: "abs", builtin: "__builtin_abs", at: IntKind::Int },
Row { name: "labs", builtin: "__builtin_labs", at: IntKind::Long },
Row { name: "llabs", builtin: "__builtin_llabs", at: IntKind::LongLong },
];
impl Checker<'_> {
pub(in crate::check) fn abs_builtin_value(
&mut self,
callee: ExprId,
function: Option<Symbol>,
args: &[ExprId],
span: Span,
) -> Option<ExprId> {
let name = function?;
let spelled = self.text(name);
let row = *FAMILY
.iter()
.find(|row| row.name == spelled || row.builtin == spelled)
.filter(|_| self.cx.means_the_library(spelled))?;
let ty = self.types.int(row.at);
if !self.callee_is_the_library_one(callee, ty, &[ty]) {
return None;
}
let &operand = args.first()?;
if self.is_poisoned(operand) {
return Some(self.poison(span));
}
Some(self.tast.expr(Expr::new(ExprKind::Abs { operand }, ty, Category::Rvalue), span))
}
}
#[cfg(test)]
mod tests {
use rucc_gnu::{Kind, Status};
use super::*;
#[test]
fn every_prefixed_spelling_is_a_row_of_the_table_with_the_type_this_expects() {
for row in FAMILY {
let Some(feature) = rucc_gnu::lookup(Kind::Builtin, row.builtin) else {
panic!("{} is answered here and is not in features.toml", row.builtin);
};
assert_eq!(feature.status, Status::Implemented, "{}", row.builtin);
let written = match row.at {
IntKind::Int => "int(int)",
IntKind::Long => "long(long)",
IntKind::LongLong => "long long(long long)",
other => panic!("{other:?} is not one of the three widths this family has"),
};
assert_eq!(feature.signature, written, "{}", row.builtin);
}
}
#[test]
fn the_prefixed_spelling_is_the_plain_name_with_the_prefix_on_it() {
for row in FAMILY {
assert_eq!(row.builtin.strip_prefix("__builtin_"), Some(row.name));
}
}
}