use std::{ffi::c_void, marker::PhantomData};
use jl_sys::jl_string_type;
use super::abstract_type::AbstractType;
use crate::{
convert::into_julia::IntoJulia,
data::managed::{Managed, datatype::DataType, type_name::TypeName, union_all::UnionAll},
memory::target::unrooted::Unrooted,
prelude::LocalScope,
};
#[diagnostic::on_unimplemented(
message = "the trait bound `{Self}: Typecheck` is not satisfied",
label = "the trait `Typecheck` is not implemented for `{Self}`",
note = "Custom types that implement `Typecheck` should be generated with JlrsCore.reflect",
note = "Do not implement `ForeignType` or `OpaqueType` unless this type is exported to Julia with `julia_module!`"
)]
pub unsafe trait Typecheck {
fn typecheck(t: DataType) -> bool;
}
pub struct AbstractTypecheck<A: AbstractType>(PhantomData<A>);
unsafe impl<A: AbstractType> Typecheck for AbstractTypecheck<A> {
fn typecheck(t: DataType) -> bool {
t.unrooted_target().local_scope::<_, 1>(|mut frame| {
let ty = A::construct_type(&mut frame);
t.as_value().subtype(ty)
})
}
}
#[doc(hidden)]
#[macro_export]
macro_rules! impl_julia_typecheck {
($type:ty, $jl_type:expr_2021, $($lt:lifetime),+) => {
unsafe impl<$($lt),+> crate::data::types::typecheck::Typecheck for $type {
#[inline]
fn typecheck(t: $crate::data::managed::datatype::DataType) -> bool {
unsafe {
<$crate::data::managed::datatype::DataType as $crate::data::managed::private::ManagedPriv>::unwrap(t, crate::private::Private) == $jl_type
}
}
}
};
($type:ty, $jl_type:expr_2021) => {
unsafe impl crate::data::types::typecheck::Typecheck for $type {
#[inline]
fn typecheck(t: $crate::data::managed::datatype::DataType) -> bool {
unsafe {
<$crate::data::managed::datatype::DataType as $crate::data::managed::private::ManagedPriv>::unwrap(t, crate::private::Private) == $jl_type
}
}
}
};
($type:ty) => {
unsafe impl crate::data::types::typecheck::Typecheck for $type {
#[inline]
fn typecheck(t: crate::data::managed::datatype::DataType) -> bool {
unsafe {
let global = $crate::memory::target::unrooted::Unrooted::new();
<$crate::data::managed::datatype::DataType as $crate::data::managed::private::ManagedPriv>::unwrap(t, crate::private::Private) == <$type as $crate::convert::into_julia::IntoJulia>::julia_type(global).ptr().as_ptr()
}
}
}
};
}
impl_julia_typecheck!(i8);
impl_julia_typecheck!(i16);
impl_julia_typecheck!(i32);
impl_julia_typecheck!(i64);
impl_julia_typecheck!(isize);
impl_julia_typecheck!(u8);
impl_julia_typecheck!(u16);
impl_julia_typecheck!(u32);
impl_julia_typecheck!(u64);
impl_julia_typecheck!(usize);
impl_julia_typecheck!(f32);
impl_julia_typecheck!(f64);
impl_julia_typecheck!(bool);
impl_julia_typecheck!(char);
impl_julia_typecheck!(*mut c_void);
unsafe impl<T: IntoJulia> Typecheck for *mut T {
#[inline]
fn typecheck(t: DataType) -> bool {
unsafe {
let global = Unrooted::new();
let ptr_tname = TypeName::of_pointer(&global);
if t.type_name() != ptr_tname {
return false;
}
let params = t.parameters();
let param = params.data().get(global, 0);
let inner_ty = T::julia_type(global);
if param.unwrap_unchecked().as_value() != inner_ty.as_value() {
return false;
}
true
}
}
}
pub struct Type;
unsafe impl Typecheck for Type {
#[inline]
fn typecheck(t: DataType) -> bool {
t.as_value().is_kind()
}
}
pub struct Bits;
unsafe impl Typecheck for Bits {
#[inline]
fn typecheck(t: DataType) -> bool {
t.is_bits()
}
}
pub struct Abstract;
unsafe impl Typecheck for Abstract {
#[inline]
fn typecheck(t: DataType) -> bool {
t.is_abstract()
}
}
pub struct AbstractRef;
unsafe impl Typecheck for AbstractRef {
fn typecheck(t: DataType) -> bool {
unsafe {
t.type_name()
== UnionAll::ref_type(&Unrooted::new())
.body()
.cast_unchecked::<DataType>()
.type_name()
}
}
}
pub struct VecElement;
unsafe impl Typecheck for VecElement {
#[inline]
fn typecheck(t: DataType) -> bool {
unsafe { t.type_name() == TypeName::of_vecelement(&Unrooted::new()) }
}
}
pub struct TypeType;
unsafe impl Typecheck for TypeType {
fn typecheck(t: DataType) -> bool {
unsafe {
t.type_name()
== UnionAll::type_type(&Unrooted::new())
.body()
.cast_unchecked::<DataType>()
.type_name()
}
}
}
pub struct Mutable;
unsafe impl Typecheck for Mutable {
#[inline]
fn typecheck(t: DataType) -> bool {
t.mutable()
}
}
pub struct Immutable;
unsafe impl Typecheck for Immutable {
#[inline]
fn typecheck(t: DataType) -> bool {
!t.mutable()
}
}
pub struct PrimitiveType;
unsafe impl Typecheck for PrimitiveType {
fn typecheck(t: DataType) -> bool {
unsafe {
t.is::<Immutable>()
&& t.has_layout()
&& t.n_fields().unwrap_unchecked() == 0
&& t.size().unwrap_unchecked() > 0
}
}
}
pub struct StructType;
unsafe impl Typecheck for StructType {
#[inline]
fn typecheck(t: DataType) -> bool {
!t.is_abstract() && !t.is::<PrimitiveType>()
}
}
pub struct Singleton;
unsafe impl Typecheck for Singleton {
#[inline]
fn typecheck(t: DataType) -> bool {
t.instance().is_some()
}
}
impl_julia_typecheck!(String, jl_string_type);
pub struct Pointer;
unsafe impl Typecheck for Pointer {
#[inline]
fn typecheck(t: DataType) -> bool {
unsafe { t.type_name() == TypeName::of_pointer(&Unrooted::new()) }
}
}
pub struct LLVMPointer;
unsafe impl Typecheck for LLVMPointer {
#[inline]
fn typecheck(t: DataType) -> bool {
unsafe { t.type_name() == TypeName::of_llvmpointer(&Unrooted::new()) }
}
}
pub struct Concrete;
unsafe impl Typecheck for Concrete {
#[inline]
fn typecheck(t: DataType) -> bool {
t.is_concrete_type()
}
}