jlrs/data/types/
construct_type.rs

1//! Construct Julia type objects from Rust types.
2
3use std::{any::TypeId, ffi::c_void, marker::PhantomData, ptr::NonNull, string::FromUtf8Error};
4
5use fnv::FnvHashMap;
6use jl_sys::{
7    jl_bool_type, jl_bottom_type, jl_char_type, jl_float32_type, jl_float64_type, jl_int8_type,
8    jl_int16_type, jl_int32_type, jl_int64_type, jl_pointer_type, jl_uint8_type, jl_uint16_type,
9    jl_uint32_type, jl_uint64_type, jl_uniontype_type, jl_value_t, jl_voidpointer_type,
10};
11use rustc_hash::FxHashSet;
12
13use super::abstract_type::{AbstractType, AnyType};
14use crate::{
15    convert::to_symbol::ToSymbol,
16    data::{
17        cache::Cache,
18        layout::{is_bits::IsBits, tuple::Tuple, typed_layout::HasLayout},
19        managed::{
20            Managed,
21            array::dimensions::DimsExt,
22            datatype::DataType,
23            simple_vector::SimpleVector,
24            type_var::{TypeVar, TypeVarData},
25            union::Union,
26            union_all::UnionAll,
27            value::{Value, ValueData, ValueUnbound},
28        },
29    },
30    memory::{
31        PTls,
32        scope::{LocalScope, LocalScopeExt},
33        target::{RootingTarget, Target, unrooted::Unrooted},
34    },
35    prelude::{ConstructTypedArray, Symbol, WeakValue},
36    private::Private,
37};
38
39type CacheImpl = ConstructedTypes;
40
41static CONSTRUCTED_TYPE_CACHE: CacheImpl = CacheImpl::new();
42
43pub(crate) unsafe fn mark_constructed_type_cache(ptls: PTls, full: bool) {
44    unsafe {
45        CONSTRUCTED_TYPE_CACHE.data.mark(ptls, full);
46    }
47}
48
49/// Define a fast key type for a constructible type.
50///
51/// See [`FastKey`] for an example.
52#[macro_export]
53macro_rules! define_fast_key {
54    ($(#[$meta:meta])* $vis:vis $ty:ident, $for_ty:ty) => {
55        $(#[$meta])*
56        $vis struct $ty;
57
58        unsafe impl $crate::data::types::construct_type::FastKey for $ty {
59            type For = $for_ty;
60
61            #[inline]
62            fn construct_type_fast<'target, Tgt>(
63                target: &Tgt,
64            ) -> $crate::data::managed::value::Value<'target, 'static>
65            where
66                Tgt: $crate::memory::target::Target<'target>,
67            {
68                static REF: $crate::data::static_data::StaticConstructibleType<$for_ty> =
69                    $crate::data::static_data::StaticConstructibleType::<$for_ty>::new();
70                REF.get_or_init(&target)
71            }
72        }
73    };
74}
75
76/// Define a fast array key type for a constructible array type.
77///
78/// See [`FastArrayKey`] for an example.
79#[macro_export]
80macro_rules! define_fast_array_key {
81    ($(#[$meta:meta])* $vis:vis $ty:ident, $elem_ty:ty, $rank:literal) => {
82        $(#[$meta])*
83        $vis struct $ty;
84
85        unsafe impl $crate::data::types::construct_type::FastKey for $ty {
86            type For = $crate::data::managed::array::TypedRankedArray<
87                'static,
88                'static,
89                <$elem_ty as $crate::data::types::construct_type::ConstructType>::Static,
90                $rank
91            >;
92
93            #[inline]
94            fn construct_type_fast<'target, Tgt>(
95                target: &Tgt,
96            ) -> $crate::data::managed::value::Value<'target, 'static>
97            where
98                Tgt: $crate::memory::target::Target<'target>,
99            {
100                type Ty = $crate::data::types::construct_type::RankedArrayType<$elem_ty, $rank>;
101                static REF: $crate::data::static_data::StaticConstructibleType<Ty> =
102                    $crate::data::static_data::StaticConstructibleType::<Ty>::new();
103                REF.get_or_init(&target)
104            }
105        }
106
107        unsafe impl $crate::data::types::construct_type::FastArrayKey<$rank> for $ty {
108            type ElemType = $elem_ty;
109        }
110    };
111}
112
113/// Shorthand macro for [`TypeVarConstructor`].
114///
115/// In Julia, `TypeVar`s have a name, an upper bound, and a lower bound. In most cases, you will
116/// only care about the name and maybe the upper bound. The `TypeVarConstructor` type is quite
117/// verbose, this macro provides a useful shorthand.
118///
119/// This macro expands as follows:
120///
121/// `tvar!('S') -> TypeVarConstructor<Name<'S'>>`
122/// `tvar!(Name<'S'>) -> TypeVarConstructor<Name<'S'>>`
123/// `tvar!('S'; AnyType) -> TypeVarConstructor<Name<'S'>, AnyType>`
124/// `tvar!(Name<'S'>; AnyType) -> TypeVarConstructor<Name<'S'>, AnyType>`
125/// `tvar!(BottomType; 'S'; AnyType) -> TypeVarConstructor<Name<'S'>, AnyType, BottomType>`
126/// `tvar!(BottomType; Name<'S'>; AnyType) -> TypeVarConstructor<Name<'S'>, AnyType, BottomType>`
127///
128/// As you can see, it's similar to Julia's `lb <: S <: ub` syntax for `TypeVar`s, except that
129/// `<:` has been replaced with `;` (because Rust macros don't allow `<:` in this position).
130#[macro_export]
131macro_rules! tvar {
132    ($name:ty) => {
133        $crate::data::types::construct_type::TypeVarConstructor::<$name>
134    };
135    ($name:literal) => {
136        $crate::data::types::construct_type::TypeVarConstructor::<
137            $crate::data::types::construct_type::Name<$name>,
138        >
139    };
140    ($name:literal; $ub:ty) => {
141        $crate::data::types::construct_type::TypeVarConstructor::<
142            $crate::data::types::construct_type::Name<$name>,
143            $ub,
144        >
145    };
146    ($name:ty; $ub:ty) => {
147        $crate::data::types::construct_type::TypeVarConstructor::<$name, $ub>
148    };
149    ($lb:ty; $name:literal; $ub:ty) => {
150        $crate::data::types::construct_type::TypeVarConstructor::<
151            $crate::data::types::construct_type::Name<$name>,
152            $ub,
153            $lb,
154        >
155    };
156    ($lb:ty; $name:ty; $ub:ty) => {
157        $crate::data::types::construct_type::TypeVarConstructor::<$name, $ub, $lb>
158    };
159}
160
161/// Combine multiple [`TypeVarConstructor`]s into a single type. The resulting type implements
162/// [`TypeVars`].
163///
164/// This macro has a very niche application: it can be used with the [`julia_module`] macro to
165/// expose a function with a signature that has a where-clause, for example
166///
167/// `function foo(a::A) where {T, N, A <: AbstractArray{T, N}} end`.
168///
169/// This macro is used to generate the `{T, N, A <: AbstractArray{T, N}}` PART.
170///
171/// [`julia_module`]: crate::prelude::julia_module
172#[macro_export]
173macro_rules! tvars {
174    ($t:ty) => {
175        $t
176    };
177    ($t1:ty, $R:ty) => {
178        $crate::data::types::construct_type::TypeVarFragment<$t1, $R>
179    };
180    ($t1:ty, $R:ty, $($rest:ty),+) => {
181        $crate::data::types::construct_type::TypeVarFragment<$t1, tvars!($R, $($rest),+)>
182    };
183}
184
185/// Encode bytes into `ConstantBytes`.
186///
187/// See [`ConstantBytes`] for more information.
188#[macro_export]
189macro_rules! bytes {
190    ($t1:literal, $R:literal) => {
191        $crate::data::types::construct_type::ConstantBytes<
192            $crate::data::types::construct_type::ConstantU8<$t1>,
193            $crate::data::types::construct_type::ConstantU8<$R>
194        >
195    };
196    ($t1:literal, $R:literal, $($rest:literal),+) => {
197        $crate::data::types::construct_type::ConstantBytes<
198            $crate::data::types::construct_type::ConstantU8<$t1>,
199            $crate::bytes!($R, $($rest),+)
200        >
201    };
202}
203
204/// Associate a Julia type object with a Rust type.
205///
206/// Safety:
207///
208/// `ConstructType::construct_type` must either return a valid type object, or an instance of an
209/// isbits type which is immediately used as a type parameter of another constructed type.
210#[diagnostic::on_unimplemented(
211    message = "the trait bound `{Self}: ConstructType` is not satisfied",
212    label = "the trait `ConstructType` is not implemented for `{Self}`",
213    note = "Custom types that implement `ConstructType` should be generated with JlrsCore.reflect",
214    note = "Do not implement `ForeignType` or `OpaqueType` unless this type is exported to Julia with `julia_module!`"
215)]
216
217pub unsafe trait ConstructType: Sized {
218    /// `Self`, but with all lifetimes set to `'static`. This ensures `Self::Static` has a type
219    /// id.
220    type Static: 'static + ConstructType;
221
222    /// Indicates whether the type might be cacheable.
223    ///
224    /// If set to `false`, `construct_type` will never try to cache or look up the
225    /// constructed type. It should be set to `false` if the constructed type is not globally
226    /// rooted.
227    const CACHEABLE: bool = true;
228
229    /// Returns the `TypeId` of `Self::Static`.
230    #[inline(always)]
231    fn type_id() -> TypeId {
232        TypeId::of::<Self::Static>()
233    }
234
235    /// Construct the type object and try to cache the result. If a cached entry is available, it
236    /// is returned.
237    #[inline]
238    fn construct_type<'target, Tgt>(target: Tgt) -> ValueData<'target, 'static, Tgt>
239    where
240        Tgt: Target<'target>,
241    {
242        if Self::CACHEABLE {
243            unsafe {
244                CONSTRUCTED_TYPE_CACHE
245                    .find_or_construct::<Self>()
246                    .root(target)
247            }
248        } else {
249            Self::construct_type_uncached(target)
250        }
251    }
252
253    /// Constructs the type object associated with this type.
254    fn construct_type_uncached<'target, Tgt>(target: Tgt) -> ValueData<'target, 'static, Tgt>
255    where
256        Tgt: Target<'target>;
257
258    /// Construct the type object with an environment of `TypeVar`s and try to cache the result.
259    /// If a cached entry is available, it is returned.
260    ///
261    /// No new type vars are constructed, if one is used and it don't already exist in `env`,
262    /// this method panics. The result may have free `TypeVar`s, you can call
263    /// [`DataType::wrap_with_env`] to create the appropriate `UnionAll`.
264    #[inline]
265    fn construct_type_with_env<'target, Tgt>(
266        target: Tgt,
267        env: &TypeVarEnv,
268    ) -> ValueData<'target, 'static, Tgt>
269    where
270        Tgt: Target<'target>,
271    {
272        if Self::CACHEABLE {
273            unsafe {
274                CONSTRUCTED_TYPE_CACHE
275                    .find_or_construct_with_env::<Self>(env)
276                    .root(target)
277            }
278        } else {
279            Self::construct_type_with_env_uncached(target, env)
280        }
281    }
282
283    /// Constructs the type object associated with this type.
284    ///
285    /// No new type vars are constructed, if one is used and it don't already exist in `env`,
286    /// this method panics.
287    fn construct_type_with_env_uncached<'target, Tgt>(
288        target: Tgt,
289        env: &TypeVarEnv,
290    ) -> ValueData<'target, 'static, Tgt>
291    where
292        Tgt: Target<'target>;
293
294    /// Returns the base type object associated with this type.
295    ///
296    /// The base type object is the type object without any types applied to it. If there is no
297    /// such type object, e.g. when `Self` is a value type, `None` is returned. The base type must
298    /// be globally rooted.
299    fn base_type<'target, Tgt>(target: &Tgt) -> Option<Value<'target, 'static>>
300    where
301        Tgt: Target<'target>;
302}
303
304/// Returns the type constructed with the `ConstructType` implementation of `T1` if it is a
305/// concrete type, otherwise that of `T2`.
306pub struct IfConcreteElse<T1: ConstructType, T2: ConstructType> {
307    _marker: PhantomData<(T1, T2)>,
308}
309
310unsafe impl<T1: ConstructType, T2: ConstructType> ConstructType for IfConcreteElse<T1, T2> {
311    type Static = IfConcreteElse<T1::Static, T2::Static>;
312
313    fn construct_type_uncached<'target, Tgt>(target: Tgt) -> ValueData<'target, 'static, Tgt>
314    where
315        Tgt: Target<'target>,
316    {
317        let t1 = T1::construct_type(&target);
318        unsafe {
319            let v = t1.as_value();
320            if v.is::<DataType>() {
321                if v.cast_unchecked::<DataType>().is_concrete_type() {
322                    return t1.root(target);
323                }
324            }
325
326            T2::construct_type(target)
327        }
328    }
329
330    fn construct_type_with_env_uncached<'target, Tgt>(
331        target: Tgt,
332        env: &crate::data::types::construct_type::TypeVarEnv,
333    ) -> ValueData<'target, 'static, Tgt>
334    where
335        Tgt: Target<'target>,
336    {
337        let t1 = T1::construct_type_with_env_uncached(&target, env);
338        unsafe {
339            let v = t1.as_value();
340            if v.is::<DataType>() {
341                if v.cast_unchecked::<DataType>().is_concrete_type() {
342                    return t1.root(target);
343                }
344            }
345
346            T2::construct_type_with_env_uncached(target, env)
347        }
348    }
349
350    fn base_type<'target, Tgt>(target: &Tgt) -> Option<Value<'target, 'static>>
351    where
352        Tgt: Target<'target>,
353    {
354        let t1 = T1::construct_type(&target);
355        unsafe {
356            let v = t1.as_value();
357            if v.is::<DataType>() {
358                if v.cast_unchecked::<DataType>().is_concrete_type() {
359                    return T1::base_type(target);
360                }
361            }
362
363            T2::base_type(target)
364        }
365    }
366}
367
368/// Cache a single constructible type.
369///
370/// By default, constructible types with type parameters are significantly slower to access than
371/// types without type parameters. The reason is that types without type parameters can be cached
372/// in a local static variable, while types with type parameters are stored in a global hash map.
373///
374/// It's not possible to generally cache types with type parameters in a local static variable,
375/// but specific types can be cached by implementing this trait for a new type.
376///
377/// Safety: the constructed type must not have any free type parameters, [`define_fast_key`]
378/// should be used to create the type and implementation.
379///
380/// Example:
381///
382/// ```
383/// # use jlrs::prelude::*;
384/// use jlrs::data::types::{abstract_type::AbstractSet, construct_type::{ConstructType, Key}};
385///
386/// define_fast_key!(pub AbstractSetF64, AbstractSet<f64>);
387///
388/// # fn main() {
389/// # let mut julia = Builder::new().start_local().unwrap();
390///
391/// julia.local_scope::<_, 2>(|mut frame| {
392///     let ty = Key::<AbstractSetF64>::construct_type(&mut frame);
393///     let ty2 = AbstractSet::<f64>::construct_type(&mut frame);
394///     assert_eq!(ty, ty2);
395/// });
396/// # }
397/// ```
398pub unsafe trait FastKey: 'static {
399    type For: ConstructType;
400    fn construct_type_fast<'target, Tgt>(target: &Tgt) -> Value<'target, 'static>
401    where
402        Tgt: Target<'target>;
403}
404
405/// Cache a single constructible array type.
406///
407/// Similar to [`FastKey`] but specifically intended to be used with array types. The implementation
408/// must not be generic over `N`, but must be set to a specific, non-negative value.
409///
410/// All implementations of `FastArrayKey` implement [`ConstructTypedArray`].
411///
412/// Safety: the constructed type must not have any free type parameters and must be an array type,
413/// [`define_fast_array_key!`] should be used to create the type and implementation.
414///
415/// Example:
416///
417/// ```
418/// # use jlrs::prelude::*;
419/// use jlrs::data::types::construct_type::{ConstructType, Key};
420///
421/// define_fast_array_key!(pub VecF32, f32, 1);
422///
423/// # fn main() {
424/// # let mut julia = Builder::new().start_local().unwrap();
425///
426/// julia.local_scope::<_, 3>(|mut frame| {
427///     let ty = Key::<VecF32>::construct_type(&mut frame);
428///     let ty2 = TypedRankedArray::<f32, 1>::construct_type(&mut frame);
429///     assert_eq!(ty, ty2);
430///
431///     let v = VecF32::new(&mut frame, 4);
432///     assert!(v.is_ok());
433/// });
434/// # }
435/// ```
436pub unsafe trait FastArrayKey<const N: isize>: 'static + FastKey {
437    /// The element type of this array type.
438    type ElemType: ConstructType;
439
440    /// Assert that `Self::RANK` is non-negative.
441    const ASSERT_VALID_RANK: () = assert!(N >= 0, "Array rank must be non-negative");
442}
443
444impl<T: FastArrayKey<N>, const N: isize> ConstructTypedArray<T::ElemType, N> for T {
445    #[inline]
446    fn array_type<'target, D, Tgt>(target: Tgt, _dims: &D) -> ValueData<'target, 'static, Tgt>
447    where
448        Tgt: Target<'target>,
449        D: DimsExt,
450    {
451        let _ = Self::ASSERT_VALID_RANK;
452        Key::<T>::construct_type(target)
453    }
454}
455
456/// Type used to expose [`ConstructType`] for types that implement [`FastKey`].
457///
458/// See [`FastKey`] and [`FastArrayKey`] for examples.
459pub struct Key<K: FastKey>(PhantomData<K>);
460
461unsafe impl<K: FastKey> ConstructType for Key<K> {
462    type Static = <K::For as ConstructType>::Static;
463    const CACHEABLE: bool = false;
464
465    fn construct_type_uncached<'target, Tgt>(target: Tgt) -> ValueData<'target, 'static, Tgt>
466    where
467        Tgt: Target<'target>,
468    {
469        K::construct_type_fast(&target).root(target)
470    }
471
472    fn construct_type_with_env_uncached<'target, Tgt>(
473        target: Tgt,
474        _env: &TypeVarEnv,
475    ) -> ValueData<'target, 'static, Tgt>
476    where
477        Tgt: Target<'target>,
478    {
479        K::construct_type_fast(&target).root(target)
480    }
481
482    fn base_type<'target, Tgt>(target: &Tgt) -> Option<Value<'target, 'static>>
483    where
484        Tgt: Target<'target>,
485    {
486        unsafe {
487            let v = K::construct_type_fast(target);
488            if v.is::<DataType>() {
489                let dt = v.cast_unchecked::<DataType>();
490                Some(dt.type_name().wrapper())
491            } else {
492                Some(v)
493            }
494        }
495    }
496}
497
498unsafe impl<K> AbstractType for Key<K>
499where
500    K: FastKey,
501    K::For: AbstractType,
502{
503}
504
505/// One or more `TypeVar`s. Types that implement this trait should be generated with the [`tvars`]
506/// and [`tvar`] macros.
507pub trait TypeVars {
508    /// The number of `TypeVar`s encoded by `Self`.
509    const SIZE: usize;
510
511    /// Construct the `TypeVars` and convert them to a context that can be used with
512    /// [`ConstructType::construct_type_with_env`].
513    fn into_env<'target, Tgt: RootingTarget<'target>>(target: Tgt) -> TypeVarEnv<'target>;
514
515    #[doc(hidden)]
516    // internal trait method used by `into_context`.
517    fn extend_env<'target, Tgt: Target<'target>>(target: &Tgt, env: &mut TypeVarEnv, offset: usize);
518}
519
520impl<N: TypeVarName, U: ConstructType, L: ConstructType> TypeVars for TypeVarConstructor<N, U, L> {
521    const SIZE: usize = 1;
522
523    fn into_env<'target, Tgt: RootingTarget<'target>>(target: Tgt) -> TypeVarEnv<'target> {
524        target.with_local_scope::<_, 1>(|target, mut frame| {
525            let svec = SimpleVector::with_capacity(&mut frame, Self::SIZE);
526            let mut env = TypeVarEnv { svec };
527
528            Self::extend_env(&frame, &mut env, 0);
529
530            let svec = Tgt::into_concrete_type(svec.root(target));
531            TypeVarEnv { svec }
532        })
533    }
534
535    fn extend_env<'target, Tgt: Target<'target>>(
536        target: &Tgt,
537        env: &mut TypeVarEnv,
538        offset: usize,
539    ) {
540        target.local_scope::<_, 1>(|mut frame| {
541            let sym = N::symbol(&frame);
542            if let Some(_) = env.get(sym) {
543                panic!("Duplicate tvar");
544            }
545
546            let tvar = Self::new(&mut frame, env);
547            env.set(offset, tvar);
548        })
549    }
550}
551
552/// Type that combines two or more `TypeVarConstructor`s.
553///
554/// Rust doesn't have variadic generics, which prevents us from writing `TypeVars<TV1, TV2, ...>`,
555/// instead this type lets us build it recursively:
556/// `TypeVarFragment<TV1, TypeVarFragment<TV2, ...>>`. It's neither necessary nor recommended to
557/// write out this type manually, you should use the [`tvars`] macro instead:
558/// `tvars!(TV1, TV2, ...)`.
559pub struct TypeVarFragment<T1: TypeVars, R: TypeVars>(PhantomData<T1>, PhantomData<R>);
560
561impl<T1: TypeVars, R: TypeVars> TypeVars for TypeVarFragment<T1, R> {
562    const SIZE: usize = T1::SIZE + R::SIZE;
563
564    fn into_env<'target, Tgt: RootingTarget<'target>>(target: Tgt) -> TypeVarEnv<'target> {
565        target.with_local_scope::<_, 1>(|target, mut frame| {
566            let svec = SimpleVector::with_capacity(&mut frame, Self::SIZE);
567            let mut env = TypeVarEnv { svec };
568
569            T1::extend_env(&frame, &mut env, 0);
570            R::extend_env(&frame, &mut env, T1::SIZE);
571
572            let svec = Tgt::into_concrete_type(svec.root(target));
573            TypeVarEnv { svec }
574        })
575    }
576
577    fn extend_env<'target, Tgt: Target<'target>>(
578        target: &Tgt,
579        env: &mut TypeVarEnv,
580        offset: usize,
581    ) {
582        T1::extend_env(target, env, offset);
583        R::extend_env(target, env, offset + T1::SIZE);
584    }
585}
586
587/// An environment of [`TypeVar`]s, i.e. all `TypeVar`s that appear in a function signature.
588#[derive(Debug)]
589pub struct TypeVarEnv<'scope> {
590    svec: SimpleVector<'scope>,
591}
592
593impl<'scope> TypeVarEnv<'scope> {
594    /// Returns the `TypeVar` with name `sym` if it exists.
595    pub fn get(&self, sym: Symbol) -> Option<TypeVar<'scope>> {
596        let unrooted = sym.unrooted_target();
597        let svec = self.svec.data();
598
599        (0..svec.len())
600            .filter_map(|idx| svec.get(unrooted, idx))
601            .map(|elem| unsafe { elem.as_value().cast_unchecked::<TypeVar>() })
602            .find(|elem| elem.name() == sym)
603            .map(|elem| unsafe { elem.as_weak().leak().as_managed() })
604    }
605
606    /// Returns `true` if the environment is empty.
607    pub fn is_empty(&self) -> bool {
608        self.svec.len() == 0
609    }
610
611    /// Create an empty environment.
612    pub fn empty<Tgt: Target<'scope>>(tgt: &Tgt) -> Self {
613        TypeVarEnv {
614            svec: SimpleVector::emptysvec(tgt),
615        }
616    }
617
618    /// Access this environment as a `SimpleVector`.
619    pub fn to_svec(&self) -> SimpleVector<'scope> {
620        self.svec
621    }
622
623    fn set(&mut self, offset: usize, tvar: TypeVar) {
624        unsafe {
625            let len = self.svec.len();
626            assert!(offset < len);
627            let data = self.svec.data();
628            data.set(offset, Some(tvar.as_value())).unwrap();
629        }
630    }
631}
632
633macro_rules! impl_construct_julia_type_constant {
634    ($ty:ty, $const_ty:ty) => {
635        unsafe impl<const N: $const_ty> ConstructType for $ty {
636            type Static = $ty;
637
638            const CACHEABLE: bool = false;
639
640            #[inline]
641            fn construct_type_uncached<'target, Tgt>(
642                target: Tgt,
643            ) -> ValueData<'target, 'static, Tgt>
644            where
645                Tgt: Target<'target>,
646            {
647                Value::new(target, N)
648            }
649
650            #[inline]
651            fn construct_type_with_env_uncached<'target, Tgt>(
652                target: Tgt,
653                _: &TypeVarEnv,
654            ) -> ValueData<'target, 'static, Tgt>
655            where
656                Tgt: Target<'target>,
657            {
658                Value::new(target, N)
659            }
660
661            #[inline]
662            fn base_type<'target, Tgt>(_target: &Tgt) -> Option<Value<'target, 'static>>
663            where
664                Tgt: Target<'target>,
665            {
666                None
667            }
668        }
669    };
670}
671
672macro_rules! impl_construct_julia_type_constant_cached {
673    ($ty:ty, $const_ty:ty) => {
674        unsafe impl<const N: $const_ty> ConstructType for $ty {
675            type Static = $ty;
676
677            const CACHEABLE: bool = true;
678
679            #[inline]
680            fn construct_type_uncached<'target, Tgt>(
681                target: Tgt,
682            ) -> ValueData<'target, 'static, Tgt>
683            where
684                Tgt: Target<'target>,
685            {
686                Value::new(target, N)
687            }
688
689            #[inline]
690            fn construct_type_with_env_uncached<'target, Tgt>(
691                target: Tgt,
692                _: &TypeVarEnv,
693            ) -> ValueData<'target, 'static, Tgt>
694            where
695                Tgt: Target<'target>,
696            {
697                Value::new(target, N)
698            }
699
700            #[inline]
701            fn base_type<'target, Tgt>(_target: &Tgt) -> Option<Value<'target, 'static>>
702            where
703                Tgt: Target<'target>,
704            {
705                None
706            }
707        }
708    };
709}
710
711macro_rules! impl_construct_julia_type_primitive {
712    ($ty:ty, $jl_ty:ident) => {
713        unsafe impl ConstructType for $ty {
714            type Static = $ty;
715
716            const CACHEABLE: bool = false;
717
718            #[inline]
719            fn construct_type_uncached<'target, Tgt>(
720                target: Tgt,
721            ) -> ValueData<'target, 'static, Tgt>
722            where
723                Tgt: Target<'target>,
724            {
725                unsafe {
726                    let ptr = NonNull::new_unchecked($jl_ty.cast::<jl_value_t>());
727                    target.data_from_ptr(ptr, Private)
728                }
729            }
730
731            #[inline]
732            fn construct_type_with_env_uncached<'target, Tgt>(
733                target: Tgt,
734                _env: &TypeVarEnv,
735            ) -> ValueData<'target, 'static, Tgt>
736            where
737                Tgt: Target<'target>,
738            {
739                unsafe {
740                    let ptr = NonNull::new_unchecked($jl_ty.cast::<jl_value_t>());
741                    target.data_from_ptr(ptr, Private)
742                }
743            }
744
745            #[inline]
746            fn base_type<'target, Tgt>(_target: &Tgt) -> Option<Value<'target, 'static>>
747            where
748                Tgt: Target<'target>,
749            {
750                unsafe {
751                    let ptr = NonNull::new_unchecked($jl_ty.cast::<jl_value_t>());
752                    Some(
753                        <Value as $crate::data::managed::private::ManagedPriv>::wrap_non_null(
754                            ptr,
755                            $crate::private::Private,
756                        ),
757                    )
758                }
759            }
760        }
761    };
762}
763
764/// Constant `i8`.
765pub struct ConstantI8<const N: i8>;
766impl_construct_julia_type_constant_cached!(ConstantI8<N>, i8);
767
768/// Constant `i16`.
769pub struct ConstantI16<const N: i16>;
770impl_construct_julia_type_constant!(ConstantI16<N>, i16);
771
772/// Constant `i32`.
773pub struct ConstantI32<const N: i32>;
774impl_construct_julia_type_constant!(ConstantI32<N>, i32);
775
776/// Constant `i64`.
777pub struct ConstantI64<const N: i64>;
778impl_construct_julia_type_constant!(ConstantI64<N>, i64);
779
780/// Constant `isize`.
781pub struct ConstantIsize<const N: isize>;
782impl_construct_julia_type_constant!(ConstantIsize<N>, isize);
783
784/// Constant `isize`.
785pub struct ConstantSize<const N: usize>;
786unsafe impl<const N: usize> ConstructType for ConstantSize<N> {
787    type Static = ConstantSize<N>;
788
789    const CACHEABLE: bool = false;
790
791    #[inline]
792    fn construct_type_uncached<'target, Tgt>(target: Tgt) -> ValueData<'target, 'static, Tgt>
793    where
794        Tgt: Target<'target>,
795    {
796        Value::new(target, N as isize)
797    }
798
799    #[inline]
800    fn base_type<'target, Tgt>(_target: &Tgt) -> Option<Value<'target, 'static>>
801    where
802        Tgt: Target<'target>,
803    {
804        None
805    }
806
807    fn construct_type_with_env_uncached<'target, Tgt>(
808        target: Tgt,
809        _env: &TypeVarEnv,
810    ) -> ValueData<'target, 'static, Tgt>
811    where
812        Tgt: Target<'target>,
813    {
814        Value::new(target, N as isize)
815    }
816}
817
818/// Constant `u8`.
819pub struct ConstantU8<const N: u8>;
820impl_construct_julia_type_constant_cached!(ConstantU8<N>, u8);
821
822/// Constant `u16`.
823pub struct ConstantU16<const N: u16>;
824impl_construct_julia_type_constant!(ConstantU16<N>, u16);
825
826/// Constant `u32`.
827pub struct ConstantU32<const N: u32>;
828impl_construct_julia_type_constant!(ConstantU32<N>, u32);
829
830/// Constant `u64`.
831pub struct ConstantU64<const N: u64>;
832impl_construct_julia_type_constant!(ConstantU64<N>, u64);
833
834/// Constant `usize`.
835pub struct ConstantUsize<const N: usize>;
836impl_construct_julia_type_constant!(ConstantUsize<N>, usize);
837
838/// Constant `bool`.
839pub struct ConstantBool<const N: bool>;
840impl_construct_julia_type_constant!(ConstantBool<N>, bool);
841
842/// Constant `char`.
843pub struct ConstantChar<const N: char>;
844impl_construct_julia_type_constant!(ConstantChar<N>, char);
845
846/// Trait implemented by types that encode bytes.
847pub trait EncodedBytes: 'static {
848    type B: AsRef<[u8]>;
849    fn encoded_bytes() -> Self::B;
850}
851
852/// Trait implemented by types that encode a string.
853pub trait EncodedString: 'static {
854    type S: AsRef<str>;
855    fn encoded_string() -> Self::S;
856}
857
858/// Constant string. Not a type constructor, but can be used to construct `TypeVar`s with names
859/// longer than one character.
860pub trait ConstantStr: 'static {
861    /// The string constant.
862    const STR: &'static str;
863}
864
865impl<S: ConstantStr> EncodedString for S {
866    type S = &'static str;
867    fn encoded_string() -> Self::S {
868        Self::STR
869    }
870}
871
872/// Constant byte slice.
873pub trait ConstantByteSlice: 'static {
874    /// The byte slice constant.
875    const BYTES: &'static [u8];
876}
877
878impl<S: ConstantByteSlice> EncodedBytes for S {
879    type B = &'static [u8];
880    fn encoded_bytes() -> Self::B {
881        Self::BYTES
882    }
883}
884
885/// Trait implemented by `ConstantU8` and `ConstantBytes` to build a list of constant bytes.
886pub trait ConstantBytesFragment: 'static {
887    /// The size of this fragment.
888    const SIZE: usize;
889
890    #[doc(hidden)]
891    fn extend(slice: &mut [u8], offset: usize);
892}
893
894/// Constant bytes.
895///
896/// `ConstantBytes` implements `TypeVarName`. In general you should prefer to implement
897/// [`ConstantStr`] over using `ConstantBytes`.
898///
899/// While it isn't possible to use static string slice as a const generic parameter, it is
900/// possible to recursively encode its bytes into a type. For example, the string "Foo" can be
901/// represented as follows:
902///
903/// `type Foo = ConstantBytes<ConstantU8<70>, ConstantBytes<ConstantU8<111>, ConstantU8<111>>>`.
904///
905/// The [`bytes`] macro is less verbose, but only accepts `u8`'s:
906///
907/// `type Foo = bytes!(70, 111, 111)`
908///
909/// The [`encode_as_constant_bytes`] macro converts a string literal to `ConstantBytes`:
910///
911/// `type Foo = encode_as_constant_bytes!("Foo")`.
912///
913/// The main advantage of `encode_as_constant_bytes` is that it doesn't represent the string as a
914/// list like the `bytes` macro does, but represents it as a tree with the minimal depth:
915///
916/// `type Foo = ConstantBytes<ConstantBytes<ConstantU8<70>, ConstantU8<111>>, ConstantU8<111>>`
917///
918/// [`encode_as_constant_bytes`]: jlrs_macros::encode_as_constant_bytes
919pub struct ConstantBytes<L: ConstantBytesFragment, R: ConstantBytesFragment>(
920    PhantomData<L>,
921    PhantomData<R>,
922);
923
924impl<L: ConstantBytesFragment, R: ConstantBytesFragment> ConstantBytes<L, R> {
925    /// Convert the encoded bytes to `Vec<u8>`.
926    pub fn into_vec() -> Vec<u8> {
927        let mut v = vec![0; Self::SIZE];
928        Self::extend(v.as_mut_slice(), 0);
929        v
930    }
931
932    /// Try to convert the encoded bytes into a string.
933    pub fn into_string() -> Result<String, FromUtf8Error> {
934        let v = Self::into_vec();
935        String::from_utf8(v)
936    }
937}
938
939impl<L: ConstantBytesFragment, R: ConstantBytesFragment> EncodedBytes for ConstantBytes<L, R> {
940    type B = Vec<u8>;
941    fn encoded_bytes() -> Vec<u8> {
942        Self::into_vec()
943    }
944}
945
946impl<L: ConstantBytesFragment, R: ConstantBytesFragment> EncodedString for ConstantBytes<L, R> {
947    type S = String;
948    fn encoded_string() -> String {
949        Self::into_string().expect("Invalid string")
950    }
951}
952
953impl<L: ConstantBytesFragment, R: ConstantBytesFragment> TypeVarName for ConstantBytes<L, R> {
954    fn symbol<'target, Tgt: Target<'target>>(target: &Tgt) -> Symbol<'target> {
955        Self::into_string()
956            .expect("Invalid string")
957            .to_symbol(target)
958    }
959}
960
961impl<const N: u8> ConstantBytesFragment for ConstantU8<N> {
962    const SIZE: usize = 1;
963
964    #[inline]
965    fn extend(slice: &mut [u8], offset: usize) {
966        slice[offset] = N;
967    }
968}
969
970impl<L: ConstantBytesFragment, R: ConstantBytesFragment> ConstantBytesFragment
971    for ConstantBytes<L, R>
972{
973    const SIZE: usize = L::SIZE + R::SIZE;
974
975    #[inline]
976    fn extend(slice: &mut [u8], offset: usize) {
977        L::extend(slice, offset);
978        R::extend(slice, offset + L::SIZE);
979    }
980}
981
982/// The name of a `TypeVar`, alternative for [`ConstantChar`].
983pub struct Name<const N: char>;
984
985/// Trait to set the name of a `TypeVar`.
986///
987/// Implemented by [`Name`], [`ConstantChar`], and implementations of [`ConstantStr`].
988pub trait TypeVarName: 'static {
989    /// Returns the name as a symbol.
990    fn symbol<'target, Tgt: Target<'target>>(target: &Tgt) -> Symbol<'target>;
991}
992
993impl<const N: char> TypeVarName for Name<N> {
994    #[inline]
995    fn symbol<'target, Tgt: Target<'target>>(target: &Tgt) -> Symbol<'target> {
996        let mut bytes = [0; 4];
997        let s = N.encode_utf8(&mut bytes);
998        s.to_symbol(target)
999    }
1000}
1001
1002impl<const N: char> TypeVarName for ConstantChar<N> {
1003    #[inline]
1004    fn symbol<'target, Tgt: Target<'target>>(target: &Tgt) -> Symbol<'target> {
1005        let mut bytes = [0; 4];
1006        let s = N.encode_utf8(&mut bytes);
1007        s.to_symbol(target)
1008    }
1009}
1010
1011impl<T: ConstantStr> TypeVarName for T {
1012    #[inline]
1013    fn symbol<'target, Tgt: Target<'target>>(target: &Tgt) -> Symbol<'target> {
1014        Self::STR.to_symbol(target)
1015    }
1016}
1017
1018/// Construct a new `TypeVar` from the provided type parameters.
1019pub struct TypeVarConstructor<
1020    N: TypeVarName,
1021    U: ConstructType = AnyType,
1022    L: ConstructType = BottomType,
1023> {
1024    _name: PhantomData<N>,
1025    _upper: PhantomData<U>,
1026    _lower: PhantomData<L>,
1027}
1028
1029impl<N: TypeVarName, U: ConstructType, L: ConstructType> TypeVarConstructor<N, U, L> {
1030    fn new<'target, Tgt>(target: Tgt, env: &TypeVarEnv) -> TypeVarData<'target, Tgt>
1031    where
1032        Tgt: Target<'target>,
1033    {
1034        target.with_local_scope::<_, 2>(|target, mut frame| {
1035            let upper_bound = U::construct_type_with_env(&mut frame, env);
1036            let lower_bound = L::construct_type_with_env(&mut frame, env);
1037            unsafe {
1038                TypeVar::new_unchecked(
1039                    &target,
1040                    N::symbol(&target),
1041                    Some(lower_bound),
1042                    Some(upper_bound),
1043                )
1044                .root(target)
1045            }
1046        })
1047    }
1048}
1049
1050unsafe impl<N: TypeVarName, U: ConstructType, L: ConstructType> ConstructType
1051    for TypeVarConstructor<N, U, L>
1052{
1053    type Static = TypeVarConstructor<N, U::Static, L::Static>;
1054
1055    fn construct_type_uncached<'target, Tgt>(target: Tgt) -> ValueData<'target, 'static, Tgt>
1056    where
1057        Tgt: Target<'target>,
1058    {
1059        target.with_local_scope::<_, 2>(|target, mut frame| {
1060            let upper_bound = U::construct_type(&mut frame);
1061            let lower_bound = L::construct_type(&mut frame);
1062            unsafe {
1063                TypeVar::new_unchecked(
1064                    &target,
1065                    N::symbol(&target),
1066                    Some(lower_bound),
1067                    Some(upper_bound),
1068                )
1069                .as_value()
1070                .root(target)
1071            }
1072        })
1073    }
1074
1075    #[inline]
1076    fn base_type<'target, Tgt>(target: &Tgt) -> Option<Value<'target, 'static>>
1077    where
1078        Tgt: Target<'target>,
1079    {
1080        Some(DataType::tvar_type(target).as_value())
1081    }
1082
1083    fn construct_type_with_env_uncached<'target, Tgt>(
1084        target: Tgt,
1085        env: &TypeVarEnv,
1086    ) -> ValueData<'target, 'static, Tgt>
1087    where
1088        Tgt: Target<'target>,
1089    {
1090        let sym = N::symbol(&target);
1091        env.get(sym).unwrap().as_value().root(target)
1092    }
1093}
1094
1095/// Construct a new `Array` type from the provided type parameters.
1096pub struct ArrayTypeConstructor<T: ConstructType, N: ConstructType> {
1097    _type: PhantomData<T>,
1098    _rank: PhantomData<N>,
1099}
1100
1101unsafe impl<T: ConstructType, N: ConstructType> ConstructType for ArrayTypeConstructor<T, N> {
1102    type Static = ArrayTypeConstructor<T::Static, N::Static>;
1103
1104    fn construct_type_uncached<'target, Tgt>(target: Tgt) -> ValueData<'target, 'static, Tgt>
1105    where
1106        Tgt: Target<'target>,
1107    {
1108        unsafe {
1109            target.with_local_scope::<_, 3>(|target, mut frame| {
1110                let ty_param = T::construct_type(&mut frame);
1111                let rank_param = N::construct_type(&mut frame);
1112                if rank_param.is::<isize>() {
1113                    if rank_param.unbox_unchecked::<isize>() < 0 {
1114                        panic!("ArrayTypeConstructor rank must be a TypeVar or non-negative ConstantIsize, got {rank_param:?}")
1115                    }
1116                }
1117                let params = [ty_param, rank_param];
1118                Self::base_type(&frame)
1119                    .unwrap_unchecked()
1120                    .apply_type_unchecked(&mut frame, params)
1121                    .cast_unchecked::<DataType>()
1122                    .rewrap(target)
1123            })
1124        }
1125    }
1126
1127    #[inline]
1128    fn base_type<'target, Tgt>(target: &Tgt) -> Option<Value<'target, 'static>>
1129    where
1130        Tgt: Target<'target>,
1131    {
1132        let wrapper = UnionAll::array_type(target).as_value();
1133        Some(wrapper)
1134    }
1135
1136    fn construct_type_with_env_uncached<'target, Tgt>(
1137        target: Tgt,
1138        env: &TypeVarEnv,
1139    ) -> ValueData<'target, 'static, Tgt>
1140    where
1141        Tgt: Target<'target>,
1142    {
1143        unsafe {
1144            target.with_local_scope::<_, 3>(|target, mut frame| {
1145                let ty_param = T::construct_type_with_env(&mut frame, env);
1146                let rank_param = N::construct_type_with_env(&mut frame, env);
1147                if rank_param.is::<isize>() {
1148                    if rank_param.unbox_unchecked::<isize>() < 0 {
1149                        panic!("ArrayTypeConstructor rank must be a TypeVar or non-negative ConstantIsize, got {rank_param:?}")
1150                    }
1151                }
1152                let params = [ty_param, rank_param];
1153                Self::base_type(&frame)
1154                    .unwrap_unchecked()
1155                    .apply_type_unchecked(&mut frame, params)
1156                    .cast_unchecked::<DataType>()
1157                    .wrap_with_env(target, env)
1158            })
1159        }
1160    }
1161}
1162
1163pub type RankedArrayType<T, const N: isize> = ArrayTypeConstructor<T, ConstantIsize<N>>;
1164
1165/// Converts two or more types into a nested `BitsUnionConstructor` type.
1166#[macro_export]
1167macro_rules! UnionOf {
1168    [$l:ty, $r:ty] => {
1169        $crate::data::types::construct_type::UnionTypeConstructor<$l, $r>
1170    };
1171    [$l:ty, $($rest:ty),+] => {
1172        $crate::UnionOf![$l, $crate::UnionOf![$($rest),+]]
1173    };
1174}
1175
1176/// Construct a new `Union` type from the provided type parameters. Larger unions can be built
1177/// by nesting `UnionTypeConstructor`.
1178pub struct UnionTypeConstructor<L: ConstructType, R: ConstructType> {
1179    _l: PhantomData<L>,
1180    _r: PhantomData<R>,
1181}
1182
1183unsafe impl<L: ConstructType, R: ConstructType> ConstructType for UnionTypeConstructor<L, R> {
1184    type Static = UnionTypeConstructor<L::Static, R::Static>;
1185
1186    fn construct_type_uncached<'target, Tgt>(target: Tgt) -> ValueData<'target, 'static, Tgt>
1187    where
1188        Tgt: Target<'target>,
1189    {
1190        target.with_local_scope::<_, 2>(|target, mut frame| {
1191            let l = L::construct_type(&mut frame);
1192            let r = R::construct_type(&mut frame);
1193
1194            unsafe { Union::new_unchecked(target, [l, r]) }
1195        })
1196    }
1197
1198    #[inline]
1199    fn base_type<'target, Tgt>(_target: &Tgt) -> Option<Value<'target, 'static>>
1200    where
1201        Tgt: Target<'target>,
1202    {
1203        unsafe {
1204            let ptr = NonNull::new_unchecked(jl_uniontype_type.cast::<jl_value_t>());
1205            Some(
1206                <Value as crate::data::managed::private::ManagedPriv>::wrap_non_null(
1207                    ptr,
1208                    crate::private::Private,
1209                ),
1210            )
1211        }
1212    }
1213
1214    fn construct_type_with_env_uncached<'target, Tgt>(
1215        target: Tgt,
1216        env: &TypeVarEnv,
1217    ) -> ValueData<'target, 'static, Tgt>
1218    where
1219        Tgt: Target<'target>,
1220    {
1221        target.with_local_scope::<_, 2>(|target, mut frame| {
1222            let l = L::construct_type_with_env(&mut frame, env);
1223            let r = R::construct_type_with_env(&mut frame, env);
1224
1225            unsafe { Union::new_unchecked(target, [l, r]) }
1226        })
1227    }
1228}
1229
1230/// Trait implemented by `UnionTypeConstructor` if all variants are bits types.
1231pub trait BitsUnionCtor: BitsUnionCtorVariant {
1232    /// Returns the number of unique variants.
1233    fn n_unique_variants() -> usize {
1234        Self::get_variants().len()
1235    }
1236
1237    /// Returns the set of type ids of all unique variants, and the number of variants.
1238    fn get_variants() -> FxHashSet<TypeId>;
1239}
1240
1241/// Trait implemented by type constructors that have an `IsBits` layout, and unions of such types.
1242pub trait BitsUnionCtorVariant: ConstructType {
1243    const N: usize;
1244    #[doc(hidden)]
1245    fn add_variants(ids: &mut FxHashSet<TypeId>);
1246}
1247
1248impl<'scope, 'data, T> BitsUnionCtorVariant for T
1249where
1250    T: ConstructType + HasLayout<'scope, 'data>,
1251    T::Layout: IsBits,
1252{
1253    const N: usize = 1;
1254    fn add_variants(ids: &mut FxHashSet<TypeId>) {
1255        ids.insert(Self::type_id());
1256    }
1257}
1258
1259impl<L: BitsUnionCtorVariant, R: BitsUnionCtorVariant> BitsUnionCtorVariant
1260    for UnionTypeConstructor<L, R>
1261{
1262    const N: usize = L::N + R::N;
1263    fn add_variants(ids: &mut FxHashSet<TypeId>) {
1264        L::add_variants(ids);
1265        R::add_variants(ids);
1266    }
1267}
1268
1269impl<L: BitsUnionCtorVariant, R: BitsUnionCtorVariant> BitsUnionCtor
1270    for UnionTypeConstructor<L, R>
1271{
1272    fn get_variants() -> FxHashSet<TypeId> {
1273        let mut set = FxHashSet::<TypeId>::default();
1274
1275        L::add_variants(&mut set);
1276        R::add_variants(&mut set);
1277
1278        set
1279    }
1280}
1281
1282pub struct BottomType;
1283
1284unsafe impl ConstructType for BottomType {
1285    type Static = BottomType;
1286
1287    #[inline]
1288    fn construct_type_uncached<'target, Tgt>(target: Tgt) -> ValueData<'target, 'static, Tgt>
1289    where
1290        Tgt: Target<'target>,
1291    {
1292        unsafe {
1293            let ptr = NonNull::new_unchecked(jl_bottom_type.cast::<jl_value_t>());
1294            target.data_from_ptr(ptr, Private)
1295        }
1296    }
1297
1298    #[inline]
1299    fn base_type<'target, Tgt>(_target: &Tgt) -> Option<Value<'target, 'static>>
1300    where
1301        Tgt: Target<'target>,
1302    {
1303        unsafe {
1304            let ptr = NonNull::new_unchecked(jl_bottom_type.cast::<jl_value_t>());
1305            Some(
1306                <Value as crate::data::managed::private::ManagedPriv>::wrap_non_null(
1307                    ptr,
1308                    crate::private::Private,
1309                ),
1310            )
1311        }
1312    }
1313
1314    #[inline]
1315    fn construct_type_with_env_uncached<'target, Tgt>(
1316        target: Tgt,
1317        _env: &TypeVarEnv,
1318    ) -> ValueData<'target, 'static, Tgt>
1319    where
1320        Tgt: Target<'target>,
1321    {
1322        unsafe {
1323            let ptr = NonNull::new_unchecked(jl_bottom_type.cast::<jl_value_t>());
1324            target.data_from_ptr(ptr, Private)
1325        }
1326    }
1327}
1328
1329impl_construct_julia_type_primitive!(u8, jl_uint8_type);
1330impl_construct_julia_type_primitive!(u16, jl_uint16_type);
1331impl_construct_julia_type_primitive!(u32, jl_uint32_type);
1332impl_construct_julia_type_primitive!(u64, jl_uint64_type);
1333
1334#[cfg(target_pointer_width = "64")]
1335impl_construct_julia_type_primitive!(usize, jl_uint64_type);
1336#[cfg(target_pointer_width = "32")]
1337impl_construct_julia_type_primitive!(usize, jl_uint32_type);
1338
1339impl_construct_julia_type_primitive!(i8, jl_int8_type);
1340impl_construct_julia_type_primitive!(i16, jl_int16_type);
1341impl_construct_julia_type_primitive!(i32, jl_int32_type);
1342impl_construct_julia_type_primitive!(i64, jl_int64_type);
1343
1344#[cfg(target_pointer_width = "64")]
1345impl_construct_julia_type_primitive!(isize, jl_int64_type);
1346#[cfg(target_pointer_width = "32")]
1347impl_construct_julia_type_primitive!(isize, jl_int32_type);
1348
1349impl_construct_julia_type_primitive!(f32, jl_float32_type);
1350impl_construct_julia_type_primitive!(f64, jl_float64_type);
1351
1352impl_construct_julia_type_primitive!(bool, jl_bool_type);
1353impl_construct_julia_type_primitive!(char, jl_char_type);
1354
1355impl_construct_julia_type_primitive!(*mut c_void, jl_voidpointer_type);
1356
1357unsafe impl<U: ConstructType> ConstructType for *mut U {
1358    type Static = *mut U::Static;
1359
1360    fn construct_type_uncached<'target, Tgt>(target: Tgt) -> ValueData<'target, 'static, Tgt>
1361    where
1362        Tgt: Target<'target>,
1363    {
1364        target.with_local_scope::<_, 1>(|target, mut frame| {
1365            let ty = U::construct_type(&mut frame);
1366            unsafe {
1367                UnionAll::pointer_type(&frame)
1368                    .as_value()
1369                    .apply_type_unchecked(target, [ty])
1370            }
1371        })
1372    }
1373
1374    #[inline]
1375    fn base_type<'target, Tgt>(_target: &Tgt) -> Option<Value<'target, 'static>>
1376    where
1377        Tgt: Target<'target>,
1378    {
1379        unsafe {
1380            let ptr = NonNull::new_unchecked(jl_pointer_type.cast::<jl_value_t>());
1381            Some(
1382                <Value as crate::data::managed::private::ManagedPriv>::wrap_non_null(
1383                    ptr,
1384                    crate::private::Private,
1385                ),
1386            )
1387        }
1388    }
1389
1390    fn construct_type_with_env_uncached<'target, Tgt>(
1391        target: Tgt,
1392        env: &TypeVarEnv,
1393    ) -> ValueData<'target, 'static, Tgt>
1394    where
1395        Tgt: Target<'target>,
1396    {
1397        target.with_local_scope::<_, 1>(|target, mut frame| {
1398            let ty = U::construct_type_with_env(&mut frame, env);
1399            unsafe {
1400                UnionAll::pointer_type(&frame)
1401                    .as_value()
1402                    .apply_type_unchecked(target, [ty])
1403            }
1404        })
1405    }
1406}
1407
1408struct ConstructedTypes {
1409    data: Cache<FnvHashMap<TypeId, ValueUnbound>>,
1410}
1411
1412impl ConstructedTypes {
1413    const fn new() -> Self {
1414        let hasher = fnv::FnvBuildHasher::new();
1415        let map = std::collections::HashMap::with_hasher(hasher);
1416        let cache = Cache::new(map);
1417
1418        ConstructedTypes { data: cache }
1419    }
1420
1421    #[inline]
1422    fn find_or_construct<T: ConstructType>(&self) -> WeakValue<'static, 'static> {
1423        let tid = T::type_id();
1424        let res = unsafe {
1425            self.data.read(
1426                #[inline]
1427                |cache| {
1428                    if let Some(res) = cache.cache().get(&tid).copied() {
1429                        return Some(res.as_weak());
1430                    }
1431
1432                    None
1433                },
1434            )
1435        };
1436
1437        if let Some(res) = res {
1438            return res;
1439        }
1440
1441        do_construct::<T>(self, tid)
1442    }
1443
1444    #[inline]
1445    fn find_or_construct_with_env<T: ConstructType>(
1446        &self,
1447        env: &TypeVarEnv,
1448    ) -> WeakValue<'static, 'static> {
1449        let tid = T::type_id();
1450        let res = unsafe {
1451            self.data.read(
1452                #[inline]
1453                |cache| {
1454                    if let Some(res) = cache.cache().get(&tid).copied() {
1455                        return Some(res.as_weak());
1456                    }
1457
1458                    None
1459                },
1460            )
1461        };
1462
1463        if let Some(res) = res {
1464            return res;
1465        }
1466
1467        do_construct_with_context::<T>(self, tid, env)
1468    }
1469}
1470
1471// #[inline(never)]
1472#[cold]
1473fn do_construct<T: ConstructType>(
1474    ct: &ConstructedTypes,
1475    tid: TypeId,
1476) -> WeakValue<'static, 'static> {
1477    unsafe {
1478        let unrooted = Unrooted::new();
1479        unrooted.with_local_scope::<_, 1>(|target, mut frame| {
1480            let ty = T::construct_type_uncached(&mut frame);
1481
1482            if ty.is::<DataType>() {
1483                let dt = ty.cast_unchecked::<DataType>();
1484                if !dt.has_free_type_vars() && (!dt.is::<Tuple>() || dt.is_concrete_type()) {
1485                    ct.data.write(
1486                        #[inline]
1487                        |cache| {
1488                            cache.cache_mut().insert(tid, ty.leak().as_value());
1489                            cache.roots_mut().insert(ty.as_value());
1490                        },
1491                    );
1492                }
1493            } else if ty.is::<u8>() || ty.is::<i8>() {
1494                ct.data.write(|cache| {
1495                    cache.cache_mut().insert(tid, ty.leak().as_value());
1496                    cache.roots_mut().insert(ty.as_value());
1497                });
1498            }
1499
1500            ty.root(target)
1501        })
1502    }
1503}
1504
1505// #[inline(never)]
1506#[cold]
1507fn do_construct_with_context<T: ConstructType>(
1508    ct: &ConstructedTypes,
1509    tid: TypeId,
1510    env: &TypeVarEnv,
1511) -> WeakValue<'static, 'static> {
1512    unsafe {
1513        let unrooted = Unrooted::new();
1514        unrooted.with_local_scope::<_, 1>(|target, mut frame| {
1515            let ty = T::construct_type_with_env_uncached(&mut frame, env);
1516
1517            if ty.is::<DataType>() {
1518                let dt = ty.cast_unchecked::<DataType>();
1519                if !dt.has_free_type_vars() && (!dt.is::<Tuple>() || dt.is_concrete_type()) {
1520                    ct.data.write(
1521                        #[inline]
1522                        |cache| {
1523                            cache.cache_mut().insert(tid, ty.leak().as_value());
1524                            cache.roots_mut().insert(ty.as_value());
1525                        },
1526                    );
1527                }
1528            } else if ty.is::<u8>() || ty.is::<i8>() {
1529                ct.data.write(
1530                    #[inline]
1531                    |cache| {
1532                        cache.cache_mut().insert(tid, ty.leak().as_value());
1533                        cache.roots_mut().insert(ty.as_value());
1534                    },
1535                );
1536            }
1537
1538            ty.root(target)
1539        })
1540    }
1541}