use std::{
any::{type_name, TypeId},
cmp::Ordering,
collections::hash_set::Iter,
fmt::{Debug, Display, Formatter},
hash::{Hash, Hasher},
iter::FusedIterator,
mem::transmute,
ops::Deref,
ptr::NonNull,
};
use ahash::{AHashMap, AHashSet};
use lady_deirdre::sync::Lazy;
use crate::{
report::debug_unreachable,
runtime::{
ops::{
DynamicType,
Fn0Repr,
Fn1Repr,
Fn2Repr,
Fn3Repr,
Fn4Repr,
Fn5Repr,
Fn6Repr,
Fn7Repr,
},
RustOrigin,
__intrinsics::DeclarationGroup,
},
};
pub trait ScriptType: sealed::Sealed + Send + Sync + 'static {
#[inline(always)]
fn type_meta() -> &'static TypeMeta {
match TypeMeta::by_id(&TypeId::of::<Self>()) {
Some(meta) => meta,
None => {
let name = type_name::<Self>();
panic!("{name} type was not registered. Probably because export has been disabled for this type.")
}
}
}
}
mod sealed {
use crate::runtime::{ScriptType, __intrinsics::RegisteredType};
pub trait Sealed {}
impl<T: RegisteredType + ?Sized> Sealed for T {}
impl<T: RegisteredType + ?Sized> ScriptType for T {}
}
#[derive(Clone, Copy, Debug)]
pub struct TypeMeta {
id: TypeId,
name: &'static str,
origin: &'static RustOrigin,
doc: Option<&'static str>,
family: TypeFamilyInner,
size: usize,
}
impl Default for &'static TypeMeta {
#[inline(always)]
fn default() -> Self {
TypeMeta::nil()
}
}
impl PartialEq for TypeMeta {
#[inline(always)]
fn eq(&self, other: &Self) -> bool {
self.id.eq(&other.id)
}
}
impl PartialEq<TypeId> for TypeMeta {
#[inline(always)]
fn eq(&self, other: &TypeId) -> bool {
self.id.eq(other)
}
}
impl Eq for TypeMeta {}
impl Ord for TypeMeta {
#[inline(always)]
fn cmp(&self, other: &Self) -> Ordering {
self.id.cmp(&other.id)
}
}
impl PartialOrd for TypeMeta {
#[inline(always)]
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Hash for TypeMeta {
#[inline(always)]
fn hash<H: Hasher>(&self, state: &mut H) {
self.id.hash(state)
}
}
impl Display for TypeMeta {
#[inline(always)]
fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
formatter.write_str(self.name)
}
}
impl TypeMeta {
#[inline(always)]
pub fn nil() -> &'static Self {
<()>::type_meta()
}
#[inline(always)]
pub fn dynamic() -> &'static Self {
<DynamicType>::type_meta()
}
#[inline(always)]
pub(crate) fn script_fn(arity: usize) -> Option<&'static Self> {
match arity {
0 => Some(Fn0Repr::type_meta()),
1 => Some(Fn1Repr::type_meta()),
2 => Some(Fn2Repr::type_meta()),
3 => Some(Fn3Repr::type_meta()),
4 => Some(Fn4Repr::type_meta()),
5 => Some(Fn5Repr::type_meta()),
6 => Some(Fn6Repr::type_meta()),
7 => Some(Fn7Repr::type_meta()),
_ => None,
}
}
#[inline(always)]
pub(super) fn enumerate() -> impl Iterator<Item = &'static TypeId> {
let registry = TypeRegistry::get();
registry.type_index.keys()
}
#[inline(always)]
pub(super) fn by_id(id: &TypeId) -> Option<&'static Self> {
let registry = TypeRegistry::get();
registry.type_index.get(id)
}
#[inline(always)]
pub fn id(&self) -> &TypeId {
&self.id
}
#[inline(always)]
pub fn is_nil(&self) -> bool {
self.id.eq(&TypeId::of::<()>())
}
#[inline(always)]
pub fn is_dynamic(&self) -> bool {
self.id.eq(&TypeId::of::<DynamicType>())
}
#[inline(always)]
pub fn is_fn(&self) -> bool {
self.family().is_fn()
}
#[inline(always)]
pub fn name(&self) -> &'static str {
self.name
}
#[inline(always)]
pub fn origin(&self) -> &'static RustOrigin {
self.origin
}
#[inline(always)]
pub fn doc(&self) -> Option<&'static str> {
self.doc
}
#[inline(always)]
pub(super) fn size(&self) -> usize {
self.size
}
#[inline(always)]
pub fn family(&self) -> &TypeFamily {
unsafe { transmute::<&TypeFamilyInner, &TypeFamily>(&self.family) }
}
}
#[repr(transparent)]
pub struct TypeFamily(TypeFamilyInner);
impl PartialEq for TypeFamily {
#[inline]
fn eq(&self, other: &Self) -> bool {
let this_ptr = match &self.0 {
TypeFamilyInner::Singleton { id: this_id } => {
if let TypeFamilyInner::Singleton { id: other_id } = &other.0 {
return this_id.eq(other_id);
}
return false;
}
TypeFamilyInner::Group { .. } => unsafe { self.ptr() },
TypeFamilyInner::Reference { ptr } => *ptr,
};
let other_ptr = match &other.0 {
TypeFamilyInner::Singleton { .. } => return false,
TypeFamilyInner::Group { .. } => unsafe { other.ptr() },
TypeFamilyInner::Reference { ptr } => *ptr,
};
this_ptr.eq(&other_ptr)
}
}
impl Eq for TypeFamily {}
impl Hash for TypeFamily {
#[inline]
fn hash<H: Hasher>(&self, state: &mut H) {
let reference = match &self.0 {
TypeFamilyInner::Singleton { id } => return id.hash(state),
TypeFamilyInner::Group { .. } => unsafe { self.ptr() },
TypeFamilyInner::Reference { ptr } => *ptr,
};
reference.hash(state);
}
}
impl Display for TypeFamily {
#[inline]
fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
let name = self.name();
formatter.write_str(name)?;
if !formatter.alternate() {
return Ok(());
}
formatter.write_str("(")?;
let mut first = true;
for ty in self {
match first {
true => first = false,
false => formatter.write_str(", ")?,
}
Display::fmt(ty.name, formatter)?;
}
formatter.write_str(")")?;
Ok(())
}
}
impl Debug for TypeFamily {
#[inline(always)]
fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
Display::fmt(self, formatter)
}
}
impl<'a> IntoIterator for &'a TypeFamily {
type Item = &'static TypeMeta;
type IntoIter = TypeFamilyIter;
#[inline]
fn into_iter(self) -> Self::IntoIter {
let reference = match &self.0 {
TypeFamilyInner::Singleton { id } => return TypeFamilyIter::Singleton(*id),
TypeFamilyInner::Group { .. } => unsafe { self.ptr() },
TypeFamilyInner::Reference { ptr } => *ptr,
};
let registry = TypeRegistry::get();
match registry.family_index.get(&reference) {
None => TypeFamilyIter::Ended,
Some(set) => TypeFamilyIter::Group(set.iter()),
}
}
}
impl TypeFamily {
#[inline(always)]
pub const fn new(name: &'static str) -> Self {
Self(TypeFamilyInner::Group { name, doc: None })
}
#[inline(always)]
pub const fn with_doc(name: &'static str, doc: &'static str) -> Self {
Self(TypeFamilyInner::Group {
name,
doc: Some(doc),
})
}
#[inline(always)]
pub fn nil() -> &'static Self {
TypeMeta::nil().family()
}
#[inline(always)]
pub fn dynamic() -> &'static Self {
TypeMeta::dynamic().family()
}
#[inline(always)]
pub fn fn_family() -> &'static Self {
&crate::runtime::__intrinsics::FUNCTION_FAMILY
}
#[inline(always)]
pub fn package() -> &'static Self {
&crate::runtime::__intrinsics::PACKAGE_FAMILY
}
#[inline(always)]
pub fn number() -> &'static Self {
&NUMBER_FAMILY
}
#[inline(always)]
pub fn is_nil(&self) -> bool {
self == Self::nil()
}
#[inline(always)]
pub fn is_dynamic(&self) -> bool {
self == Self::dynamic()
}
#[inline(always)]
pub fn is_fn(&self) -> bool {
self == Self::fn_family()
}
#[inline(always)]
pub fn is_package(&self) -> bool {
self == Self::package()
}
#[inline(always)]
pub fn is_number(&self) -> bool {
self == Self::number()
}
#[inline(always)]
pub fn len(&self) -> usize {
let reference = match &self.0 {
TypeFamilyInner::Singleton { .. } => return 1,
TypeFamilyInner::Group { .. } => unsafe { self.ptr() },
TypeFamilyInner::Reference { ptr } => *ptr,
};
let registry = TypeRegistry::get();
let Some(set) = registry.family_index.get(&reference) else {
return 0;
};
set.len()
}
#[inline(always)]
pub fn name(&self) -> &'static str {
match &self.0 {
TypeFamilyInner::Singleton { id } => {
let registry = TypeRegistry::get();
match registry.type_index.get(id) {
Some(meta) => meta.name,
None => unsafe { debug_unreachable!("Missing singleton type family entry.") },
}
}
TypeFamilyInner::Group { name, .. } => *name,
TypeFamilyInner::Reference { ptr } => {
let family = unsafe { ptr.as_ref() };
match family.0 {
TypeFamilyInner::Group { name, .. } => name,
_ => unsafe { debug_unreachable!("TypeFamily broken reference.") },
}
}
}
}
#[inline(always)]
pub fn doc(&self) -> Option<&'static str> {
match &self.0 {
TypeFamilyInner::Singleton { id } => {
let registry = TypeRegistry::get();
match registry.type_index.get(id) {
Some(meta) => meta.doc,
None => unsafe { debug_unreachable!("Missing singleton type family entry.") },
}
}
TypeFamilyInner::Group { doc, .. } => *doc,
TypeFamilyInner::Reference { ptr } => {
let family = unsafe { ptr.as_ref() };
match family.0 {
TypeFamilyInner::Group { doc, .. } => doc,
_ => unsafe { debug_unreachable!("TypeFamily broken reference.") },
}
}
}
}
#[inline(always)]
pub fn includes(&self, ty: &TypeId) -> bool {
let ptr = match &self.0 {
TypeFamilyInner::Singleton { id } => return id.eq(ty),
TypeFamilyInner::Group { .. } => unsafe { self.ptr() },
TypeFamilyInner::Reference { ptr } => *ptr,
};
let registry = TypeRegistry::get();
let set = match registry.family_index.get(&ptr) {
None => return false,
Some(set) => set,
};
set.contains(ty)
}
unsafe fn ptr(&self) -> NonNull<TypeFamily> {
match &self.0 {
TypeFamilyInner::Group { .. } => unsafe {
NonNull::new_unchecked(self as *const TypeFamily as *mut TypeFamily)
},
_ => unsafe {
debug_unreachable!("An attempt to crate pointer from non-Group TypeFamily.")
},
}
}
}
pub enum TypeFamilyIter {
Ended,
Singleton(TypeId),
Group(Iter<'static, TypeId>),
}
impl Iterator for TypeFamilyIter {
type Item = &'static TypeMeta;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
match self {
Self::Ended => None,
Self::Singleton(id) => {
let id = *id;
*self = Self::Ended;
match TypeMeta::by_id(&id) {
None => unsafe { debug_unreachable!("Invalid TypeFamily singleton.") },
Some(meta) => Some(meta),
}
}
Self::Group(iterator) => match iterator.next() {
None => None,
Some(id) => match TypeMeta::by_id(&id) {
None => unsafe { debug_unreachable!("Invalid TypeFamily group.") },
Some(meta) => Some(meta),
},
},
}
}
}
impl FusedIterator for TypeFamilyIter {}
#[derive(Copy)]
enum TypeFamilyInner {
Singleton {
id: TypeId,
},
Group {
name: &'static str,
doc: Option<&'static str>,
},
Reference {
ptr: NonNull<TypeFamily>,
},
}
impl Debug for TypeFamilyInner {
#[inline(always)]
fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
let this = unsafe { transmute::<&TypeFamilyInner, &TypeFamily>(self) };
Debug::fmt(this, formatter)
}
}
unsafe impl Send for TypeFamilyInner {}
unsafe impl Sync for TypeFamilyInner {}
impl Clone for TypeFamilyInner {
fn clone(&self) -> Self {
match self {
Self::Singleton { id } => Self::Singleton { id: *id },
Self::Reference { ptr } => Self::Reference { ptr: *ptr },
Self::Group { .. } => unsafe {
debug_unreachable!("An attempt to clone Group TypeFamily")
},
}
}
}
#[macro_export]
macro_rules! type_family {
(
$vis:vis static $ident:ident = $name:expr;
) => {
$vis static $ident: $crate::runtime::TypeFamily = $crate::runtime::TypeFamily::new($name);
};
(
$(#[doc = $doc:expr])+
$vis:vis static $ident:ident = $name:expr;
) => {
$(#[doc = $doc])+
$vis static $ident: $crate::runtime::TypeFamily = $crate::runtime::TypeFamily::with_doc(
$name, ::std::concat!($($doc, "\n"),+)
);
};
{
$(
$(#[doc = $doc:expr])*
$vis:vis static $ident:ident = $name:expr;
)*
} => {
$(
$crate::type_family!{
$(#[doc = $doc])*
$vis static $ident = $name;
}
)*
};
}
struct TypeRegistry {
type_index: AHashMap<TypeId, TypeMeta>,
family_index: AHashMap<NonNull<TypeFamily>, AHashSet<TypeId>>,
}
unsafe impl Send for TypeRegistry {}
unsafe impl Sync for TypeRegistry {}
impl TypeRegistry {
#[inline(always)]
fn get() -> &'static Self {
static REGISTRY: Lazy<TypeRegistry> = Lazy::new(|| {
let mut type_index = AHashMap::<TypeId, TypeMeta>::new();
let mut family_index = AHashMap::<NonNull<TypeFamily>, AHashSet<TypeId>>::new();
for group in DeclarationGroup::enumerate() {
let origin = group.origin;
for declaration in &group.type_metas {
let declaration = declaration();
if let Some(previous) = type_index.get(&declaration.id) {
origin.blame(&format!(
"Type {} already declared in {} as {}.",
declaration.name, previous.origin, previous.name,
))
}
let family = match declaration.family {
None => TypeFamilyInner::Singleton { id: declaration.id },
Some(group) => {
let ptr = unsafe { group.ptr() };
let set = family_index.entry(ptr).or_default();
if !set.insert(declaration.id) {
unsafe { debug_unreachable!("Duplicate type family entry.") }
}
TypeFamilyInner::Reference { ptr }
}
};
let meta = TypeMeta {
id: declaration.id,
name: declaration.name,
origin,
doc: declaration.doc,
family,
size: declaration.size,
};
if let Some(_) = type_index.insert(declaration.id, meta) {
unsafe { debug_unreachable!("Duplicate type meta entry.") }
}
}
}
TypeRegistry {
type_index,
family_index,
}
});
REGISTRY.deref()
}
}
use crate::exports::NUMBER_FAMILY;