diff --git a/crates/bevy_reflect/src/func/args/list.rs b/crates/bevy_reflect/src/func/args/list.rs index 6ed7eace98..145414424f 100644 --- a/crates/bevy_reflect/src/func/args/list.rs +++ b/crates/bevy_reflect/src/func/args/list.rs @@ -5,7 +5,10 @@ use crate::{ }, PartialReflect, Reflect, TypePath, }; -use alloc::{boxed::Box, collections::VecDeque}; +use alloc::{ + boxed::Box, + collections::vec_deque::{Iter, VecDeque}, +}; #[cfg(not(feature = "std"))] use alloc::{boxed::Box, format, vec}; @@ -286,6 +289,11 @@ impl<'a> ArgList<'a> { self.pop_arg()?.take_mut() } + /// Returns an iterator over the arguments in the list. + pub fn iter(&self) -> Iter<'_, Arg<'a>> { + self.list.iter() + } + /// Returns the number of arguments in the list. pub fn len(&self) -> usize { self.list.len() diff --git a/crates/bevy_reflect/src/func/dynamic_function.rs b/crates/bevy_reflect/src/func/dynamic_function.rs index 991cc86c0e..aa0a47007e 100644 --- a/crates/bevy_reflect/src/func/dynamic_function.rs +++ b/crates/bevy_reflect/src/func/dynamic_function.rs @@ -3,7 +3,9 @@ use crate::{ __macro_exports::RegisterForReflection, func::{ args::ArgList, - info::{FunctionInfo, FunctionInfoType}, + function_map::{merge_function_map, FunctionMap}, + info::FunctionInfoType, + signature::ArgumentSignature, DynamicFunctionMut, Function, FunctionError, FunctionResult, IntoFunction, IntoFunctionMut, }, serde::Serializable, @@ -12,11 +14,22 @@ use crate::{ }; use alloc::{borrow::Cow, boxed::Box, sync::Arc}; use bevy_reflect_derive::impl_type_path; +use bevy_utils::HashMap; use core::fmt::{Debug, Formatter}; #[cfg(not(feature = "std"))] use alloc::{boxed::Box, format, vec}; +/// An [`Arc`] containing a callback to a reflected function. +/// +/// The `Arc` is used to both ensure that it is `Send + Sync` +/// and to allow for the callback to be cloned. +/// +/// Note that cloning is okay since we only ever need an immutable reference +/// to call a `dyn Fn` function. +/// If we were to contain a `dyn FnMut` instead, cloning would be a lot more complicated. +type ArcFn<'env> = Arc Fn(ArgList<'a>) -> FunctionResult<'a> + Send + Sync + 'env>; + /// A dynamic representation of a function. /// /// This type can be used to represent any callable that satisfies [`Fn`] @@ -58,7 +71,7 @@ use alloc::{boxed::Box, format, vec}; pub struct DynamicFunction<'env> { pub(super) name: Option>, pub(super) info: FunctionInfoType, - pub(super) func: Arc Fn(ArgList<'a>) -> FunctionResult<'a> + Send + Sync + 'env>, + pub(super) function_map: FunctionMap>, } impl<'env> DynamicFunction<'env> { @@ -69,6 +82,7 @@ impl<'env> DynamicFunction<'env> { /// /// It's important that the function signature matches the provided [`FunctionInfo`]. /// as this will be used to validate arguments when [calling] the function. + /// This is also required in order for [function overloading] to work correctly. /// /// # Panics /// @@ -76,19 +90,30 @@ impl<'env> DynamicFunction<'env> { /// /// [calling]: crate::func::dynamic_function::DynamicFunction::call /// [`FunctionInfo`]: crate::func::FunctionInfo + /// [function overloading]: Self::with_overload pub fn new Fn(ArgList<'a>) -> FunctionResult<'a> + Send + Sync + 'env>( func: F, info: impl TryInto, ) -> Self { let info = info.try_into().unwrap(); + let func: ArcFn = Arc::new(func); + Self { name: match &info { FunctionInfoType::Standard(info) => info.name().cloned(), FunctionInfoType::Overloaded(_) => None, }, + function_map: match &info { + FunctionInfoType::Standard(_) => FunctionMap::Standard(func), + FunctionInfoType::Overloaded(infos) => { + FunctionMap::Overloaded(HashMap::from_iter(infos.iter().map(|info| { + let sig = ArgumentSignature::from(info); + (sig, func.clone()) + }))) + } + }, info, - func: Arc::new(func), } } @@ -105,6 +130,104 @@ impl<'env> DynamicFunction<'env> { self } + /// Add an overload to this function. + /// + /// Overloads allow a single [`DynamicFunction`] to represent multiple functions of different signatures. + /// + /// This can be used to handle multiple monomorphizations of a generic function + /// or to allow functions with a variable number of arguments. + /// + /// Any functions with the same [argument signature] will be overwritten by the one from the new function, `F`. + /// For example, if the existing function had the signature `(i32, i32) -> i32`, + /// and the new function, `F`, also had the signature `(i32, i32) -> i32`, + /// the one from `F` would replace the one from the existing function. + /// + /// Overloaded functions retain the [name] of the original function. + /// + /// # Examples + /// + /// ``` + /// # use std::ops::Add; + /// # use bevy_reflect::func::{ArgList, IntoFunction}; + /// # + /// fn add>(a: T, b: T) -> T { + /// a + b + /// } + /// + /// // Currently, the only generic type `func` supports is `i32`: + /// let mut func = add::.into_function(); + /// + /// // However, we can add an overload to handle `f32` as well: + /// func = func.with_overload(add::); + /// + /// // Test `i32`: + /// let args = ArgList::default().push_owned(25_i32).push_owned(75_i32); + /// let result = func.call(args).unwrap().unwrap_owned(); + /// assert_eq!(result.try_take::().unwrap(), 100); + /// + /// // Test `f32`: + /// let args = ArgList::default().push_owned(25.0_f32).push_owned(75.0_f32); + /// let result = func.call(args).unwrap().unwrap_owned(); + /// assert_eq!(result.try_take::().unwrap(), 100.0); + ///``` + /// + /// ``` + /// # use bevy_reflect::func::{ArgList, IntoFunction}; + /// # + /// fn add_2(a: i32, b: i32) -> i32 { + /// a + b + /// } + /// + /// fn add_3(a: i32, b: i32, c: i32) -> i32 { + /// a + b + c + /// } + /// + /// // Currently, `func` only supports two arguments. + /// let mut func = add_2.into_function(); + /// + /// // However, we can add an overload to handle three arguments as well. + /// func = func.with_overload(add_3); + /// + /// // Test two arguments: + /// let args = ArgList::default().push_owned(25_i32).push_owned(75_i32); + /// let result = func.call(args).unwrap().unwrap_owned(); + /// assert_eq!(result.try_take::().unwrap(), 100); + /// + /// // Test three arguments: + /// let args = ArgList::default() + /// .push_owned(25_i32) + /// .push_owned(75_i32) + /// .push_owned(100_i32); + /// let result = func.call(args).unwrap().unwrap_owned(); + /// assert_eq!(result.try_take::().unwrap(), 200); + /// ``` + /// + /// [argument signature]: ArgumentSignature + /// [name]: Self::name + pub fn with_overload<'a, F: IntoFunction<'a, Marker>, Marker>( + self, + function: F, + ) -> DynamicFunction<'a> + where + 'env: 'a, + { + let function = function.into_function(); + + let name = self.name; + let (function_map, info) = merge_function_map( + self.function_map, + self.info, + function.function_map, + function.info, + ); + + DynamicFunction { + name, + info, + function_map, + } + } + /// Call the function with the given arguments. /// /// # Example @@ -132,13 +255,26 @@ impl<'env> DynamicFunction<'env> { let expected_arg_count = self.info.arg_count(); let received_arg_count = args.len(); - if expected_arg_count != received_arg_count { - Err(FunctionError::ArgCountMismatch { - expected: expected_arg_count, - received: received_arg_count, - }) - } else { - (self.func)(args) + match self.function_map { + FunctionMap::Standard(ref func) => { + if expected_arg_count != received_arg_count { + Err(FunctionError::ArgCountMismatch { + expected: expected_arg_count, + received: received_arg_count, + }) + } else { + func(args) + } + } + FunctionMap::Overloaded(ref map) => { + let sig = ArgumentSignature::from(&args); + let func = map.get(&sig).ok_or_else(|| FunctionError::NoOverload { + expected: map.keys().cloned().collect(), + received: sig, + })?; + + func(args) + } } } @@ -156,8 +292,11 @@ impl<'env> DynamicFunction<'env> { /// /// This can be overridden using [`with_name`]. /// + /// If the function was [overloaded], it will retain its original name if it had one. + /// /// [`DynamicFunctions`]: DynamicFunction /// [`with_name`]: Self::with_name + /// [overloaded]: Self::with_overload pub fn name(&self) -> Option<&Cow<'static, str>> { self.name.as_ref() } @@ -306,7 +445,7 @@ impl<'env> Clone for DynamicFunction<'env> { Self { name: self.name.clone(), info: self.info.clone(), - func: Arc::clone(&self.func), + function_map: self.function_map.clone(), } } } @@ -329,6 +468,9 @@ impl<'env> IntoFunctionMut<'env, ()> for DynamicFunction<'env> { mod tests { use super::*; use crate::func::{FunctionInfo, IntoReturn}; + use crate::Type; + use bevy_utils::HashSet; + use std::ops::Add; #[test] fn should_overwrite_function_name() { @@ -476,4 +618,115 @@ mod tests { let result = func.call(args).unwrap().unwrap_owned(); assert_eq!(result.try_take::().unwrap(), 100.0); } + + #[test] + #[should_panic( + expected = "called `Result::unwrap()` on an `Err` value: MissingFunctionInfoError" + )] + fn should_panic_on_missing_function_info() { + let _ = DynamicFunction::new(|_| Ok(().into_return()), Vec::new()); + } + + #[test] + fn should_allow_function_overloading() { + fn add>(a: T, b: T) -> T { + a + b + } + + let func = add::.into_function().with_overload(add::); + + let args = ArgList::default().push_owned(25_i32).push_owned(75_i32); + let result = func.call(args).unwrap().unwrap_owned(); + assert_eq!(result.try_take::().unwrap(), 100); + + let args = ArgList::default().push_owned(25.0_f32).push_owned(75.0_f32); + let result = func.call(args).unwrap().unwrap_owned(); + assert_eq!(result.try_take::().unwrap(), 100.0); + } + + #[test] + fn should_allow_variable_arguments_via_overloading() { + fn add_2(a: i32, b: i32) -> i32 { + a + b + } + + fn add_3(a: i32, b: i32, c: i32) -> i32 { + a + b + c + } + + let func = add_2.into_function().with_overload(add_3); + + let args = ArgList::default().push_owned(25_i32).push_owned(75_i32); + let result = func.call(args).unwrap().unwrap_owned(); + assert_eq!(result.try_take::().unwrap(), 100); + + let args = ArgList::default() + .push_owned(25_i32) + .push_owned(75_i32) + .push_owned(100_i32); + let result = func.call(args).unwrap().unwrap_owned(); + assert_eq!(result.try_take::().unwrap(), 200); + } + + #[test] + fn should_allow_function_overloading_with_manual_overload() { + let manual = DynamicFunction::new( + |mut args| { + let a = args.take_arg()?; + let b = args.take_arg()?; + + if a.is::() { + let a = a.take::()?; + let b = b.take::()?; + Ok((a + b).into_return()) + } else { + let a = a.take::()?; + let b = b.take::()?; + Ok((a + b).into_return()) + } + }, + vec![ + FunctionInfo::named("add::") + .with_arg::("a") + .with_arg::("b") + .with_return::(), + FunctionInfo::named("add::") + .with_arg::("a") + .with_arg::("b") + .with_return::(), + ], + ); + + let func = manual.with_overload(|a: u32, b: u32| a + b); + + let args = ArgList::default().push_owned(25_i32).push_owned(75_i32); + let result = func.call(args).unwrap().unwrap_owned(); + assert_eq!(result.try_take::().unwrap(), 100); + + let args = ArgList::default().push_owned(25_u32).push_owned(75_u32); + let result = func.call(args).unwrap().unwrap_owned(); + assert_eq!(result.try_take::().unwrap(), 100); + } + + #[test] + fn should_return_error_on_unknown_overload() { + fn add>(a: T, b: T) -> T { + a + b + } + + let func = add::.into_function().with_overload(add::); + + let args = ArgList::default().push_owned(25_u32).push_owned(75_u32); + let result = func.call(args); + assert_eq!( + result.unwrap_err(), + FunctionError::NoOverload { + expected: HashSet::from([ + ArgumentSignature::from_iter(vec![Type::of::(), Type::of::()]), + ArgumentSignature::from_iter(vec![Type::of::(), Type::of::()]) + ]), + received: ArgumentSignature::from_iter(vec![Type::of::(), Type::of::()]), + } + ); + } } diff --git a/crates/bevy_reflect/src/func/dynamic_function_mut.rs b/crates/bevy_reflect/src/func/dynamic_function_mut.rs index 1ad379e6d4..c00866c2f3 100644 --- a/crates/bevy_reflect/src/func/dynamic_function_mut.rs +++ b/crates/bevy_reflect/src/func/dynamic_function_mut.rs @@ -1,14 +1,19 @@ -use alloc::{borrow::Cow, boxed::Box}; +use alloc::{borrow::Cow, boxed::Box, sync::Arc}; use core::fmt::{Debug, Formatter}; #[cfg(not(feature = "std"))] use alloc::{boxed::Box, format, vec}; use crate::func::{ - args::ArgList, DynamicFunction, FunctionError, FunctionInfoType, FunctionResult, - IntoFunctionMut, + args::ArgList, + function_map::{merge_function_map, FunctionMap}, + signature::ArgumentSignature, + DynamicFunction, FunctionError, FunctionInfoType, FunctionResult, IntoFunctionMut, }; +/// A [`Box`] containing a callback to a reflected function. +type BoxFnMut<'env> = Box FnMut(ArgList<'a>) -> FunctionResult<'a> + 'env>; + /// A dynamic representation of a function. /// /// This type can be used to represent any callable that satisfies [`FnMut`] @@ -68,7 +73,7 @@ use crate::func::{ pub struct DynamicFunctionMut<'env> { name: Option>, info: FunctionInfoType, - func: Box FnMut(ArgList<'a>) -> FunctionResult<'a> + 'env>, + function_map: FunctionMap>, } impl<'env> DynamicFunctionMut<'env> { @@ -79,6 +84,7 @@ impl<'env> DynamicFunctionMut<'env> { /// /// It's important that the function signature matches the provided [`FunctionInfo`]. /// as this will be used to validate arguments when [calling] the function. + /// This is also required in order for [function overloading] to work correctly. /// /// # Panics /// @@ -86,6 +92,7 @@ impl<'env> DynamicFunctionMut<'env> { /// /// [calling]: crate::func::dynamic_function_mut::DynamicFunctionMut::call /// [`FunctionInfo`]: crate::func::FunctionInfo + /// [function overloading]: Self::with_overload pub fn new FnMut(ArgList<'a>) -> FunctionResult<'a> + 'env>( func: F, info: impl TryInto, @@ -98,7 +105,7 @@ impl<'env> DynamicFunctionMut<'env> { FunctionInfoType::Overloaded(_) => None, }, info, - func: Box::new(func), + function_map: FunctionMap::Standard(Box::new(func)), } } @@ -115,6 +122,80 @@ impl<'env> DynamicFunctionMut<'env> { self } + /// Add an overload to this function. + /// + /// Overloads allow a single [`DynamicFunctionMut`] to represent multiple functions of different signatures. + /// + /// This can be used to handle multiple monomorphizations of a generic function + /// or to allow functions with a variable number of arguments. + /// + /// Any functions with the same [argument signature] will be overwritten by the one from the new function, `F`. + /// For example, if the existing function had the signature `(i32, i32) -> i32`, + /// and the new function, `F`, also had the signature `(i32, i32) -> i32`, + /// the one from `F` would replace the one from the existing function. + /// + /// Overloaded functions retain the [name] of the original function. + /// + /// Note that it may be impossible to overload closures that mutably borrow from their environment + /// due to Rust's borrowing rules. + /// However, it's still possible to overload functions that do not capture their environment mutably, + /// or those that maintain mutually exclusive mutable references to their environment. + /// + /// # Example + /// + /// ``` + /// # use bevy_reflect::func::IntoFunctionMut; + /// let mut total_i32 = 0; + /// let mut add_i32 = |a: i32| total_i32 += a; + /// + /// let mut total_f32 = 0.0; + /// let mut add_f32 = |a: f32| total_f32 += a; + /// + /// // Currently, the only generic type `func` supports is `i32`. + /// let mut func = add_i32.into_function_mut(); + /// + /// // However, we can add an overload to handle `f32` as well: + /// func = func.with_overload(add_f32); + /// + /// // Test `i32`: + /// let args = bevy_reflect::func::ArgList::new().push_owned(123_i32); + /// func.call(args).unwrap(); + /// + /// // Test `f32`: + /// let args = bevy_reflect::func::ArgList::new().push_owned(1.23_f32); + /// func.call(args).unwrap(); + /// + /// drop(func); + /// assert_eq!(total_i32, 123); + /// assert_eq!(total_f32, 1.23); + /// ``` + /// + /// [argument signature]: ArgumentSignature + /// [name]: Self::name + pub fn with_overload<'a, F: IntoFunctionMut<'a, Marker>, Marker>( + self, + function: F, + ) -> DynamicFunctionMut<'a> + where + 'env: 'a, + { + let function = function.into_function_mut(); + + let name = self.name; + let (function_map, info) = merge_function_map( + self.function_map, + self.info, + function.function_map, + function.info, + ); + + DynamicFunctionMut { + name, + info, + function_map, + } + } + /// Call the function with the given arguments. /// /// Variables that are captured mutably by this function @@ -150,13 +231,28 @@ impl<'env> DynamicFunctionMut<'env> { let expected_arg_count = self.info.arg_count(); let received_arg_count = args.len(); - if expected_arg_count != received_arg_count { - Err(FunctionError::ArgCountMismatch { - expected: expected_arg_count, - received: received_arg_count, - }) - } else { - (self.func)(args) + match self.function_map { + FunctionMap::Standard(ref mut func) => { + if expected_arg_count != received_arg_count { + Err(FunctionError::ArgCountMismatch { + expected: expected_arg_count, + received: received_arg_count, + }) + } else { + func(args) + } + } + FunctionMap::Overloaded(ref mut map) => { + let sig = ArgumentSignature::from(&args); + if let Some(func) = map.get_mut(&sig) { + func(args) + } else { + Err(FunctionError::NoOverload { + expected: map.keys().cloned().collect(), + received: sig, + }) + } + } } } @@ -189,17 +285,7 @@ impl<'env> DynamicFunctionMut<'env> { /// /// The function itself may also return any errors it needs to. pub fn call_once(mut self, args: ArgList) -> FunctionResult { - let expected_arg_count = self.info.arg_count(); - let received_arg_count = args.len(); - - if expected_arg_count != received_arg_count { - Err(FunctionError::ArgCountMismatch { - expected: expected_arg_count, - received: received_arg_count, - }) - } else { - (self.func)(args) - } + self.call(args) } /// Returns the function info. @@ -261,11 +347,28 @@ impl<'env> From> for DynamicFunctionMut<'env> { Self { name: function.name, info: function.info, - func: Box::new(move |args| (function.func)(args)), + function_map: match function.function_map { + FunctionMap::Standard(func) => FunctionMap::Standard(arc_to_box(func)), + FunctionMap::Overloaded(functions) => FunctionMap::Overloaded( + functions + .into_iter() + .map(|(name, func)| (name, arc_to_box(func))) + .collect(), + ), + }, } } } +/// Helper function from converting an [`Arc`] function to a [`Box`] function. +/// +/// This is needed to help the compiler infer the correct types. +fn arc_to_box<'env>( + f: Arc Fn(ArgList<'a>) -> FunctionResult<'a> + 'env>, +) -> BoxFnMut<'env> { + Box::new(move |args| f(args)) +} + impl<'env> IntoFunctionMut<'env, ()> for DynamicFunctionMut<'env> { #[inline] fn into_function_mut(self) -> DynamicFunctionMut<'env> { @@ -277,6 +380,7 @@ impl<'env> IntoFunctionMut<'env, ()> for DynamicFunctionMut<'env> { mod tests { use super::*; use crate::func::{FunctionInfo, IntoReturn}; + use std::ops::Add; #[test] fn should_overwrite_function_name() { @@ -360,4 +464,25 @@ mod tests { drop(func); assert_eq!(total, 100); } + + // Closures that mutably borrow from their environment cannot realistically + // be overloaded since that would break Rust's borrowing rules. + // However, we still need to verify overloaded functions work since a + // `DynamicFunctionMut` can also be made from a non-mutably borrowing closure/function. + #[test] + fn should_allow_function_overloading() { + fn add>(a: T, b: T) -> T { + a + b + } + + let mut func = add::.into_function_mut().with_overload(add::); + + let args = ArgList::default().push_owned(25_i32).push_owned(75_i32); + let result = func.call(args).unwrap().unwrap_owned(); + assert_eq!(result.try_take::().unwrap(), 100); + + let args = ArgList::default().push_owned(25.0_f32).push_owned(75.0_f32); + let result = func.call(args).unwrap().unwrap_owned(); + assert_eq!(result.try_take::().unwrap(), 100.0); + } } diff --git a/crates/bevy_reflect/src/func/error.rs b/crates/bevy_reflect/src/func/error.rs index 9a6b5407fb..3dbd70386d 100644 --- a/crates/bevy_reflect/src/func/error.rs +++ b/crates/bevy_reflect/src/func/error.rs @@ -1,5 +1,7 @@ +use crate::func::signature::ArgumentSignature; use crate::func::{args::ArgError, Return}; use alloc::borrow::Cow; +use bevy_utils::HashSet; use thiserror::Error; #[cfg(not(feature = "std"))] @@ -17,6 +19,12 @@ pub enum FunctionError { /// The number of arguments provided does not match the expected number. #[error("expected {expected} arguments but received {received}")] ArgCountMismatch { expected: usize, received: usize }, + /// No overload was found for the given set of arguments. + #[error("no overload found for arguments with signature `{received:?}`, expected one of `{expected:?}`")] + NoOverload { + expected: HashSet, + received: ArgumentSignature, + }, } /// The result of calling a [`DynamicFunction`] or [`DynamicFunctionMut`]. diff --git a/crates/bevy_reflect/src/func/function_map.rs b/crates/bevy_reflect/src/func/function_map.rs new file mode 100644 index 0000000000..f4ebf81928 --- /dev/null +++ b/crates/bevy_reflect/src/func/function_map.rs @@ -0,0 +1,102 @@ +use crate::func::signature::ArgumentSignature; +use crate::func::FunctionInfoType; +use bevy_utils::HashMap; + +/// A helper type for storing either a single function or a mapping of overloaded functions. +#[derive(Clone)] +pub(super) enum FunctionMap { + Standard(F), + Overloaded(HashMap), +} + +/// Merges the given [`FunctionMap`]s and [`FunctionInfoType`]s into a new [`FunctionMap`] and [`FunctionInfoType`]. +/// +/// # Panics +/// +/// Panics if a [`FunctionMap`]'s corresponding [`FunctionInfoType`] does not match its overload status. +pub(super) fn merge_function_map( + map_a: FunctionMap, + info_a: FunctionInfoType, + map_b: FunctionMap, + info_b: FunctionInfoType, +) -> (FunctionMap, FunctionInfoType) { + match (map_a, info_a, map_b, info_b) { + ( + FunctionMap::Standard(old), + FunctionInfoType::Standard(info_a), + FunctionMap::Standard(new), + FunctionInfoType::Standard(info_b), + ) => { + let sig_a = ArgumentSignature::from(&info_a); + let sig_b = ArgumentSignature::from(&info_b); + + if sig_a == sig_b { + ( + FunctionMap::Standard(new), + FunctionInfoType::Standard(info_b), + ) + } else { + ( + FunctionMap::Overloaded(HashMap::from([(sig_a, old), (sig_b, new)])), + FunctionInfoType::Overloaded(Box::new([info_a, info_b])), + ) + } + } + ( + FunctionMap::Overloaded(old), + FunctionInfoType::Overloaded(info_a), + FunctionMap::Standard(new), + FunctionInfoType::Standard(info_b), + ) => { + let sig_b = ArgumentSignature::from(&info_b); + let mut map = old; + map.insert(sig_b, new); + + let mut info = Vec::from_iter(info_a); + info.push(info_b); + + ( + FunctionMap::Overloaded(map), + FunctionInfoType::Overloaded(info.into_boxed_slice()), + ) + } + ( + FunctionMap::Standard(old), + FunctionInfoType::Standard(info_a), + FunctionMap::Overloaded(new), + FunctionInfoType::Overloaded(info_b), + ) => { + let sig_a = ArgumentSignature::from(&info_a); + let mut map = new; + map.insert(sig_a, old); + + let mut info = vec![info_a]; + info.extend(info_b); + + ( + FunctionMap::Overloaded(map), + FunctionInfoType::Overloaded(info.into_boxed_slice()), + ) + } + ( + FunctionMap::Overloaded(map1), + FunctionInfoType::Overloaded(info_a), + FunctionMap::Overloaded(map2), + FunctionInfoType::Overloaded(info_b), + ) => { + let mut map = map1; + map.extend(map2); + + let mut info = Vec::from_iter(info_a); + info.extend(info_b); + + ( + FunctionMap::Overloaded(map), + FunctionInfoType::Overloaded(info.into_boxed_slice()), + ) + } + _ => { + panic!("`FunctionMap` and `FunctionInfoType` mismatch"); + } + } +} diff --git a/crates/bevy_reflect/src/func/mod.rs b/crates/bevy_reflect/src/func/mod.rs index b21b78d914..4e0e89bc41 100644 --- a/crates/bevy_reflect/src/func/mod.rs +++ b/crates/bevy_reflect/src/func/mod.rs @@ -146,6 +146,7 @@ mod dynamic_function; mod dynamic_function_mut; mod error; mod function; +mod function_map; mod info; mod into_function; mod into_function_mut; diff --git a/crates/bevy_reflect/src/func/signature.rs b/crates/bevy_reflect/src/func/signature.rs index eaea303d1b..b330db3706 100644 --- a/crates/bevy_reflect/src/func/signature.rs +++ b/crates/bevy_reflect/src/func/signature.rs @@ -10,7 +10,7 @@ //! arguments and return type. use crate::func::args::ArgInfo; -use crate::func::FunctionInfo; +use crate::func::{ArgList, FunctionInfo}; use crate::Type; use core::borrow::Borrow; use core::fmt::{Debug, Formatter}; @@ -107,6 +107,24 @@ impl> From for ArgumentSignature { } } +impl From<&ArgList<'_>> for ArgumentSignature { + fn from(args: &ArgList) -> Self { + Self( + args.iter() + .map(|arg| { + arg.value() + .get_represented_type_info() + .unwrap_or_else(|| { + panic!("no `TypeInfo` found for argument: {:?}", arg); + }) + .ty() + }) + .copied() + .collect(), + ) + } +} + #[cfg(test)] mod tests { use super::*;