From 784a32916e8743fb3b8e52a70eb3cb01849aa250 Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Sat, 27 Apr 2024 22:20:01 -0700 Subject: [PATCH 01/31] Add support for dynamic functions --- crates/bevy_reflect/src/func/args/arg.rs | 7 + crates/bevy_reflect/src/func/args/error.rs | 21 +++ crates/bevy_reflect/src/func/args/from_arg.rs | 134 +++++++++++++++ crates/bevy_reflect/src/func/args/info.rs | 49 ++++++ crates/bevy_reflect/src/func/args/list.rs | 40 +++++ crates/bevy_reflect/src/func/args/mod.rs | 11 ++ crates/bevy_reflect/src/func/error.rs | 10 ++ crates/bevy_reflect/src/func/function.rs | 40 +++++ crates/bevy_reflect/src/func/info.rs | 27 ++++ crates/bevy_reflect/src/func/into.rs | 59 +++++++ crates/bevy_reflect/src/func/mod.rs | 153 ++++++++++++++++++ crates/bevy_reflect/src/func/utils.rs | 12 ++ crates/bevy_reflect/src/lib.rs | 1 + 13 files changed, 564 insertions(+) create mode 100644 crates/bevy_reflect/src/func/args/arg.rs create mode 100644 crates/bevy_reflect/src/func/args/error.rs create mode 100644 crates/bevy_reflect/src/func/args/from_arg.rs create mode 100644 crates/bevy_reflect/src/func/args/info.rs create mode 100644 crates/bevy_reflect/src/func/args/list.rs create mode 100644 crates/bevy_reflect/src/func/args/mod.rs create mode 100644 crates/bevy_reflect/src/func/error.rs create mode 100644 crates/bevy_reflect/src/func/function.rs create mode 100644 crates/bevy_reflect/src/func/info.rs create mode 100644 crates/bevy_reflect/src/func/into.rs create mode 100644 crates/bevy_reflect/src/func/mod.rs create mode 100644 crates/bevy_reflect/src/func/utils.rs diff --git a/crates/bevy_reflect/src/func/args/arg.rs b/crates/bevy_reflect/src/func/args/arg.rs new file mode 100644 index 0000000000000..8a705f95e0b5a --- /dev/null +++ b/crates/bevy_reflect/src/func/args/arg.rs @@ -0,0 +1,7 @@ +use crate::Reflect; + +pub enum Arg<'a> { + Owned(Box), + Ref(&'a dyn Reflect), + Mut(&'a mut dyn Reflect), +} diff --git a/crates/bevy_reflect/src/func/args/error.rs b/crates/bevy_reflect/src/func/args/error.rs new file mode 100644 index 0000000000000..29b1a13400d68 --- /dev/null +++ b/crates/bevy_reflect/src/func/args/error.rs @@ -0,0 +1,21 @@ +use alloc::borrow::Cow; + +use thiserror::Error; + +use crate::func::args::info::{ArgId, Ownership}; + +#[derive(Debug, Error, PartialEq)] +pub enum ArgError { + #[error("expected `{expected}` but received `{received}` (@ {id:?})")] + UnexpectedType { + id: ArgId, + expected: Cow<'static, str>, + received: Cow<'static, str>, + }, + #[error("expected {expected} value but received {received} value (@ {id:?})")] + InvalidOwnership { + id: ArgId, + expected: Ownership, + received: Ownership, + }, +} diff --git a/crates/bevy_reflect/src/func/args/from_arg.rs b/crates/bevy_reflect/src/func/args/from_arg.rs new file mode 100644 index 0000000000000..0690277c5055b --- /dev/null +++ b/crates/bevy_reflect/src/func/args/from_arg.rs @@ -0,0 +1,134 @@ +use crate::func::args::{Arg, ArgError, ArgInfo}; + +pub trait FromArg { + type Item<'a>; + fn from_arg<'a>(arg: Arg<'a>, info: &ArgInfo) -> Result, ArgError>; +} + +// TODO: Move this into the `Reflect` derive +macro_rules! impl_from_arg { + ($name: ty) => { + impl $crate::func::args::FromArg for $name { + type Item<'a> = $name; + fn from_arg<'a>( + arg: $crate::func::args::Arg<'a>, + info: &$crate::func::args::ArgInfo, + ) -> Result, $crate::func::args::ArgError> { + match arg { + $crate::func::args::Arg::Owned(arg) => { + arg.take() + .map_err(|arg| $crate::func::args::ArgError::UnexpectedType { + id: info.id().clone(), + expected: ::std::borrow::Cow::Borrowed(info.type_path()), + received: ::std::borrow::Cow::Owned(arg.reflect_type_path().to_string()), + }) + } + $crate::func::args::Arg::Ref(_) => { + Err($crate::func::args::ArgError::InvalidOwnership { + id: info.id().clone(), + expected: $crate::func::args::Ownership::Owned, + received: $crate::func::args::Ownership::Ref, + }) + } + $crate::func::args::Arg::Mut(_) => { + Err($crate::func::args::ArgError::InvalidOwnership { + id: info.id().clone(), + expected: $crate::func::args::Ownership::Owned, + received: $crate::func::args::Ownership::Mut, + }) + } + } + } + } + + impl $crate::func::args::FromArg for &'static $name { + type Item<'a> = &'a $name; + fn from_arg<'a>( + arg: $crate::func::args::Arg<'a>, + info: &$crate::func::args::ArgInfo, + ) -> Result, $crate::func::args::ArgError> { + match arg { + $crate::func::args::Arg::Owned(_) => { + Err($crate::func::args::ArgError::InvalidOwnership { + id: info.id().clone(), + expected: $crate::func::args::Ownership::Ref, + received: $crate::func::args::Ownership::Owned, + }) + } + $crate::func::args::Arg::Ref(arg) => { + Ok(arg.downcast_ref().ok_or_else(|| { + $crate::func::args::ArgError::UnexpectedType { + id: info.id().clone(), + expected: ::std::borrow::Cow::Borrowed(info.type_path()), + received: ::std::borrow::Cow::Owned(arg.reflect_type_path().to_string()), + } + })?) + } + $crate::func::args::Arg::Mut(_) => { + Err($crate::func::args::ArgError::InvalidOwnership { + id: info.id().clone(), + expected: $crate::func::args::Ownership::Ref, + received: $crate::func::args::Ownership::Mut, + }) + } + } + } + } + + impl $crate::func::args::FromArg for &'static mut $name { + type Item<'a> = &'a mut $name; + fn from_arg<'a>( + arg: $crate::func::args::Arg<'a>, + info: &$crate::func::args::ArgInfo, + ) -> Result, $crate::func::args::ArgError> { + match arg { + $crate::func::args::Arg::Owned(_) => { + Err($crate::func::args::ArgError::InvalidOwnership { + id: info.id().clone(), + expected: $crate::func::args::Ownership::Mut, + received: $crate::func::args::Ownership::Owned, + }) + } + $crate::func::args::Arg::Ref(_) => { + Err($crate::func::args::ArgError::InvalidOwnership { + id: info.id().clone(), + expected: $crate::func::args::Ownership::Mut, + received: $crate::func::args::Ownership::Ref, + }) + } + $crate::func::args::Arg::Mut(arg) => { + let received = ::std::borrow::Cow::Owned(arg.reflect_type_path().to_string()); + Ok(arg.downcast_mut().ok_or_else(|| { + $crate::func::args::ArgError::UnexpectedType { + id: info.id().clone(), + expected: ::std::borrow::Cow::Borrowed(info.type_path()), + received, + } + })?) + } + } + } + } + }; +} + +pub(crate) use impl_from_arg; + +impl_from_arg!(bool); +impl_from_arg!(char); +impl_from_arg!(f32); +impl_from_arg!(f64); +impl_from_arg!(i8); +impl_from_arg!(i16); +impl_from_arg!(i32); +impl_from_arg!(i64); +impl_from_arg!(i128); +impl_from_arg!(isize); +impl_from_arg!(u8); +impl_from_arg!(u16); +impl_from_arg!(u32); +impl_from_arg!(u64); +impl_from_arg!(u128); +impl_from_arg!(usize); +impl_from_arg!(String); +impl_from_arg!(&'static str); diff --git a/crates/bevy_reflect/src/func/args/info.rs b/crates/bevy_reflect/src/func/args/info.rs new file mode 100644 index 0000000000000..13f00890feb17 --- /dev/null +++ b/crates/bevy_reflect/src/func/args/info.rs @@ -0,0 +1,49 @@ +use alloc::borrow::Cow; +use core::fmt::{Display, Formatter}; +use crate::TypePath; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ArgInfo { + id: ArgId, + type_path: &'static str, +} + +impl ArgInfo { + pub fn new(id: ArgId) -> Self { + Self { + id, + type_path: T::type_path(), + } + } + + pub fn id(&self) -> &ArgId { + &self.id + } + + pub fn type_path(&self) -> &'static str { + self.type_path + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum ArgId { + Index(usize), + Name(Cow<'static, str>), +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum Ownership { + Ref, + Mut, + Owned, +} + +impl Display for Ownership { + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + match self { + Self::Ref => write!(f, "reference"), + Self::Mut => write!(f, "mutable reference"), + Self::Owned => write!(f, "owned"), + } + } +} diff --git a/crates/bevy_reflect/src/func/args/list.rs b/crates/bevy_reflect/src/func/args/list.rs new file mode 100644 index 0000000000000..a4a32f41102a3 --- /dev/null +++ b/crates/bevy_reflect/src/func/args/list.rs @@ -0,0 +1,40 @@ +use crate::func::args::Arg; +use crate::Reflect; + +#[derive(Default)] +pub struct ArgList<'a>(Vec>); + +impl<'a> ArgList<'a> { + pub fn push(mut self, arg: Arg<'a>) -> Self { + self.0.push(arg); + self + } + + pub fn push_ref(self, arg: &'a dyn Reflect) -> Self { + self.push(Arg::Ref(arg)) + } + + pub fn push_mut(self, arg: &'a mut dyn Reflect) -> Self { + self.push(Arg::Mut(arg)) + } + + pub fn push_owned(self, arg: impl Reflect) -> Self { + self.push(Arg::Owned(Box::new(arg))) + } + + pub fn push_boxed(self, arg: Box) -> Self { + self.push(Arg::Owned(arg)) + } + + pub fn len(&self) -> usize { + self.0.len() + } + + pub fn is_empty(&self) -> bool { + self.0.is_empty() + } + + pub fn take(self) -> Vec> { + self.0 + } +} diff --git a/crates/bevy_reflect/src/func/args/mod.rs b/crates/bevy_reflect/src/func/args/mod.rs new file mode 100644 index 0000000000000..5b22135395e2d --- /dev/null +++ b/crates/bevy_reflect/src/func/args/mod.rs @@ -0,0 +1,11 @@ +pub use arg::*; +pub use error::*; +pub use from_arg::*; +pub use info::*; +pub use list::*; + +mod arg; +mod error; +mod from_arg; +mod info; +mod list; diff --git a/crates/bevy_reflect/src/func/error.rs b/crates/bevy_reflect/src/func/error.rs new file mode 100644 index 0000000000000..68307d35ae8d3 --- /dev/null +++ b/crates/bevy_reflect/src/func/error.rs @@ -0,0 +1,10 @@ +use crate::func::args::ArgError; +use thiserror::Error; + +#[derive(Debug, Error, PartialEq)] +pub enum FuncError { + #[error(transparent)] + Arg(#[from] ArgError), + #[error("expected {expected} arguments but received {received}")] + ArgCount { expected: usize, received: usize }, +} diff --git a/crates/bevy_reflect/src/func/function.rs b/crates/bevy_reflect/src/func/function.rs new file mode 100644 index 0000000000000..c3c3ca747b6b7 --- /dev/null +++ b/crates/bevy_reflect/src/func/function.rs @@ -0,0 +1,40 @@ +use crate::func::args::{ArgInfo, ArgList}; +use crate::func::error::FuncError; +use crate::func::info::FunctionInfo; +use crate::Reflect; +use alloc::borrow::Cow; +use std::ops::DerefMut; + +// TODO: Support reference return types +pub type FunctionResult = Result>, FuncError>; + +pub struct Function { + info: FunctionInfo, + func: Box FnMut(ArgList<'a>, &FunctionInfo) -> FunctionResult + 'static>, +} + +impl Function { + pub fn new FnMut(ArgList<'a>, &FunctionInfo) -> FunctionResult + 'static>( + func: F, + args: Vec, + ) -> Self { + Self { + info: FunctionInfo::new(args), + func: Box::new(func), + } + } + + pub fn with_name(mut self, name: impl Into>) -> Self { + self.info = self.info.with_name(name); + self + } + + pub fn with_args(mut self, args: Vec) -> Self { + self.info = self.info.with_args(args); + self + } + + pub fn call<'a>(&mut self, args: ArgList<'a>) -> FunctionResult { + (self.func.deref_mut())(args, &self.info) + } +} diff --git a/crates/bevy_reflect/src/func/info.rs b/crates/bevy_reflect/src/func/info.rs new file mode 100644 index 0000000000000..d46cf89a74816 --- /dev/null +++ b/crates/bevy_reflect/src/func/info.rs @@ -0,0 +1,27 @@ +use crate::func::args::ArgInfo; +use alloc::borrow::Cow; + +pub struct FunctionInfo { + name: Option>, + args: Vec, +} + +impl FunctionInfo { + pub fn new(args: Vec) -> Self { + Self { name: None, args } + } + + pub fn with_name(mut self, name: impl Into>) -> Self { + self.name = Some(name.into()); + self + } + + pub fn with_args(mut self, args: Vec) -> Self { + self.args = args; + self + } + + pub fn args(&self) -> &[ArgInfo] { + &self.args + } +} diff --git a/crates/bevy_reflect/src/func/into.rs b/crates/bevy_reflect/src/func/into.rs new file mode 100644 index 0000000000000..0cb2de92c0f63 --- /dev/null +++ b/crates/bevy_reflect/src/func/into.rs @@ -0,0 +1,59 @@ +use crate::func::function::Function; +use bevy_utils::all_tuples; + +pub trait IntoFunction { + fn into_function(self) -> Function; +} + +// 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 }; +} + +macro_rules! impl_into_function { + ($(($Arg:ident, $arg:ident)),*) => { + impl<$($Arg,)* R, F> $crate::func::IntoFunction R> for F + where + $($Arg: $crate::func::args::FromArg + $crate::TypePath,)* + R: $crate::Reflect, + F: FnMut($($Arg),*) -> R + 'static, + F: for<'a> FnMut($($Arg::Item<'a>),*) -> R + 'static, + { + fn into_function(mut self) -> $crate::func::Function { + const COUNT: usize = count_tts!($($Arg)*); + + $crate::func::Function::new(move |args, _info| { + if args.len() != COUNT { + return Err($crate::func::error::FuncError::ArgCount { + expected: COUNT, + received: args.len(), + }); + } + + let [$($arg,)*] = args.take().try_into().ok().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") + })?,)*); + $crate::func::utils::to_function_result((self)($($arg,)*)) + }, { + #[allow(unused_mut)] + let mut _index = 0; + vec![ + $($crate::func::args::ArgInfo::new::<$Arg>($crate::func::args::ArgId::Index({ + _index += 1; + _index - 1 + })),)* + ] + }) + } + } + } +} + +all_tuples!(impl_into_function, 0, 15, Arg, arg); diff --git a/crates/bevy_reflect/src/func/mod.rs b/crates/bevy_reflect/src/func/mod.rs new file mode 100644 index 0000000000000..787ec286471d5 --- /dev/null +++ b/crates/bevy_reflect/src/func/mod.rs @@ -0,0 +1,153 @@ +pub use error::*; +pub use function::*; +pub use info::*; +pub use into::*; + +pub mod args; +mod error; +mod function; +mod info; +mod into; +mod utils; + +#[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::default().push_owned(25_i32).push_owned(75_i32); + let result = func.call(args).unwrap().unwrap(); + 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::default().push_owned(25_i32).push_owned(75_i32); + let result = func.call(args).unwrap().unwrap(); + 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) + } + } + + crate::func::args::impl_from_arg!(Foo); + + let foo_a = Foo(25); + let foo_b = Foo(75); + + let mut func = Foo::add.into_function(); + let args = ArgList::default().push_ref(&foo_a).push_ref(&foo_b); + let result = func.call(args).unwrap().unwrap(); + 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::default(); + let result = func.call(args).unwrap().unwrap(); + 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::default().push_owned(123_i32); + let result = func.call(args).unwrap(); + assert!(result.is_none()); + } + + #[test] + fn should_error_on_missing_args() { + fn foo(_: i32) {} + + let mut func = foo.into_function(); + let args = ArgList::default(); + let result = func.call(args); + assert_eq!( + result.unwrap_err(), + FuncError::ArgCount { + expected: 1, + received: 0 + } + ); + } + + #[test] + fn should_error_on_too_many_args() { + fn foo() {} + + let mut func = foo.into_function(); + let args = ArgList::default().push_owned(123_i32); + let result = func.call(args); + assert_eq!( + result.unwrap_err(), + FuncError::ArgCount { + expected: 0, + received: 1 + } + ); + } + + #[test] + fn should_error_on_invalid_arg_type() { + fn foo(_: i32) {} + + let mut func = foo.into_function(); + let args = ArgList::default().push_owned(123_u32); + let result = func.call(args); + assert_eq!( + result.unwrap_err(), + FuncError::Arg(ArgError::UnexpectedType { + id: ArgId::Index(0), + expected: Cow::Borrowed(i32::type_path()), + received: Cow::Borrowed(u32::type_path()) + }) + ); + } + + #[test] + fn should_error_on_invalid_arg_ownership() { + fn foo(_: &i32) {} + + let mut func = foo.into_function(); + let args = ArgList::default().push_owned(123_i32); + let result = func.call(args); + assert_eq!( + result.unwrap_err(), + FuncError::Arg(ArgError::InvalidOwnership { + id: ArgId::Index(0), + expected: Ownership::Ref, + received: Ownership::Owned + }) + ); + } +} diff --git a/crates/bevy_reflect/src/func/utils.rs b/crates/bevy_reflect/src/func/utils.rs new file mode 100644 index 0000000000000..7f408a8070853 --- /dev/null +++ b/crates/bevy_reflect/src/func/utils.rs @@ -0,0 +1,12 @@ +use std::any::TypeId; + +use crate::func::function::FunctionResult; +use crate::Reflect; + +pub(super) fn to_function_result(value: R) -> FunctionResult { + if TypeId::of::() == TypeId::of::<()>() { + Ok(None) + } else { + Ok(Some(Box::new(value))) + } +} diff --git a/crates/bevy_reflect/src/lib.rs b/crates/bevy_reflect/src/lib.rs index 1ba4d1a3ec3ac..b24e952f45150 100644 --- a/crates/bevy_reflect/src/lib.rs +++ b/crates/bevy_reflect/src/lib.rs @@ -476,6 +476,7 @@ mod array; mod fields; mod from_reflect; +pub mod func; mod list; mod map; mod path; From 4e47c864674528688abdd7de94dd6386ecb0edc8 Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Sun, 28 Apr 2024 15:37:05 -0700 Subject: [PATCH 02/31] Add support for reference return types --- crates/bevy_reflect/src/func/args/arg.rs | 65 +++++++++++++ crates/bevy_reflect/src/func/args/from_arg.rs | 78 +-------------- crates/bevy_reflect/src/func/function.rs | 20 ++-- crates/bevy_reflect/src/func/info.rs | 1 + crates/bevy_reflect/src/func/into.rs | 97 ++++++++++++++++++- crates/bevy_reflect/src/func/mod.rs | 46 +++++++-- crates/bevy_reflect/src/func/return_type.rs | 90 +++++++++++++++++ crates/bevy_reflect/src/func/utils.rs | 12 --- 8 files changed, 307 insertions(+), 102 deletions(-) create mode 100644 crates/bevy_reflect/src/func/return_type.rs delete mode 100644 crates/bevy_reflect/src/func/utils.rs diff --git a/crates/bevy_reflect/src/func/args/arg.rs b/crates/bevy_reflect/src/func/args/arg.rs index 8a705f95e0b5a..1b7fe455da5a6 100644 --- a/crates/bevy_reflect/src/func/args/arg.rs +++ b/crates/bevy_reflect/src/func/args/arg.rs @@ -1,3 +1,4 @@ +use crate::func::args::{ArgError, ArgInfo, Ownership}; use crate::Reflect; pub enum Arg<'a> { @@ -5,3 +6,67 @@ pub enum Arg<'a> { Ref(&'a dyn Reflect), Mut(&'a mut dyn Reflect), } + +impl<'a> Arg<'a> { + pub fn take_owned(self, info: &ArgInfo) -> Result { + match self { + Arg::Owned(arg) => arg.take().map_err(|arg| ArgError::UnexpectedType { + id: info.id().clone(), + expected: ::std::borrow::Cow::Borrowed(info.type_path()), + received: ::std::borrow::Cow::Owned(arg.reflect_type_path().to_string()), + }), + Arg::Ref(_) => Err(ArgError::InvalidOwnership { + id: info.id().clone(), + expected: Ownership::Owned, + received: Ownership::Ref, + }), + Arg::Mut(_) => Err(ArgError::InvalidOwnership { + id: info.id().clone(), + expected: Ownership::Owned, + received: Ownership::Mut, + }), + } + } + pub fn take_ref(self, info: &ArgInfo) -> Result<&'a T, ArgError> { + match self { + Arg::Owned(_) => Err(ArgError::InvalidOwnership { + id: info.id().clone(), + expected: Ownership::Ref, + received: Ownership::Owned, + }), + Arg::Ref(arg) => Ok(arg.downcast_ref().ok_or_else(|| ArgError::UnexpectedType { + id: info.id().clone(), + expected: ::std::borrow::Cow::Borrowed(info.type_path()), + received: ::std::borrow::Cow::Owned(arg.reflect_type_path().to_string()), + })?), + Arg::Mut(_) => Err(ArgError::InvalidOwnership { + id: info.id().clone(), + expected: Ownership::Ref, + received: Ownership::Mut, + }), + } + } + + pub fn take_mut(self, info: &ArgInfo) -> Result<&'a mut T, ArgError> { + match self { + Arg::Owned(_) => Err(ArgError::InvalidOwnership { + id: info.id().clone(), + expected: Ownership::Mut, + received: Ownership::Owned, + }), + Arg::Ref(_) => Err(ArgError::InvalidOwnership { + id: info.id().clone(), + expected: Ownership::Mut, + received: Ownership::Ref, + }), + Arg::Mut(arg) => { + let received = ::std::borrow::Cow::Owned(arg.reflect_type_path().to_string()); + Ok(arg.downcast_mut().ok_or_else(|| ArgError::UnexpectedType { + id: info.id().clone(), + expected: ::std::borrow::Cow::Borrowed(info.type_path()), + received, + })?) + } + } + } +} diff --git a/crates/bevy_reflect/src/func/args/from_arg.rs b/crates/bevy_reflect/src/func/args/from_arg.rs index 0690277c5055b..cca53e27dc50c 100644 --- a/crates/bevy_reflect/src/func/args/from_arg.rs +++ b/crates/bevy_reflect/src/func/args/from_arg.rs @@ -14,30 +14,7 @@ macro_rules! impl_from_arg { arg: $crate::func::args::Arg<'a>, info: &$crate::func::args::ArgInfo, ) -> Result, $crate::func::args::ArgError> { - match arg { - $crate::func::args::Arg::Owned(arg) => { - arg.take() - .map_err(|arg| $crate::func::args::ArgError::UnexpectedType { - id: info.id().clone(), - expected: ::std::borrow::Cow::Borrowed(info.type_path()), - received: ::std::borrow::Cow::Owned(arg.reflect_type_path().to_string()), - }) - } - $crate::func::args::Arg::Ref(_) => { - Err($crate::func::args::ArgError::InvalidOwnership { - id: info.id().clone(), - expected: $crate::func::args::Ownership::Owned, - received: $crate::func::args::Ownership::Ref, - }) - } - $crate::func::args::Arg::Mut(_) => { - Err($crate::func::args::ArgError::InvalidOwnership { - id: info.id().clone(), - expected: $crate::func::args::Ownership::Owned, - received: $crate::func::args::Ownership::Mut, - }) - } - } + arg.take_owned(info) } } @@ -47,31 +24,7 @@ macro_rules! impl_from_arg { arg: $crate::func::args::Arg<'a>, info: &$crate::func::args::ArgInfo, ) -> Result, $crate::func::args::ArgError> { - match arg { - $crate::func::args::Arg::Owned(_) => { - Err($crate::func::args::ArgError::InvalidOwnership { - id: info.id().clone(), - expected: $crate::func::args::Ownership::Ref, - received: $crate::func::args::Ownership::Owned, - }) - } - $crate::func::args::Arg::Ref(arg) => { - Ok(arg.downcast_ref().ok_or_else(|| { - $crate::func::args::ArgError::UnexpectedType { - id: info.id().clone(), - expected: ::std::borrow::Cow::Borrowed(info.type_path()), - received: ::std::borrow::Cow::Owned(arg.reflect_type_path().to_string()), - } - })?) - } - $crate::func::args::Arg::Mut(_) => { - Err($crate::func::args::ArgError::InvalidOwnership { - id: info.id().clone(), - expected: $crate::func::args::Ownership::Ref, - received: $crate::func::args::Ownership::Mut, - }) - } - } + arg.take_ref(info) } } @@ -81,32 +34,7 @@ macro_rules! impl_from_arg { arg: $crate::func::args::Arg<'a>, info: &$crate::func::args::ArgInfo, ) -> Result, $crate::func::args::ArgError> { - match arg { - $crate::func::args::Arg::Owned(_) => { - Err($crate::func::args::ArgError::InvalidOwnership { - id: info.id().clone(), - expected: $crate::func::args::Ownership::Mut, - received: $crate::func::args::Ownership::Owned, - }) - } - $crate::func::args::Arg::Ref(_) => { - Err($crate::func::args::ArgError::InvalidOwnership { - id: info.id().clone(), - expected: $crate::func::args::Ownership::Mut, - received: $crate::func::args::Ownership::Ref, - }) - } - $crate::func::args::Arg::Mut(arg) => { - let received = ::std::borrow::Cow::Owned(arg.reflect_type_path().to_string()); - Ok(arg.downcast_mut().ok_or_else(|| { - $crate::func::args::ArgError::UnexpectedType { - id: info.id().clone(), - expected: ::std::borrow::Cow::Borrowed(info.type_path()), - received, - } - })?) - } - } + arg.take_mut(info) } } }; diff --git a/crates/bevy_reflect/src/func/function.rs b/crates/bevy_reflect/src/func/function.rs index c3c3ca747b6b7..12b2bd2ee13fd 100644 --- a/crates/bevy_reflect/src/func/function.rs +++ b/crates/bevy_reflect/src/func/function.rs @@ -1,20 +1,28 @@ use crate::func::args::{ArgInfo, ArgList}; use crate::func::error::FuncError; use crate::func::info::FunctionInfo; -use crate::Reflect; +use crate::func::return_type::Return; use alloc::borrow::Cow; +use core::fmt::{Debug, Formatter}; use std::ops::DerefMut; -// TODO: Support reference return types -pub type FunctionResult = Result>, FuncError>; +pub type FunctionResult<'a> = Result, FuncError>; pub struct Function { info: FunctionInfo, - func: Box FnMut(ArgList<'a>, &FunctionInfo) -> FunctionResult + 'static>, + func: Box FnMut(ArgList<'a>, &FunctionInfo) -> FunctionResult<'a> + 'static>, +} + +impl Debug for Function { + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + f.debug_struct("Function") + .field("info", &self.info) + .finish() + } } impl Function { - pub fn new FnMut(ArgList<'a>, &FunctionInfo) -> FunctionResult + 'static>( + pub fn new FnMut(ArgList<'a>, &FunctionInfo) -> FunctionResult<'a> + 'static>( func: F, args: Vec, ) -> Self { @@ -34,7 +42,7 @@ impl Function { self } - pub fn call<'a>(&mut self, args: ArgList<'a>) -> FunctionResult { + pub fn call<'a>(&mut self, args: ArgList<'a>) -> FunctionResult<'a> { (self.func.deref_mut())(args, &self.info) } } diff --git a/crates/bevy_reflect/src/func/info.rs b/crates/bevy_reflect/src/func/info.rs index d46cf89a74816..d364e666bb16b 100644 --- a/crates/bevy_reflect/src/func/info.rs +++ b/crates/bevy_reflect/src/func/info.rs @@ -1,6 +1,7 @@ use crate::func::args::ArgInfo; use alloc::borrow::Cow; +#[derive(Debug)] pub struct FunctionInfo { name: Option>, args: Vec, diff --git a/crates/bevy_reflect/src/func/into.rs b/crates/bevy_reflect/src/func/into.rs index 0cb2de92c0f63..ed2da6a823a7d 100644 --- a/crates/bevy_reflect/src/func/into.rs +++ b/crates/bevy_reflect/src/func/into.rs @@ -14,10 +14,11 @@ macro_rules! count_tts { macro_rules! impl_into_function { ($(($Arg:ident, $arg:ident)),*) => { + // === Owned Return === // impl<$($Arg,)* R, F> $crate::func::IntoFunction R> for F where $($Arg: $crate::func::args::FromArg + $crate::TypePath,)* - R: $crate::Reflect, + R: $crate::func::IntoReturn, F: FnMut($($Arg),*) -> R + 'static, F: for<'a> FnMut($($Arg::Item<'a>),*) -> R + 'static, { @@ -40,7 +41,7 @@ macro_rules! impl_into_function { _index += 1; _info.args().get(_index - 1).expect("argument index out of bounds") })?,)*); - $crate::func::utils::to_function_result((self)($($arg,)*)) + Ok((self)($($arg,)*).into_return()) }, { #[allow(unused_mut)] let mut _index = 0; @@ -53,7 +54,97 @@ macro_rules! impl_into_function { }) } } - } + + // === Ref Return === // + impl $crate::func::IntoFunction (R,)> for F + where + Receiver: $crate::Reflect + $crate::TypePath, + R: $crate::Reflect, + $($Arg: $crate::func::args::FromArg + $crate::TypePath,)* + F: for<'a> FnMut(&'a Receiver, $($Arg),*) -> &'a R + 'static, + F: for<'a> FnMut(&'a Receiver, $($Arg::Item<'a>),*) -> &'a R + 'static, + { + fn into_function(mut self) -> $crate::func::Function { + const COUNT: usize = count_tts!(Receiver $($Arg)*); + + $crate::func::Function::new(move |args, _info| { + if args.len() != COUNT { + return Err($crate::func::error::FuncError::ArgCount { + expected: COUNT, + received: args.len(), + }); + } + + let [receiver, $($arg,)*] = args.take().try_into().ok().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($crate::func::Return::Ref((self)(receiver, $($arg,)*))) + }, { + #[allow(unused_mut)] + let mut _index = 1; + vec![ + $crate::func::args::ArgInfo::new::<&Receiver>($crate::func::args::ArgId::Index(0)), + $($crate::func::args::ArgInfo::new::<$Arg>($crate::func::args::ArgId::Index({ + _index += 1; + _index - 1 + })),)* + ] + }) + } + } + + // === Mut Return === // + impl $crate::func::IntoFunction (R,)> for F + where + Receiver: $crate::Reflect + $crate::TypePath, + R: $crate::Reflect, + $($Arg: $crate::func::args::FromArg + $crate::TypePath,)* + F: for<'a> FnMut(&'a mut Receiver, $($Arg),*) -> &'a mut R + 'static, + F: for<'a> FnMut(&'a mut Receiver, $($Arg::Item<'a>),*) -> &'a mut R + 'static, + { + fn into_function(mut self) -> $crate::func::Function { + const COUNT: usize = count_tts!(Receiver $($Arg)*); + + $crate::func::Function::new(move |args, _info| { + if args.len() != COUNT { + return Err($crate::func::error::FuncError::ArgCount { + expected: COUNT, + received: args.len(), + }); + } + + let [receiver, $($arg,)*] = args.take().try_into().ok().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,)*))) + }, { + #[allow(unused_mut)] + let mut _index = 1; + vec![ + $crate::func::args::ArgInfo::new::<&mut Receiver>($crate::func::args::ArgId::Index(0)), + $($crate::func::args::ArgInfo::new::<$Arg>($crate::func::args::ArgId::Index({ + _index += 1; + _index - 1 + })),)* + ] + }) + } + } + }; } all_tuples!(impl_into_function, 0, 15, Arg, arg); diff --git a/crates/bevy_reflect/src/func/mod.rs b/crates/bevy_reflect/src/func/mod.rs index 787ec286471d5..5b59dbc469653 100644 --- a/crates/bevy_reflect/src/func/mod.rs +++ b/crates/bevy_reflect/src/func/mod.rs @@ -2,13 +2,14 @@ pub use error::*; pub use function::*; pub use info::*; pub use into::*; +pub use return_type::*; pub mod args; mod error; mod function; mod info; mod into; -mod utils; +mod return_type; #[cfg(test)] mod tests { @@ -26,7 +27,7 @@ mod tests { let mut func = add.into_function(); let args = ArgList::default().push_owned(25_i32).push_owned(75_i32); - let result = func.call(args).unwrap().unwrap(); + let result = func.call(args).unwrap().unwrap_owned(); assert_eq!(result.downcast_ref::(), Some(&100)); } @@ -34,7 +35,7 @@ mod tests { fn should_create_dynamic_closure() { let mut func = (|a: i32, b: i32| a + b).into_function(); let args = ArgList::default().push_owned(25_i32).push_owned(75_i32); - let result = func.call(args).unwrap().unwrap(); + let result = func.call(args).unwrap().unwrap_owned(); assert_eq!(result.downcast_ref::(), Some(&100)); } @@ -50,13 +51,14 @@ mod tests { } crate::func::args::impl_from_arg!(Foo); + crate::func::return_type::impl_into_return!(Foo); let foo_a = Foo(25); let foo_b = Foo(75); let mut func = Foo::add.into_function(); let args = ArgList::default().push_ref(&foo_a).push_ref(&foo_b); - let result = func.call(args).unwrap().unwrap(); + let result = func.call(args).unwrap().unwrap_owned(); assert_eq!(result.downcast_ref::(), Some(&Foo(100))); } @@ -68,7 +70,7 @@ mod tests { let mut func = foo.into_function(); let args = ArgList::default(); - let result = func.call(args).unwrap().unwrap(); + let result = func.call(args).unwrap().unwrap_owned(); assert_eq!( result.downcast_ref::(), Some(&String::from("Hello, World!")) @@ -82,7 +84,39 @@ mod tests { let mut func = foo.into_function(); let args = ArgList::default().push_owned(123_i32); let result = func.call(args).unwrap(); - assert!(result.is_none()); + 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::default() + .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::default() + .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] diff --git a/crates/bevy_reflect/src/func/return_type.rs b/crates/bevy_reflect/src/func/return_type.rs new file mode 100644 index 0000000000000..267cb4682f109 --- /dev/null +++ b/crates/bevy_reflect/src/func/return_type.rs @@ -0,0 +1,90 @@ +use crate::Reflect; + +#[derive(Debug)] +pub enum Return<'a> { + Unit, + Owned(Box), + Ref(&'a dyn Reflect), + Mut(&'a mut dyn Reflect), +} + +impl<'a> Return<'a> { + pub fn is_unit(&self) -> bool { + matches!(self, Return::Unit) + } + + pub fn unwrap_owned(self) -> Box { + match self { + Return::Owned(value) => value, + _ => panic!("expected owned value"), + } + } + + pub fn unwrap_ref(self) -> &'a dyn Reflect { + match self { + Return::Ref(value) => value, + _ => panic!("expected reference value"), + } + } + + pub fn unwrap_mut(self) -> &'a mut dyn Reflect { + match self { + Return::Mut(value) => value, + _ => panic!("expected mutable reference value"), + } + } +} + +pub trait IntoReturn { + fn into_return<'a>(self) -> Return<'a>; +} + +impl IntoReturn for () { + fn into_return<'a>(self) -> Return<'a> { + Return::Unit + } +} + +// TODO: Move this into the `Reflect` derive +macro_rules! impl_into_return { + ($name: ty) => { + impl IntoReturn for $name { + fn into_return<'a>(self) -> Return<'a> { + Return::Owned(Box::new(self)) + } + } + + impl IntoReturn for &'static $name { + fn into_return<'a>(self) -> Return<'a> { + Return::Ref(self) + } + } + + impl IntoReturn for &'static mut $name { + fn into_return<'a>(self) -> Return<'a> { + Return::Mut(self) + } + } + }; +} + +pub(crate) use impl_into_return; + +impl_into_return!(bool); +impl_into_return!(char); +impl_into_return!(f32); +impl_into_return!(f64); +impl_into_return!(i8); +impl_into_return!(i16); +impl_into_return!(i32); +impl_into_return!(i64); +impl_into_return!(i128); +impl_into_return!(isize); +impl_into_return!(u8); +impl_into_return!(u16); +impl_into_return!(u32); +impl_into_return!(u64); +impl_into_return!(u128); +impl_into_return!(usize); +impl_into_return!(String); +impl_into_return!(&'static str); diff --git a/crates/bevy_reflect/src/func/utils.rs b/crates/bevy_reflect/src/func/utils.rs deleted file mode 100644 index 7f408a8070853..0000000000000 --- a/crates/bevy_reflect/src/func/utils.rs +++ /dev/null @@ -1,12 +0,0 @@ -use std::any::TypeId; - -use crate::func::function::FunctionResult; -use crate::Reflect; - -pub(super) fn to_function_result(value: R) -> FunctionResult { - if TypeId::of::() == TypeId::of::<()>() { - Ok(None) - } else { - Ok(Some(Box::new(value))) - } -} From 2d8804dfd7b3ab249e448fd40fb9ab2f9c62eb75 Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Sun, 28 Apr 2024 16:16:10 -0700 Subject: [PATCH 03/31] Simplify ArgInfo --- crates/bevy_reflect/src/func/args/error.rs | 2 +- crates/bevy_reflect/src/func/args/info.rs | 54 ++++++++------- crates/bevy_reflect/src/func/args/mod.rs | 2 + .../bevy_reflect/src/func/args/ownership.rs | 67 +++++++++++++++++++ crates/bevy_reflect/src/func/into.rs | 24 ++++--- crates/bevy_reflect/src/func/mod.rs | 1 + 6 files changed, 115 insertions(+), 35 deletions(-) create mode 100644 crates/bevy_reflect/src/func/args/ownership.rs diff --git a/crates/bevy_reflect/src/func/args/error.rs b/crates/bevy_reflect/src/func/args/error.rs index 29b1a13400d68..919fd118ebf38 100644 --- a/crates/bevy_reflect/src/func/args/error.rs +++ b/crates/bevy_reflect/src/func/args/error.rs @@ -2,7 +2,7 @@ use alloc::borrow::Cow; use thiserror::Error; -use crate::func::args::info::{ArgId, Ownership}; +use crate::func::args::{ArgId, Ownership}; #[derive(Debug, Error, PartialEq)] pub enum ArgError { diff --git a/crates/bevy_reflect/src/func/args/info.rs b/crates/bevy_reflect/src/func/args/info.rs index 13f00890feb17..7b4c37f6c720a 100644 --- a/crates/bevy_reflect/src/func/args/info.rs +++ b/crates/bevy_reflect/src/func/args/info.rs @@ -1,23 +1,48 @@ use alloc::borrow::Cow; -use core::fmt::{Display, Formatter}; + +use crate::func::args::{GetOwnership, Ownership}; use crate::TypePath; #[derive(Debug, Clone, PartialEq, Eq)] pub struct ArgInfo { - id: ArgId, + index: usize, + name: Option>, + ownership: Ownership, type_path: &'static str, } impl ArgInfo { - pub fn new(id: ArgId) -> Self { + pub fn new(index: usize) -> Self { Self { - id, + index, + name: None, + ownership: T::ownership(), type_path: T::type_path(), } } - pub fn id(&self) -> &ArgId { - &self.id + pub fn with_name(mut self, name: impl Into>) -> Self { + self.name = Some(name.into()); + self + } + + pub fn index(&self) -> usize { + self.index + } + + pub fn name(&self) -> Option<&str> { + self.name.as_deref() + } + + pub fn ownership(&self) -> Ownership { + self.ownership + } + + pub fn id(&self) -> ArgId { + self.name + .clone() + .map(ArgId::Name) + .unwrap_or_else(|| ArgId::Index(self.index)) } pub fn type_path(&self) -> &'static str { @@ -30,20 +55,3 @@ pub enum ArgId { Index(usize), Name(Cow<'static, str>), } - -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub enum Ownership { - Ref, - Mut, - Owned, -} - -impl Display for Ownership { - fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { - match self { - Self::Ref => write!(f, "reference"), - Self::Mut => write!(f, "mutable reference"), - Self::Owned => write!(f, "owned"), - } - } -} diff --git a/crates/bevy_reflect/src/func/args/mod.rs b/crates/bevy_reflect/src/func/args/mod.rs index 5b22135395e2d..21ed8b0a24604 100644 --- a/crates/bevy_reflect/src/func/args/mod.rs +++ b/crates/bevy_reflect/src/func/args/mod.rs @@ -3,9 +3,11 @@ pub use error::*; pub use from_arg::*; pub use info::*; pub use list::*; +pub use ownership::*; mod arg; mod error; mod from_arg; mod info; mod list; +mod ownership; diff --git a/crates/bevy_reflect/src/func/args/ownership.rs b/crates/bevy_reflect/src/func/args/ownership.rs new file mode 100644 index 0000000000000..6b08b4f3996ff --- /dev/null +++ b/crates/bevy_reflect/src/func/args/ownership.rs @@ -0,0 +1,67 @@ +use core::fmt::{Display, Formatter}; + +pub trait GetOwnership { + fn ownership() -> Ownership; +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum Ownership { + Ref, + Mut, + Owned, +} + +impl Display for Ownership { + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + match self { + Self::Ref => write!(f, "reference"), + Self::Mut => write!(f, "mutable reference"), + Self::Owned => write!(f, "owned"), + } + } +} + +// TODO: Move this into the `Reflect` derive +macro_rules! impl_get_ownership { + ($name: ty) => { + impl $crate::func::args::GetOwnership for $name { + fn ownership() -> $crate::func::args::Ownership { + $crate::func::args::Ownership::Owned + } + } + + impl<'a> $crate::func::args::GetOwnership for &'a $name { + fn ownership() -> $crate::func::args::Ownership { + $crate::func::args::Ownership::Ref + } + } + + impl<'a> $crate::func::args::GetOwnership for &'a mut $name { + fn ownership() -> $crate::func::args::Ownership { + $crate::func::args::Ownership::Mut + } + } + }; +} + +pub(crate) use impl_get_ownership; + +impl_get_ownership!(()); +impl_get_ownership!(bool); +impl_get_ownership!(char); +impl_get_ownership!(f32); +impl_get_ownership!(f64); +impl_get_ownership!(i8); +impl_get_ownership!(i16); +impl_get_ownership!(i32); +impl_get_ownership!(i64); +impl_get_ownership!(i128); +impl_get_ownership!(isize); +impl_get_ownership!(u8); +impl_get_ownership!(u16); +impl_get_ownership!(u32); +impl_get_ownership!(u64); +impl_get_ownership!(u128); +impl_get_ownership!(usize); +impl_get_ownership!(String); +impl_get_ownership!(&'static str); diff --git a/crates/bevy_reflect/src/func/into.rs b/crates/bevy_reflect/src/func/into.rs index ed2da6a823a7d..839c379bc3183 100644 --- a/crates/bevy_reflect/src/func/into.rs +++ b/crates/bevy_reflect/src/func/into.rs @@ -17,7 +17,7 @@ macro_rules! impl_into_function { // === Owned Return === // impl<$($Arg,)* R, F> $crate::func::IntoFunction R> for F where - $($Arg: $crate::func::args::FromArg + $crate::TypePath,)* + $($Arg: $crate::func::args::FromArg + $crate::func::args::GetOwnership + $crate::TypePath,)* R: $crate::func::IntoReturn, F: FnMut($($Arg),*) -> R + 'static, F: for<'a> FnMut($($Arg::Item<'a>),*) -> R + 'static, @@ -46,10 +46,10 @@ macro_rules! impl_into_function { #[allow(unused_mut)] let mut _index = 0; vec![ - $($crate::func::args::ArgInfo::new::<$Arg>($crate::func::args::ArgId::Index({ + $($crate::func::args::ArgInfo::new::<$Arg>({ _index += 1; _index - 1 - })),)* + }),)* ] }) } @@ -59,8 +59,9 @@ macro_rules! impl_into_function { impl $crate::func::IntoFunction (R,)> for F where Receiver: $crate::Reflect + $crate::TypePath, + for<'a> &'a Receiver: $crate::func::args::GetOwnership, R: $crate::Reflect, - $($Arg: $crate::func::args::FromArg + $crate::TypePath,)* + $($Arg: $crate::func::args::FromArg + $crate::func::args::GetOwnership + $crate::TypePath,)* F: for<'a> FnMut(&'a Receiver, $($Arg),*) -> &'a R + 'static, F: for<'a> FnMut(&'a Receiver, $($Arg::Item<'a>),*) -> &'a R + 'static, { @@ -90,11 +91,11 @@ macro_rules! impl_into_function { #[allow(unused_mut)] let mut _index = 1; vec![ - $crate::func::args::ArgInfo::new::<&Receiver>($crate::func::args::ArgId::Index(0)), - $($crate::func::args::ArgInfo::new::<$Arg>($crate::func::args::ArgId::Index({ + $crate::func::args::ArgInfo::new::<&Receiver>(0), + $($crate::func::args::ArgInfo::new::<$Arg>({ _index += 1; _index - 1 - })),)* + }),)* ] }) } @@ -104,8 +105,9 @@ macro_rules! impl_into_function { impl $crate::func::IntoFunction (R,)> for F where Receiver: $crate::Reflect + $crate::TypePath, + for<'a> &'a mut Receiver: $crate::func::args::GetOwnership, R: $crate::Reflect, - $($Arg: $crate::func::args::FromArg + $crate::TypePath,)* + $($Arg: $crate::func::args::FromArg + $crate::func::args::GetOwnership + $crate::TypePath,)* F: for<'a> FnMut(&'a mut Receiver, $($Arg),*) -> &'a mut R + 'static, F: for<'a> FnMut(&'a mut Receiver, $($Arg::Item<'a>),*) -> &'a mut R + 'static, { @@ -135,11 +137,11 @@ macro_rules! impl_into_function { #[allow(unused_mut)] let mut _index = 1; vec![ - $crate::func::args::ArgInfo::new::<&mut Receiver>($crate::func::args::ArgId::Index(0)), - $($crate::func::args::ArgInfo::new::<$Arg>($crate::func::args::ArgId::Index({ + $crate::func::args::ArgInfo::new::<&mut Receiver>(0), + $($crate::func::args::ArgInfo::new::<$Arg>({ _index += 1; _index - 1 - })),)* + }),)* ] }) } diff --git a/crates/bevy_reflect/src/func/mod.rs b/crates/bevy_reflect/src/func/mod.rs index 5b59dbc469653..6043a3e84c404 100644 --- a/crates/bevy_reflect/src/func/mod.rs +++ b/crates/bevy_reflect/src/func/mod.rs @@ -51,6 +51,7 @@ mod tests { } crate::func::args::impl_from_arg!(Foo); + crate::func::args::impl_get_ownership!(Foo); crate::func::return_type::impl_into_return!(Foo); let foo_a = Foo(25); From 42f51e9a21b4dc706ac650d6ccdb031d71d8888f Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Sun, 28 Apr 2024 16:25:22 -0700 Subject: [PATCH 04/31] Add ReturnInfo --- crates/bevy_reflect/src/func/function.rs | 4 +- crates/bevy_reflect/src/func/info.rs | 38 +++++++++++- crates/bevy_reflect/src/func/into.rs | 78 +++++++++++++----------- 3 files changed, 81 insertions(+), 39 deletions(-) diff --git a/crates/bevy_reflect/src/func/function.rs b/crates/bevy_reflect/src/func/function.rs index 12b2bd2ee13fd..0bf7d0e97b766 100644 --- a/crates/bevy_reflect/src/func/function.rs +++ b/crates/bevy_reflect/src/func/function.rs @@ -24,10 +24,10 @@ impl Debug for Function { impl Function { pub fn new FnMut(ArgList<'a>, &FunctionInfo) -> FunctionResult<'a> + 'static>( func: F, - args: Vec, + info: FunctionInfo, ) -> Self { Self { - info: FunctionInfo::new(args), + info, func: Box::new(func), } } diff --git a/crates/bevy_reflect/src/func/info.rs b/crates/bevy_reflect/src/func/info.rs index d364e666bb16b..fae1e60c0cd17 100644 --- a/crates/bevy_reflect/src/func/info.rs +++ b/crates/bevy_reflect/src/func/info.rs @@ -1,15 +1,21 @@ -use crate::func::args::ArgInfo; +use crate::func::args::{ArgInfo, GetOwnership, Ownership}; +use crate::TypePath; use alloc::borrow::Cow; #[derive(Debug)] pub struct FunctionInfo { name: Option>, args: Vec, + return_info: ReturnInfo, } impl FunctionInfo { pub fn new(args: Vec) -> Self { - Self { name: None, args } + Self { + name: None, + args, + return_info: ReturnInfo::new::<()>(), + } } pub fn with_name(mut self, name: impl Into>) -> Self { @@ -22,7 +28,35 @@ impl FunctionInfo { self } + pub fn with_return_info(mut self, return_info: ReturnInfo) -> Self { + self.return_info = return_info; + self + } + pub fn args(&self) -> &[ArgInfo] { &self.args } } + +#[derive(Debug)] +pub struct ReturnInfo { + type_path: &'static str, + ownership: Ownership, +} + +impl ReturnInfo { + pub fn new() -> Self { + Self { + type_path: T::type_path(), + ownership: T::ownership(), + } + } + + pub fn type_path(&self) -> &'static str { + self.type_path + } + + pub fn ownership(&self) -> Ownership { + self.ownership + } +} diff --git a/crates/bevy_reflect/src/func/into.rs b/crates/bevy_reflect/src/func/into.rs index 839c379bc3183..6b6c881b3a432 100644 --- a/crates/bevy_reflect/src/func/into.rs +++ b/crates/bevy_reflect/src/func/into.rs @@ -18,13 +18,24 @@ macro_rules! impl_into_function { impl<$($Arg,)* R, F> $crate::func::IntoFunction R> for F where $($Arg: $crate::func::args::FromArg + $crate::func::args::GetOwnership + $crate::TypePath,)* - R: $crate::func::IntoReturn, + R: $crate::func::IntoReturn + $crate::func::args::GetOwnership + $crate::TypePath, F: FnMut($($Arg),*) -> R + 'static, F: for<'a> FnMut($($Arg::Item<'a>),*) -> R + 'static, { fn into_function(mut self) -> $crate::func::Function { const COUNT: usize = count_tts!($($Arg)*); + let info = $crate::func::FunctionInfo::new({ + #[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::Function::new(move |args, _info| { if args.len() != COUNT { return Err($crate::func::error::FuncError::ArgCount { @@ -42,16 +53,7 @@ macro_rules! impl_into_function { _info.args().get(_index - 1).expect("argument index out of bounds") })?,)*); Ok((self)($($arg,)*).into_return()) - }, { - #[allow(unused_mut)] - let mut _index = 0; - vec![ - $($crate::func::args::ArgInfo::new::<$Arg>({ - _index += 1; - _index - 1 - }),)* - ] - }) + }, info) } } @@ -60,7 +62,8 @@ macro_rules! impl_into_function { where Receiver: $crate::Reflect + $crate::TypePath, for<'a> &'a Receiver: $crate::func::args::GetOwnership, - R: $crate::Reflect, + 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 + 'static, F: for<'a> FnMut(&'a Receiver, $($Arg::Item<'a>),*) -> &'a R + 'static, @@ -68,6 +71,18 @@ macro_rules! impl_into_function { fn into_function(mut self) -> $crate::func::Function { const COUNT: usize = count_tts!(Receiver $($Arg)*); + let info = $crate::func::FunctionInfo::new({ + #[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::Function::new(move |args, _info| { if args.len() != COUNT { return Err($crate::func::error::FuncError::ArgCount { @@ -87,17 +102,7 @@ macro_rules! impl_into_function { _info.args().get(_index - 1).expect("argument index out of bounds") })?,)*); Ok($crate::func::Return::Ref((self)(receiver, $($arg,)*))) - }, { - #[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 - }),)* - ] - }) + }, info) } } @@ -106,7 +111,8 @@ macro_rules! impl_into_function { where Receiver: $crate::Reflect + $crate::TypePath, for<'a> &'a mut Receiver: $crate::func::args::GetOwnership, - R: $crate::Reflect, + 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 + 'static, F: for<'a> FnMut(&'a mut Receiver, $($Arg::Item<'a>),*) -> &'a mut R + 'static, @@ -114,6 +120,18 @@ macro_rules! impl_into_function { fn into_function(mut self) -> $crate::func::Function { const COUNT: usize = count_tts!(Receiver $($Arg)*); + let info = $crate::func::FunctionInfo::new({ + #[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::Function::new(move |args, _info| { if args.len() != COUNT { return Err($crate::func::error::FuncError::ArgCount { @@ -133,17 +151,7 @@ macro_rules! impl_into_function { _info.args().get(_index - 1).expect("argument index out of bounds") })?,)*); Ok($crate::func::Return::Mut((self)(receiver, $($arg,)*))) - }, { - #[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 - }),)* - ] - }) + }, info) } } }; From 277c3cbf7cdede1246e2668761804ae2533d8d76 Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Sun, 28 Apr 2024 16:59:57 -0700 Subject: [PATCH 05/31] Simplify Debug output for Function --- crates/bevy_reflect/src/func/function.rs | 18 +++++++++++++++--- crates/bevy_reflect/src/func/info.rs | 8 ++++++++ 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/crates/bevy_reflect/src/func/function.rs b/crates/bevy_reflect/src/func/function.rs index 0bf7d0e97b766..61083ac1339b3 100644 --- a/crates/bevy_reflect/src/func/function.rs +++ b/crates/bevy_reflect/src/func/function.rs @@ -15,9 +15,21 @@ pub struct Function { impl Debug for Function { fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { - f.debug_struct("Function") - .field("info", &self.info) - .finish() + let name = self.info.name().unwrap_or("_"); + write!(f, "Function(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})") } } diff --git a/crates/bevy_reflect/src/func/info.rs b/crates/bevy_reflect/src/func/info.rs index fae1e60c0cd17..4308809383bc6 100644 --- a/crates/bevy_reflect/src/func/info.rs +++ b/crates/bevy_reflect/src/func/info.rs @@ -33,9 +33,17 @@ impl FunctionInfo { self } + pub fn name(&self) -> Option<&str> { + self.name.as_deref() + } + pub fn args(&self) -> &[ArgInfo] { &self.args } + + pub fn return_info(&self) -> &ReturnInfo { + &self.return_info + } } #[derive(Debug)] From d274242400454e12dba806e207d671d90bab84ca Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Sun, 28 Apr 2024 21:37:06 -0700 Subject: [PATCH 06/31] Update FunctionInfo::new signature Removed the `args` parameter, opting fully into the `with_***` pattern --- crates/bevy_reflect/src/func/info.rs | 10 +++- crates/bevy_reflect/src/func/into.rs | 70 +++++++++++++++------------- 2 files changed, 46 insertions(+), 34 deletions(-) diff --git a/crates/bevy_reflect/src/func/info.rs b/crates/bevy_reflect/src/func/info.rs index 4308809383bc6..c524e3c79675e 100644 --- a/crates/bevy_reflect/src/func/info.rs +++ b/crates/bevy_reflect/src/func/info.rs @@ -10,10 +10,10 @@ pub struct FunctionInfo { } impl FunctionInfo { - pub fn new(args: Vec) -> Self { + pub fn new() -> Self { Self { name: None, - args, + args: Vec::new(), return_info: ReturnInfo::new::<()>(), } } @@ -46,6 +46,12 @@ impl FunctionInfo { } } +impl Default for FunctionInfo { + fn default() -> Self { + Self::new() + } +} + #[derive(Debug)] pub struct ReturnInfo { type_path: &'static str, diff --git a/crates/bevy_reflect/src/func/into.rs b/crates/bevy_reflect/src/func/into.rs index 6b6c881b3a432..a71b58c174601 100644 --- a/crates/bevy_reflect/src/func/into.rs +++ b/crates/bevy_reflect/src/func/into.rs @@ -25,16 +25,18 @@ macro_rules! impl_into_function { fn into_function(mut self) -> $crate::func::Function { const COUNT: usize = count_tts!($($Arg)*); - let info = $crate::func::FunctionInfo::new({ - #[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::()); + let info = $crate::func::FunctionInfo::new() + .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::Function::new(move |args, _info| { if args.len() != COUNT { @@ -71,17 +73,19 @@ macro_rules! impl_into_function { fn into_function(mut self) -> $crate::func::Function { const COUNT: usize = count_tts!(Receiver $($Arg)*); - let info = $crate::func::FunctionInfo::new({ - #[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>()); + let info = $crate::func::FunctionInfo::new() + .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::Function::new(move |args, _info| { if args.len() != COUNT { @@ -120,17 +124,19 @@ macro_rules! impl_into_function { fn into_function(mut self) -> $crate::func::Function { const COUNT: usize = count_tts!(Receiver $($Arg)*); - let info = $crate::func::FunctionInfo::new({ - #[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 info = $crate::func::FunctionInfo::new() + .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::Function::new(move |args, _info| { if args.len() != COUNT { From 6395ff5669a7b8e1fa8552fcd6f4ab83b8946f42 Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Sun, 28 Apr 2024 21:38:47 -0700 Subject: [PATCH 07/31] Add ArgList::new function --- crates/bevy_reflect/src/func/args/list.rs | 4 ++++ crates/bevy_reflect/src/func/mod.rs | 22 +++++++++++----------- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/crates/bevy_reflect/src/func/args/list.rs b/crates/bevy_reflect/src/func/args/list.rs index a4a32f41102a3..1d4845b11a1ad 100644 --- a/crates/bevy_reflect/src/func/args/list.rs +++ b/crates/bevy_reflect/src/func/args/list.rs @@ -5,6 +5,10 @@ use crate::Reflect; pub struct ArgList<'a>(Vec>); impl<'a> ArgList<'a> { + pub fn new() -> Self { + Self(Vec::new()) + } + pub fn push(mut self, arg: Arg<'a>) -> Self { self.0.push(arg); self diff --git a/crates/bevy_reflect/src/func/mod.rs b/crates/bevy_reflect/src/func/mod.rs index 6043a3e84c404..af832d8a5172e 100644 --- a/crates/bevy_reflect/src/func/mod.rs +++ b/crates/bevy_reflect/src/func/mod.rs @@ -26,7 +26,7 @@ mod tests { } let mut func = add.into_function(); - let args = ArgList::default().push_owned(25_i32).push_owned(75_i32); + 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)); } @@ -34,7 +34,7 @@ mod tests { #[test] fn should_create_dynamic_closure() { let mut func = (|a: i32, b: i32| a + b).into_function(); - let args = ArgList::default().push_owned(25_i32).push_owned(75_i32); + 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)); } @@ -58,7 +58,7 @@ mod tests { let foo_b = Foo(75); let mut func = Foo::add.into_function(); - let args = ArgList::default().push_ref(&foo_a).push_ref(&foo_b); + 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))); } @@ -70,7 +70,7 @@ mod tests { } let mut func = foo.into_function(); - let args = ArgList::default(); + let args = ArgList::new(); let result = func.call(args).unwrap().unwrap_owned(); assert_eq!( result.downcast_ref::(), @@ -83,7 +83,7 @@ mod tests { fn foo(_: i32) {} let mut func = foo.into_function(); - let args = ArgList::default().push_owned(123_i32); + let args = ArgList::new().push_owned(123_i32); let result = func.call(args).unwrap(); assert!(result.is_unit()); } @@ -96,7 +96,7 @@ mod tests { let value: i32 = 123; let mut func = foo.into_function(); - let args = ArgList::default() + let args = ArgList::new() .push_ref(&value) .push_owned(String::from("Hello, World!")) .push_ref(&true); @@ -112,7 +112,7 @@ mod tests { let mut value: i32 = 123; let mut func = foo.into_function(); - let args = ArgList::default() + let args = ArgList::new() .push_mut(&mut value) .push_owned(String::from("Hello, World!")) .push_ref(&true); @@ -125,7 +125,7 @@ mod tests { fn foo(_: i32) {} let mut func = foo.into_function(); - let args = ArgList::default(); + let args = ArgList::new(); let result = func.call(args); assert_eq!( result.unwrap_err(), @@ -141,7 +141,7 @@ mod tests { fn foo() {} let mut func = foo.into_function(); - let args = ArgList::default().push_owned(123_i32); + let args = ArgList::new().push_owned(123_i32); let result = func.call(args); assert_eq!( result.unwrap_err(), @@ -157,7 +157,7 @@ mod tests { fn foo(_: i32) {} let mut func = foo.into_function(); - let args = ArgList::default().push_owned(123_u32); + let args = ArgList::new().push_owned(123_u32); let result = func.call(args); assert_eq!( result.unwrap_err(), @@ -174,7 +174,7 @@ mod tests { fn foo(_: &i32) {} let mut func = foo.into_function(); - let args = ArgList::default().push_owned(123_i32); + let args = ArgList::new().push_owned(123_i32); let result = func.call(args); assert_eq!( result.unwrap_err(), From 4dd061c7e1b9460d8e88166aeb6492ab312ab016 Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Sun, 28 Apr 2024 22:46:28 -0700 Subject: [PATCH 08/31] Add documentation --- crates/bevy_reflect/src/func/args/arg.rs | 7 ++ crates/bevy_reflect/src/func/args/error.rs | 3 + crates/bevy_reflect/src/func/args/from_arg.rs | 8 ++ crates/bevy_reflect/src/func/args/info.rs | 27 +++++- crates/bevy_reflect/src/func/args/list.rs | 32 +++++++ crates/bevy_reflect/src/func/args/mod.rs | 4 + .../bevy_reflect/src/func/args/ownership.rs | 6 ++ crates/bevy_reflect/src/func/error.rs | 5 + crates/bevy_reflect/src/func/function.rs | 91 +++++++++++++++---- crates/bevy_reflect/src/func/info.rs | 23 +++++ crates/bevy_reflect/src/func/into.rs | 75 +++++++++++++++ crates/bevy_reflect/src/func/mod.rs | 35 +++++++ crates/bevy_reflect/src/func/return_type.rs | 25 +++++ 13 files changed, 317 insertions(+), 24 deletions(-) diff --git a/crates/bevy_reflect/src/func/args/arg.rs b/crates/bevy_reflect/src/func/args/arg.rs index 1b7fe455da5a6..ef0fbdabe93e9 100644 --- a/crates/bevy_reflect/src/func/args/arg.rs +++ b/crates/bevy_reflect/src/func/args/arg.rs @@ -1,6 +1,9 @@ use crate::func::args::{ArgError, ArgInfo, Ownership}; use crate::Reflect; +/// Represents an argument that can be passed to a dynamic [`Function`]. +/// +/// [`Function`]: crate::func::Function pub enum Arg<'a> { Owned(Box), Ref(&'a dyn Reflect), @@ -8,6 +11,7 @@ pub enum Arg<'a> { } impl<'a> Arg<'a> { + /// Returns `Ok(T)` if the argument is [`Arg::Owned`]. pub fn take_owned(self, info: &ArgInfo) -> Result { match self { Arg::Owned(arg) => arg.take().map_err(|arg| ArgError::UnexpectedType { @@ -27,6 +31,8 @@ impl<'a> Arg<'a> { }), } } + + /// Returns `Ok(&T)` if the argument is [`Arg::Ref`]. pub fn take_ref(self, info: &ArgInfo) -> Result<&'a T, ArgError> { match self { Arg::Owned(_) => Err(ArgError::InvalidOwnership { @@ -47,6 +53,7 @@ impl<'a> Arg<'a> { } } + /// Returns `Ok(&mut T)` if the argument is [`Arg::Mut`]. pub fn take_mut(self, info: &ArgInfo) -> Result<&'a mut T, ArgError> { match self { Arg::Owned(_) => Err(ArgError::InvalidOwnership { diff --git a/crates/bevy_reflect/src/func/args/error.rs b/crates/bevy_reflect/src/func/args/error.rs index 919fd118ebf38..e8473b6d3b4fc 100644 --- a/crates/bevy_reflect/src/func/args/error.rs +++ b/crates/bevy_reflect/src/func/args/error.rs @@ -4,14 +4,17 @@ use thiserror::Error; use crate::func::args::{ArgId, Ownership}; +/// An error that occurs when converting an argument. #[derive(Debug, Error, PartialEq)] pub enum ArgError { + /// The argument is not the expected type. #[error("expected `{expected}` but received `{received}` (@ {id:?})")] UnexpectedType { id: ArgId, expected: Cow<'static, str>, received: Cow<'static, str>, }, + /// The argument has the wrong ownership. #[error("expected {expected} value but received {received} value (@ {id:?})")] InvalidOwnership { id: ArgId, diff --git a/crates/bevy_reflect/src/func/args/from_arg.rs b/crates/bevy_reflect/src/func/args/from_arg.rs index cca53e27dc50c..5b0e5deaf29bd 100644 --- a/crates/bevy_reflect/src/func/args/from_arg.rs +++ b/crates/bevy_reflect/src/func/args/from_arg.rs @@ -1,7 +1,15 @@ use crate::func::args::{Arg, ArgError, ArgInfo}; +/// A trait for types that can be created from an [`Arg`]. pub trait FromArg { + /// The type of the item created from the argument. + /// + /// This should almost always be the same as `Self`, but with the lifetime `'a`. type Item<'a>; + + /// Creates an item from an argument. + /// + /// The argument must be of the expected type and ownership. fn from_arg<'a>(arg: Arg<'a>, info: &ArgInfo) -> Result, ArgError>; } diff --git a/crates/bevy_reflect/src/func/args/info.rs b/crates/bevy_reflect/src/func/args/info.rs index 7b4c37f6c720a..495f108d3ec2f 100644 --- a/crates/bevy_reflect/src/func/args/info.rs +++ b/crates/bevy_reflect/src/func/args/info.rs @@ -3,6 +3,9 @@ use alloc::borrow::Cow; use crate::func::args::{GetOwnership, Ownership}; use crate::TypePath; +/// Type information for an [`Arg`]. +/// +/// [`Arg`]: crate::func::args::Arg #[derive(Debug, Clone, PartialEq, Eq)] pub struct ArgInfo { index: usize, @@ -12,6 +15,7 @@ pub struct ArgInfo { } impl ArgInfo { + /// Create a new [`ArgInfo`] with the given argument index and type `T`. pub fn new(index: usize) -> Self { Self { index, @@ -21,37 +25,52 @@ impl ArgInfo { } } + /// Set the name of the argument. pub fn with_name(mut self, name: impl Into>) -> Self { self.name = Some(name.into()); self } + /// The index of the argument within its function. pub fn index(&self) -> usize { self.index } + /// The name of the argument, if it was given one. + /// + /// Note that this may return `None` even if the argument has a name. + /// 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. pub fn name(&self) -> Option<&str> { self.name.as_deref() } + /// The ownership of the argument. pub fn ownership(&self) -> Ownership { self.ownership } + pub fn type_path(&self) -> &'static str { + self.type_path + } + + /// Get an ID representing the argument. + /// + /// This will return `ArgId::Name` if the argument has a name, + /// otherwise `ArgId::Index`. pub fn id(&self) -> ArgId { self.name .clone() .map(ArgId::Name) .unwrap_or_else(|| ArgId::Index(self.index)) } - - pub fn type_path(&self) -> &'static str { - self.type_path - } } +/// A representation of an argument. #[derive(Debug, Clone, PartialEq, Eq)] pub enum ArgId { + /// The index of the argument within its function. Index(usize), + /// The name of the argument. Name(Cow<'static, str>), } diff --git a/crates/bevy_reflect/src/func/args/list.rs b/crates/bevy_reflect/src/func/args/list.rs index 1d4845b11a1ad..b3ae768bb3e6c 100644 --- a/crates/bevy_reflect/src/func/args/list.rs +++ b/crates/bevy_reflect/src/func/args/list.rs @@ -1,43 +1,75 @@ use crate::func::args::Arg; use crate::Reflect; +/// A list of arguments that can be passed to a dynamic [`Function`]. +/// +/// # Example +/// +/// ``` +/// # use bevy_reflect::func::{Arg, ArgList}; +/// let foo = 123; +/// let bar = 456; +/// let mut baz = 789; +/// let args = ArgList::new() +/// // Push an owned argument +/// .push_owned(foo) +/// // Push an owned and boxed argument +/// .push_boxed(Box::new(foo)) +/// // Push a reference argument +/// .push_ref(&bar) +/// // Push a mutable reference argument +/// .push_mut(&mut baz) +/// // Push a manually constructed argument +/// .push(Arg::Ref(&3.14)); +/// ``` +/// +/// [`Function`]: crate::func::Function #[derive(Default)] pub struct ArgList<'a>(Vec>); impl<'a> ArgList<'a> { + /// Create a new empty list of arguments. pub fn new() -> Self { Self(Vec::new()) } + /// Push an [`Arg`] onto the list. pub fn push(mut self, arg: Arg<'a>) -> Self { self.0.push(arg); self } + /// Push an [`Arg::Ref`] onto the list with the given reference. pub fn push_ref(self, arg: &'a dyn Reflect) -> Self { self.push(Arg::Ref(arg)) } + /// Push an [`Arg::Mut`] onto the list with the given mutable reference. pub fn push_mut(self, arg: &'a mut dyn Reflect) -> Self { self.push(Arg::Mut(arg)) } + /// Push an [`Arg::Owned`] onto the list with the given owned value. pub fn push_owned(self, arg: impl Reflect) -> Self { self.push(Arg::Owned(Box::new(arg))) } + /// Push an [`Arg::Owned`] onto the list with the given boxed value. pub fn push_boxed(self, arg: Box) -> Self { self.push(Arg::Owned(arg)) } + /// Returns the number of arguments in the list. pub fn len(&self) -> usize { self.0.len() } + /// Returns `true` if the list of arguments is empty. pub fn is_empty(&self) -> bool { self.0.is_empty() } + /// Take ownership of the list of arguments. pub fn take(self) -> Vec> { self.0 } diff --git a/crates/bevy_reflect/src/func/args/mod.rs b/crates/bevy_reflect/src/func/args/mod.rs index 21ed8b0a24604..61ee5ec54af5a 100644 --- a/crates/bevy_reflect/src/func/args/mod.rs +++ b/crates/bevy_reflect/src/func/args/mod.rs @@ -1,3 +1,7 @@ +//! Argument types and utilities for working with dynamic [`Functions`]. +//! +//! [`Functions`]: crate::func::Function + pub use arg::*; pub use error::*; pub use from_arg::*; diff --git a/crates/bevy_reflect/src/func/args/ownership.rs b/crates/bevy_reflect/src/func/args/ownership.rs index 6b08b4f3996ff..e20b614bcf1b2 100644 --- a/crates/bevy_reflect/src/func/args/ownership.rs +++ b/crates/bevy_reflect/src/func/args/ownership.rs @@ -1,13 +1,19 @@ use core::fmt::{Display, Formatter}; +/// A trait for getting the ownership of a type. pub trait GetOwnership { + /// Returns the ownership of [`Self`]. fn ownership() -> Ownership; } +/// The ownership of a type. #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum Ownership { + /// The type is a reference (i.e. `&T`). Ref, + /// The type is a mutable reference (i.e. `&mut T`). Mut, + /// The type is owned (i.e. `T`). Owned, } diff --git a/crates/bevy_reflect/src/func/error.rs b/crates/bevy_reflect/src/func/error.rs index 68307d35ae8d3..61c7280c94e34 100644 --- a/crates/bevy_reflect/src/func/error.rs +++ b/crates/bevy_reflect/src/func/error.rs @@ -1,10 +1,15 @@ use crate::func::args::ArgError; use thiserror::Error; +/// An error that occurs when calling a dynamic [`Function`]. +/// +/// [`Function`]: crate::func::Function #[derive(Debug, Error, PartialEq)] pub enum FuncError { + /// An error occurred while converting an argument. #[error(transparent)] Arg(#[from] ArgError), + /// The number of arguments provided does not match the expected number. #[error("expected {expected} arguments but received {received}")] ArgCount { expected: usize, received: usize }, } diff --git a/crates/bevy_reflect/src/func/function.rs b/crates/bevy_reflect/src/func/function.rs index 61083ac1339b3..4ba63ac59968e 100644 --- a/crates/bevy_reflect/src/func/function.rs +++ b/crates/bevy_reflect/src/func/function.rs @@ -2,38 +2,52 @@ use crate::func::args::{ArgInfo, ArgList}; use crate::func::error::FuncError; use crate::func::info::FunctionInfo; use crate::func::return_type::Return; +use crate::func::ReturnInfo; use alloc::borrow::Cow; use core::fmt::{Debug, Formatter}; use std::ops::DerefMut; +/// The result of calling a dynamic [`Function`]. +/// +/// Returns `Ok(value)` if the function was called successfully, +/// where `value` is the [`Return`] value of the function. pub type FunctionResult<'a> = Result, FuncError>; +/// A dynamic representation of a Rust function. +/// +/// Internally this stores a function pointer and associated info. +/// +/// You will generally not need to construct this manually. +/// Instead, many functions and closures can be automatically converted using the [`IntoFunction`] trait. +/// +/// # Example +/// +/// ``` +/// # use bevy_reflect::func::args::ArgList; +/// # use bevy_reflect::func::{Function, IntoFunction}; +/// fn add(a: i32, b: i32) -> i32 { +/// a + b +/// } +/// +/// let mut func: Function = add.into_function(); +/// let args = ArgList::default().push_owned(25_i32).push_owned(75_i32); +/// let result = func.call(args).unwrap().unwrap_owned(); +/// assert_eq!(result.downcast_ref::(), Some(&100)); +/// ``` +/// +/// [`IntoFunction`]: crate::func::IntoFunction pub struct Function { info: FunctionInfo, func: Box FnMut(ArgList<'a>, &FunctionInfo) -> FunctionResult<'a> + 'static>, } -impl Debug for Function { - fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { - let name = self.info.name().unwrap_or("_"); - write!(f, "Function(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 Function { + /// Create a new dynamic [`Function`]. + /// + /// 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> + 'static>( func: F, info: FunctionInfo, @@ -44,17 +58,54 @@ impl Function { } } + /// Set the name of the function. pub fn with_name(mut self, name: impl Into>) -> Self { self.info = self.info.with_name(name); self } + /// Set the arguments of the function. + /// + /// It is very important that the arguments match the intended function signature, + /// as this is used to validate arguments passed to the function. pub fn with_args(mut self, args: Vec) -> Self { self.info = self.info.with_args(args); self } + /// Set the return information of the function. + pub fn with_return_info(mut self, return_info: ReturnInfo) -> Self { + self.info = self.info.with_return_info(return_info); + self + } + + /// Call the function with the given arguments. pub fn call<'a>(&mut self, args: ArgList<'a>) -> FunctionResult<'a> { (self.func.deref_mut())(args, &self.info) } } + +/// Outputs the function signature. +/// +/// This takes the format: `Function(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 Debug for Function { + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + let name = self.info.name().unwrap_or("_"); + write!(f, "Function(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})") + } +} diff --git a/crates/bevy_reflect/src/func/info.rs b/crates/bevy_reflect/src/func/info.rs index c524e3c79675e..d0b6ea15b87a3 100644 --- a/crates/bevy_reflect/src/func/info.rs +++ b/crates/bevy_reflect/src/func/info.rs @@ -2,6 +2,9 @@ use crate::func::args::{ArgInfo, GetOwnership, Ownership}; use crate::TypePath; use alloc::borrow::Cow; +/// Type information for a [`Function`]. +/// +/// [`Function`]: crate::func::Function #[derive(Debug)] pub struct FunctionInfo { name: Option>, @@ -10,6 +13,7 @@ pub struct FunctionInfo { } impl FunctionInfo { + /// Create a new [`FunctionInfo`]. pub fn new() -> Self { Self { name: None, @@ -18,29 +22,42 @@ impl FunctionInfo { } } + /// Set the name of the function. pub fn with_name(mut self, name: impl Into>) -> Self { self.name = Some(name.into()); self } + /// Set the arguments of the function. + /// + /// It is very important that the arguments match the intended function signature, + /// as this is used to validate arguments passed to the function. pub fn with_args(mut self, args: Vec) -> Self { self.args = args; self } + /// Set the return information of the function. pub fn with_return_info(mut self, return_info: ReturnInfo) -> Self { self.return_info = return_info; self } + /// The name of the function, if it was given one. + /// + /// Note that this may return `None` even if the function has a name. + /// 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. pub fn name(&self) -> Option<&str> { self.name.as_deref() } + /// The arguments of the function. pub fn args(&self) -> &[ArgInfo] { &self.args } + /// The return information of the function. pub fn return_info(&self) -> &ReturnInfo { &self.return_info } @@ -52,6 +69,9 @@ impl Default for FunctionInfo { } } +/// Information about the return type of a [`Function`]. +/// +/// [`Function`]: crate::func::Function #[derive(Debug)] pub struct ReturnInfo { type_path: &'static str, @@ -59,6 +79,7 @@ pub struct ReturnInfo { } impl ReturnInfo { + /// Create a new [`ReturnInfo`] representing the given type, `T`. pub fn new() -> Self { Self { type_path: T::type_path(), @@ -66,10 +87,12 @@ impl ReturnInfo { } } + /// The type path of the return type. pub fn type_path(&self) -> &'static str { self.type_path } + /// The ownership of the return type. pub fn ownership(&self) -> Ownership { self.ownership } diff --git a/crates/bevy_reflect/src/func/into.rs b/crates/bevy_reflect/src/func/into.rs index a71b58c174601..881fe400991b6 100644 --- a/crates/bevy_reflect/src/func/into.rs +++ b/crates/bevy_reflect/src/func/into.rs @@ -1,7 +1,82 @@ use crate::func::function::Function; use bevy_utils::all_tuples; +/// A trait for types that can be converted into a [`Function`]. +/// +/// # 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). +/// 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, +/// ) { +/// // ... +/// } +/// +/// 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. +/// +/// If the return type is either an owned type or a static reference type, +/// then it must implement [`IntoReturn`]. +/// Otherwise, it must implement [`GetOwnership`], [`TypePath`], and [`Reflect`]. +/// +/// ``` +/// # 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(); +/// ``` +/// +/// [`FromArg`]: crate::func::args::FromArg +/// [`GetOwnership`]: crate::func::args::GetOwnership +/// [`TypePath`]: crate::TypePath +/// [`IntoReturn`]: crate::func::IntoReturn +/// [`Reflect`]: crate::Reflect pub trait IntoFunction { + /// Converts [`Self`] into a [`Function`]. fn into_function(self) -> Function; } diff --git a/crates/bevy_reflect/src/func/mod.rs b/crates/bevy_reflect/src/func/mod.rs index af832d8a5172e..b5b066c968dd8 100644 --- a/crates/bevy_reflect/src/func/mod.rs +++ b/crates/bevy_reflect/src/func/mod.rs @@ -1,9 +1,44 @@ +//! Reflection-based dynamic functions. +//! +//! This module provides a way to pass around and call functions dynamically +//! using the [`Function`] type. +//! +//! Many simple functions and closures can be automatically converted to [`Function`] +//! using the [`IntoFunction`] trait. +//! +//! Once the [`Function`] 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 +//! +//! ``` +//! # use bevy_reflect::func::args::ArgList; +//! # use bevy_reflect::func::{Function, FunctionResult, IntoFunction, Return}; +//! fn add(a: i32, b: i32) -> i32 { +//! a + b +//! } +//! +//! let mut func: Function = add.into_function(); +//! let args: ArgList = ArgList::default().push_owned(25_i32).push_owned(75_i32); +//! let result: FunctionResult = func.call(args); +//! let value: Return = result.unwrap(); +//! assert_eq!(value.unwrap_owned().downcast_ref::(), Some(&100)); +//! ``` +//! +//! [`Reflect`]: crate::Reflect + pub use error::*; pub use function::*; pub use info::*; pub use into::*; pub use return_type::*; +pub use args::{Arg, ArgError, ArgList}; + pub mod args; mod error; mod function; diff --git a/crates/bevy_reflect/src/func/return_type.rs b/crates/bevy_reflect/src/func/return_type.rs index 267cb4682f109..7d5e1bc4af412 100644 --- a/crates/bevy_reflect/src/func/return_type.rs +++ b/crates/bevy_reflect/src/func/return_type.rs @@ -1,18 +1,31 @@ use crate::Reflect; +/// The return type of a [`Function`]. +/// +/// [`Function`]: crate::func::Function #[derive(Debug)] pub enum Return<'a> { + /// The function returns nothing (i.e. it returns `()`). Unit, + /// The function returns an owned value. Owned(Box), + /// The function returns a reference to a value. Ref(&'a dyn Reflect), + /// The function returns a mutable reference to a value. Mut(&'a mut dyn Reflect), } impl<'a> Return<'a> { + /// Returns `true` if the return value is [`Self::Unit`]. pub fn is_unit(&self) -> bool { matches!(self, Return::Unit) } + /// Unwraps the return value as an owned value. + /// + /// # Panics + /// + /// Panics if the return value is not [`Self::Owned`]. pub fn unwrap_owned(self) -> Box { match self { Return::Owned(value) => value, @@ -20,6 +33,11 @@ impl<'a> Return<'a> { } } + /// Unwraps the return value as a reference to a value. + /// + /// # Panics + /// + /// Panics if the return value is not [`Self::Ref`]. pub fn unwrap_ref(self) -> &'a dyn Reflect { match self { Return::Ref(value) => value, @@ -27,6 +45,11 @@ impl<'a> Return<'a> { } } + /// Unwraps the return value as a mutable reference to a value. + /// + /// # Panics + /// + /// Panics if the return value is not [`Self::Mut`]. pub fn unwrap_mut(self) -> &'a mut dyn Reflect { match self { Return::Mut(value) => value, @@ -35,7 +58,9 @@ impl<'a> Return<'a> { } } +/// A trait for types that can be converted into a [`Return`] value. pub trait IntoReturn { + /// Converts [`Self`] into a [`Return`] value. fn into_return<'a>(self) -> Return<'a>; } From 83c4090275e0bfdff7640a5bd3b83259220e6205 Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Mon, 29 Apr 2024 14:43:37 -0700 Subject: [PATCH 09/31] Rename module into -> into_function --- .../bevy_reflect_derive/src/impls/func/from_arg.rs | 0 crates/bevy_reflect/bevy_reflect_derive/src/impls/func/mod.rs | 0 crates/bevy_reflect/src/func/{into.rs => into_function.rs} | 0 crates/bevy_reflect/src/func/mod.rs | 4 ++-- 4 files changed, 2 insertions(+), 2 deletions(-) create mode 100644 crates/bevy_reflect/bevy_reflect_derive/src/impls/func/from_arg.rs create mode 100644 crates/bevy_reflect/bevy_reflect_derive/src/impls/func/mod.rs rename crates/bevy_reflect/src/func/{into.rs => into_function.rs} (100%) diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/impls/func/from_arg.rs b/crates/bevy_reflect/bevy_reflect_derive/src/impls/func/from_arg.rs new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/impls/func/mod.rs b/crates/bevy_reflect/bevy_reflect_derive/src/impls/func/mod.rs new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/crates/bevy_reflect/src/func/into.rs b/crates/bevy_reflect/src/func/into_function.rs similarity index 100% rename from crates/bevy_reflect/src/func/into.rs rename to crates/bevy_reflect/src/func/into_function.rs diff --git a/crates/bevy_reflect/src/func/mod.rs b/crates/bevy_reflect/src/func/mod.rs index b5b066c968dd8..cdcbbebfafa37 100644 --- a/crates/bevy_reflect/src/func/mod.rs +++ b/crates/bevy_reflect/src/func/mod.rs @@ -34,7 +34,7 @@ pub use error::*; pub use function::*; pub use info::*; -pub use into::*; +pub use into_function::*; pub use return_type::*; pub use args::{Arg, ArgError, ArgList}; @@ -43,7 +43,7 @@ pub mod args; mod error; mod function; mod info; -mod into; +mod into_function; mod return_type; #[cfg(test)] From 73f07b9f50d54628ad2b3df86ecd3719f35ad510 Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Mon, 29 Apr 2024 21:31:11 -0700 Subject: [PATCH 10/31] Move local macros to derive macro --- .../src/impls/func/from_arg.rs | 0 .../bevy_reflect_derive/src/impls/func/mod.rs | 0 crates/bevy_reflect/derive/src/impls/enums.rs | 6 +- .../derive/src/impls/func/from_arg.rs | 47 ++++++++++ .../derive/src/impls/func/function_impls.rs | 23 +++++ .../derive/src/impls/func/get_ownership.rs | 34 +++++++ .../derive/src/impls/func/into_return.rs | 34 +++++++ .../bevy_reflect/derive/src/impls/func/mod.rs | 6 ++ crates/bevy_reflect/derive/src/impls/mod.rs | 5 +- .../bevy_reflect/derive/src/impls/structs.rs | 6 +- .../derive/src/impls/tuple_structs.rs | 6 +- .../bevy_reflect/derive/src/impls/values.rs | 6 +- crates/bevy_reflect/derive/src/utility.rs | 8 +- crates/bevy_reflect/src/array.rs | 2 + crates/bevy_reflect/src/enums/dynamic_enum.rs | 2 + crates/bevy_reflect/src/func/args/from_arg.rs | 92 +++++++++++-------- .../bevy_reflect/src/func/args/ownership.rs | 69 +++++++++----- crates/bevy_reflect/src/func/into_function.rs | 4 +- crates/bevy_reflect/src/func/macros.rs | 73 +++++++++++++++ crates/bevy_reflect/src/func/mod.rs | 5 +- crates/bevy_reflect/src/func/return_type.rs | 80 ++++++++++------ crates/bevy_reflect/src/impls/smallvec.rs | 23 +++-- crates/bevy_reflect/src/impls/std.rs | 36 ++++++++ crates/bevy_reflect/src/list.rs | 2 + crates/bevy_reflect/src/map.rs | 2 + crates/bevy_reflect/src/struct_trait.rs | 2 + crates/bevy_reflect/src/tuple.rs | 25 +++++ crates/bevy_reflect/src/tuple_struct.rs | 2 + 28 files changed, 485 insertions(+), 115 deletions(-) delete mode 100644 crates/bevy_reflect/bevy_reflect_derive/src/impls/func/from_arg.rs delete mode 100644 crates/bevy_reflect/bevy_reflect_derive/src/impls/func/mod.rs create mode 100644 crates/bevy_reflect/derive/src/impls/func/from_arg.rs create mode 100644 crates/bevy_reflect/derive/src/impls/func/function_impls.rs create mode 100644 crates/bevy_reflect/derive/src/impls/func/get_ownership.rs create mode 100644 crates/bevy_reflect/derive/src/impls/func/into_return.rs create mode 100644 crates/bevy_reflect/derive/src/impls/func/mod.rs create mode 100644 crates/bevy_reflect/src/func/macros.rs diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/impls/func/from_arg.rs b/crates/bevy_reflect/bevy_reflect_derive/src/impls/func/from_arg.rs deleted file mode 100644 index e69de29bb2d1d..0000000000000 diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/impls/func/mod.rs b/crates/bevy_reflect/bevy_reflect_derive/src/impls/func/mod.rs deleted file mode 100644 index e69de29bb2d1d..0000000000000 diff --git a/crates/bevy_reflect/derive/src/impls/enums.rs b/crates/bevy_reflect/derive/src/impls/enums.rs index 42162717a4670..2d479ac2b94e8 100644 --- a/crates/bevy_reflect/derive/src/impls/enums.rs +++ b/crates/bevy_reflect/derive/src/impls/enums.rs @@ -1,6 +1,6 @@ use crate::derive_data::{EnumVariantFields, ReflectEnum, StructField}; use crate::enum_utility::{EnumVariantOutputData, TryApplyVariantBuilder, VariantBuilder}; -use crate::impls::{impl_type_path, impl_typed}; +use crate::impls::{impl_function_traits, impl_type_path, impl_typed}; use bevy_macro_utils::fq_std::{FQAny, FQBox, FQOption, FQResult}; use proc_macro2::{Ident, Span}; use quote::quote; @@ -65,6 +65,8 @@ pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> proc_macro2::TokenStream let type_path_impl = impl_type_path(reflect_enum.meta()); + let function_impls = impl_function_traits(reflect_enum.meta(), &where_clause_options); + let get_type_registration_impl = reflect_enum.get_type_registration(&where_clause_options); let (impl_generics, ty_generics, where_clause) = @@ -79,6 +81,8 @@ pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> proc_macro2::TokenStream #type_path_impl + #function_impls + impl #impl_generics #bevy_reflect_path::Enum for #enum_path #ty_generics #where_reflect_clause { fn field(&self, #ref_name: &str) -> #FQOption<&dyn #bevy_reflect_path::Reflect> { match self { diff --git a/crates/bevy_reflect/derive/src/impls/func/from_arg.rs b/crates/bevy_reflect/derive/src/impls/func/from_arg.rs new file mode 100644 index 0000000000000..a52f3e2fb43fd --- /dev/null +++ b/crates/bevy_reflect/derive/src/impls/func/from_arg.rs @@ -0,0 +1,47 @@ +use crate::derive_data::ReflectMeta; +use crate::utility::WhereClauseOptions; +use bevy_macro_utils::fq_std::FQResult; +use quote::quote; + +pub(crate) fn impl_from_arg( + meta: &ReflectMeta, + where_clause_options: &WhereClauseOptions, +) -> proc_macro2::TokenStream { + let bevy_reflect = meta.bevy_reflect_path(); + let type_path = meta.type_path(); + + let (impl_generics, ty_generics, where_clause) = type_path.generics().split_for_impl(); + let where_reflect_clause = where_clause_options.extend_where_clause(where_clause); + + quote! { + impl #impl_generics #bevy_reflect::func::args::FromArg for #type_path #ty_generics #where_reflect_clause { + type Item<'from_arg> = #type_path #ty_generics; + fn from_arg<'from_arg>( + arg: #bevy_reflect::func::args::Arg<'from_arg>, + info: &#bevy_reflect::func::args::ArgInfo, + ) -> #FQResult, #bevy_reflect::func::args::ArgError> { + arg.take_owned(info) + } + } + + impl #impl_generics #bevy_reflect::func::args::FromArg for &'static #type_path #ty_generics #where_reflect_clause { + type Item<'from_arg> = &'from_arg #type_path #ty_generics; + fn from_arg<'from_arg>( + arg: #bevy_reflect::func::args::Arg<'from_arg>, + info: &#bevy_reflect::func::args::ArgInfo, + ) -> #FQResult, #bevy_reflect::func::args::ArgError> { + arg.take_ref(info) + } + } + + impl #impl_generics #bevy_reflect::func::args::FromArg for &'static mut #type_path #ty_generics #where_reflect_clause { + type Item<'from_arg> = &'from_arg mut #type_path #ty_generics; + fn from_arg<'from_arg>( + arg: #bevy_reflect::func::args::Arg<'from_arg>, + info: &#bevy_reflect::func::args::ArgInfo, + ) -> #FQResult, #bevy_reflect::func::args::ArgError> { + arg.take_mut(info) + } + } + } +} diff --git a/crates/bevy_reflect/derive/src/impls/func/function_impls.rs b/crates/bevy_reflect/derive/src/impls/func/function_impls.rs new file mode 100644 index 0000000000000..3b42203d41c00 --- /dev/null +++ b/crates/bevy_reflect/derive/src/impls/func/function_impls.rs @@ -0,0 +1,23 @@ +use crate::derive_data::ReflectMeta; +use crate::impls::func::from_arg::impl_from_arg; +use crate::impls::func::get_ownership::impl_get_ownership; +use crate::impls::func::into_return::impl_into_return; +use crate::utility::WhereClauseOptions; +use quote::quote; + +pub(crate) fn impl_function_traits( + meta: &ReflectMeta, + where_clause_options: &WhereClauseOptions, +) -> proc_macro2::TokenStream { + let get_ownership = impl_get_ownership(meta, where_clause_options); + let from_arg = impl_from_arg(meta, where_clause_options); + let into_return = impl_into_return(meta, where_clause_options); + + quote! { + #get_ownership + + #from_arg + + #into_return + } +} diff --git a/crates/bevy_reflect/derive/src/impls/func/get_ownership.rs b/crates/bevy_reflect/derive/src/impls/func/get_ownership.rs new file mode 100644 index 0000000000000..beec516078b03 --- /dev/null +++ b/crates/bevy_reflect/derive/src/impls/func/get_ownership.rs @@ -0,0 +1,34 @@ +use crate::derive_data::ReflectMeta; +use crate::utility::WhereClauseOptions; +use quote::quote; + +pub(crate) fn impl_get_ownership( + meta: &ReflectMeta, + where_clause_options: &WhereClauseOptions, +) -> proc_macro2::TokenStream { + let bevy_reflect = meta.bevy_reflect_path(); + let type_path = meta.type_path(); + + let (impl_generics, ty_generics, where_clause) = type_path.generics().split_for_impl(); + let where_reflect_clause = where_clause_options.extend_where_clause(where_clause); + + quote! { + impl #impl_generics #bevy_reflect::func::args::GetOwnership for #type_path #ty_generics #where_reflect_clause { + fn ownership() -> #bevy_reflect::func::args::Ownership { + #bevy_reflect::func::args::Ownership::Owned + } + } + + impl #impl_generics #bevy_reflect::func::args::GetOwnership for &'_ #type_path #ty_generics #where_reflect_clause { + fn ownership() -> #bevy_reflect::func::args::Ownership { + #bevy_reflect::func::args::Ownership::Ref + } + } + + impl #impl_generics #bevy_reflect::func::args::GetOwnership for &'_ mut #type_path #ty_generics #where_reflect_clause { + fn ownership() -> #bevy_reflect::func::args::Ownership { + #bevy_reflect::func::args::Ownership::Mut + } + } + } +} diff --git a/crates/bevy_reflect/derive/src/impls/func/into_return.rs b/crates/bevy_reflect/derive/src/impls/func/into_return.rs new file mode 100644 index 0000000000000..02c9fcf67ade1 --- /dev/null +++ b/crates/bevy_reflect/derive/src/impls/func/into_return.rs @@ -0,0 +1,34 @@ +use crate::derive_data::ReflectMeta; +use crate::utility::WhereClauseOptions; +use quote::quote; + +pub(crate) fn impl_into_return( + meta: &ReflectMeta, + where_clause_options: &WhereClauseOptions, +) -> proc_macro2::TokenStream { + let bevy_reflect = meta.bevy_reflect_path(); + let type_path = meta.type_path(); + + let (impl_generics, ty_generics, where_clause) = type_path.generics().split_for_impl(); + let where_reflect_clause = where_clause_options.extend_where_clause(where_clause); + + 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> { + #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> { + #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> { + #bevy_reflect::func::Return::Mut(self) + } + } + } +} diff --git a/crates/bevy_reflect/derive/src/impls/func/mod.rs b/crates/bevy_reflect/derive/src/impls/func/mod.rs new file mode 100644 index 0000000000000..b092366146e48 --- /dev/null +++ b/crates/bevy_reflect/derive/src/impls/func/mod.rs @@ -0,0 +1,6 @@ +pub(crate) use function_impls::impl_function_traits; + +mod from_arg; +mod function_impls; +mod get_ownership; +mod into_return; diff --git a/crates/bevy_reflect/derive/src/impls/mod.rs b/crates/bevy_reflect/derive/src/impls/mod.rs index db5418b446348..276101bf8bbb5 100644 --- a/crates/bevy_reflect/derive/src/impls/mod.rs +++ b/crates/bevy_reflect/derive/src/impls/mod.rs @@ -1,12 +1,13 @@ mod enums; +mod func; mod structs; mod tuple_structs; mod typed; mod values; pub(crate) use enums::impl_enum; +pub(crate) use func::impl_function_traits; pub(crate) use structs::impl_struct; pub(crate) use tuple_structs::impl_tuple_struct; -pub(crate) use typed::impl_type_path; -pub(crate) use typed::impl_typed; +pub(crate) use typed::{impl_type_path, impl_typed}; pub(crate) use values::impl_value; diff --git a/crates/bevy_reflect/derive/src/impls/structs.rs b/crates/bevy_reflect/derive/src/impls/structs.rs index 249ac22745b08..4899aed701565 100644 --- a/crates/bevy_reflect/derive/src/impls/structs.rs +++ b/crates/bevy_reflect/derive/src/impls/structs.rs @@ -1,4 +1,4 @@ -use crate::impls::{impl_type_path, impl_typed}; +use crate::impls::{impl_function_traits, impl_type_path, impl_typed}; use crate::utility::ident_or_index; use crate::ReflectStruct; use bevy_macro_utils::fq_std::{FQAny, FQBox, FQDefault, FQOption, FQResult}; @@ -54,6 +54,8 @@ pub(crate) fn impl_struct(reflect_struct: &ReflectStruct) -> proc_macro2::TokenS let type_path_impl = impl_type_path(reflect_struct.meta()); + let function_impls = impl_function_traits(reflect_struct.meta(), &where_clause_options); + let get_type_registration_impl = reflect_struct.get_type_registration(&where_clause_options); let (impl_generics, ty_generics, where_clause) = reflect_struct @@ -70,6 +72,8 @@ pub(crate) fn impl_struct(reflect_struct: &ReflectStruct) -> proc_macro2::TokenS #typed_impl #type_path_impl + + #function_impls impl #impl_generics #bevy_reflect_path::Struct for #struct_path #ty_generics #where_reflect_clause { fn field(&self, name: &str) -> #FQOption<&dyn #bevy_reflect_path::Reflect> { diff --git a/crates/bevy_reflect/derive/src/impls/tuple_structs.rs b/crates/bevy_reflect/derive/src/impls/tuple_structs.rs index cf43e5fe9874d..559f63343c9e7 100644 --- a/crates/bevy_reflect/derive/src/impls/tuple_structs.rs +++ b/crates/bevy_reflect/derive/src/impls/tuple_structs.rs @@ -1,4 +1,4 @@ -use crate::impls::{impl_type_path, impl_typed}; +use crate::impls::{impl_function_traits, impl_type_path, impl_typed}; use crate::ReflectStruct; use bevy_macro_utils::fq_std::{FQAny, FQBox, FQDefault, FQOption, FQResult}; use quote::{quote, ToTokens}; @@ -46,6 +46,8 @@ pub(crate) fn impl_tuple_struct(reflect_struct: &ReflectStruct) -> proc_macro2:: let type_path_impl = impl_type_path(reflect_struct.meta()); + let function_impls = impl_function_traits(reflect_struct.meta(), &where_clause_options); + let (impl_generics, ty_generics, where_clause) = reflect_struct .meta() .type_path() @@ -61,6 +63,8 @@ pub(crate) fn impl_tuple_struct(reflect_struct: &ReflectStruct) -> proc_macro2:: #type_path_impl + #function_impls + impl #impl_generics #bevy_reflect_path::TupleStruct for #struct_path #ty_generics #where_reflect_clause { fn field(&self, index: usize) -> #FQOption<&dyn #bevy_reflect_path::Reflect> { match index { diff --git a/crates/bevy_reflect/derive/src/impls/values.rs b/crates/bevy_reflect/derive/src/impls/values.rs index c0e7b2d4fee44..b7eedca2122a8 100644 --- a/crates/bevy_reflect/derive/src/impls/values.rs +++ b/crates/bevy_reflect/derive/src/impls/values.rs @@ -1,4 +1,4 @@ -use crate::impls::{impl_type_path, impl_typed}; +use crate::impls::{impl_function_traits, impl_type_path, impl_typed}; use crate::utility::WhereClauseOptions; use crate::ReflectMeta; use bevy_macro_utils::fq_std::{FQAny, FQBox, FQClone, FQOption, FQResult}; @@ -33,6 +33,8 @@ pub(crate) fn impl_value(meta: &ReflectMeta) -> proc_macro2::TokenStream { let type_path_impl = impl_type_path(meta); + let function_impls = impl_function_traits(meta, &where_clause_options); + let (impl_generics, ty_generics, where_clause) = type_path.generics().split_for_impl(); let where_reflect_clause = where_clause_options.extend_where_clause(where_clause); let get_type_registration_impl = meta.get_type_registration(&where_clause_options); @@ -44,6 +46,8 @@ pub(crate) fn impl_value(meta: &ReflectMeta) -> proc_macro2::TokenStream { #typed_impl + #function_impls + impl #impl_generics #bevy_reflect_path::Reflect for #type_path #ty_generics #where_reflect_clause { #[inline] fn get_represented_type_info(&self) -> #FQOption<&'static #bevy_reflect_path::TypeInfo> { diff --git a/crates/bevy_reflect/derive/src/utility.rs b/crates/bevy_reflect/derive/src/utility.rs index a32fcabeea640..a12a0a08d5cd8 100644 --- a/crates/bevy_reflect/derive/src/utility.rs +++ b/crates/bevy_reflect/derive/src/utility.rs @@ -154,13 +154,17 @@ impl<'a, 'b> WhereClauseOptions<'a, 'b> { &self, where_clause: Option<&WhereClause>, ) -> proc_macro2::TokenStream { + let type_path = self.meta.type_path(); + let (_, ty_generics, _) = self.meta.type_path().generics().split_for_impl(); + let required_bounds = self.required_bounds(); + // Maintain existing where clause, if any. let mut generic_where_clause = if let Some(where_clause) = where_clause { let predicates = where_clause.predicates.iter(); - quote! {where Self: #required_bounds, #(#predicates,)*} + quote! {where #type_path #ty_generics: #required_bounds, #(#predicates,)*} } else { - quote!(where Self: #required_bounds,) + quote!(where #type_path #ty_generics: #required_bounds,) }; // Add additional reflection trait bounds diff --git a/crates/bevy_reflect/src/array.rs b/crates/bevy_reflect/src/array.rs index f5f1158c20f11..c66b7b3b676db 100644 --- a/crates/bevy_reflect/src/array.rs +++ b/crates/bevy_reflect/src/array.rs @@ -1,3 +1,4 @@ +use crate::func::macros::impl_function_traits; use crate::{ self as bevy_reflect, utility::reflect_hasher, ApplyError, Reflect, ReflectKind, ReflectMut, ReflectOwned, ReflectRef, TypeInfo, TypePath, TypePathTable, @@ -358,6 +359,7 @@ impl Array for DynamicArray { } impl_type_path!((in bevy_reflect) DynamicArray); +impl_function_traits!(DynamicArray); /// An iterator over an [`Array`]. pub struct ArrayIter<'a> { array: &'a dyn Array, diff --git a/crates/bevy_reflect/src/enums/dynamic_enum.rs b/crates/bevy_reflect/src/enums/dynamic_enum.rs index 7d0bdccc1289b..9b3dea9601aa9 100644 --- a/crates/bevy_reflect/src/enums/dynamic_enum.rs +++ b/crates/bevy_reflect/src/enums/dynamic_enum.rs @@ -1,5 +1,6 @@ use bevy_reflect_derive::impl_type_path; +use crate::func::macros::impl_function_traits; use crate::{ self as bevy_reflect, enum_debug, enum_hash, enum_partial_eq, ApplyError, DynamicStruct, DynamicTuple, Enum, Reflect, ReflectKind, ReflectMut, ReflectOwned, ReflectRef, Struct, Tuple, @@ -427,3 +428,4 @@ impl Reflect for DynamicEnum { } impl_type_path!((in bevy_reflect) DynamicEnum); +impl_function_traits!(DynamicEnum); diff --git a/crates/bevy_reflect/src/func/args/from_arg.rs b/crates/bevy_reflect/src/func/args/from_arg.rs index 5b0e5deaf29bd..734dca671dd52 100644 --- a/crates/bevy_reflect/src/func/args/from_arg.rs +++ b/crates/bevy_reflect/src/func/args/from_arg.rs @@ -13,35 +13,74 @@ pub trait FromArg { fn from_arg<'a>(arg: Arg<'a>, info: &ArgInfo) -> Result, ArgError>; } -// TODO: Move this into the `Reflect` derive macro_rules! impl_from_arg { - ($name: ty) => { - impl $crate::func::args::FromArg for $name { - type Item<'a> = $name; - fn from_arg<'a>( - arg: $crate::func::args::Arg<'a>, + ( + $name: ty + $(; + < + $($T: ident $(: $T1: tt $(+ $T2: tt)*)?),* + > + )? + $( + [ + $(const $N: ident : $size: ident),* + ] + )? + $( + where + $($U: ty $(: $U1: tt $(+ $U2: tt)*)?),* + )? + ) => { + impl < + $($($T $(: $T1 $(+ $T2)*)?),*)? + $(, $(const $N : $size),*)? + > $crate::func::args::FromArg for $name + $( + where + $($U $(: $U1 $(+ $U2)*)?),* + )? + { + type Item<'from_arg> = $name; + fn from_arg<'from_arg>( + arg: $crate::func::args::Arg<'from_arg>, info: &$crate::func::args::ArgInfo, - ) -> Result, $crate::func::args::ArgError> { + ) -> Result, $crate::func::args::ArgError> { arg.take_owned(info) } } - impl $crate::func::args::FromArg for &'static $name { - type Item<'a> = &'a $name; - fn from_arg<'a>( - arg: $crate::func::args::Arg<'a>, + impl < + $($($T $(: $T1 $(+ $T2)*)?),*)? + $(, $(const $N : $size),*)? + > $crate::func::args::FromArg for &'static $name + $( + where + $($U $(: $U1 $(+ $U2)*)?),* + )? + { + type Item<'from_arg> = &'from_arg $name; + fn from_arg<'from_arg>( + arg: $crate::func::args::Arg<'from_arg>, info: &$crate::func::args::ArgInfo, - ) -> Result, $crate::func::args::ArgError> { + ) -> Result, $crate::func::args::ArgError> { arg.take_ref(info) } } - impl $crate::func::args::FromArg for &'static mut $name { - type Item<'a> = &'a mut $name; - fn from_arg<'a>( - arg: $crate::func::args::Arg<'a>, + impl < + $($($T $(: $T1 $(+ $T2)*)?),*)? + $(, $(const $N : $size),*)? + > $crate::func::args::FromArg for &'static mut $name + $( + where + $($U $(: $U1 $(+ $U2)*)?),* + )? + { + type Item<'from_arg> = &'from_arg mut $name; + fn from_arg<'from_arg>( + arg: $crate::func::args::Arg<'from_arg>, info: &$crate::func::args::ArgInfo, - ) -> Result, $crate::func::args::ArgError> { + ) -> Result, $crate::func::args::ArgError> { arg.take_mut(info) } } @@ -49,22 +88,3 @@ macro_rules! impl_from_arg { } pub(crate) use impl_from_arg; - -impl_from_arg!(bool); -impl_from_arg!(char); -impl_from_arg!(f32); -impl_from_arg!(f64); -impl_from_arg!(i8); -impl_from_arg!(i16); -impl_from_arg!(i32); -impl_from_arg!(i64); -impl_from_arg!(i128); -impl_from_arg!(isize); -impl_from_arg!(u8); -impl_from_arg!(u16); -impl_from_arg!(u32); -impl_from_arg!(u64); -impl_from_arg!(u128); -impl_from_arg!(usize); -impl_from_arg!(String); -impl_from_arg!(&'static str); diff --git a/crates/bevy_reflect/src/func/args/ownership.rs b/crates/bevy_reflect/src/func/args/ownership.rs index e20b614bcf1b2..77fb2214d3f3e 100644 --- a/crates/bevy_reflect/src/func/args/ownership.rs +++ b/crates/bevy_reflect/src/func/args/ownership.rs @@ -27,22 +27,61 @@ impl Display for Ownership { } } -// TODO: Move this into the `Reflect` derive macro_rules! impl_get_ownership { - ($name: ty) => { - impl $crate::func::args::GetOwnership for $name { + ( + $name: ty + $(; + < + $($T: ident $(: $T1: tt $(+ $T2: tt)*)?),* + > + )? + $( + [ + $(const $N: ident : $size: ident),* + ] + )? + $( + where + $($U: ty $(: $U1: tt $(+ $U2: tt)*)?),* + )? + ) => { + impl < + $($($T $(: $T1 $(+ $T2)*)?),*)? + $(, $(const $N : $size),*)? + > $crate::func::args::GetOwnership for $name + $( + where + $($U $(: $U1 $(+ $U2)*)?),* + )? + { fn ownership() -> $crate::func::args::Ownership { $crate::func::args::Ownership::Owned } } - impl<'a> $crate::func::args::GetOwnership for &'a $name { + impl < + $($($T $(: $T1 $(+ $T2)*)?),*)? + $(, $(const $N : $size),*)? + > $crate::func::args::GetOwnership for &'_ $name + $( + where + $($U $(: $U1 $(+ $U2)*)?),* + )? + { fn ownership() -> $crate::func::args::Ownership { $crate::func::args::Ownership::Ref } } - impl<'a> $crate::func::args::GetOwnership for &'a mut $name { + impl < + $($($T $(: $T1 $(+ $T2)*)?),*)? + $(, $(const $N : $size),*)? + > $crate::func::args::GetOwnership for &'_ mut $name + $( + where + $($U $(: $U1 $(+ $U2)*)?),* + )? + { fn ownership() -> $crate::func::args::Ownership { $crate::func::args::Ownership::Mut } @@ -51,23 +90,3 @@ macro_rules! impl_get_ownership { } pub(crate) use impl_get_ownership; - -impl_get_ownership!(()); -impl_get_ownership!(bool); -impl_get_ownership!(char); -impl_get_ownership!(f32); -impl_get_ownership!(f64); -impl_get_ownership!(i8); -impl_get_ownership!(i16); -impl_get_ownership!(i32); -impl_get_ownership!(i64); -impl_get_ownership!(i128); -impl_get_ownership!(isize); -impl_get_ownership!(u8); -impl_get_ownership!(u16); -impl_get_ownership!(u32); -impl_get_ownership!(u64); -impl_get_ownership!(u128); -impl_get_ownership!(usize); -impl_get_ownership!(String); -impl_get_ownership!(&'static str); diff --git a/crates/bevy_reflect/src/func/into_function.rs b/crates/bevy_reflect/src/func/into_function.rs index 881fe400991b6..9296bf101b188 100644 --- a/crates/bevy_reflect/src/func/into_function.rs +++ b/crates/bevy_reflect/src/func/into_function.rs @@ -135,7 +135,7 @@ macro_rules! impl_into_function { } // === Ref Return === // - impl $crate::func::IntoFunction (R,)> for F + impl $crate::func::IntoFunction fn(&R)> for F where Receiver: $crate::Reflect + $crate::TypePath, for<'a> &'a Receiver: $crate::func::args::GetOwnership, @@ -186,7 +186,7 @@ macro_rules! impl_into_function { } // === Mut Return === // - impl $crate::func::IntoFunction (R,)> for F + impl $crate::func::IntoFunction fn(&mut R)> for F where Receiver: $crate::Reflect + $crate::TypePath, for<'a> &'a mut Receiver: $crate::func::args::GetOwnership, diff --git a/crates/bevy_reflect/src/func/macros.rs b/crates/bevy_reflect/src/func/macros.rs new file mode 100644 index 0000000000000..60d0b297d370e --- /dev/null +++ b/crates/bevy_reflect/src/func/macros.rs @@ -0,0 +1,73 @@ +macro_rules! impl_function_traits { + ( + $name: ty + $(; + < + $($T: ident $(: $T1: tt $(+ $T2: tt)*)?),* + > + )? + $( + [ + $(const $N: ident : $size: ident),* + ] + )? + $( + where + $($U: ty $(: $U1: tt $(+ $U2: tt)*)?),* + )? + ) => { + $crate::func::args::impl_get_ownership!( + $name + $(; + < + $($T $(: $T1 $(+ $T2)*)?),* + > + )? + $( + [ + $(const $N : $size),* + ] + )? + $( + where + $($U $(: $U1 $(+ $U2)*)?),* + )? + ); + $crate::func::args::impl_from_arg!( + $name + $(; + < + $($T $(: $T1 $(+ $T2)*)?),* + > + )? + $( + [ + $(const $N : $size),* + ] + )? + $( + where + $($U $(: $U1 $(+ $U2)*)?),* + )? + ); + $crate::func::impl_into_return!( + $name + $(; + < + $($T $(: $T1 $(+ $T2)*)?),* + > + )? + $( + [ + $(const $N : $size),* + ] + )? + $( + where + $($U $(: $U1 $(+ $U2)*)?),* + )? + ); + }; +} + +pub(crate) use impl_function_traits; diff --git a/crates/bevy_reflect/src/func/mod.rs b/crates/bevy_reflect/src/func/mod.rs index cdcbbebfafa37..522755736bdcc 100644 --- a/crates/bevy_reflect/src/func/mod.rs +++ b/crates/bevy_reflect/src/func/mod.rs @@ -44,6 +44,7 @@ mod error; mod function; mod info; mod into_function; +pub(crate) mod macros; mod return_type; #[cfg(test)] @@ -85,10 +86,6 @@ mod tests { } } - crate::func::args::impl_from_arg!(Foo); - crate::func::args::impl_get_ownership!(Foo); - crate::func::return_type::impl_into_return!(Foo); - let foo_a = Foo(25); let foo_b = Foo(75); diff --git a/crates/bevy_reflect/src/func/return_type.rs b/crates/bevy_reflect/src/func/return_type.rs index 7d5e1bc4af412..33cea637193af 100644 --- a/crates/bevy_reflect/src/func/return_type.rs +++ b/crates/bevy_reflect/src/func/return_type.rs @@ -70,46 +70,66 @@ impl IntoReturn for () { } } -// TODO: Move this into the `Reflect` derive macro_rules! impl_into_return { - ($name: ty) => { - impl IntoReturn for $name { - fn into_return<'a>(self) -> Return<'a> { - Return::Owned(Box::new(self)) + ( + $name: ty + $(; + < + $($T: ident $(: $T1: tt $(+ $T2: tt)*)?),* + > + )? + $( + [ + $(const $N: ident : $size: ident),* + ] + )? + $( + where + $($U: ty $(: $U1: tt $(+ $U2: tt)*)?),* + )? + ) => { + impl < + $($($T $(: $T1 $(+ $T2)*)?),*)? + $(, $(const $N : $size),*)? + > $crate::func::IntoReturn for $name + $( + where + $($U $(: $U1 $(+ $U2)*)?),* + )? + { + fn into_return<'into_return>(self) -> $crate::func::Return<'into_return> { + $crate::func::Return::Owned(Box::new(self)) } } - impl IntoReturn for &'static $name { - fn into_return<'a>(self) -> Return<'a> { - Return::Ref(self) + impl < + $($($T $(: $T1 $(+ $T2)*)?),*)? + $(, $(const $N : $size),*)? + > $crate::func::IntoReturn for &'static $name + $( + where + $($U $(: $U1 $(+ $U2)*)?),* + )? + { + fn into_return<'into_return>(self) -> $crate::func::Return<'into_return> { + $crate::func::Return::Ref(self) } } - impl IntoReturn for &'static mut $name { - fn into_return<'a>(self) -> Return<'a> { - Return::Mut(self) + impl < + $($($T $(: $T1 $(+ $T2)*)?),*)? + $(, $(const $N : $size),*)? + > $crate::func::IntoReturn for &'static mut $name + $( + where + $($U $(: $U1 $(+ $U2)*)?),* + )? + { + fn into_return<'into_return>(self) -> $crate::func::Return<'into_return> { + $crate::func::Return::Mut(self) } } }; } pub(crate) use impl_into_return; - -impl_into_return!(bool); -impl_into_return!(char); -impl_into_return!(f32); -impl_into_return!(f64); -impl_into_return!(i8); -impl_into_return!(i16); -impl_into_return!(i32); -impl_into_return!(i64); -impl_into_return!(i128); -impl_into_return!(isize); -impl_into_return!(u8); -impl_into_return!(u16); -impl_into_return!(u32); -impl_into_return!(u64); -impl_into_return!(u128); -impl_into_return!(usize); -impl_into_return!(String); -impl_into_return!(&'static str); diff --git a/crates/bevy_reflect/src/impls/smallvec.rs b/crates/bevy_reflect/src/impls/smallvec.rs index 3943ac269bb86..00e0cc9544755 100644 --- a/crates/bevy_reflect/src/impls/smallvec.rs +++ b/crates/bevy_reflect/src/impls/smallvec.rs @@ -1,8 +1,9 @@ use bevy_reflect_derive::impl_type_path; -use smallvec::SmallVec; +use smallvec::{Array as SmallArray, SmallVec}; use std::any::Any; +use crate::func::macros::impl_function_traits; use crate::utility::GenericTypeInfoCell; use crate::{ self as bevy_reflect, ApplyError, FromReflect, FromType, GetTypeRegistration, List, ListInfo, @@ -10,7 +11,7 @@ use crate::{ TypePath, TypeRegistration, Typed, }; -impl List for SmallVec +impl List for SmallVec where T::Item: FromReflect + TypePath, { @@ -32,7 +33,7 @@ where fn insert(&mut self, index: usize, value: Box) { let value = value.take::().unwrap_or_else(|value| { - ::Item::from_reflect(&*value).unwrap_or_else(|| { + ::Item::from_reflect(&*value).unwrap_or_else(|| { panic!( "Attempted to insert invalid value of type {}.", value.reflect_type_path() @@ -48,7 +49,7 @@ where fn push(&mut self, value: Box) { let value = value.take::().unwrap_or_else(|value| { - ::Item::from_reflect(&*value).unwrap_or_else(|| { + ::Item::from_reflect(&*value).unwrap_or_else(|| { panic!( "Attempted to push invalid value of type {}.", value.reflect_type_path() @@ -77,7 +78,7 @@ where } } -impl Reflect for SmallVec +impl Reflect for SmallVec where T::Item: FromReflect + TypePath, { @@ -147,7 +148,7 @@ where } } -impl Typed for SmallVec +impl Typed for SmallVec where T::Item: FromReflect + TypePath, { @@ -157,9 +158,9 @@ where } } -impl_type_path!(::smallvec::SmallVec); +impl_type_path!(::smallvec::SmallVec); -impl FromReflect for SmallVec +impl FromReflect for SmallVec where T::Item: FromReflect + TypePath, { @@ -167,7 +168,7 @@ where if let ReflectRef::List(ref_list) = reflect.reflect_ref() { let mut new_list = Self::with_capacity(ref_list.len()); for field in ref_list.iter() { - new_list.push(::Item::from_reflect(field)?); + new_list.push(::Item::from_reflect(field)?); } Some(new_list) } else { @@ -176,7 +177,7 @@ where } } -impl GetTypeRegistration for SmallVec +impl GetTypeRegistration for SmallVec where T::Item: FromReflect + TypePath, { @@ -186,3 +187,5 @@ where registration } } + +impl_function_traits!(SmallVec; where T::Item: FromReflect + TypePath); diff --git a/crates/bevy_reflect/src/impls/std.rs b/crates/bevy_reflect/src/impls/std.rs index bcee1b51ee6e8..4c1a8284055a6 100644 --- a/crates/bevy_reflect/src/impls/std.rs +++ b/crates/bevy_reflect/src/impls/std.rs @@ -1,3 +1,4 @@ +use crate::func::macros::impl_function_traits; use crate::std_traits::ReflectDefault; use crate::utility::{ reflect_hasher, GenericTypeInfoCell, GenericTypePathCell, NonGenericTypeInfoCell, @@ -397,6 +398,8 @@ impl_reflect_for_veclike!( Vec::pop, [T] ); +impl_function_traits!(Vec; ); + impl_reflect_for_veclike!( ::alloc::collections::VecDeque, VecDeque::insert, @@ -405,6 +408,7 @@ impl_reflect_for_veclike!( VecDeque::pop_back, VecDeque:: ); +impl_function_traits!(VecDeque; ); macro_rules! impl_reflect_for_hashmap { ($ty:path) => { @@ -634,10 +638,24 @@ macro_rules! impl_reflect_for_hashmap { impl_reflect_for_hashmap!(::std::collections::HashMap); impl_type_path!(::std::collections::hash_map::RandomState); impl_type_path!(::std::collections::HashMap); +impl_function_traits!(::std::collections::HashMap; + < + K: FromReflect + TypePath + GetTypeRegistration + Eq + Hash, + V: FromReflect + TypePath + GetTypeRegistration, + S: TypePath + BuildHasher + Default + Send + Sync + > +); impl_reflect_for_hashmap!(bevy_utils::hashbrown::HashMap); impl_type_path!(::bevy_utils::hashbrown::hash_map::DefaultHashBuilder); impl_type_path!(::bevy_utils::hashbrown::HashMap); +impl_function_traits!(::bevy_utils::hashbrown::HashMap; + < + K: FromReflect + TypePath + GetTypeRegistration + Eq + Hash, + V: FromReflect + TypePath + GetTypeRegistration, + S: TypePath + BuildHasher + Default + Send + Sync + > +); impl Map for ::std::collections::BTreeMap where @@ -851,6 +869,12 @@ where } impl_type_path!(::std::collections::BTreeMap); +impl_function_traits!(::std::collections::BTreeMap; + < + K: FromReflect + TypePath + GetTypeRegistration + Eq + Ord, + V: FromReflect + TypePath + GetTypeRegistration + > +); impl Array for [T; N] { #[inline] @@ -1011,6 +1035,8 @@ impl GetTypeRegistr } } +impl_function_traits!([T; N]; [const N: usize]); + impl_reflect! { #[type_path = "core::option"] enum Option { @@ -1168,6 +1194,8 @@ impl FromReflect for Cow<'static, str> { } } +impl_function_traits!(Cow<'static, str>); + impl TypePath for [T] where [T]: ToOwned, @@ -1346,6 +1374,8 @@ impl FromReflect for Co } } +impl_function_traits!(Cow<'static, [T]>; ); + impl Reflect for &'static str { fn get_represented_type_info(&self) -> Option<&'static TypeInfo> { Some(::type_info()) @@ -1452,6 +1482,8 @@ impl FromReflect for &'static str { } } +impl_function_traits!(&'static str); + impl Reflect for &'static Path { fn get_represented_type_info(&self) -> Option<&'static TypeInfo> { Some(::type_info()) @@ -1557,6 +1589,8 @@ impl FromReflect for &'static Path { } } +impl_function_traits!(&'static Path); + impl Reflect for Cow<'static, Path> { fn get_represented_type_info(&self) -> Option<&'static TypeInfo> { Some(::type_info()) @@ -1672,6 +1706,8 @@ impl GetTypeRegistration for Cow<'static, Path> { } } +impl_function_traits!(Cow<'static, Path>); + #[cfg(test)] mod tests { use crate as bevy_reflect; diff --git a/crates/bevy_reflect/src/list.rs b/crates/bevy_reflect/src/list.rs index 5b786ee76a991..8b27eadefbb46 100644 --- a/crates/bevy_reflect/src/list.rs +++ b/crates/bevy_reflect/src/list.rs @@ -4,6 +4,7 @@ use std::hash::{Hash, Hasher}; use bevy_reflect_derive::impl_type_path; +use crate::func::macros::impl_function_traits; use crate::utility::reflect_hasher; use crate::{ self as bevy_reflect, ApplyError, FromReflect, Reflect, ReflectKind, ReflectMut, ReflectOwned, @@ -369,6 +370,7 @@ impl Reflect for DynamicList { } impl_type_path!((in bevy_reflect) DynamicList); +impl_function_traits!(DynamicList); impl Debug for DynamicList { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { diff --git a/crates/bevy_reflect/src/map.rs b/crates/bevy_reflect/src/map.rs index 5b182c68a973a..1c466d40e1a12 100644 --- a/crates/bevy_reflect/src/map.rs +++ b/crates/bevy_reflect/src/map.rs @@ -4,6 +4,7 @@ use std::fmt::{Debug, Formatter}; use bevy_reflect_derive::impl_type_path; use bevy_utils::{Entry, HashMap}; +use crate::func::macros::impl_function_traits; use crate::{ self as bevy_reflect, ApplyError, Reflect, ReflectKind, ReflectMut, ReflectOwned, ReflectRef, TypeInfo, TypePath, TypePathTable, @@ -391,6 +392,7 @@ impl Reflect for DynamicMap { } impl_type_path!((in bevy_reflect) DynamicMap); +impl_function_traits!(DynamicMap); impl Debug for DynamicMap { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { diff --git a/crates/bevy_reflect/src/struct_trait.rs b/crates/bevy_reflect/src/struct_trait.rs index 4585a4382fd6d..e088fe32190d7 100644 --- a/crates/bevy_reflect/src/struct_trait.rs +++ b/crates/bevy_reflect/src/struct_trait.rs @@ -1,4 +1,5 @@ use crate::attributes::{impl_custom_attribute_methods, CustomAttributes}; +use crate::func::macros::impl_function_traits; use crate::{ self as bevy_reflect, ApplyError, NamedField, Reflect, ReflectKind, ReflectMut, ReflectOwned, ReflectRef, TypeInfo, TypePath, TypePathTable, @@ -499,6 +500,7 @@ impl Reflect for DynamicStruct { } impl_type_path!((in bevy_reflect) DynamicStruct); +impl_function_traits!(DynamicStruct); impl Debug for DynamicStruct { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { diff --git a/crates/bevy_reflect/src/tuple.rs b/crates/bevy_reflect/src/tuple.rs index cf111edcdfb99..4e96d50f20351 100644 --- a/crates/bevy_reflect/src/tuple.rs +++ b/crates/bevy_reflect/src/tuple.rs @@ -710,6 +710,31 @@ macro_rules! impl_type_path_tuple { all_tuples!(impl_type_path_tuple, 0, 12, P); +macro_rules! impl_get_ownership_tuple { + ($($name: ident),*) => { + $crate::func::args::impl_get_ownership!(($($name,)*); <$($name),*>); + }; +} + +all_tuples!(impl_get_ownership_tuple, 0, 12, P); + +macro_rules! impl_from_arg_tuple { + ($($name: ident),*) => { + $crate::func::args::impl_from_arg!(($($name,)*); <$($name: FromReflect + TypePath + GetTypeRegistration),*>); + }; +} + +all_tuples!(impl_from_arg_tuple, 0, 12, P); + +macro_rules! impl_into_return_tuple { + ($($name: ident),+) => { + $crate::func::impl_into_return!(($($name,)*); <$($name: FromReflect + TypePath + GetTypeRegistration),*>); + }; +} + +// The unit type (i.e. `()`) is special-cased, so we skip implementing it here. +all_tuples!(impl_into_return_tuple, 1, 12, P); + #[cfg(test)] mod tests { use super::Tuple; diff --git a/crates/bevy_reflect/src/tuple_struct.rs b/crates/bevy_reflect/src/tuple_struct.rs index 56767cd0e106c..ac3a4d4877493 100644 --- a/crates/bevy_reflect/src/tuple_struct.rs +++ b/crates/bevy_reflect/src/tuple_struct.rs @@ -1,6 +1,7 @@ use bevy_reflect_derive::impl_type_path; use crate::attributes::{impl_custom_attribute_methods, CustomAttributes}; +use crate::func::macros::impl_function_traits; use crate::{ self as bevy_reflect, ApplyError, DynamicTuple, Reflect, ReflectKind, ReflectMut, ReflectOwned, ReflectRef, Tuple, TypeInfo, TypePath, TypePathTable, UnnamedField, @@ -408,6 +409,7 @@ impl Reflect for DynamicTupleStruct { } impl_type_path!((in bevy_reflect) DynamicTupleStruct); +impl_function_traits!(DynamicTupleStruct); impl Debug for DynamicTupleStruct { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { From 4c62a83669b5e9a3cc73fee2379981dd4c272975 Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Mon, 29 Apr 2024 21:53:07 -0700 Subject: [PATCH 11/31] Derive Debug on Arg and ArgList --- crates/bevy_reflect/src/func/args/arg.rs | 1 + crates/bevy_reflect/src/func/args/list.rs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/bevy_reflect/src/func/args/arg.rs b/crates/bevy_reflect/src/func/args/arg.rs index ef0fbdabe93e9..5304b0f9f1f5b 100644 --- a/crates/bevy_reflect/src/func/args/arg.rs +++ b/crates/bevy_reflect/src/func/args/arg.rs @@ -4,6 +4,7 @@ use crate::Reflect; /// Represents an argument that can be passed to a dynamic [`Function`]. /// /// [`Function`]: crate::func::Function +#[derive(Debug)] pub enum Arg<'a> { Owned(Box), Ref(&'a dyn Reflect), diff --git a/crates/bevy_reflect/src/func/args/list.rs b/crates/bevy_reflect/src/func/args/list.rs index b3ae768bb3e6c..62b6aff459244 100644 --- a/crates/bevy_reflect/src/func/args/list.rs +++ b/crates/bevy_reflect/src/func/args/list.rs @@ -24,7 +24,7 @@ use crate::Reflect; /// ``` /// /// [`Function`]: crate::func::Function -#[derive(Default)] +#[derive(Default, Debug)] pub struct ArgList<'a>(Vec>); impl<'a> ArgList<'a> { From d6ab4c0967ae5d6e1271e83bbc990d7f19c821fb Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Tue, 30 Apr 2024 11:40:11 -0700 Subject: [PATCH 12/31] Add lifetime to Function --- crates/bevy_reflect/src/func/function.rs | 45 ++++++++++++++++--- crates/bevy_reflect/src/func/into_function.rs | 28 ++++++------ 2 files changed, 54 insertions(+), 19 deletions(-) diff --git a/crates/bevy_reflect/src/func/function.rs b/crates/bevy_reflect/src/func/function.rs index 4ba63ac59968e..fcc94d778df06 100644 --- a/crates/bevy_reflect/src/func/function.rs +++ b/crates/bevy_reflect/src/func/function.rs @@ -36,19 +36,19 @@ pub type FunctionResult<'a> = Result, FuncError>; /// ``` /// /// [`IntoFunction`]: crate::func::IntoFunction -pub struct Function { +pub struct Function<'env> { info: FunctionInfo, - func: Box FnMut(ArgList<'a>, &FunctionInfo) -> FunctionResult<'a> + 'static>, + func: Box FnMut(ArgList<'a>, &FunctionInfo) -> FunctionResult<'a> + 'env>, } -impl Function { +impl<'env> Function<'env> { /// Create a new dynamic [`Function`]. /// /// 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> + 'static>( + pub fn new FnMut(ArgList<'a>, &FunctionInfo) -> FunctionResult<'a> + 'env>( func: F, info: FunctionInfo, ) -> Self { @@ -80,9 +80,44 @@ impl Function { } /// Call the function with the given arguments. + /// + /// # Example + /// + /// ``` + /// # use bevy_reflect::func::{IntoFunction, ArgList}; + /// fn add(left: i32, right: i32) -> i32 { + /// left + right + /// } + /// + /// 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.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. + /// + /// # 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); + /// increment_function.call_once(args).unwrap(); + /// assert_eq!(count, 5); + /// ``` + pub fn call_once<'a>(mut self, args: ArgList<'a>) -> FunctionResult<'a> { + (self.func.deref_mut())(args, &self.info) + } } /// Outputs the function signature. @@ -90,7 +125,7 @@ impl Function { /// This takes the format: `Function(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 Debug for Function { +impl<'env> Debug for Function<'env> { fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { let name = self.info.name().unwrap_or("_"); write!(f, "Function(fn {name}(")?; diff --git a/crates/bevy_reflect/src/func/into_function.rs b/crates/bevy_reflect/src/func/into_function.rs index 9296bf101b188..1eeef8263b38b 100644 --- a/crates/bevy_reflect/src/func/into_function.rs +++ b/crates/bevy_reflect/src/func/into_function.rs @@ -75,9 +75,9 @@ use bevy_utils::all_tuples; /// [`TypePath`]: crate::TypePath /// [`IntoReturn`]: crate::func::IntoReturn /// [`Reflect`]: crate::Reflect -pub trait IntoFunction { +pub trait IntoFunction<'env, T> { /// Converts [`Self`] into a [`Function`]. - fn into_function(self) -> Function; + fn into_function(self) -> Function<'env>; } // https://veykril.github.io/tlborm/decl-macros/building-blocks/counting.html#bit-twiddling @@ -90,14 +90,14 @@ macro_rules! count_tts { macro_rules! impl_into_function { ($(($Arg:ident, $arg:ident)),*) => { // === Owned Return === // - impl<$($Arg,)* R, F> $crate::func::IntoFunction R> for F + 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 + 'static, - F: for<'a> FnMut($($Arg::Item<'a>),*) -> R + 'static, + F: FnMut($($Arg),*) -> R + 'env, + F: for<'a> FnMut($($Arg::Item<'a>),*) -> R + 'env, { - fn into_function(mut self) -> $crate::func::Function { + fn into_function(mut self) -> $crate::func::Function<'env> { const COUNT: usize = count_tts!($($Arg)*); let info = $crate::func::FunctionInfo::new() @@ -135,17 +135,17 @@ macro_rules! impl_into_function { } // === Ref Return === // - impl $crate::func::IntoFunction fn(&R)> for F + 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 + 'static, - F: for<'a> FnMut(&'a Receiver, $($Arg::Item<'a>),*) -> &'a R + 'static, + 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::Function { + fn into_function(mut self) -> $crate::func::Function<'env> { const COUNT: usize = count_tts!(Receiver $($Arg)*); let info = $crate::func::FunctionInfo::new() @@ -186,17 +186,17 @@ macro_rules! impl_into_function { } // === Mut Return === // - impl $crate::func::IntoFunction fn(&mut R)> for F + 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 + 'static, - F: for<'a> FnMut(&'a mut Receiver, $($Arg::Item<'a>),*) -> &'a mut R + 'static, + 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::Function { + fn into_function(mut self) -> $crate::func::Function<'env> { const COUNT: usize = count_tts!(Receiver $($Arg)*); let info = $crate::func::FunctionInfo::new() From 33eefd6a38a0874510fd6dab4e89977b564a2caa Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Tue, 30 Apr 2024 11:42:24 -0700 Subject: [PATCH 13/31] Allow &mut receiver to return & data --- crates/bevy_reflect/src/func/into_function.rs | 55 ++++++++++++++++++- 1 file changed, 53 insertions(+), 2 deletions(-) diff --git a/crates/bevy_reflect/src/func/into_function.rs b/crates/bevy_reflect/src/func/into_function.rs index 1eeef8263b38b..b2a77c3ebb596 100644 --- a/crates/bevy_reflect/src/func/into_function.rs +++ b/crates/bevy_reflect/src/func/into_function.rs @@ -134,7 +134,7 @@ macro_rules! impl_into_function { } } - // === Ref Return === // + // === 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, @@ -185,7 +185,7 @@ macro_rules! impl_into_function { } } - // === Mut Return === // + // === 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, @@ -235,6 +235,57 @@ macro_rules! impl_into_function { }, info) } } + + // === 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::Function<'env> { + const COUNT: usize = count_tts!(Receiver $($Arg)*); + + let info = $crate::func::FunctionInfo::new() + .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::Function::new(move |args, _info| { + if args.len() != COUNT { + return Err($crate::func::error::FuncError::ArgCount { + expected: COUNT, + received: args.len(), + }); + } + + let [receiver, $($arg,)*] = args.take().try_into().ok().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::Ref((self)(receiver, $($arg,)*))) + }, info) + } + } }; } From 010121ea334afbad15f2abd646f467a496d20f31 Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Tue, 30 Apr 2024 12:02:53 -0700 Subject: [PATCH 14/31] Add function_reflection example --- Cargo.toml | 11 ++ crates/bevy_reflect/src/func/args/list.rs | 5 + examples/README.md | 1 + examples/reflection/function_reflection.rs | 156 +++++++++++++++++++++ 4 files changed, 173 insertions(+) create mode 100644 examples/reflection/function_reflection.rs diff --git a/Cargo.toml b/Cargo.toml index f5f95239f8dc4..77f034092ecc1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2089,6 +2089,17 @@ description = "How dynamic types are used with reflection" category = "Reflection" wasm = false +[[example]] +name = "function_reflection" +path = "examples/reflection/function_reflection.rs" +doc-scrape-examples = true + +[package.metadata.example.function_reflection] +name = "Function Reflection" +description = "Demonstrates how functions can be called dynamically using reflection" +category = "Reflection" +wasm = false + [[example]] name = "generic_reflection" path = "examples/reflection/generic_reflection.rs" diff --git a/crates/bevy_reflect/src/func/args/list.rs b/crates/bevy_reflect/src/func/args/list.rs index 62b6aff459244..30a283440301b 100644 --- a/crates/bevy_reflect/src/func/args/list.rs +++ b/crates/bevy_reflect/src/func/args/list.rs @@ -59,6 +59,11 @@ impl<'a> ArgList<'a> { self.push(Arg::Owned(arg)) } + /// Pop the last argument from the list, if there is one. + pub fn pop(&mut self) -> Option> { + self.0.pop() + } + /// Returns the number of arguments in the list. pub fn len(&self) -> usize { self.0.len() diff --git a/examples/README.md b/examples/README.md index 966f2552cecfe..7317199c45d47 100644 --- a/examples/README.md +++ b/examples/README.md @@ -325,6 +325,7 @@ Example | Description --- | --- [Custom Attributes](../examples/reflection/custom_attributes.rs) | Registering and accessing custom attributes on reflected types [Dynamic Types](../examples/reflection/dynamic_types.rs) | How dynamic types are used with reflection +[Function Reflection](../examples/reflection/function_reflection.rs) | Demonstrates how functions can be called dynamically using reflection [Generic Reflection](../examples/reflection/generic_reflection.rs) | Registers concrete instances of generic types that may be used with reflection [Reflection](../examples/reflection/reflection.rs) | Demonstrates how reflection in Bevy provides a way to dynamically interact with Rust types [Reflection Types](../examples/reflection/reflection_types.rs) | Illustrates the various reflection types available diff --git a/examples/reflection/function_reflection.rs b/examples/reflection/function_reflection.rs new file mode 100644 index 0000000000000..68b41a050ef5c --- /dev/null +++ b/examples/reflection/function_reflection.rs @@ -0,0 +1,156 @@ +//! This example demonstrates how functions can be called dynamically using reflection. + +use bevy::reflect::func::args::ArgInfo; +use bevy::reflect::func::{ArgList, Function, FunctionInfo, IntoFunction, Return, ReturnInfo}; +use bevy::reflect::Reflect; + +// Note that the `dbg!` invocations are used purely for reference purposes +// and are not strictly necessary for the example to work. +fn main() { + // There are times when it may be helpful to store a function away for later. + // In Rust, we can do this by storing either a function pointer or a function trait object. + // For example, say we wanted to store the following function: + fn add(left: i32, right: i32) -> i32 { + left + right + } + + // We could store it as either of the following: + let fn_pointer: fn(i32, i32) -> i32 = add; + let fn_trait_object: Box i32> = Box::new(add); + + // And we can call them like so: + let result = fn_pointer(2, 2); + assert_eq!(result, 4); + let result = fn_trait_object(2, 2); + assert_eq!(result, 4); + + // However, you'll notice that we have to know the types of the arguments and return value at compile time. + // This means there's not really a way to store or call these functions dynamically at runtime. + // 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 `Function` type + // using the `IntoFunction` trait. + let mut function: Function = dbg!(add.into_function()); + + // This time, you'll notice that `Function` doesn't take any information about the function's arguments or return value. + // This is because `Function` checks the types of the arguments and return value at runtime. + // Now we can generate a list of arguments: + let args: ArgList = dbg!(ArgList::new().push_owned(2_i32).push_owned(2_i32)); + + // And finally, we can call the function. + // This returns a `Result` indicating whether the function was called successfully. + // For now, we'll just unwrap it to get our `Return` value, + // which is an enum containing the function's return value. + let result: Return = dbg!(function.call(args).unwrap()); + + // The `Return` value can be pattern matched or unwrapped to get the underlying reflection data. + // For the sake of brevity, we'll just unwrap it here. + let result: Box = result.unwrap_owned(); + assert_eq!(result.take::().unwrap(), 4); + + // The same can also be done for closures. + let mut count = 0; + let increment = |amount: i32| { + count += amount; + }; + let increment_function: Function = dbg!(increment.into_function()); + let args = dbg!(ArgList::new().push_owned(5_i32)); + // Functions 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()); + assert_eq!(count, 5); + + // All closures must be `'static`— that is, they take full ownership of any captured variables. + let add_closure = |left: i32, right: i32| -> i32 { left + right }; + let mut count_function = dbg!(add_closure.into_function()); + let args = dbg!(ArgList::new().push_owned(2_i32).push_owned(2_i32)); + let result = dbg!(count_function.call(args).unwrap()).unwrap_owned(); + assert_eq!(result.take::().unwrap(), 4); + + // 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. + // Additionally, the lifetime of the return value is tied to the lifetime of the first argument. + // However, this means that many methods are also supported: + #[derive(Reflect, Default)] + struct Data { + value: String, + } + + impl Data { + fn set_value(&mut self, value: String) { + self.value = value; + } + + // Note that only `&'static str` implements `Reflect`. + // To get around this limitation we can use `&String` instead. + fn get_value(&self) -> &String { + &self.value + } + } + + let mut data = Data::default(); + + let mut 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 args = dbg!(ArgList::new().push_ref(&data)); + let result = dbg!(get_value.call(args).unwrap()); + let result: &dyn Reflect = result.unwrap_ref(); + assert_eq!(result.downcast_ref::().unwrap(), "Hello, world!"); + + // Lastly, for more complex use cases, you can always create a custom `Function` manually. + // This is useful for functions that can't be converted via the `IntoFunction` trait. + // For example, this function doesn't implement `IntoFunction` due to the fact that + // the lifetime of the return value is not tied to the lifetime of the first argument. + fn get_or_insert(value: i32, container: &mut Option) -> &i32 { + if container.is_none() { + *container = Some(value); + } + + container.as_ref().unwrap() + } + + let mut get_or_insert_function = dbg!(Function::new( + |mut args, info| { + let container_info = &info.args()[1]; + let value_info = &info.args()[0]; + + // The `ArgList` contains the arguments in the order they were pushed. + // Therefore, we need to pop them in reverse order. + let container = args + .pop() + .unwrap() + .take_mut::>(container_info) + .unwrap(); + let value = args.pop().unwrap().take_owned::(value_info).unwrap(); + + Ok(Return::Ref(get_or_insert(value, container))) + }, + FunctionInfo::new() + // We can optionally provide a name for the function + .with_name("get_or_insert") + // Since our function takes arguments, we MUST provide that argument information. + // The arguments should be provided in the order they are defined in the function. + // This is used to validate any arguments given at runtime. + .with_args(vec![ + ArgInfo::new::(0).with_name("value"), + ArgInfo::new::<&mut Option>(1).with_name("container"), + ]) + // We can optionally provide return information as well. + .with_return_info(ReturnInfo::new::<&i32>()), + )); + + let mut container: Option = None; + + let args = dbg!(ArgList::new().push_owned(5_i32).push_mut(&mut container)); + let result = dbg!(get_or_insert_function.call(args).unwrap()).unwrap_ref(); + assert_eq!(result.downcast_ref::(), Some(&5)); + + let args = dbg!(ArgList::new().push_owned(500_i32).push_mut(&mut container)); + let result = dbg!(get_or_insert_function.call(args).unwrap()).unwrap_ref(); + assert_eq!(result.downcast_ref::(), Some(&5)); +} From f0b575d9745046bbcfcdd53fec96111d3936d90c Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Tue, 30 Apr 2024 13:16:39 -0700 Subject: [PATCH 15/31] Fix formatting and lints --- crates/bevy_reflect/derive/src/impls/func/get_ownership.rs | 2 +- crates/bevy_reflect/derive/src/impls/structs.rs | 2 +- crates/bevy_reflect/src/func/function.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/bevy_reflect/derive/src/impls/func/get_ownership.rs b/crates/bevy_reflect/derive/src/impls/func/get_ownership.rs index beec516078b03..65f75c7a64e86 100644 --- a/crates/bevy_reflect/derive/src/impls/func/get_ownership.rs +++ b/crates/bevy_reflect/derive/src/impls/func/get_ownership.rs @@ -8,7 +8,7 @@ pub(crate) fn impl_get_ownership( ) -> proc_macro2::TokenStream { let bevy_reflect = meta.bevy_reflect_path(); let type_path = meta.type_path(); - + let (impl_generics, ty_generics, where_clause) = type_path.generics().split_for_impl(); let where_reflect_clause = where_clause_options.extend_where_clause(where_clause); diff --git a/crates/bevy_reflect/derive/src/impls/structs.rs b/crates/bevy_reflect/derive/src/impls/structs.rs index 4899aed701565..e9e555f662ce6 100644 --- a/crates/bevy_reflect/derive/src/impls/structs.rs +++ b/crates/bevy_reflect/derive/src/impls/structs.rs @@ -72,7 +72,7 @@ pub(crate) fn impl_struct(reflect_struct: &ReflectStruct) -> proc_macro2::TokenS #typed_impl #type_path_impl - + #function_impls impl #impl_generics #bevy_reflect_path::Struct for #struct_path #ty_generics #where_reflect_clause { diff --git a/crates/bevy_reflect/src/func/function.rs b/crates/bevy_reflect/src/func/function.rs index fcc94d778df06..c0c0bfe9cd712 100644 --- a/crates/bevy_reflect/src/func/function.rs +++ b/crates/bevy_reflect/src/func/function.rs @@ -115,7 +115,7 @@ impl<'env> Function<'env> { /// increment_function.call_once(args).unwrap(); /// assert_eq!(count, 5); /// ``` - pub fn call_once<'a>(mut self, args: ArgList<'a>) -> FunctionResult<'a> { + pub fn call_once(mut self, args: ArgList) -> FunctionResult { (self.func.deref_mut())(args, &self.info) } } From 6952bceb1a41f7dcf27ea0d04479f53b898e527f Mon Sep 17 00:00:00 2001 From: Gino Valente <49806985+MrGVSV@users.noreply.github.com> Date: Sun, 5 May 2024 11:05:06 -0700 Subject: [PATCH 16/31] Apply suggestions from code review Co-authored-by: Periwink --- crates/bevy_reflect/src/func/args/info.rs | 2 +- crates/bevy_reflect/src/func/args/list.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/bevy_reflect/src/func/args/info.rs b/crates/bevy_reflect/src/func/args/info.rs index 495f108d3ec2f..a33dcdffea986 100644 --- a/crates/bevy_reflect/src/func/args/info.rs +++ b/crates/bevy_reflect/src/func/args/info.rs @@ -3,7 +3,7 @@ use alloc::borrow::Cow; use crate::func::args::{GetOwnership, Ownership}; use crate::TypePath; -/// Type information for an [`Arg`]. +/// Type information for an [`Arg`] used in a [`Function`](super::function::Function) /// /// [`Arg`]: crate::func::args::Arg #[derive(Debug, Clone, PartialEq, Eq)] diff --git a/crates/bevy_reflect/src/func/args/list.rs b/crates/bevy_reflect/src/func/args/list.rs index 30a283440301b..8d47705cc6553 100644 --- a/crates/bevy_reflect/src/func/args/list.rs +++ b/crates/bevy_reflect/src/func/args/list.rs @@ -13,7 +13,7 @@ use crate::Reflect; /// let args = ArgList::new() /// // Push an owned argument /// .push_owned(foo) -/// // Push an owned and boxed argument +/// // Push an owned and boxed argument /// .push_boxed(Box::new(foo)) /// // Push a reference argument /// .push_ref(&bar) From e51ac2c7e346a6c79a3cdcb276a37c96cf78943f Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Sun, 5 May 2024 11:18:36 -0700 Subject: [PATCH 17/31] Update docs --- crates/bevy_reflect/src/func/function.rs | 5 ++++- crates/bevy_reflect/src/func/into_function.rs | 12 +++++++++--- crates/bevy_reflect/src/func/mod.rs | 7 ++++++- 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/crates/bevy_reflect/src/func/function.rs b/crates/bevy_reflect/src/func/function.rs index c0c0bfe9cd712..eddae01db4072 100644 --- a/crates/bevy_reflect/src/func/function.rs +++ b/crates/bevy_reflect/src/func/function.rs @@ -100,7 +100,8 @@ impl<'env> Function<'env> { /// Call the function with the given arguments and consume the function. /// - /// This is useful for closures that capture their environment. + /// This is useful for closures that capture their environment because otherwise + /// any captured variables would still be borrowed by this function. /// /// # Example /// @@ -112,6 +113,8 @@ impl<'env> Function<'env> { /// }; /// 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); /// ``` diff --git a/crates/bevy_reflect/src/func/into_function.rs b/crates/bevy_reflect/src/func/into_function.rs index b2a77c3ebb596..bde0178d46c77 100644 --- a/crates/bevy_reflect/src/func/into_function.rs +++ b/crates/bevy_reflect/src/func/into_function.rs @@ -38,6 +38,7 @@ use bevy_utils::all_tuples; /// // ... /// } /// +/// // This will fail to compile: /// too_many_args.into_function(); /// ``` /// @@ -53,9 +54,13 @@ use bevy_utils::all_tuples; /// - If the first argument is an owned type, /// then the return type may be either an owned type or a static reference type. /// -/// If the return type is either an owned type or a static reference type, -/// then it must implement [`IntoReturn`]. -/// Otherwise, it must implement [`GetOwnership`], [`TypePath`], and [`Reflect`]. +/// 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`]. +/// +/// Note that both `GetOwnership`, `TypePath`, and `IntoReturn` are automatically implemented +/// when [deriving `Reflect`]. /// /// ``` /// # use bevy_reflect::func::IntoFunction; @@ -75,6 +80,7 @@ use bevy_utils::all_tuples; /// [`TypePath`]: crate::TypePath /// [`IntoReturn`]: crate::func::IntoReturn /// [`Reflect`]: crate::Reflect +/// [deriving `Reflect`]: derive@crate::Reflect pub trait IntoFunction<'env, T> { /// Converts [`Self`] into a [`Function`]. fn into_function(self) -> Function<'env>; diff --git a/crates/bevy_reflect/src/func/mod.rs b/crates/bevy_reflect/src/func/mod.rs index 522755736bdcc..ae6f2d4b9c84d 100644 --- a/crates/bevy_reflect/src/func/mod.rs +++ b/crates/bevy_reflect/src/func/mod.rs @@ -16,6 +16,7 @@ //! # Example //! //! ``` +//! # use bevy_reflect::Reflect; //! # use bevy_reflect::func::args::ArgList; //! # use bevy_reflect::func::{Function, FunctionResult, IntoFunction, Return}; //! fn add(a: i32, b: i32) -> i32 { @@ -23,7 +24,11 @@ //! } //! //! let mut func: Function = add.into_function(); -//! let args: ArgList = ArgList::default().push_owned(25_i32).push_owned(75_i32); +//! let args: ArgList = ArgList::default() +//! // Pushing a known type with owned ownership +//! .push_owned(25_i32) +//! // Pushing a reflected type with owned ownership +//! .push_boxed(Box::new(75_i32) as Box); //! let result: FunctionResult = func.call(args); //! let value: Return = result.unwrap(); //! assert_eq!(value.unwrap_owned().downcast_ref::(), Some(&100)); From a70dd8a72fb78135d9bbfb093246ecbb6d8c9143 Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Sun, 5 May 2024 12:13:10 -0700 Subject: [PATCH 18/31] Add some basic compile fail tests --- crates/bevy_reflect/compile_fail/Cargo.toml | 4 ++ .../bevy_reflect/compile_fail/tests/derive.rs | 3 +- .../bevy_reflect/compile_fail/tests/func.rs | 3 ++ .../tests/into_function/arguments_fail.rs | 40 +++++++++++++++++++ .../tests/into_function/return_fail.rs | 34 ++++++++++++++++ 5 files changed, 83 insertions(+), 1 deletion(-) create mode 100644 crates/bevy_reflect/compile_fail/tests/func.rs create mode 100644 crates/bevy_reflect/compile_fail/tests/into_function/arguments_fail.rs create mode 100644 crates/bevy_reflect/compile_fail/tests/into_function/return_fail.rs diff --git a/crates/bevy_reflect/compile_fail/Cargo.toml b/crates/bevy_reflect/compile_fail/Cargo.toml index 2e8d542e2a8e4..dd1a6ef66c304 100644 --- a/crates/bevy_reflect/compile_fail/Cargo.toml +++ b/crates/bevy_reflect/compile_fail/Cargo.toml @@ -16,3 +16,7 @@ compile_fail_utils = { path = "../../../tools/compile_fail_utils" } [[test]] name = "derive" harness = false + +[[test]] +name = "func" +harness = false diff --git a/crates/bevy_reflect/compile_fail/tests/derive.rs b/crates/bevy_reflect/compile_fail/tests/derive.rs index 1b1922254ceed..366509084b213 100644 --- a/crates/bevy_reflect/compile_fail/tests/derive.rs +++ b/crates/bevy_reflect/compile_fail/tests/derive.rs @@ -1,3 +1,4 @@ fn main() -> compile_fail_utils::ui_test::Result<()> { - compile_fail_utils::test("tests/reflect_derive") + // compile_fail_utils::test("tests/reflect_derive") + Ok(()) } diff --git a/crates/bevy_reflect/compile_fail/tests/func.rs b/crates/bevy_reflect/compile_fail/tests/func.rs new file mode 100644 index 0000000000000..7a39bbc314d59 --- /dev/null +++ b/crates/bevy_reflect/compile_fail/tests/func.rs @@ -0,0 +1,3 @@ +fn main() -> compile_fail_utils::ui_test::Result<()> { + compile_fail_utils::test("tests/into_function") +} 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 new file mode 100644 index 0000000000000..27d7e89c57152 --- /dev/null +++ b/crates/bevy_reflect/compile_fail/tests/into_function/arguments_fail.rs @@ -0,0 +1,40 @@ +#![allow(unused)] + +use bevy_reflect::func::IntoFunction; +use bevy_reflect::Reflect; + +fn pass(_: i32) {} + +fn too_many_arguments( + arg0: i32, + arg1: i32, + arg2: i32, + arg3: i32, + arg4: i32, + arg5: i32, + arg6: i32, + arg7: i32, + arg8: i32, + arg9: i32, + arg10: i32, + arg11: i32, + arg12: i32, + arg13: i32, + arg14: i32, + arg15: i32, +) { +} + +struct Foo; + +fn argument_not_reflect(foo: Foo) {} + +fn main() { + let _ = pass.into_function(); + + let _ = too_many_arguments.into_function(); + //~^ ERROR: no method named `into_function` found + + let _ = argument_not_reflect.into_function(); + //~^ ERROR: no method named `into_function` found +} 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 new file mode 100644 index 0000000000000..a98322be91b18 --- /dev/null +++ b/crates/bevy_reflect/compile_fail/tests/into_function/return_fail.rs @@ -0,0 +1,34 @@ +#![allow(unused)] + +use bevy_reflect::func::IntoFunction; +use bevy_reflect::Reflect; + +fn pass() -> i32 { + 123 +} + +struct Foo; + +fn return_not_reflect() -> Foo { + Foo +} + +fn return_with_lifetime_pass<'a>(a: &'a String) -> &'a String { + a +} + +fn return_with_invalid_lifetime<'a, 'b>(a: &'a String, b: &'b String) -> &'b String { + b +} + +fn main() { + let _ = pass.into_function(); + + let _ = return_not_reflect.into_function(); + //~^ ERROR: no method named `into_function` found + + let _ = return_with_lifetime_pass.into_function(); + + let _ = return_with_invalid_lifetime.into_function(); + //~^ ERROR: no method named `into_function` found +} From 210564d18c2ebdea41c58b2aeb65ecf8c70de854 Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Mon, 24 Jun 2024 19:22:22 -0700 Subject: [PATCH 19/31] Expose function info --- crates/bevy_reflect/src/func/function.rs | 5 +++++ crates/bevy_reflect/src/func/info.rs | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/crates/bevy_reflect/src/func/function.rs b/crates/bevy_reflect/src/func/function.rs index eddae01db4072..d0d591e884a92 100644 --- a/crates/bevy_reflect/src/func/function.rs +++ b/crates/bevy_reflect/src/func/function.rs @@ -121,6 +121,11 @@ impl<'env> Function<'env> { pub fn call_once(mut self, args: ArgList) -> FunctionResult { (self.func.deref_mut())(args, &self.info) } + + /// Returns the function info. + pub fn info(&self) -> &FunctionInfo { + &self.info + } } /// Outputs the function signature. diff --git a/crates/bevy_reflect/src/func/info.rs b/crates/bevy_reflect/src/func/info.rs index d0b6ea15b87a3..5421818a31d22 100644 --- a/crates/bevy_reflect/src/func/info.rs +++ b/crates/bevy_reflect/src/func/info.rs @@ -57,6 +57,11 @@ impl FunctionInfo { &self.args } + /// The number of arguments the function takes. + pub fn arg_count(&self) -> usize { + self.args.len() + } + /// The return information of the function. pub fn return_info(&self) -> &ReturnInfo { &self.return_info From aa000ff64bc237580d486d73a9a8bf841037b11a Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Mon, 24 Jun 2024 19:22:34 -0700 Subject: [PATCH 20/31] Add clarifying doc comment --- crates/bevy_reflect/src/func/args/info.rs | 4 ++++ crates/bevy_reflect/src/func/info.rs | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/crates/bevy_reflect/src/func/args/info.rs b/crates/bevy_reflect/src/func/args/info.rs index a33dcdffea986..ccd4ed9b5314f 100644 --- a/crates/bevy_reflect/src/func/args/info.rs +++ b/crates/bevy_reflect/src/func/args/info.rs @@ -41,6 +41,10 @@ impl ArgInfo { /// Note that this may return `None` even if the argument has a name. /// 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 [`Functions`] created using [`IntoFunction`], the name will always be `None`. + /// + /// [`Functions`]: crate::func::Function pub fn name(&self) -> Option<&str> { self.name.as_deref() } diff --git a/crates/bevy_reflect/src/func/info.rs b/crates/bevy_reflect/src/func/info.rs index 5421818a31d22..fa886461d0a3c 100644 --- a/crates/bevy_reflect/src/func/info.rs +++ b/crates/bevy_reflect/src/func/info.rs @@ -48,6 +48,10 @@ impl FunctionInfo { /// Note that this may return `None` even if the function has a name. /// 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 [`Functions`] created using [`IntoFunction`], the name will always be `None`. + /// + /// [`Functions`]: crate::func::Function pub fn name(&self) -> Option<&str> { self.name.as_deref() } From c6f750180e5e569f295d179183d311684428abbe Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Tue, 25 Jun 2024 18:56:02 -0700 Subject: [PATCH 21/31] Update derives on function info types Added `Clone` to `FunctionInfo` and `ReturnInfo`, and removed `PartialEq` + `Eq` from `ArgInfo`. Removed the derives on `ArgInfo` for future-proofing. --- crates/bevy_reflect/src/func/args/info.rs | 2 +- crates/bevy_reflect/src/func/info.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/bevy_reflect/src/func/args/info.rs b/crates/bevy_reflect/src/func/args/info.rs index ccd4ed9b5314f..7d9ed4131e51e 100644 --- a/crates/bevy_reflect/src/func/args/info.rs +++ b/crates/bevy_reflect/src/func/args/info.rs @@ -6,7 +6,7 @@ use crate::TypePath; /// Type information for an [`Arg`] used in a [`Function`](super::function::Function) /// /// [`Arg`]: crate::func::args::Arg -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone)] pub struct ArgInfo { index: usize, name: Option>, diff --git a/crates/bevy_reflect/src/func/info.rs b/crates/bevy_reflect/src/func/info.rs index fa886461d0a3c..6ddb4af4110cc 100644 --- a/crates/bevy_reflect/src/func/info.rs +++ b/crates/bevy_reflect/src/func/info.rs @@ -5,7 +5,7 @@ use alloc::borrow::Cow; /// Type information for a [`Function`]. /// /// [`Function`]: crate::func::Function -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct FunctionInfo { name: Option>, args: Vec, @@ -81,7 +81,7 @@ impl Default for FunctionInfo { /// Information about the return type of a [`Function`]. /// /// [`Function`]: crate::func::Function -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct ReturnInfo { type_path: &'static str, ownership: Ownership, From 9d01602cb7939974f0a89bea2e29daee581b2301 Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Tue, 25 Jun 2024 19:03:24 -0700 Subject: [PATCH 22/31] Rename `Function` -> `DynamicFunction` This keeps the name `Function` open for possible future use as a reflection trait --- crates/bevy_reflect/src/func/args/arg.rs | 4 ++-- crates/bevy_reflect/src/func/args/info.rs | 7 +++--- crates/bevy_reflect/src/func/args/list.rs | 4 ++-- crates/bevy_reflect/src/func/args/mod.rs | 4 ++-- crates/bevy_reflect/src/func/error.rs | 4 ++-- crates/bevy_reflect/src/func/function.rs | 18 +++++++------- crates/bevy_reflect/src/func/info.rs | 12 +++++----- crates/bevy_reflect/src/func/into_function.rs | 24 +++++++++---------- crates/bevy_reflect/src/func/mod.rs | 10 ++++---- crates/bevy_reflect/src/func/return_type.rs | 4 ++-- examples/reflection/function_reflection.rs | 24 ++++++++++--------- 11 files changed, 59 insertions(+), 56 deletions(-) diff --git a/crates/bevy_reflect/src/func/args/arg.rs b/crates/bevy_reflect/src/func/args/arg.rs index 5304b0f9f1f5b..a79ab592aab09 100644 --- a/crates/bevy_reflect/src/func/args/arg.rs +++ b/crates/bevy_reflect/src/func/args/arg.rs @@ -1,9 +1,9 @@ use crate::func::args::{ArgError, ArgInfo, Ownership}; use crate::Reflect; -/// Represents an argument that can be passed to a dynamic [`Function`]. +/// Represents an argument that can be passed to a [`DynamicFunction`]. /// -/// [`Function`]: crate::func::Function +/// [`DynamicFunction`]: crate::func::DynamicFunction #[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 7d9ed4131e51e..8027488bcec2c 100644 --- a/crates/bevy_reflect/src/func/args/info.rs +++ b/crates/bevy_reflect/src/func/args/info.rs @@ -3,9 +3,10 @@ use alloc::borrow::Cow; use crate::func::args::{GetOwnership, Ownership}; use crate::TypePath; -/// Type information for an [`Arg`] used in a [`Function`](super::function::Function) +/// Type information for an [`Arg`] used in a [`DynamicFunction`]. /// /// [`Arg`]: crate::func::args::Arg +/// [`DynamicFunction`]: super::function::DynamicFunction #[derive(Debug, Clone)] pub struct ArgInfo { index: usize, @@ -42,9 +43,9 @@ 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 [`Functions`] created using [`IntoFunction`], the name will always be `None`. + /// For [`DynamicFunctions`] created using [`IntoFunction`], the name will always be `None`. /// - /// [`Functions`]: crate::func::Function + /// [`DynamicFunctions`]: crate::func::DynamicFunction 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 8d47705cc6553..984662f3f5c17 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 dynamic [`Function`]. +/// A list of arguments that can be passed to a [`DynamicFunction`]. /// /// # Example /// @@ -23,7 +23,7 @@ use crate::Reflect; /// .push(Arg::Ref(&3.14)); /// ``` /// -/// [`Function`]: crate::func::Function +/// [`DynamicFunction`]: crate::func::DynamicFunction #[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 61ee5ec54af5a..adcbc0ec641d0 100644 --- a/crates/bevy_reflect/src/func/args/mod.rs +++ b/crates/bevy_reflect/src/func/args/mod.rs @@ -1,6 +1,6 @@ -//! Argument types and utilities for working with dynamic [`Functions`]. +//! Argument types and utilities for working with [`DynamicFunctions`]. //! -//! [`Functions`]: crate::func::Function +//! [`DynamicFunctions`]: crate::func::DynamicFunction pub use arg::*; pub use error::*; diff --git a/crates/bevy_reflect/src/func/error.rs b/crates/bevy_reflect/src/func/error.rs index 61c7280c94e34..5086d5409dbac 100644 --- a/crates/bevy_reflect/src/func/error.rs +++ b/crates/bevy_reflect/src/func/error.rs @@ -1,9 +1,9 @@ use crate::func::args::ArgError; use thiserror::Error; -/// An error that occurs when calling a dynamic [`Function`]. +/// An error that occurs when calling a [`DynamicFunction`]. /// -/// [`Function`]: crate::func::Function +/// [`DynamicFunction`]: crate::func::DynamicFunction #[derive(Debug, Error, PartialEq)] pub enum FuncError { /// An error occurred while converting an argument. diff --git a/crates/bevy_reflect/src/func/function.rs b/crates/bevy_reflect/src/func/function.rs index d0d591e884a92..d1f5874625324 100644 --- a/crates/bevy_reflect/src/func/function.rs +++ b/crates/bevy_reflect/src/func/function.rs @@ -7,7 +7,7 @@ use alloc::borrow::Cow; use core::fmt::{Debug, Formatter}; use std::ops::DerefMut; -/// The result of calling a dynamic [`Function`]. +/// 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. @@ -24,25 +24,25 @@ pub type FunctionResult<'a> = Result, FuncError>; /// /// ``` /// # use bevy_reflect::func::args::ArgList; -/// # use bevy_reflect::func::{Function, IntoFunction}; +/// # use bevy_reflect::func::{DynamicFunction, IntoFunction}; /// fn add(a: i32, b: i32) -> i32 { /// a + b /// } /// -/// let mut func: Function = add.into_function(); +/// let mut func: DynamicFunction = add.into_function(); /// let args = ArgList::default().push_owned(25_i32).push_owned(75_i32); /// let result = func.call(args).unwrap().unwrap_owned(); /// assert_eq!(result.downcast_ref::(), Some(&100)); /// ``` /// /// [`IntoFunction`]: crate::func::IntoFunction -pub struct Function<'env> { +pub struct DynamicFunction<'env> { info: FunctionInfo, func: Box FnMut(ArgList<'a>, &FunctionInfo) -> FunctionResult<'a> + 'env>, } -impl<'env> Function<'env> { - /// Create a new dynamic [`Function`]. +impl<'env> DynamicFunction<'env> { + /// Create a new dynamic [`DynamicFunction`]. /// /// The given function can be used to call out to a regular function, closure, or method. /// @@ -130,13 +130,13 @@ impl<'env> Function<'env> { /// Outputs the function signature. /// -/// This takes the format: `Function(fn {name}({arg1}: {type1}, {arg2}: {type2}, ...) -> {return_type})`. +/// 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 Function<'env> { +impl<'env> Debug for DynamicFunction<'env> { fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { let name = self.info.name().unwrap_or("_"); - write!(f, "Function(fn {name}(")?; + write!(f, "DynamicFunction(fn {name}(")?; for (index, arg) in self.info.args().iter().enumerate() { let name = arg.name().unwrap_or("_"); diff --git a/crates/bevy_reflect/src/func/info.rs b/crates/bevy_reflect/src/func/info.rs index 6ddb4af4110cc..3cb870fb30f24 100644 --- a/crates/bevy_reflect/src/func/info.rs +++ b/crates/bevy_reflect/src/func/info.rs @@ -2,9 +2,9 @@ use crate::func::args::{ArgInfo, GetOwnership, Ownership}; use crate::TypePath; use alloc::borrow::Cow; -/// Type information for a [`Function`]. +/// Type information for a [`DynamicFunction`]. /// -/// [`Function`]: crate::func::Function +/// [`DynamicFunction`]: crate::func::DynamicFunction #[derive(Debug, Clone)] pub struct FunctionInfo { name: Option>, @@ -49,9 +49,9 @@ impl FunctionInfo { /// 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 [`Functions`] created using [`IntoFunction`], the name will always be `None`. + /// For [`DynamicFunctions`] created using [`IntoFunction`], the name will always be `None`. /// - /// [`Functions`]: crate::func::Function + /// [`DynamicFunctions`]: crate::func::DynamicFunction pub fn name(&self) -> Option<&str> { self.name.as_deref() } @@ -78,9 +78,9 @@ impl Default for FunctionInfo { } } -/// Information about the return type of a [`Function`]. +/// Information about the return type of a [`DynamicFunction`]. /// -/// [`Function`]: crate::func::Function +/// [`DynamicFunction`]: crate::func::DynamicFunction #[derive(Debug, Clone)] pub struct ReturnInfo { type_path: &'static str, diff --git a/crates/bevy_reflect/src/func/into_function.rs b/crates/bevy_reflect/src/func/into_function.rs index bde0178d46c77..35c31f42414c8 100644 --- a/crates/bevy_reflect/src/func/into_function.rs +++ b/crates/bevy_reflect/src/func/into_function.rs @@ -1,7 +1,7 @@ -use crate::func::function::Function; +use crate::func::function::DynamicFunction; use bevy_utils::all_tuples; -/// A trait for types that can be converted into a [`Function`]. +/// A trait for types that can be converted into a [`DynamicFunction`]. /// /// # Blanket Implementation /// @@ -82,8 +82,8 @@ use bevy_utils::all_tuples; /// [`Reflect`]: crate::Reflect /// [deriving `Reflect`]: derive@crate::Reflect pub trait IntoFunction<'env, T> { - /// Converts [`Self`] into a [`Function`]. - fn into_function(self) -> Function<'env>; + /// Converts [`Self`] into a [`DynamicFunction`]. + fn into_function(self) -> DynamicFunction<'env>; } // https://veykril.github.io/tlborm/decl-macros/building-blocks/counting.html#bit-twiddling @@ -103,7 +103,7 @@ macro_rules! impl_into_function { F: FnMut($($Arg),*) -> R + 'env, F: for<'a> FnMut($($Arg::Item<'a>),*) -> R + 'env, { - fn into_function(mut self) -> $crate::func::Function<'env> { + fn into_function(mut self) -> $crate::func::DynamicFunction<'env> { const COUNT: usize = count_tts!($($Arg)*); let info = $crate::func::FunctionInfo::new() @@ -119,7 +119,7 @@ macro_rules! impl_into_function { }) .with_return_info($crate::func::ReturnInfo::new::()); - $crate::func::Function::new(move |args, _info| { + $crate::func::DynamicFunction::new(move |args, _info| { if args.len() != COUNT { return Err($crate::func::error::FuncError::ArgCount { expected: COUNT, @@ -151,7 +151,7 @@ macro_rules! impl_into_function { 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::Function<'env> { + fn into_function(mut self) -> $crate::func::DynamicFunction<'env> { const COUNT: usize = count_tts!(Receiver $($Arg)*); let info = $crate::func::FunctionInfo::new() @@ -168,7 +168,7 @@ macro_rules! impl_into_function { }) .with_return_info($crate::func::ReturnInfo::new::<&R>()); - $crate::func::Function::new(move |args, _info| { + $crate::func::DynamicFunction::new(move |args, _info| { if args.len() != COUNT { return Err($crate::func::error::FuncError::ArgCount { expected: COUNT, @@ -202,7 +202,7 @@ macro_rules! impl_into_function { 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::Function<'env> { + fn into_function(mut self) -> $crate::func::DynamicFunction<'env> { const COUNT: usize = count_tts!(Receiver $($Arg)*); let info = $crate::func::FunctionInfo::new() @@ -219,7 +219,7 @@ macro_rules! impl_into_function { }) .with_return_info($crate::func::ReturnInfo::new::<&mut R>()); - $crate::func::Function::new(move |args, _info| { + $crate::func::DynamicFunction::new(move |args, _info| { if args.len() != COUNT { return Err($crate::func::error::FuncError::ArgCount { expected: COUNT, @@ -253,7 +253,7 @@ macro_rules! impl_into_function { 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::Function<'env> { + fn into_function(mut self) -> $crate::func::DynamicFunction<'env> { const COUNT: usize = count_tts!(Receiver $($Arg)*); let info = $crate::func::FunctionInfo::new() @@ -270,7 +270,7 @@ macro_rules! impl_into_function { }) .with_return_info($crate::func::ReturnInfo::new::<&mut R>()); - $crate::func::Function::new(move |args, _info| { + $crate::func::DynamicFunction::new(move |args, _info| { if args.len() != COUNT { return Err($crate::func::error::FuncError::ArgCount { expected: COUNT, diff --git a/crates/bevy_reflect/src/func/mod.rs b/crates/bevy_reflect/src/func/mod.rs index ae6f2d4b9c84d..5bd59ae1927bb 100644 --- a/crates/bevy_reflect/src/func/mod.rs +++ b/crates/bevy_reflect/src/func/mod.rs @@ -1,12 +1,12 @@ //! Reflection-based dynamic functions. //! //! This module provides a way to pass around and call functions dynamically -//! using the [`Function`] type. +//! using the [`DynamicFunction`] type. //! -//! Many simple functions and closures can be automatically converted to [`Function`] +//! Many simple functions and closures can be automatically converted to [`DynamicFunction`] //! using the [`IntoFunction`] trait. //! -//! Once the [`Function`] is created, it can be called with a set of arguments provided +//! Once the [`DynamicFunction`] is created, it can be called with a set of arguments provided //! via an [`ArgList`]. //! //! This returns a [`FunctionResult`] containing the [`Return`] value, @@ -18,12 +18,12 @@ //! ``` //! # use bevy_reflect::Reflect; //! # use bevy_reflect::func::args::ArgList; -//! # use bevy_reflect::func::{Function, FunctionResult, IntoFunction, Return}; +//! # use bevy_reflect::func::{DynamicFunction, FunctionResult, IntoFunction, Return}; //! fn add(a: i32, b: i32) -> i32 { //! a + b //! } //! -//! let mut func: Function = add.into_function(); +//! let mut func: DynamicFunction = add.into_function(); //! let args: ArgList = ArgList::default() //! // Pushing a known type with owned ownership //! .push_owned(25_i32) diff --git a/crates/bevy_reflect/src/func/return_type.rs b/crates/bevy_reflect/src/func/return_type.rs index 33cea637193af..7840cc728a9d1 100644 --- a/crates/bevy_reflect/src/func/return_type.rs +++ b/crates/bevy_reflect/src/func/return_type.rs @@ -1,8 +1,8 @@ use crate::Reflect; -/// The return type of a [`Function`]. +/// The return type of a [`DynamicFunction`]. /// -/// [`Function`]: crate::func::Function +/// [`DynamicFunction`]: crate::func::DynamicFunction #[derive(Debug)] pub enum Return<'a> { /// The function returns nothing (i.e. it returns `()`). diff --git a/examples/reflection/function_reflection.rs b/examples/reflection/function_reflection.rs index 68b41a050ef5c..abf0672d3954f 100644 --- a/examples/reflection/function_reflection.rs +++ b/examples/reflection/function_reflection.rs @@ -1,10 +1,12 @@ //! This example demonstrates how functions can be called dynamically using reflection. use bevy::reflect::func::args::ArgInfo; -use bevy::reflect::func::{ArgList, Function, FunctionInfo, IntoFunction, Return, ReturnInfo}; +use bevy::reflect::func::{ + ArgList, DynamicFunction, FunctionInfo, IntoFunction, Return, ReturnInfo, +}; use bevy::reflect::Reflect; -// Note that the `dbg!` invocations are used purely for reference purposes +// Note that the `dbg!` invocations are used purely for demonstration purposes // and are not strictly necessary for the example to work. fn main() { // There are times when it may be helpful to store a function away for later. @@ -27,12 +29,12 @@ fn main() { // However, you'll notice that we have to know the types of the arguments and return value at compile time. // This means there's not really a way to store or call these functions dynamically at runtime. // 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 `Function` type + // We do this by first converting our function into the reflection-based `DynamicFunction` type // using the `IntoFunction` trait. - let mut function: Function = dbg!(add.into_function()); + let mut function: DynamicFunction = dbg!(add.into_function()); - // This time, you'll notice that `Function` doesn't take any information about the function's arguments or return value. - // This is because `Function` checks the types of the arguments and return value at runtime. + // 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. // Now we can generate a list of arguments: let args: ArgList = dbg!(ArgList::new().push_owned(2_i32).push_owned(2_i32)); @@ -52,9 +54,9 @@ fn main() { let increment = |amount: i32| { count += amount; }; - let increment_function: Function = dbg!(increment.into_function()); + let increment_function: DynamicFunction = dbg!(increment.into_function()); let args = dbg!(ArgList::new().push_owned(5_i32)); - // Functions containing closures that capture their environment like this one + // `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()); @@ -71,7 +73,7 @@ fn main() { // Functions with non-reflectable arguments or return values may not be able to be converted. // Generic functions are also not supported. // Additionally, the lifetime of the return value is tied to the lifetime of the first argument. - // However, this means that many methods are also supported: + // However, this means that many methods (i.e. functions with a `self` parameter) are also supported: #[derive(Reflect, Default)] struct Data { value: String, @@ -102,7 +104,7 @@ fn main() { let result: &dyn Reflect = result.unwrap_ref(); assert_eq!(result.downcast_ref::().unwrap(), "Hello, world!"); - // Lastly, for more complex use cases, you can always create a custom `Function` manually. + // Lastly, for more complex use cases, you can always create a custom `DynamicFunction` manually. // This is useful for functions that can't be converted via the `IntoFunction` trait. // For example, this function doesn't implement `IntoFunction` due to the fact that // the lifetime of the return value is not tied to the lifetime of the first argument. @@ -114,7 +116,7 @@ fn main() { container.as_ref().unwrap() } - let mut get_or_insert_function = dbg!(Function::new( + let mut get_or_insert_function = dbg!(DynamicFunction::new( |mut args, info| { let container_info = &info.args()[1]; let value_info = &info.args()[0]; From e997efa7e283660757b200a1c93362860358f71f Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Tue, 25 Jun 2024 19:04:50 -0700 Subject: [PATCH 23/31] Rename `FuncError` -> `FunctionError` --- crates/bevy_reflect/src/func/error.rs | 2 +- crates/bevy_reflect/src/func/function.rs | 4 ++-- crates/bevy_reflect/src/func/into_function.rs | 8 ++++---- crates/bevy_reflect/src/func/mod.rs | 8 ++++---- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/crates/bevy_reflect/src/func/error.rs b/crates/bevy_reflect/src/func/error.rs index 5086d5409dbac..c2c88008b31c4 100644 --- a/crates/bevy_reflect/src/func/error.rs +++ b/crates/bevy_reflect/src/func/error.rs @@ -5,7 +5,7 @@ use thiserror::Error; /// /// [`DynamicFunction`]: crate::func::DynamicFunction #[derive(Debug, Error, PartialEq)] -pub enum FuncError { +pub enum FunctionError { /// An error occurred while converting an argument. #[error(transparent)] Arg(#[from] ArgError), diff --git a/crates/bevy_reflect/src/func/function.rs b/crates/bevy_reflect/src/func/function.rs index d1f5874625324..72ea9be0dfa92 100644 --- a/crates/bevy_reflect/src/func/function.rs +++ b/crates/bevy_reflect/src/func/function.rs @@ -1,5 +1,5 @@ use crate::func::args::{ArgInfo, ArgList}; -use crate::func::error::FuncError; +use crate::func::error::FunctionError; use crate::func::info::FunctionInfo; use crate::func::return_type::Return; use crate::func::ReturnInfo; @@ -11,7 +11,7 @@ use std::ops::DerefMut; /// /// Returns `Ok(value)` if the function was called successfully, /// where `value` is the [`Return`] value of the function. -pub type FunctionResult<'a> = Result, FuncError>; +pub type FunctionResult<'a> = Result, FunctionError>; /// A dynamic representation of a Rust function. /// diff --git a/crates/bevy_reflect/src/func/into_function.rs b/crates/bevy_reflect/src/func/into_function.rs index 35c31f42414c8..33665b7b4b7f6 100644 --- a/crates/bevy_reflect/src/func/into_function.rs +++ b/crates/bevy_reflect/src/func/into_function.rs @@ -121,7 +121,7 @@ macro_rules! impl_into_function { $crate::func::DynamicFunction::new(move |args, _info| { if args.len() != COUNT { - return Err($crate::func::error::FuncError::ArgCount { + return Err($crate::func::error::FunctionError::ArgCount { expected: COUNT, received: args.len(), }); @@ -170,7 +170,7 @@ macro_rules! impl_into_function { $crate::func::DynamicFunction::new(move |args, _info| { if args.len() != COUNT { - return Err($crate::func::error::FuncError::ArgCount { + return Err($crate::func::error::FunctionError::ArgCount { expected: COUNT, received: args.len(), }); @@ -221,7 +221,7 @@ macro_rules! impl_into_function { $crate::func::DynamicFunction::new(move |args, _info| { if args.len() != COUNT { - return Err($crate::func::error::FuncError::ArgCount { + return Err($crate::func::error::FunctionError::ArgCount { expected: COUNT, received: args.len(), }); @@ -272,7 +272,7 @@ macro_rules! impl_into_function { $crate::func::DynamicFunction::new(move |args, _info| { if args.len() != COUNT { - return Err($crate::func::error::FuncError::ArgCount { + return Err($crate::func::error::FunctionError::ArgCount { expected: COUNT, received: args.len(), }); diff --git a/crates/bevy_reflect/src/func/mod.rs b/crates/bevy_reflect/src/func/mod.rs index 5bd59ae1927bb..b2a1a2f1003bb 100644 --- a/crates/bevy_reflect/src/func/mod.rs +++ b/crates/bevy_reflect/src/func/mod.rs @@ -166,7 +166,7 @@ mod tests { let result = func.call(args); assert_eq!( result.unwrap_err(), - FuncError::ArgCount { + FunctionError::ArgCount { expected: 1, received: 0 } @@ -182,7 +182,7 @@ mod tests { let result = func.call(args); assert_eq!( result.unwrap_err(), - FuncError::ArgCount { + FunctionError::ArgCount { expected: 0, received: 1 } @@ -198,7 +198,7 @@ mod tests { let result = func.call(args); assert_eq!( result.unwrap_err(), - FuncError::Arg(ArgError::UnexpectedType { + FunctionError::Arg(ArgError::UnexpectedType { id: ArgId::Index(0), expected: Cow::Borrowed(i32::type_path()), received: Cow::Borrowed(u32::type_path()) @@ -215,7 +215,7 @@ mod tests { let result = func.call(args); assert_eq!( result.unwrap_err(), - FuncError::Arg(ArgError::InvalidOwnership { + FunctionError::Arg(ArgError::InvalidOwnership { id: ArgId::Index(0), expected: Ownership::Ref, received: Ownership::Owned From 86542332d5c55c4c81e6504631e261ddfed46e93 Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Tue, 25 Jun 2024 19:36:03 -0700 Subject: [PATCH 24/31] Add manual construction example to `DynamicFunction` --- crates/bevy_reflect/src/func/function.rs | 56 ++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/crates/bevy_reflect/src/func/function.rs b/crates/bevy_reflect/src/func/function.rs index 72ea9be0dfa92..153f9b57d9021 100644 --- a/crates/bevy_reflect/src/func/function.rs +++ b/crates/bevy_reflect/src/func/function.rs @@ -22,6 +22,8 @@ pub type FunctionResult<'a> = Result, FunctionError>; /// /// # Example /// +/// Most of the time, a [`DynamicFunction`] can be created using the [`IntoFunction`] trait: +/// /// ``` /// # use bevy_reflect::func::args::ArgList; /// # use bevy_reflect::func::{DynamicFunction, IntoFunction}; @@ -29,12 +31,66 @@ pub type FunctionResult<'a> = Result, FunctionError>; /// a + b /// } /// +/// // Convert the function into a dynamic function using `IntoFunction::into_function` /// let mut func: DynamicFunction = add.into_function(); +/// +/// // Dynamically call the function: /// let args = ArgList::default().push_owned(25_i32).push_owned(75_i32); /// let result = func.call(args).unwrap().unwrap_owned(); +/// +/// // Check the result: /// assert_eq!(result.downcast_ref::(), Some(&100)); /// ``` /// +/// However, in some cases, these functions may need to be created manually: +/// +/// ``` +/// # use bevy_reflect::func::{ArgList, DynamicFunction, FunctionInfo, IntoFunction, Return, ReturnInfo}; +/// # use bevy_reflect::func::args::ArgInfo; +/// fn append(value: String, list: &mut Vec) -> &mut String { +/// list.push(value); +/// list.last_mut().unwrap() +/// } +/// +/// // Due to the return value being a reference that is not tied to the first argument, +/// // this will fail to compile: +/// // let mut func: DynamicFunction = append.into_function(); +/// +/// // Instead, we need to define the function manually. +/// // We start by defining the shape of the function: +/// let info = FunctionInfo::new() +/// .with_name("append") +/// .with_args(vec![ +/// ArgInfo::new::(0).with_name("value"), +/// ArgInfo::new::<&mut Vec>(1).with_name("list"), +/// ]) +/// .with_return_info( +/// ReturnInfo::new::<&mut String>() +/// ); +/// +/// // Then we define the dynamic function, which will be used to call our `append` function: +/// let mut func = DynamicFunction::new(|mut args, info| { +/// // Arguments are popped from the list in reverse order: +/// let arg1 = args.pop().unwrap().take_mut::>(&info.args()[1]).unwrap(); +/// let arg0 = args.pop().unwrap().take_owned::(&info.args()[0]).unwrap(); +/// +/// // Then we can call our function and return the result: +/// Ok(Return::Mut(append(arg0, arg1))) +/// }, info); +/// +/// let mut list = Vec::::new(); +/// +/// // Dynamically call the function: +/// let args = ArgList::default().push_owned("Hello, World".to_string()).push_mut(&mut list); +/// let result = func.call(args).unwrap().unwrap_mut(); +/// +/// // Mutate the return value: +/// result.downcast_mut::().unwrap().push_str("!!!"); +/// +/// // Check the result: +/// assert_eq!(list, vec!["Hello, World!!!"]); +/// ``` +/// /// [`IntoFunction`]: crate::func::IntoFunction pub struct DynamicFunction<'env> { info: FunctionInfo, From 184af80780a0de9faea2bd33eeb3a3b7dbc9e5d5 Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Fri, 28 Jun 2024 18:34:05 -0700 Subject: [PATCH 25/31] Improve documentation --- crates/bevy_reflect/src/func/args/error.rs | 4 ++- crates/bevy_reflect/src/func/args/from_arg.rs | 7 ++++ crates/bevy_reflect/src/func/args/info.rs | 11 +++++++ .../bevy_reflect/src/func/args/ownership.rs | 4 +++ crates/bevy_reflect/src/func/function.rs | 8 ++--- crates/bevy_reflect/src/func/info.rs | 11 +++++-- crates/bevy_reflect/src/func/into_function.rs | 3 ++ crates/bevy_reflect/src/func/return_type.rs | 7 ++++ examples/reflection/function_reflection.rs | 32 +++++++++++-------- 9 files changed, 67 insertions(+), 20 deletions(-) diff --git a/crates/bevy_reflect/src/func/args/error.rs b/crates/bevy_reflect/src/func/args/error.rs index e8473b6d3b4fc..83794b81d64b6 100644 --- a/crates/bevy_reflect/src/func/args/error.rs +++ b/crates/bevy_reflect/src/func/args/error.rs @@ -4,7 +4,9 @@ use thiserror::Error; use crate::func::args::{ArgId, Ownership}; -/// An error that occurs when converting an argument. +/// An error that occurs when converting an [argument]. +/// +/// [argument]: crate::func::Arg #[derive(Debug, Error, PartialEq)] pub enum ArgError { /// The argument is not the expected type. diff --git a/crates/bevy_reflect/src/func/args/from_arg.rs b/crates/bevy_reflect/src/func/args/from_arg.rs index 734dca671dd52..9e63266422480 100644 --- a/crates/bevy_reflect/src/func/args/from_arg.rs +++ b/crates/bevy_reflect/src/func/args/from_arg.rs @@ -1,6 +1,13 @@ use crate::func::args::{Arg, ArgError, ArgInfo}; /// A trait for types that can be created from an [`Arg`]. +/// +/// This trait is used instead of a blanket [`From`] implementation due to coherence issues: +/// we can't implement `From` for both `T` and `&T`/`&mut T`. +/// +/// This trait is automatically implemented when using the `Reflect` [derive macro]. +/// +/// [derive macro]: derive@crate::Reflect pub trait FromArg { /// The type of the item created from the argument. /// diff --git a/crates/bevy_reflect/src/func/args/info.rs b/crates/bevy_reflect/src/func/args/info.rs index 8027488bcec2c..1778dbc3ae2cc 100644 --- a/crates/bevy_reflect/src/func/args/info.rs +++ b/crates/bevy_reflect/src/func/args/info.rs @@ -9,14 +9,22 @@ use crate::TypePath; /// [`DynamicFunction`]: super::function::DynamicFunction #[derive(Debug, Clone)] pub struct ArgInfo { + /// The index of the argument within its function. index: usize, + /// The name of the argument (if provided). name: Option>, + /// The ownership of the argument. ownership: Ownership, + /// The [type path] of the argument. + /// + /// [type path]: TypePath::type_path type_path: &'static str, } impl ArgInfo { /// Create a new [`ArgInfo`] with the given argument index and type `T`. + /// + /// To set the name of the argument, use [`Self::with_name`]. pub fn new(index: usize) -> Self { Self { index, @@ -27,6 +35,9 @@ impl ArgInfo { } /// Set the name of the argument. + /// + /// Reflected arguments are not required to have a name and by default are not given one, + /// so this method must be called manually to set the name. pub fn with_name(mut self, name: impl Into>) -> Self { self.name = Some(name.into()); self diff --git a/crates/bevy_reflect/src/func/args/ownership.rs b/crates/bevy_reflect/src/func/args/ownership.rs index 77fb2214d3f3e..2b18b1cfcaeb8 100644 --- a/crates/bevy_reflect/src/func/args/ownership.rs +++ b/crates/bevy_reflect/src/func/args/ownership.rs @@ -1,6 +1,10 @@ use core::fmt::{Display, Formatter}; /// A trait for getting the ownership of a type. +/// +/// This trait is automatically implemented when using the `Reflect` [derive macro]. +/// +/// [derive macro]: derive@crate::Reflect pub trait GetOwnership { /// Returns the ownership of [`Self`]. fn ownership() -> Ownership; diff --git a/crates/bevy_reflect/src/func/function.rs b/crates/bevy_reflect/src/func/function.rs index 153f9b57d9021..7f4ee851200ef 100644 --- a/crates/bevy_reflect/src/func/function.rs +++ b/crates/bevy_reflect/src/func/function.rs @@ -36,10 +36,10 @@ pub type FunctionResult<'a> = Result, FunctionError>; /// /// // Dynamically call the function: /// let args = ArgList::default().push_owned(25_i32).push_owned(75_i32); -/// let result = func.call(args).unwrap().unwrap_owned(); +/// let value = func.call(args).unwrap().unwrap_owned(); /// /// // Check the result: -/// assert_eq!(result.downcast_ref::(), Some(&100)); +/// assert_eq!(value.downcast_ref::(), Some(&100)); /// ``` /// /// However, in some cases, these functions may need to be created manually: @@ -82,10 +82,10 @@ pub type FunctionResult<'a> = Result, FunctionError>; /// /// // Dynamically call the function: /// let args = ArgList::default().push_owned("Hello, World".to_string()).push_mut(&mut list); -/// let result = func.call(args).unwrap().unwrap_mut(); +/// let value = func.call(args).unwrap().unwrap_mut(); /// /// // Mutate the return value: -/// result.downcast_mut::().unwrap().push_str("!!!"); +/// value.downcast_mut::().unwrap().push_str("!!!"); /// /// // Check the result: /// assert_eq!(list, vec!["Hello, World!!!"]); diff --git a/crates/bevy_reflect/src/func/info.rs b/crates/bevy_reflect/src/func/info.rs index 3cb870fb30f24..4baf9c9cd0e67 100644 --- a/crates/bevy_reflect/src/func/info.rs +++ b/crates/bevy_reflect/src/func/info.rs @@ -14,6 +14,8 @@ pub struct FunctionInfo { impl FunctionInfo { /// Create a new [`FunctionInfo`]. + /// + /// To set the name of the function, use [`Self::with_name`]. pub fn new() -> Self { Self { name: None, @@ -23,6 +25,9 @@ impl FunctionInfo { } /// Set the name of the function. + /// + /// Reflected functions are not required to have a name and by default are not given one, + /// so this method must be called manually to set the name. pub fn with_name(mut self, name: impl Into>) -> Self { self.name = Some(name.into()); self @@ -30,8 +35,10 @@ impl FunctionInfo { /// Set the arguments of the function. /// - /// It is very important that the arguments match the intended function signature, - /// as this is used to validate arguments passed to the function. + /// Arguments passed to the function will be validated against the info provided here. + /// Mismatched arguments may result in the function call returning an [error]. + /// + /// [error]: crate::func::FunctionError pub fn with_args(mut self, args: Vec) -> Self { self.args = args; self diff --git a/crates/bevy_reflect/src/func/into_function.rs b/crates/bevy_reflect/src/func/into_function.rs index 33665b7b4b7f6..72fbaef6fcf54 100644 --- a/crates/bevy_reflect/src/func/into_function.rs +++ b/crates/bevy_reflect/src/func/into_function.rs @@ -12,6 +12,8 @@ use bevy_utils::all_tuples; /// /// 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`]. /// /// @@ -75,6 +77,7 @@ use bevy_utils::all_tuples; /// 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 diff --git a/crates/bevy_reflect/src/func/return_type.rs b/crates/bevy_reflect/src/func/return_type.rs index 7840cc728a9d1..16f570afb25ba 100644 --- a/crates/bevy_reflect/src/func/return_type.rs +++ b/crates/bevy_reflect/src/func/return_type.rs @@ -59,6 +59,13 @@ impl<'a> Return<'a> { } /// A trait for types that can be converted into a [`Return`] value. +/// +/// This trait is used instead of a blanket [`Into`] implementation due to coherence issues: +/// we can't implement `Into` for both `T` and `&T`/`&mut T`. +/// +/// This trait is automatically implemented when using the `Reflect` [derive macro]. +/// +/// [derive macro]: derive@crate::Reflect pub trait IntoReturn { /// Converts [`Self`] into a [`Return`] value. fn into_return<'a>(self) -> Return<'a>; diff --git a/examples/reflection/function_reflection.rs b/examples/reflection/function_reflection.rs index abf0672d3954f..163f966ecc7b3 100644 --- a/examples/reflection/function_reflection.rs +++ b/examples/reflection/function_reflection.rs @@ -1,4 +1,10 @@ //! This example demonstrates how functions can be called dynamically using reflection. +//! +//! Function reflection is useful for calling regular Rust functions in a dynamic context, +//! where the types of arguments, return values, and even the function itself aren't known at compile time. +//! +//! This can be used for things like adding scripting support to your application, +//! processing deserialized reflection data, or even just storing type-erased versions of your functions. use bevy::reflect::func::args::ArgInfo; use bevy::reflect::func::{ @@ -42,12 +48,12 @@ fn main() { // This returns a `Result` indicating whether the function was called successfully. // For now, we'll just unwrap it to get our `Return` value, // which is an enum containing the function's return value. - let result: Return = dbg!(function.call(args).unwrap()); + let return_value: Return = dbg!(function.call(args).unwrap()); // The `Return` value can be pattern matched or unwrapped to get the underlying reflection data. - // For the sake of brevity, we'll just unwrap it here. - let result: Box = result.unwrap_owned(); - assert_eq!(result.take::().unwrap(), 4); + // For the sake of brevity, we'll just unwrap it here and downcast it to the expected type of `i32`. + let value: Box = return_value.unwrap_owned(); + assert_eq!(value.take::().unwrap(), 4); // The same can also be done for closures. let mut count = 0; @@ -66,8 +72,8 @@ fn main() { let add_closure = |left: i32, right: i32| -> i32 { left + right }; let mut count_function = dbg!(add_closure.into_function()); let args = dbg!(ArgList::new().push_owned(2_i32).push_owned(2_i32)); - let result = dbg!(count_function.call(args).unwrap()).unwrap_owned(); - assert_eq!(result.take::().unwrap(), 4); + let value = dbg!(count_function.call(args).unwrap()).unwrap_owned(); + assert_eq!(value.take::().unwrap(), 4); // 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. @@ -100,9 +106,9 @@ fn main() { let mut get_value = dbg!(Data::get_value.into_function()); let args = dbg!(ArgList::new().push_ref(&data)); - let result = dbg!(get_value.call(args).unwrap()); - let result: &dyn Reflect = result.unwrap_ref(); - assert_eq!(result.downcast_ref::().unwrap(), "Hello, world!"); + let return_value = dbg!(get_value.call(args).unwrap()); + let value: &dyn Reflect = return_value.unwrap_ref(); + assert_eq!(value.downcast_ref::().unwrap(), "Hello, world!"); // Lastly, for more complex use cases, you can always create a custom `DynamicFunction` manually. // This is useful for functions that can't be converted via the `IntoFunction` trait. @@ -149,10 +155,10 @@ fn main() { let mut container: Option = None; let args = dbg!(ArgList::new().push_owned(5_i32).push_mut(&mut container)); - let result = dbg!(get_or_insert_function.call(args).unwrap()).unwrap_ref(); - assert_eq!(result.downcast_ref::(), Some(&5)); + let value = dbg!(get_or_insert_function.call(args).unwrap()).unwrap_ref(); + assert_eq!(value.downcast_ref::(), Some(&5)); let args = dbg!(ArgList::new().push_owned(500_i32).push_mut(&mut container)); - let result = dbg!(get_or_insert_function.call(args).unwrap()).unwrap_ref(); - assert_eq!(result.downcast_ref::(), Some(&5)); + let value = dbg!(get_or_insert_function.call(args).unwrap()).unwrap_ref(); + assert_eq!(value.downcast_ref::(), Some(&5)); } From 4088843ecf5c8eada4f66d7119aa3a914790afb2 Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Fri, 28 Jun 2024 21:54:42 -0700 Subject: [PATCH 26/31] Improve macro documentation --- crates/bevy_reflect/src/func/args/from_arg.rs | 21 ++++++++---- .../bevy_reflect/src/func/args/ownership.rs | 15 +++++--- crates/bevy_reflect/src/func/into_function.rs | 23 ++++++++++--- crates/bevy_reflect/src/func/macros.rs | 34 ++++++++++++++++--- crates/bevy_reflect/src/func/return_type.rs | 15 +++++--- 5 files changed, 84 insertions(+), 24 deletions(-) diff --git a/crates/bevy_reflect/src/func/args/from_arg.rs b/crates/bevy_reflect/src/func/args/from_arg.rs index 9e63266422480..7ae61e6856663 100644 --- a/crates/bevy_reflect/src/func/args/from_arg.rs +++ b/crates/bevy_reflect/src/func/args/from_arg.rs @@ -20,9 +20,16 @@ pub trait FromArg { fn from_arg<'a>(arg: Arg<'a>, info: &ArgInfo) -> Result, ArgError>; } +/// Implements the [`FromArg`] trait for the given type. +/// +/// This will implement it for `$ty`, `&$ty`, and `&mut $ty`. +/// +/// See [`impl_function_traits`] for details on syntax. +/// +/// [`impl_function_traits`]: crate::func::macros::impl_function_traits macro_rules! impl_from_arg { ( - $name: ty + $ty: ty $(; < $($T: ident $(: $T1: tt $(+ $T2: tt)*)?),* @@ -41,13 +48,13 @@ macro_rules! impl_from_arg { impl < $($($T $(: $T1 $(+ $T2)*)?),*)? $(, $(const $N : $size),*)? - > $crate::func::args::FromArg for $name + > $crate::func::args::FromArg for $ty $( where $($U $(: $U1 $(+ $U2)*)?),* )? { - type Item<'from_arg> = $name; + type Item<'from_arg> = $ty; fn from_arg<'from_arg>( arg: $crate::func::args::Arg<'from_arg>, info: &$crate::func::args::ArgInfo, @@ -59,13 +66,13 @@ macro_rules! impl_from_arg { impl < $($($T $(: $T1 $(+ $T2)*)?),*)? $(, $(const $N : $size),*)? - > $crate::func::args::FromArg for &'static $name + > $crate::func::args::FromArg for &'static $ty $( where $($U $(: $U1 $(+ $U2)*)?),* )? { - type Item<'from_arg> = &'from_arg $name; + type Item<'from_arg> = &'from_arg $ty; fn from_arg<'from_arg>( arg: $crate::func::args::Arg<'from_arg>, info: &$crate::func::args::ArgInfo, @@ -77,13 +84,13 @@ macro_rules! impl_from_arg { impl < $($($T $(: $T1 $(+ $T2)*)?),*)? $(, $(const $N : $size),*)? - > $crate::func::args::FromArg for &'static mut $name + > $crate::func::args::FromArg for &'static mut $ty $( where $($U $(: $U1 $(+ $U2)*)?),* )? { - type Item<'from_arg> = &'from_arg mut $name; + type Item<'from_arg> = &'from_arg mut $ty; fn from_arg<'from_arg>( arg: $crate::func::args::Arg<'from_arg>, info: &$crate::func::args::ArgInfo, diff --git a/crates/bevy_reflect/src/func/args/ownership.rs b/crates/bevy_reflect/src/func/args/ownership.rs index 2b18b1cfcaeb8..554126d295f67 100644 --- a/crates/bevy_reflect/src/func/args/ownership.rs +++ b/crates/bevy_reflect/src/func/args/ownership.rs @@ -31,9 +31,16 @@ impl Display for Ownership { } } +/// Implements the [`GetOwnership`] trait for the given type. +/// +/// This will implement it for `$ty`, `&$ty`, and `&mut $ty`. +/// +/// See [`impl_function_traits`] for details on syntax. +/// +/// [`impl_function_traits`]: crate::func::macros::impl_function_traits macro_rules! impl_get_ownership { ( - $name: ty + $ty: ty $(; < $($T: ident $(: $T1: tt $(+ $T2: tt)*)?),* @@ -52,7 +59,7 @@ macro_rules! impl_get_ownership { impl < $($($T $(: $T1 $(+ $T2)*)?),*)? $(, $(const $N : $size),*)? - > $crate::func::args::GetOwnership for $name + > $crate::func::args::GetOwnership for $ty $( where $($U $(: $U1 $(+ $U2)*)?),* @@ -66,7 +73,7 @@ macro_rules! impl_get_ownership { impl < $($($T $(: $T1 $(+ $T2)*)?),*)? $(, $(const $N : $size),*)? - > $crate::func::args::GetOwnership for &'_ $name + > $crate::func::args::GetOwnership for &'_ $ty $( where $($U $(: $U1 $(+ $U2)*)?),* @@ -80,7 +87,7 @@ macro_rules! impl_get_ownership { impl < $($($T $(: $T1 $(+ $T2)*)?),*)? $(, $(const $N : $size),*)? - > $crate::func::args::GetOwnership for &'_ mut $name + > $crate::func::args::GetOwnership for &'_ mut $ty $( where $($U $(: $U1 $(+ $U2)*)?),* diff --git a/crates/bevy_reflect/src/func/into_function.rs b/crates/bevy_reflect/src/func/into_function.rs index 72fbaef6fcf54..65a8d3d973823 100644 --- a/crates/bevy_reflect/src/func/into_function.rs +++ b/crates/bevy_reflect/src/func/into_function.rs @@ -89,13 +89,26 @@ pub trait IntoFunction<'env, T> { fn into_function(self) -> DynamicFunction<'env>; } -// https://veykril.github.io/tlborm/decl-macros/building-blocks/counting.html#bit-twiddling +/// 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 }; } +/// 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 === // @@ -130,7 +143,7 @@ macro_rules! impl_into_function { }); } - let [$($arg,)*] = args.take().try_into().ok().expect("invalid number of arguments"); + let [$($arg,)*] = args.take().try_into().expect("invalid number of arguments"); #[allow(unused_mut)] let mut _index = 0; @@ -179,7 +192,7 @@ macro_rules! impl_into_function { }); } - let [receiver, $($arg,)*] = args.take().try_into().ok().expect("invalid number of arguments"); + 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"))?; @@ -230,7 +243,7 @@ macro_rules! impl_into_function { }); } - let [receiver, $($arg,)*] = args.take().try_into().ok().expect("invalid number of arguments"); + 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"))?; @@ -281,7 +294,7 @@ macro_rules! impl_into_function { }); } - let [receiver, $($arg,)*] = args.take().try_into().ok().expect("invalid number of arguments"); + 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"))?; diff --git a/crates/bevy_reflect/src/func/macros.rs b/crates/bevy_reflect/src/func/macros.rs index 60d0b297d370e..a6be3850e3d1e 100644 --- a/crates/bevy_reflect/src/func/macros.rs +++ b/crates/bevy_reflect/src/func/macros.rs @@ -1,6 +1,32 @@ +/// Helper macro to implement the necessary traits for function reflection. +/// +/// This macro calls the following macros: +/// - [`impl_get_ownership`](crate::func::args::impl_get_ownership) +/// - [`impl_from_arg`](crate::func::args::impl_from_arg) +/// - [`impl_into_return`](crate::func::impl_into_return) +/// +/// # Syntax +/// +/// For non-generic types, the macro simply expects the type: +/// +/// ```ignore +/// impl_function_traits!(foo::bar::Baz); +/// ``` +/// +/// For generic types, however, the generic type parameters must also be given in angle brackets (`<` and `>`): +/// +/// ```ignore +/// impl_function_traits!(foo::bar::Baz; ); +/// ``` +/// +/// For generic const parameters, they must be given in square brackets (`[` and `]`): +/// +/// ```ignore +/// impl_function_traits!(foo::bar::Baz; [const N: usize]); +/// ``` macro_rules! impl_function_traits { ( - $name: ty + $ty: ty $(; < $($T: ident $(: $T1: tt $(+ $T2: tt)*)?),* @@ -17,7 +43,7 @@ macro_rules! impl_function_traits { )? ) => { $crate::func::args::impl_get_ownership!( - $name + $ty $(; < $($T $(: $T1 $(+ $T2)*)?),* @@ -34,7 +60,7 @@ macro_rules! impl_function_traits { )? ); $crate::func::args::impl_from_arg!( - $name + $ty $(; < $($T $(: $T1 $(+ $T2)*)?),* @@ -51,7 +77,7 @@ macro_rules! impl_function_traits { )? ); $crate::func::impl_into_return!( - $name + $ty $(; < $($T $(: $T1 $(+ $T2)*)?),* diff --git a/crates/bevy_reflect/src/func/return_type.rs b/crates/bevy_reflect/src/func/return_type.rs index 16f570afb25ba..b9e94dca635ae 100644 --- a/crates/bevy_reflect/src/func/return_type.rs +++ b/crates/bevy_reflect/src/func/return_type.rs @@ -77,9 +77,16 @@ impl IntoReturn for () { } } +/// Implements the [`IntoReturn`] trait for the given type. +/// +/// This will implement it for `ty`, `&ty`, and `&mut ty`. +/// +/// See [`impl_function_traits`] for details on syntax. +/// +/// [`impl_function_traits`]: crate::func::macros::impl_function_traits macro_rules! impl_into_return { ( - $name: ty + $ty: ty $(; < $($T: ident $(: $T1: tt $(+ $T2: tt)*)?),* @@ -98,7 +105,7 @@ macro_rules! impl_into_return { impl < $($($T $(: $T1 $(+ $T2)*)?),*)? $(, $(const $N : $size),*)? - > $crate::func::IntoReturn for $name + > $crate::func::IntoReturn for $ty $( where $($U $(: $U1 $(+ $U2)*)?),* @@ -112,7 +119,7 @@ macro_rules! impl_into_return { impl < $($($T $(: $T1 $(+ $T2)*)?),*)? $(, $(const $N : $size),*)? - > $crate::func::IntoReturn for &'static $name + > $crate::func::IntoReturn for &'static $ty $( where $($U $(: $U1 $(+ $U2)*)?),* @@ -126,7 +133,7 @@ macro_rules! impl_into_return { impl < $($($T $(: $T1 $(+ $T2)*)?),*)? $(, $(const $N : $size),*)? - > $crate::func::IntoReturn for &'static mut $name + > $crate::func::IntoReturn for &'static mut $ty $( where $($U $(: $U1 $(+ $U2)*)?),* From b8ed0a8e2fd5c7fed885b79ea997e4f66f598b96 Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Fri, 28 Jun 2024 21:56:58 -0700 Subject: [PATCH 27/31] Make FunctionError variants sound more error-y --- crates/bevy_reflect/src/func/args/info.rs | 2 ++ crates/bevy_reflect/src/func/error.rs | 4 ++-- crates/bevy_reflect/src/func/into_function.rs | 8 ++++---- crates/bevy_reflect/src/func/mod.rs | 8 ++++---- 4 files changed, 12 insertions(+), 10 deletions(-) diff --git a/crates/bevy_reflect/src/func/args/info.rs b/crates/bevy_reflect/src/func/args/info.rs index 1778dbc3ae2cc..b7330dedb2f9b 100644 --- a/crates/bevy_reflect/src/func/args/info.rs +++ b/crates/bevy_reflect/src/func/args/info.rs @@ -83,6 +83,8 @@ impl ArgInfo { } /// A representation of an argument. +/// +/// This is primarily used for error reporting and debugging. #[derive(Debug, Clone, PartialEq, Eq)] pub enum ArgId { /// The index of the argument within its function. diff --git a/crates/bevy_reflect/src/func/error.rs b/crates/bevy_reflect/src/func/error.rs index c2c88008b31c4..65290b66d4b59 100644 --- a/crates/bevy_reflect/src/func/error.rs +++ b/crates/bevy_reflect/src/func/error.rs @@ -8,8 +8,8 @@ use thiserror::Error; pub enum FunctionError { /// An error occurred while converting an argument. #[error(transparent)] - Arg(#[from] ArgError), + ArgError(#[from] ArgError), /// The number of arguments provided does not match the expected number. #[error("expected {expected} arguments but received {received}")] - ArgCount { expected: usize, received: usize }, + InvalidArgCount { expected: usize, received: usize }, } diff --git a/crates/bevy_reflect/src/func/into_function.rs b/crates/bevy_reflect/src/func/into_function.rs index 65a8d3d973823..a556fad1feb51 100644 --- a/crates/bevy_reflect/src/func/into_function.rs +++ b/crates/bevy_reflect/src/func/into_function.rs @@ -137,7 +137,7 @@ macro_rules! impl_into_function { $crate::func::DynamicFunction::new(move |args, _info| { if args.len() != COUNT { - return Err($crate::func::error::FunctionError::ArgCount { + return Err($crate::func::error::FunctionError::InvalidArgCount { expected: COUNT, received: args.len(), }); @@ -186,7 +186,7 @@ macro_rules! impl_into_function { $crate::func::DynamicFunction::new(move |args, _info| { if args.len() != COUNT { - return Err($crate::func::error::FunctionError::ArgCount { + return Err($crate::func::error::FunctionError::InvalidArgCount { expected: COUNT, received: args.len(), }); @@ -237,7 +237,7 @@ macro_rules! impl_into_function { $crate::func::DynamicFunction::new(move |args, _info| { if args.len() != COUNT { - return Err($crate::func::error::FunctionError::ArgCount { + return Err($crate::func::error::FunctionError::InvalidArgCount { expected: COUNT, received: args.len(), }); @@ -288,7 +288,7 @@ macro_rules! impl_into_function { $crate::func::DynamicFunction::new(move |args, _info| { if args.len() != COUNT { - return Err($crate::func::error::FunctionError::ArgCount { + return Err($crate::func::error::FunctionError::InvalidArgCount { expected: COUNT, received: args.len(), }); diff --git a/crates/bevy_reflect/src/func/mod.rs b/crates/bevy_reflect/src/func/mod.rs index b2a1a2f1003bb..378f0926574dc 100644 --- a/crates/bevy_reflect/src/func/mod.rs +++ b/crates/bevy_reflect/src/func/mod.rs @@ -166,7 +166,7 @@ mod tests { let result = func.call(args); assert_eq!( result.unwrap_err(), - FunctionError::ArgCount { + FunctionError::InvalidArgCount { expected: 1, received: 0 } @@ -182,7 +182,7 @@ mod tests { let result = func.call(args); assert_eq!( result.unwrap_err(), - FunctionError::ArgCount { + FunctionError::InvalidArgCount { expected: 0, received: 1 } @@ -198,7 +198,7 @@ mod tests { let result = func.call(args); assert_eq!( result.unwrap_err(), - FunctionError::Arg(ArgError::UnexpectedType { + FunctionError::ArgError(ArgError::UnexpectedType { id: ArgId::Index(0), expected: Cow::Borrowed(i32::type_path()), received: Cow::Borrowed(u32::type_path()) @@ -215,7 +215,7 @@ mod tests { let result = func.call(args); assert_eq!( result.unwrap_err(), - FunctionError::Arg(ArgError::InvalidOwnership { + FunctionError::ArgError(ArgError::InvalidOwnership { id: ArgId::Index(0), expected: Ownership::Ref, received: Ownership::Owned From ba727898f2adff817838fc4cdb49871bbce37356 Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Fri, 28 Jun 2024 22:04:36 -0700 Subject: [PATCH 28/31] Remove incorrect section from example Closures no longer need to be `'static` as we now track the lifetime of the wrapped function --- examples/reflection/function_reflection.rs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/examples/reflection/function_reflection.rs b/examples/reflection/function_reflection.rs index 163f966ecc7b3..8f02cec4574f4 100644 --- a/examples/reflection/function_reflection.rs +++ b/examples/reflection/function_reflection.rs @@ -68,13 +68,6 @@ fn main() { dbg!(increment_function.call_once(args).unwrap()); assert_eq!(count, 5); - // All closures must be `'static`— that is, they take full ownership of any captured variables. - let add_closure = |left: i32, right: i32| -> i32 { left + right }; - let mut count_function = dbg!(add_closure.into_function()); - let args = dbg!(ArgList::new().push_owned(2_i32).push_owned(2_i32)); - let value = dbg!(count_function.call(args).unwrap()).unwrap_owned(); - assert_eq!(value.take::().unwrap(), 4); - // 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. From 1b32c3ef58bb0880447055f9be63b80e71989188 Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Fri, 28 Jun 2024 22:18:33 -0700 Subject: [PATCH 29/31] Use `Marker` instead of `T` for `IntoFunction` This takes after similar concepts in bevy_ecs --- crates/bevy_reflect/src/func/into_function.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_reflect/src/func/into_function.rs b/crates/bevy_reflect/src/func/into_function.rs index a556fad1feb51..24a67a6566b63 100644 --- a/crates/bevy_reflect/src/func/into_function.rs +++ b/crates/bevy_reflect/src/func/into_function.rs @@ -84,7 +84,7 @@ use bevy_utils::all_tuples; /// [`IntoReturn`]: crate::func::IntoReturn /// [`Reflect`]: crate::Reflect /// [deriving `Reflect`]: derive@crate::Reflect -pub trait IntoFunction<'env, T> { +pub trait IntoFunction<'env, Marker> { /// Converts [`Self`] into a [`DynamicFunction`]. fn into_function(self) -> DynamicFunction<'env>; } From c95210c534621e8cb7857b8fe9e911420c7b423c Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Sat, 29 Jun 2024 16:45:15 -0700 Subject: [PATCH 30/31] Infer function name using std::any::type_name --- crates/bevy_reflect/src/func/function.rs | 6 ++++ crates/bevy_reflect/src/func/info.rs | 9 ++---- crates/bevy_reflect/src/func/into_function.rs | 4 +++ crates/bevy_reflect/src/func/mod.rs | 30 +++++++++++++++++++ 4 files changed, 43 insertions(+), 6 deletions(-) diff --git a/crates/bevy_reflect/src/func/function.rs b/crates/bevy_reflect/src/func/function.rs index 7f4ee851200ef..6661fbd7ae793 100644 --- a/crates/bevy_reflect/src/func/function.rs +++ b/crates/bevy_reflect/src/func/function.rs @@ -115,6 +115,12 @@ impl<'env> DynamicFunction<'env> { } /// Set the name of the function. + /// + /// For [`DynamicFunctions`] created using [`IntoFunction`], + /// the default name will always be the full path to the function as returned by [`std::any::type_name`]. + /// + /// [`DynamicFunctions`]: DynamicFunction + /// [`IntoFunction`]: crate::func::IntoFunction pub fn with_name(mut self, name: impl Into>) -> Self { self.info = self.info.with_name(name); self diff --git a/crates/bevy_reflect/src/func/info.rs b/crates/bevy_reflect/src/func/info.rs index 4baf9c9cd0e67..2e4c985cf2f73 100644 --- a/crates/bevy_reflect/src/func/info.rs +++ b/crates/bevy_reflect/src/func/info.rs @@ -26,7 +26,7 @@ impl FunctionInfo { /// Set the name of the function. /// - /// Reflected functions are not required to have a name and by default are not given one, + /// Reflected functions are not required to have a name, /// so this method must be called manually to set the name. pub fn with_name(mut self, name: impl Into>) -> Self { self.name = Some(name.into()); @@ -52,11 +52,8 @@ impl FunctionInfo { /// The name of the function, if it was given one. /// - /// Note that this may return `None` even if the function has a name. - /// 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`], + /// the name will always be the full path to the function as returned by [`std::any::type_name`]. /// /// [`DynamicFunctions`]: crate::func::DynamicFunction pub fn name(&self) -> Option<&str> { diff --git a/crates/bevy_reflect/src/func/into_function.rs b/crates/bevy_reflect/src/func/into_function.rs index 24a67a6566b63..351fc50ab50bc 100644 --- a/crates/bevy_reflect/src/func/into_function.rs +++ b/crates/bevy_reflect/src/func/into_function.rs @@ -123,6 +123,7 @@ macro_rules! impl_into_function { 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; @@ -171,6 +172,7 @@ macro_rules! impl_into_function { 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; @@ -222,6 +224,7 @@ macro_rules! impl_into_function { 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; @@ -273,6 +276,7 @@ macro_rules! impl_into_function { 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; diff --git a/crates/bevy_reflect/src/func/mod.rs b/crates/bevy_reflect/src/func/mod.rs index 378f0926574dc..ac0efc0c57900 100644 --- a/crates/bevy_reflect/src/func/mod.rs +++ b/crates/bevy_reflect/src/func/mod.rs @@ -157,6 +157,36 @@ mod tests { 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() {} + + let func = foo.into_function().with_name("my_function"); + assert_eq!(func.info().name(), Some("my_function")); + } + #[test] fn should_error_on_missing_args() { fn foo(_: i32) {} From 91f249fc92716f9d86f791ffaee8034960d547c0 Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Sat, 29 Jun 2024 16:50:21 -0700 Subject: [PATCH 31/31] Implement IntoFunction for DynamicFunction --- crates/bevy_reflect/src/func/function.rs | 12 ++++++++---- crates/bevy_reflect/src/func/mod.rs | 10 ++++++++++ 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/crates/bevy_reflect/src/func/function.rs b/crates/bevy_reflect/src/func/function.rs index 6661fbd7ae793..6ac4b1bbf8d85 100644 --- a/crates/bevy_reflect/src/func/function.rs +++ b/crates/bevy_reflect/src/func/function.rs @@ -2,7 +2,7 @@ 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::ReturnInfo; +use crate::func::{IntoFunction, ReturnInfo}; use alloc::borrow::Cow; use core::fmt::{Debug, Formatter}; use std::ops::DerefMut; @@ -90,8 +90,6 @@ pub type FunctionResult<'a> = Result, FunctionError>; /// // Check the result: /// assert_eq!(list, vec!["Hello, World!!!"]); /// ``` -/// -/// [`IntoFunction`]: crate::func::IntoFunction pub struct DynamicFunction<'env> { info: FunctionInfo, func: Box FnMut(ArgList<'a>, &FunctionInfo) -> FunctionResult<'a> + 'env>, @@ -120,7 +118,6 @@ impl<'env> DynamicFunction<'env> { /// the default name will always be the full path to the function as returned by [`std::any::type_name`]. /// /// [`DynamicFunctions`]: DynamicFunction - /// [`IntoFunction`]: crate::func::IntoFunction pub fn with_name(mut self, name: impl Into>) -> Self { self.info = self.info.with_name(name); self @@ -214,3 +211,10 @@ impl<'env> Debug for DynamicFunction<'env> { write!(f, ") -> {ret})") } } + +impl<'env> IntoFunction<'env, ()> for DynamicFunction<'env> { + #[inline] + fn into_function(self) -> DynamicFunction<'env> { + self + } +} diff --git a/crates/bevy_reflect/src/func/mod.rs b/crates/bevy_reflect/src/func/mod.rs index ac0efc0c57900..a964cf60f7a22 100644 --- a/crates/bevy_reflect/src/func/mod.rs +++ b/crates/bevy_reflect/src/func/mod.rs @@ -252,4 +252,14 @@ 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); + } }