use std::{marker::PhantomData, ptr::NonNull};
use jl_sys::{
jl_abstractarray_type, jl_anytuple_type_type, jl_apply_type, jl_array_type, jl_densearray_type,
jl_llvmpointer_type, jl_namedtuple_type, jl_pointer_type, jl_ref_type, jl_type_type,
jl_type_unionall, jl_unionall_t, jl_unionall_type, jl_value_t,
};
use jlrs_macros::julia_version;
use jlrs_sys::{jlrs_unionall_body, jlrs_unionall_tvar};
use super::{
Managed, Weak, erase_scope_lifetime,
value::{ValueData, ValueResult},
};
use crate::{
catch::{catch_exceptions, unwrap_exc},
data::managed::{datatype::DataType, private::ManagedPriv, type_var::TypeVar, value::Value},
impl_julia_typecheck,
memory::{
scope::LocalScopeExt,
target::{Target, TargetResult},
},
private::Private,
};
#[derive(Copy, Clone)]
#[repr(transparent)]
pub struct UnionAll<'scope>(NonNull<jl_unionall_t>, PhantomData<&'scope ()>);
impl<'scope> UnionAll<'scope> {
pub fn new<'target, Tgt>(
target: Tgt,
tvar: TypeVar,
body: Value<'_, 'static>,
) -> ValueResult<'target, 'static, Tgt>
where
Tgt: Target<'target>,
{
unsafe {
let callback = || jl_type_unionall(tvar.unwrap(Private), body.unwrap(Private));
let res = match catch_exceptions(callback, unwrap_exc) {
Ok(ptr) => Ok(NonNull::new_unchecked(ptr)),
Err(e) => Err(e),
};
target.result_from_ptr(res, Private)
}
}
#[inline]
pub unsafe fn new_unchecked<'target, Tgt>(
target: Tgt,
tvar: TypeVar,
body: Value<'_, 'static>,
) -> ValueData<'target, 'static, Tgt>
where
Tgt: Target<'target>,
{
unsafe {
let ua = jl_type_unionall(tvar.unwrap(Private), body.unwrap(Private));
target.data_from_ptr(NonNull::new_unchecked(ua), Private)
}
}
#[inline]
pub fn base_type(self) -> DataType<'scope> {
let mut b = self;
unsafe {
while b.body().is::<UnionAll>() {
b = b.body().cast_unchecked();
}
b.body().cast_unchecked::<DataType>()
}
}
#[inline]
pub fn body(self) -> Value<'scope, 'static> {
unsafe {
let body = jlrs_unionall_body(self.unwrap(Private));
debug_assert!(!body.is_null());
Value::wrap_non_null(NonNull::new_unchecked(body), Private)
}
}
#[inline]
pub fn var(self) -> TypeVar<'scope> {
unsafe {
let var = jlrs_unionall_tvar(self.unwrap(Private));
debug_assert!(!var.is_null());
TypeVar::wrap_non_null(NonNull::new_unchecked(var), Private)
}
}
pub unsafe fn apply_types<'target, 'params, V, Tgt>(
self,
target: Tgt,
types: V,
) -> ValueResult<'target, 'static, Tgt>
where
V: AsRef<[Value<'params, 'static>]>,
Tgt: Target<'target>,
{
let types = types.as_ref();
let n = types.len();
let types_ptr = types.as_ptr() as *mut *mut jl_value_t;
unsafe {
let callback = || jl_apply_type(self.as_value().unwrap(Private), types_ptr, n);
let res = match catch_exceptions(callback, unwrap_exc) {
Ok(ptr) => Ok(NonNull::new_unchecked(ptr)),
Err(e) => Err(e),
};
target.result_from_ptr(res, Private)
}
}
#[inline]
pub unsafe fn apply_types_unchecked<'target, 'params, V, Tgt>(
self,
target: Tgt,
types: V,
) -> ValueData<'target, 'static, Tgt>
where
V: AsRef<[Value<'params, 'static>]>,
Tgt: Target<'target>,
{
unsafe {
let types = types.as_ref();
let n = types.len();
let types_ptr = types.as_ptr() as *mut *mut jl_value_t;
let applied = jl_apply_type(self.as_value().unwrap(Private), types_ptr, n);
debug_assert!(!applied.is_null());
target.data_from_ptr(NonNull::new_unchecked(applied), Private)
}
}
pub fn rewrap<'target, Tgt: Target<'target>>(
target: Tgt,
ty: DataType,
) -> ValueData<'target, 'static, Tgt> {
target.with_local_scope::<_, 1>(|target, mut frame| unsafe {
let params = ty.parameters();
let params = params.data();
let mut output = frame.output();
let mut body = erase_scope_lifetime(ty.as_value());
for pidx in (0..params.len()).rev() {
let param = params.get(&target, pidx);
let param = param.unwrap_unchecked().as_value();
if param.is::<TypeVar>() {
let tvar = param.cast_unchecked::<TypeVar>();
let b = UnionAll::new_unchecked(&mut output, tvar, body).as_value();
body = erase_scope_lifetime(b);
}
}
body.root(target)
})
}
pub fn depends_on(&self, tvar: TypeVar) -> bool {
self.body().depends_on(tvar) || self.var().depends_on(tvar)
}
}
impl<'base> UnionAll<'base> {
#[inline]
pub fn type_type<Tgt>(_: &Tgt) -> Self
where
Tgt: Target<'base>,
{
unsafe { Self::wrap_non_null(NonNull::new_unchecked(jl_type_type), Private) }
}
#[inline]
pub fn anytuple_type_type<Tgt>(_: &Tgt) -> Self
where
Tgt: Target<'base>,
{
unsafe { Self::wrap_non_null(NonNull::new_unchecked(jl_anytuple_type_type), Private) }
}
#[inline]
pub fn abstractarray_type<Tgt>(_: &Tgt) -> Self
where
Tgt: Target<'base>,
{
unsafe { Self::wrap_non_null(NonNull::new_unchecked(jl_abstractarray_type), Private) }
}
#[inline]
pub fn densearray_type<Tgt>(_: &Tgt) -> Self
where
Tgt: Target<'base>,
{
unsafe { Self::wrap_non_null(NonNull::new_unchecked(jl_densearray_type), Private) }
}
#[inline]
pub fn array_type<Tgt>(_: &Tgt) -> Self
where
Tgt: Target<'base>,
{
unsafe { Self::wrap_non_null(NonNull::new_unchecked(jl_array_type), Private) }
}
#[inline]
pub fn pointer_type<Tgt>(_: &Tgt) -> Self
where
Tgt: Target<'base>,
{
unsafe { Self::wrap_non_null(NonNull::new_unchecked(jl_pointer_type), Private) }
}
#[inline]
pub fn llvmpointer_type<Tgt>(_: &Tgt) -> Self
where
Tgt: Target<'base>,
{
unsafe { Self::wrap_non_null(NonNull::new_unchecked(jl_llvmpointer_type), Private) }
}
#[inline]
pub fn ref_type<Tgt>(_: &Tgt) -> Self
where
Tgt: Target<'base>,
{
unsafe { Self::wrap_non_null(NonNull::new_unchecked(jl_ref_type), Private) }
}
#[inline]
pub fn namedtuple_type<Tgt>(_: &Tgt) -> Self
where
Tgt: Target<'base>,
{
unsafe { Self::wrap_non_null(NonNull::new_unchecked(jl_namedtuple_type), Private) }
}
#[julia_version(since = "1.11")]
#[inline]
pub fn genericmemory_type<Tgt>(_: &Tgt) -> Self
where
Tgt: Target<'base>,
{
unsafe {
Self::wrap_non_null(
NonNull::new_unchecked(jl_sys::jl_genericmemory_type),
Private,
)
}
}
#[julia_version(since = "1.11")]
#[inline]
pub fn genericmemoryref_type<Tgt>(_: &Tgt) -> Self
where
Tgt: Target<'base>,
{
unsafe {
Self::wrap_non_null(
NonNull::new_unchecked(jl_sys::jl_genericmemoryref_type),
Private,
)
}
}
}
impl_julia_typecheck!(UnionAll<'scope>, jl_unionall_type, 'scope);
impl_debug!(UnionAll<'_>);
impl<'scope> ManagedPriv<'scope, '_> for UnionAll<'scope> {
type Wraps = jl_unionall_t;
type WithLifetimes<'target, 'da> = UnionAll<'target>;
const NAME: &'static str = "UnionAll";
#[inline]
unsafe fn wrap_non_null(inner: NonNull<Self::Wraps>, _: Private) -> Self {
Self(inner, PhantomData)
}
#[inline]
fn unwrap_non_null(self, _: Private) -> NonNull<Self::Wraps> {
self.0
}
}
impl_construct_type_managed!(UnionAll, 1, jl_unionall_type);
pub type WeakUnionAll<'scope> = Weak<'scope, 'static, UnionAll<'scope>>;
pub type UnionAllRet = WeakUnionAll<'static>;
impl_valid_layout!(WeakUnionAll, UnionAll, jl_unionall_type);
use crate::memory::target::TargetType;
pub type UnionAllData<'target, Tgt> =
<Tgt as TargetType<'target>>::Data<'static, UnionAll<'target>>;
pub type UnionAllResult<'target, Tgt> = TargetResult<'target, 'static, UnionAll<'target>, Tgt>;
impl_ccall_arg_managed!(UnionAll, 1);
impl_into_typed!(UnionAll);
pub unsafe fn tvar_to_unionall<'target, Tgt: Target<'target>>(
target: Tgt,
body: Value<'_, 'static>,
) -> ValueData<'target, 'static, Tgt> {
unsafe {
let tvar = body.cast_unchecked::<TypeVar>();
UnionAll::new_unchecked(&target, tvar, body)
.as_value()
.root(target)
}
}