use indexmap::IndexSet;
use crate::{
ast::Identifier,
runtime::{
Rt,
layout::{Layout, LayoutBuilder},
},
typechecker::{
info::TypeInfo,
scoped_display::TypeDisplay,
types::{FloatSize, IntKind, IntSize, Primitive},
},
value::ErasedList,
};
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum Ty {
Unit,
Never,
Record(Vec<(Identifier, TyRef)>),
Enum(Vec<(Identifier, Vec<TyRef>)>),
Primitive(Primitive),
List(TyRef),
Runtime(std::any::TypeId),
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Signature {
pub parameter_types: Vec<TyRef>,
pub return_type: TyRef,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct TyRef(usize);
impl TyRef {
pub fn type_id(self) -> usize {
self.0
}
}
impl TyRef {
pub const UNIT: Self = Self(0);
pub const NEVER: Self = Self(1);
pub const BOOL: Self = Self(2);
pub const U8: Self = Self(3);
pub const U16: Self = Self(4);
pub const U32: Self = Self(5);
pub const U64: Self = Self(6);
pub const I8: Self = Self(7);
pub const I16: Self = Self(8);
pub const I32: Self = Self(9);
pub const I64: Self = Self(10);
pub const F32: Self = Self(11);
pub const F64: Self = Self(12);
pub const STRING: Self = Self(13);
}
#[derive(Clone)]
pub struct Pool {
types: IndexSet<Ty>,
}
impl Default for Pool {
fn default() -> Self {
Self::new()
}
}
impl Pool {
pub fn new() -> Self {
let mut this = Self {
types: IndexSet::new(),
};
let actual_val = this.intern(Ty::Unit);
assert_eq!(TyRef::UNIT, actual_val);
let actual_val = this.intern(Ty::Never);
assert_eq!(TyRef::NEVER, actual_val);
for (expected_val, primitive) in [
(TyRef::BOOL, Primitive::Bool),
(TyRef::U8, Primitive::Int(IntKind::Unsigned, IntSize::I8)),
(TyRef::U16, Primitive::Int(IntKind::Unsigned, IntSize::I16)),
(TyRef::U32, Primitive::Int(IntKind::Unsigned, IntSize::I32)),
(TyRef::U64, Primitive::Int(IntKind::Unsigned, IntSize::I64)),
(TyRef::I8, Primitive::Int(IntKind::Signed, IntSize::I8)),
(TyRef::I16, Primitive::Int(IntKind::Signed, IntSize::I16)),
(TyRef::I32, Primitive::Int(IntKind::Signed, IntSize::I32)),
(TyRef::I64, Primitive::Int(IntKind::Signed, IntSize::I64)),
(TyRef::F32, Primitive::Float(FloatSize::F32)),
(TyRef::F64, Primitive::Float(FloatSize::F64)),
(TyRef::STRING, Primitive::String),
] {
let actual_val = this.intern(Ty::Primitive(primitive));
assert_eq!(expected_val, actual_val);
}
this
}
pub(crate) fn layout_of(&self, ty: TyRef, rt: &Rt) -> Option<Layout> {
let layout = match self.get(ty) {
Ty::Never => return None,
Ty::Unit => Layout::new(0, 1),
Ty::Primitive(primitive) => primitive.layout(),
Ty::Runtime(type_id) => {
rt.get_runtime_type(*type_id).unwrap().layout()
}
Ty::Record(fields) => {
let mut builder = LayoutBuilder::new();
for &(_, t) in fields {
builder.add(&self.layout_of(t, rt)?);
}
builder.finish()
}
Ty::Enum(variants) => {
let mut layout = None;
for (_, fields) in variants {
let mut builder = LayoutBuilder::new();
builder.add(&Layout::of::<u8>());
let builder =
fields.iter().try_fold(builder, |mut b, t| {
let layout = self.layout_of(*t, rt)?;
b.add(&layout);
Some(b)
});
let Some(builder) = builder else {
continue;
};
let variant_layout = builder.finish();
layout = Some(
layout.map_or(variant_layout.clone(), |l: Layout| {
l.union(&variant_layout)
}),
);
}
layout?
}
Ty::List(_) => Layout::of::<ErasedList>(),
};
Some(layout)
}
pub(crate) fn is_reference_type(
&self,
ty: TyRef,
rt: &Rt,
) -> Option<bool> {
if self.layout_of(ty, rt)?.size() == 0 {
return Some(false);
}
let res = match self.get(ty) {
Ty::Never => return None,
Ty::Record(_) => true,
Ty::Enum(_) => true,
Ty::Primitive(
Primitive::String | Primitive::IpAddr | Primitive::Prefix,
) => true,
Ty::List(_) => true,
Ty::Runtime(_) => true,
Ty::Unit => false,
Ty::Primitive(
Primitive::Int(..)
| Primitive::Float(..)
| Primitive::Bool
| Primitive::Char
| Primitive::Asn,
) => false,
};
Some(res)
}
pub fn intern(&mut self, ty: Ty) -> TyRef {
TyRef(self.types.insert_full(ty).0)
}
pub fn get(&self, ty_ref: TyRef) -> &Ty {
&self.types[ty_ref.0]
}
}
impl TypeDisplay for TyRef {
fn fmt(
&self,
type_info: &TypeInfo,
f: &mut core::fmt::Formatter<'_>,
) -> core::fmt::Result {
let ty = type_info.ty_pool.get(*self);
ty.fmt(type_info, f)
}
}
impl TypeDisplay for Ty {
fn fmt(
&self,
type_info: &TypeInfo,
f: &mut core::fmt::Formatter<'_>,
) -> core::fmt::Result {
match self {
Ty::Unit => f.write_str("()")?,
Ty::Never => f.write_str("!")?,
Ty::Record(fields) => {
f.write_str("{")?;
let mut first = true;
for (field_name, field_ty) in fields {
if !first {
f.write_str(", ")?;
}
f.write_str(field_name.as_str())?;
f.write_str(": ")?;
field_ty.fmt(type_info, f)?;
first = false;
}
f.write_str("}")?;
}
Ty::Enum(variants) => {
f.write_str("enum { ")?;
let mut first_variant = true;
for (identifier, ty_refs) in variants {
if !first_variant {
f.write_str(", ")?;
}
identifier.fmt(type_info, f)?;
f.write_str("(")?;
let mut first_field = true;
for field in ty_refs {
if !first_field {
f.write_str(", ")?;
}
field.fmt(type_info, f)?;
first_field = false;
}
f.write_str(")")?;
first_variant = false;
}
f.write_str(" }")?;
}
Ty::Primitive(primitive) => primitive.fmt(type_info, f)?,
Ty::List(inner) => {
f.write_str("List[")?;
inner.fmt(type_info, f)?;
f.write_str("]")?;
}
Ty::Runtime(type_id) => {
write!(f, "runtime({:?})", type_id)?;
}
}
Ok(())
}
}