kimchi_stubs/caml/
caml_pointer.rs

1macro_rules! impl_caml_pointer {
2    ($name: ident => $typ: ty) => {
3        #[derive(core::fmt::Debug, Clone, ::ocaml_gen::CustomType)]
4        pub struct $name(pub ::std::rc::Rc<std::cell::UnsafeCell<$typ>>);
5
6        impl $name {
7            extern "C" fn caml_pointer_finalize(v: ocaml::Raw) {
8                unsafe {
9                    let v: ocaml::Pointer<$name> = v.as_pointer();
10                    v.drop_in_place();
11                }
12            }
13
14            extern "C" fn caml_pointer_compare(_: ocaml::Raw, _: ocaml::Raw) -> i32 {
15                // Always return equal. We can use this for sanity checks, and
16                // anything else using this would be broken anyway.
17                0
18            }
19        }
20
21        ocaml::custom!($name {
22            finalize: $name::caml_pointer_finalize,
23            compare: $name::caml_pointer_compare,
24        });
25
26        unsafe impl<'a> ocaml::FromValue<'a> for $name {
27            fn from_value(x: ocaml::Value) -> Self {
28                let x = ocaml::Pointer::<Self>::from_value(x);
29                $name(x.as_ref().0.clone())
30            }
31        }
32
33        impl $name {
34            pub fn create(x: $typ) -> $name {
35                $name(::std::rc::Rc::new(std::cell::UnsafeCell::new(x)))
36            }
37        }
38
39        impl ::core::ops::Deref for $name {
40            type Target = $typ;
41
42            fn deref(&self) -> &Self::Target {
43                unsafe { &*self.0.get() }
44            }
45        }
46
47        impl ::core::ops::DerefMut for $name {
48            fn deref_mut(&mut self) -> &mut Self::Target {
49                unsafe {
50                    // Wholely unsafe, Batman!
51                    // We would use [`get_mut_unchecked`] here, but it is nightly-only.
52                    // Instead, we get coerce our constant pointer to a mutable
53                    // pointer, in the knowledge that
54                    // * all of our mutations called from OCaml are blocking, so
55                    // we won't have multiple live mutable references live
56                    // simultaneously, and
57                    // * the underlying pointer is in the correct state to be
58                    //   mutable, since we can call [`get_mut_unchecked`] in
59                    //   nightly, or can call [`get_mut`] and unwrap if this is
60                    //   the only live reference.
61                    &mut *self.0.get()
62                }
63            }
64        }
65    };
66}