Implement the foundations for annotation generation

This commit is contained in:
2025-06-28 04:15:27 +10:00
parent 6a4c726965
commit e05e2f4cb3
9 changed files with 510 additions and 268 deletions

View File

@@ -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<F: Future<Output: IntoFfi> + 'static> Type for lua_future<F> {
unsafe impl<F: Future<Output: IntoFfi> + 'static> Cdef for lua_future<F> {
fn build(s: &mut CdefBuilder) {
s.field_opaque(mem::offset_of!(Self, take)) // opaque .sig, .poll and .state
.field::<UnsafeExternCFn<(&mut Self,), <F::Output as IntoFfi>::Into>>("__take")
.field::<UnsafeExternCFn<(&mut Self,), ()>>("__drop");
.field::<ExternCFn<(&mut Self,), <F::Output as IntoFfi>::Into>>("__take")
.field::<ExternCFn<(&mut Self,), ()>>("__drop");
}
}

View File

@@ -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<T: 'static>() -> u64 {
let mut hash = FxHasher::default();
TypeId::of::<T>().hash(&mut hash);

View File

@@ -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::<UnsafeExternCFn<(*const c_void,), ()>>(KEEP_FN);
s.declare::<UnsafeExternCFn<(*const u8, usize), bool>>(IS_UTF8_FN);
s.declare::<UnsafeExternCFn<(*mut lua_buffer,), ()>>(DROP_BUFFER_FN);
s.declare::<ExternCFn<(*const c_void,), ()>>(KEEP_FN);
s.declare::<ExternCFn<(*const u8, usize), bool>>(IS_UTF8_FN);
s.declare::<ExternCFn<(*mut lua_buffer,), ()>>(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<T: Type> Type for $ty {
macro_rules! impl_ptr {
($ty:ty, $mutable:expr) => {
unsafe impl<T> 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<T: Type> 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::<T>();
impl<T> 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<T> 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<T: Type> FromFfi for $ty {
unsafe impl<T> 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<T: Type> IntoFfi for $ty {
unsafe impl<T> 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<T: Type> IntoFfi for $ty {
unsafe impl<T> 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<T: Type> Type for [T] {
unsafe impl<T> Type for [T]
where
T: Type,
{
fn name() -> impl Display {
display!("{}_arr", T::name())
}
@@ -965,7 +1013,19 @@ unsafe impl<T: Type> Type for [T] {
}
}
unsafe impl<T: Type, const N: usize> Type for [T; N] {
impl<T> Annotation for [T]
where
T: Annotation,
{
fn annotation() -> impl Display {
display!("{}[]", T::annotation())
}
}
unsafe impl<T, const N: usize> Type for [T; N]
where
T: Type,
{
fn name() -> impl Display {
display!("{}_arr{N}", T::name())
}
@@ -983,14 +1043,23 @@ unsafe impl<T: Type, const N: usize> Type for [T; N] {
}
}
pub struct UnsafeExternCFn<In, Out>(PhantomData<unsafe extern "C" fn(In) -> Out>);
impl<T, const N: usize> 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<I, O>(PhantomData<extern "C" fn(I) -> 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"
}
}

193
crates/luaffi/src/marker.rs Normal file
View File

@@ -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<K, V>(Marker, PhantomData<*mut [(K, V)]>);
impl<K, V> Annotation for table<K, V>
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<I, O>(Marker, PhantomData<fn(I) -> 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<T, U>(Marker, PhantomData<(T, U)>);
impl<T, U> Annotation for Either<T, U>
where
T: Annotation,
U: Annotation,
{
fn annotation() -> impl Display {
display!("({} | {})", T::annotation(), U::annotation())
}
}
pub struct OneOf<X>(Marker, PhantomData<X>);
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<T> Annotation for Option<T>
where
T: Annotation,
{
fn annotation() -> impl Display {
display!("{}?", T::annotation())
}
}
impl<T, E> Annotation for Result<T, E>
where
T: Annotation,
{
fn annotation() -> impl Display {
display!("{}", T::annotation())
}
}