use formalang::ast::PrimitiveType;
use formalang::ir::ResolvedType;
use thiserror::Error;
use wasm_encoder::ValType;
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum TypeMapError {
#[error("type {kind} is not yet supported by the backend")]
NotYetSupported {
kind: String,
},
}
pub fn primitive_value_type(p: PrimitiveType) -> Result<Option<ValType>, TypeMapError> {
match p {
PrimitiveType::I32 | PrimitiveType::Boolean => Ok(Some(ValType::I32)),
PrimitiveType::I64 => Ok(Some(ValType::I64)),
PrimitiveType::F32 => Ok(Some(ValType::F32)),
PrimitiveType::F64 => Ok(Some(ValType::F64)),
PrimitiveType::Never => Ok(None),
PrimitiveType::String | PrimitiveType::Path | PrimitiveType::Regex => {
Ok(Some(ValType::I32))
}
_ => Err(TypeMapError::NotYetSupported {
kind: format!("{p:?}"),
}),
}
}
pub fn resolved_value_type(ty: &ResolvedType) -> Result<Option<ValType>, TypeMapError> {
match ty {
ResolvedType::Primitive(p) => primitive_value_type(*p),
ResolvedType::Struct(_) => Err(TypeMapError::NotYetSupported {
kind: "Struct".to_owned(),
}),
ResolvedType::Trait(_) => Err(TypeMapError::NotYetSupported {
kind: "Trait".to_owned(),
}),
ResolvedType::Enum(_) => Err(TypeMapError::NotYetSupported {
kind: "Enum".to_owned(),
}),
ResolvedType::Tuple(_) => Err(TypeMapError::NotYetSupported {
kind: "Tuple".to_owned(),
}),
ResolvedType::Generic { .. } => Err(TypeMapError::NotYetSupported {
kind: "Generic".to_owned(),
}),
ResolvedType::TypeParam(name) => Err(TypeMapError::NotYetSupported {
kind: format!("TypeParam({name})"),
}),
ResolvedType::External { name, .. } => Err(TypeMapError::NotYetSupported {
kind: format!(
"External({name}) — should have been inlined by upstream MonomorphisePass; reaching the backend means an upstream invariant violation"
),
}),
ResolvedType::Closure { .. } => Err(TypeMapError::NotYetSupported {
kind: "Closure".to_owned(),
}),
ResolvedType::Error => Err(TypeMapError::NotYetSupported {
kind: "Error".to_owned(),
}),
}
}
pub const CLOSURE_VALUE_SIZE: u32 = 8;
pub const CLOSURE_VALUE_ALIGN: u32 = 4;
pub const CLOSURE_FUNCREF_OFFSET: u32 = 0;
pub const CLOSURE_ENV_OFFSET: u32 = 4;
pub fn result_types(return_ty: Option<&ResolvedType>) -> Result<Vec<ValType>, TypeMapError> {
let Some(ty) = return_ty else {
return Ok(Vec::new());
};
Ok(resolved_value_type(ty)?.map_or_else(Vec::new, |vt| vec![vt]))
}
pub fn body_result_types(return_ty: Option<&ResolvedType>) -> Result<Vec<ValType>, TypeMapError> {
let Some(ty) = return_ty else {
return Ok(Vec::new());
};
Ok(body_value_type(ty)?.map_or_else(Vec::new, |vt| vec![vt]))
}
pub fn body_value_type(ty: &ResolvedType) -> Result<Option<ValType>, TypeMapError> {
match ty {
ResolvedType::Primitive(p) => primitive_value_type(*p),
ResolvedType::Struct(_)
| ResolvedType::Tuple(_)
| ResolvedType::Enum(_)
| ResolvedType::Generic { .. }
| ResolvedType::Closure { .. }
| ResolvedType::Trait(_) => Ok(Some(ValType::I32)),
ResolvedType::TypeParam(name) => Err(TypeMapError::NotYetSupported {
kind: format!("TypeParam({name})"),
}),
ResolvedType::External { name, .. } => Err(TypeMapError::NotYetSupported {
kind: format!(
"External({name}) — should have been inlined by upstream MonomorphisePass; reaching the backend means an upstream invariant violation"
),
}),
ResolvedType::Error => Err(TypeMapError::NotYetSupported {
kind: "Error".to_owned(),
}),
}
}