use crate::kind::{ArrayLen, FloatKind, FunctionType, IntKind, RecordId, TypeKind};
use crate::types::{TypeId, Types};
#[must_use]
pub fn compatible(types: &Types, left: TypeId, right: TypeId) -> bool {
let mut assumed = Vec::new();
same(types, left, right, &mut assumed)
}
pub fn composite(types: &mut Types, left: TypeId, right: TypeId) -> Option<TypeId> {
if !compatible(types, left, right) {
return None;
}
Some(build(types, left, right))
}
pub fn adjust_parameter(types: &mut Types, id: TypeId) -> TypeId {
let canonical = types.canonical(id);
match types.kind(canonical) {
TypeKind::Array { elem, .. } => types.pointer(elem),
TypeKind::Function(_) => types.pointer(canonical),
_ => types.unqualified(id),
}
}
fn same(
types: &Types,
left: TypeId,
right: TypeId,
assumed: &mut Vec<(RecordId, RecordId)>,
) -> bool {
let left = types.canonical(left);
let right = types.canonical(right);
if left == right {
return true;
}
if types.quals(left) != types.quals(right) {
return false;
}
match (types.kind(left), types.kind(right)) {
(TypeKind::Enum(_), TypeKind::Enum(_)) => false,
(TypeKind::Enum(id), _) => match types.enum_info(id).underlying {
Some(underlying) => same(types, underlying, right, assumed),
None => false,
},
(_, TypeKind::Enum(id)) => match types.enum_info(id).underlying {
Some(underlying) => same(types, left, underlying, assumed),
None => false,
},
(TypeKind::Pointer(a), TypeKind::Pointer(b))
| (TypeKind::Atomic(a), TypeKind::Atomic(b)) => same(types, a, b, assumed),
(TypeKind::Array { elem: a, len: x }, TypeKind::Array { elem: b, len: y }) => {
lengths_agree(x, y) && same(types, a, b, assumed)
}
(TypeKind::Vector { elem: a, len: x }, TypeKind::Vector { elem: b, len: y }) => {
x == y && same(types, a, b, assumed)
}
(TypeKind::Function(a), TypeKind::Function(b)) => {
functions(types, types.signature(a), types.signature(b), assumed)
}
(TypeKind::Record(a), TypeKind::Record(b)) => records(types, a, b, assumed),
_ => false,
}
}
fn lengths_agree(left: ArrayLen, right: ArrayLen) -> bool {
match (left, right) {
(ArrayLen::Fixed(a), ArrayLen::Fixed(b)) => a == b,
_ => true,
}
}
fn functions(
types: &Types,
left: &FunctionType,
right: &FunctionType,
assumed: &mut Vec<(RecordId, RecordId)>,
) -> bool {
if !same(types, left.ret, right.ret, assumed) {
return false;
}
match (left.prototyped, right.prototyped) {
(true, true) => {
left.variadic == right.variadic
&& left.params.len() == right.params.len()
&& left.params.iter().zip(&right.params).all(|(&a, &b)| same(types, a, b, assumed))
}
(true, false) => stands_for(types, left),
(false, true) => stands_for(types, right),
(false, false) => true,
}
}
fn stands_for(types: &Types, signature: &FunctionType) -> bool {
!signature.variadic && signature.params.iter().all(|¶m| survives_promotion(types, param))
}
fn survives_promotion(types: &Types, id: TypeId) -> bool {
match types.kind(types.canonical(id)) {
TypeKind::Bool => false,
TypeKind::Int(kind) => kind.rank() >= IntKind::Int.rank(),
TypeKind::Float(FloatKind::Float) => false,
TypeKind::Enum(id) => match types.enum_info(id).underlying {
Some(underlying) => survives_promotion(types, underlying),
None => false,
},
_ => true,
}
}
fn records(
types: &Types,
left: RecordId,
right: RecordId,
assumed: &mut Vec<(RecordId, RecordId)>,
) -> bool {
if left == right || assumed.contains(&(left, right)) {
return true;
}
let a = types.record_info(left);
let b = types.record_info(right);
if a.kind != b.kind || a.tag.is_none() || a.tag != b.tag {
return false;
}
if a.layout.is_none() || b.layout.is_none() || a.fields.len() != b.fields.len() {
return false;
}
assumed.push((left, right));
let answer = a
.fields
.iter()
.zip(&b.fields)
.all(|(x, y)| x.name == y.name && x.bits == y.bits && same(types, x.ty, y.ty, assumed));
assumed.pop();
answer
}
fn build(types: &mut Types, left: TypeId, right: TypeId) -> TypeId {
if left == right {
return left;
}
let canonical = types.canonical(left);
match (types.kind(canonical), types.kind(types.canonical(right))) {
(TypeKind::Array { elem: a, len: x }, TypeKind::Array { elem: b, len: y }) => {
let elem = build(types, a, b);
let len = if matches!(x, ArrayLen::Fixed(_)) { x } else { y };
types.array(elem, len)
}
(TypeKind::Pointer(a), TypeKind::Pointer(b)) => {
let inner = build(types, a, b);
let quals = types.quals(canonical);
let pointer = types.pointer(inner);
types.qualified(pointer, quals)
}
(TypeKind::Function(a), TypeKind::Function(b)) => {
let a = types.signature(a).clone();
let b = types.signature(b).clone();
composite_function(types, &a, &b)
}
_ => left,
}
}
fn composite_function(types: &mut Types, left: &FunctionType, right: &FunctionType) -> TypeId {
let ret = build(types, left.ret, right.ret);
let (params, variadic, prototyped) = match (left.prototyped, right.prototyped) {
(true, true) => {
let params =
left.params.iter().zip(&right.params).map(|(&a, &b)| build(types, a, b)).collect();
(params, left.variadic, true)
}
(true, false) => (left.params.clone(), left.variadic, true),
(false, true) => (right.params.clone(), right.variadic, true),
(false, false) => (Vec::new(), false, false),
};
types.function(FunctionType { ret, params, variadic, prototyped })
}