Added reflection function overloading

This commit is contained in:
Gino Valente 2024-09-02 19:40:18 -07:00
parent ba987ef60d
commit 8ce37d975a
7 changed files with 552 additions and 37 deletions

View file

@ -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()

View file

@ -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<dyn for<'a> 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<Cow<'static, str>>,
pub(super) info: FunctionInfoType,
pub(super) func: Arc<dyn for<'a> Fn(ArgList<'a>) -> FunctionResult<'a> + Send + Sync + 'env>,
pub(super) function_map: FunctionMap<ArcFn<'env>>,
}
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<F: for<'a> Fn(ArgList<'a>) -> FunctionResult<'a> + Send + Sync + 'env>(
func: F,
info: impl TryInto<FunctionInfoType, Error: Debug>,
) -> 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<T: Add<Output = T>>(a: T, b: T) -> T {
/// a + b
/// }
///
/// // Currently, the only generic type `func` supports is `i32`:
/// let mut func = add::<i32>.into_function();
///
/// // However, we can add an overload to handle `f32` as well:
/// func = func.with_overload(add::<f32>);
///
/// // 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::<i32>().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::<f32>().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::<i32>().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::<i32>().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();
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 {
(self.func)(args)
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::<f32>().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<T: Add<Output = T>>(a: T, b: T) -> T {
a + b
}
let func = add::<i32>.into_function().with_overload(add::<f32>);
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::<i32>().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::<f32>().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::<i32>().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::<i32>().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::<i32>() {
let a = a.take::<i32>()?;
let b = b.take::<i32>()?;
Ok((a + b).into_return())
} else {
let a = a.take::<f32>()?;
let b = b.take::<f32>()?;
Ok((a + b).into_return())
}
},
vec![
FunctionInfo::named("add::<i32>")
.with_arg::<i32>("a")
.with_arg::<i32>("b")
.with_return::<i32>(),
FunctionInfo::named("add::<f32>")
.with_arg::<f32>("a")
.with_arg::<f32>("b")
.with_return::<f32>(),
],
);
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::<i32>().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::<u32>().unwrap(), 100);
}
#[test]
fn should_return_error_on_unknown_overload() {
fn add<T: Add<Output = T>>(a: T, b: T) -> T {
a + b
}
let func = add::<i32>.into_function().with_overload(add::<f32>);
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::<i32>(), Type::of::<i32>()]),
ArgumentSignature::from_iter(vec![Type::of::<f32>(), Type::of::<f32>()])
]),
received: ArgumentSignature::from_iter(vec![Type::of::<u32>(), Type::of::<u32>()]),
}
);
}
}

View file

@ -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<dyn for<'a> 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<Cow<'static, str>>,
info: FunctionInfoType,
func: Box<dyn for<'a> FnMut(ArgList<'a>) -> FunctionResult<'a> + 'env>,
function_map: FunctionMap<BoxFnMut<'env>>,
}
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<F: for<'a> FnMut(ArgList<'a>) -> FunctionResult<'a> + 'env>(
func: F,
info: impl TryInto<FunctionInfoType, Error: Debug>,
@ -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();
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 {
(self.func)(args)
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<DynamicFunction<'env>> 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<dyn for<'a> 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<T: Add<Output = T>>(a: T, b: T) -> T {
a + b
}
let mut func = add::<i32>.into_function_mut().with_overload(add::<f32>);
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::<i32>().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::<f32>().unwrap(), 100.0);
}
}

View file

@ -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<ArgumentSignature>,
received: ArgumentSignature,
},
}
/// The result of calling a [`DynamicFunction`] or [`DynamicFunctionMut`].

View file

@ -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<F> {
Standard(F),
Overloaded(HashMap<ArgumentSignature, F>),
}
/// 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<F>(
map_a: FunctionMap<F>,
info_a: FunctionInfoType,
map_b: FunctionMap<F>,
info_b: FunctionInfoType,
) -> (FunctionMap<F>, 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");
}
}
}

View file

@ -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;

View file

@ -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<T: Borrow<FunctionInfo>> From<T> 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::*;