#![allow(unsafe_code)]
use lean_rs_sys::ctor::{
lean_box_float, lean_box_uint64, lean_box_usize, lean_unbox_float, lean_unbox_uint64, lean_unbox_usize,
};
use lean_rs_sys::object::{lean_box, lean_is_scalar, lean_obj_tag, lean_unbox};
use crate::abi::traits::{IntoLean, LeanAbi, TryFromLean, conversion_error, sealed};
use crate::error::{LeanError, LeanResult};
use crate::runtime::LeanRuntime;
use crate::runtime::obj::Obj;
fn wrong_kind(expected: &str, found_tag: u32) -> LeanError {
conversion_error(format!("expected Lean {expected}, found object with tag {found_tag}"))
}
fn wrong_kind_scalar(expected: &str) -> LeanError {
conversion_error(format!("expected Lean {expected}, found scalar-tagged object"))
}
fn out_of_range(expected: &str) -> LeanError {
conversion_error(format!("Lean value does not fit Rust {expected}"))
}
#[inline]
fn require_scalar(obj: &Obj<'_>, expected: &str) -> LeanResult<()> {
let ptr = obj.as_raw_borrowed();
if unsafe { lean_is_scalar(ptr) } {
Ok(())
} else {
let found_tag = unsafe { lean_obj_tag(ptr) };
Err(wrong_kind(expected, found_tag))
}
}
#[inline]
fn require_ctor(obj: &Obj<'_>, expected: &str) -> LeanResult<()> {
let ptr = obj.as_raw_borrowed();
if unsafe { lean_is_scalar(ptr) } {
return Err(wrong_kind_scalar(expected));
}
if unsafe { lean_rs_sys::object::lean_is_ctor(ptr) } {
Ok(())
} else {
let found_tag = unsafe { lean_obj_tag(ptr) };
Err(wrong_kind(expected, found_tag))
}
}
impl<'lean> IntoLean<'lean> for () {
fn into_lean(self, runtime: &'lean LeanRuntime) -> Obj<'lean> {
unsafe { Obj::from_owned_raw(runtime, lean_box(0)) }
}
}
impl<'lean> TryFromLean<'lean> for () {
fn try_from_lean(obj: Obj<'lean>) -> LeanResult<Self> {
require_scalar(&obj, "Unit")?;
Ok(())
}
}
impl<'lean> IntoLean<'lean> for bool {
fn into_lean(self, runtime: &'lean LeanRuntime) -> Obj<'lean> {
unsafe { Obj::from_owned_raw(runtime, lean_box(usize::from(self))) }
}
}
impl<'lean> TryFromLean<'lean> for bool {
fn try_from_lean(obj: Obj<'lean>) -> LeanResult<Self> {
require_scalar(&obj, "Bool")?;
let payload = unsafe { lean_unbox(obj.as_raw_borrowed()) };
match payload {
0 => Ok(false),
1 => Ok(true),
_ => Err(out_of_range("bool")),
}
}
}
macro_rules! impl_scalar_abi_small_int {
($($ty:ty as $unsigned:ty => $name:literal),* $(,)?) => {
$(
impl<'lean> IntoLean<'lean> for $ty {
fn into_lean(self, runtime: &'lean LeanRuntime) -> Obj<'lean> {
let payload = self as $unsigned as usize;
unsafe { Obj::from_owned_raw(runtime, lean_box(payload)) }
}
}
impl<'lean> TryFromLean<'lean> for $ty {
fn try_from_lean(obj: Obj<'lean>) -> LeanResult<Self> {
require_scalar(&obj, $name)?;
let raw = unsafe { lean_unbox(obj.as_raw_borrowed()) };
let unsigned = <$unsigned>::try_from(raw)
.map_err(|_| out_of_range($name))?;
Ok(unsigned as $ty)
}
}
)*
};
}
impl_scalar_abi_small_int! {
u8 as u8 => "u8",
u16 as u16 => "u16",
u32 as u32 => "u32",
i8 as u8 => "i8",
i16 as u16 => "i16",
i32 as u32 => "i32",
}
impl<'lean> IntoLean<'lean> for u64 {
fn into_lean(self, runtime: &'lean LeanRuntime) -> Obj<'lean> {
unsafe { Obj::from_owned_raw(runtime, lean_box_uint64(self)) }
}
}
impl<'lean> TryFromLean<'lean> for u64 {
fn try_from_lean(obj: Obj<'lean>) -> LeanResult<Self> {
require_ctor(&obj, "u64")?;
Ok(unsafe { lean_unbox_uint64(obj.as_raw_borrowed()) })
}
}
impl<'lean> IntoLean<'lean> for usize {
fn into_lean(self, runtime: &'lean LeanRuntime) -> Obj<'lean> {
unsafe { Obj::from_owned_raw(runtime, lean_box_usize(self)) }
}
}
impl<'lean> TryFromLean<'lean> for usize {
fn try_from_lean(obj: Obj<'lean>) -> LeanResult<Self> {
require_ctor(&obj, "usize")?;
Ok(unsafe { lean_unbox_usize(obj.as_raw_borrowed()) })
}
}
impl<'lean> IntoLean<'lean> for i64 {
fn into_lean(self, runtime: &'lean LeanRuntime) -> Obj<'lean> {
#[allow(clippy::cast_sign_loss, reason = "Int64 reuses UInt64's bit pattern")]
let bits = self as u64;
bits.into_lean(runtime)
}
}
impl<'lean> TryFromLean<'lean> for i64 {
fn try_from_lean(obj: Obj<'lean>) -> LeanResult<Self> {
require_ctor(&obj, "i64")?;
let bits = unsafe { lean_unbox_uint64(obj.as_raw_borrowed()) };
#[allow(clippy::cast_possible_wrap, reason = "Int64 reuses UInt64's bit pattern")]
Ok(bits as Self)
}
}
impl<'lean> IntoLean<'lean> for isize {
fn into_lean(self, runtime: &'lean LeanRuntime) -> Obj<'lean> {
#[allow(clippy::cast_sign_loss, reason = "ISize reuses USize's bit pattern")]
let bits = self as usize;
bits.into_lean(runtime)
}
}
impl<'lean> TryFromLean<'lean> for isize {
fn try_from_lean(obj: Obj<'lean>) -> LeanResult<Self> {
require_ctor(&obj, "isize")?;
let bits = unsafe { lean_unbox_usize(obj.as_raw_borrowed()) };
#[allow(clippy::cast_possible_wrap, reason = "ISize reuses USize's bit pattern")]
Ok(bits as Self)
}
}
impl<'lean> IntoLean<'lean> for char {
fn into_lean(self, runtime: &'lean LeanRuntime) -> Obj<'lean> {
u32::from(self).into_lean(runtime)
}
}
impl<'lean> TryFromLean<'lean> for char {
fn try_from_lean(obj: Obj<'lean>) -> LeanResult<Self> {
require_scalar(&obj, "char")?;
let raw = unsafe { lean_unbox(obj.as_raw_borrowed()) };
let code_point = u32::try_from(raw).map_err(|_| out_of_range("char"))?;
Self::from_u32(code_point)
.ok_or_else(|| conversion_error(format!("Lean char {code_point:#x} is not a Unicode scalar value")))
}
}
impl<'lean> IntoLean<'lean> for f64 {
fn into_lean(self, runtime: &'lean LeanRuntime) -> Obj<'lean> {
unsafe { Obj::from_owned_raw(runtime, lean_box_float(self)) }
}
}
impl<'lean> TryFromLean<'lean> for f64 {
fn try_from_lean(obj: Obj<'lean>) -> LeanResult<Self> {
require_ctor(&obj, "f64")?;
Ok(unsafe { lean_unbox_float(obj.as_raw_borrowed()) })
}
}
impl sealed::SealedAbi for () {}
impl<'lean> LeanAbi<'lean> for () {
type CRepr = *mut lean_rs_sys::lean_object;
fn into_c(self, _runtime: &'lean LeanRuntime) -> Self::CRepr {
unsafe { lean_box(0) }
}
#[allow(
clippy::not_unsafe_ptr_arg_deref,
reason = "sealed trait — caller invariant documented on LeanAbi::from_c"
)]
fn from_c(c: Self::CRepr, runtime: &'lean LeanRuntime) -> LeanResult<Self> {
let obj = unsafe { Obj::from_owned_raw(runtime, c) };
<()>::try_from_lean(obj)
}
}
impl sealed::SealedAbi for bool {}
impl<'lean> LeanAbi<'lean> for bool {
type CRepr = u8;
fn into_c(self, _runtime: &'lean LeanRuntime) -> u8 {
u8::from(self)
}
fn from_c(c: u8, _runtime: &'lean LeanRuntime) -> LeanResult<Self> {
match c {
0 => Ok(false),
1 => Ok(true),
_ => Err(out_of_range("bool")),
}
}
}
macro_rules! impl_scalar_abi_passthrough {
($($ty:ty),* $(,)?) => {
$(
impl sealed::SealedAbi for $ty {}
impl<'lean> LeanAbi<'lean> for $ty {
type CRepr = $ty;
fn into_c(self, _runtime: &'lean LeanRuntime) -> $ty { self }
fn from_c(c: $ty, _runtime: &'lean LeanRuntime) -> LeanResult<$ty> { Ok(c) }
}
)*
};
}
impl_scalar_abi_passthrough!(u8, u16, u32, u64, usize, f64);
impl_scalar_abi_passthrough!(i8, i16, i32, i64, isize);
impl sealed::SealedAbi for char {}
impl<'lean> LeanAbi<'lean> for char {
type CRepr = u32;
fn into_c(self, _runtime: &'lean LeanRuntime) -> u32 {
u32::from(self)
}
fn from_c(c: u32, _runtime: &'lean LeanRuntime) -> LeanResult<Self> {
Self::from_u32(c).ok_or_else(|| conversion_error(format!("Lean char {c:#x} is not a Unicode scalar value")))
}
}