use rucc_target::TargetInfo;
use crate::kind::{FloatKind, IntKind, TypeKind};
use crate::layout::{float_format, int_width};
use crate::types::{TypeId, Types};
pub fn promote(types: &mut Types, id: TypeId, target: &TargetInfo) -> TypeId {
let id = value_type(types, id);
match types.kind(id) {
TypeKind::Bool => types.int(IntKind::Int),
TypeKind::Int(kind) => promoted_int(types, kind, int_width(kind, target), target),
TypeKind::Enum(id) => {
let underlying = types.enum_info(id).underlying;
match underlying {
Some(underlying) => promote(types, underlying, target),
None => types.int(IntKind::Int),
}
}
_ => id,
}
}
pub fn promote_bit_field(types: &mut Types, id: TypeId, width: u32, target: &TargetInfo) -> TypeId {
let id = value_type(types, id);
let signed = match types.kind(id) {
TypeKind::Bool => false,
TypeKind::Int(kind) => kind.is_signed(target.char_is_signed),
TypeKind::BitInt { signed, .. } => signed,
_ => return promote(types, id, target),
};
let int = int_width(IntKind::Int, target);
if width < int || (signed && width == int) {
return types.int(IntKind::Int);
}
if !signed && width == int {
return types.int(IntKind::UInt);
}
promote(types, id, target)
}
pub fn usual_arithmetic(
types: &mut Types,
left: TypeId,
right: TypeId,
target: &TargetInfo,
) -> Option<TypeId> {
let left = value_type(types, left);
let right = value_type(types, right);
if let Some(common) = floating(types, left, right, target) {
return Some(common);
}
let left = promote(types, left, target);
let right = promote(types, right, target);
if left == right {
return integer_shape(types, left, target).map(|_| left);
}
let left = integer_shape(types, left, target)?;
let right = integer_shape(types, right, target)?;
Some(common_integer(types, left, right, target))
}
fn value_type(types: &mut Types, id: TypeId) -> TypeId {
let id = types.canonical(id);
let id = match types.kind(id) {
TypeKind::Atomic(inner) => types.canonical(inner),
_ => id,
};
types.unqualified(id)
}
fn promoted_int(types: &mut Types, kind: IntKind, width: u32, target: &TargetInfo) -> TypeId {
if kind.rank() >= IntKind::Int.rank() {
return types.int(kind);
}
let int = int_width(IntKind::Int, target);
let signed = kind.is_signed(target.char_is_signed);
if width < int || (signed && width == int) {
return types.int(IntKind::Int);
}
types.int(IntKind::UInt)
}
fn floating(types: &mut Types, left: TypeId, right: TypeId, target: &TargetInfo) -> Option<TypeId> {
let left = float_part(types, left);
let right = float_part(types, right);
let (kind, complex) = match (left, right) {
(None, None) => return None,
(Some((kind, complex)), None) | (None, Some((kind, complex))) => (kind, complex),
(Some((a, a_complex)), Some((b, b_complex))) => {
let kind = if float_rank(a, target) >= float_rank(b, target) { a } else { b };
(kind, a_complex || b_complex)
}
};
Some(if complex { types.complex(kind) } else { types.float(kind) })
}
fn float_rank(kind: FloatKind, target: &TargetInfo) -> (u32, i32, u8) {
let format = float_format(kind, target);
(format.precision(), format.max_exponent(), kind.tie_break())
}
fn float_part(types: &Types, id: TypeId) -> Option<(FloatKind, bool)> {
match types.kind(id) {
TypeKind::Float(kind) => Some((kind, false)),
TypeKind::Complex(kind) => Some((kind, true)),
_ => None,
}
}
#[derive(Clone, Copy)]
struct IntShape {
signed: bool,
width: u32,
standard: Option<IntKind>,
}
impl IntShape {
fn rank(self) -> (u32, u8, u8) {
match self.standard {
Some(kind) => (self.width, 1, kind.rank()),
None => (self.width, 0, 0),
}
}
fn covers(self, other: IntShape) -> bool {
if self.signed == other.signed {
return self.width >= other.width;
}
self.signed && self.width > other.width
}
}
fn integer_shape(types: &Types, id: TypeId, target: &TargetInfo) -> Option<IntShape> {
match types.kind(id) {
TypeKind::Int(kind) => Some(IntShape {
signed: kind.is_signed(target.char_is_signed),
width: int_width(kind, target),
standard: Some(kind),
}),
TypeKind::BitInt { signed, width } => Some(IntShape { signed, width, standard: None }),
_ => None,
}
}
fn common_integer(
types: &mut Types,
left: IntShape,
right: IntShape,
target: &TargetInfo,
) -> TypeId {
let (higher, lower) = if left.rank() >= right.rank() { (left, right) } else { (right, left) };
if higher.signed == lower.signed || !higher.signed || higher.covers(lower) {
return build(types, higher, target);
}
build(types, IntShape { signed: false, ..higher }, target)
}
fn build(types: &mut Types, shape: IntShape, target: &TargetInfo) -> TypeId {
match shape.standard {
Some(kind) if kind.is_signed(target.char_is_signed) == shape.signed => types.int(kind),
Some(kind) => types.int(kind.flip_sign()),
None => types.bit_int(shape.signed, shape.width),
}
}