From e05e2f4cb3f9e104fa36c7c45176d0454f2baf8d Mon Sep 17 00:00:00 2001 From: luaneko Date: Sat, 28 Jun 2025 04:15:27 +1000 Subject: [PATCH] Implement the foundations for annotation generation --- crates/lb/src/chan.rs | 2 +- crates/lb/src/net.rs | 40 ++++- crates/lb/src/task.rs | 6 +- crates/luaffi/src/future.rs | 8 +- crates/luaffi/src/internal.rs | 18 -- crates/luaffi/src/lib.rs | 219 ++++++++++++++++------- crates/luaffi/src/marker.rs | 193 +++++++++++++++++++++ crates/luaffi_impl/src/cdef.rs | 24 +-- crates/luaffi_impl/src/metatype.rs | 268 ++++++++++++----------------- 9 files changed, 510 insertions(+), 268 deletions(-) create mode 100644 crates/luaffi/src/marker.rs diff --git a/crates/lb/src/chan.rs b/crates/lb/src/chan.rs index f4ab890..491f63f 100644 --- a/crates/lb/src/chan.rs +++ b/crates/lb/src/chan.rs @@ -32,7 +32,7 @@ impl lb_chanlib { (send, recv) } - extern "Lua" fn bounded(self, cap: number) { + extern "Lua" fn bounded(self, cap: u32) { assert(cap >= 0, "channel capacity must be nonnegative"); let (send, recv) = (__new(__ct.lb_sender), __new(__ct.lb_receiver)); self.__bounded(cap, send, recv); diff --git a/crates/lb/src/net.rs b/crates/lb/src/net.rs index 3cfae98..6c0226d 100644 --- a/crates/lb/src/net.rs +++ b/crates/lb/src/net.rs @@ -7,7 +7,7 @@ //! //! See [`lb_netlib`] for items exported by this library. use derive_more::{From, FromStr}; -use luaffi::{cdef, metatype}; +use luaffi::{cdef, marker::OneOf, metatype}; use std::{ net::{AddrParseError, IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr}, time::Duration, @@ -82,7 +82,10 @@ impl lb_netlib { /// If `s` is an [`lb_ipaddr`], a copy of that value is returned. If `s` is an /// [`lb_socketaddr`], the IP address part of the socket address is returned. Otherwise, parses /// `s` as an IP address string. Both IPv4 or IPv6 addresses are supported. - pub extern "Lua" fn ipaddr(&self, addr: any) -> Result { + pub extern "Lua" fn ipaddr( + &self, + addr: OneOf<(&lb_ipaddr, &lb_socketaddr, &str)>, + ) -> Result { if __istype(__ct.lb_ipaddr, addr) { __new(__ct.lb_ipaddr, addr) // copy constructor } else if __istype(__ct.lb_socketaddr, addr) { @@ -105,7 +108,11 @@ impl lb_netlib { /// socket address string. Both IPv4 and IPv6 addresses are supported. /// /// If `port` is not specified, `0` is used as the default. - pub extern "Lua" fn socketaddr(&self, addr: any, port: any) -> Result { + pub extern "Lua" fn socketaddr( + &self, + addr: OneOf<(&lb_ipaddr, &lb_socketaddr, &str)>, + port: Option, + ) -> Result { if port != () { self.__new_skaddr(self.ipaddr(addr), port) } else { @@ -145,7 +152,11 @@ impl lb_netlib { Ok(Some(TcpSocket::new_v6()?).into()) } - pub async extern "Lua" fn bind_tcp(&self, addr: any, port: any) -> Result { + pub async extern "Lua" fn bind_tcp( + &self, + addr: OneOf<(&lb_ipaddr, &lb_socketaddr, &str)>, + port: Option, + ) -> Result { let addr = self.socketaddr(addr, port); let socket; if addr.ip().is_v6() { @@ -157,7 +168,11 @@ impl lb_netlib { socket } - pub async extern "Lua" fn connect_tcp(&self, addr: any, port: any) -> Result { + pub async extern "Lua" fn connect_tcp( + &self, + addr: OneOf<(&lb_ipaddr, &lb_socketaddr, &str)>, + port: Option, + ) -> Result { let addr = self.socketaddr(addr, port); let socket; if addr.ip().is_v6() { @@ -168,7 +183,11 @@ impl lb_netlib { socket.connect(addr) } - pub async extern "Lua" fn listen_tcp(&self, addr: any, port: any) -> Result { + pub async extern "Lua" fn listen_tcp( + &self, + addr: OneOf<(&lb_ipaddr, &lb_socketaddr, &str)>, + port: Option, + ) -> Result { self.bind_tcp(addr, port).listen(1024) } } @@ -208,7 +227,7 @@ impl lb_ipaddr { } /// Returns the string `"v4"` if this is an IPv4 address or `"v6"` if this is an IPv6 address. - pub extern "Lua" fn family(&self) -> string { + pub extern "Lua" fn family(&self) -> String { if self.is_v6() { "v6" } else { "v4" } } @@ -313,7 +332,10 @@ impl lb_socketaddr { /// Sets the IP part of this address. /// /// This function accepts the same arguments as [`ipaddr`](lb_netlib::ipaddr). - pub extern "Lua" fn set_ip(&mut self, addr: any) -> &mut Self { + pub extern "Lua" fn set_ip( + &mut self, + addr: OneOf<(&lb_ipaddr, &lb_socketaddr, &str)>, + ) -> &mut Self { if __istype(__ct.lb_ipaddr, addr) { self.__set_ip(addr); } else if __istype(__ct.lb_socketaddr, addr) { @@ -338,7 +360,7 @@ impl lb_socketaddr { } /// Sets the port part of this address. - pub extern "Lua" fn set_port(&mut self, port: integer) -> &mut Self { + pub extern "Lua" fn set_port(&mut self, port: u16) -> &mut Self { self.__set_port(port); self } diff --git a/crates/lb/src/task.rs b/crates/lb/src/task.rs index a4b1f99..ad65a4e 100644 --- a/crates/lb/src/task.rs +++ b/crates/lb/src/task.rs @@ -7,7 +7,11 @@ //! //! See [`lb_tasklib`] for items exported by this library. use crate::runtime::spawn; -use luaffi::{cdef, metatype}; +use luaffi::{ + cdef, + marker::{function, many}, + metatype, +}; use luajit::{LUA_MULTRET, Type}; use std::{ffi::c_int, time::Duration}; use tokio::{task::JoinHandle, time::sleep}; diff --git a/crates/luaffi/src/future.rs b/crates/luaffi/src/future.rs index b30b297..7a24646 100644 --- a/crates/luaffi/src/future.rs +++ b/crates/luaffi/src/future.rs @@ -1,7 +1,7 @@ use crate::{ __internal::{display, type_id}, - Cdef, CdefBuilder, FfiReturnConvention, IntoFfi, Metatype, MetatypeBuilder, Type, TypeBuilder, - TypeType, UnsafeExternCFn, + Cdef, CdefBuilder, ExternCFn, FfiReturnConvention, IntoFfi, Metatype, MetatypeBuilder, Type, + TypeBuilder, TypeType, }; use luaify::luaify; use std::{ @@ -152,8 +152,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::::Into>>("__take") - .field::>("__drop"); + .field::::Into>>("__take") + .field::>("__drop"); } } diff --git a/crates/luaffi/src/internal.rs b/crates/luaffi/src/internal.rs index 26bd577..67a0823 100644 --- a/crates/luaffi/src/internal.rs +++ b/crates/luaffi/src/internal.rs @@ -6,24 +6,6 @@ use std::{ hash::{Hash, Hasher}, }; -#[allow(non_camel_case_types)] -pub mod stub_types { - pub struct any; - pub struct many; - pub struct nil; - pub type boolean = bool; - pub struct lightuserdata; - pub struct number; - pub struct integer; - pub type string = String; - pub struct table; - pub struct function; - pub struct userdata; - pub struct thread; - pub struct cdata; - pub struct variadic; -} - pub fn type_id() -> u64 { let mut hash = FxHasher::default(); TypeId::of::().hash(&mut hash); diff --git a/crates/luaffi/src/lib.rs b/crates/luaffi/src/lib.rs index 8ff8ec0..fee204a 100644 --- a/crates/luaffi/src/lib.rs +++ b/crates/luaffi/src/lib.rs @@ -15,6 +15,7 @@ use std::{ #[path = "./internal.rs"] pub mod __internal; pub mod future; +pub mod marker; pub mod option; pub mod result; pub mod string; @@ -136,9 +137,9 @@ pub struct Registry { impl Registry { pub fn new() -> Self { let mut s = Self::default(); - s.declare::>(KEEP_FN); - s.declare::>(IS_UTF8_FN); - s.declare::>(DROP_BUFFER_FN); + s.declare::>(KEEP_FN); + s.declare::>(IS_UTF8_FN); + s.declare::>(DROP_BUFFER_FN); s } @@ -603,6 +604,10 @@ impl<'r, 'm> MetatypeFunctionBuilder<'r, 'm> { } } +pub trait Annotation { + fn annotation() -> impl Display; +} + // // SAFETY: Unit type return maps to a C void return, which is a nil return in lua. There is no // equivalent to passing a unit type as an argument in C. @@ -636,7 +641,7 @@ impl_void!(()); impl_void!(c_void); macro_rules! impl_primitive { - ($rty:ty, $cty:expr) => { + ($rty:ty, $cty:expr, $lty:expr) => { unsafe impl Type for $rty { fn name() -> impl Display { $cty @@ -652,22 +657,28 @@ macro_rules! impl_primitive { fn build(_b: &mut TypeBuilder) {} } + + impl Annotation for $rty { + fn annotation() -> impl Display { + $lty + } + } }; } -impl_primitive!(bool, "bool"); -impl_primitive!(u8, "uint8_t"); -impl_primitive!(u16, "uint16_t"); -impl_primitive!(u32, "uint32_t"); -impl_primitive!(u64, "uint64_t"); -impl_primitive!(usize, "uintptr_t"); -impl_primitive!(i8, "int8_t"); -impl_primitive!(i16, "int16_t"); -impl_primitive!(i32, "int32_t"); -impl_primitive!(i64, "int64_t"); -impl_primitive!(isize, "intptr_t"); -impl_primitive!(c_float, "float"); -impl_primitive!(c_double, "double"); +impl_primitive!(bool, "bool", "boolean"); +impl_primitive!(u8, "uint8_t", "number"); +impl_primitive!(u16, "uint16_t", "number"); +impl_primitive!(u32, "uint32_t", "number"); +impl_primitive!(u64, "uint64_t", "number"); +impl_primitive!(usize, "uintptr_t", "number"); +impl_primitive!(i8, "int8_t", "number"); +impl_primitive!(i16, "int16_t", "number"); +impl_primitive!(i32, "int32_t", "number"); +impl_primitive!(i64, "int64_t", "number"); +impl_primitive!(isize, "intptr_t", "number"); +impl_primitive!(c_float, "float", "number"); +impl_primitive!(c_double, "double", "number"); unsafe impl FromFfi for bool { type From = bool; @@ -772,11 +783,19 @@ impl_bigint_intoabi!(usize); #[cfg(target_pointer_width = "64")] impl_bigint_intoabi!(isize); -macro_rules! impl_const_ptr { - ($ty:ty) => { - unsafe impl Type for $ty { +macro_rules! impl_ptr { + ($ty:ty, $mutable:expr) => { + unsafe impl Type for $ty + where + T: Type, + { fn name() -> impl Display { - display!("const_{}_ptr", T::name()) + disp(|f| { + if !$mutable { + write!(f, "const_")?; + } + write!(f, "{}_ptr", T::name()) + }) } fn ty() -> TypeType { @@ -784,7 +803,12 @@ macro_rules! impl_const_ptr { } fn cdecl(name: impl Display) -> impl Display { - T::cdecl(display!("const *{name}")) + T::cdecl(disp(move |f| { + if !$mutable { + write!(f, "const ")?; + } + write!(f, "*{name}") + })) } fn build(b: &mut TypeBuilder) { @@ -794,35 +818,44 @@ macro_rules! impl_const_ptr { }; } -impl_const_ptr!(*const T); -impl_const_ptr!(&T); -impl_const_ptr!(Option<&T>); +impl_ptr!(&T, false); +impl_ptr!(&mut T, true); +impl_ptr!(*const T, false); +impl_ptr!(*mut T, true); +impl_ptr!(Option<&T>, false); +impl_ptr!(Option<&mut T>, true); -macro_rules! impl_mut_ptr { +macro_rules! impl_ptr_annotation { ($ty:ty) => { - unsafe impl Type for $ty { - fn name() -> impl Display { - display!("{}_ptr", T::name()) - } - - fn ty() -> TypeType { - TypeType::Primitive - } - - fn cdecl(name: impl Display) -> impl Display { - T::cdecl(display!("*{name}")) - } - - fn build(b: &mut TypeBuilder) { - b.include::(); + impl Annotation for $ty + where + T: Annotation, + { + fn annotation() -> impl Display { + "lightuserdata" } } }; } -impl_mut_ptr!(*mut T); -impl_mut_ptr!(&mut T); -impl_mut_ptr!(Option<&mut T>); +impl_ptr_annotation!(*const T); +impl_ptr_annotation!(*mut T); + +macro_rules! impl_ref_annotation { + ($ty:ty) => { + impl Annotation for $ty + where + T: Annotation, + { + fn annotation() -> impl Display { + display!("{}", T::annotation()) + } + } + }; +} + +impl_ref_annotation!(&T); +impl_ref_annotation!(&mut T); // // SAFETY: Pass by value for pointers, which maps to a `cdata` argument in lua containing either: @@ -835,7 +868,10 @@ impl_mut_ptr!(Option<&mut T>); // macro_rules! impl_ptr_fromabi { ($ty:ty) => { - unsafe impl FromFfi for $ty { + unsafe impl FromFfi for $ty + where + T: Type, + { type From = Self; fn convert(from: Self::From) -> Self { @@ -857,7 +893,10 @@ impl_ptr_fromabi!(Option<&mut T>); // macro_rules! impl_ptr_intoabi { ($ty:ty) => { - unsafe impl IntoFfi for $ty { + unsafe impl IntoFfi for $ty + where + T: Type, + { type Into = Self; fn convert(self) -> Self::Into { @@ -898,7 +937,10 @@ impl_ptr_intoabi!(Option<&'static mut T>); // macro_rules! impl_ref_fromabi { ($ty:ty) => { - unsafe impl<'s, T: Type> FromFfi for $ty { + unsafe impl<'s, T> FromFfi for $ty + where + T: Type, + { type From = Option<$ty>; fn prelude(arg: &str) -> impl Display { @@ -928,7 +970,10 @@ impl_ref_fromabi!(&'s mut T); // macro_rules! impl_ref_intoabi { ($ty:ty) => { - unsafe impl IntoFfi for $ty { + unsafe impl IntoFfi for $ty + where + T: Type, + { type Into = Self; fn convert(self) -> Self::Into { @@ -947,7 +992,10 @@ impl_ref_intoabi!(&'static mut T); // // TODO: we could automatically convert them to tables and vice-versa // -unsafe impl Type for [T] { +unsafe impl Type for [T] +where + T: Type, +{ fn name() -> impl Display { display!("{}_arr", T::name()) } @@ -965,7 +1013,19 @@ unsafe impl Type for [T] { } } -unsafe impl Type for [T; N] { +impl Annotation for [T] +where + T: Annotation, +{ + fn annotation() -> impl Display { + display!("{}[]", T::annotation()) + } +} + +unsafe impl Type for [T; N] +where + T: Type, +{ fn name() -> impl Display { display!("{}_arr{N}", T::name()) } @@ -983,14 +1043,23 @@ unsafe impl Type for [T; N] { } } -pub struct UnsafeExternCFn(PhantomData Out>); +impl Annotation for [T; N] +where + T: Annotation, +{ + fn annotation() -> impl Display { + display!("{}[]", T::annotation()) + } +} -macro_rules! impl_function { - (fn($($arg:tt),*) -> $ret:tt) => { - impl_function!(UnsafeExternCFn, fn($($arg),*) -> $ret); +pub struct ExternCFn(PhantomData O>); + +macro_rules! impl_externcfn { + (fn($($arg:ident),*) -> $ret:ident) => { + impl_externcfn!(ExternCFn, fn($($arg),*) -> $ret); }; - ($ty:tt, fn($($arg:tt),*) -> $ret:tt) => { + ($ty:ident, fn($($arg:ident),*) -> $ret:ident) => { // // SAFETY: No `FromFfi` for function pointers because of borrow safety invariants (see above // in `&mut T`). @@ -998,7 +1067,11 @@ macro_rules! impl_function { // We also can't implement `IntoFfi` because we can't call `FromFfi` and `IntoFfi` for the // function's respective argument and return values. // - unsafe impl<$($arg: Type,)* $ret: Type> Type for $ty<($($arg,)*), $ret> { + unsafe impl<$($arg,)* $ret> Type for $ty<($($arg,)*), $ret> + where + $($arg: Type,)* + $ret: Type, + { fn name() -> impl Display { disp(|f| Ok({ write!(f, "fn_{}", $ret::name())?; @@ -1012,8 +1085,8 @@ macro_rules! impl_function { fn cdecl(name: impl Display) -> impl Display { $ret::cdecl(disp(move |f| Ok({ - let mut _n = 0; write!(f, "(*{name})(")?; + let mut _n = 0; $(if _n != 0 { write!(f, ", ")?; } write!(f, "{}", $arg::cdecl(""))?; _n += 1;)* write!(f, ")")?; }))) @@ -1022,8 +1095,8 @@ macro_rules! impl_function { fn extern_cdecl(name: impl Display) -> impl Display { $ret::cdecl(disp(move |f| Ok({ // for top-level function declarations in cdef - let mut _n = 0; write!(f, "{name}(")?; + let mut _n = 0; $(if _n != 0 { write!(f, ", ")?; } write!(f, "{}", $arg::cdecl(""))?; _n += 1;)* write!(f, ")")?; }))) @@ -1037,11 +1110,25 @@ macro_rules! impl_function { }; } -impl_function!(fn() -> Z); -impl_function!(fn(A) -> Z); -impl_function!(fn(A, B) -> Z); -impl_function!(fn(A, B, C) -> Z); -impl_function!(fn(A, B, C, D) -> Z); -impl_function!(fn(A, B, C, D, E) -> Z); -impl_function!(fn(A, B, C, D, E, F) -> Z); -impl_function!(fn(A, B, C, D, E, F, G) -> Z); +impl_externcfn!(fn() -> A); +impl_externcfn!(fn(A) -> B); +impl_externcfn!(fn(A, B) -> C); +impl_externcfn!(fn(A, B, C) -> D); +impl_externcfn!(fn(A, B, C, D) -> E); +impl_externcfn!(fn(A, B, C, D, E) -> F); +impl_externcfn!(fn(A, B, C, D, E, F) -> G); +impl_externcfn!(fn(A, B, C, D, E, F, G) -> H); +impl_externcfn!(fn(A, B, C, D, E, F, G, H) -> I); +impl_externcfn!(fn(A, B, C, D, E, F, G, H, I) -> J); + +impl<'s> Annotation for &'s str { + fn annotation() -> impl Display { + "string" + } +} + +impl Annotation for String { + fn annotation() -> impl Display { + "string" + } +} diff --git a/crates/luaffi/src/marker.rs b/crates/luaffi/src/marker.rs new file mode 100644 index 0000000..48d79ff --- /dev/null +++ b/crates/luaffi/src/marker.rs @@ -0,0 +1,193 @@ +#![allow(non_camel_case_types)] +use crate::{ + __internal::{disp, display}, + Annotation, +}; +use std::{fmt::Display, marker::PhantomData}; + +enum Marker {} + +pub struct any(Marker); + +impl Annotation for any { + fn annotation() -> impl Display { + "any" + } +} + +pub struct many(Marker); + +impl Annotation for many { + fn annotation() -> impl Display { + "..." + } +} + +pub struct nil(Marker); + +impl Annotation for nil { + fn annotation() -> impl Display { + "nil" + } +} + +pub struct lightuserdata(Marker); + +impl Annotation for lightuserdata { + fn annotation() -> impl Display { + "lightuserdata" + } +} + +pub struct table(Marker, PhantomData<*mut [(K, V)]>); + +impl Annotation for table +where + K: Annotation, + V: Annotation, +{ + fn annotation() -> impl Display { + display!("table<{}, {}>", K::annotation(), V::annotation()) + } +} + +pub struct function(Marker); + +impl Annotation for function { + fn annotation() -> impl Display { + "function" + } +} + +pub struct fun(Marker, PhantomData O>); + +macro_rules! impl_fun { + (fn($($arg:ident),*)) => { + impl<$($arg,)*> Annotation for fun<($($arg,)*), ()> + where + $($arg: Annotation,)* + { + fn annotation() -> impl Display { + disp(|f| { + write!(f, "fun(")?; + let mut _n = 0; + $(if _n != 0 { write!(f, ", ")?; } write!(f, "{}", $arg::annotation())?; _n += 1;)* + write!(f, ")") + }) + } + } + }; + + (fn($($arg:ident),*) -> $ret:ident) => { + impl<$($arg,)* $ret> Annotation for fun<($($arg,)*), $ret> + where + $($arg: Annotation,)* + $ret: Annotation, + { + fn annotation() -> impl Display { + disp(|f| { + write!(f, "fun(")?; + let mut _n = 0; + $(if _n != 0 { write!(f, ", ")?; } write!(f, "{}", $arg::annotation())?; _n += 1;)* + write!(f, "): {}", $ret::annotation()) + }) + } + } + + impl_fun!(fn($($arg),*)); + }; +} + +impl_fun!(fn() -> A); +impl_fun!(fn(A) -> B); +impl_fun!(fn(A, B) -> C); +impl_fun!(fn(A, B, C) -> D); +impl_fun!(fn(A, B, C, D) -> E); +impl_fun!(fn(A, B, C, D, E) -> F); +impl_fun!(fn(A, B, C, D, E, F) -> G); +impl_fun!(fn(A, B, C, D, E, F, G) -> H); +impl_fun!(fn(A, B, C, D, E, F, G, H) -> I); +impl_fun!(fn(A, B, C, D, E, F, G, H, I) -> J); + +pub struct userdata(Marker); + +impl Annotation for userdata { + fn annotation() -> impl Display { + "userdata" + } +} + +pub struct thread(Marker); + +impl Annotation for thread { + fn annotation() -> impl Display { + "thread" + } +} + +pub struct cdata(Marker); + +impl Annotation for cdata { + fn annotation() -> impl Display { + "cdata" + } +} + +pub struct Either(Marker, PhantomData<(T, U)>); + +impl Annotation for Either +where + T: Annotation, + U: Annotation, +{ + fn annotation() -> impl Display { + display!("({} | {})", T::annotation(), U::annotation()) + } +} + +pub struct OneOf(Marker, PhantomData); + +macro_rules! impl_oneof { + ($($ty:ident),+) => { + impl<$($ty),+> Annotation for OneOf<($($ty,)+)> + where + $($ty: Annotation),+ + { + fn annotation() -> impl Display { + disp(|f| { + write!(f, "(")?; + let mut _n = 0; + $(if _n != 0 { write!(f, " | ")?; } write!(f, "{}", $ty::annotation())?; _n += 1;)* + write!(f, ")") + }) + } + } + }; +} + +impl_oneof!(A, B); +impl_oneof!(A, B, C); +impl_oneof!(A, B, C, D); +impl_oneof!(A, B, C, D, E); +impl_oneof!(A, B, C, D, E, F); +impl_oneof!(A, B, C, D, E, F, G); +impl_oneof!(A, B, C, D, E, F, G, H); +impl_oneof!(A, B, C, D, E, F, G, H, I); + +impl Annotation for Option +where + T: Annotation, +{ + fn annotation() -> impl Display { + display!("{}?", T::annotation()) + } +} + +impl Annotation for Result +where + T: Annotation, +{ + fn annotation() -> impl Display { + display!("{}", T::annotation()) + } +} diff --git a/crates/luaffi_impl/src/cdef.rs b/crates/luaffi_impl/src/cdef.rs index 53e0a3a..6931c24 100644 --- a/crates/luaffi_impl/src/cdef.rs +++ b/crates/luaffi_impl/src/cdef.rs @@ -32,7 +32,7 @@ pub fn transform(args: Args, mut item: Item) -> Result { let mod_name = format_ident!("__{name}_cdef"); - Ok(quote_spanned!(name.span() => + Ok(quote!( #[repr(C)] #[allow(non_camel_case_types)] #item @@ -51,10 +51,9 @@ pub fn transform(args: Args, mut item: Item) -> Result { fn generate_type(ty: &Ident) -> Result { let ffi = ffi_crate(); - let span = ty.span(); - let name = LitStr::new(&ty.unraw().to_string(), span); + let name = ty.unraw().to_string(); - Ok(quote_spanned!(span => + Ok(quote!( unsafe impl #ffi::Type for #ty { fn name() -> impl ::std::fmt::Display { #name } @@ -71,6 +70,10 @@ fn generate_type(ty: &Ident) -> Result { } } + impl #ffi::Annotation for #ty { + fn annotation() -> impl ::std::fmt::Display { #name } + } + // SAFETY: we can always implement `IntoFfi` because it transfers ownership from Rust to Lua unsafe impl #ffi::IntoFfi for #ty { type Into = Self; @@ -82,7 +85,7 @@ fn generate_type(ty: &Ident) -> Result { fn generate_module(name: &str, ty: &Ident) -> Result { let ffi = ffi_crate(); - Ok(quote_spanned!(ty.span() => + Ok(quote!( impl #ffi::Module for #ty { fn name() -> impl ::std::fmt::Display { #name } } @@ -98,10 +101,9 @@ fn generate_cdef_structure(str: &mut ItemStruct) -> Result { let ffi = ffi_crate(); let ty = &str.ident; - let span = ty.span(); let build = generate_cdef_build(&get_cfields(&mut str.fields)?)?; - Ok(quote_spanned!(span => + Ok(quote!( unsafe impl #ffi::Cdef for #ty { fn build(b: &mut #ffi::CdefBuilder) { #build } } @@ -117,18 +119,16 @@ fn generate_cdef_enum(enu: &mut ItemEnum) -> Result { let ffi = ffi_crate(); let ty = &enu.ident; - let span = ty.span(); let build = enu .variants .iter_mut() .map(|variant| { - let span = variant.span(); let build = generate_cdef_build(&get_cfields(&mut variant.fields)?)?; - Ok(quote_spanned!(span => b.inner_struct(|b| { #build }))) + Ok(quote!(b.inner_struct(|b| { #build }))) }) .collect::>>()?; - Ok(quote_spanned!(span => + Ok(quote!( unsafe impl #ffi::Cdef for #ty { fn build(b: &mut #ffi::CdefBuilder) { b.field::<::std::ffi::c_int>("__tag").inner_union(|b| { #(#build;)* }); @@ -201,7 +201,7 @@ fn generate_cdef_build(fields: &[CField]) -> Result { for (i, field) in fields.iter().enumerate() { let ty = &field.ty; let offset = offset(i); - body.push(quote_spanned!(ty.span() => + body.push(quote!( // round up current offset to the alignment of field type for field offset offset = (offset + #align_of::<#ty>() - 1) & !(#align_of::<#ty>() - 1); align = #max(align, #align_of::<#ty>()); diff --git a/crates/luaffi_impl/src/metatype.rs b/crates/luaffi_impl/src/metatype.rs index 384e4cf..e5f7cc0 100644 --- a/crates/luaffi_impl/src/metatype.rs +++ b/crates/luaffi_impl/src/metatype.rs @@ -21,7 +21,7 @@ pub fn transform(args: Args, mut imp: ItemImpl) -> Result { let impls = generate_impls(&args, &mut imp)?; let mod_name = format_ident!("__{}_metatype", ty_name(&imp.self_ty)?); - Ok(quote_spanned!(imp.self_ty.span() => + Ok(quote!( #imp #[doc(hidden)] @@ -119,7 +119,7 @@ fn generate_impls(_args: &Args, imp: &mut ItemImpl) -> Result { let build = ®istry.build; let exports = generate_ffi_exports(®istry)?; - Ok(quote_spanned!(ty.span() => + Ok(quote!( impl #ty { #(#shims)* } unsafe impl #ffi::Metatype for #ty { @@ -219,10 +219,10 @@ impl ToTokens for Metamethod { struct FfiFunction { name: Ident, - is_async: bool, - params: Vec, + params: Vec<(Ident, Type)>, ret: Type, attrs: FfiFunctionAttrs, + is_async: bool, } #[derive(Default)] @@ -251,30 +251,31 @@ fn get_ffi_functions(imp: &mut ItemImpl) -> Result> { .sig .inputs .iter() - .map(|arg| match arg { - FnArg::Receiver(recv) => { - let ty = &recv.ty; - parse_quote_spanned!(ty.span() => self: #ty) - } - FnArg::Typed(ty) => ty.clone(), + .map(|arg| { + Ok(match arg { + FnArg::Receiver(recv) => { + (Ident::new("self", recv.span()), (*recv.ty).clone()) + } + FnArg::Typed(ty) => (pat_ident(&ty.pat)?.clone(), (*ty.ty).clone()), + }) }) - .collect(); + .collect::>()?; let ret = match func.sig.output { ReturnType::Default => parse_quote!(()), ReturnType::Type(_, ref ty) => (**ty).clone(), }; - for param in params.iter() { - // double underscores are reserved for generated glue code + for (name, ty) in params.iter() { + // double underscores are reserved for glue code syn_assert!( - !pat_ident(¶m.pat)?.to_string().starts_with("__"), - param.pat, + !name.to_string().starts_with("__"), + name, "parameter names should not start with `__`" ); - // lifetime should be determined by the caller (lua) - if let Type::Reference(ref ty) = *param.ty { + // lifetime should be determined by the caller (which is lua) + if let Type::Reference(ty) = ty { syn_assert!( ty.lifetime.is_none(), ty.lifetime, @@ -290,10 +291,10 @@ fn get_ffi_functions(imp: &mut ItemImpl) -> Result> { funcs.push(FfiFunction { name: func.sig.ident.clone(), - is_async: func.sig.asyncness.is_some(), params, ret, attrs, + is_async: func.sig.asyncness.is_some(), }); } } @@ -387,16 +388,8 @@ fn add_ffi_function(registry: &mut Registry, func: &FfiFunction) -> Result<()> { let func_params = &func.params; // target function parameters let func_ret = &func.ret; // target function return type let mut func_args = vec![]; // target function arguments - let mut shim_params = vec![]; // shim function parameters - let mut shim_ret = if func.is_async { - // shim function return type - quote_spanned!(func_ret.span() => #ffi::future::lua_future>) - } else { - quote_spanned!(func_ret.span() => <#func_ret as #ffi::IntoFfi>::Into) - }; - - let mut asserts = vec![]; // compile-time builder asserts + let mut asserts = vec![]; // compile-time asserts let mut build = vec![]; // ffi builder body // for __new metamethods, ignore the first argument (ctype of self, for which there is no @@ -407,99 +400,102 @@ fn add_ffi_function(registry: &mut Registry, func: &FfiFunction) -> Result<()> { )); } - for (i, param) in func_params.iter().enumerate() { - let func_param = ¶m.ty; + for (i, (name, func_param)) in func_params.iter().enumerate() { + let span = func_param.span(); + let name = name.unraw().to_string(); let shim_param = format_ident!("arg{i}"); - let name = pat_ident(¶m.pat)?.unraw().to_string(); match get_ffi_param_type(func_param) { FfiParameterType::Default => { - shim_params.push(quote_spanned!(func_param.span() => + shim_params.push(quote_spanned!(span => #shim_param: <#func_param as #ffi::FromFfi>::From )); - func_args.push(quote_spanned!(param.pat.span() => + func_args.push(quote_spanned!(span => <#func_param as #ffi::FromFfi>::convert(#shim_param) )); - build.push(quote_spanned!(param.pat.span() => + build.push(quote_spanned!(span => b.param::<#func_param>(#name); )); } ty @ (FfiParameterType::StringLike(str) | FfiParameterType::OptionStringLike(str)) => { let shim_param_len = format_ident!("arg{i}_len"); - shim_params.push(quote_spanned!(func_param.span() => + shim_params.push(quote_spanned!(span => #shim_param: ::std::option::Option<&::std::primitive::u8>, #shim_param_len: ::std::primitive::usize )); let allow_nil = matches!(ty, FfiParameterType::OptionStringLike(_)); let check_utf8 = matches!(str, StringLike::Str); - let mut func_arg = quote_spanned!(func_param.span() => + let mut func_arg = quote_spanned!(span => #shim_param.map(|s| ::std::slice::from_raw_parts(s, #shim_param_len)) ); func_arg = match str { StringLike::SliceU8 => func_arg, StringLike::BStr => { - quote_spanned!(func_param.span() => #func_arg.map(::bstr::BStr::new)) + quote_spanned!(span => #func_arg.map(::bstr::BStr::new)) } StringLike::Str => { - quote_spanned!(func_param.span() => #func_arg.map(|s| { + quote_spanned!(span => #func_arg.map(|s| { ::std::debug_assert!(::std::str::from_utf8(s).is_ok()); ::std::str::from_utf8_unchecked(s) })) } }; if !allow_nil { - func_arg = quote_spanned!(func_param.span() => { + func_arg = quote_spanned!(span => { let arg = #func_arg; ::std::debug_assert!(arg.is_some()); arg.unwrap_unchecked() }); } func_args.push(func_arg); - build.push(quote_spanned!(param.pat.span() => + build.push(quote_spanned!(span => b.param_str(#name, #allow_nil, #check_utf8); )); } } } - // shim function body - let mut shim_body = if func.is_async { - // for async functions, wrapped the returned future in lua_future - quote_spanned!(func_name.span() => #ffi::future::lua_future::new(Self::#func_name(#(#func_args),*))) - } else { - quote_spanned!(func_name.span() => <#func_ret as #ffi::IntoFfi>::convert(Self::#func_name(#(#func_args),*))) - }; + let func_call = quote!(Self::#func_name(#(#func_args),*)); // target function call - if !func.is_async { + // shim function body and return type + let (shim_body, shim_ret) = if func.is_async { + let span = func_ret.span(); + ( + // for async functions, wrapped the returned future in lua_future + quote_spanned!(span => #ffi::future::lua_future::new(#func_call)), + quote_spanned!(span => #ffi::future::lua_future>), + ) + } else { + let span = func_ret.span(); match get_ffi_ret_type(&func_ret) { FfiReturnType::Void => { - asserts.push(quote_spanned!(func_ret.span() => + asserts.push(quote_spanned!(span => <<#func_ret as #ffi::IntoFfi>::Into as #ffi::Type>::ty() == #ffi::TypeType::Void )); + (func_call, quote_spanned!(span => ())) } FfiReturnType::ByValue => { - asserts.push(quote_spanned!(func_ret.span() => + asserts.push(quote_spanned!(span => <#func_ret as #ffi::IntoFfi>::convention() == #ffi::FfiReturnConvention::ByValue )); + ( + quote_spanned!(span => <#func_ret as #ffi::IntoFfi>::convert(#func_call)), + quote_spanned!(span => <#func_ret as #ffi::IntoFfi>::Into), + ) } FfiReturnType::ByOutParam => { - asserts.push(quote_spanned!(func_ret.span() => + asserts.push(quote_spanned!(span => <#func_ret as #ffi::IntoFfi>::convention() == #ffi::FfiReturnConvention::ByOutParam )); - - shim_params.insert(0, quote_spanned!(func_ret.span() => out: *mut #shim_ret)); - - (shim_body, shim_ret) = ( - quote_spanned!(func_ret.span() => ::std::ptr::write(out, #shim_body)), - quote_spanned!(func_ret.span() => ()), - ); + let out = quote_spanned!(span => out: *mut <#func_ret as #ffi::IntoFfi>::Into); + shim_params.insert(0, out); + ( + quote_spanned!(span => ::std::ptr::write(out, <#func_ret as #ffi::IntoFfi>::convert(#func_call))), + quote_spanned!(span => ()), + ) } - }; - } - - // build.push(quote_spanned!(func_name.span() => - // b.call_inferred(#c_name, Self::#func_name); - // )); + } + }; build.push({ let infer_args = iter::repeat_n(quote!(::std::unreachable!()), func_params.len()); @@ -508,23 +504,16 @@ fn add_ffi_function(registry: &mut Registry, func: &FfiFunction) -> Result<()> { } else { quote!(|| Self::#func_name(#(#infer_args),*)) }; - quote_spanned!(func_name.span() => b.call_inferred(#c_name, #infer);) + quote!(b.call_inferred(#c_name, #infer);) }); - registry.build.push(quote_spanned!(func_name.span() => - #(::std::assert!(#asserts);)* - )); - + registry.build.push(quote!(#(::std::assert!(#asserts);)*)); registry.build.push(match func.attrs.metamethod { - Some(ref mm) => quote_spanned!(func_name.span() => - b.metatable(#mm, |b| { #(#build)* }); - ), - None => quote_spanned!(func_name.span() => - b.index(#lua_name, |b| { #(#build)* }); - ), + Some(ref mm) => quote!(b.metatable(#mm, |b| { #(#build)* });), + None => quote!(b.index(#lua_name, |b| { #(#build)* });), }); - registry.shims.push(parse_quote_spanned!(func_name.span() => + registry.shims.push(parse_quote!( #[unsafe(export_name = #c_name)] unsafe extern "C" fn #shim_name(#(#shim_params),*) -> #shim_ret { unsafe { #shim_body } } )); @@ -536,7 +525,7 @@ fn generate_ffi_exports(registry: &Registry) -> Result { let ty = ®istry.ty; let names = registry.shims.iter().map(|f| &f.sig.ident); - Ok(quote_spanned!(ty.span() => + Ok(quote!( // this ensures ffi function symbol exports are actually present in the resulting binary, // otherwise they may get dead code-eliminated before it reaches the linker #[used] @@ -583,18 +572,14 @@ fn get_lua_functions(imp: &mut ItemImpl) -> Result> { .sig .inputs .iter() - .map(|arg| { - Ok(match arg { - FnArg::Receiver(recv) => Pat::Type(parse_quote_spanned!(recv.span() => - self: cdata - )), - FnArg::Typed(ty) => Pat::Type(ty.clone()), - }) + .map(|arg| match arg { + FnArg::Receiver(recv) => parse_quote_spanned!(recv.span() => self), + FnArg::Typed(ty) => (*ty.pat).clone(), // ignore parameter type (only used for documentation purposes) }) - .collect::>()?; + .collect(); if let Some(ref variadic) = func.sig.variadic { - params.push(parse_quote_spanned!(variadic.span() => variadic!())); + params.push(parse_quote_spanned!(variadic.span() => variadic!())); // luaify builtin macro } let attrs = parse_lua_function_attrs(&mut func.attrs)?; @@ -625,62 +610,45 @@ fn stub_lua_function(func: &mut ImplItemFn) -> Result<()> { func.attrs.push(parse_quote!(#[allow(unused)])); func.block.stmts.clear(); func.block.stmts.push(parse_quote!( - ::std::unreachable!("cannot call lua function from rust"); + const fn has_annotation() {} )); - let inputs = &mut func.sig.inputs; - let output = &mut func.sig.output; - - for input in inputs.iter_mut() { - if let FnArg::Typed(pat) = input - && let Ok(stub) = stub_lua_type(&pat.ty) - { - pat.ty = stub.into(); - } - } - - if let ReturnType::Type(_, ty) = output - && let Ok(stub) = stub_lua_type(ty) - { - *ty = stub.into(); - } - + // convert `...` variadic to a `rest: luaffi::marker::Many` parameter if let Some(ref variadic) = func.sig.variadic { - let ty = quote_spanned!(variadic.span() => variadic); - inputs.push(parse_quote!(rest: #ffi::__internal::stub_types::#ty)); + let param = Ident::new("rest", variadic.span()); + let ty = quote_spanned!(variadic.span() => many); func.sig.variadic = None; + func.sig + .inputs + .push(parse_quote!(#param: #ffi::marker::#ty)); } + for param in func.sig.inputs.iter() { + let ty = match param { + FnArg::Receiver(recv) => &recv.ty, + FnArg::Typed(ty) => &ty.ty, + }; + + // temporary assertion until we implement annotation generation + func.block.stmts.push(parse_quote!( + has_annotation::<#ty>(); + )); + } + + // temporary assertion until we implement annotation generation + if let ReturnType::Type(_, ref ty) = func.sig.output { + func.block.stmts.push(parse_quote!( + has_annotation::<#ty>(); + )); + } + + func.block.stmts.push(parse_quote!( + ::std::unreachable!(r#"cannot call extern "Lua" function from rust"#); + )); + Ok(()) } -fn stub_lua_type(ty: &Type) -> Result { - let ffi = ffi_crate(); - let span = ty.span(); - let ty = if let Type::Infer(_) = ty { - quote_spanned!(span => any) - } else { - match ty_name(ty)?.to_string().as_str() { - "any" => quote_spanned!(span => any), - "many" => quote_spanned!(span => many), - "nil" => quote_spanned!(span => nil), - "boolean" => quote_spanned!(span => boolean), - "lightuserdata" => quote_spanned!(span => lightuserdata), - "number" => quote_spanned!(span => number), - "integer" => quote_spanned!(span => integer), - "string" => quote_spanned!(span => string), - "table" => quote_spanned!(span => table), - "function" => quote_spanned!(span => function), - "userdata" => quote_spanned!(span => userdata), - "thread" => quote_spanned!(span => thread), - "cdata" => quote_spanned!(span => cdata), - _ => syn_error!(ty, "unknown lua type"), - } - }; - - Ok(parse_quote!(#ffi::__internal::stub_types::#ty)) -} - fn parse_lua_function_attrs(attrs: &mut Vec) -> Result { let mut parsed = LuaFunctionAttrs::default(); let mut i = 0; @@ -707,12 +675,8 @@ fn add_lua_function(registry: &mut Registry, func: &LuaFunction) -> Result<()> { let name = func_name.unraw().to_string(); registry.build.push(match func.attrs.metamethod { - Some(ref mm) => quote_spanned!(func_name.span() => - b.metatable_raw(#mm, #luaify(|#(#params),*| #body)); - ), - None => quote_spanned!(func_name.span() => - b.index_raw(#name, #luaify(|#(#params),*| #body)); - ), + Some(ref mm) => quote!(b.metatable_raw(#mm, #luaify(|#(#params),*| #body));), + None => quote!(b.index_raw(#name, #luaify(|#(#params),*| #body));), }); Ok(()) @@ -747,46 +711,36 @@ fn inject_merged_drop(registry: &mut Registry, lua: Option<&LuaFunction>) -> Res "finaliser must take exactly one parameter" ); - match lua.params[0] { - // should be `self: cdata` PatType - Pat::Type(ref ty) => { - syn_assert!( - pat_ident(&ty.pat)? == "self", - lua.params[0], - "finaliser parameter must be `self`" - ); - } - _ => syn_error!(lua.params[0], "finaliser parameter must be `self`"), - } - - let params = &lua.params; + let param = pat_ident(&lua.params[0])?; let body = &lua.body; - registry.build.push(quote_spanned!(ty.span() => + syn_assert!(param == "self", param, "finaliser parameter must be `self`"); + + registry.build.push(quote!( if ::std::mem::needs_drop::() { // if we have both a lua-side finaliser and a rust drop, then merge the finalisers // by doing the lua part first then drop rust - b.declare::<#ffi::UnsafeExternCFn<(*mut Self,), ()>>(#c_name_str); + b.declare::<#ffi::ExternCFn<(*mut Self,), ()>>(#c_name_str); b.metatable_raw("gc", #luaify(|self| { raw!(#luaify(#body)); // embed the lua part inside a do block __C::#c_name(self); })); } else { // we only have a lua-side finaliser - b.metatable_raw("gc", #luaify(|#(#params),*| #body)); + b.metatable_raw("gc", #luaify(|self| #body)); } )); } else { - registry.build.push(quote_spanned!(ty.span() => + registry.build.push(quote!( if ::std::mem::needs_drop::() { // we only have a rust drop - b.declare::<#ffi::UnsafeExternCFn<(*mut Self,), ()>>(#c_name_str); + b.declare::<#ffi::ExternCFn<(*mut Self,), ()>>(#c_name_str); b.metatable_raw("gc", ::std::format_args!("__C.{}", #c_name_str)); } )); } - registry.shims.push(parse_quote_spanned!(ty.span() => + registry.shims.push(parse_quote!( #[unsafe(export_name = #c_name_str)] unsafe extern "C" fn #shim_name(ptr: *mut Self) { unsafe { ::std::ptr::drop_in_place(ptr) }