use mun_hir::{
FloatBitness, HirDatabase, HirDisplay, IntBitness, ResolveBitness, Signedness, Ty, TyKind,
};
use crate::{
ir::IsIrType,
type_info::{HasStaticTypeId, TypeId, TypeIdData},
};
use inkwell::types::AnyTypeEnum;
use inkwell::{
context::Context,
targets::TargetData,
types::FunctionType,
types::PointerType,
types::{BasicType, BasicTypeEnum, FloatType, IntType, StructType},
AddressSpace,
};
use smallvec::SmallVec;
use std::{cell::RefCell, collections::HashMap, sync::Arc};
use mun_abi::Guid;
pub struct HirTypeCache<'db, 'ink> {
context: &'ink Context,
db: &'db dyn HirDatabase,
target_data: TargetData,
types: RefCell<HashMap<mun_hir::TyKind, StructType<'ink>>>,
array_ty_to_type_id: RefCell<HashMap<mun_hir::TyKind, Arc<TypeId>>>,
struct_to_type_id: RefCell<HashMap<mun_hir::Struct, Arc<TypeId>>>,
}
impl<'db, 'ink> HirTypeCache<'db, 'ink> {
pub fn new(context: &'ink Context, db: &'db dyn HirDatabase, target_data: TargetData) -> Self {
Self {
context,
db,
target_data,
types: RefCell::new(HashMap::default()),
struct_to_type_id: Default::default(),
array_ty_to_type_id: Default::default(),
}
}
pub fn get_float_type(&self, ty: mun_hir::FloatTy) -> FloatType<'ink> {
match ty.bitness {
FloatBitness::X64 => self.context.f64_type(),
FloatBitness::X32 => self.context.f32_type(),
}
}
pub fn get_int_type(&self, ty: mun_hir::IntTy) -> IntType<'ink> {
match ty.bitness {
IntBitness::X128 => self.context.i128_type(),
IntBitness::X64 => self.context.i64_type(),
IntBitness::X32 => self.context.i32_type(),
IntBitness::X16 => self.context.i16_type(),
IntBitness::X8 => self.context.i8_type(),
IntBitness::Xsize => self.get_usize_type(),
}
}
pub fn get_bool_type(&self) -> IntType<'ink> {
self.context.bool_type()
}
pub fn get_usize_type(&self) -> IntType<'ink> {
usize::ir_type(self.context, &self.target_data)
}
pub fn get_struct_type(&self, struct_ty: mun_hir::Struct) -> StructType<'ink> {
let ty = TyKind::Struct(struct_ty);
if let Some(ir_ty) = self.types.borrow().get(&ty) {
return *ir_ty;
};
let ir_ty = self
.context
.opaque_struct_type(&struct_ty.name(self.db).to_string());
self.types.borrow_mut().insert(ty, ir_ty);
let field_types: Vec<_> = struct_ty
.fields(self.db)
.into_iter()
.map(|field| field.ty(self.db))
.map(|ty| {
self.get_basic_type(&ty)
.expect("could not convert struct field to basic type")
})
.collect();
ir_ty.set_body(&field_types, false);
ir_ty
}
pub fn get_array_type(&self, element_ty: &Ty) -> StructType<'ink> {
let ty = TyKind::Array(element_ty.clone());
if let Some(ir_ty) = self.types.borrow().get(&ty) {
return *ir_ty;
};
let ir_ty = self
.context
.opaque_struct_type(&format!("[{}]", element_ty.display(self.db)));
self.types.borrow_mut().insert(ty, ir_ty);
let length_ir_type = self.context.ptr_sized_int_type(&self.target_data, None);
let capacity_ir_type = self.context.ptr_sized_int_type(&self.target_data, None);
let element_ir_type = self
.get_basic_type(element_ty)
.expect("could not convert array element type to basic type");
ir_ty.set_body(
&[
length_ir_type.into(),
capacity_ir_type.into(),
element_ir_type,
],
false,
);
ir_ty
}
pub fn get_array_reference_type(&self, element_ty: &Ty) -> PointerType<'ink> {
let ir_ty = self.get_array_type(element_ty);
ir_ty
.ptr_type(AddressSpace::Generic)
.ptr_type(AddressSpace::Generic)
}
pub fn get_struct_reference_type(&self, struct_ty: mun_hir::Struct) -> BasicTypeEnum<'ink> {
let ir_ty = self.get_struct_type(struct_ty);
match struct_ty.data(self.db.upcast()).memory_kind {
mun_hir::StructMemoryKind::Gc => {
ir_ty
.ptr_type(AddressSpace::Generic)
.ptr_type(AddressSpace::Generic)
.into()
}
mun_hir::StructMemoryKind::Value => {
ir_ty.into()
}
}
}
pub fn get_public_struct_reference_type(
&self,
struct_ty: mun_hir::Struct,
) -> BasicTypeEnum<'ink> {
let ir_ty = self.get_struct_type(struct_ty);
ir_ty
.ptr_type(AddressSpace::Generic)
.ptr_type(AddressSpace::Generic)
.into()
}
pub fn get_function_type(&self, ty: mun_hir::Function) -> FunctionType<'ink> {
let ty = self.db.callable_sig(ty.into());
let param_tys: Vec<_> = ty
.params()
.iter()
.map(|p| {
self.get_basic_type(p)
.expect("could not convert function argument to basic type")
.into()
})
.collect();
let return_type = ty.ret();
match return_type.interned() {
TyKind::Tuple(0, _) => self.context.void_type().fn_type(¶m_tys, false),
_ => self
.get_basic_type(return_type)
.expect("could not convert return value")
.fn_type(¶m_tys, false),
}
}
pub fn get_public_function_type(&self, ty: mun_hir::Function) -> FunctionType<'ink> {
let ty = self.db.callable_sig(ty.into());
let param_tys: Vec<_> = ty
.params()
.iter()
.map(|p| {
self.get_public_basic_type(p)
.expect("could not convert function argument to public basic type")
.into()
})
.collect();
let return_type = ty.ret();
match return_type.interned() {
TyKind::Tuple(0, _) => self.context.void_type().fn_type(¶m_tys, false),
_ => self
.get_public_basic_type(return_type)
.expect("could not convert return value")
.fn_type(¶m_tys, false),
}
}
pub fn get_basic_type(&self, ty: &mun_hir::Ty) -> Option<BasicTypeEnum<'ink>> {
match ty.interned() {
TyKind::Tuple(_, substs) => Some(self.get_tuple_type(substs).into()),
TyKind::Float(float_ty) => Some(self.get_float_type(*float_ty).into()),
TyKind::Int(int_ty) => Some(self.get_int_type(*int_ty).into()),
TyKind::Struct(struct_ty) => Some(self.get_struct_reference_type(*struct_ty)),
TyKind::Bool => Some(self.get_bool_type().into()),
TyKind::Array(element_ty) => Some(self.get_array_reference_type(element_ty).into()),
_ => None,
}
}
pub fn get_public_basic_type(&self, ty: &mun_hir::Ty) -> Option<BasicTypeEnum<'ink>> {
match ty.interned() {
TyKind::Tuple(_, substs) => Some(self.get_tuple_type(substs).into()),
TyKind::Float(float_ty) => Some(self.get_float_type(*float_ty).into()),
TyKind::Int(int_ty) => Some(self.get_int_type(*int_ty).into()),
TyKind::Struct(struct_ty) => Some(self.get_public_struct_reference_type(*struct_ty)),
TyKind::Bool => Some(self.get_bool_type().into()),
TyKind::Array(element_ty) => Some(self.get_array_reference_type(element_ty).into()),
_ => None,
}
}
pub fn get_any_type(&self, ty: &mun_hir::Ty) -> Option<AnyTypeEnum<'ink>> {
match ty.interned() {
TyKind::Tuple(_, substs) => Some(self.get_tuple_type(substs).into()),
TyKind::Float(float_ty) => Some(self.get_float_type(*float_ty).into()),
TyKind::Int(int_ty) => Some(self.get_int_type(*int_ty).into()),
TyKind::Struct(struct_ty) => Some(self.get_struct_type(*struct_ty).into()),
TyKind::FnDef(mun_hir::CallableDef::Function(fn_ty), type_params) => {
if !type_params.is_empty() {
unimplemented!("cannot yet deal with type parameters in functions");
}
Some(self.get_function_type(*fn_ty).into())
}
TyKind::Bool => Some(self.get_bool_type().into()),
TyKind::Array(element_ty) => Some(self.get_array_reference_type(element_ty).into()),
_ => None,
}
}
pub fn get_empty_type(&self) -> StructType<'ink> {
self.context.struct_type(&[], false)
}
pub fn get_tuple_type(&self, type_params: &[Ty]) -> StructType<'ink> {
let mut tuple_ir_types: SmallVec<[BasicTypeEnum<'ink>; 2]> =
SmallVec::with_capacity(type_params.len());
for ty in type_params {
tuple_ir_types.push(
self.get_basic_type(ty)
.expect("tuple type should be a basic type"),
)
}
self.context.struct_type(&tuple_ir_types, false)
}
pub fn type_id(&self, ty: &Ty) -> Arc<TypeId> {
match ty.interned() {
&TyKind::Float(ty) => {
let resolved_ty = ty.resolve(&self.db.target_data_layout());
match resolved_ty.bitness {
FloatBitness::X32 => f32::type_id().clone(),
FloatBitness::X64 => f64::type_id().clone(),
}
}
&TyKind::Int(ty) => {
let resolved_ty = ty.resolve(&self.db.target_data_layout());
match (resolved_ty.signedness, resolved_ty.bitness) {
(Signedness::Signed, IntBitness::X8) => i8::type_id().clone(),
(Signedness::Signed, IntBitness::X16) => i16::type_id().clone(),
(Signedness::Signed, IntBitness::X32) => i32::type_id().clone(),
(Signedness::Signed, IntBitness::X64) => i64::type_id().clone(),
(Signedness::Signed, IntBitness::X128) => i128::type_id().clone(),
(Signedness::Unsigned, IntBitness::X8) => u8::type_id().clone(),
(Signedness::Unsigned, IntBitness::X16) => u16::type_id().clone(),
(Signedness::Unsigned, IntBitness::X32) => u32::type_id().clone(),
(Signedness::Unsigned, IntBitness::X64) => u64::type_id().clone(),
(Signedness::Unsigned, IntBitness::X128) => u128::type_id().clone(),
(_, IntBitness::Xsize) => unreachable!(
"after resolve there should no longer be an undefined size type"
),
}
}
TyKind::Bool => bool::type_id().clone(),
&TyKind::Struct(s) => self
.struct_to_type_id
.borrow_mut()
.entry(s)
.or_insert_with(|| {
Arc::new(TypeId {
name: s.full_name(self.db),
data: TypeIdData::Concrete(guid_from_struct(self.db, s)),
})
})
.clone(),
TyKind::Array(a) => {
{
let read_only = self.array_ty_to_type_id.borrow();
if let Some(a) = read_only.get(a.interned()) {
return a.clone();
}
}
let element_type_id = self.type_id(a);
let array_type_id = Arc::new(TypeId {
name: format!("[{}]", &element_type_id.name),
data: TypeIdData::Array(element_type_id),
});
let previous_entry = self
.array_ty_to_type_id
.borrow_mut()
.insert(a.interned().clone(), array_type_id.clone());
if previous_entry.is_some() {
panic!("array cyclic reference?");
}
array_type_id
}
_ => unimplemented!("{} unhandled", ty.display(self.db)),
}
}
}
pub fn guid_from_struct(db: &dyn HirDatabase, s: mun_hir::Struct) -> Guid {
let name = s.full_name(db);
let fields: Vec<String> = s
.fields(db)
.into_iter()
.map(|f| {
let ty_string = f
.ty(db)
.guid_string(db)
.expect("type should be convertible to a string");
format!("{}: {}", f.name(db), ty_string)
})
.collect();
Guid::from_str(&format!(
"struct {name}{{{fields}}}",
name = &name,
fields = fields.join(",")
))
}