use std::sync::{
Mutex, RwLock, RwLockReadGuard, RwLockWriteGuard,
atomic::{AtomicPtr, Ordering},
};
use rustc_hash::FxHashMap as HashMap;
use crate::{
space::MemorySpaceId,
value::{
FunctionId,
insn::{Binop, IntBinop},
},
};
#[derive(
Copy, Clone, Hash, Eq, PartialEq, Debug, Ord, PartialOrd, serde::Serialize, serde::Deserialize,
)]
pub struct TypeId(u32);
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum TypeRequest {
Aggregate { fields: Vec<AggregateField> },
StructPointer { size: usize, pointee: TypeId },
Array { elem: TypeId, count: usize },
List { elem: TypeId, bound: Option<usize> },
}
impl TypeRequest {
pub fn aggregate(fields: Vec<AggregateField>) -> Self {
Self::Aggregate { fields }
}
pub const fn struct_pointer(size: usize, pointee: TypeId) -> Self {
Self::StructPointer { size, pointee }
}
pub const fn array(elem: TypeId, count: usize) -> Self {
Self::Array { elem, count }
}
pub const fn list(elem: TypeId, bound: Option<usize>) -> Self {
Self::List { elem, bound }
}
}
pub trait Type: Send + Sync {
fn size(&self) -> usize;
fn space(&self) -> Option<MemorySpaceId> {
None
}
fn fields(&self) -> Option<&[AggregateField]> {
None
}
fn struct_name(&self) -> Option<&str> {
None
}
fn function_return_owner(&self) -> Option<FunctionId> {
None
}
fn pointee(&self) -> Option<TypeId> {
None
}
fn array(&self) -> Option<(TypeId, usize)> {
None
}
fn list(&self) -> Option<(TypeId, Option<usize>)> {
None
}
fn clone_box(&self) -> Box<dyn Type>;
fn repr(&self) -> TypeRepr;
}
#[derive(Clone, serde::Serialize, serde::Deserialize)]
pub enum TypeRepr {
Int {
size: usize,
},
Bool,
SpaceAddress {
size: usize,
space: MemorySpaceId,
},
Aggregate {
fields: Vec<AggregateField>,
},
Struct {
name: String,
size: usize,
fields: Vec<AggregateField>,
},
StructPointer {
size: usize,
pointee: TypeId,
},
Array {
elem: TypeId,
count: usize,
},
List {
elem: TypeId,
bound: Option<usize>,
},
FunctionReturn {
owner: FunctionId,
fields: Vec<AggregateField>,
},
CodePointer {
size: usize,
},
}
#[derive(Clone, Debug, Hash, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct AggregateField {
pub name: String,
pub type_id: TypeId,
pub offset: usize,
}
impl AggregateField {
pub fn new(name: impl Into<String>, type_id: TypeId) -> Self {
Self::new_at(name, type_id, 0)
}
pub fn new_at(name: impl Into<String>, type_id: TypeId, offset: usize) -> Self {
Self {
name: name.into(),
type_id,
offset,
}
}
}
impl Clone for Box<dyn Type> {
fn clone(&self) -> Self {
self.clone_box()
}
}
#[derive(Clone)]
struct IntType {
size: usize,
}
impl Type for IntType {
fn size(&self) -> usize {
self.size
}
fn clone_box(&self) -> Box<dyn Type> {
Box::new(self.clone())
}
fn repr(&self) -> TypeRepr {
TypeRepr::Int { size: self.size }
}
}
#[derive(Clone)]
struct BoolType;
impl Type for BoolType {
fn size(&self) -> usize {
1
}
fn clone_box(&self) -> Box<dyn Type> {
Box::new(self.clone())
}
fn repr(&self) -> TypeRepr {
TypeRepr::Bool
}
}
#[derive(Clone)]
pub struct SpaceAddress {
size: usize,
space: MemorySpaceId,
}
impl Type for SpaceAddress {
fn size(&self) -> usize {
self.size
}
fn space(&self) -> Option<MemorySpaceId> {
Some(self.space)
}
fn clone_box(&self) -> Box<dyn Type> {
Box::new(self.clone())
}
fn repr(&self) -> TypeRepr {
TypeRepr::SpaceAddress {
size: self.size,
space: self.space,
}
}
}
#[derive(Clone)]
struct AggregateType {
fields: Vec<AggregateField>,
size: usize,
}
#[derive(Clone)]
struct FunctionReturnType {
owner: FunctionId,
fields: Vec<AggregateField>,
size: usize,
}
impl Type for FunctionReturnType {
fn size(&self) -> usize {
self.size
}
fn fields(&self) -> Option<&[AggregateField]> {
Some(&self.fields)
}
fn function_return_owner(&self) -> Option<FunctionId> {
Some(self.owner)
}
fn clone_box(&self) -> Box<dyn Type> {
Box::new(self.clone())
}
fn repr(&self) -> TypeRepr {
TypeRepr::FunctionReturn {
owner: self.owner,
fields: self.fields.clone(),
}
}
}
impl Type for AggregateType {
fn size(&self) -> usize {
self.size
}
fn fields(&self) -> Option<&[AggregateField]> {
Some(&self.fields)
}
fn clone_box(&self) -> Box<dyn Type> {
Box::new(self.clone())
}
fn repr(&self) -> TypeRepr {
TypeRepr::Aggregate {
fields: self.fields.clone(),
}
}
}
#[derive(Clone)]
struct StructType {
name: String,
fields: Vec<AggregateField>,
size: usize,
}
impl Type for StructType {
fn size(&self) -> usize {
self.size
}
fn fields(&self) -> Option<&[AggregateField]> {
Some(&self.fields)
}
fn struct_name(&self) -> Option<&str> {
Some(&self.name)
}
fn clone_box(&self) -> Box<dyn Type> {
Box::new(self.clone())
}
fn repr(&self) -> TypeRepr {
TypeRepr::Struct {
name: self.name.clone(),
size: self.size,
fields: self.fields.clone(),
}
}
}
#[derive(Clone)]
struct StructPointer {
size: usize,
pointee: TypeId,
}
impl Type for StructPointer {
fn size(&self) -> usize {
self.size
}
fn pointee(&self) -> Option<TypeId> {
Some(self.pointee)
}
fn clone_box(&self) -> Box<dyn Type> {
Box::new(self.clone())
}
fn repr(&self) -> TypeRepr {
TypeRepr::StructPointer {
size: self.size,
pointee: self.pointee,
}
}
}
#[derive(Clone)]
struct CodePointerType {
size: usize,
}
impl Type for CodePointerType {
fn size(&self) -> usize {
self.size
}
fn clone_box(&self) -> Box<dyn Type> {
Box::new(self.clone())
}
fn repr(&self) -> TypeRepr {
TypeRepr::CodePointer { size: self.size }
}
}
#[derive(Clone)]
struct ArrayType {
elem: TypeId,
count: usize,
size: usize,
}
impl Type for ArrayType {
fn size(&self) -> usize {
self.size
}
fn array(&self) -> Option<(TypeId, usize)> {
Some((self.elem, self.count))
}
fn clone_box(&self) -> Box<dyn Type> {
Box::new(self.clone())
}
fn repr(&self) -> TypeRepr {
TypeRepr::Array {
elem: self.elem,
count: self.count,
}
}
}
#[derive(Clone)]
struct ListType {
elem: TypeId,
bound: Option<usize>,
size: usize,
}
impl Type for ListType {
fn size(&self) -> usize {
self.size
}
fn list(&self) -> Option<(TypeId, Option<usize>)> {
Some((self.elem, self.bound))
}
fn clone_box(&self) -> Box<dyn Type> {
Box::new(self.clone())
}
fn repr(&self) -> TypeRepr {
TypeRepr::List {
elem: self.elem,
bound: self.bound,
}
}
}
fn default_named_fields(fields: Vec<TypeId>) -> Vec<AggregateField> {
fields
.into_iter()
.enumerate()
.map(|(i, type_id)| AggregateField::new(format!("field{}", i + 1), type_id))
.collect()
}
fn validate_unique_fields(fields: &[AggregateField]) -> Result<(), String> {
for (i, field) in fields.iter().enumerate() {
if fields[..i]
.iter()
.any(|previous| previous.name == field.name)
{
return Err(format!(
"aggregate field names must be unique; duplicate `{}`",
field.name
));
}
}
Ok(())
}
#[derive(Clone)]
struct TypeManagerInner {
types: Vec<Box<dyn Type>>,
retired_types: Vec<Box<dyn Type>>,
int_by_size: HashMap<usize, TypeId>,
bool_id: Option<TypeId>,
space_address: HashMap<(usize, MemorySpaceId), TypeId>,
aggregate_by_fields: HashMap<Vec<AggregateField>, TypeId>,
function_return: HashMap<FunctionId, TypeId>,
struct_by_name: HashMap<String, TypeId>,
struct_pointer: HashMap<(usize, TypeId), TypeId>,
code_pointer: HashMap<usize, TypeId>,
array_by_elem_count: HashMap<(TypeId, usize), TypeId>,
list_by_elem_bound: HashMap<(TypeId, Option<usize>), TypeId>,
}
impl Default for TypeManagerInner {
fn default() -> Self {
Self::new()
}
}
impl TypeManagerInner {
fn new() -> Self {
Self {
types: Vec::new(),
retired_types: Vec::new(),
int_by_size: HashMap::default(),
bool_id: None,
space_address: HashMap::default(),
aggregate_by_fields: HashMap::default(),
function_return: HashMap::default(),
struct_by_name: HashMap::default(),
struct_pointer: HashMap::default(),
array_by_elem_count: HashMap::default(),
list_by_elem_bound: HashMap::default(),
code_pointer: HashMap::default(),
}
}
fn register(&mut self, ty: Box<dyn Type>) -> TypeId {
let id = TypeId(self.types.len() as u32);
self.types.push(ty);
id
}
pub fn get_or_make_int(&mut self, size: usize) -> TypeId {
if let Some(&id) = self.int_by_size.get(&size) {
return id;
}
let id = self.register(Box::new(IntType { size }));
self.int_by_size.insert(size, id);
id
}
pub fn get_or_make_code_pointer(&mut self, size: usize) -> TypeId {
if let Some(&id) = self.code_pointer.get(&size) {
return id;
}
let id = self.register(Box::new(CodePointerType { size }));
self.code_pointer.insert(size, id);
id
}
pub fn get_or_make_bool(&mut self) -> TypeId {
if let Some(id) = self.bool_id {
return id;
}
let id = self.register(Box::new(BoolType));
self.bool_id = Some(id);
id
}
pub fn bool_id(&self) -> Option<TypeId> {
self.bool_id
}
pub fn is_bool(&self, id: TypeId) -> bool {
matches!(self.get(id).repr(), TypeRepr::Bool)
}
pub fn get_or_make_space_address(&mut self, size: usize, space: MemorySpaceId) -> TypeId {
if let Some(&id) = self.space_address.get(&(size, space)) {
return id;
}
let id = self.register(Box::new(SpaceAddress { size, space }));
self.space_address.insert((size, space), id);
id
}
pub fn get_or_make_named_aggregate(&mut self, fields: Vec<AggregateField>) -> TypeId {
validate_unique_fields(&fields).expect("aggregate field names must be unique");
if let Some(&id) = self.aggregate_by_fields.get(&fields) {
return id;
}
let size = fields.iter().map(|f| self.size_of(f.type_id)).sum();
let id = self.register(Box::new(AggregateType {
fields: fields.clone(),
size,
}));
self.aggregate_by_fields.insert(fields, id);
id
}
fn create_function_return(
&mut self,
owner: FunctionId,
fields: Vec<AggregateField>,
) -> Result<TypeId, String> {
validate_unique_fields(&fields)?;
if let Some(&existing) = self.function_return.get(&owner) {
return Err(format!(
"function {owner:?} already owns return type {existing:?}"
));
}
let size = fields.iter().map(|field| self.size_of(field.type_id)).sum();
let id = self.register(Box::new(FunctionReturnType {
owner,
fields,
size,
}));
self.function_return.insert(owner, id);
Ok(id)
}
fn edit_function_return(
&mut self,
owner: FunctionId,
fields: Vec<AggregateField>,
) -> Result<TypeId, String> {
validate_unique_fields(&fields)?;
let id = self
.function_return
.get(&owner)
.copied()
.ok_or_else(|| format!("function {owner:?} has no owned return type"))?;
let size = fields.iter().map(|field| self.size_of(field.type_id)).sum();
let replacement: Box<dyn Type> = Box::new(FunctionReturnType {
owner,
fields,
size,
});
let old = std::mem::replace(&mut self.types[id.0 as usize], replacement);
self.retired_types.push(old);
Ok(id)
}
pub fn get_or_make_struct(
&mut self,
name: impl Into<String>,
size: usize,
fields: Vec<AggregateField>,
) -> TypeId {
let name = name.into();
if let Some(&id) = self.struct_by_name.get(&name) {
return id;
}
let id = self.register(Box::new(StructType {
name: name.clone(),
fields,
size,
}));
self.struct_by_name.insert(name, id);
id
}
pub fn struct_by_name(&self, name: &str) -> Option<TypeId> {
self.struct_by_name.get(name).copied()
}
pub fn get_or_make_struct_pointer(&mut self, size: usize, pointee: TypeId) -> TypeId {
if let Some(&id) = self.struct_pointer.get(&(size, pointee)) {
return id;
}
let id = self.register(Box::new(StructPointer { size, pointee }));
self.struct_pointer.insert((size, pointee), id);
id
}
pub fn get_or_make_array(&mut self, elem: TypeId, count: usize) -> TypeId {
if let Some(&id) = self.array_by_elem_count.get(&(elem, count)) {
return id;
}
let size = self.size_of(elem) * count;
let id = self.register(Box::new(ArrayType { elem, count, size }));
self.array_by_elem_count.insert((elem, count), id);
id
}
pub fn get_or_make_list(&mut self, elem: TypeId, bound: usize) -> TypeId {
self.get_or_make_list_opt(elem, Some(bound))
}
pub fn get_or_make_unbounded_list(&mut self, elem: TypeId) -> TypeId {
self.get_or_make_list_opt(elem, None)
}
fn get_or_make_list_opt(&mut self, elem: TypeId, bound: Option<usize>) -> TypeId {
if let Some(&id) = self.list_by_elem_bound.get(&(elem, bound)) {
return id;
}
let size = bound.map_or(0, |b| self.size_of(elem) * b);
let id = self.register(Box::new(ListType { elem, bound, size }));
self.list_by_elem_bound.insert((elem, bound), id);
id
}
pub fn get(&self, id: TypeId) -> &dyn Type {
&*self.types[id.0 as usize]
}
pub fn size_of(&self, id: TypeId) -> usize {
self.get(id).size()
}
pub fn binop_result(&mut self, lhs: TypeId, op: Binop, rhs: TypeId) -> TypeId {
if let Some(id) = self.binop_result_probe(lhs, op, rhs) {
id
} else {
self.get_or_make_bool()
}
}
fn binop_result_probe(&self, lhs: TypeId, op: Binop, _rhs: TypeId) -> Option<TypeId> {
match op {
Binop::Int(int_op) => match int_op {
IntBinop::Equal
| IntBinop::NotEqual
| IntBinop::Less
| IntBinop::LessEqual
| IntBinop::SLess
| IntBinop::SLessEqual => self.bool_id,
IntBinop::And | IntBinop::Or | IntBinop::Xor if self.is_bool(lhs) => Some(lhs),
_ => Some(lhs),
},
Binop::Float(float_op) => {
if float_op.is_comparison() {
self.bool_id
} else {
Some(lhs)
}
}
}
}
}
pub struct TypeManager {
inner: RwLock<TypeManagerInner>,
published: AtomicPtr<PublishedTypes>,
#[allow(clippy::vec_box)]
published_generations: Mutex<Vec<Box<PublishedTypes>>>,
}
struct PublishedTypes {
entries: Box<[*const dyn Type]>,
}
unsafe impl Send for PublishedTypes {}
unsafe impl Sync for PublishedTypes {}
impl PublishedTypes {
fn from_inner(inner: &TypeManagerInner) -> Self {
Self {
entries: inner
.types
.iter()
.map(|ty| &**ty as *const dyn Type)
.collect(),
}
}
}
impl Default for TypeManager {
fn default() -> Self {
Self::new()
}
}
impl Clone for TypeManager {
fn clone(&self) -> Self {
Self::from_inner(self.read().clone())
}
}
impl TypeManager {
pub fn new() -> Self {
Self::from_inner(TypeManagerInner::new())
}
fn from_inner(inner: TypeManagerInner) -> Self {
let generation = Box::new(PublishedTypes::from_inner(&inner));
let published = AtomicPtr::new((&*generation as *const PublishedTypes).cast_mut());
Self {
inner: RwLock::new(inner),
published,
published_generations: Mutex::new(vec![generation]),
}
}
fn read(&self) -> RwLockReadGuard<'_, TypeManagerInner> {
self.inner.read().expect("type manager RwLock poisoned")
}
fn write(&self) -> RwLockWriteGuard<'_, TypeManagerInner> {
self.inner.write().expect("type manager RwLock poisoned")
}
fn publish(&self, inner: &TypeManagerInner) {
let generation = Box::new(PublishedTypes::from_inner(inner));
let ptr = (&*generation as *const PublishedTypes).cast_mut();
self.published_generations
.lock()
.expect("type publication generation lock poisoned")
.push(generation);
self.published.store(ptr, Ordering::Release);
}
fn publish_exclusive(&mut self) {
let generation = {
let inner = self.inner.get_mut().expect("type manager RwLock poisoned");
Box::new(PublishedTypes::from_inner(inner))
};
let ptr = (&*generation as *const PublishedTypes).cast_mut();
self.published_generations
.get_mut()
.expect("type publication generation lock poisoned")
.push(generation);
self.published.store(ptr, Ordering::Release);
}
fn published(&self) -> &PublishedTypes {
let ptr = self.published.load(Ordering::Acquire);
debug_assert!(!ptr.is_null(), "type publication pointer is null");
unsafe { &*ptr }
}
fn mint(&self, f: impl FnOnce(&mut TypeManagerInner) -> TypeId) -> TypeId {
let mut inner = self.write();
let old_len = inner.types.len();
let id = f(&mut inner);
if inner.types.len() != old_len {
self.publish(&inner);
}
id
}
pub fn get_or_make_int(&self, size: usize) -> TypeId {
if let Some(&id) = self.read().int_by_size.get(&size) {
return id;
}
self.mint(|inner| inner.get_or_make_int(size))
}
pub fn get_or_make_bool(&self) -> TypeId {
if let Some(id) = self.read().bool_id {
return id;
}
self.mint(TypeManagerInner::get_or_make_bool)
}
pub fn get_or_make_space_address(
&self,
size: usize,
space: impl Into<MemorySpaceId>,
) -> TypeId {
let space = space.into();
if let Some(&id) = self.read().space_address.get(&(size, space)) {
return id;
}
self.mint(|inner| inner.get_or_make_space_address(size, space))
}
pub fn get_or_make_aggregate(&self, fields: Vec<TypeId>) -> TypeId {
self.get_or_make_named_aggregate(default_named_fields(fields))
}
pub fn get_or_make_named_aggregate(&self, fields: Vec<AggregateField>) -> TypeId {
if let Some(&id) = self.read().aggregate_by_fields.get(&fields) {
return id;
}
self.mint(|inner| inner.get_or_make_named_aggregate(fields))
}
pub fn create_function_return(
&mut self,
owner: FunctionId,
fields: Vec<AggregateField>,
) -> Result<TypeId, String> {
let id = self
.inner
.get_mut()
.expect("type manager RwLock poisoned")
.create_function_return(owner, fields)?;
self.publish_exclusive();
Ok(id)
}
pub fn edit_function_return(
&mut self,
owner: FunctionId,
fields: Vec<AggregateField>,
) -> Result<TypeId, String> {
let id = self
.inner
.get_mut()
.expect("type manager RwLock poisoned")
.edit_function_return(owner, fields)?;
self.publish_exclusive();
Ok(id)
}
pub fn create_requested_types(&mut self, requests: &[TypeRequest]) -> Vec<TypeId> {
if requests.is_empty() {
return Vec::new();
}
let (ids, changed) = {
let inner = self.inner.get_mut().expect("type manager RwLock poisoned");
let before = inner.types.len();
let ids = requests
.iter()
.map(|request| match *request {
TypeRequest::Aggregate { ref fields } => {
inner.get_or_make_named_aggregate(fields.clone())
}
TypeRequest::StructPointer { size, pointee } => {
inner.get_or_make_struct_pointer(size, pointee)
}
TypeRequest::Array { elem, count } => inner.get_or_make_array(elem, count),
TypeRequest::List { elem, bound } => inner.get_or_make_list_opt(elem, bound),
})
.collect();
(ids, inner.types.len() != before)
};
if changed {
self.publish_exclusive();
}
ids
}
pub fn get_or_make_struct(
&self,
name: impl Into<String>,
size: usize,
fields: Vec<AggregateField>,
) -> TypeId {
let name = name.into();
if let Some(&id) = self.read().struct_by_name.get(&name) {
return id;
}
self.mint(|inner| inner.get_or_make_struct(name, size, fields))
}
pub fn get_or_make_struct_pointer(&self, size: usize, pointee: TypeId) -> TypeId {
if let Some(&id) = self.read().struct_pointer.get(&(size, pointee)) {
return id;
}
self.mint(|inner| inner.get_or_make_struct_pointer(size, pointee))
}
pub fn get_or_make_code_pointer(&self, size: usize) -> TypeId {
if let Some(&id) = self.read().code_pointer.get(&size) {
return id;
}
self.mint(|inner| inner.get_or_make_code_pointer(size))
}
pub fn get_struct_pointer(&self, size: usize, pointee: TypeId) -> Option<TypeId> {
self.read().struct_pointer.get(&(size, pointee)).copied()
}
pub fn get_named_aggregate(&self, fields: &[AggregateField]) -> Option<TypeId> {
self.read().aggregate_by_fields.get(fields).copied()
}
pub fn get_or_make_array(&self, elem: TypeId, count: usize) -> TypeId {
if let Some(&id) = self.read().array_by_elem_count.get(&(elem, count)) {
return id;
}
self.mint(|inner| inner.get_or_make_array(elem, count))
}
pub fn get_array(&self, elem: TypeId, count: usize) -> Option<TypeId> {
self.read().array_by_elem_count.get(&(elem, count)).copied()
}
pub fn get_list(&self, elem: TypeId, bound: Option<usize>) -> Option<TypeId> {
self.read().list_by_elem_bound.get(&(elem, bound)).copied()
}
pub fn get_seq(&self, elem: TypeId, len: usize, is_list: bool) -> Option<TypeId> {
if is_list {
self.get_list(elem, Some(len))
} else {
self.get_array(elem, len)
}
}
pub fn get_or_make_list(&self, elem: TypeId, bound: usize) -> TypeId {
if let Some(&id) = self.read().list_by_elem_bound.get(&(elem, Some(bound))) {
return id;
}
self.mint(|inner| inner.get_or_make_list(elem, bound))
}
pub fn get_or_make_unbounded_list(&self, elem: TypeId) -> TypeId {
if let Some(&id) = self.read().list_by_elem_bound.get(&(elem, None)) {
return id;
}
self.mint(|inner| inner.get_or_make_unbounded_list(elem))
}
pub fn get_or_make_seq(&self, elem: TypeId, len: usize, is_list: bool) -> TypeId {
if is_list {
self.get_or_make_list(elem, len)
} else {
self.get_or_make_array(elem, len)
}
}
pub fn binop_result(&self, lhs: TypeId, op: Binop, rhs: TypeId) -> TypeId {
if let Some(id) = self.read().binop_result_probe(lhs, op, rhs) {
return id;
}
self.mint(|inner| inner.binop_result(lhs, op, rhs))
}
pub fn bool_id(&self) -> Option<TypeId> {
self.read().bool_id()
}
pub fn get_int(&self, size: usize) -> TypeId {
self.read()
.int_by_size
.get(&size)
.copied()
.unwrap_or_else(|| panic!("canonical integer type i{} is not published", size * 8))
}
pub fn get_bool(&self) -> TypeId {
self.bool_id()
.expect("canonical bool type is not published")
}
pub fn struct_by_name(&self, name: &str) -> Option<TypeId> {
self.read().struct_by_name(name)
}
pub fn function_return(&self, owner: FunctionId) -> Option<TypeId> {
self.read().function_return.get(&owner).copied()
}
pub fn is_bool(&self, id: TypeId) -> bool {
matches!(self.get(id).repr(), TypeRepr::Bool)
}
pub fn function_return_owner(&self, id: TypeId) -> Option<FunctionId> {
self.get(id).function_return_owner()
}
pub fn size_of(&self, id: TypeId) -> usize {
self.get(id).size()
}
pub fn space_of(&self, id: TypeId) -> Option<MemorySpaceId> {
self.get(id).space()
}
pub fn pointee_of(&self, id: TypeId) -> Option<TypeId> {
self.get(id).pointee()
}
pub fn array_of(&self, id: TypeId) -> Option<(TypeId, usize)> {
self.get(id).array()
}
pub fn list_of(&self, id: TypeId) -> Option<(TypeId, Option<usize>)> {
self.get(id).list()
}
pub fn seq_of(&self, id: TypeId) -> Option<(TypeId, usize, bool)> {
if let Some((elem, count)) = self.array_of(id) {
return Some((elem, count, false));
}
self.list_of(id)
.and_then(|(elem, bound)| bound.map(|bound| (elem, bound, true)))
}
pub fn seq_elem_of(&self, id: TypeId) -> Option<TypeId> {
self.array_of(id)
.map(|(elem, _)| elem)
.or_else(|| self.list_of(id).map(|(elem, _)| elem))
}
pub fn type_name(&self, id: TypeId) -> String {
match self.get(id).repr() {
TypeRepr::Bool => "bool".to_string(),
TypeRepr::Struct { name, .. } => name,
TypeRepr::StructPointer { pointee, .. } => {
format!("{}*", self.type_name(pointee))
}
TypeRepr::Array { elem, count } => {
format!("[{};{}]", self.type_name(elem), count)
}
TypeRepr::List { elem, bound } => match bound {
Some(bound) => format!("[{};<={}]", self.type_name(elem), bound),
None => format!("[{};*]", self.type_name(elem)),
},
TypeRepr::CodePointer { size } => format!("code{}*", size * 8),
_ => format!("i{}", self.size_of(id) * 8),
}
}
pub fn field_type(&self, id: TypeId, index: usize) -> Option<TypeId> {
self.aggregate_fields(id)?
.get(index)
.map(|field| field.type_id)
}
pub fn field_index(&self, id: TypeId, name: &str) -> Option<usize> {
self.aggregate_fields(id)?
.iter()
.position(|field| field.name == name)
}
pub fn published_len(&self) -> usize {
self.published().entries.len()
}
pub fn has_sequence_types(&self) -> bool {
let inner = self.read();
!inner.array_by_elem_count.is_empty() || !inner.list_by_elem_bound.is_empty()
}
pub fn get(&self, id: TypeId) -> &dyn Type {
let entries = &self.published().entries;
let ptr = *entries.get(id.0 as usize).unwrap_or_else(|| {
panic!(
"missing published type {id:?}; published type count is {}",
entries.len()
)
});
unsafe { &*ptr }
}
pub fn struct_name_of(&self, id: TypeId) -> Option<&str> {
self.get(id).struct_name()
}
pub fn aggregate_fields(&self, id: TypeId) -> Option<&[AggregateField]> {
self.get(id).fields()
}
pub fn field_by_offset(&self, id: TypeId, offset: usize) -> Option<(usize, &AggregateField)> {
self.aggregate_fields(id)?
.iter()
.enumerate()
.find(|(_, field)| field.offset == offset)
}
pub fn field_name(&self, id: TypeId, index: usize) -> Option<&str> {
self.aggregate_fields(id)?
.get(index)
.map(|field| field.name.as_str())
}
}
impl serde::Serialize for TypeManager {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let reprs: Vec<TypeRepr> = self
.published()
.entries
.iter()
.map(|&ptr| {
unsafe { &*ptr }.repr()
})
.collect();
reprs.serialize(serializer)
}
}
impl<'de> serde::Deserialize<'de> for TypeManager {
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
let reprs = Vec::<TypeRepr>::deserialize(deserializer)?;
let mut manager = TypeManager::new();
let mut pending_returns: Vec<(FunctionId, Vec<AggregateField>)> = Vec::new();
for repr in reprs {
match repr {
TypeRepr::Int { size } => {
manager.get_or_make_int(size);
}
TypeRepr::Bool => {
manager.get_or_make_bool();
}
TypeRepr::SpaceAddress { size, space } => {
manager.get_or_make_space_address(size, space);
}
TypeRepr::Aggregate { fields } => {
manager.get_or_make_named_aggregate(fields);
}
TypeRepr::FunctionReturn { owner, fields } => {
manager
.create_function_return(owner, Vec::new())
.map_err(serde::de::Error::custom)?;
pending_returns.push((owner, fields));
}
TypeRepr::Struct { name, size, fields } => {
manager.get_or_make_struct(name, size, fields);
}
TypeRepr::StructPointer { size, pointee } => {
manager.get_or_make_struct_pointer(size, pointee);
}
TypeRepr::Array { elem, count } => {
manager.get_or_make_array(elem, count);
}
TypeRepr::List { elem, bound } => match bound {
Some(b) => {
manager.get_or_make_list(elem, b);
}
None => {
manager.get_or_make_unbounded_list(elem);
}
},
TypeRepr::CodePointer { size } => {
manager.get_or_make_code_pointer(size);
}
}
}
for (owner, fields) in pending_returns {
manager
.edit_function_return(owner, fields)
.map_err(serde::de::Error::custom)?;
}
Ok(manager)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn binop_result_bool_rules() {
use crate::value::insn::{Binop, FloatBinop, IntBinop};
let tm = TypeManager::new();
let i32 = tm.get_or_make_int(4);
let boolt = tm.get_or_make_bool();
assert_eq!(tm.binop_result(i32, Binop::Int(IntBinop::Less), i32), boolt);
assert_eq!(
tm.binop_result(i32, Binop::Int(IntBinop::Equal), i32),
boolt
);
assert_eq!(
tm.binop_result(i32, Binop::Float(FloatBinop::Less), i32),
boolt
);
assert_eq!(
tm.binop_result(boolt, Binop::Int(IntBinop::And), boolt),
boolt
);
assert_eq!(
tm.binop_result(boolt, Binop::Int(IntBinop::Or), boolt),
boolt
);
assert_eq!(tm.binop_result(i32, Binop::Int(IntBinop::And), i32), i32);
assert_eq!(tm.binop_result(i32, Binop::Int(IntBinop::Add), i32), i32);
}
#[test]
fn bool_is_byte_stored_and_interned() {
let tm = TypeManager::new();
let b = tm.get_or_make_bool();
assert_eq!(tm.size_of(b), 1);
assert!(tm.is_bool(b));
assert_eq!(tm.get_or_make_bool(), b);
assert_eq!(tm.type_name(b), "bool");
let i8 = tm.get_or_make_int(1);
assert!(!tm.is_bool(i8));
}
#[test]
fn bool_round_trips_through_serde() {
let tm = TypeManager::new();
let _i8 = tm.get_or_make_int(1);
let b = tm.get_or_make_bool();
let config = bincode::config::standard();
let bytes = bincode::serde::encode_to_vec(&tm, config).unwrap();
let (back, _): (TypeManager, _) =
bincode::serde::decode_from_slice(&bytes, config).unwrap();
assert!(back.is_bool(b));
assert_eq!(back.size_of(b), 1);
}
#[test]
fn array_is_a_disguised_width_n_scalar() {
let tm = TypeManager::new();
let i8 = tm.get_or_make_int(1);
let arr = tm.get_or_make_array(i8, 20);
assert_eq!(tm.size_of(arr), 20);
assert!(tm.aggregate_fields(arr).is_none());
assert_eq!(tm.array_of(arr), Some((i8, 20)));
assert_eq!(tm.get_or_make_array(i8, 20), arr);
assert_ne!(tm.get_or_make_array(i8, 21), arr);
assert_eq!(tm.type_name(arr), "[i8;20]");
}
#[test]
fn array_round_trips_through_serde() {
let tm = TypeManager::new();
let i8 = tm.get_or_make_int(1);
let arr = tm.get_or_make_array(i8, 20);
let config = bincode::config::standard();
let bytes = bincode::serde::encode_to_vec(&tm, config).unwrap();
let (back, _): (TypeManager, _) =
bincode::serde::decode_from_slice(&bytes, config).unwrap();
assert_eq!(back.array_of(arr), Some((i8, 20)));
assert_eq!(back.size_of(arr), 20);
}
#[test]
fn list_round_trips_through_serde() {
let tm = TypeManager::new();
let i8 = tm.get_or_make_int(1);
let list = tm.get_or_make_list(i8, 20);
let config = bincode::config::standard();
let bytes = bincode::serde::encode_to_vec(&tm, config).unwrap();
let (back, _): (TypeManager, _) =
bincode::serde::decode_from_slice(&bytes, config).unwrap();
assert_eq!(back.list_of(list), Some((i8, Some(20))));
assert_eq!(back.array_of(list), None);
assert_eq!(back.size_of(list), 20);
}
#[test]
fn unbounded_list_round_trips_and_has_no_footprint() {
let tm = TypeManager::new();
let i8 = tm.get_or_make_int(1);
let list = tm.get_or_make_unbounded_list(i8);
assert_eq!(tm.list_of(list), Some((i8, None)));
assert_eq!(tm.size_of(list), 0);
assert_ne!(list, tm.get_or_make_list(i8, 20));
let config = bincode::config::standard();
let bytes = bincode::serde::encode_to_vec(&tm, config).unwrap();
let (back, _): (TypeManager, _) =
bincode::serde::decode_from_slice(&bytes, config).unwrap();
assert_eq!(back.list_of(list), Some((i8, None)));
assert_eq!(back.array_of(list), None);
}
#[test]
fn newly_minted_type_is_published_before_return() {
let tm = TypeManager::new();
let i16 = tm.get_or_make_int(2);
let array = tm.get_or_make_array(i16, 7);
assert_eq!(tm.size_of(array), 14);
assert_eq!(tm.array_of(array), Some((i16, 7)));
}
#[test]
fn concurrent_mint_and_published_reads_are_consistent() {
let tm = TypeManager::new();
let byte = tm.get_or_make_int(1);
std::thread::scope(|scope| {
for _ in 0..8 {
scope.spawn(|| {
for count in 1..=128 {
let array = tm.get_or_make_array(byte, count);
assert_eq!(tm.size_of(array), count);
assert_eq!(tm.array_of(array), Some((byte, count)));
assert_eq!(tm.size_of(byte), 1);
}
});
}
});
}
#[test]
fn requested_types_are_created_and_published_as_one_barrier_batch() {
let mut tm = TypeManager::new();
let byte = tm.get_or_make_int(1);
let requests = [
TypeRequest::array(byte, 4),
TypeRequest::array(byte, 8),
TypeRequest::array(byte, 4),
];
assert_eq!(tm.get_array(byte, 4), None);
let ids = tm.create_requested_types(&requests);
assert_eq!(ids[0], ids[2], "duplicate requests must intern once");
assert_eq!(tm.get_array(byte, 4), Some(ids[0]));
assert_eq!(tm.get_array(byte, 8), Some(ids[1]));
assert_eq!(tm.array_of(ids[0]), Some((byte, 4)));
assert_eq!(tm.array_of(ids[1]), Some((byte, 8)));
}
#[test]
fn function_return_types_are_unique_owned_and_editable() {
let mut tm = TypeManager::new();
let i32 = tm.get_or_make_int(4);
let fields = vec![AggregateField::new("value", i32)];
let first_owner = FunctionId::from(0usize);
let second_owner = FunctionId::from(1usize);
let first = tm
.create_function_return(first_owner, fields.clone())
.unwrap();
let second = tm
.create_function_return(second_owner, fields.clone())
.unwrap();
assert_ne!(first, second, "owned declarations must not deduplicate");
assert_eq!(tm.function_return(first_owner), Some(first));
assert_eq!(tm.function_return(second_owner), Some(second));
assert_eq!(tm.function_return_owner(first), Some(first_owner));
assert_eq!(tm.function_return_owner(second), Some(second_owner));
assert!(
tm.create_function_return(first_owner, fields.clone())
.is_err(),
"one function cannot acquire a second return identity"
);
let edited = tm
.edit_function_return(
first_owner,
vec![
AggregateField::new("value", i32),
AggregateField::new("status", i32),
],
)
.unwrap();
assert_eq!(edited, first, "editing must preserve nominal identity");
assert_eq!(tm.size_of(first), 8);
assert_eq!(tm.aggregate_fields(first).unwrap().len(), 2);
assert_eq!(tm.size_of(second), 4, "the other owner must not change");
assert_eq!(tm.aggregate_fields(second).unwrap(), fields.as_slice());
}
#[test]
fn function_return_type_round_trips_with_owner_and_identity() {
let mut tm = TypeManager::new();
let i16 = tm.get_or_make_int(2);
let owner = FunctionId::from(7usize);
let return_type = tm
.create_function_return(owner, vec![AggregateField::new("result", i16)])
.unwrap();
let bytes = bincode::serde::encode_to_vec(&tm, bincode::config::standard()).unwrap();
let (mut restored, _): (TypeManager, _) =
bincode::serde::decode_from_slice(&bytes, bincode::config::standard()).unwrap();
assert_eq!(restored.function_return(owner), Some(return_type));
assert_eq!(restored.function_return_owner(return_type), Some(owner));
assert_eq!(restored.size_of(return_type), 2);
assert_eq!(
restored
.edit_function_return(
owner,
vec![
AggregateField::new("result", i16),
AggregateField::new("carry", i16),
],
)
.unwrap(),
return_type
);
assert_eq!(restored.size_of(return_type), 4);
}
}