diff --git a/crates/bevy_reflect/compile_fail/tests/into_function/arguments_fail.rs b/crates/bevy_reflect/compile_fail/tests/into_function/arguments_fail.rs index 27d7e89c57152..9187e874ec823 100644 --- a/crates/bevy_reflect/compile_fail/tests/into_function/arguments_fail.rs +++ b/crates/bevy_reflect/compile_fail/tests/into_function/arguments_fail.rs @@ -33,8 +33,8 @@ fn main() { let _ = pass.into_function(); let _ = too_many_arguments.into_function(); - //~^ ERROR: no method named `into_function` found + //~^ E0599 let _ = argument_not_reflect.into_function(); - //~^ ERROR: no method named `into_function` found + //~^ E0599 } diff --git a/crates/bevy_reflect/compile_fail/tests/into_function/closure_fail.rs b/crates/bevy_reflect/compile_fail/tests/into_function/closure_fail.rs new file mode 100644 index 0000000000000..15316677ca5e0 --- /dev/null +++ b/crates/bevy_reflect/compile_fail/tests/into_function/closure_fail.rs @@ -0,0 +1,19 @@ +#![allow(unused)] + +use bevy_reflect::func::IntoFunction; +use bevy_reflect::Reflect; + +fn main() { + let value = String::from("Hello, World!"); + let closure_capture_owned = move || println!("{}", value); + + let _ = closure_capture_owned.into_function(); + //~^ E0277 + + let value = String::from("Hello, World!"); + let closure_capture_reference = || println!("{}", value); + + let _ = closure_capture_reference.into_function(); + // ↑ This should be an error (E0277) but `compile_fail_utils` fails to pick it up + // when the `closure_capture_owned` test is present +} diff --git a/crates/bevy_reflect/compile_fail/tests/into_function/return_fail.rs b/crates/bevy_reflect/compile_fail/tests/into_function/return_fail.rs index a98322be91b18..d73a3406b306b 100644 --- a/crates/bevy_reflect/compile_fail/tests/into_function/return_fail.rs +++ b/crates/bevy_reflect/compile_fail/tests/into_function/return_fail.rs @@ -25,10 +25,10 @@ fn main() { let _ = pass.into_function(); let _ = return_not_reflect.into_function(); - //~^ ERROR: no method named `into_function` found + //~^ E0599 let _ = return_with_lifetime_pass.into_function(); let _ = return_with_invalid_lifetime.into_function(); - //~^ ERROR: no method named `into_function` found + //~^ E0599 } diff --git a/crates/bevy_reflect/derive/src/impls/func/into_return.rs b/crates/bevy_reflect/derive/src/impls/func/into_return.rs index 02c9fcf67ade1..db964b3cbbe12 100644 --- a/crates/bevy_reflect/derive/src/impls/func/into_return.rs +++ b/crates/bevy_reflect/derive/src/impls/func/into_return.rs @@ -14,19 +14,19 @@ pub(crate) fn impl_into_return( quote! { impl #impl_generics #bevy_reflect::func::IntoReturn for #type_path #ty_generics #where_reflect_clause { - fn into_return<'into_return>(self) -> #bevy_reflect::func::Return<'into_return> { + fn into_return<'into_return>(self) -> #bevy_reflect::func::Return<'into_return> where Self: 'into_return { #bevy_reflect::func::Return::Owned(Box::new(self)) } } - impl #impl_generics #bevy_reflect::func::IntoReturn for &'static #type_path #ty_generics #where_reflect_clause { - fn into_return<'into_return>(self) -> #bevy_reflect::func::Return<'into_return> { + impl #impl_generics #bevy_reflect::func::IntoReturn for &#type_path #ty_generics #where_reflect_clause { + fn into_return<'into_return>(self) -> #bevy_reflect::func::Return<'into_return> where Self: 'into_return { #bevy_reflect::func::Return::Ref(self) } } - impl #impl_generics #bevy_reflect::func::IntoReturn for &'static mut #type_path #ty_generics #where_reflect_clause { - fn into_return<'into_return>(self) -> #bevy_reflect::func::Return<'into_return> { + impl #impl_generics #bevy_reflect::func::IntoReturn for &mut #type_path #ty_generics #where_reflect_clause { + fn into_return<'into_return>(self) -> #bevy_reflect::func::Return<'into_return> where Self: 'into_return { #bevy_reflect::func::Return::Mut(self) } } diff --git a/crates/bevy_reflect/src/func/args/arg.rs b/crates/bevy_reflect/src/func/args/arg.rs index a79ab592aab09..bc523af2851a2 100644 --- a/crates/bevy_reflect/src/func/args/arg.rs +++ b/crates/bevy_reflect/src/func/args/arg.rs @@ -1,9 +1,10 @@ use crate::func::args::{ArgError, ArgInfo, Ownership}; use crate::Reflect; -/// Represents an argument that can be passed to a [`DynamicFunction`]. +/// Represents an argument that can be passed to a [`DynamicFunction`] or [`DynamicClosure`]. /// /// [`DynamicFunction`]: crate::func::DynamicFunction +/// [`DynamicClosure`]: crate::func::DynamicClosure #[derive(Debug)] pub enum Arg<'a> { Owned(Box), diff --git a/crates/bevy_reflect/src/func/args/info.rs b/crates/bevy_reflect/src/func/args/info.rs index baf85e0207918..34f10cda533a0 100644 --- a/crates/bevy_reflect/src/func/args/info.rs +++ b/crates/bevy_reflect/src/func/args/info.rs @@ -3,10 +3,11 @@ use alloc::borrow::Cow; use crate::func::args::{GetOwnership, Ownership}; use crate::TypePath; -/// Type information for an [`Arg`] used in a [`DynamicFunction`]. +/// Type information for an [`Arg`] used in a [`DynamicFunction`] or [`DynamicClosure`]. /// -/// [`Arg`]: crate::func::Arg -/// [`DynamicFunction`]: crate::func::DynamicFunction +/// [`Arg`]: crate::func::args::Arg +/// [`DynamicFunction`]: crate::func::function::DynamicFunction +/// [`DynamicClosure`]: crate::func::closures::DynamicClosure #[derive(Debug, Clone)] pub struct ArgInfo { /// The index of the argument within its function. @@ -54,10 +55,14 @@ impl ArgInfo { /// This is because the name needs to be manually set using [`Self::with_name`] /// since the name can't be inferred from the function type alone. /// - /// For [`DynamicFunctions`] created using [`IntoFunction`], the name will always be `None`. + /// For [`DynamicFunctions`] created using [`IntoFunction`] + /// or [`DynamicClosures`] created using [`IntoClosure`], + /// the name will always be `None`. /// /// [`DynamicFunctions`]: crate::func::DynamicFunction /// [`IntoFunction`]: crate::func::IntoFunction + /// [`DynamicClosures`]: crate::func::DynamicClosure + /// [`IntoClosure`]: crate::func::IntoClosure pub fn name(&self) -> Option<&str> { self.name.as_deref() } diff --git a/crates/bevy_reflect/src/func/args/list.rs b/crates/bevy_reflect/src/func/args/list.rs index 984662f3f5c17..319d6f88537eb 100644 --- a/crates/bevy_reflect/src/func/args/list.rs +++ b/crates/bevy_reflect/src/func/args/list.rs @@ -1,7 +1,7 @@ use crate::func::args::Arg; use crate::Reflect; -/// A list of arguments that can be passed to a [`DynamicFunction`]. +/// A list of arguments that can be passed to a [`DynamicFunction`] or [`DynamicClosure`]. /// /// # Example /// @@ -24,6 +24,7 @@ use crate::Reflect; /// ``` /// /// [`DynamicFunction`]: crate::func::DynamicFunction +/// [`DynamicClosure`]: crate::func::DynamicClosure #[derive(Default, Debug)] pub struct ArgList<'a>(Vec>); diff --git a/crates/bevy_reflect/src/func/args/mod.rs b/crates/bevy_reflect/src/func/args/mod.rs index adcbc0ec641d0..57237b70e8207 100644 --- a/crates/bevy_reflect/src/func/args/mod.rs +++ b/crates/bevy_reflect/src/func/args/mod.rs @@ -1,6 +1,7 @@ -//! Argument types and utilities for working with [`DynamicFunctions`]. +//! Argument types and utilities for working with [`DynamicFunctions`] and [`DynamicClosures`]. //! //! [`DynamicFunctions`]: crate::func::DynamicFunction +//! [`DynamicClosures`]: crate::func::DynamicClosure pub use arg::*; pub use error::*; diff --git a/crates/bevy_reflect/src/func/closures/dynamic_closure.rs b/crates/bevy_reflect/src/func/closures/dynamic_closure.rs new file mode 100644 index 0000000000000..8fbdbbfd2b62f --- /dev/null +++ b/crates/bevy_reflect/src/func/closures/dynamic_closure.rs @@ -0,0 +1,180 @@ +use alloc::borrow::Cow; +use core::fmt::{Debug, Formatter}; + +use crate::func::args::{ArgInfo, ArgList}; +use crate::func::info::FunctionInfo; +use crate::func::{FunctionResult, IntoClosure, ReturnInfo}; + +/// A dynamic representation of a Rust closure. +/// +/// This type can be used to represent any Rust closure that captures its environment immutably. +/// For closures that need to capture their environment mutably, +/// see [`DynamicClosureMut`]. +/// +/// This type can be seen as a superset of [`DynamicFunction`]. +/// +/// See the [module-level documentation] for more information. +/// +/// You will generally not need to construct this manually. +/// Instead, many functions and closures can be automatically converted using the [`IntoClosure`] trait. +/// +/// # Example +/// +/// Most of the time, a [`DynamicClosure`] can be created using the [`IntoClosure`] trait: +/// +/// ``` +/// # use bevy_reflect::func::{ArgList, DynamicClosure, FunctionInfo, IntoClosure}; +/// # +/// let punct = String::from("!!!"); +/// +/// let punctuate = |text: &String| -> String { +/// format!("{}{}", text, punct) +/// }; +/// +/// // Convert the closure into a dynamic closure using `IntoClosure::into_closure` +/// let mut func: DynamicClosure = punctuate.into_closure(); +/// +/// // Dynamically call the closure: +/// let text = String::from("Hello, world"); +/// let args = ArgList::default().push_ref(&text); +/// let value = func.call(args).unwrap().unwrap_owned(); +/// +/// // Check the result: +/// assert_eq!(value.take::().unwrap(), "Hello, world!!!"); +/// ``` +/// +/// [`DynamicClosureMut`]: crate::func::closures::DynamicClosureMut +/// [`DynamicFunction`]: crate::func::DynamicFunction +pub struct DynamicClosure<'env> { + info: FunctionInfo, + func: Box Fn(ArgList<'a>, &FunctionInfo) -> FunctionResult<'a> + 'env>, +} + +impl<'env> DynamicClosure<'env> { + /// Create a new [`DynamicClosure`]. + /// + /// The given function can be used to call out to a regular function, closure, or method. + /// + /// It's important that the closure signature matches the provided [`FunctionInfo`]. + /// This info is used to validate the arguments and return value. + pub fn new Fn(ArgList<'a>, &FunctionInfo) -> FunctionResult<'a> + 'env>( + func: F, + info: FunctionInfo, + ) -> Self { + Self { + info, + func: Box::new(func), + } + } + + /// Set the name of the closure. + /// + /// For [`DynamicClosures`] created using [`IntoClosure`], + /// the default name will always be the full path to the closure as returned by [`std::any::type_name`]. + /// + /// This default name generally does not contain the actual name of the closure, only its module path. + /// It is therefore recommended to set the name manually using this method. + /// + /// [`DynamicClosures`]: DynamicClosure + pub fn with_name(mut self, name: impl Into>) -> Self { + self.info = self.info.with_name(name); + self + } + + /// Set the arguments of the closure. + /// + /// It is very important that the arguments match the intended closure signature, + /// as this is used to validate arguments passed to the closure. + pub fn with_args(mut self, args: Vec) -> Self { + self.info = self.info.with_args(args); + self + } + + /// Set the return information of the closure. + pub fn with_return_info(mut self, return_info: ReturnInfo) -> Self { + self.info = self.info.with_return_info(return_info); + self + } + + /// Call the closure with the given arguments. + /// + /// # Example + /// + /// ``` + /// # use bevy_reflect::func::{IntoClosure, ArgList}; + /// let c = 23; + /// let add = |a: i32, b: i32| -> i32 { + /// a + b + c + /// }; + /// + /// let mut func = add.into_closure().with_name("add"); + /// let args = ArgList::new().push_owned(25_i32).push_owned(75_i32); + /// let result = func.call(args).unwrap().unwrap_owned(); + /// assert_eq!(result.take::().unwrap(), 123); + /// ``` + pub fn call<'a>(&self, args: ArgList<'a>) -> FunctionResult<'a> { + (self.func)(args, &self.info) + } + + /// Returns the closure info. + pub fn info(&self) -> &FunctionInfo { + &self.info + } +} + +/// Outputs the closure's signature. +/// +/// This takes the format: `DynamicClosure(fn {name}({arg1}: {type1}, {arg2}: {type2}, ...) -> {return_type})`. +/// +/// Names for arguments and the closure itself are optional and will default to `_` if not provided. +impl<'env> Debug for DynamicClosure<'env> { + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + let name = self.info.name().unwrap_or("_"); + write!(f, "DynamicClosure(fn {name}(")?; + + for (index, arg) in self.info.args().iter().enumerate() { + let name = arg.name().unwrap_or("_"); + let ty = arg.type_path(); + write!(f, "{name}: {ty}")?; + + if index + 1 < self.info.args().len() { + write!(f, ", ")?; + } + } + + let ret = self.info.return_info().type_path(); + write!(f, ") -> {ret})") + } +} + +impl<'env> IntoClosure<'env, ()> for DynamicClosure<'env> { + #[inline] + fn into_closure(self) -> DynamicClosure<'env> { + self + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn should_overwrite_closure_name() { + let c = 23; + let func = (|a: i32, b: i32| a + b + c) + .into_closure() + .with_name("my_closure"); + assert_eq!(func.info().name(), Some("my_closure")); + } + + #[test] + fn should_convert_dynamic_closure_with_into_closure() { + fn make_closure<'env, F: IntoClosure<'env, M>, M>(f: F) -> DynamicClosure<'env> { + f.into_closure() + } + + let c = 23; + let closure: DynamicClosure = make_closure(|a: i32, b: i32| a + b + c); + let _: DynamicClosure = make_closure(closure); + } +} diff --git a/crates/bevy_reflect/src/func/closures/dynamic_closure_mut.rs b/crates/bevy_reflect/src/func/closures/dynamic_closure_mut.rs new file mode 100644 index 0000000000000..eb63463d9a87d --- /dev/null +++ b/crates/bevy_reflect/src/func/closures/dynamic_closure_mut.rs @@ -0,0 +1,222 @@ +use alloc::borrow::Cow; +use core::fmt::{Debug, Formatter}; + +use crate::func::args::{ArgInfo, ArgList}; +use crate::func::info::FunctionInfo; +use crate::func::{FunctionResult, IntoClosureMut, ReturnInfo}; + +/// A dynamic representation of a Rust closure. +/// +/// This type can be used to represent any Rust closure that captures its environment mutably. +/// For closures that only need to capture their environment immutably, +/// consider using [`DynamicClosure`]. +/// +/// This type can be seen as a superset of [`DynamicClosure`]. +/// +/// See the [module-level documentation] for more information. +/// +/// You will generally not need to construct this manually. +/// Instead, many functions and closures can be automatically converted using the [`IntoClosureMut`] trait. +/// +/// # Example +/// +/// Most of the time, a [`DynamicClosureMut`] can be created using the [`IntoClosureMut`] trait: +/// +/// ``` +/// # use bevy_reflect::func::{ArgList, DynamicClosureMut, FunctionInfo, IntoClosureMut}; +/// # +/// let mut list: Vec = vec![1, 2, 3]; +/// +/// // `replace` is a closure that captures a mutable reference to `list` +/// let mut replace = |index: usize, value: i32| -> i32 { +/// let old_value = list[index]; +/// list[index] = value; +/// old_value +/// }; +/// +/// // Convert the closure into a dynamic closure using `IntoClosureMut::into_closure_mut` +/// let mut func: DynamicClosureMut = replace.into_closure_mut(); +/// +/// // Dynamically call the closure: +/// let args = ArgList::default().push_owned(1_usize).push_owned(-2_i32); +/// let value = func.call(args).unwrap().unwrap_owned(); +/// +/// // Check the result: +/// assert_eq!(value.take::().unwrap(), 2); +/// +/// // Note that `func` still has a reference to `list`, +/// // so we need to drop it before we can access `list` again. +/// // Alternatively, we could have called the `func` using +/// // `DynamicClosureMut::call_once` to immediately consume the closure. +/// drop(func); +/// assert_eq!(list, vec![1, -2, 3]); +/// ``` +/// +/// [`DynamicClosure`]: crate::func::closures::DynamicClosure +/// [`DynamicFunction`]: crate::func::DynamicFunction +pub struct DynamicClosureMut<'env> { + info: FunctionInfo, + func: Box FnMut(ArgList<'a>, &FunctionInfo) -> FunctionResult<'a> + 'env>, +} + +impl<'env> DynamicClosureMut<'env> { + /// Create a new [`DynamicClosureMut`]. + /// + /// The given function can be used to call out to a regular function, closure, or method. + /// + /// It's important that the closure signature matches the provided [`FunctionInfo`]. + /// This info is used to validate the arguments and return value. + pub fn new FnMut(ArgList<'a>, &FunctionInfo) -> FunctionResult<'a> + 'env>( + func: F, + info: FunctionInfo, + ) -> Self { + Self { + info, + func: Box::new(func), + } + } + + /// Set the name of the closure. + /// + /// For [`DynamicClosureMuts`] created using [`IntoClosureMut`], + /// the default name will always be the full path to the closure as returned by [`std::any::type_name`]. + /// + /// This default name generally does not contain the actual name of the closure, only its module path. + /// It is therefore recommended to set the name manually using this method. + /// + /// [`DynamicClosureMuts`]: DynamicClosureMut + pub fn with_name(mut self, name: impl Into>) -> Self { + self.info = self.info.with_name(name); + self + } + + /// Set the arguments of the closure. + /// + /// It is very important that the arguments match the intended closure signature, + /// as this is used to validate arguments passed to the closure. + pub fn with_args(mut self, args: Vec) -> Self { + self.info = self.info.with_args(args); + self + } + + /// Set the return information of the closure. + pub fn with_return_info(mut self, return_info: ReturnInfo) -> Self { + self.info = self.info.with_return_info(return_info); + self + } + + /// Call the closure with the given arguments. + /// + /// Variables that are captured mutably by this closure + /// won't be usable until this closure is dropped. + /// Consider using [`call_once`] if you want to consume the closure + /// immediately after calling it. + /// + /// # Example + /// + /// ``` + /// # use bevy_reflect::func::{IntoClosureMut, ArgList}; + /// let mut total = 0; + /// let add = |a: i32, b: i32| -> i32 { + /// total = a + b; + /// total + /// }; + /// + /// let mut func = add.into_closure_mut().with_name("add"); + /// let args = ArgList::new().push_owned(25_i32).push_owned(75_i32); + /// let result = func.call(args).unwrap().unwrap_owned(); + /// assert_eq!(result.take::().unwrap(), 100); + /// ``` + /// + /// [`call_once`]: DynamicClosureMut::call_once + pub fn call<'a>(&mut self, args: ArgList<'a>) -> FunctionResult<'a> { + (self.func)(args, &self.info) + } + + /// Call the closure with the given arguments and consume the closure. + /// + /// This is useful for closures that capture their environment mutably + /// because otherwise any captured variables would still be borrowed by this closure. + /// + /// # Example + /// + /// ``` + /// # use bevy_reflect::func::{IntoClosureMut, ArgList}; + /// let mut count = 0; + /// let increment = |amount: i32| count += amount; + /// + /// let increment_function = increment.into_closure_mut(); + /// let args = ArgList::new().push_owned(5_i32); + /// + /// // We need to drop `increment_function` here so that we + /// // can regain access to `count`. + /// // `call_once` does this automatically for us. + /// increment_function.call_once(args).unwrap(); + /// assert_eq!(count, 5); + /// ``` + pub fn call_once(mut self, args: ArgList) -> FunctionResult { + (self.func)(args, &self.info) + } + + /// Returns the closure info. + pub fn info(&self) -> &FunctionInfo { + &self.info + } +} + +/// Outputs the closure's signature. +/// +/// This takes the format: `DynamicClosureMut(fn {name}({arg1}: {type1}, {arg2}: {type2}, ...) -> {return_type})`. +/// +/// Names for arguments and the closure itself are optional and will default to `_` if not provided. +impl<'env> Debug for DynamicClosureMut<'env> { + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + let name = self.info.name().unwrap_or("_"); + write!(f, "DynamicClosureMut(fn {name}(")?; + + for (index, arg) in self.info.args().iter().enumerate() { + let name = arg.name().unwrap_or("_"); + let ty = arg.type_path(); + write!(f, "{name}: {ty}")?; + + if index + 1 < self.info.args().len() { + write!(f, ", ")?; + } + } + + let ret = self.info.return_info().type_path(); + write!(f, ") -> {ret})") + } +} + +impl<'env> IntoClosureMut<'env, ()> for DynamicClosureMut<'env> { + #[inline] + fn into_closure_mut(self) -> DynamicClosureMut<'env> { + self + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn should_overwrite_closure_name() { + let mut total = 0; + let func = (|a: i32, b: i32| total = a + b) + .into_closure_mut() + .with_name("my_closure"); + assert_eq!(func.info().name(), Some("my_closure")); + } + + #[test] + fn should_convert_dynamic_closure_mut_with_into_closure() { + fn make_closure<'env, F: IntoClosureMut<'env, M>, M>(f: F) -> DynamicClosureMut<'env> { + f.into_closure_mut() + } + + let mut total = 0; + let closure: DynamicClosureMut = make_closure(|a: i32, b: i32| total = a + b); + let _: DynamicClosureMut = make_closure(closure); + } +} diff --git a/crates/bevy_reflect/src/func/closures/into_closure.rs b/crates/bevy_reflect/src/func/closures/into_closure.rs new file mode 100644 index 0000000000000..3d191a1e1151f --- /dev/null +++ b/crates/bevy_reflect/src/func/closures/into_closure.rs @@ -0,0 +1,66 @@ +use crate::func::{DynamicClosure, ReflectFn, TypedFunction}; + +/// A trait for types that can be converted into a [`DynamicClosure`]. +/// +/// This trait is automatically implemented for any type that implements +/// [`ReflectFn`] and [`TypedFunction`]. +/// +/// This trait can be seen as a supertrait of [`IntoFunction`]. +/// +/// See the [module-level documentation] for more information. +/// +/// [`IntoFunction`]: crate::func::IntoFunction +/// [module-level documentation]: crate::func +pub trait IntoClosure<'env, Marker> { + /// Converts [`Self`] into a [`DynamicClosure`]. + fn into_closure(self) -> DynamicClosure<'env>; +} + +impl<'env, F, Marker1, Marker2> IntoClosure<'env, (Marker1, Marker2)> for F +where + F: ReflectFn<'env, Marker1> + TypedFunction + 'env, +{ + fn into_closure(self) -> DynamicClosure<'env> { + DynamicClosure::new( + move |args, info| self.reflect_call(args, info), + Self::function_info(), + ) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::func::ArgList; + + #[test] + fn should_create_dynamic_closure_from_closure() { + let c = 23; + let func = (|a: i32, b: i32| a + b + c).into_closure(); + let args = ArgList::new().push_owned(25_i32).push_owned(75_i32); + let result = func.call(args).unwrap().unwrap_owned(); + assert_eq!(result.downcast_ref::(), Some(&123)); + } + + #[test] + fn should_create_dynamic_closure_from_function() { + fn add(a: i32, b: i32) -> i32 { + a + b + } + + let func = add.into_closure(); + let args = ArgList::new().push_owned(25_i32).push_owned(75_i32); + let result = func.call(args).unwrap().unwrap_owned(); + assert_eq!(result.downcast_ref::(), Some(&100)); + } + + #[test] + fn should_default_with_closure_type_name() { + let c = 23; + let func = (|a: i32, b: i32| a + b + c).into_closure(); + assert_eq!( + func.info().name(), + Some("bevy_reflect::func::closures::into_closure::tests::should_default_with_closure_type_name::{{closure}}") + ); + } +} diff --git a/crates/bevy_reflect/src/func/closures/into_closure_mut.rs b/crates/bevy_reflect/src/func/closures/into_closure_mut.rs new file mode 100644 index 0000000000000..a3daff6caadca --- /dev/null +++ b/crates/bevy_reflect/src/func/closures/into_closure_mut.rs @@ -0,0 +1,76 @@ +use crate::func::{DynamicClosureMut, ReflectFnMut, TypedFunction}; + +/// A trait for types that can be converted into a [`DynamicClosureMut`]. +/// +/// This trait is automatically implemented for any type that implements +/// [`ReflectFnMut`] and [`TypedFunction`]. +/// +/// This trait can be seen as a supertrait of [`IntoClosure`]. +/// +/// See the [module-level documentation] for more information. +/// +/// [`ReflectFn`]: crate::func::ReflectFn +/// [`IntoClosure`]: crate::func::closures::IntoClosure +/// [module-level documentation]: crate::func +pub trait IntoClosureMut<'env, Marker> { + /// Converts [`Self`] into a [`DynamicClosureMut`]. + fn into_closure_mut(self) -> DynamicClosureMut<'env>; +} + +impl<'env, F, Marker1, Marker2> IntoClosureMut<'env, (Marker1, Marker2)> for F +where + F: ReflectFnMut<'env, Marker1> + TypedFunction + 'env, +{ + fn into_closure_mut(mut self) -> DynamicClosureMut<'env> { + DynamicClosureMut::new( + move |args, info| self.reflect_call_mut(args, info), + Self::function_info(), + ) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::func::{ArgList, IntoClosure}; + + #[test] + fn should_create_dynamic_closure_mut_from_closure() { + let c = 23; + let func = (|a: i32, b: i32| a + b + c).into_closure(); + let args = ArgList::new().push_owned(25_i32).push_owned(75_i32); + let result = func.call(args).unwrap().unwrap_owned(); + assert_eq!(result.downcast_ref::(), Some(&123)); + } + + #[test] + fn should_create_dynamic_closure_mut_from_closure_with_mutable_capture() { + let mut total = 0; + let func = (|a: i32, b: i32| total = a + b).into_closure_mut(); + let args = ArgList::new().push_owned(25_i32).push_owned(75_i32); + func.call_once(args).unwrap(); + assert_eq!(total, 100); + } + + #[test] + fn should_create_dynamic_closure_mut_from_function() { + fn add(a: i32, b: i32) -> i32 { + a + b + } + + let mut func = add.into_closure_mut(); + let args = ArgList::new().push_owned(25_i32).push_owned(75_i32); + let result = func.call(args).unwrap().unwrap_owned(); + assert_eq!(result.downcast_ref::(), Some(&100)); + } + + #[test] + fn should_default_with_closure_type_name() { + let mut total = 0; + let func = (|a: i32, b: i32| total = a + b).into_closure_mut(); + assert_eq!( + func.info().name(), + Some("bevy_reflect::func::closures::into_closure_mut::tests::should_default_with_closure_type_name::{{closure}}") + ); + } +} diff --git a/crates/bevy_reflect/src/func/closures/mod.rs b/crates/bevy_reflect/src/func/closures/mod.rs new file mode 100644 index 0000000000000..cdba3bab061c8 --- /dev/null +++ b/crates/bevy_reflect/src/func/closures/mod.rs @@ -0,0 +1,9 @@ +pub use dynamic_closure::*; +pub use dynamic_closure_mut::*; +pub use into_closure::*; +pub use into_closure_mut::*; + +mod dynamic_closure; +mod dynamic_closure_mut; +mod into_closure; +mod into_closure_mut; diff --git a/crates/bevy_reflect/src/func/error.rs b/crates/bevy_reflect/src/func/error.rs index 65290b66d4b59..81d9f535d4deb 100644 --- a/crates/bevy_reflect/src/func/error.rs +++ b/crates/bevy_reflect/src/func/error.rs @@ -1,9 +1,11 @@ use crate::func::args::ArgError; +use crate::func::Return; use thiserror::Error; -/// An error that occurs when calling a [`DynamicFunction`]. +/// An error that occurs when calling a [`DynamicFunction`] or [`DynamicClosure`]. /// /// [`DynamicFunction`]: crate::func::DynamicFunction +/// [`DynamicClosure`]: crate::func::DynamicClosure #[derive(Debug, Error, PartialEq)] pub enum FunctionError { /// An error occurred while converting an argument. @@ -13,3 +15,12 @@ pub enum FunctionError { #[error("expected {expected} arguments but received {received}")] InvalidArgCount { expected: usize, received: usize }, } + +/// The result of calling a dynamic [`DynamicFunction`] or [`DynamicClosure`]. +/// +/// Returns `Ok(value)` if the function was called successfully, +/// where `value` is the [`Return`] value of the function. +/// +/// [`DynamicFunction`]: crate::func::DynamicFunction +/// [`DynamicClosure`]: crate::func::DynamicClosure +pub type FunctionResult<'a> = Result, FunctionError>; diff --git a/crates/bevy_reflect/src/func/function.rs b/crates/bevy_reflect/src/func/function.rs index 6ac4b1bbf8d85..a84e5dca1f895 100644 --- a/crates/bevy_reflect/src/func/function.rs +++ b/crates/bevy_reflect/src/func/function.rs @@ -1,21 +1,23 @@ -use crate::func::args::{ArgInfo, ArgList}; -use crate::func::error::FunctionError; -use crate::func::info::FunctionInfo; -use crate::func::return_type::Return; -use crate::func::{IntoFunction, ReturnInfo}; use alloc::borrow::Cow; use core::fmt::{Debug, Formatter}; -use std::ops::DerefMut; +use std::sync::Arc; -/// The result of calling a dynamic [`DynamicFunction`]. -/// -/// Returns `Ok(value)` if the function was called successfully, -/// where `value` is the [`Return`] value of the function. -pub type FunctionResult<'a> = Result, FunctionError>; +use crate::func::args::{ArgInfo, ArgList}; +use crate::func::info::FunctionInfo; +use crate::func::{FunctionResult, IntoFunction, ReturnInfo}; /// A dynamic representation of a Rust function. /// -/// Internally this stores a function pointer and associated info. +/// For our purposes, a "function" is just a callable that may not reference its environment. +/// +/// This includes: +/// - Functions and methods defined with the `fn` keyword +/// - Closures that do not capture their environment +/// - Closures that take ownership of captured variables +/// +/// To handle closures that capture references to their environment, see [`DynamicClosure`]. +/// +/// See the [module-level documentation] for more information. /// /// You will generally not need to construct this manually. /// Instead, many functions and closures can be automatically converted using the [`IntoFunction`] trait. @@ -90,25 +92,28 @@ pub type FunctionResult<'a> = Result, FunctionError>; /// // Check the result: /// assert_eq!(list, vec!["Hello, World!!!"]); /// ``` -pub struct DynamicFunction<'env> { +/// +/// [`DynamicClosure`]: crate::func::DynamicClosure +/// [module-level documentation]: crate::func +pub struct DynamicFunction { info: FunctionInfo, - func: Box FnMut(ArgList<'a>, &FunctionInfo) -> FunctionResult<'a> + 'env>, + func: Arc Fn(ArgList<'a>, &FunctionInfo) -> FunctionResult<'a> + 'static>, } -impl<'env> DynamicFunction<'env> { +impl DynamicFunction { /// Create a new dynamic [`DynamicFunction`]. /// /// The given function can be used to call out to a regular function, closure, or method. /// /// It's important that the function signature matches the provided [`FunctionInfo`]. /// This info is used to validate the arguments and return value. - pub fn new FnMut(ArgList<'a>, &FunctionInfo) -> FunctionResult<'a> + 'env>( + pub fn new Fn(ArgList<'a>, &FunctionInfo) -> FunctionResult<'a> + 'static>( func: F, info: FunctionInfo, ) -> Self { Self { info, - func: Box::new(func), + func: Arc::new(func), } } @@ -144,41 +149,17 @@ impl<'env> DynamicFunction<'env> { /// /// ``` /// # use bevy_reflect::func::{IntoFunction, ArgList}; - /// fn add(left: i32, right: i32) -> i32 { - /// left + right + /// fn add(a: i32, b: i32) -> i32 { + /// a + b /// } /// - /// let mut func = add.into_function(); + /// let func = add.into_function(); /// let args = ArgList::new().push_owned(25_i32).push_owned(75_i32); /// let result = func.call(args).unwrap().unwrap_owned(); /// assert_eq!(result.take::().unwrap(), 100); /// ``` - pub fn call<'a>(&mut self, args: ArgList<'a>) -> FunctionResult<'a> { - (self.func.deref_mut())(args, &self.info) - } - - /// Call the function with the given arguments and consume the function. - /// - /// This is useful for closures that capture their environment because otherwise - /// any captured variables would still be borrowed by this function. - /// - /// # Example - /// - /// ``` - /// # use bevy_reflect::func::{IntoFunction, ArgList}; - /// let mut count = 0; - /// let increment = |amount: i32| { - /// count += amount; - /// }; - /// let increment_function = increment.into_function(); - /// let args = ArgList::new().push_owned(5_i32); - /// // We need to drop `increment_function` here so that we - /// // can regain access to `count`. - /// increment_function.call_once(args).unwrap(); - /// assert_eq!(count, 5); - /// ``` - pub fn call_once(mut self, args: ArgList) -> FunctionResult { - (self.func.deref_mut())(args, &self.info) + pub fn call<'a>(&self, args: ArgList<'a>) -> FunctionResult<'a> { + (self.func)(args, &self.info) } /// Returns the function info. @@ -192,7 +173,7 @@ impl<'env> DynamicFunction<'env> { /// This takes the format: `DynamicFunction(fn {name}({arg1}: {type1}, {arg2}: {type2}, ...) -> {return_type})`. /// /// Names for arguments and the function itself are optional and will default to `_` if not provided. -impl<'env> Debug for DynamicFunction<'env> { +impl Debug for DynamicFunction { fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { let name = self.info.name().unwrap_or("_"); write!(f, "DynamicFunction(fn {name}(")?; @@ -212,9 +193,41 @@ impl<'env> Debug for DynamicFunction<'env> { } } -impl<'env> IntoFunction<'env, ()> for DynamicFunction<'env> { +impl Clone for DynamicFunction { + fn clone(&self) -> Self { + Self { + info: self.info.clone(), + func: Arc::clone(&self.func), + } + } +} + +impl IntoFunction<()> for DynamicFunction { #[inline] - fn into_function(self) -> DynamicFunction<'env> { + fn into_function(self) -> DynamicFunction { self } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn should_overwrite_function_name() { + fn foo() {} + + let func = foo.into_function().with_name("my_function"); + assert_eq!(func.info().name(), Some("my_function")); + } + + #[test] + fn should_convert_dynamic_function_with_into_function() { + fn make_function, M>(f: F) -> DynamicFunction { + f.into_function() + } + + let function: DynamicFunction = make_function(|| {}); + let _: DynamicFunction = make_function(function); + } +} diff --git a/crates/bevy_reflect/src/func/info.rs b/crates/bevy_reflect/src/func/info.rs index 49c0789c58551..6793b654a49cc 100644 --- a/crates/bevy_reflect/src/func/info.rs +++ b/crates/bevy_reflect/src/func/info.rs @@ -1,10 +1,17 @@ +use alloc::borrow::Cow; + +use bevy_utils::all_tuples; + use crate::func::args::{ArgInfo, GetOwnership, Ownership}; use crate::TypePath; -use alloc::borrow::Cow; -/// Type information for a [`DynamicFunction`]. +/// Type information for a [`DynamicFunction`] or [`DynamicClosure`]. +/// +/// This information can be retrieved from certain functions and closures +/// using the [`TypedFunction`] trait. /// /// [`DynamicFunction`]: crate::func::DynamicFunction +/// [`DynamicClosure`]: crate::func::DynamicClosure #[derive(Debug, Clone)] pub struct FunctionInfo { name: Option>, @@ -24,6 +31,14 @@ impl FunctionInfo { } } + /// Create a new [`FunctionInfo`] from the given function or closure. + pub fn from(function: &F) -> Self + where + F: TypedFunction, + { + function.get_function_info() + } + /// Set the name of the function. /// /// Reflected functions are not required to have a name, @@ -52,11 +67,13 @@ impl FunctionInfo { /// The name of the function, if it was given one. /// - /// For [`DynamicFunctions`] created using [`IntoFunction`], + /// For [`DynamicFunctions`] created using [`IntoFunction`] or [`DynamicClosures`] created using [`IntoClosure`], /// the name will always be the full path to the function as returned by [`std::any::type_name`]. /// /// [`DynamicFunctions`]: crate::func::DynamicFunction /// [`IntoFunction`]: crate::func::IntoFunction + /// [`DynamicClosures`]: crate::func::DynamicClosure + /// [`IntoClosure`]: crate::func::IntoClosure pub fn name(&self) -> Option<&str> { self.name.as_deref() } @@ -83,9 +100,10 @@ impl Default for FunctionInfo { } } -/// Information about the return type of a [`DynamicFunction`]. +/// Information about the return type of a [`DynamicFunction`] or [`DynamicClosure`]. /// /// [`DynamicFunction`]: crate::func::DynamicFunction +/// [`DynamicClosure`]: crate::func::DynamicClosure #[derive(Debug, Clone)] pub struct ReturnInfo { type_path: &'static str, @@ -111,3 +129,175 @@ impl ReturnInfo { self.ownership } } + +/// A static accessor to compile-time type information for functions. +/// +/// This is the equivalent of [`Typed`] for functions. +/// +/// # Blanket Implementation +/// +/// This trait has a blanket implementation that covers: +/// - Functions and methods defined with the `fn` keyword +/// - Closures that do not capture their environment +/// - Closures that capture immutable references to their environment +/// - Closures that capture mutable references to their environment +/// - Closures that take ownership of captured variables +/// +/// For each of the above cases, the function signature may only have up to 15 arguments, +/// not including an optional receiver argument (often `&self` or `&mut self`). +/// This optional receiver argument may be either a mutable or immutable reference to a type. +/// If the return type is also a reference, its lifetime will be bound to the lifetime of this receiver. +/// +/// See the [module-level documentation] for more information on valid signatures. +/// +/// Arguments and the return type are expected to implement both [`GetOwnership`] and [`TypePath`]. +/// By default, these traits are automatically implemented when using the `Reflect` [derive macro]. +/// +/// # Example +/// +/// ``` +/// # use bevy_reflect::func::{ArgList, FunctionInfo, ReflectFnMut, TypedFunction}; +/// # +/// fn print(value: String) { +/// println!("{}", value); +/// } +/// +/// let info = print.get_function_info(); +/// assert!(info.name().unwrap().ends_with("print")); +/// assert_eq!(info.arg_count(), 1); +/// assert_eq!(info.args()[0].type_path(), "alloc::string::String"); +/// assert_eq!(info.return_info().type_path(), "()"); +/// ``` +/// +/// # Trait Parameters +/// +/// This trait has a `Marker` type parameter that is used to get around issues with +/// [unconstrained type parameters] when defining impls with generic arguments or return types. +/// This `Marker` can be any type, provided it doesn't conflict with other implementations. +/// +/// [module-level documentation]: crate::func +/// [`Typed`]: crate::Typed +pub trait TypedFunction { + /// Get the [`FunctionInfo`] for this type. + fn function_info() -> FunctionInfo; + + /// Get the [`FunctionInfo`] for this type. + fn get_function_info(&self) -> FunctionInfo { + Self::function_info() + } +} + +/// Helper macro for implementing [`TypedFunction`] on Rust closures. +/// +/// This currently implements it for the following signatures (where `argX` may be any of `T`, `&T`, or `&mut T`): +/// - `FnMut(arg0, arg1, ..., argN) -> R` +/// - `FnMut(&Receiver, arg0, arg1, ..., argN) -> &R` +/// - `FnMut(&mut Receiver, arg0, arg1, ..., argN) -> &mut R` +/// - `FnMut(&mut Receiver, arg0, arg1, ..., argN) -> &R` +macro_rules! impl_typed_function { + ($(($Arg:ident, $arg:ident)),*) => { + // === (...) -> ReturnType === // + impl<$($Arg,)* ReturnType, Function> TypedFunction [ReturnType]> for Function + where + $($Arg: TypePath + GetOwnership,)* + ReturnType: TypePath + GetOwnership, + Function: FnMut($($Arg),*) -> ReturnType, + { + fn function_info() -> FunctionInfo { + FunctionInfo::new() + .with_name(std::any::type_name::()) + .with_args({ + #[allow(unused_mut)] + let mut _index = 0; + vec![ + $(ArgInfo::new::<$Arg>({ + _index += 1; + _index - 1 + }),)* + ] + }) + .with_return_info(ReturnInfo::new::()) + } + } + + // === (&self, ...) -> &ReturnType === // + impl TypedFunction &ReturnType> for Function + where + for<'a> &'a Receiver: TypePath + GetOwnership, + $($Arg: TypePath + GetOwnership,)* + for<'a> &'a ReturnType: TypePath + GetOwnership, + Function: for<'a> FnMut(&'a Receiver, $($Arg),*) -> &'a ReturnType, + { + fn function_info() -> $crate::func::FunctionInfo { + FunctionInfo::new() + .with_name(std::any::type_name::()) + .with_args({ + #[allow(unused_mut)] + let mut _index = 1; + vec![ + ArgInfo::new::<&Receiver>(0), + $($crate::func::args::ArgInfo::new::<$Arg>({ + _index += 1; + _index - 1 + }),)* + ] + }) + .with_return_info(ReturnInfo::new::<&ReturnType>()) + } + } + + // === (&mut self, ...) -> &mut ReturnType === // + impl TypedFunction &mut ReturnType> for Function + where + for<'a> &'a mut Receiver: TypePath + GetOwnership, + $($Arg: TypePath + GetOwnership,)* + for<'a> &'a mut ReturnType: TypePath + GetOwnership, + Function: for<'a> FnMut(&'a mut Receiver, $($Arg),*) -> &'a mut ReturnType, + { + fn function_info() -> FunctionInfo { + FunctionInfo::new() + .with_name(std::any::type_name::()) + .with_args({ + #[allow(unused_mut)] + let mut _index = 1; + vec![ + ArgInfo::new::<&mut Receiver>(0), + $(ArgInfo::new::<$Arg>({ + _index += 1; + _index - 1 + }),)* + ] + }) + .with_return_info(ReturnInfo::new::<&mut ReturnType>()) + } + } + + // === (&mut self, ...) -> &ReturnType === // + impl TypedFunction &ReturnType> for Function + where + for<'a> &'a mut Receiver: TypePath + GetOwnership, + $($Arg: TypePath + GetOwnership,)* + for<'a> &'a ReturnType: TypePath + GetOwnership, + Function: for<'a> FnMut(&'a mut Receiver, $($Arg),*) -> &'a ReturnType, + { + fn function_info() -> FunctionInfo { + FunctionInfo::new() + .with_name(std::any::type_name::()) + .with_args({ + #[allow(unused_mut)] + let mut _index = 1; + vec![ + ArgInfo::new::<&mut Receiver>(0), + $(ArgInfo::new::<$Arg>({ + _index += 1; + _index - 1 + }),)* + ] + }) + .with_return_info(ReturnInfo::new::<&ReturnType>()) + } + } + }; +} + +all_tuples!(impl_typed_function, 0, 15, Arg, arg); diff --git a/crates/bevy_reflect/src/func/into_function.rs b/crates/bevy_reflect/src/func/into_function.rs index 30609c948ccfd..7104eb1a72325 100644 --- a/crates/bevy_reflect/src/func/into_function.rs +++ b/crates/bevy_reflect/src/func/into_function.rs @@ -1,318 +1,183 @@ +use std::panic::{RefUnwindSafe, UnwindSafe}; + use crate::func::function::DynamicFunction; -use bevy_utils::all_tuples; +use crate::func::{ReflectFn, TypedFunction}; /// A trait for types that can be converted into a [`DynamicFunction`]. /// -/// # Blanket Implementation -/// -/// This trait has a blanket implementation that covers many functions, closures, and methods. -/// And though it works for many cases, it does have some limitations. -/// -/// ## Arguments -/// -/// Firstly, the function signature may only have up to 15 arguments -/// (or 16 if the first argument is a mutable/immutable reference). -/// This limitation is unfortunately due to the [lack of variadic generics] in Rust. -/// -/// Each argument must implement [`FromArg`], [`GetOwnership`], and [`TypePath`]. -/// -/// -/// ```compile_fail -/// # use bevy_reflect::func::IntoFunction; -/// fn too_many_args( -/// arg01: i32, -/// arg02: i32, -/// arg03: i32, -/// arg04: i32, -/// arg05: i32, -/// arg06: i32, -/// arg07: i32, -/// arg08: i32, -/// arg09: i32, -/// arg10: i32, -/// arg11: i32, -/// arg12: i32, -/// arg13: i32, -/// arg14: i32, -/// arg15: i32, -/// arg16: i32, -/// ) { -/// // ... -/// } -/// -/// // This will fail to compile: -/// too_many_args.into_function(); -/// ``` -/// -/// ## Return Type -/// -/// Secondly, the allowed return type is dependent on the first argument of the function: -/// - If the first argument is an immutable reference, -/// then the return type may be either an owned type, a static reference type, or a reference type -/// bound to the lifetime of the first argument. -/// - If the first argument is a mutable reference, -/// then the return type may be either an owned type, a static reference type, or be a mutable reference type -/// bound to the lifetime of the first argument. -/// - If the first argument is an owned type, -/// then the return type may be either an owned type or a static reference type. +/// This trait is automatically implemented for many standard Rust functions +/// that also implement [`ReflectFn`] and [`TypedFunction`]. /// -/// The return type must always implement [`GetOwnership`] and [`TypePath`]. -/// If it is either an owned type or a static reference type, -/// then it must also implement [`IntoReturn`]. -/// Otherwise, it must also implement [`Reflect`]. +/// To handle types such as closures that capture references to their environment, +/// see [`IntoClosure`] instead. /// -/// Note that both `GetOwnership`, `TypePath`, and `IntoReturn` are automatically implemented -/// when [deriving `Reflect`]. +/// See the [module-level documentation] for more information. /// -/// ``` -/// # use bevy_reflect::func::IntoFunction; -/// fn owned_return(arg: i32) -> i32 { arg * 2 } -/// fn ref_return(arg: &i32) -> &i32 { arg } -/// fn mut_return(arg: &mut i32) -> &mut i32 { arg } -/// fn static_return(arg: i32) -> &'static i32 { &123 } -/// -/// owned_return.into_function(); -/// ref_return.into_function(); -/// mut_return.into_function(); -/// static_return.into_function(); -/// ``` -/// -/// [lack of variadic generics]: https://poignardazur.github.io/2024/05/25/report-on-rustnl-variadics/ -/// [`FromArg`]: crate::func::args::FromArg -/// [`GetOwnership`]: crate::func::args::GetOwnership -/// [`TypePath`]: crate::TypePath -/// [`IntoReturn`]: crate::func::IntoReturn -/// [`Reflect`]: crate::Reflect -/// [deriving `Reflect`]: derive@crate::Reflect -pub trait IntoFunction<'env, Marker> { +/// [`IntoClosure`]: crate::func::IntoClosure +/// [module-level documentation]: crate::func +pub trait IntoFunction { /// Converts [`Self`] into a [`DynamicFunction`]. - fn into_function(self) -> DynamicFunction<'env>; + fn into_function(self) -> DynamicFunction; } -/// Helper macro that returns the number of tokens it receives. -/// -/// This is used to get the argument count. -/// -/// See [here] for details. -/// -/// [here]: https://veykril.github.io/tlborm/decl-macros/building-blocks/counting.html#bit-twiddling -macro_rules! count_tts { - () => { 0 }; - ($odd:tt $($a:tt $b:tt)*) => { (count_tts!($($a)*) << 1) | 1 }; - ($($a:tt $even:tt)*) => { count_tts!($($a)*) << 1 }; +impl IntoFunction<(Marker1, Marker2)> for F +where + F: ReflectFn<'static, Marker1> + + TypedFunction + // Ideally, we'd only implement `IntoFunction` on actual function types + // (i.e. functions that do not capture their environment at all), + // but this would only work if users first explicitly coerced their functions + // to a function pointer like `(add as fn(i32, i32) -> i32).into_function()`, + // which is certainly not the best user experience. + // So as a compromise, we'll stick to allowing any type that implements + // `ReflectFn` and `TypedFunction`, but also add the following trait bounds + // that all `fn` types implement: + + Clone + + Copy + + Send + + Sync + + Unpin + + UnwindSafe + + RefUnwindSafe + + 'static, +{ + fn into_function(self) -> DynamicFunction { + // Note that to further guarantee that `self` is a true `fn` type, + // we could add a compile time assertion that `F` is zero-sized. + // However, we don't do this because it would prevent users from + // converting function pointers into `DynamicFunction`s. + + DynamicFunction::new( + move |args, info| self.reflect_call(args, info), + Self::function_info(), + ) + } } -/// Helper macro for implementing [`IntoFunction`] on Rust functions. -/// -/// This currently implements it for the following signatures (where `argX` may be any of `T`, `&T`, or `&mut T`): -/// - `fn(arg0, arg1, ..., argN) -> R` -/// - `fn(&Receiver, arg0, arg1, ..., argN) -> &R` -/// - `fn(&mut Receiver, arg0, arg1, ..., argN) -> &mut R` -/// - `fn(&mut Receiver, arg0, arg1, ..., argN) -> &R` -macro_rules! impl_into_function { - ($(($Arg:ident, $arg:ident)),*) => { - // === Owned Return === // - impl<'env, $($Arg,)* R, F> $crate::func::IntoFunction<'env, fn($($Arg),*) -> R> for F - where - $($Arg: $crate::func::args::FromArg + $crate::func::args::GetOwnership + $crate::TypePath,)* - R: $crate::func::IntoReturn + $crate::func::args::GetOwnership + $crate::TypePath, - F: FnMut($($Arg),*) -> R + 'env, - F: for<'a> FnMut($($Arg::Item<'a>),*) -> R + 'env, - { - fn into_function(mut self) -> $crate::func::DynamicFunction<'env> { - const COUNT: usize = count_tts!($($Arg)*); - - let info = $crate::func::FunctionInfo::new() - .with_name(std::any::type_name::()) - .with_args({ - #[allow(unused_mut)] - let mut _index = 0; - vec![ - $($crate::func::args::ArgInfo::new::<$Arg>({ - _index += 1; - _index - 1 - }),)* - ] - }) - .with_return_info($crate::func::ReturnInfo::new::()); - - $crate::func::DynamicFunction::new(move |args, _info| { - if args.len() != COUNT { - return Err($crate::func::error::FunctionError::InvalidArgCount { - expected: COUNT, - received: args.len(), - }); - } - - let [$($arg,)*] = args.take().try_into().expect("invalid number of arguments"); - - #[allow(unused_mut)] - let mut _index = 0; - let ($($arg,)*) = ($($Arg::from_arg($arg, { - _index += 1; - _info.args().get(_index - 1).expect("argument index out of bounds") - })?,)*); - Ok((self)($($arg,)*).into_return()) - }, info) - } +#[cfg(test)] +mod tests { + use super::*; + use crate as bevy_reflect; + use crate::func::ArgList; + use bevy_reflect_derive::Reflect; + + #[test] + fn should_create_dynamic_function_from_function() { + fn add(a: i32, b: i32) -> i32 { + a + b } - // === Ref Receiver + Ref Return === // - impl<'env, Receiver, $($Arg,)* R, F> $crate::func::IntoFunction<'env, fn(&Receiver, $($Arg),*) -> fn(&R)> for F - where - Receiver: $crate::Reflect + $crate::TypePath, - for<'a> &'a Receiver: $crate::func::args::GetOwnership, - R: $crate::Reflect + $crate::TypePath, - for<'a> &'a R: $crate::func::args::GetOwnership, - $($Arg: $crate::func::args::FromArg + $crate::func::args::GetOwnership + $crate::TypePath,)* - F: for<'a> FnMut(&'a Receiver, $($Arg),*) -> &'a R + 'env, - F: for<'a> FnMut(&'a Receiver, $($Arg::Item<'a>),*) -> &'a R + 'env, - { - fn into_function(mut self) -> $crate::func::DynamicFunction<'env> { - const COUNT: usize = count_tts!(Receiver $($Arg)*); - - let info = $crate::func::FunctionInfo::new() - .with_name(std::any::type_name::()) - .with_args({ - #[allow(unused_mut)] - let mut _index = 1; - vec![ - $crate::func::args::ArgInfo::new::<&Receiver>(0), - $($crate::func::args::ArgInfo::new::<$Arg>({ - _index += 1; - _index - 1 - }),)* - ] - }) - .with_return_info($crate::func::ReturnInfo::new::<&R>()); - - $crate::func::DynamicFunction::new(move |args, _info| { - if args.len() != COUNT { - return Err($crate::func::error::FunctionError::InvalidArgCount { - expected: COUNT, - received: args.len(), - }); - } - - let [receiver, $($arg,)*] = args.take().try_into().expect("invalid number of arguments"); + let func = add.into_function(); + let args = ArgList::new().push_owned(25_i32).push_owned(75_i32); + let result = func.call(args).unwrap().unwrap_owned(); + assert_eq!(result.downcast_ref::(), Some(&100)); + } - let receiver = receiver.take_ref::(_info.args().get(0).expect("argument index out of bounds"))?; - - #[allow(unused_mut)] - let mut _index = 1; - let ($($arg,)*) = ($($Arg::from_arg($arg, { - _index += 1; - _info.args().get(_index - 1).expect("argument index out of bounds") - })?,)*); - Ok($crate::func::Return::Ref((self)(receiver, $($arg,)*))) - }, info) - } + #[test] + fn should_create_dynamic_function_from_function_pointer() { + fn add(a: i32, b: i32) -> i32 { + a + b } - // === Mut Receiver + Mut Return === // - impl<'env, Receiver, $($Arg,)* R, F> $crate::func::IntoFunction<'env, fn(&mut Receiver, $($Arg),*) -> fn(&mut R)> for F - where - Receiver: $crate::Reflect + $crate::TypePath, - for<'a> &'a mut Receiver: $crate::func::args::GetOwnership, - R: $crate::Reflect + $crate::TypePath, - for<'a> &'a mut R: $crate::func::args::GetOwnership, - $($Arg: $crate::func::args::FromArg + $crate::func::args::GetOwnership + $crate::TypePath,)* - F: for<'a> FnMut(&'a mut Receiver, $($Arg),*) -> &'a mut R + 'env, - F: for<'a> FnMut(&'a mut Receiver, $($Arg::Item<'a>),*) -> &'a mut R + 'env, - { - fn into_function(mut self) -> $crate::func::DynamicFunction<'env> { - const COUNT: usize = count_tts!(Receiver $($Arg)*); - - let info = $crate::func::FunctionInfo::new() - .with_name(std::any::type_name::()) - .with_args({ - #[allow(unused_mut)] - let mut _index = 1; - vec![ - $crate::func::args::ArgInfo::new::<&mut Receiver>(0), - $($crate::func::args::ArgInfo::new::<$Arg>({ - _index += 1; - _index - 1 - }),)* - ] - }) - .with_return_info($crate::func::ReturnInfo::new::<&mut R>()); - - $crate::func::DynamicFunction::new(move |args, _info| { - if args.len() != COUNT { - return Err($crate::func::error::FunctionError::InvalidArgCount { - expected: COUNT, - received: args.len(), - }); - } - - let [receiver, $($arg,)*] = args.take().try_into().expect("invalid number of arguments"); - - let receiver = receiver.take_mut::(_info.args().get(0).expect("argument index out of bounds"))?; - - #[allow(unused_mut)] - let mut _index = 1; - let ($($arg,)*) = ($($Arg::from_arg($arg, { - _index += 1; - _info.args().get(_index - 1).expect("argument index out of bounds") - })?,)*); - Ok($crate::func::Return::Mut((self)(receiver, $($arg,)*))) - }, info) + let func = (add as fn(i32, i32) -> i32).into_function(); + let args = ArgList::new().push_owned(25_i32).push_owned(75_i32); + let result = func.call(args).unwrap().unwrap_owned(); + assert_eq!(result.downcast_ref::(), Some(&100)); + } + + #[test] + fn should_create_dynamic_function_from_anonymous_function() { + let func = (|a: i32, b: i32| a + b).into_function(); + let args = ArgList::new().push_owned(25_i32).push_owned(75_i32); + let result = func.call(args).unwrap().unwrap_owned(); + assert_eq!(result.downcast_ref::(), Some(&100)); + } + + #[test] + fn should_create_dynamic_function_from_method() { + #[derive(Reflect, Debug, PartialEq)] + struct Foo(i32); + + impl Foo { + pub fn add(&self, other: &Foo) -> Foo { + Foo(self.0 + other.0) } } - // === Mut Receiver + Ref Return === // - impl<'env, Receiver, $($Arg,)* R, F> $crate::func::IntoFunction<'env, fn(&mut Receiver, $($Arg),*) -> fn(&mut R) -> &R> for F - where - Receiver: $crate::Reflect + $crate::TypePath, - for<'a> &'a mut Receiver: $crate::func::args::GetOwnership, - R: $crate::Reflect + $crate::TypePath, - for<'a> &'a mut R: $crate::func::args::GetOwnership, - $($Arg: $crate::func::args::FromArg + $crate::func::args::GetOwnership + $crate::TypePath,)* - F: for<'a> FnMut(&'a mut Receiver, $($Arg),*) -> &'a R + 'env, - F: for<'a> FnMut(&'a mut Receiver, $($Arg::Item<'a>),*) -> &'a R + 'env, - { - fn into_function(mut self) -> $crate::func::DynamicFunction<'env> { - const COUNT: usize = count_tts!(Receiver $($Arg)*); + let foo_a = Foo(25); + let foo_b = Foo(75); - let info = $crate::func::FunctionInfo::new() - .with_name(std::any::type_name::()) - .with_args({ - #[allow(unused_mut)] - let mut _index = 1; - vec![ - $crate::func::args::ArgInfo::new::<&mut Receiver>(0), - $($crate::func::args::ArgInfo::new::<$Arg>({ - _index += 1; - _index - 1 - }),)* - ] - }) - .with_return_info($crate::func::ReturnInfo::new::<&mut R>()); + let func = Foo::add.into_function(); + let args = ArgList::new().push_ref(&foo_a).push_ref(&foo_b); + let result = func.call(args).unwrap().unwrap_owned(); + assert_eq!(result.downcast_ref::(), Some(&Foo(100))); + } - $crate::func::DynamicFunction::new(move |args, _info| { - if args.len() != COUNT { - return Err($crate::func::error::FunctionError::InvalidArgCount { - expected: COUNT, - received: args.len(), - }); - } - - let [receiver, $($arg,)*] = args.take().try_into().expect("invalid number of arguments"); + #[test] + fn should_allow_zero_args() { + fn foo() -> String { + String::from("Hello, World!") + } - let receiver = receiver.take_mut::(_info.args().get(0).expect("argument index out of bounds"))?; + let func = foo.into_function(); + let args = ArgList::new(); + let result = func.call(args).unwrap().unwrap_owned(); + assert_eq!( + result.downcast_ref::(), + Some(&String::from("Hello, World!")) + ); + } + + #[test] + fn should_allow_unit_return() { + fn foo(_: i32) {} + + let func = foo.into_function(); + let args = ArgList::new().push_owned(123_i32); + let result = func.call(args).unwrap(); + assert!(result.is_unit()); + } + + #[test] + fn should_allow_reference_return() { + fn foo<'a>(value: &'a i32, _: String, _: &bool) -> &'a i32 { + value + } - #[allow(unused_mut)] - let mut _index = 1; - let ($($arg,)*) = ($($Arg::from_arg($arg, { - _index += 1; - _info.args().get(_index - 1).expect("argument index out of bounds") - })?,)*); - Ok($crate::func::Return::Ref((self)(receiver, $($arg,)*))) - }, info) - } + let value: i32 = 123; + let func = foo.into_function(); + let args = ArgList::new() + .push_ref(&value) + .push_owned(String::from("Hello, World!")) + .push_ref(&true); + let result = func.call(args).unwrap().unwrap_ref(); + assert_eq!(result.downcast_ref::(), Some(&123)); + } + + #[test] + fn should_allow_mutable_reference_return() { + fn foo<'a>(value: &'a mut i32, _: String, _: &bool) -> &'a mut i32 { + value } - }; -} -all_tuples!(impl_into_function, 0, 15, Arg, arg); + let mut value: i32 = 123; + let func = foo.into_function(); + let args = ArgList::new() + .push_mut(&mut value) + .push_owned(String::from("Hello, World!")) + .push_ref(&true); + let result = func.call(args).unwrap().unwrap_mut(); + assert_eq!(result.downcast_mut::(), Some(&mut 123)); + } + + #[test] + fn should_default_with_function_type_name() { + fn foo() {} + + let func = foo.into_function(); + assert_eq!( + func.info().name(), + Some("bevy_reflect::func::into_function::tests::should_default_with_function_type_name::foo") + ); + } +} diff --git a/crates/bevy_reflect/src/func/macros.rs b/crates/bevy_reflect/src/func/macros.rs index a6be3850e3d1e..3fb93a2230610 100644 --- a/crates/bevy_reflect/src/func/macros.rs +++ b/crates/bevy_reflect/src/func/macros.rs @@ -97,3 +97,16 @@ macro_rules! impl_function_traits { } pub(crate) use impl_function_traits; + +/// Helper macro that returns the number of tokens it receives. +/// +/// See [here] for details. +/// +/// [here]: https://veykril.github.io/tlborm/decl-macros/building-blocks/counting.html#bit-twiddling +macro_rules! count_tokens { + () => { 0 }; + ($odd:tt $($a:tt $b:tt)*) => { ($crate::func::macros::count_tokens!($($a)*) << 1) | 1 }; + ($($a:tt $even:tt)*) => { $crate::func::macros::count_tokens!($($a)*) << 1 }; +} + +pub(crate) use count_tokens; diff --git a/crates/bevy_reflect/src/func/mod.rs b/crates/bevy_reflect/src/func/mod.rs index a964cf60f7a22..0c741cf048087 100644 --- a/crates/bevy_reflect/src/func/mod.rs +++ b/crates/bevy_reflect/src/func/mod.rs @@ -1,18 +1,17 @@ //! Reflection-based dynamic functions. //! //! This module provides a way to pass around and call functions dynamically -//! using the [`DynamicFunction`] type. +//! using the [`DynamicFunction`], [`DynamicClosure`], and [`DynamicClosureMut`] types. //! -//! Many simple functions and closures can be automatically converted to [`DynamicFunction`] -//! using the [`IntoFunction`] trait. +//! Many simple functions and closures can be automatically converted to these types +//! using the [`IntoFunction`], [`IntoClosure`], and [`IntoClosureMut`] traits, respectively. //! -//! Once the [`DynamicFunction`] is created, it can be called with a set of arguments provided +//! Once this dynamic representation is created, it can be called with a set of arguments provided //! via an [`ArgList`]. //! //! This returns a [`FunctionResult`] containing the [`Return`] value, //! which can be used to extract a [`Reflect`] trait object. //! -//! //! # Example //! //! ``` @@ -34,164 +33,107 @@ //! assert_eq!(value.unwrap_owned().downcast_ref::(), Some(&100)); //! ``` //! +//! # Functions vs Closures +//! +//! In Rust, a "function" is any callable that does not capture its environment. +//! These are typically defined with the `fn` keyword, but may also use anonymous function syntax. +//! +//! ```rust +//! // This is a standard Rust function: +//! fn add(a: i32, b: i32) -> i32 { +//! a + b +//! } +//! +//! // This is an anonymous Rust function: +//! let add = |a: i32, b: i32| a + b; +//! ``` +//! +//! Rust also has the concept of "closures", which are special functions that capture their environment. +//! These are always defined with anonymous function syntax. +//! +//! ```rust +//! // A closure that captures an immutable reference to a variable +//! let c = 123; +//! let add = |a: i32, b: i32| a + b + c; +//! +//! // A closure that captures a mutable reference to a variable +//! let mut total = 0; +//! let add = |a: i32, b: i32| total += a + b; +//! +//! // A closure that takes ownership of its captured variables by moving them +//! let c = 123; +//! let add = move |a: i32, b: i32| a + b + c; +//! ``` +//! +//! Each callable may be considered a subset of the other: +//! functions are a subset of immutable closures which are a subset of mutable closures. +//! +//! This means that, in terms of traits, you could imagine that any type that implements +//! [`IntoFunction`], also implements [`IntoClosure`] and [`IntoClosureMut`]. +//! And every type that implements [`IntoClosure`] also implements [`IntoClosureMut`]. +//! +//! # Valid Signatures +//! +//! Many of the traits in this module have default blanket implementations over a specific set of function signatures. +//! +//! These signatures are: +//! - `(...) -> R` +//! - `for<'a> (&'a arg, ...) -> &'a R` +//! - `for<'a> (&'a mut arg, ...) -> &'a R` +//! - `for<'a> (&'a mut arg, ...) -> &'a mut R` +//! +//! Where `...` represents 0 to 15 arguments (inclusive) of the form `T`, `&T`, or `&mut T`. +//! The lifetime of any reference to the return type `R`, must be tied to a "receiver" argument +//! (i.e. the first argument in the signature, normally `self`). +//! +//! Each trait will also have its own requirements for what traits are required for both arguments and return types, +//! but a good rule-of-thumb is that all types should derive [`Reflect`]. +//! +//! The reason for such a small subset of valid signatures is due to limitations in Rust— +//! namely the [lack of variadic generics] and certain [coherence issues]. +//! +//! For other functions that don't conform to one of the above signatures, +//! [`DynamicFunction`] and [`DynamicClosure`] can instead be created manually. +//! //! [`Reflect`]: crate::Reflect +//! [lack of variadic generics]: https://poignardazur.github.io/2024/05/25/report-on-rustnl-variadics/ +//! [coherence issues]: https://doc.rust-lang.org/rustc/lints/listing/warn-by-default.html#coherence-leak-check +pub use args::{Arg, ArgError, ArgList}; +pub use closures::*; pub use error::*; pub use function::*; pub use info::*; pub use into_function::*; +pub use reflect_fn::*; +pub use reflect_fn_mut::*; pub use return_type::*; -pub use args::{Arg, ArgError, ArgList}; - pub mod args; +mod closures; mod error; mod function; mod info; mod into_function; pub(crate) mod macros; +mod reflect_fn; +mod reflect_fn_mut; mod return_type; #[cfg(test)] mod tests { - use super::*; - use crate as bevy_reflect; - use crate::func::args::{ArgError, ArgId, ArgList, Ownership}; - use crate::{Reflect, TypePath}; use alloc::borrow::Cow; - #[test] - fn should_create_dynamic_function() { - fn add(a: i32, b: i32) -> i32 { - a + b - } - - let mut func = add.into_function(); - let args = ArgList::new().push_owned(25_i32).push_owned(75_i32); - let result = func.call(args).unwrap().unwrap_owned(); - assert_eq!(result.downcast_ref::(), Some(&100)); - } - - #[test] - fn should_create_dynamic_closure() { - let mut func = (|a: i32, b: i32| a + b).into_function(); - let args = ArgList::new().push_owned(25_i32).push_owned(75_i32); - let result = func.call(args).unwrap().unwrap_owned(); - assert_eq!(result.downcast_ref::(), Some(&100)); - } - - #[test] - fn should_create_dynamic_method() { - #[derive(Reflect, Debug, PartialEq)] - struct Foo(i32); - - impl Foo { - pub fn add(&self, other: &Foo) -> Foo { - Foo(self.0 + other.0) - } - } - - let foo_a = Foo(25); - let foo_b = Foo(75); - - let mut func = Foo::add.into_function(); - let args = ArgList::new().push_ref(&foo_a).push_ref(&foo_b); - let result = func.call(args).unwrap().unwrap_owned(); - assert_eq!(result.downcast_ref::(), Some(&Foo(100))); - } - - #[test] - fn should_allow_zero_args() { - fn foo() -> String { - String::from("Hello, World!") - } - - let mut func = foo.into_function(); - let args = ArgList::new(); - let result = func.call(args).unwrap().unwrap_owned(); - assert_eq!( - result.downcast_ref::(), - Some(&String::from("Hello, World!")) - ); - } - - #[test] - fn should_allow_unit_return() { - fn foo(_: i32) {} - - let mut func = foo.into_function(); - let args = ArgList::new().push_owned(123_i32); - let result = func.call(args).unwrap(); - assert!(result.is_unit()); - } - - #[test] - fn should_allow_reference_return() { - fn foo<'a>(value: &'a i32, _: String, _: &bool) -> &'a i32 { - value - } - - let value: i32 = 123; - let mut func = foo.into_function(); - let args = ArgList::new() - .push_ref(&value) - .push_owned(String::from("Hello, World!")) - .push_ref(&true); - let result = func.call(args).unwrap().unwrap_ref(); - assert_eq!(result.downcast_ref::(), Some(&123)); - } - - #[test] - fn should_allow_mutable_reference_return() { - fn foo<'a>(value: &'a mut i32, _: String, _: &bool) -> &'a mut i32 { - value - } - - let mut value: i32 = 123; - let mut func = foo.into_function(); - let args = ArgList::new() - .push_mut(&mut value) - .push_owned(String::from("Hello, World!")) - .push_ref(&true); - let result = func.call(args).unwrap().unwrap_mut(); - assert_eq!(result.downcast_mut::(), Some(&mut 123)); - } - - #[test] - fn should_default_with_function_type_name() { - fn foo() {} - - let func = foo.into_function(); - assert_eq!( - func.info().name(), - Some("bevy_reflect::func::tests::should_default_with_function_type_name::foo") - ); - } - - #[test] - fn should_default_with_closure_type_name() { - let bar = |_: i32| {}; - - let func = bar.into_function(); - assert_eq!( - func.info().name(), - Some("bevy_reflect::func::tests::should_default_with_closure_type_name::{{closure}}") - ); - } - - #[test] - fn should_overwrite_function_name() { - fn foo() {} + use crate::func::args::{ArgError, ArgId, ArgList, Ownership}; + use crate::TypePath; - let func = foo.into_function().with_name("my_function"); - assert_eq!(func.info().name(), Some("my_function")); - } + use super::*; #[test] fn should_error_on_missing_args() { fn foo(_: i32) {} - let mut func = foo.into_function(); + let func = foo.into_function(); let args = ArgList::new(); let result = func.call(args); assert_eq!( @@ -207,7 +149,7 @@ mod tests { fn should_error_on_too_many_args() { fn foo() {} - let mut func = foo.into_function(); + let func = foo.into_function(); let args = ArgList::new().push_owned(123_i32); let result = func.call(args); assert_eq!( @@ -223,7 +165,7 @@ mod tests { fn should_error_on_invalid_arg_type() { fn foo(_: i32) {} - let mut func = foo.into_function(); + let func = foo.into_function(); let args = ArgList::new().push_owned(123_u32); let result = func.call(args); assert_eq!( @@ -240,7 +182,7 @@ mod tests { fn should_error_on_invalid_arg_ownership() { fn foo(_: &i32) {} - let mut func = foo.into_function(); + let func = foo.into_function(); let args = ArgList::new().push_owned(123_i32); let result = func.call(args); assert_eq!( @@ -252,14 +194,4 @@ mod tests { }) ); } - - #[test] - fn should_convert_dynamic_function_with_into_function() { - fn make_function<'a, F: IntoFunction<'a, M>, M>(f: F) -> DynamicFunction<'a> { - f.into_function() - } - - let function: DynamicFunction = make_function(|| {}); - let _: DynamicFunction = make_function(function); - } } diff --git a/crates/bevy_reflect/src/func/reflect_fn.rs b/crates/bevy_reflect/src/func/reflect_fn.rs new file mode 100644 index 0000000000000..7a80f4cbd1cbc --- /dev/null +++ b/crates/bevy_reflect/src/func/reflect_fn.rs @@ -0,0 +1,223 @@ +use bevy_utils::all_tuples; + +use crate::func::args::FromArg; +use crate::func::macros::count_tokens; +use crate::func::{ArgList, FunctionError, FunctionInfo, FunctionResult, IntoReturn, ReflectFnMut}; +use crate::Reflect; + +/// A reflection-based version of the [`Fn`] trait. +/// +/// This allows functions to be called dynamically through [reflection]. +/// +/// # Blanket Implementation +/// +/// This trait has a blanket implementation that covers: +/// - Functions and methods defined with the `fn` keyword +/// - Closures that do not capture their environment +/// - Closures that capture immutable references to their environment +/// - Closures that take ownership of captured variables +/// +/// For each of the above cases, the function signature may only have up to 15 arguments, +/// not including an optional receiver argument (often `&self` or `&mut self`). +/// This optional receiver argument may be either a mutable or immutable reference to a type. +/// If the return type is also a reference, its lifetime will be bound to the lifetime of this receiver. +/// +/// See the [module-level documentation] for more information on valid signatures. +/// +/// To handle closures that capture mutable references to their environment, +/// see the [`ReflectFnMut`] trait instead. +/// +/// Arguments are expected to implement [`FromArg`], and the return type is expected to implement [`IntoReturn`]. +/// Both of these traits are automatically implemented when using the `Reflect` [derive macro]. +/// +/// # Example +/// +/// ``` +/// # use bevy_reflect::func::{ArgList, FunctionInfo, ReflectFn, TypedFunction}; +/// # +/// fn add(a: i32, b: i32) -> i32 { +/// a + b +/// } +/// +/// let args = ArgList::new().push_owned(25_i32).push_owned(75_i32); +/// let info = add.get_function_info(); +/// +/// let value = add.reflect_call(args, &info).unwrap().unwrap_owned(); +/// assert_eq!(value.take::().unwrap(), 100); +/// ``` +/// +/// # Trait Parameters +/// +/// This trait has a `Marker` type parameter that is used to get around issues with +/// [unconstrained type parameters] when defining impls with generic arguments or return types. +/// This `Marker` can be any type, provided it doesn't conflict with other implementations. +/// +/// Additionally, it has a lifetime parameter, `'env`, that is used to bound the lifetime of the function. +/// For most functions, this will end up just being `'static`, +/// however, closures that borrow from their environment will have a lifetime bound to that environment. +/// +/// [reflection]: crate +/// [module-level documentation]: crate::func +/// [derive macro]: derive@crate::Reflect +/// [unconstrained type parameters]: https://doc.rust-lang.org/error_codes/E0207.html +pub trait ReflectFn<'env, Marker>: ReflectFnMut<'env, Marker> { + /// Call the function with the given arguments and return the result. + fn reflect_call<'a>(&self, args: ArgList<'a>, info: &FunctionInfo) -> FunctionResult<'a>; +} + +/// Helper macro for implementing [`ReflectFn`] on Rust closures. +/// +/// This currently implements it for the following signatures (where `argX` may be any of `T`, `&T`, or `&mut T`): +/// - `Fn(arg0, arg1, ..., argN) -> R` +/// - `Fn(&Receiver, arg0, arg1, ..., argN) -> &R` +/// - `Fn(&mut Receiver, arg0, arg1, ..., argN) -> &mut R` +/// - `Fn(&mut Receiver, arg0, arg1, ..., argN) -> &R` +macro_rules! impl_reflect_fn { + ($(($Arg:ident, $arg:ident)),*) => { + // === (...) -> ReturnType === // + impl<'env, $($Arg,)* ReturnType, Function> ReflectFn<'env, fn($($Arg),*) -> [ReturnType]> for Function + where + $($Arg: FromArg,)* + // This clause allows us to convert `ReturnType` into `Return` + ReturnType: IntoReturn + Reflect, + Function: Fn($($Arg),*) -> ReturnType + 'env, + // This clause essentially asserts that `Arg::Item` is the same type as `Arg` + Function: for<'a> Fn($($Arg::Item<'a>),*) -> ReturnType + 'env, + { + fn reflect_call<'a>(&self, args: ArgList<'a>, _info: &FunctionInfo) -> FunctionResult<'a> { + const COUNT: usize = count_tokens!($($Arg)*); + + if args.len() != COUNT { + return Err(FunctionError::InvalidArgCount { + expected: COUNT, + received: args.len(), + }); + } + + let [$($arg,)*] = args.take().try_into().expect("invalid number of arguments"); + + #[allow(unused_mut)] + let mut _index = 0; + let ($($arg,)*) = ($($Arg::from_arg($arg, { + _index += 1; + _info.args().get(_index - 1).expect("argument index out of bounds") + })?,)*); + + Ok((self)($($arg,)*).into_return()) + } + } + + // === (&self, ...) -> &ReturnType === // + impl<'env, Receiver, $($Arg,)* ReturnType, Function> ReflectFn<'env, fn(&Receiver, $($Arg),*) -> &ReturnType> for Function + where + Receiver: Reflect, + $($Arg: FromArg,)* + ReturnType: Reflect, + // This clause allows us to convert `&ReturnType` into `Return` + for<'a> &'a ReturnType: IntoReturn, + Function: for<'a> Fn(&'a Receiver, $($Arg),*) -> &'a ReturnType + 'env, + // This clause essentially asserts that `Arg::Item` is the same type as `Arg` + Function: for<'a> Fn(&'a Receiver, $($Arg::Item<'a>),*) -> &'a ReturnType + 'env, + { + fn reflect_call<'a>(&self, args: ArgList<'a>, _info: &FunctionInfo) -> FunctionResult<'a> { + const COUNT: usize = count_tokens!(Receiver $($Arg)*); + + if args.len() != COUNT { + return Err(FunctionError::InvalidArgCount { + expected: COUNT, + received: args.len(), + }); + } + + let [receiver, $($arg,)*] = args.take().try_into().expect("invalid number of arguments"); + + let receiver = receiver.take_ref::(_info.args().get(0).expect("argument index out of bounds"))?; + + #[allow(unused_mut)] + let mut _index = 1; + let ($($arg,)*) = ($($Arg::from_arg($arg, { + _index += 1; + _info.args().get(_index - 1).expect("argument index out of bounds") + })?,)*); + + Ok((self)(receiver, $($arg,)*).into_return()) + } + } + + // === (&mut self, ...) -> &mut ReturnType === // + impl<'env, Receiver, $($Arg,)* ReturnType, Function> ReflectFn<'env, fn(&mut Receiver, $($Arg),*) -> &mut ReturnType> for Function + where + Receiver: Reflect, + $($Arg: FromArg,)* + ReturnType: Reflect, + // This clause allows us to convert `&mut ReturnType` into `Return` + for<'a> &'a mut ReturnType: IntoReturn, + Function: for<'a> Fn(&'a mut Receiver, $($Arg),*) -> &'a mut ReturnType + 'env, + // This clause essentially asserts that `Arg::Item` is the same type as `Arg` + Function: for<'a> Fn(&'a mut Receiver, $($Arg::Item<'a>),*) -> &'a mut ReturnType + 'env, + { + fn reflect_call<'a>(&self, args: ArgList<'a>, _info: &FunctionInfo) -> FunctionResult<'a> { + const COUNT: usize = count_tokens!(Receiver $($Arg)*); + + if args.len() != COUNT { + return Err(FunctionError::InvalidArgCount { + expected: COUNT, + received: args.len(), + }); + } + + let [receiver, $($arg,)*] = args.take().try_into().expect("invalid number of arguments"); + + let receiver = receiver.take_mut::(_info.args().get(0).expect("argument index out of bounds"))?; + + #[allow(unused_mut)] + let mut _index = 1; + let ($($arg,)*) = ($($Arg::from_arg($arg, { + _index += 1; + _info.args().get(_index - 1).expect("argument index out of bounds") + })?,)*); + + Ok((self)(receiver, $($arg,)*).into_return()) + } + } + + // === (&mut self, ...) -> &ReturnType === // + impl<'env, Receiver, $($Arg,)* ReturnType, Function> ReflectFn<'env, fn(&mut Receiver, $($Arg),*) -> &ReturnType> for Function + where + Receiver: Reflect, + $($Arg: FromArg,)* + ReturnType: Reflect, + // This clause allows us to convert `&ReturnType` into `Return` + for<'a> &'a ReturnType: IntoReturn, + Function: for<'a> Fn(&'a mut Receiver, $($Arg),*) -> &'a ReturnType + 'env, + // This clause essentially asserts that `Arg::Item` is the same type as `Arg` + Function: for<'a> Fn(&'a mut Receiver, $($Arg::Item<'a>),*) -> &'a ReturnType + 'env, + { + fn reflect_call<'a>(&self, args: ArgList<'a>, _info: &FunctionInfo) -> FunctionResult<'a> { + const COUNT: usize = count_tokens!(Receiver $($Arg)*); + + if args.len() != COUNT { + return Err(FunctionError::InvalidArgCount { + expected: COUNT, + received: args.len(), + }); + } + + let [receiver, $($arg,)*] = args.take().try_into().expect("invalid number of arguments"); + + let receiver = receiver.take_mut::(_info.args().get(0).expect("argument index out of bounds"))?; + + #[allow(unused_mut)] + let mut _index = 1; + let ($($arg,)*) = ($($Arg::from_arg($arg, { + _index += 1; + _info.args().get(_index - 1).expect("argument index out of bounds") + })?,)*); + + Ok((self)(receiver, $($arg,)*).into_return()) + } + } + }; +} + +all_tuples!(impl_reflect_fn, 0, 15, Arg, arg); diff --git a/crates/bevy_reflect/src/func/reflect_fn_mut.rs b/crates/bevy_reflect/src/func/reflect_fn_mut.rs new file mode 100644 index 0000000000000..a396d562620ba --- /dev/null +++ b/crates/bevy_reflect/src/func/reflect_fn_mut.rs @@ -0,0 +1,233 @@ +use bevy_utils::all_tuples; + +use crate::func::args::FromArg; +use crate::func::macros::count_tokens; +use crate::func::{ArgList, FunctionError, FunctionInfo, FunctionResult, IntoReturn}; +use crate::Reflect; + +/// A reflection-based version of the [`FnMut`] trait. +/// +/// This allows functions to be called dynamically through [reflection]. +/// +/// This is a supertrait of [`ReflectFn`], and is used for closures that may mutate their environment. +/// +/// # Blanket Implementation +/// +/// This trait has a blanket implementation that covers everything that [`ReflectFn`] does: +/// - Functions and methods defined with the `fn` keyword +/// - Closures that do not capture their environment +/// - Closures that capture immutable references to their environment +/// - Closures that take ownership of captured variables +/// +/// But also allows for: +/// - Closures that capture mutable references to their environment +/// +/// For each of the above cases, the function signature may only have up to 15 arguments, +/// not including an optional receiver argument (often `&self` or `&mut self`). +/// This optional receiver argument may be either a mutable or immutable reference to a type. +/// If the return type is also a reference, its lifetime will be bound to the lifetime of this receiver. +/// +/// See the [module-level documentation] for more information on valid signatures. +/// +/// Arguments are expected to implement [`FromArg`], and the return type is expected to implement [`IntoReturn`]. +/// Both of these traits are automatically implemented when using the `Reflect` [derive macro]. +/// +/// # Example +/// +/// ``` +/// # use bevy_reflect::func::{ArgList, FunctionInfo, ReflectFnMut, TypedFunction}; +/// # +/// let mut list: Vec = vec![1, 3]; +/// +/// // `insert` is a closure that captures a mutable reference to `list` +/// let mut insert = |index: usize, value: i32| { +/// list.insert(index, value); +/// }; +/// +/// let args = ArgList::new().push_owned(1_usize).push_owned(2_i32); +/// let info = insert.get_function_info(); +/// +/// insert.reflect_call_mut(args, &info).unwrap(); +/// assert_eq!(list, vec![1, 2, 3]); +/// ``` +/// +/// # Trait Parameters +/// +/// This trait has a `Marker` type parameter that is used to get around issues with +/// [unconstrained type parameters] when defining impls with generic arguments or return types. +/// This `Marker` can be any type, provided it doesn't conflict with other implementations. +/// +/// Additionally, it has a lifetime parameter, `'env`, that is used to bound the lifetime of the function. +/// For most functions, this will end up just being `'static`, +/// however, closures that borrow from their environment will have a lifetime bound to that environment. +/// +/// [reflection]: crate +/// [`ReflectFn`]: crate::func::ReflectFn +/// [module-level documentation]: crate::func +/// [derive macro]: derive@crate::Reflect +/// [unconstrained type parameters]: https://doc.rust-lang.org/error_codes/E0207.html +pub trait ReflectFnMut<'env, Marker> { + /// Call the function with the given arguments and return the result. + fn reflect_call_mut<'a>( + &mut self, + args: ArgList<'a>, + info: &FunctionInfo, + ) -> FunctionResult<'a>; +} + +/// Helper macro for implementing [`ReflectFnMut`] on Rust closures. +/// +/// This currently implements it for the following signatures (where `argX` may be any of `T`, `&T`, or `&mut T`): +/// - `FnMut(arg0, arg1, ..., argN) -> R` +/// - `FnMut(&Receiver, arg0, arg1, ..., argN) -> &R` +/// - `FnMut(&mut Receiver, arg0, arg1, ..., argN) -> &mut R` +/// - `FnMut(&mut Receiver, arg0, arg1, ..., argN) -> &R` +macro_rules! impl_reflect_fn_mut { + ($(($Arg:ident, $arg:ident)),*) => { + // === (...) -> ReturnType === // + impl<'env, $($Arg,)* ReturnType, Function> ReflectFnMut<'env, fn($($Arg),*) -> [ReturnType]> for Function + where + $($Arg: FromArg,)* + // This clause allows us to convert `ReturnType` into `Return` + ReturnType: IntoReturn + Reflect, + Function: FnMut($($Arg),*) -> ReturnType + 'env, + // This clause essentially asserts that `Arg::Item` is the same type as `Arg` + Function: for<'a> FnMut($($Arg::Item<'a>),*) -> ReturnType + 'env, + { + fn reflect_call_mut<'a>(&mut self, args: ArgList<'a>, _info: &FunctionInfo) -> FunctionResult<'a> { + const COUNT: usize = count_tokens!($($Arg)*); + + if args.len() != COUNT { + return Err(FunctionError::InvalidArgCount { + expected: COUNT, + received: args.len(), + }); + } + + let [$($arg,)*] = args.take().try_into().expect("invalid number of arguments"); + + #[allow(unused_mut)] + let mut _index = 0; + let ($($arg,)*) = ($($Arg::from_arg($arg, { + _index += 1; + _info.args().get(_index - 1).expect("argument index out of bounds") + })?,)*); + + Ok((self)($($arg,)*).into_return()) + } + } + + // === (&self, ...) -> &ReturnType === // + impl<'env, Receiver, $($Arg,)* ReturnType, Function> ReflectFnMut<'env, fn(&Receiver, $($Arg),*) -> &ReturnType> for Function + where + Receiver: Reflect, + $($Arg: FromArg,)* + ReturnType: Reflect, + // This clause allows us to convert `&ReturnType` into `Return` + for<'a> &'a ReturnType: IntoReturn, + Function: for<'a> FnMut(&'a Receiver, $($Arg),*) -> &'a ReturnType + 'env, + // This clause essentially asserts that `Arg::Item` is the same type as `Arg` + Function: for<'a> FnMut(&'a Receiver, $($Arg::Item<'a>),*) -> &'a ReturnType + 'env, + { + fn reflect_call_mut<'a>(&mut self, args: ArgList<'a>, _info: &FunctionInfo) -> FunctionResult<'a> { + const COUNT: usize = count_tokens!(Receiver $($Arg)*); + + if args.len() != COUNT { + return Err(FunctionError::InvalidArgCount { + expected: COUNT, + received: args.len(), + }); + } + + let [receiver, $($arg,)*] = args.take().try_into().expect("invalid number of arguments"); + + let receiver = receiver.take_ref::(_info.args().get(0).expect("argument index out of bounds"))?; + + #[allow(unused_mut)] + let mut _index = 1; + let ($($arg,)*) = ($($Arg::from_arg($arg, { + _index += 1; + _info.args().get(_index - 1).expect("argument index out of bounds") + })?,)*); + + Ok((self)(receiver, $($arg,)*).into_return()) + } + } + + // === (&mut self, ...) -> &mut ReturnType === // + impl<'env, Receiver, $($Arg,)* ReturnType, Function> ReflectFnMut<'env, fn(&mut Receiver, $($Arg),*) -> &mut ReturnType> for Function + where + Receiver: Reflect, + $($Arg: FromArg,)* + ReturnType: Reflect, + // This clause allows us to convert `&mut ReturnType` into `Return` + for<'a> &'a mut ReturnType: IntoReturn, + Function: for<'a> FnMut(&'a mut Receiver, $($Arg),*) -> &'a mut ReturnType + 'env, + // This clause essentially asserts that `Arg::Item` is the same type as `Arg` + Function: for<'a> FnMut(&'a mut Receiver, $($Arg::Item<'a>),*) -> &'a mut ReturnType + 'env, + { + fn reflect_call_mut<'a>(&mut self, args: ArgList<'a>, _info: &FunctionInfo) -> FunctionResult<'a> { + const COUNT: usize = count_tokens!(Receiver $($Arg)*); + + if args.len() != COUNT { + return Err(FunctionError::InvalidArgCount { + expected: COUNT, + received: args.len(), + }); + } + + let [receiver, $($arg,)*] = args.take().try_into().expect("invalid number of arguments"); + + let receiver = receiver.take_mut::(_info.args().get(0).expect("argument index out of bounds"))?; + + #[allow(unused_mut)] + let mut _index = 1; + let ($($arg,)*) = ($($Arg::from_arg($arg, { + _index += 1; + _info.args().get(_index - 1).expect("argument index out of bounds") + })?,)*); + + Ok((self)(receiver, $($arg,)*).into_return()) + } + } + + // === (&mut self, ...) -> &ReturnType === // + impl<'env, Receiver, $($Arg,)* ReturnType, Function> ReflectFnMut<'env, fn(&mut Receiver, $($Arg),*) -> &ReturnType> for Function + where + Receiver: Reflect, + $($Arg: FromArg,)* + ReturnType: Reflect, + // This clause allows us to convert `&ReturnType` into `Return` + for<'a> &'a ReturnType: IntoReturn, + Function: for<'a> FnMut(&'a mut Receiver, $($Arg),*) -> &'a ReturnType + 'env, + // This clause essentially asserts that `Arg::Item` is the same type as `Arg` + Function: for<'a> FnMut(&'a mut Receiver, $($Arg::Item<'a>),*) -> &'a ReturnType + 'env, + { + fn reflect_call_mut<'a>(&mut self, args: ArgList<'a>, _info: &FunctionInfo) -> FunctionResult<'a> { + const COUNT: usize = count_tokens!(Receiver $($Arg)*); + + if args.len() != COUNT { + return Err(FunctionError::InvalidArgCount { + expected: COUNT, + received: args.len(), + }); + } + + let [receiver, $($arg,)*] = args.take().try_into().expect("invalid number of arguments"); + + let receiver = receiver.take_mut::(_info.args().get(0).expect("argument index out of bounds"))?; + + #[allow(unused_mut)] + let mut _index = 1; + let ($($arg,)*) = ($($Arg::from_arg($arg, { + _index += 1; + _info.args().get(_index - 1).expect("argument index out of bounds") + })?,)*); + + Ok((self)(receiver, $($arg,)*).into_return()) + } + } + }; +} + +all_tuples!(impl_reflect_fn_mut, 0, 15, Arg, arg); diff --git a/crates/bevy_reflect/src/func/return_type.rs b/crates/bevy_reflect/src/func/return_type.rs index b9e94dca635ae..59a0f36adc3ef 100644 --- a/crates/bevy_reflect/src/func/return_type.rs +++ b/crates/bevy_reflect/src/func/return_type.rs @@ -1,8 +1,9 @@ use crate::Reflect; -/// The return type of a [`DynamicFunction`]. +/// The return type of a [`DynamicFunction`] or [`DynamicClosure`]. /// /// [`DynamicFunction`]: crate::func::DynamicFunction +/// [`DynamicClosure`]: crate::func::DynamicClosure #[derive(Debug)] pub enum Return<'a> { /// The function returns nothing (i.e. it returns `()`). @@ -68,7 +69,9 @@ impl<'a> Return<'a> { /// [derive macro]: derive@crate::Reflect pub trait IntoReturn { /// Converts [`Self`] into a [`Return`] value. - fn into_return<'a>(self) -> Return<'a>; + fn into_return<'a>(self) -> Return<'a> + where + Self: 'a; } impl IntoReturn for () { @@ -111,7 +114,7 @@ macro_rules! impl_into_return { $($U $(: $U1 $(+ $U2)*)?),* )? { - fn into_return<'into_return>(self) -> $crate::func::Return<'into_return> { + fn into_return<'into_return>(self) -> $crate::func::Return<'into_return> where Self: 'into_return { $crate::func::Return::Owned(Box::new(self)) } } @@ -125,7 +128,7 @@ macro_rules! impl_into_return { $($U $(: $U1 $(+ $U2)*)?),* )? { - fn into_return<'into_return>(self) -> $crate::func::Return<'into_return> { + fn into_return<'into_return>(self) -> $crate::func::Return<'into_return> where Self: 'into_return { $crate::func::Return::Ref(self) } } @@ -139,7 +142,7 @@ macro_rules! impl_into_return { $($U $(: $U1 $(+ $U2)*)?),* )? { - fn into_return<'into_return>(self) -> $crate::func::Return<'into_return> { + fn into_return<'into_return>(self) -> $crate::func::Return<'into_return> where Self: 'into_return { $crate::func::Return::Mut(self) } } diff --git a/examples/reflection/function_reflection.rs b/examples/reflection/function_reflection.rs index 8f02cec4574f4..4d1faf7c1a77a 100644 --- a/examples/reflection/function_reflection.rs +++ b/examples/reflection/function_reflection.rs @@ -8,7 +8,8 @@ use bevy::reflect::func::args::ArgInfo; use bevy::reflect::func::{ - ArgList, DynamicFunction, FunctionInfo, IntoFunction, Return, ReturnInfo, + ArgList, DynamicClosure, DynamicClosureMut, DynamicFunction, FunctionInfo, IntoClosure, + IntoClosureMut, IntoFunction, Return, ReturnInfo, }; use bevy::reflect::Reflect; @@ -37,7 +38,7 @@ fn main() { // Luckily, Bevy's reflection crate comes with a set of tools for doing just that! // We do this by first converting our function into the reflection-based `DynamicFunction` type // using the `IntoFunction` trait. - let mut function: DynamicFunction = dbg!(add.into_function()); + let function: DynamicFunction = dbg!(add.into_function()); // This time, you'll notice that `DynamicFunction` doesn't take any information about the function's arguments or return value. // This is because `DynamicFunction` checks the types of the arguments and return value at runtime. @@ -55,22 +56,34 @@ fn main() { let value: Box = return_value.unwrap_owned(); assert_eq!(value.take::().unwrap(), 4); - // The same can also be done for closures. + // The same can also be done for closures that capture references to their environment. + // Closures that capture their environment immutably can be converted into a `DynamicClosure` + // using the `IntoClosure` trait. + let minimum = 5; + let clamp = |value: i32| value.max(minimum); + + let function: DynamicClosure = dbg!(clamp.into_closure()); + let args = dbg!(ArgList::new().push_owned(2_i32)); + let return_value = dbg!(function.call(args).unwrap()); + let value: Box = return_value.unwrap_owned(); + assert_eq!(value.take::().unwrap(), 5); + + // We can also handle closures that capture their environment mutably + // using the `IntoClosureMut` trait. let mut count = 0; - let increment = |amount: i32| { - count += amount; - }; - let increment_function: DynamicFunction = dbg!(increment.into_function()); + let increment = |amount: i32| count += amount; + + let closure: DynamicClosureMut = dbg!(increment.into_closure_mut()); let args = dbg!(ArgList::new().push_owned(5_i32)); - // `DynamicFunction`s containing closures that capture their environment like this one - // may need to be dropped before those captured variables may be used again. - // This can be done manually with `drop` or by using the `Function::call_once` method. - dbg!(increment_function.call_once(args).unwrap()); + // Because `DynamicClosureMut` mutably borrows `total`, + // it will need to be dropped before `total` can be accessed again. + // This can be done manually with `drop(closure)` or by using the `DynamicClosureMut::call_once` method. + dbg!(closure.call_once(args).unwrap()); assert_eq!(count, 5); // As stated before, this works for many kinds of simple functions. // Functions with non-reflectable arguments or return values may not be able to be converted. - // Generic functions are also not supported. + // Generic functions are also not supported (unless manually monomorphized like `foo::.into_function()`). // Additionally, the lifetime of the return value is tied to the lifetime of the first argument. // However, this means that many methods (i.e. functions with a `self` parameter) are also supported: #[derive(Reflect, Default)] @@ -92,12 +105,12 @@ fn main() { let mut data = Data::default(); - let mut set_value = dbg!(Data::set_value.into_function()); + let set_value = dbg!(Data::set_value.into_function()); let args = dbg!(ArgList::new().push_mut(&mut data)).push_owned(String::from("Hello, world!")); dbg!(set_value.call(args).unwrap()); assert_eq!(data.value, "Hello, world!"); - let mut get_value = dbg!(Data::get_value.into_function()); + let get_value = dbg!(Data::get_value.into_function()); let args = dbg!(ArgList::new().push_ref(&data)); let return_value = dbg!(get_value.call(args).unwrap()); let value: &dyn Reflect = return_value.unwrap_ref(); @@ -115,7 +128,7 @@ fn main() { container.as_ref().unwrap() } - let mut get_or_insert_function = dbg!(DynamicFunction::new( + let get_or_insert_function = dbg!(DynamicFunction::new( |mut args, info| { let container_info = &info.args()[1]; let value_info = &info.args()[0];