1use super::{Callee, FromArgs, FuncArgs};
2use crate::{
3 Py, PyPayload, PyRef, PyResult, VirtualMachine, convert::ToPyResult,
4 object::PyThreadingConstraint,
5};
6use core::marker::PhantomData;
7
8pub trait PyNativeFn:
11 Fn(&VirtualMachine, FuncArgs, Callee) -> PyResult + PyThreadingConstraint + 'static
12{
13}
14
15impl<F> PyNativeFn for F where
16 F: Fn(&VirtualMachine, FuncArgs, Callee) -> PyResult + PyThreadingConstraint + 'static
17{
18}
19
20pub trait IntoPyNativeFn<Kind>: Sized + PyThreadingConstraint + 'static {
40 fn call(&self, vm: &VirtualMachine, args: FuncArgs, callee: Callee) -> PyResult;
41
42 fn into_func(self) -> impl PyNativeFn {
46 into_func(self)
47 }
48}
49
50const fn into_func<F: IntoPyNativeFn<Kind>, Kind>(f: F) -> impl PyNativeFn {
51 move |vm: &VirtualMachine, args, callee| f.call(vm, args, callee)
52}
53
54const fn zst_ref_out_of_thin_air<T: 'static>(x: T) -> &'static T {
55 core::mem::forget(x);
59 const {
60 assert!(
61 core::mem::size_of::<T>() == 0,
62 "can't use a non-zero-sized type here"
63 );
64 unsafe { core::ptr::NonNull::<T>::dangling().as_ref() }
67 }
68}
69
70#[inline(always)]
77pub const fn static_func<Kind, F: IntoPyNativeFn<Kind>>(f: F) -> &'static dyn PyNativeFn {
78 zst_ref_out_of_thin_air(into_func(f))
79}
80
81#[inline(always)]
82pub const fn static_raw_func<F: PyNativeFn>(f: F) -> &'static dyn PyNativeFn {
83 zst_ref_out_of_thin_air(f)
84}
85
86impl<F, T, R, VM> IntoPyNativeFn<(T, R, VM)> for F
89where
90 F: PyNativeFnInternal<T, R, VM>,
91{
92 #[inline(always)]
93 fn call(&self, vm: &VirtualMachine, args: FuncArgs, callee: Callee) -> PyResult {
94 self.call_(vm, args, callee)
95 }
96}
97
98mod sealed {
99 use super::*;
100 pub trait PyNativeFnInternal<T, R, VM>: Sized + PyThreadingConstraint + 'static {
101 fn call_(&self, vm: &VirtualMachine, args: FuncArgs, callee: Callee) -> PyResult;
102 }
103}
104use sealed::PyNativeFnInternal;
105
106#[doc(hidden)]
107pub struct OwnedParam<T>(PhantomData<T>);
108
109#[doc(hidden)]
110pub struct BorrowedParam<T>(PhantomData<T>);
111
112#[doc(hidden)]
113pub struct RefParam<T>(PhantomData<T>);
114
115macro_rules! into_py_native_fn_tuple {
120 ($(($n:tt, $T:ident)),*) => {
121 impl<F, $($T,)* R> PyNativeFnInternal<($(OwnedParam<$T>,)*), R, VirtualMachine> for F
122 where
123 F: Fn($($T,)* &VirtualMachine) -> R + PyThreadingConstraint + 'static,
124 $($T: FromArgs,)*
125 R: ToPyResult,
126 {
127 fn call_(&self, vm: &VirtualMachine, args: FuncArgs, callee: Callee) -> PyResult {
128 let ($($n,)*) = args.bind_for::<($($T,)*)>(vm, callee)?;
129
130 (self)($($n,)* vm).to_pyresult(vm)
131 }
132 }
133
134 impl<F, S, $($T,)* R> PyNativeFnInternal<(BorrowedParam<S>, $(OwnedParam<$T>,)*), R, VirtualMachine> for F
135 where
136 F: Fn(&Py<S>, $($T,)* &VirtualMachine) -> R + PyThreadingConstraint + 'static,
137 S: PyPayload,
138 $($T: FromArgs,)*
139 R: ToPyResult,
140 {
141 fn call_(&self, vm: &VirtualMachine, args: FuncArgs, callee: Callee) -> PyResult {
142 let (zelf, $($n,)*) = args.bind_for::<(PyRef<S>, $($T,)*)>(vm, callee)?;
143
144 (self)(&zelf, $($n,)* vm).to_pyresult(vm)
145 }
146 }
147
148 impl<F, S, $($T,)* R> PyNativeFnInternal<(RefParam<S>, $(OwnedParam<$T>,)*), R, VirtualMachine> for F
149 where
150 F: Fn(&S, $($T,)* &VirtualMachine) -> R + PyThreadingConstraint + 'static,
151 S: PyPayload,
152 $($T: FromArgs,)*
153 R: ToPyResult,
154 {
155 fn call_(&self, vm: &VirtualMachine, args: FuncArgs, callee: Callee) -> PyResult {
156 let (zelf, $($n,)*) = args.bind_for::<(PyRef<S>, $($T,)*)>(vm, callee)?;
157
158 (self)(&zelf, $($n,)* vm).to_pyresult(vm)
159 }
160 }
161
162 impl<F, $($T,)* R> PyNativeFnInternal<($(OwnedParam<$T>,)*), R, ()> for F
163 where
164 F: Fn($($T,)*) -> R + PyThreadingConstraint + 'static,
165 $($T: FromArgs,)*
166 R: ToPyResult,
167 {
168 fn call_(&self, vm: &VirtualMachine, args: FuncArgs, callee: Callee) -> PyResult {
169 let ($($n,)*) = args.bind_for::<($($T,)*)>(vm, callee)?;
170
171 (self)($($n,)*).to_pyresult(vm)
172 }
173 }
174
175 impl<F, S, $($T,)* R> PyNativeFnInternal<(BorrowedParam<S>, $(OwnedParam<$T>,)*), R, ()> for F
176 where
177 F: Fn(&Py<S>, $($T,)*) -> R + PyThreadingConstraint + 'static,
178 S: PyPayload,
179 $($T: FromArgs,)*
180 R: ToPyResult,
181 {
182 fn call_(&self, vm: &VirtualMachine, args: FuncArgs, callee: Callee) -> PyResult {
183 let (zelf, $($n,)*) = args.bind_for::<(PyRef<S>, $($T,)*)>(vm, callee)?;
184
185 (self)(&zelf, $($n,)*).to_pyresult(vm)
186 }
187 }
188
189 impl<F, S, $($T,)* R> PyNativeFnInternal<(RefParam<S>, $(OwnedParam<$T>,)*), R, ()> for F
190 where
191 F: Fn(&S, $($T,)*) -> R + PyThreadingConstraint + 'static,
192 S: PyPayload,
193 $($T: FromArgs,)*
194 R: ToPyResult,
195 {
196 fn call_(&self, vm: &VirtualMachine, args: FuncArgs, callee: Callee) -> PyResult {
197 let (zelf, $($n,)*) = args.bind_for::<(PyRef<S>, $($T,)*)>(vm, callee)?;
198
199 (self)(&zelf, $($n,)*).to_pyresult(vm)
200 }
201 }
202 };
203}
204
205into_py_native_fn_tuple!();
206into_py_native_fn_tuple!((v1, T1));
207into_py_native_fn_tuple!((v1, T1), (v2, T2));
208into_py_native_fn_tuple!((v1, T1), (v2, T2), (v3, T3));
209into_py_native_fn_tuple!((v1, T1), (v2, T2), (v3, T3), (v4, T4));
210into_py_native_fn_tuple!((v1, T1), (v2, T2), (v3, T3), (v4, T4), (v5, T5));
211into_py_native_fn_tuple!((v1, T1), (v2, T2), (v3, T3), (v4, T4), (v5, T5), (v6, T6));
212into_py_native_fn_tuple!(
213 (v1, T1),
214 (v2, T2),
215 (v3, T3),
216 (v4, T4),
217 (v5, T5),
218 (v6, T6),
219 (v7, T7)
220);
221
222#[cfg(test)]
223mod tests {
224 use super::*;
225 use core::mem::size_of_val;
226
227 #[test]
228 fn into_native_fn_noalloc() {
229 fn py_func(_b: bool, _vm: &crate::VirtualMachine) -> i32 {
230 1
231 }
232 assert_eq!(size_of_val(&py_func.into_func()), 0);
233 let empty_closure = || "foo".to_owned();
234 assert_eq!(size_of_val(&empty_closure.into_func()), 0);
235 assert_eq!(size_of_val(static_func(empty_closure)), 0);
236 }
237}