bevy_reflect: Feature-gate function reflection (#14174)

# Objective

Function reflection requires a lot of macro code generation in the form
of several `all_tuples!` invocations, as well as impls generated in the
`Reflect` derive macro.

Seeing as function reflection is currently a bit more niche, it makes
sense to gate it all behind a feature.

## Solution

Add a `functions` feature to `bevy_reflect`, which can be enabled in
Bevy using the `reflect_functions` feature.

## Testing

You can test locally by running:

```
cargo test --package bevy_reflect
```

That should ensure that everything still works with the feature
disabled.

To test with the feature on, you can run:

```
cargo test --package bevy_reflect --features functions
```

---

## Changelog

- Moved function reflection behind a Cargo feature
(`bevy/reflect_functions` and `bevy_reflect/functions`)
- Add `IntoFunction` export in `bevy_reflect::prelude`

## Internal Migration Guide

> [!important]
> Function reflection was introduced as part of the 0.15 dev cycle. This
migration guide was written for developers relying on `main` during this
cycle, and is not a breaking change coming from 0.14.

Function reflection is now gated behind a feature. To use function
reflection, enable the feature:
- If using `bevy_reflect` directly, enable the `functions` feature
- If using `bevy`, enable the `reflect_functions` feature
This commit is contained in:
Gino Valente 2024-07-14 08:55:31 -07:00 committed by GitHub
parent 57d05927d6
commit 99c9218b56
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
21 changed files with 89 additions and 42 deletions

View file

@ -348,6 +348,9 @@ ios_simulator = ["bevy_internal/ios_simulator"]
# Enable built in global state machines
bevy_state = ["bevy_internal/bevy_state"]
# Enable function reflection
reflect_functions = ["bevy_internal/reflect_functions"]
[dependencies]
bevy_internal = { path = "crates/bevy_internal", version = "0.15.0-dev", default-features = false }
@ -2158,6 +2161,7 @@ wasm = false
name = "function_reflection"
path = "examples/reflection/function_reflection.rs"
doc-scrape-examples = true
required-features = ["reflect_functions"]
[package.metadata.example.function_reflection]
name = "Function Reflection"

View file

@ -195,6 +195,9 @@ ios_simulator = ["bevy_pbr?/ios_simulator", "bevy_render?/ios_simulator"]
# Enable built in global state machines
bevy_state = ["dep:bevy_state"]
# Enable function reflection
reflect_functions = ["bevy_reflect/functions"]
[dependencies]
# bevy
bevy_a11y = { path = "../bevy_a11y", version = "0.15.0-dev" }

View file

@ -19,6 +19,8 @@ smallvec = ["dep:smallvec"]
uuid = ["dep:uuid"]
# When enabled, allows documentation comments to be accessed via reflection
documentation = ["bevy_reflect_derive/documentation"]
# Enables function reflection
functions = ["bevy_reflect_derive/functions"]
[dependencies]
# bevy

View file

@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0"
publish = false
[dependencies]
bevy_reflect = { path = "../" }
bevy_reflect = { path = "../", features = ["functions"] }
[dev-dependencies]
compile_fail_utils = { path = "../../../tools/compile_fail_utils" }

View file

@ -15,6 +15,8 @@ proc-macro = true
default = []
# When enabled, allows documentation comments to be processed by the reflection macros
documentation = []
# Enables macro logic related to function reflection
functions = []
[dependencies]
bevy_macro_utils = { path = "../../bevy_macro_utils", version = "0.15.0-dev" }

View file

@ -1,6 +1,6 @@
use crate::derive_data::{EnumVariantFields, ReflectEnum, StructField};
use crate::enum_utility::{EnumVariantOutputData, TryApplyVariantBuilder, VariantBuilder};
use crate::impls::{impl_function_traits, impl_type_path, impl_typed};
use crate::impls::{impl_type_path, impl_typed};
use bevy_macro_utils::fq_std::{FQAny, FQBox, FQOption, FQResult};
use proc_macro2::{Ident, Span};
use quote::quote;
@ -65,7 +65,11 @@ 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);
#[cfg(not(feature = "functions"))]
let function_impls = None::<proc_macro2::TokenStream>;
#[cfg(feature = "functions")]
let function_impls =
crate::impls::impl_function_traits(reflect_enum.meta(), &where_clause_options);
let get_type_registration_impl = reflect_enum.get_type_registration(&where_clause_options);

