Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

bevy_reflect: Function reflection #13152

Merged
Merged
Show file tree
Hide file tree
Changes from 29 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
784a329
Add support for dynamic functions
MrGVSV Apr 28, 2024
4e47c86
Add support for reference return types
MrGVSV Apr 28, 2024
2d8804d
Simplify ArgInfo
MrGVSV Apr 28, 2024
42f51e9
Add ReturnInfo
MrGVSV Apr 28, 2024
277c3cb
Simplify Debug output for Function
MrGVSV Apr 28, 2024
d274242
Update FunctionInfo::new signature
MrGVSV Apr 29, 2024
6395ff5
Add ArgList::new function
MrGVSV Apr 29, 2024
4dd061c
Add documentation
MrGVSV Apr 29, 2024
83c4090
Rename module
MrGVSV Apr 29, 2024
73f07b9
Move local macros to derive macro
MrGVSV Apr 30, 2024
4c62a83
Derive Debug on Arg and ArgList
MrGVSV Apr 30, 2024
d6ab4c0
Add lifetime to Function
MrGVSV Apr 30, 2024
33eefd6
Allow &mut receiver to return & data
MrGVSV Apr 30, 2024
010121e
Add function_reflection example
MrGVSV Apr 30, 2024
f0b575d
Fix formatting and lints
MrGVSV Apr 30, 2024
6952bce
Apply suggestions from code review
MrGVSV May 5, 2024
e51ac2c
Update docs
MrGVSV May 5, 2024
a70dd8a
Add some basic compile fail tests
MrGVSV May 5, 2024
210564d
Expose function info
MrGVSV Jun 25, 2024
aa000ff
Add clarifying doc comment
MrGVSV Jun 25, 2024
c6f7501
Update derives on function info types
MrGVSV Jun 26, 2024
9d01602
Rename `Function` -> `DynamicFunction`
MrGVSV Jun 26, 2024
e997efa
Rename `FuncError` -> `FunctionError`
MrGVSV Jun 26, 2024
8654233
Add manual construction example to `DynamicFunction`
MrGVSV Jun 26, 2024
184af80
Improve documentation
MrGVSV Jun 29, 2024
4088843
Improve macro documentation
MrGVSV Jun 29, 2024
b8ed0a8
Make FunctionError variants sound more error-y
MrGVSV Jun 29, 2024
ba72789
Remove incorrect section from example
MrGVSV Jun 29, 2024
1b32c3e
Use `Marker` instead of `T` for `IntoFunction`
MrGVSV Jun 29, 2024
c95210c
Infer function name using std::any::type_name
MrGVSV Jun 29, 2024
91f249f
Implement IntoFunction for DynamicFunction
MrGVSV Jun 29, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
4 changes: 4 additions & 0 deletions crates/bevy_reflect/compile_fail/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,7 @@ compile_fail_utils = { path = "../../../tools/compile_fail_utils" }
[[test]]
name = "derive"
harness = false

[[test]]
name = "func"
harness = false
3 changes: 2 additions & 1 deletion crates/bevy_reflect/compile_fail/tests/derive.rs
Original file line number Diff line number Diff line change
@@ -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(())
}
3 changes: 3 additions & 0 deletions crates/bevy_reflect/compile_fail/tests/func.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
fn main() -> compile_fail_utils::ui_test::Result<()> {
compile_fail_utils::test("tests/into_function")
}
Original file line number Diff line number Diff line change
@@ -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
}
Original file line number Diff line number Diff line change
@@ -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
}
6 changes: 5 additions & 1 deletion crates/bevy_reflect/derive/src/impls/enums.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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) =
Expand All @@ -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 {
Expand Down
47 changes: 47 additions & 0 deletions crates/bevy_reflect/derive/src/impls/func/from_arg.rs
Original file line number Diff line number Diff line change
@@ -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<Self::Item<'from_arg>, #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<Self::Item<'from_arg>, #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<Self::Item<'from_arg>, #bevy_reflect::func::args::ArgError> {
arg.take_mut(info)
}
}
}
}
23 changes: 23 additions & 0 deletions crates/bevy_reflect/derive/src/impls/func/function_impls.rs
Original file line number Diff line number Diff line change
@@ -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
}
}
34 changes: 34 additions & 0 deletions crates/bevy_reflect/derive/src/impls/func/get_ownership.rs
Original file line number Diff line number Diff line change
@@ -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
}
}
}
}
34 changes: 34 additions & 0 deletions crates/bevy_reflect/derive/src/impls/func/into_return.rs
Original file line number Diff line number Diff line change
@@ -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)
}
}
}
}
6 changes: 6 additions & 0 deletions crates/bevy_reflect/derive/src/impls/func/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
pub(crate) use function_impls::impl_function_traits;

mod from_arg;
mod function_impls;
mod get_ownership;
mod into_return;
5 changes: 3 additions & 2 deletions crates/bevy_reflect/derive/src/impls/mod.rs
Original file line number Diff line number Diff line change
@@ -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;
6 changes: 5 additions & 1 deletion crates/bevy_reflect/derive/src/impls/structs.rs
Original file line number Diff line number Diff line change
@@ -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};
Expand Down Expand Up @@ -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
Expand All @@ -71,6 +73,8 @@ pub(crate) fn impl_struct(reflect_struct: &ReflectStruct) -> proc_macro2::TokenS

#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> {
match name {
Expand Down
6 changes: 5 additions & 1 deletion crates/bevy_reflect/derive/src/impls/tuple_structs.rs
Original file line number Diff line number Diff line change
@@ -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};
Expand Down Expand Up @@ -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()
Expand All @@ -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 {
Expand Down
6 changes: 5 additions & 1 deletion crates/bevy_reflect/derive/src/impls/values.rs
Original file line number Diff line number Diff line change
@@ -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};
Expand Down Expand Up @@ -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);
Expand All @@ -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> {
Expand Down
8 changes: 6 additions & 2 deletions crates/bevy_reflect/derive/src/utility.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading