jlrs/data/managed/value/
typed.rs

1//! A `Value` annotated with its type constructor
2//!
3//! When a Rust function is exported to Julia with the [`julia_module`] macro, the generated Julia
4//! function looks like this:
5//!
6//! ```julia
7//! function fn_name(arg1::FnArg1, arg2::FnArg2, ...)::FnRet
8//!     ccall(fn_ptr, CCallRet, (CCallArg1, CCallArg2, ...), arg1, arg2, ...)
9//! end
10//! ```
11//!
12//! The argument and return types are generated from the signature of the exported function. When
13//! `TypedValue<Ty>` is used as an argument, the `CCallArg` is `Any` and the `FnArg` is the type
14//! that is constructed from `Ty`. The same is true for `CCallRet` and `FnRet` when
15//! `TypedValueRet<Ty>` is returned.
16//!
17//! [`julia_module`]: ::jlrs_macros::julia_module
18
19use std::{
20    fmt::Debug,
21    marker::PhantomData,
22    ops::{Deref, DerefMut},
23    ptr::NonNull,
24};
25
26use jl_sys::jl_value_t;
27
28use super::{
29    Value, ValueData, WeakValue,
30    tracked::{Tracked, TrackedMut},
31};
32use crate::{
33    convert::{
34        ccall_types::{CCallArg, CCallReturn},
35        into_julia::IntoJulia,
36    },
37    data::{
38        layout::valid_layout::{ValidField, ValidLayout},
39        managed::{Managed, Weak, datatype::DataType, private::ManagedPriv},
40        types::{
41            abstract_type::AnyType,
42            construct_type::{ArrayTypeConstructor, ConstantIsize, ConstructType},
43            typecheck::Typecheck,
44        },
45    },
46    error::{JlrsResult, TypeError},
47    memory::{
48        scope::LocalScope,
49        target::{Target, TargetResult},
50    },
51    prelude::{TypedArray, TypedRankedArray},
52    private::Private,
53};
54
55/// Convert managed data to a `TypedValue`.
56pub trait AsTyped<'scope, 'data>: Managed<'scope, 'data> {
57    fn as_typed(self) -> JlrsResult<TypedValue<'scope, 'data, Self>>;
58}
59
60/// A `Value` and its type constructor.
61#[repr(transparent)]
62pub struct TypedValue<'scope, 'data, T>(
63    NonNull<jl_value_t>,
64    PhantomData<T>,
65    PhantomData<&'scope ()>,
66    PhantomData<&'data ()>,
67);
68
69impl<U: ConstructType + IntoJulia> TypedValue<'_, '_, U> {
70    /// Create a new typed value, any type that implements [`IntoJulia`] can be converted using
71    /// this function.
72    #[inline]
73    pub fn new<'target, Tgt>(target: Tgt, data: U) -> TypedValueData<'target, 'static, U, Tgt>
74    where
75        Tgt: Target<'target>,
76    {
77        unsafe {
78            Value::new(&target, data)
79                .as_value()
80                .cast_unchecked::<TypedValue<U>>()
81                .root(target)
82        }
83    }
84}
85
86impl<U: ConstructType> TypedValue<'_, '_, U> {
87    /// Create a new typed value, any type that implements [`ValidLayout`] can be converted using
88    /// this function as long as it's valid for `U`.
89    #[inline]
90    pub fn try_new_with<'target, L, Tgt>(
91        target: Tgt,
92        data: L,
93    ) -> JlrsResult<TypedValueData<'target, 'static, U, Tgt>>
94    where
95        L: ValidLayout,
96        Tgt: Target<'target>,
97    {
98        unsafe {
99            let v = Value::try_new_with::<U, _, _>(&target, data)?.as_value();
100            Ok(TypedValue::<U>::from_value_unchecked(v).root(target))
101        }
102    }
103}
104
105impl<'scope, 'data, U: ConstructType> TypedValue<'scope, 'data, U> {
106    /// Create a new typed value from an existing value.
107    pub fn from_value<'target, Tgt>(
108        target: &Tgt,
109        value: Value<'scope, 'data>,
110    ) -> JlrsResult<TypedValue<'scope, 'data, U>>
111    where
112        Tgt: Target<'target>,
113    {
114        unsafe {
115            target.local_scope::<_, 1>(|mut frame| {
116                let ty = U::construct_type(&mut frame).as_value();
117                if value.isa(ty) {
118                    Ok(TypedValue::<U>::wrap_non_null(
119                        value.unwrap_non_null(Private),
120                        Private,
121                    ))
122                } else {
123                    Err(TypeError::NotA {
124                        value: value.display_string_or("<Cannot display value>"),
125                        field_type: ty.display_string_or("<Cannot display type>"),
126                    })?
127                }
128            })
129        }
130    }
131
132    /// Create a new typed value from an existing value without checking the value is an instance
133    /// of `U`.
134    ///
135    /// Safety: `value` must be an instance of the constructed type `U`.
136    #[inline]
137    pub unsafe fn from_value_unchecked(
138        value: Value<'scope, 'data>,
139    ) -> TypedValue<'scope, 'data, U> {
140        unsafe { TypedValue::<U>::wrap_non_null(value.unwrap_non_null(Private), Private) }
141    }
142}
143
144impl<'scope, 'data, U: ValidLayout + ConstructType> TypedValue<'scope, 'data, U> {
145    /// Track `self` immutably.
146    ///
147    /// See [`Value::track_shared`] for more information.
148    #[inline]
149    pub unsafe fn track_shared<'tracked>(
150        &'tracked self,
151    ) -> JlrsResult<Tracked<'tracked, 'scope, 'data, U>> {
152        self.deref().track_shared()
153    }
154
155    /// Track `self` mutably.
156    ///
157    /// See [`Value::track_exclusive`] for more information.
158    #[inline]
159    pub unsafe fn track_exclusive<'tracked>(
160        &'tracked mut self,
161    ) -> JlrsResult<TrackedMut<'tracked, 'scope, 'data, U>> {
162        unsafe { self.deref_mut().track_exclusive() }
163    }
164}
165
166impl<'scope, 'data, U: ConstructType> TypedValue<'scope, 'data, U> {
167    /// Track `self` immutably.
168    ///
169    /// See [`Value::track_shared`] for more information.
170    #[inline]
171    pub unsafe fn track_shared_as<'tracked, V: ValidLayout>(
172        &'tracked self,
173    ) -> JlrsResult<Tracked<'tracked, 'scope, 'data, V>> {
174        self.deref().track_shared()
175    }
176
177    /// Track `self` mutably.
178    ///
179    /// See [`Value::track_exclusive`] for more information.
180    #[inline]
181    pub unsafe fn track_exclusive_as<'tracked, V: ValidLayout>(
182        &'tracked mut self,
183    ) -> JlrsResult<TrackedMut<'tracked, 'scope, 'data, V>> {
184        unsafe { self.deref_mut().track_exclusive() }
185    }
186}
187
188impl<'scope, 'data, T: ConstructType, const N: isize>
189    TypedValue<'scope, 'data, ArrayTypeConstructor<T, ConstantIsize<N>>>
190{
191    /// Convert `self` to the equivalent `TypedRankedArray` type.
192    #[inline]
193    pub fn as_typed_ranked_array(self) -> TypedRankedArray<'scope, 'data, T, N> {
194        unsafe { std::mem::transmute(self) }
195    }
196}
197
198impl<'scope, 'data, T: ConstructType, N: ConstructType>
199    TypedValue<'scope, 'data, ArrayTypeConstructor<T, N>>
200{
201    /// Convert `self` to the equivalent `TypedArray` type.
202    #[inline]
203    pub fn as_typed_array(self) -> TypedArray<'scope, 'data, T> {
204        unsafe { std::mem::transmute(self) }
205    }
206}
207
208impl<U: ConstructType + ValidLayout + Send> TypedValueUnbound<U> {
209    /// Track `self` immutably.
210    ///
211    /// See [`Value::track_shared_unbound`] for more information.
212    #[inline]
213    pub unsafe fn track_shared_unbound(self) -> JlrsResult<Tracked<'static, 'static, 'static, U>> {
214        unsafe { self.as_value().track_shared_unbound() }
215    }
216
217    /// Track `self` mutably.
218    ///
219    /// See [`Value::track_exclusive_unbound`] for more information.
220    #[inline]
221    pub unsafe fn track_exclusive_unbound(
222        self,
223    ) -> JlrsResult<TrackedMut<'static, 'static, 'static, U>> {
224        unsafe { self.as_value().track_exclusive_unbound() }
225    }
226}
227
228impl<U: ConstructType> TypedValueUnbound<U> {
229    /// Track `self` immutably.
230    ///
231    /// See [`Value::track_shared_unbound`] for more information.
232    #[inline]
233    pub unsafe fn track_shared_unbound_as<V: ValidLayout + Send>(
234        self,
235    ) -> JlrsResult<Tracked<'static, 'static, 'static, V>> {
236        unsafe { self.as_value().track_shared_unbound() }
237    }
238
239    /// Track `self` mutably.
240    ///
241    /// See [`Value::track_exclusive_unbound`] for more information.
242    #[inline]
243    pub unsafe fn track_exclusive_unbound_as<V: ValidLayout + Send>(
244        self,
245    ) -> JlrsResult<TrackedMut<'static, 'static, 'static, V>> {
246        unsafe { self.as_value().track_exclusive_unbound() }
247    }
248}
249
250impl<'scope, 'data, U: ConstructType> Deref for TypedValue<'scope, 'data, U> {
251    type Target = Value<'scope, 'data>;
252
253    #[inline]
254    fn deref(&self) -> &Self::Target {
255        unsafe { std::mem::transmute(self) }
256    }
257}
258impl<'scope, 'data, U: ConstructType> DerefMut for TypedValue<'scope, 'data, U> {
259    #[inline]
260    fn deref_mut(&mut self) -> &mut Self::Target {
261        unsafe { std::mem::transmute(self) }
262    }
263}
264
265impl<T> Debug for TypedValue<'_, '_, T>
266where
267    T: ConstructType,
268{
269    fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
270        write!(fmt, "{:?}", self.as_value())
271    }
272}
273
274impl<T> Clone for TypedValue<'_, '_, T>
275where
276    T: ConstructType,
277{
278    #[inline]
279    fn clone(&self) -> Self {
280        unsafe { Self::wrap_non_null(self.unwrap_non_null(Private), Private) }
281    }
282}
283
284impl<T> Copy for TypedValue<'_, '_, T> where T: ConstructType {}
285
286unsafe impl<T> ValidLayout for TypedValue<'_, '_, T>
287where
288    T: ConstructType,
289{
290    #[inline]
291    fn valid_layout(v: Value) -> bool {
292        WeakValue::valid_layout(v)
293    }
294
295    #[inline]
296    fn type_object<'target, Tgt>(target: &Tgt) -> Value<'target, 'static>
297    where
298        Tgt: Target<'target>,
299    {
300        T::base_type(target).expect("Type has no base type")
301    }
302
303    const IS_REF: bool = true;
304}
305unsafe impl<T> ValidField for Option<TypedValue<'_, '_, T>>
306where
307    T: ConstructType,
308{
309    #[inline]
310    fn valid_field(v: Value) -> bool {
311        Option::<WeakValue>::valid_field(v)
312    }
313}
314
315impl<'scope, 'data, T> ManagedPriv<'scope, 'data> for TypedValue<'scope, 'data, T>
316where
317    T: ConstructType,
318{
319    type Wraps = jl_value_t;
320    type WithLifetimes<'target, 'da> = TypedValue<'target, 'da, T>;
321    const NAME: &'static str = "TypedValue";
322
323    // Safety: `inner` must not have been freed yet, the result must never be
324    // used after the GC might have freed it.
325    #[inline]
326    unsafe fn wrap_non_null(inner: NonNull<Self::Wraps>, _: Private) -> Self {
327        Self(inner, PhantomData, PhantomData, PhantomData)
328    }
329
330    #[inline]
331    fn unwrap_non_null(self, _: Private) -> NonNull<Self::Wraps> {
332        self.0
333    }
334}
335
336unsafe impl<'scope, 'data, T> Typecheck for TypedValue<'scope, 'data, T>
337where
338    T: ValidLayout + ConstructType,
339{
340    #[inline]
341    fn typecheck(t: DataType) -> bool {
342        T::valid_layout(t.as_value())
343    }
344}
345
346unsafe impl<U> ConstructType for TypedValue<'_, '_, U>
347where
348    U: ConstructType,
349{
350    type Static = U::Static;
351
352    #[inline]
353    fn construct_type_uncached<'target, Tgt>(target: Tgt) -> ValueData<'target, 'static, Tgt>
354    where
355        Tgt: Target<'target>,
356    {
357        U::construct_type_uncached(target)
358    }
359
360    #[inline]
361    fn base_type<'target, Tgt>(target: &Tgt) -> Option<Value<'target, 'static>>
362    where
363        Tgt: Target<'target>,
364    {
365        U::base_type(target)
366    }
367
368    fn construct_type_with_env_uncached<'target, Tgt>(
369        target: Tgt,
370        env: &crate::data::types::construct_type::TypeVarEnv,
371    ) -> ValueData<'target, 'static, Tgt>
372    where
373        Tgt: Target<'target>,
374    {
375        U::construct_type_with_env_uncached(target, env)
376    }
377}
378
379use crate::memory::target::TargetType;
380
381pub type WeakTypedValue<'scope, 'data, T> = Weak<'scope, 'data, TypedValue<'scope, 'data, T>>;
382
383impl<'scope, 'data> WeakTypedValue<'scope, 'data, AnyType> {
384    #[inline]
385    pub fn from_value_ref(value_ref: WeakValue<'scope, 'data>) -> Self {
386        WeakTypedValue::wrap(value_ref.ptr())
387    }
388}
389
390/// A [`WeakTypedValue`] with static lifetimes. This is a useful shorthand for signatures of
391/// `ccall`able functions that return a [`TypedValue`].
392pub type TypedValueRet<T> = Weak<'static, 'static, TypedValue<'static, 'static, T>>;
393
394/// `TypedValue` or `WeakTypedValue`, depending on the target type `Tgt`.
395pub type TypedValueData<'target, 'data, U, Tgt> =
396    <Tgt as TargetType<'target>>::Data<'data, TypedValue<'target, 'data, U>>;
397
398/// `JuliaResult<TypedValue>` or `WeakJuliaResult<WeakTypedValue>`, depending on the target type
399/// `Tgt`.
400pub type TypedValueResult<'target, 'data, U, T> =
401    TargetResult<'target, 'data, TypedValue<'target, 'data, U>, T>;
402
403pub type TypedValueUnbound<T> = TypedValue<'static, 'static, T>;
404
405unsafe impl<'scope, 'data, T: ConstructType> CCallArg for TypedValue<'scope, 'data, T> {
406    type CCallArgType = Value<'scope, 'data>;
407    type FunctionArgType = T;
408}
409
410unsafe impl<T: ConstructType> CCallReturn for WeakTypedValue<'static, 'static, T> {
411    type CCallReturnType = Value<'static, 'static>;
412    type FunctionReturnType = T;
413    type ReturnAs = Self;
414
415    #[inline]
416    unsafe fn return_or_throw(self) -> Self::ReturnAs {
417        self
418    }
419}