View file

@ -1,4 +1,5 @@
mod enums;
#[cfg(feature = "functions")]
mod func;
mod structs;
mod tuple_structs;
@ -6,6 +7,7 @@ mod typed;
mod values;
pub(crate) use enums::impl_enum;
#[cfg(feature = "functions")]
pub(crate) use func::impl_function_traits;
pub(crate) use structs::impl_struct;
pub(crate) use tuple_structs::impl_tuple_struct;

View file

@ -1,4 +1,4 @@
use crate::impls::{impl_function_traits, impl_type_path, impl_typed};
use crate::impls::{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,7 +54,11 @@ 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);
#[cfg(not(feature = "functions"))]
let function_impls = None::<proc_macro2::TokenStream>;
#[cfg(feature = "functions")]
let function_impls =
crate::impls::impl_function_traits(reflect_struct.meta(), &where_clause_options);
let get_type_registration_impl = reflect_struct.get_type_registration(&where_clause_options);

View file

@ -1,4 +1,4 @@
use crate::impls::{impl_function_traits, impl_type_path, impl_typed};
use crate::impls::{impl_type_path, impl_typed};
use crate::ReflectStruct;
use bevy_macro_utils::fq_std::{FQAny, FQBox, FQDefault, FQOption, FQResult};
use quote::{quote, ToTokens};
@ -46,7 +46,11 @@ 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);
#[cfg(not(feature = "functions"))]
let function_impls = None::<proc_macro2::TokenStream>;
#[cfg(feature = "functions")]
let function_impls =
crate::impls::impl_function_traits(reflect_struct.meta(), &where_clause_options);
let (impl_generics, ty_generics, where_clause) = reflect_struct
.meta()

View file

@ -1,4 +1,4 @@
use crate::impls::{impl_function_traits, impl_type_path, impl_typed};
use crate::impls::{impl_type_path, impl_typed};
use crate::utility::WhereClauseOptions;
use crate::ReflectMeta;
use bevy_macro_utils::fq_std::{FQAny, FQBox, FQClone, FQOption, FQResult};
@ -33,7 +33,10 @@ 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);
#[cfg(not(feature = "functions"))]
let function_impls = None::<proc_macro2::TokenStream>;
#[cfg(feature = "functions")]
let function_impls = crate::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);

View file

@ -1,4 +1,3 @@
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,
@ -359,7 +358,9 @@ impl Array for DynamicArray {
}
impl_type_path!((in bevy_reflect) DynamicArray);
impl_function_traits!(DynamicArray);
#[cfg(feature = "functions")]
crate::func::macros::impl_function_traits!(DynamicArray);
/// An iterator over an [`Array`].
pub struct ArrayIter<'a> {
array: &'a dyn Array,

View file

@ -1,6 +1,5 @@
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,
@ -428,4 +427,5 @@ impl Reflect for DynamicEnum {
}
impl_type_path!((in bevy_reflect) DynamicEnum);
impl_function_traits!(DynamicEnum);
#[cfg(feature = "functions")]
crate::func::macros::impl_function_traits!(DynamicEnum);

View file

@ -3,7 +3,6 @@ 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,
@ -188,4 +187,5 @@ where
}
}
impl_function_traits!(SmallVec<T>; <T: SmallArray + TypePath + Send + Sync> where T::Item: FromReflect + TypePath);
#[cfg(feature = "functions")]
crate::func::macros::impl_function_traits!(SmallVec<T>; <T: SmallArray + TypePath + Send + Sync> where T::Item: FromReflect + TypePath);

View file

