use yog_ids::script_problem;
use super::Checker;
use super::model::Type;
use crate::ast::BinOp;
use crate::parser::expr::sign_of;
use crate::source::token::Span;
impl Checker<'_, '_, '_> {
pub(super) fn bitwise(&mut self, op: BinOp, (a, b): (&Type, &Type), at: Span) -> Type {
if !a.known() || !b.known() {
return Type::Unknown;
}
let fits = match op {
BinOp::BitAnd | BinOp::BitOr | BinOp::BitXor if a == b && self.is_enum(a) => {
Some(a.clone())
}
BinOp::Shl | BinOp::Shr => {
(a.is_integer() && b.is_integer() && b.converts_to(&Type::I32)).then(|| promoted(a))
}
_ if *a == Type::Bool && *b == Type::Bool => Some(Type::Bool),
_ if a.is_integer() && b.is_integer() => Some(promoted(&Type::wider(a, b))),
_ => None,
};
fits.unwrap_or_else(|| {
let sign = sign_of(op);
self.mismatch(at, format!("`{a} {sign} {b}`: bits are whole numbers' (a shift's count an `i32`), `&` `|` `^` two `bool`s' too"));
Type::Unknown
})
}
pub(super) fn is_enum(&self, t: &Type) -> bool {
match t.non_null() {
Type::Own(name, args) => self.own.get(name, args.len()).is_some_and(|o| o.is_enum),
_ => false,
}
}
pub(super) fn flipped(&mut self, t: &Type, at: Span) -> Type {
if !t.known() || t.is_integer() {
return promoted(t);
}
if self.is_enum(t) {
return t.clone();
}
self.mismatch(
at,
format!("`~` flips a whole number's bits, not a `{t}`'s"),
);
Type::Unknown
}
pub(super) fn stepped(&mut self, t: &Type, at: Span) -> Type {
if !t.known() || t.is_numeric() {
return t.clone();
}
self.mismatch(at, format!("`++` and `--` step a number, not a `{t}`"));
Type::Unknown
}
fn mismatch(&mut self, at: Span, message: String) {
self.report
.error(script_problem::TYPE_MISMATCH, at, message);
}
}
fn promoted(t: &Type) -> Type {
match t {
Type::I8 | Type::I16 | Type::U8 | Type::U16 | Type::Char => Type::I32,
_ => t.clone(),
}
}