use crate::{StaticReflect, FieldReflect};
#[cfg(feature = "num")]
pub use self::num::{PrimNum, PrimValue};
use std::cmp::Ordering;
use std::marker::PhantomData;
use std::fmt::{self, Formatter, Display, Debug};
#[cfg(feature = "gc")]
use zerogc_derive::{unsafe_gc_impl};
#[cfg(feature = "builtins")]
use crate::builtins::{AsmSlice, AsmStr};
pub unsafe trait SimpleNonZeroRepr: StaticReflect {}
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
#[repr(u8)]
pub enum IntSize {
Byte = 1,
Short = 2,
Int = 4,
Long = 8
}
impl IntSize {
#[inline]
pub const fn bytes(self) -> usize {
self as usize
}
#[inline]
pub const fn from_bytes(bytes: usize) -> Result<IntSize, InvalidSizeErr> {
Ok(match bytes {
1 => IntSize::Byte,
2 => IntSize::Short,
4 => IntSize::Int,
8 => IntSize::Long,
_ => return Err(InvalidSizeErr { bytes })
})
}
}
impl Default for IntSize {
#[inline]
fn default() -> IntSize {
IntSize::Int }
}
#[derive(Debug)]
pub struct InvalidSizeErr {
pub bytes: usize,
}
impl Display for InvalidSizeErr {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "Invalid size: {}", self.bytes)
}
}
impl std::error::Error for InvalidSizeErr {}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Ord, PartialOrd)]
pub enum FloatSize {
Single = 4,
Double = 8
}
impl FloatSize {
#[inline]
pub const fn bytes(self) -> usize {
self as usize
}
#[inline]
pub const fn from_bytes(bytes: usize) -> Result<FloatSize, InvalidSizeErr> {
Ok(match bytes {
4 => FloatSize::Single,
8 => FloatSize::Double,
_ => return Err(InvalidSizeErr { bytes })
})
}
}
impl Default for FloatSize {
#[inline]
fn default() -> FloatSize {
FloatSize::Double
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub enum TypeInfo<'a> {
Unit,
#[cfg(feature = "never")]
Never,
Bool,
Integer {
size: IntSize,
signed: bool
},
Float {
size: FloatSize
},
#[cfg(feature = "builtins")]
Slice {
element_type: &'a TypeInfo<'a>,
},
#[cfg(feature = "builtins")]
Str,
#[cfg(feature = "builtins")]
Optional(&'a TypeInfo<'a>),
Pointer,
Structure(&'a StructureDef<'a>),
Union(&'a UnionDef<'a>),
Extern {
name: &'static str
},
Magic {
id: &'static &'static str,
extra: Option<&'a TypeInfo<'a>>,
}
}
#[cfg(feature = "gc")]
unsafe_gc_impl! {
target => TypeInfo<'a>,
params => ['a],
bounds => {
Trace => always,
TraceImmutable => always,
GcSafe => always,
GcRebrand => { where 'a: 'new_gc },
GcErase => { where 'a: 'min }
},
null_trace => always,
branded_type => Self,
erased_type => Self,
NEEDS_TRACE => false,
NEEDS_DROP => ::std::mem::needs_drop::<Self>(),
visit => |self, visitor| { Ok(()) }
}
impl TypeInfo<'static> {
pub const F32: Self = TypeInfo::Float { size: FloatSize::Single };
pub const F64: Self = TypeInfo::Float { size: FloatSize::Double };
#[inline]
pub const fn integer(size: usize, signed: bool) -> Self {
let size = match IntSize::from_bytes(size) {
Ok(s) => s,
Err(_) => panic!("Invalid size")
};
TypeInfo::Integer { size, signed }
}
}
impl<'tp> TypeInfo<'tp> {
pub const fn size(&self) -> usize {
use std::mem::size_of;
use self::TypeInfo::*;
match *self {
Unit => 0,
#[cfg(feature = "never")]
Never => size_of::<!>(),
Bool => size_of::<bool>(),
Integer { size, signed: _ } => size.bytes(),
Float { size } => size.bytes(),
#[cfg(feature = "builtins")]
Slice { .. } => std::mem::size_of::<AsmSlice<()>>(),
#[cfg(feature = "builtins")]
Optional(ref _inner) => unimplemented!(),
Pointer => size_of::<*const ()>(),
#[cfg(feature = "builtins")]
Str => size_of::<AsmStr>(),
Structure(ref def) => def.size,
Union(ref def) => def.size,
TypeInfo::Magic { .. } | TypeInfo::Extern { .. } => 0xFFFF_FFFF
}
}
pub const fn alignment(&self) -> usize {
use std::mem::align_of;
match *self {
TypeInfo::Unit => align_of::<()>(),
#[cfg(feature = "never")]
TypeInfo::Never => align_of::<!>(),
TypeInfo::Magic { .. } | TypeInfo::Extern { .. } => 0,
TypeInfo::Bool => align_of::<bool>(),
TypeInfo::Integer { size: IntSize::Byte, signed: false } => align_of::<u8>(),
TypeInfo::Integer { size: IntSize::Short, signed: false } => align_of::<u16>(),
TypeInfo::Integer { size: IntSize::Int, signed: false } => align_of::<u32>(),
TypeInfo::Integer { size: IntSize::Long, signed: false } => align_of::<u64>(),
TypeInfo::Integer { size: IntSize::Byte, signed: true } => align_of::<i8>(),
TypeInfo::Integer { size: IntSize::Short, signed: true } => align_of::<i16>(),
TypeInfo::Integer { size: IntSize::Int, signed: true } => align_of::<i32>(),
TypeInfo::Integer { size: IntSize::Long, signed: true } => align_of::<i64>(),
TypeInfo::Float { size: FloatSize::Single } => align_of::<f32>(),
TypeInfo::Float { size: FloatSize::Double } => align_of::<f64>(),
#[cfg(feature = "builtins")]
TypeInfo::Slice { .. } | TypeInfo::Optional(_) => unimplemented!(),
TypeInfo::Pointer => align_of::<*const ()>(),
#[cfg(feature = "builtins")]
TypeInfo::Str => align_of::<AsmStr>(),
TypeInfo::Structure(ref def) => def.alignment,
TypeInfo::Union(ref def) => def.alignment,
}
}
}
impl<'a> Display for TypeInfo<'a> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match *self {
TypeInfo::Unit => f.write_str("()"),
TypeInfo::Never => f.write_str("!"),
TypeInfo::Bool => f.write_str("bool"),
TypeInfo::Integer { size, signed: true } => write!(f, "i{}", size.bytes() * 8),
TypeInfo::Integer { size, signed: false } => write!(f, "u{}", size.bytes() * 8),
TypeInfo::Float { size } => write!(f, "f{}", size.bytes() * 8),
TypeInfo::Slice { element_type } => write!(f, "[{}]", element_type),
TypeInfo::Str => f.write_str("str"),
TypeInfo::Optional(inner_type) => write!(f, "Option<{}>", inner_type),
TypeInfo::Pointer => f.write_str("*mut void"),
TypeInfo::Structure(ref def) => f.write_str(def.name),
TypeInfo::Union(ref def) => f.write_str(def.name),
TypeInfo::Extern { name } => write!(f, "extern {}", name),
TypeInfo::Magic { id, extra: None } => write!(f, "magic::{}", id),
TypeInfo::Magic { id, extra: Some(extra) } => write!(f, "magic::{}<{}>", id, extra)
}
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub struct StructureDef<'a> {
pub name: &'a str,
pub fields: &'a [FieldDef<'a>],
pub size: usize,
pub alignment: usize,
}
impl<T: StaticReflect> Copy for FieldDef<'_, T> {}
impl<T: StaticReflect> Clone for FieldDef<'_, T> {
#[inline]
fn clone(&self) -> Self {
*self
}
}
#[derive(Debug, Eq, PartialEq, Hash)]
pub struct FieldDef<'tp, T: StaticReflect = ()> {
pub name: &'tp str,
pub value_type: TypeId<'tp, T>,
pub offset: usize,
pub index: usize
}
impl<'a, T: StaticReflect> FieldDef<'a, T> {
#[inline]
pub const fn erase(&self) -> FieldDef<'a> {
FieldDef {
name: self.name,
value_type: self.value_type.erase(),
offset: self.offset,
index: self.index
}
}
#[inline]
pub const fn offset(&self) -> usize {
self.offset
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub struct UnionDef<'a> {
pub name: &'a str,
pub fields: &'a [UnionFieldDef<'a>],
pub size: usize,
pub alignment: usize,
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub struct UnionFieldDef<'tp, T: StaticReflect = ()> {
pub name: &'tp str,
pub value_type: TypeId<'tp, T>,
pub index: usize
}
impl<'a, T: StaticReflect> UnionFieldDef<'a, T> {
pub const fn erase(&self) -> UnionFieldDef<'a> {
UnionFieldDef {
name: self.name,
value_type: self.value_type.erase(),
index: self.index
}
}
#[inline]
pub const fn offset(&self) -> usize {
0
}
}
#[cfg(feature = "num")]
pub mod num {
use num_traits::Num;
use crate::NativeRepr;
use std::fmt::Debug;
pub trait PrimValue: NativeRepr + PartialEq + Copy + Debug {}
impl PrimValue for bool {}
impl PrimValue for () {}
impl PrimValue for ! {}
impl<T> PrimValue for *mut T {}
impl<T: PrimNum> PrimValue for T {}
pub trait PrimNum: NativeRepr + Num + Debug + Copy {}
macro_rules! prim_num {
($($target:ty),*) => {$(
impl PrimNum for $target {}
)*};
}
prim_num!(i8, u8, i16, u16, i32, u32, i64, u64, isize, usize, f32, f64);
pub trait PrimFloat: PrimNum {}
impl PrimFloat for f32 {}
impl PrimFloat for f64 {}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum PrimitiveType {
Unit,
#[cfg(feature = "never")]
Never,
Bool,
Pointer,
Integer {
size: IntSize,
signed: bool
},
Float {
size: FloatSize
},
}
impl PrimitiveType {
pub fn type_info(&self) -> &TypeInfo<'static> {
use self::PrimitiveType::*;
use self::IntSize::*;
use self::FloatSize::*;
match *self {
Unit => &TypeInfo::Unit,
Never => &TypeInfo::Never,
Bool => &TypeInfo::Bool,
Pointer => &TypeInfo::Pointer,
Integer { size: Byte, signed: true } => &TypeInfo::Integer { size: Byte, signed: true },
Integer { size: Short, signed: true } => &TypeInfo::Integer { size: Short, signed: true },
Integer { size: Int, signed: true } => &TypeInfo::Integer { size: Int, signed: true },
Integer { size: Long, signed: true } => &TypeInfo::Integer { size: Long, signed: true },
Integer { size: Byte, signed: false } => &TypeInfo::Integer { size: Byte, signed: false },
Integer { size: Short, signed: false } => &TypeInfo::Integer { size: Int, signed: false },
Integer { size: Int, signed: false } => &TypeInfo::Integer { size: Short, signed: false },
Integer { size: Long, signed: false } => &TypeInfo::Integer { size: Long, signed: false },
Float { size: Single } => &TypeInfo::Float { size: Single },
Float { size: Double } => &TypeInfo::Float { size: Double },
}
}
pub fn bytes(&self) -> usize {
match self {
PrimitiveType::Unit | PrimitiveType::Never => 0,
PrimitiveType::Integer { size, signed: _ } => size.bytes(),
PrimitiveType::Float { size } => size.bytes(),
PrimitiveType::Pointer => {
assert_eq!(std::mem::size_of::<usize>(), std::mem::size_of::<*mut ()>());
std::mem::size_of::<*mut ()>() as usize
},
PrimitiveType::Bool => {
assert_eq!(std::mem::size_of::<bool>(), 1);
1
},
}
}
#[inline]
pub fn size(self) -> usize {
self.bytes() as usize
}
}
impl PartialOrd for PrimitiveType {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
match (*self, *other) {
(
PrimitiveType::Integer { size: first, signed: first_signed },
PrimitiveType::Integer { size: second, signed: second_signed }
) => {
Some(first.cmp(&second)
.then(first_signed.cmp(&second_signed)))
},
(PrimitiveType::Float { size: first }, PrimitiveType::Float { size: second }) => Some(first.cmp(&second)),
(first, other) if first == other => Some(Ordering::Equal),
_ => None
}
}
}
impl<T: StaticReflect> Copy for TypeId<'_, T> {}
impl<T: StaticReflect> Clone for TypeId<'_, T> {
#[inline]
fn clone(&self) -> Self {
*self
}
}
#[derive(Eq, PartialEq, Hash)]
pub struct TypeId<'a, T: StaticReflect = ()> {
value: &'a TypeInfo<'a>,
marker: PhantomData<fn() -> T>
}
#[cfg(feature = "gc")]
unsafe_gc_impl! {
target => TypeId<'a, T>,
params => ['a, T: StaticReflect],
bounds => {
Trace => always,
TraceImmutable => always,
GcSafe => always,
GcRebrand => { where 'a: 'new_gc, T: 'new_gc },
GcErase => { where 'a: 'min, T: 'min }
},
null_trace => always,
branded_type => Self,
erased_type => Self,
NEEDS_TRACE => false,
NEEDS_DROP => ::std::mem::needs_drop::<Self>(),
visit => |self, visitor| { Ok(()) }
}
impl TypeId<'static> {
#[inline]
pub const fn erased<T: StaticReflect>() -> TypeId<'static> {
TypeId::<T>::get().erase()
}
}
impl<T: StaticReflect> TypeId<'static, T> {
#[inline]
pub const fn get() -> Self {
TypeId {
value: &T::TYPE_INFO,
marker: PhantomData
}
}
#[inline]
pub const fn from_static(s: &'static TypeInfo<'static>) -> Self {
TypeId {
value: s,
marker: PhantomData
}
}
}
impl<'tp, T: StaticReflect> TypeId<'tp, T> {
#[inline]
pub const fn erase(self) -> TypeId<'tp> {
TypeId {
value: self.value,
marker: PhantomData,
}
}
#[inline]
pub fn is_bool(self) -> bool {
matches!(self.primitive(), Some(PrimitiveType::Bool))
}
#[inline]
pub fn is_int(self) -> bool {
matches!(self.primitive(), Some(PrimitiveType::Integer { .. }))
}
#[inline]
pub fn is_ptr(self) -> bool {
matches!(self.primitive(), Some(PrimitiveType::Pointer { .. }))
}
#[inline]
pub fn is_float(self) -> bool {
matches!(self.primitive(), Some(PrimitiveType::Float { .. }))
}
#[inline]
pub fn is_primitive(self) -> bool {
self.primitive().is_some()
}
#[inline]
pub fn primitive(self) -> Option<PrimitiveType> {
Some(match *self.value {
TypeInfo::Unit => PrimitiveType::Unit,
TypeInfo::Never => PrimitiveType::Never,
TypeInfo::Bool => PrimitiveType::Bool,
TypeInfo::Pointer => PrimitiveType::Pointer,
TypeInfo::Integer { size, signed } => PrimitiveType::Integer { size, signed },
TypeInfo::Float { size } => PrimitiveType::Float { size },
_ => return None
})
}
#[inline]
pub const fn type_ref(self) -> &'tp TypeInfo<'tp> {
self.value
}
#[inline]
pub const fn from_ref(tp: &'tp TypeInfo<'tp>) -> Self {
TypeId {
marker: PhantomData,
value: tp
}
}
#[inline]
pub const fn named_field_info(&self) -> <T as FieldReflect>::NamedFieldInfo
where T: FieldReflect {
T::NAMED_FIELD_INFO
}
}
impl<'a, T: StaticReflect> TypeId<'a, *mut T> {
#[inline]
pub const fn pointer_target(self) -> TypeId<'a, T> {
TypeId::get()
}
}
impl<'tp> From<&'tp TypeInfo<'tp>> for TypeId<'tp> {
#[inline]
fn from(static_type: &'tp TypeInfo<'tp>) -> Self {
TypeId::from_ref(static_type)
}
}
impl<T: StaticReflect> Display for TypeId<'_, T> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
Display::fmt(&self.value, f)
}
}
impl<T: StaticReflect> Debug for TypeId<'_, T> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_tuple("TypeId")
.field(self.value)
.finish()
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct FieldId<'a> {
pub owner: TypeId<'a>,
pub index: usize,
}