@ -1,4 +1,3 @@
use crate::func::macros::impl_function_traits;
use crate::std_traits::ReflectDefault;
use crate::utility::{
reflect_hasher, GenericTypeInfoCell, GenericTypePathCell, NonGenericTypeInfoCell,
@ -398,7 +397,8 @@ impl_reflect_for_veclike!(
Vec::pop,
[T]
);
impl_function_traits!(Vec<T>; <T: FromReflect + TypePath + GetTypeRegistration>);
#[cfg(feature = "functions")]
crate::func::macros::impl_function_traits!(Vec<T>; <T: FromReflect + TypePath + GetTypeRegistration>);
impl_reflect_for_veclike!(
::alloc::collections::VecDeque<T>,
@ -408,7 +408,8 @@ impl_reflect_for_veclike!(
VecDeque::pop_back,
VecDeque::<T>
);
impl_function_traits!(VecDeque<T>; <T: FromReflect + TypePath + GetTypeRegistration>);
#[cfg(feature = "functions")]
crate::func::macros::impl_function_traits!(VecDeque<T>; <T: FromReflect + TypePath + GetTypeRegistration>);
macro_rules! impl_reflect_for_hashmap {
($ty:path) => {
@ -638,7 +639,8 @@ macro_rules! impl_reflect_for_hashmap {
impl_reflect_for_hashmap!(::std::collections::HashMap<K, V, S>);
impl_type_path!(::std::collections::hash_map::RandomState);
impl_type_path!(::std::collections::HashMap<K, V, S>);
impl_function_traits!(::std::collections::HashMap<K, V, S>;
#[cfg(feature = "functions")]
crate::func::macros::impl_function_traits!(::std::collections::HashMap<K, V, S>;
<
K: FromReflect + TypePath + GetTypeRegistration + Eq + Hash,
V: FromReflect + TypePath + GetTypeRegistration,
@ -649,7 +651,8 @@ impl_function_traits!(::std::collections::HashMap<K, V, S>;
impl_reflect_for_hashmap!(bevy_utils::hashbrown::HashMap<K, V, S>);
impl_type_path!(::bevy_utils::hashbrown::hash_map::DefaultHashBuilder);
impl_type_path!(::bevy_utils::hashbrown::HashMap<K, V, S>);
impl_function_traits!(::bevy_utils::hashbrown::HashMap<K, V, S>;
#[cfg(feature = "functions")]
crate::func::macros::impl_function_traits!(::bevy_utils::hashbrown::HashMap<K, V, S>;
<
K: FromReflect + TypePath + GetTypeRegistration + Eq + Hash,
V: FromReflect + TypePath + GetTypeRegistration,
@ -869,7 +872,8 @@ where
}
impl_type_path!(::std::collections::BTreeMap<K, V>);
impl_function_traits!(::std::collections::BTreeMap<K, V>;
#[cfg(feature = "functions")]
crate::func::macros::impl_function_traits!(::std::collections::BTreeMap<K, V>;
<
K: FromReflect + TypePath + GetTypeRegistration + Eq + Ord,
V: FromReflect + TypePath + GetTypeRegistration
@ -1035,7 +1039,8 @@ impl<T: Reflect + TypePath + GetTypeRegistration, const N: usize> GetTypeRegistr
}
}
impl_function_traits!([T; N]; <T: Reflect + TypePath + GetTypeRegistration> [const N: usize]);
#[cfg(feature = "functions")]
crate::func::macros::impl_function_traits!([T; N]; <T: Reflect + TypePath + GetTypeRegistration> [const N: usize]);
impl_reflect! {
#[type_path = "core::option"]
@ -1194,7 +1199,8 @@ impl FromReflect for Cow<'static, str> {
}
}
impl_function_traits!(Cow<'static, str>);
#[cfg(feature = "functions")]
crate::func::macros::impl_function_traits!(Cow<'static, str>);
impl<T: TypePath> TypePath for [T]
where
@ -1374,7 +1380,8 @@ impl<T: FromReflect + Clone + TypePath + GetTypeRegistration> FromReflect for Co
}
}
impl_function_traits!(Cow<'static, [T]>; <T: FromReflect + Clone + TypePath + GetTypeRegistration>);
#[cfg(feature = "functions")]
crate::func::macros::impl_function_traits!(Cow<'static, [T]>; <T: FromReflect + Clone + TypePath + GetTypeRegistration>);
impl Reflect for &'static str {
fn get_represented_type_info(&self) -> Option<&'static TypeInfo> {
@ -1482,7 +1489,8 @@ impl FromReflect for &'static str {
}
}
impl_function_traits!(&'static str);
#[cfg(feature = "functions")]
crate::func::macros::impl_function_traits!(&'static str);
impl Reflect for &'static Path {
fn get_represented_type_info(&self) -> Option<&'static TypeInfo> {
@ -1589,7 +1597,8 @@ impl FromReflect for &'static Path {
}
}
impl_function_traits!(&'static Path);
#[cfg(feature = "functions")]
crate::func::macros::impl_function_traits!(&'static Path);
impl Reflect for Cow<'static, Path> {
fn get_represented_type_info(&self) -> Option<&'static TypeInfo> {
@ -1706,7 +1715,8 @@ impl GetTypeRegistration for Cow<'static, Path> {
}
}
impl_function_traits!(Cow<'static, Path>);
#[cfg(feature = "functions")]
crate::func::macros::impl_function_traits!(Cow<'static, Path>);
#[cfg(test)]
mod tests {

View file

@ -476,6 +476,7 @@
mod array;
mod fields;
mod from_reflect;
#[cfg(feature = "functions")]
pub mod func;
mod list;
mod map;
@ -517,6 +518,9 @@ pub mod prelude {
ReflectDeserialize, ReflectFromReflect, ReflectPath, ReflectSerialize, Struct, TupleStruct,
TypePath,
};
#[cfg(feature = "functions")]
pub use crate::func::IntoFunction;
}
pub use array::*;

View file

@ -4,7 +4,6 @@ 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,
@ -370,7 +369,8 @@ impl Reflect for DynamicList {
}
impl_type_path!((in bevy_reflect) DynamicList);
impl_function_traits!(DynamicList);
#[cfg(feature = "functions")]
crate::func::macros::impl_function_traits!(DynamicList);
impl Debug for DynamicList {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {

View file

@ -4,7 +4,6 @@ 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,
@ -418,7 +417,8 @@ impl Reflect for DynamicMap {
}
impl_type_path!((in bevy_reflect) DynamicMap);
impl_function_traits!(DynamicMap);
#[cfg(feature = "functions")]
crate::func::macros::impl_function_traits!(DynamicMap);
impl Debug for DynamicMap {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {

View file

@ -1,5 +1,4 @@
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,
@ -500,7 +499,8 @@ impl Reflect for DynamicStruct {
}
impl_type_path!((in bevy_reflect) DynamicStruct);
impl_function_traits!(DynamicStruct);
#[cfg(feature = "functions")]
crate::func::macros::impl_function_traits!(DynamicStruct);
impl Debug for DynamicStruct {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {

View file

@ -710,30 +710,33 @@ macro_rules! impl_type_path_tuple {
all_tuples!(impl_type_path_tuple, 0, 12, P);
macro_rules! impl_get_ownership_tuple {
#[cfg(feature = "functions")]
const _: () = {
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);
all_tuples!(impl_get_ownership_tuple, 0, 12, P);
macro_rules! impl_from_arg_tuple {
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);
all_tuples!(impl_from_arg_tuple, 0, 12, P);
macro_rules! impl_into_return_tuple {
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);
// 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 {

View file

@ -1,7 +1,6 @@
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,
@ -409,7 +408,8 @@ impl Reflect for DynamicTupleStruct {
}
impl_type_path!((in bevy_reflect) DynamicTupleStruct);
impl_function_traits!(DynamicTupleStruct);
#[cfg(feature = "functions")]
crate::func::macros::impl_function_traits!(DynamicTupleStruct);
impl Debug for DynamicTupleStruct {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {

View file

@ -75,6 +75,7 @@ The default feature set enables most of the expected features of a game engine,
|pbr_multi_layer_material_textures|Enable support for multi-layer material textures in the `StandardMaterial`, at the risk of blowing past the global, per-shader texture limit on older/lower-end GPUs|
|pbr_transmission_textures|Enable support for transmission-related textures in the `StandardMaterial`, at the risk of blowing past the global, per-shader texture limit on older/lower-end GPUs|
|pnm|PNM image format support, includes pam, pbm, pgm and ppm|
|reflect_functions|Enable function reflection|
|serialize|Enable serialization support through serde|
|shader_format_glsl|Enable support for shaders in GLSL|
|shader_format_spirv|Enable support for shaders in SPIR-V|