diff --git a/crates/luaffi/src/future.rs b/crates/luaffi/src/future.rs index 7f88157..147b656 100644 --- a/crates/luaffi/src/future.rs +++ b/crates/luaffi/src/future.rs @@ -1,6 +1,7 @@ use crate::{ __internal::{display, type_id}, Cdef, CdefBuilder, FfiReturnConvention, Metatype, MetatypeBuilder, ToFfi, Type, TypeBuilder, + UnsafeExternCFn, }; use luaify::luaify; use std::{ @@ -147,8 +148,8 @@ unsafe impl + 'static> Type for lua_future { unsafe impl + 'static> Cdef for lua_future { fn build(s: &mut CdefBuilder) { s.field_opaque(mem::offset_of!(Self, take)) // opaque .sig, .poll and .state - .field:: ::To>("__take") - .field::("__drop"); + .field::::To>>("__take") + .field::>("__drop"); } } diff --git a/crates/luaffi/src/lib.rs b/crates/luaffi/src/lib.rs index 0390d66..6c62d34 100644 --- a/crates/luaffi/src/lib.rs +++ b/crates/luaffi/src/lib.rs @@ -4,11 +4,12 @@ use std::{ collections::HashSet, ffi::{c_double, c_float, c_void}, fmt::{self, Display, Formatter, Write}, + marker::PhantomData, mem, slice, }; pub mod future; -pub mod option; +// pub mod option; pub mod string; #[doc(hidden)] @@ -140,8 +141,8 @@ pub struct Registry { impl Registry { pub fn new() -> Self { let mut s = Self::default(); - s.declare::(KEEP_FN); - s.declare:: bool>(IS_UTF8_FN); + s.declare::>(KEEP_FN); + s.declare::>(IS_UTF8_FN); s } @@ -542,12 +543,11 @@ unsafe impl ToFfi for () { conv == FfiReturnConvention::Void, "void type cannot be instantiated" ); - "" } } -macro_rules! impl_copy_primitive { +macro_rules! impl_primitive_abi { ($rtype:ty, $ctype:expr, $ltype:expr $(, $unwrap:expr)?) => { impl_primitive!($rtype, $ctype); @@ -555,8 +555,8 @@ macro_rules! impl_copy_primitive { // SAFETY: Primitive types are always copyable so we can pass and return them by value. // unsafe impl FromFfi for $rtype { - type From = $rtype; - type FromArg = $rtype; + type From = Self; + type FromArg = Self; fn prelude(arg: &str) -> impl Display { display!(r#"assert(type({arg}) == "{0}", "{0} expected in argument '{arg}', got " .. type({arg})); "#, $ltype) @@ -572,7 +572,7 @@ macro_rules! impl_copy_primitive { } unsafe impl ToFfi for $rtype { - type To = $rtype; + type To = Self; fn convert(self) -> Self::To { self @@ -585,7 +585,7 @@ macro_rules! impl_copy_primitive { FfiReturnConvention::Void => unreachable!(), FfiReturnConvention::ByValue => {}, // if a primitive type for some reason gets returned by out-param, unwrap - // the cdata containing the value and convert it to the equivalent lua value + // the cdata containing the value to convert it to the equivalent lua value FfiReturnConvention::ByOutParam => { $(write!(f, "{ret} = {}; ", $unwrap(ret))?;)? }, } })) @@ -594,147 +594,98 @@ macro_rules! impl_copy_primitive { }; } -impl_copy_primitive!(bool, "bool", "boolean", |n| display!("{n} ~= 0")); -impl_copy_primitive!(u8, "uint8_t", "number", |n| display!("tonumber({n})")); -impl_copy_primitive!(u16, "uint16_t", "number", |n| display!("tonumber({n})")); -impl_copy_primitive!(u32, "uint32_t", "number", |n| display!("tonumber({n})")); -impl_copy_primitive!(u64, "uint64_t", "number"); -impl_copy_primitive!(usize, "uintptr_t", "number"); -impl_copy_primitive!(i8, "int8_t", "number", |n| display!("tonumber({n})")); -impl_copy_primitive!(i16, "int16_t", "number", |n| display!("tonumber({n})")); -impl_copy_primitive!(i32, "int32_t", "number", |n| display!("tonumber({n})")); -impl_copy_primitive!(i64, "int64_t", "number"); -impl_copy_primitive!(isize, "intptr_t", "number"); -impl_copy_primitive!(c_float, "float", "number", |n| display!("tonumber({n})")); -impl_copy_primitive!(c_double, "double", "number", |n| display!("tonumber({n})")); +impl_primitive_abi!(bool, "bool", "boolean", |n| display!("{n} ~= 0")); +impl_primitive_abi!(u8, "uint8_t", "number", |n| display!("tonumber({n})")); +impl_primitive_abi!(u16, "uint16_t", "number", |n| display!("tonumber({n})")); +impl_primitive_abi!(u32, "uint32_t", "number", |n| display!("tonumber({n})")); +impl_primitive_abi!(u64, "uint64_t", "number"); +impl_primitive_abi!(usize, "uintptr_t", "number"); +impl_primitive_abi!(i8, "int8_t", "number", |n| display!("tonumber({n})")); +impl_primitive_abi!(i16, "int16_t", "number", |n| display!("tonumber({n})")); +impl_primitive_abi!(i32, "int32_t", "number", |n| display!("tonumber({n})")); +impl_primitive_abi!(i64, "int64_t", "number"); +impl_primitive_abi!(isize, "intptr_t", "number"); +impl_primitive_abi!(c_float, "float", "number", |n| display!("tonumber({n})")); +impl_primitive_abi!(c_double, "double", "number", |n| display!("tonumber({n})")); -unsafe impl Type for *const T { - fn name() -> impl Display { - display!("const_{}_ptr", T::name()) - } +macro_rules! impl_const_ptr { + ($ty:ty) => { + unsafe impl Type for $ty { + fn name() -> impl Display { + display!("const_{}_ptr", T::name()) + } - fn cdecl(name: impl Display) -> impl Display { - T::cdecl(display!("const *{name}")) - } + fn cdecl(name: impl Display) -> impl Display { + T::cdecl(display!("const *{name}")) + } - fn build(b: &mut TypeBuilder) { - b.include::(); - } + fn build(b: &mut TypeBuilder) { + b.include::(); + } + } + }; } -unsafe impl Type for *mut T { - fn name() -> impl Display { - display!("{}_ptr", T::name()) - } +impl_const_ptr!(*const T); +impl_const_ptr!(&T); +impl_const_ptr!(Option<&T>); - fn cdecl(name: impl Display) -> impl Display { - T::cdecl(display!("*{name}")) - } +macro_rules! impl_mut_ptr { + ($ty:ty) => { + unsafe impl Type for $ty { + fn name() -> impl Display { + display!("{}_ptr", T::name()) + } - fn build(b: &mut TypeBuilder) { - b.include::(); - } + fn cdecl(name: impl Display) -> impl Display { + T::cdecl(display!("*{name}")) + } + + fn build(b: &mut TypeBuilder) { + b.include::(); + } + } + }; } +impl_mut_ptr!(*mut T); +impl_mut_ptr!(&mut T); +impl_mut_ptr!(Option<&mut T>); + // // SAFETY: Pass by value for pointers, which maps to a `cdata` argument in lua containing either: -// * the pointer value itself (`T *`), or -// * the pointer to the base of the cdata payload (`T`). // -unsafe impl FromFfi for *const T { - type From = *const T; - type FromArg = *const T; +// * a reference which gets converted to a pointer (`T &` cdata), or +// * the pointer value itself (`T *` cdata), or +// * the pointer to the base of the cdata payload (`T` cdata). +// +// LuaJIT will check for pointer compatibility automatically. +// +macro_rules! impl_ptr_fromabi { + ($ty:ty) => { + unsafe impl FromFfi for $ty { + type From = Self; + type FromArg = Self; - fn convert(from: Self::From) -> Self { - from - } + fn convert(from: Self::From) -> Self { + from + } - fn convert_arg(from: Self::FromArg) -> Self { - from - } + fn convert_arg(from: Self::FromArg) -> Self { + from + } + } + }; } -unsafe impl FromFfi for *mut T { - type From = *mut T; - type FromArg = *mut T; +impl_ptr_fromabi!(*const T); +impl_ptr_fromabi!(*mut T); +impl_ptr_fromabi!(Option<&T>); +impl_ptr_fromabi!(Option<&mut T>); - fn convert(from: Self::From) -> Self { - from - } - - fn convert_arg(from: Self::FromArg) -> Self { - from - } -} - -// -// SAFETY: Return by value for pointers, which maps to a `cdata` return in lua containing the -// pointer (`T *`). We also map null pointers to `nil` for convenience (otherwise it's still a cdata -// value containing a null pointer) -// -unsafe impl ToFfi for *const T { - type To = *const T; - - fn convert(self) -> Self::To { - self - } - - fn postlude(ret: &str, _conv: FfiReturnConvention) -> impl Display { - display!("if {ret} == nil then {ret} = nil; end; ") - } -} - -unsafe impl ToFfi for *mut T { - type To = *mut T; - - fn convert(self) -> Self::To { - self - } - - fn postlude(ret: &str, _conv: FfiReturnConvention) -> impl Display { - display!("if {ret} == nil then {ret} = nil; end; ") - } -} - -// -// SAFETY: No `ToFfi` for references because we can't guarantee that the returned reference -// converted to a pointer will not outlive the pointee. -// -unsafe impl Type for &T { - fn name() -> impl Display { - display!("const_{}_ptr", T::name()) - } - - fn cdecl(name: impl Display) -> impl Display { - T::cdecl(display!("const *{name}")) - } - - fn build(b: &mut TypeBuilder) { - b.include::(); - } -} - -unsafe impl Type for &mut T { - fn name() -> impl Display { - display!("{}_ptr", T::name()) - } - - fn cdecl(name: impl Display) -> impl Display { - T::cdecl(display!("*{name}")) - } - - fn build(b: &mut TypeBuilder) { - b.include::(); - } -} - -// -// SAFETY: Pass by value for references, which have the same semantics as pointers (see above). Must -// ensure that the pointer is not nil before being converted to a reference. -// -unsafe impl FromFfi for &T { - type From = *const T; - type FromArg = *const T; +unsafe impl<'s, T: Type> FromFfi for &'s T { + type From = Option<&'s T>; + type FromArg = Option<&'s T>; fn prelude(arg: &str) -> impl Display { display!(r#"assert({arg} ~= nil, "argument '{arg}' cannot be nil"); "#) @@ -742,19 +693,19 @@ unsafe impl FromFfi for &T { fn convert(from: Self::From) -> Self { debug_assert!( - !from.is_null(), - "<&T>::convert() called on a null pointer when it was checked to be non-null" + from.is_some(), + "<&T>::convert() called on a null reference when it was checked to be non-null" ); - unsafe { &*from } + unsafe { from.unwrap_unchecked() } } fn convert_arg(from: Self::FromArg) -> Self { - Self::convert(from) + FromFfi::convert(from) } } -unsafe impl FromFfi for &mut T { +unsafe impl<'s, T: Type> FromFfi for &'s mut T { // // SAFETY: `FromFfi` for *mutable* references is safe because it is guaranteed that no two Rust // code called via FFI can be running at the same time on the same OS thread (no Lua @@ -771,8 +722,11 @@ unsafe impl FromFfi for &mut T { // FFI which could violate exclusive borrow semantics. This is prevented by not implementing // `FromFfi` for function pointers (see below). // - type From = *mut T; - type FromArg = *mut T; + // The runtime does not keep any references to Rust user objects boxed in cdata (futures are + // the only exception; their ownership is transferred to the runtime via yield). + // + type From = Option<&'s mut T>; + type FromArg = Option<&'s mut T>; fn prelude(arg: &str) -> impl Display { display!(r#"assert({arg} ~= nil, "argument '{arg}' cannot be nil"); "#) @@ -780,18 +734,46 @@ unsafe impl FromFfi for &mut T { fn convert(from: Self::From) -> Self { debug_assert!( - !from.is_null(), - "<&mut T>::convert() called on a null pointer when it was checked to be non-null" + from.is_some(), + "<&mut T>::convert() called on a null reference when it was checked to be non-null" ); - unsafe { &mut *from } + unsafe { from.unwrap_unchecked() } } fn convert_arg(from: Self::FromArg) -> Self { - Self::convert(from) + FromFfi::convert(from) } } +// +// SAFETY: Return by value for pointers, which maps to a `cdata` return in lua containing the +// pointer (`T *`). We also map null pointers to `nil` for convenience (otherwise it's still a cdata +// value containing a null pointer) +// +macro_rules! impl_ptr_toabi { + ($ty:ty) => { + unsafe impl ToFfi for $ty { + type To = Self; + + fn convert(self) -> Self::To { + self + } + + fn postlude(ret: &str, _conv: FfiReturnConvention) -> impl Display { + display!("if {ret} == nil then {ret} = nil; end; ") + } + } + }; +} + +impl_ptr_toabi!(*const T); +impl_ptr_toabi!(*mut T); +impl_ptr_toabi!(&'static T); +impl_ptr_toabi!(&'static mut T); +impl_ptr_toabi!(Option<&'static T>); +impl_ptr_toabi!(Option<&'static mut T>); + // // SAFETY: No `FromFfi` and `ToFfi` for arrays because passing or returning them by value is not a // thing in C (they are just pointers). @@ -826,13 +808,14 @@ unsafe impl Type for [T; N] { } } +pub struct UnsafeExternCFn(PhantomData Out>); + macro_rules! impl_function { (fn($($arg:tt),*) -> $ret:tt) => { - impl_function!((extern "C" fn($($arg),*) -> $ret), fn($($arg),*) -> $ret); - impl_function!((unsafe extern "C" fn($($arg),*) -> $ret), fn($($arg),*) -> $ret); + impl_function!(UnsafeExternCFn, fn($($arg),*) -> $ret); }; - (($($type:tt)+), fn($($arg:tt),*) -> $ret:tt) => { + ($ty:tt, fn($($arg:tt),*) -> $ret:tt) => { // // SAFETY: No `FromFfi` for function pointers because of borrow safety invariants (see above // in `&mut T`). @@ -840,7 +823,7 @@ macro_rules! impl_function { // We also can't implement `ToFfi` because we can't call `FromFfi` and `ToFfi` for the // function's respective argument and return values. // - unsafe impl<$($arg: Type,)* $ret: Type> Type for $($type)+ { + unsafe impl<$($arg: Type,)* $ret: Type> Type for $ty<($($arg,)*), $ret> { fn name() -> impl Display { disp(|f| Ok({ write!(f, "fn_{}", $ret::name())?; @@ -872,7 +855,7 @@ macro_rules! impl_function { b.include::<$ret>(); } } - } + }; } impl_function!(fn() -> Z); diff --git a/crates/luaffi_impl/src/metatype.rs b/crates/luaffi_impl/src/metatype.rs index 5ea21db..9a2e453 100644 --- a/crates/luaffi_impl/src/metatype.rs +++ b/crates/luaffi_impl/src/metatype.rs @@ -67,7 +67,7 @@ fn generate_impls(imp: &mut ItemImpl) -> Result { let ffi_register_drop = quote! { if ::std::mem::needs_drop::() { - b.declare::(#ffi_drop_cname); + b.declare::<#ffi::UnsafeExternCFn<(*mut Self,), ()>>(#ffi_drop_cname); b.metatable_raw("gc", ::std::format_args!("__C.{}", #ffi_drop_cname)); } }; @@ -280,9 +280,9 @@ fn generate_ffi_register(func: &FfiFunction) -> Result { }; let declare = if func.ret_by_out { - quote! { b.declare::(#c_name); } + quote! { b.declare::<#ffi::UnsafeExternCFn<(*mut #ret, #(#params,)*), ()>>(#c_name); } } else { - quote! { b.declare:: #ret>(#c_name); } + quote! { b.declare::<#ffi::UnsafeExternCFn<(#(#params,)*), #ret>>(#c_name); } }; let register = match func.attrs.metatable {