bevy/crates/bevy_reflect/derive/src/derive_data.rs

1239 lines
42 KiB
Rust
Raw Normal View History

use core::fmt;
bevy_reflect: Custom attributes (#11659) # Objective As work on the editor starts to ramp up, it might be nice to start allowing types to specify custom attributes. These can be used to provide certain functionality to fields, such as ranges or controlling how data is displayed. A good example of this can be seen in [`bevy-inspector-egui`](https://github.com/jakobhellermann/bevy-inspector-egui) with its [`InspectorOptions`](https://docs.rs/bevy-inspector-egui/0.22.1/bevy_inspector_egui/struct.InspectorOptions.html): ```rust #[derive(Reflect, Default, InspectorOptions)] #[reflect(InspectorOptions)] struct Slider { #[inspector(min = 0.0, max = 1.0)] value: f32, } ``` Normally, as demonstrated in the example above, these attributes are handled by a derive macro and stored in a corresponding `TypeData` struct (i.e. `ReflectInspectorOptions`). Ideally, we would have a good way of defining this directly via reflection so that users don't need to create and manage a whole proc macro just to allow these sorts of attributes. And note that this doesn't have to just be for inspectors and editors. It can be used for things done purely on the code side of things. ## Solution Create a new method for storing attributes on fields via the `Reflect` derive. These custom attributes are stored in type info (e.g. `NamedField`, `StructInfo`, etc.). ```rust #[derive(Reflect)] struct Slider { #[reflect(@0.0..=1.0)] value: f64, } let TypeInfo::Struct(info) = Slider::type_info() else { panic!("expected struct info"); }; let field = info.field("value").unwrap(); let range = field.get_attribute::<RangeInclusive<f64>>().unwrap(); assert_eq!(*range, 0.0..=1.0); ``` ## TODO - [x] ~~Bikeshed syntax~~ Went with a type-based approach, prefixed by `@` for ease of parsing and flexibility - [x] Add support for custom struct/tuple struct field attributes - [x] Add support for custom enum variant field attributes - [x] ~~Add support for custom enum variant attributes (maybe?)~~ ~~Will require a larger refactor. Can be saved for a future PR if we really want it.~~ Actually, we apparently still have support for variant attributes despite not using them, so it was pretty easy to add lol. - [x] Add support for custom container attributes - [x] Allow custom attributes to store any reflectable value (not just `Lit`) - [x] ~~Store attributes in registry~~ This PR used to store these in attributes in the registry, however, it has since switched over to storing them in type info - [x] Add example ## Bikeshedding > [!note] > This section was made for the old method of handling custom attributes, which stored them by name (i.e. `some_attribute = 123`). The PR has shifted away from that, to a more type-safe approach. > > This section has been left for reference. There are a number of ways we can syntactically handle custom attributes. Feel free to leave a comment on your preferred one! Ideally we want one that is clear, readable, and concise since these will potentially see _a lot_ of use. Below is a small, non-exhaustive list of them. Note that the `skip_serializing` reflection attribute is added to demonstrate how each case plays with existing reflection attributes. <details> <summary>List</summary> ##### 1. `@(name = value)` > The `@` was chosen to make them stand out from other attributes and because the "at" symbol is a subtle pneumonic for "attribute". Of course, other symbols could be used (e.g. `$`, `#`, etc.). ```rust #[derive(Reflect)] struct Slider { #[reflect(@(min = 0.0, max = 1.0), skip_serializing)] #[[reflect(@(bevy_editor::hint = "Range: 0.0 to 1.0"))] value: f32, } ``` ##### 2. `@name = value` > This is my personal favorite. ```rust #[derive(Reflect)] struct Slider { #[reflect(@min = 0.0, @max = 1.0, skip_serializing)] #[[reflect(@bevy_editor::hint = "Range: 0.0 to 1.0")] value: f32, } ``` ##### 3. `custom_attr(name = value)` > `custom_attr` can be anything. Other possibilities include `with` or `tag`. ```rust #[derive(Reflect)] struct Slider { #[reflect(custom_attr(min = 0.0, max = 1.0), skip_serializing)] #[[reflect(custom_attr(bevy_editor::hint = "Range: 0.0 to 1.0"))] value: f32, } ``` ##### 4. `reflect_attr(name = value)` ```rust #[derive(Reflect)] struct Slider { #[reflect(skip_serializing)] #[reflect_attr(min = 0.0, max = 1.0)] #[[reflect_attr(bevy_editor::hint = "Range: 0.0 to 1.0")] value: f32, } ``` </details> --- ## Changelog - Added support for custom attributes on reflected types (i.e. `#[reflect(@Foo::new("bar")]`)
2024-05-20 19:30:21 +00:00
use proc_macro2::Span;
bevy_reflect_derive: Clean up attribute logic (#11777) # Objective The code in `bevy_reflect_derive` could use some cleanup. ## Solution Took some of the changes in #11659 to create a dedicated PR for cleaning up the field and container attribute logic. #### Updated Naming I renamed `ReflectTraits` and `ReflectFieldAttr` to `ContainerAttributes` and `FieldAttributes`, respectively. I think these are clearer. #### Updated Parsing ##### Readability The parsing logic wasn't too bad before, but it was getting difficult to read. There was some duplicated logic between `Meta::List` and `Meta::Path` attributes. Additionally, all the logic was kept inside a large method. To simply things, I replaced the nested meta parsing with `ParseStream` parsing. In my opinion, this is easier to follow since it breaks up the large match statement into a small set of single-line if statements, where each if-block contains a single call to the appropriate attribute parsing method. ##### Flexibility On top of the added simplicity, this also makes our attribute parsing much more flexible. It allows us to more elegantly handle custom where clauses (i.e. `#[reflect(where T: Foo)]`) and it opens the door for more non-standard attribute syntax (e.g. #11659). ##### Errors This also allows us to automatically provide certain errors when parsing. For example, since we can use `stream.lookahead1()`, we get errors like the following for free: ``` error: expected one of: `ignore`, `skip_serializing`, `default` --> crates/bevy_reflect/src/lib.rs:1988:23 | 1988 | #[reflect(foo)] | ^^^ ``` --- ## Changelog > [!note] > All changes are internal to `bevy_reflect_derive` and should not affect the public API[^1]. - Renamed `ReflectTraits` to `ContainerAttributes` - Renamed `ReflectMeta::traits` to `ReflectMeta::attrs` - Renamed `ReflectFieldAttr` to `FieldAttributes` - Updated parsing logic for field/container attribute parsing - Now uses a `ParseStream` directly instead of nested meta parsing - General code cleanup of the field/container attribute modules for `bevy_reflect_derive` [^1]: Does not include errors, which may look slightly different. --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
2024-02-12 15:16:27 +00:00
use crate::container_attributes::{ContainerAttributes, FromReflectAttrs, TypePathAttrs};
use crate::field_attributes::FieldAttributes;
reflect: stable type path v2 (#7184) # Objective - Introduce a stable alternative to [`std::any::type_name`](https://doc.rust-lang.org/std/any/fn.type_name.html). - Rewrite of #5805 with heavy inspiration in design. - On the path to #5830. - Part of solving #3327. ## Solution - Add a `TypePath` trait for static stable type path/name information. - Add a `TypePath` derive macro. - Add a `impl_type_path` macro for implementing internal and foreign types in `bevy_reflect`. --- ## Changelog - Added `TypePath` trait. - Added `DynamicTypePath` trait and `get_type_path` method to `Reflect`. - Added a `TypePath` derive macro. - Added a `bevy_reflect::impl_type_path` for implementing `TypePath` on internal and foreign types in `bevy_reflect`. - Changed `bevy_reflect::utility::(Non)GenericTypeInfoCell` to `(Non)GenericTypedCell<T>` which allows us to be generic over both `TypeInfo` and `TypePath`. - `TypePath` is now a supertrait of `Asset`, `Material` and `Material2d`. - `impl_reflect_struct` needs a `#[type_path = "..."]` attribute to be specified. - `impl_reflect_value` needs to either specify path starting with a double colon (`::core::option::Option`) or an `in my_crate::foo` declaration. - Added `bevy_reflect_derive::ReflectTypePath`. - Most uses of `Ident` in `bevy_reflect_derive` changed to use `ReflectTypePath`. ## Migration Guide - Implementors of `Asset`, `Material` and `Material2d` now also need to derive `TypePath`. - Manual implementors of `Reflect` will need to implement the new `get_type_path` method. ## Open Questions - [x] ~This PR currently does not migrate any usages of `std::any::type_name` to use `bevy_reflect::TypePath` to ease the review process. Should it?~ Migration will be left to a follow-up PR. - [ ] This PR adds a lot of `#[derive(TypePath)]` and `T: TypePath` to satisfy new bounds, mostly when deriving `TypeUuid`. Should we make `TypePath` a supertrait of `TypeUuid`? [Should we remove `TypeUuid` in favour of `TypePath`?](https://github.com/bevyengine/bevy/pull/5805/files/2afbd855327c4b68e0a6b6f03118f289988441a4#r961067892)
2023-06-05 20:31:20 +00:00
use crate::type_path::parse_path_no_leading_colon;
bevy_reflect: Fix ignored/skipped field order (#7575) # Objective Fixes #5101 Alternative to #6511 ## Solution Corrected the behavior for ignored fields in `FromReflect`, which was previously using the incorrect field indexes. Similarly, fields marked with `#[reflect(skip_serializing)]` no longer break when using `FromReflect` after deserialization. This was done by modifying `SerializationData` to store a function pointer that can later be used to generate a default instance of the skipped field during deserialization. The function pointer points to a function generated by the derive macro using the behavior designated by `#[reflect(default)]` (or just `Default` if none provided). The entire output of the macro is now wrapped in an [unnamed constant](https://doc.rust-lang.org/stable/reference/items/constant-items.html#unnamed-constant) which keeps this behavior hygienic. #### Rationale The biggest downside to this approach is that it requires fields marked `#[reflect(skip_serializing)]` to provide the ability to create a default instance— either via a `Default` impl or by specifying a custom one. While this isn't great, I think it might be justified by the fact that we really need to create this value when using `FromReflect` on a deserialized object. And we need to do this _during_ deserialization because after that (at least for tuples and tuple structs) we lose information about which field is which: _"is the value at index 1 in this `DynamicTupleStruct` the actual value for index 1 or is it really the value for index 2 since index 1 is skippable...?"_ #### Alternatives An alternative would be to store `Option<Box<dyn Reflect>>` within `DynamicTuple` and `DynamicTupleStruct` instead of just `Box<dyn Reflect>`. This would allow us to insert "empty"/"missing" fields during deserialization, thus saving the positional information of the skipped fields. However, this may require changing the API of `Tuple` and `TupleStruct` such that they can account for their dynamic counterparts returning `None` for a skipped field. In practice this would probably mean exposing the `Option`-ness of the dynamics onto implementors via methods like `Tuple::drain` or `TupleStruct::field`. Personally, I think requiring `Default` would be better than muddying up the API to account for these special cases. But I'm open to trying out this other approach if the community feels that it's better. --- ## Changelog ### Public Changes #### Fixed - The behaviors of `#[reflect(ignore)]` and `#[reflect(skip_serializing)]` are no longer dependent on field order #### Changed - Fields marked with `#[reflect(skip_serializing)]` now need to either implement `Default` or specify a custom default function using `#[reflect(default = "path::to::some_func")]` - Deserializing a type with fields marked `#[reflect(skip_serializing)]` will now include that field initialized to its specified default value - `SerializationData::new` now takes the new `SkippedField` struct along with the skipped field index - Renamed `SerializationData::is_ignored_field` to `SerializationData::is_field_skipped` #### Added - Added `SkippedField` struct - Added methods `SerializationData::generate_default` and `SerializationData::iter_skipped` ### Internal Changes #### Changed - Replaced `members_to_serialization_denylist` and `BitSet<u32>` with `SerializationDataDef` - The `Reflect` derive is more hygienic as it now outputs within an [unnamed constant](https://doc.rust-lang.org/stable/reference/items/constant-items.html#unnamed-constant) - `StructField::index` has been split up into `StructField::declaration_index` and `StructField::reflection_index` #### Removed - Removed `bitset` dependency ## Migration Guide * Fields marked `#[reflect(skip_serializing)]` now must implement `Default` or specify a custom default function with `#[reflect(default = "path::to::some_func")]` ```rust #[derive(Reflect)] struct MyStruct { #[reflect(skip_serializing)] #[reflect(default = "get_foo_default")] foo: Foo, // <- `Foo` does not impl `Default` so requires a custom function #[reflect(skip_serializing)] bar: Bar, // <- `Bar` impls `Default` } #[derive(Reflect)] struct Foo(i32); #[derive(Reflect, Default)] struct Bar(i32); fn get_foo_default() -> Foo { Foo(123) } ``` * `SerializationData::new` has been changed to expect an iterator of `(usize, SkippedField)` rather than one of just `usize` ```rust // BEFORE SerializationData::new([0, 3].into_iter()); // AFTER SerializationData::new([ (0, SkippedField::new(field_0_default_fn)), (3, SkippedField::new(field_3_default_fn)), ].into_iter()); ``` * `Serialization::is_ignored_field` has been renamed to `Serialization::is_field_skipped` * Fields marked `#[reflect(skip_serializing)]` are now included in deserialization output. This may affect logic that expected those fields to be absent.
2023-10-22 12:43:31 +00:00
use crate::utility::{StringExpr, WhereClauseOptions};
reflect: stable type path v2 (#7184) # Objective - Introduce a stable alternative to [`std::any::type_name`](https://doc.rust-lang.org/std/any/fn.type_name.html). - Rewrite of #5805 with heavy inspiration in design. - On the path to #5830. - Part of solving #3327. ## Solution - Add a `TypePath` trait for static stable type path/name information. - Add a `TypePath` derive macro. - Add a `impl_type_path` macro for implementing internal and foreign types in `bevy_reflect`. --- ## Changelog - Added `TypePath` trait. - Added `DynamicTypePath` trait and `get_type_path` method to `Reflect`. - Added a `TypePath` derive macro. - Added a `bevy_reflect::impl_type_path` for implementing `TypePath` on internal and foreign types in `bevy_reflect`. - Changed `bevy_reflect::utility::(Non)GenericTypeInfoCell` to `(Non)GenericTypedCell<T>` which allows us to be generic over both `TypeInfo` and `TypePath`. - `TypePath` is now a supertrait of `Asset`, `Material` and `Material2d`. - `impl_reflect_struct` needs a `#[type_path = "..."]` attribute to be specified. - `impl_reflect_value` needs to either specify path starting with a double colon (`::core::option::Option`) or an `in my_crate::foo` declaration. - Added `bevy_reflect_derive::ReflectTypePath`. - Most uses of `Ident` in `bevy_reflect_derive` changed to use `ReflectTypePath`. ## Migration Guide - Implementors of `Asset`, `Material` and `Material2d` now also need to derive `TypePath`. - Manual implementors of `Reflect` will need to implement the new `get_type_path` method. ## Open Questions - [x] ~This PR currently does not migrate any usages of `std::any::type_name` to use `bevy_reflect::TypePath` to ease the review process. Should it?~ Migration will be left to a follow-up PR. - [ ] This PR adds a lot of `#[derive(TypePath)]` and `T: TypePath` to satisfy new bounds, mostly when deriving `TypeUuid`. Should we make `TypePath` a supertrait of `TypeUuid`? [Should we remove `TypeUuid` in favour of `TypePath`?](https://github.com/bevyengine/bevy/pull/5805/files/2afbd855327c4b68e0a6b6f03118f289988441a4#r961067892)
2023-06-05 20:31:20 +00:00
use quote::{quote, ToTokens};
use syn::token::Comma;
bevy_reflect_derive: Tidying up the code (#4712) # Objective The `bevy_reflect_derive` crate is not the cleanest or easiest to follow/maintain. The `lib.rs` file is especially difficult with over 1000 lines of code written in a confusing order. This is just a result of growth within the crate and it would be nice to clean it up for future work. ## Solution Split `bevy_reflect_derive` into many more submodules. The submodules include: * `container_attributes` - Code relating to container attributes * `derive_data` - Code relating to reflection-based derive metadata * `field_attributes` - Code relating to field attributes * `impls` - Code containing actual reflection implementations * `reflect_value` - Code relating to reflection-based value metadata * `registration` - Code relating to type registration * `utility` - General-purpose utility functions This leaves the `lib.rs` file to contain only the public macros, making it much easier to digest (and fewer than 200 lines). By breaking up the code into smaller modules, we make it easier for future contributors to find the code they're looking for or identify which module best fits their own additions. ### Metadata Structs This cleanup also adds two big metadata structs: `ReflectFieldAttr` and `ReflectDeriveData`. The former is used to store all attributes for a struct field (if any). The latter is used to store all metadata for struct-based derive inputs. Both significantly reduce code duplication and make editing these macros much simpler. The tradeoff is that we may collect more metadata than needed. However, this is usually a small thing (such as checking for attributes when they're not really needed or creating a `ReflectFieldAttr` for every field regardless of whether they actually have an attribute). We could try to remove these tradeoffs and squeeze some more performance out, but doing so might come at the cost of developer experience. Personally, I think it's much nicer to create a `ReflectFieldAttr` for every field since it means I don't have to do two `Option` checks. Others may disagree, though, and so we can discuss changing this either in this PR or in a future one. ### Out of Scope _Some_ documentation has been added or improved, but ultimately good docs are probably best saved for a dedicated PR. ## 🔍 Focus Points (for reviewers) I know it's a lot to sift through, so here is a list of **key points for reviewers**: - The following files contain code that was mostly just relocated: - `reflect_value.rs` - `registration.rs` - `container_attributes.rs` was also mostly moved but features some general cleanup (reducing nesting, removing hardcoded strings, etc.) and lots of doc comments - Most impl logic was moved from `lib.rs` to `impls.rs`, but they have been significantly modified to use the new `ReflectDeriveData` metadata struct in order to reduce duplication. - `derive_data.rs` and `field_attributes.rs` contain almost entirely new code and should probably be given the most attention. - Likewise, `from_reflect.rs` saw major changes using `ReflectDeriveData` so it should also be given focus. - There was no change to the `lib.rs` exports so the end-user API should be the same. ## Prior Work This task was initially tackled by @NathanSWard in #2377 (which was closed in favor of this PR), so hats off to them for beating me to the punch by nearly a year! --- ## Changelog * **[INTERNAL]** Split `bevy_reflect_derive` into smaller submodules * **[INTERNAL]** Add `ReflectFieldAttr` * **[INTERNAL]** Add `ReflectDeriveData` * Add `BevyManifest::get_path_direct()` method (`bevy_macro_utils`) Co-authored-by: MrGVSV <49806985+MrGVSV@users.noreply.github.com>
2022-05-12 19:43:23 +00:00
bevy_reflect: Reflect remote types (#6042) # Objective The goal with this PR is to allow the use of types that don't implement `Reflect` within the reflection API. Rust's [orphan rule](https://doc.rust-lang.org/book/ch10-02-traits.html#implementing-a-trait-on-a-type) prevents implementing a trait on an external type when neither type nor trait are owned by the implementor. This means that if a crate, `cool_rust_lib`, defines a type, `Foo`, then a user cannot use it with reflection. What this means is that we have to ignore it most of the time: ```rust #[derive(Reflect)] struct SomeStruct { #[reflect(ignore)] data: cool_rust_lib::Foo } ``` Obviously, it's impossible to implement `Reflect` on `Foo`. But does it *have* to be? Most of reflection doesn't deal with concrete types— it's almost all using `dyn Reflect`. And being very metadata-driven, it should theoretically be possible. I mean, [`serde`](https://serde.rs/remote-derive.html) does it. ## Solution > Special thanks to @danielhenrymantilla for their help reviewing this PR and offering wisdom wrt safety. Taking a page out of `serde`'s book, this PR adds the ability to easily use "remote types" with reflection. In this context, a "remote type" is the external type for which we have no ability to implement `Reflect`. This adds the `#[reflect_remote(...)]` attribute macro, which is used to generate "remote type wrappers". All you have to do is define the wrapper exactly the same as the remote type's definition: ```rust // Pretend this is our external crate mod cool_rust_lib { #[derive(Default)] struct Foo { pub value: String } } #[reflect_remote(cool_rust_lib::Foo)] struct FooWrapper { pub value: String } ``` > **Note:** All fields in the external type *must* be public. This could be addressed with a separate getter/setter attribute either in this PR or in another one. The macro takes this user-defined item and transforms it into a newtype wrapper around the external type, marking it as `#[repr(transparent)]`. The fields/variants defined by the user are simply used to build out the reflection impls. Additionally, it generates an implementation of the new trait, `ReflectRemote`, which helps prevent accidental misuses of this API. Therefore, the output generated by the macro would look something like: ```rust #[repr(transparent)] struct FooWrapper(pub cool_rust_lib::Foo); impl ReflectRemote for FooWrapper { type Remote = cool_rust_lib::Foo; // transmutation methods... } // reflection impls... // these will acknowledge and make use of the `value` field ``` Internally, the reflection API will pass around the `FooWrapper` and [transmute](https://doc.rust-lang.org/std/mem/fn.transmute.html) it where necessary. All we have to do is then tell `Reflect` to do that. So rather than ignoring the field, we tell `Reflect` to use our wrapper using the `#[reflect(remote = ...)]` field attribute: ```rust #[derive(Reflect)] struct SomeStruct { #[reflect(remote = FooWrapper)] data: cool_rust_lib::Foo } ``` #### Other Macros & Type Data Because this macro consumes the defined item and generates a new one, we can't just put our macros anywhere. All macros that should be passed to the generated struct need to come *below* this macro. For example, to derive `Default` and register its associated type data: ```rust // ✅ GOOD #[reflect_remote(cool_rust_lib::Foo)] #[derive(Default)] #[reflect(Default)] struct FooWrapper { pub value: String } // ❌ BAD #[derive(Default)] #[reflect_remote(cool_rust_lib::Foo)] #[reflect(Default)] struct FooWrapper { pub value: String } ``` #### Generics Generics are forwarded to the generated struct as well. They should also be defined in the same order: ```rust #[reflect_remote(RemoteGeneric<'a, T1, T2>)] struct GenericWrapper<'a, T1, T2> { pub foo: &'a T1, pub bar: &'a T2, } ``` > Naming does *not* need to match the original definition's. Only order matters here. > Also note that the code above is just a demonstration and doesn't actually compile since we'd need to enforce certain bounds (e.g. `T1: Reflect`, `'a: 'static`, etc.) #### Nesting And, yes, you can nest remote types: ```rust #[reflect_remote(RemoteOuter)] struct OuterWrapper { #[reflect(remote = InnerWrapper)] pub inner: RemoteInner } #[reflect_remote(RemoteInner)] struct InnerWrapper(usize); ``` #### Assertions This macro will also generate some compile-time assertions to ensure that the correct types are used. It's important we catch this early so users don't have to wait for something to panic. And it also helps keep our `unsafe` a little safer. For example, a wrapper definition that does not match its corresponding remote type will result in an error: ```rust mod external_crate { pub struct TheirStruct(pub u32); } #[reflect_remote(external_crate::TheirStruct)] struct MyStruct(pub String); // ERROR: expected type `u32` but found `String` ``` <details> <summary>Generated Assertion</summary> ```rust const _: () = { #[allow(non_snake_case)] #[allow(unused_variables)] #[allow(unused_assignments)] #[allow(unreachable_patterns)] #[allow(clippy::multiple_bound_locations)] fn assert_wrapper_definition_matches_remote_type( mut __remote__: external_crate::TheirStruct, ) { __remote__.0 = (|| -> ::core::option::Option<String> { None })().unwrap(); } }; ``` </details> Additionally, using the incorrect type in a `#[reflect(remote = ...)]` attribute should result in an error: ```rust mod external_crate { pub struct TheirFoo(pub u32); pub struct TheirBar(pub i32); } #[reflect_remote(external_crate::TheirFoo)] struct MyFoo(pub u32); #[reflect_remote(external_crate::TheirBar)] struct MyBar(pub i32); #[derive(Reflect)] struct MyStruct { #[reflect(remote = MyBar)] // ERROR: expected type `TheirFoo` but found struct `TheirBar` foo: external_crate::TheirFoo } ``` <details> <summary>Generated Assertion</summary> ```rust const _: () = { struct RemoteFieldAssertions; impl RemoteFieldAssertions { #[allow(non_snake_case)] #[allow(clippy::multiple_bound_locations)] fn assert__foo__is_valid_remote() { let _: <MyBar as bevy_reflect::ReflectRemote>::Remote = (|| -> ::core::option::Option<external_crate::TheirFoo> { None })().unwrap(); } } }; ``` </details> ### Discussion There are a couple points that I think still need discussion or validation. - [x] 1. `Any` shenanigans ~~If we wanted to downcast our remote type from a `dyn Reflect`, we'd have to first downcast to the wrapper then extract the inner type. This PR has a [commit](b840db9f74cb6d357f951cb11b150d46bac89ee2) that addresses this by making all the `Reflect::*any` methods return the inner type rather than the wrapper type. This allows us to downcast directly to our remote type.~~ ~~However, I'm not sure if this is something we want to do. For unknowing users, it could be confusing and seemingly inconsistent. Is it worth keeping? Or should this behavior be removed?~~ I think this should be fine. The remote wrapper is an implementation detail and users should not need to downcast to the wrapper type. Feel free to let me know if there are other opinions on this though! - [x] 2. Implementing `Deref/DerefMut` and `From` ~~We don't currently do this, but should we implement other traits on the generated transparent struct? We could implement `Deref`/`DerefMut` to easily access the inner type. And we could implement `From` for easier conversion between the two types (e.g. `T: Into<Foo>`).~~ As mentioned in the comments, we probably don't need to do this. Again, the remote wrapper is an implementation detail, and should generally not be used directly. - [x] 3. ~~Should we define a getter/setter field attribute in this PR as well or leave it for a future one?~~ I think this should be saved for a future PR - [ ] 4. Any foreseeable issues with this implementation? #### Alternatives One alternative to defining our own `ReflectRemote` would be to use [bytemuck's `TransparentWrapper`](https://docs.rs/bytemuck/1.13.1/bytemuck/trait.TransparentWrapper.html) (as suggested by @danielhenrymantilla). This is definitely a viable option, as `ReflectRemote` is pretty much the same thing as `TransparentWrapper`. However, the cost would be bringing in a new crate— though, it is already in use in a few other sub-crates like bevy_render. I think we're okay just defining `ReflectRemote` ourselves, but we can go the bytemuck route if we'd prefer offloading that work to another crate. --- ## Changelog * Added the `#[reflect_remote(...)]` attribute macro to allow `Reflect` to be used on remote types * Added `ReflectRemote` trait for ensuring proper remote wrapper usage
2024-08-12 19:12:53 +00:00
use crate::remote::RemoteType;
bevy_reflect: Fix ignored/skipped field order (#7575) # Objective Fixes #5101 Alternative to #6511 ## Solution Corrected the behavior for ignored fields in `FromReflect`, which was previously using the incorrect field indexes. Similarly, fields marked with `#[reflect(skip_serializing)]` no longer break when using `FromReflect` after deserialization. This was done by modifying `SerializationData` to store a function pointer that can later be used to generate a default instance of the skipped field during deserialization. The function pointer points to a function generated by the derive macro using the behavior designated by `#[reflect(default)]` (or just `Default` if none provided). The entire output of the macro is now wrapped in an [unnamed constant](https://doc.rust-lang.org/stable/reference/items/constant-items.html#unnamed-constant) which keeps this behavior hygienic. #### Rationale The biggest downside to this approach is that it requires fields marked `#[reflect(skip_serializing)]` to provide the ability to create a default instance— either via a `Default` impl or by specifying a custom one. While this isn't great, I think it might be justified by the fact that we really need to create this value when using `FromReflect` on a deserialized object. And we need to do this _during_ deserialization because after that (at least for tuples and tuple structs) we lose information about which field is which: _"is the value at index 1 in this `DynamicTupleStruct` the actual value for index 1 or is it really the value for index 2 since index 1 is skippable...?"_ #### Alternatives An alternative would be to store `Option<Box<dyn Reflect>>` within `DynamicTuple` and `DynamicTupleStruct` instead of just `Box<dyn Reflect>`. This would allow us to insert "empty"/"missing" fields during deserialization, thus saving the positional information of the skipped fields. However, this may require changing the API of `Tuple` and `TupleStruct` such that they can account for their dynamic counterparts returning `None` for a skipped field. In practice this would probably mean exposing the `Option`-ness of the dynamics onto implementors via methods like `Tuple::drain` or `TupleStruct::field`. Personally, I think requiring `Default` would be better than muddying up the API to account for these special cases. But I'm open to trying out this other approach if the community feels that it's better. --- ## Changelog ### Public Changes #### Fixed - The behaviors of `#[reflect(ignore)]` and `#[reflect(skip_serializing)]` are no longer dependent on field order #### Changed - Fields marked with `#[reflect(skip_serializing)]` now need to either implement `Default` or specify a custom default function using `#[reflect(default = "path::to::some_func")]` - Deserializing a type with fields marked `#[reflect(skip_serializing)]` will now include that field initialized to its specified default value - `SerializationData::new` now takes the new `SkippedField` struct along with the skipped field index - Renamed `SerializationData::is_ignored_field` to `SerializationData::is_field_skipped` #### Added - Added `SkippedField` struct - Added methods `SerializationData::generate_default` and `SerializationData::iter_skipped` ### Internal Changes #### Changed - Replaced `members_to_serialization_denylist` and `BitSet<u32>` with `SerializationDataDef` - The `Reflect` derive is more hygienic as it now outputs within an [unnamed constant](https://doc.rust-lang.org/stable/reference/items/constant-items.html#unnamed-constant) - `StructField::index` has been split up into `StructField::declaration_index` and `StructField::reflection_index` #### Removed - Removed `bitset` dependency ## Migration Guide * Fields marked `#[reflect(skip_serializing)]` now must implement `Default` or specify a custom default function with `#[reflect(default = "path::to::some_func")]` ```rust #[derive(Reflect)] struct MyStruct { #[reflect(skip_serializing)] #[reflect(default = "get_foo_default")] foo: Foo, // <- `Foo` does not impl `Default` so requires a custom function #[reflect(skip_serializing)] bar: Bar, // <- `Bar` impls `Default` } #[derive(Reflect)] struct Foo(i32); #[derive(Reflect, Default)] struct Bar(i32); fn get_foo_default() -> Foo { Foo(123) } ``` * `SerializationData::new` has been changed to expect an iterator of `(usize, SkippedField)` rather than one of just `usize` ```rust // BEFORE SerializationData::new([0, 3].into_iter()); // AFTER SerializationData::new([ (0, SkippedField::new(field_0_default_fn)), (3, SkippedField::new(field_3_default_fn)), ].into_iter()); ``` * `Serialization::is_ignored_field` has been renamed to `Serialization::is_field_skipped` * Fields marked `#[reflect(skip_serializing)]` are now included in deserialization output. This may affect logic that expected those fields to be absent.
2023-10-22 12:43:31 +00:00
use crate::serialization::SerializationDataDef;
reflect: stable type path v2 (#7184) # Objective - Introduce a stable alternative to [`std::any::type_name`](https://doc.rust-lang.org/std/any/fn.type_name.html). - Rewrite of #5805 with heavy inspiration in design. - On the path to #5830. - Part of solving #3327. ## Solution - Add a `TypePath` trait for static stable type path/name information. - Add a `TypePath` derive macro. - Add a `impl_type_path` macro for implementing internal and foreign types in `bevy_reflect`. --- ## Changelog - Added `TypePath` trait. - Added `DynamicTypePath` trait and `get_type_path` method to `Reflect`. - Added a `TypePath` derive macro. - Added a `bevy_reflect::impl_type_path` for implementing `TypePath` on internal and foreign types in `bevy_reflect`. - Changed `bevy_reflect::utility::(Non)GenericTypeInfoCell` to `(Non)GenericTypedCell<T>` which allows us to be generic over both `TypeInfo` and `TypePath`. - `TypePath` is now a supertrait of `Asset`, `Material` and `Material2d`. - `impl_reflect_struct` needs a `#[type_path = "..."]` attribute to be specified. - `impl_reflect_value` needs to either specify path starting with a double colon (`::core::option::Option`) or an `in my_crate::foo` declaration. - Added `bevy_reflect_derive::ReflectTypePath`. - Most uses of `Ident` in `bevy_reflect_derive` changed to use `ReflectTypePath`. ## Migration Guide - Implementors of `Asset`, `Material` and `Material2d` now also need to derive `TypePath`. - Manual implementors of `Reflect` will need to implement the new `get_type_path` method. ## Open Questions - [x] ~This PR currently does not migrate any usages of `std::any::type_name` to use `bevy_reflect::TypePath` to ease the review process. Should it?~ Migration will be left to a follow-up PR. - [ ] This PR adds a lot of `#[derive(TypePath)]` and `T: TypePath` to satisfy new bounds, mostly when deriving `TypeUuid`. Should we make `TypePath` a supertrait of `TypeUuid`? [Should we remove `TypeUuid` in favour of `TypePath`?](https://github.com/bevyengine/bevy/pull/5805/files/2afbd855327c4b68e0a6b6f03118f289988441a4#r961067892)
2023-06-05 20:31:20 +00:00
use crate::{
utility, REFLECT_ATTRIBUTE_NAME, REFLECT_VALUE_ATTRIBUTE_NAME, TYPE_NAME_ATTRIBUTE_NAME,
TYPE_PATH_ATTRIBUTE_NAME,
};
bevy_reflect: Reflect enums (#4761) # Objective > This is a revival of #1347. Credit for the original PR should go to @Davier. Currently, enums are treated as `ReflectRef::Value` types by `bevy_reflect`. Obviously, there needs to be better a better representation for enums using the reflection API. ## Solution Based on prior work from @Davier, an `Enum` trait has been added as well as the ability to automatically implement it via the `Reflect` derive macro. This allows enums to be expressed dynamically: ```rust #[derive(Reflect)] enum Foo { A, B(usize), C { value: f32 }, } let mut foo = Foo::B(123); assert_eq!("B", foo.variant_name()); assert_eq!(1, foo.field_len()); let new_value = DynamicEnum::from(Foo::C { value: 1.23 }); foo.apply(&new_value); assert_eq!(Foo::C{value: 1.23}, foo); ``` ### Features #### Derive Macro Use the `#[derive(Reflect)]` macro to automatically implement the `Enum` trait for enum definitions. Optionally, you can use `#[reflect(ignore)]` with both variants and variant fields, just like you can with structs. These ignored items will not be considered as part of the reflection and cannot be accessed via reflection. ```rust #[derive(Reflect)] enum TestEnum { A, // Uncomment to ignore all of `B` // #[reflect(ignore)] B(usize), C { // Uncomment to ignore only field `foo` of `C` // #[reflect(ignore)] foo: f32, bar: bool, }, } ``` #### Dynamic Enums Enums may be created/represented dynamically via the `DynamicEnum` struct. The main purpose of this struct is to allow enums to be deserialized into a partial state and to allow dynamic patching. In order to ensure conversion from a `DynamicEnum` to a concrete enum type goes smoothly, be sure to add `FromReflect` to your derive macro. ```rust let mut value = TestEnum::A; // Create from a concrete instance let dyn_enum = DynamicEnum::from(TestEnum::B(123)); value.apply(&dyn_enum); assert_eq!(TestEnum::B(123), value); // Create a purely dynamic instance let dyn_enum = DynamicEnum::new("TestEnum", "A", ()); value.apply(&dyn_enum); assert_eq!(TestEnum::A, value); ``` #### Variants An enum value is always represented as one of its variants— never the enum in its entirety. ```rust let value = TestEnum::A; assert_eq!("A", value.variant_name()); // Since we are using the `A` variant, we cannot also be the `B` variant assert_ne!("B", value.variant_name()); ``` All variant types are representable within the `Enum` trait: unit, struct, and tuple. You can get the current type like: ```rust match value.variant_type() { VariantType::Unit => println!("A unit variant!"), VariantType::Struct => println!("A struct variant!"), VariantType::Tuple => println!("A tuple variant!"), } ``` > Notice that they don't contain any values representing the fields. These are purely tags. If a variant has them, you can access the fields as well: ```rust let mut value = TestEnum::C { foo: 1.23, bar: false }; // Read/write specific fields *value.field_mut("bar").unwrap() = true; // Iterate over the entire collection of fields for field in value.iter_fields() { println!("{} = {:?}", field.name(), field.value()); } ``` #### Variant Swapping It might seem odd to group all variant types under a single trait (why allow `iter_fields` on a unit variant?), but the reason this was done ~~is to easily allow *variant swapping*.~~ As I was recently drafting up the **Design Decisions** section, I discovered that other solutions could have been made to work with variant swapping. So while there are reasons to keep the all-in-one approach, variant swapping is _not_ one of them. ```rust let mut value: Box<dyn Enum> = Box::new(TestEnum::A); value.set(Box::new(TestEnum::B(123))).unwrap(); ``` #### Serialization Enums can be serialized and deserialized via reflection without needing to implement `Serialize` or `Deserialize` themselves (which can save thousands of lines of generated code). Below are the ways an enum can be serialized. > Note, like the rest of reflection-based serialization, the order of the keys in these representations is important! ##### Unit ```json { "type": "my_crate::TestEnum", "enum": { "variant": "A" } } ``` ##### Tuple ```json { "type": "my_crate::TestEnum", "enum": { "variant": "B", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` <details> <summary>Effects on Option</summary> This ends up making `Option` look a little ugly: ```json { "type": "core::option::Option<usize>", "enum": { "variant": "Some", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` </details> ##### Struct ```json { "type": "my_crate::TestEnum", "enum": { "variant": "C", "struct": { "foo": { "type": "f32", "value": 1.23 }, "bar": { "type": "bool", "value": false } } } } ``` ## Design Decisions <details> <summary><strong>View Section</strong></summary> This section is here to provide some context for why certain decisions were made for this PR, alternatives that could have been used instead, and what could be improved upon in the future. ### Variant Representation One of the biggest decisions was to decide on how to represent variants. The current design uses a "all-in-one" design where unit, tuple, and struct variants are all simultaneously represented by the `Enum` trait. This is not the only way it could have been done, though. #### Alternatives ##### 1. Variant Traits One way of representing variants would be to define traits for each variant, implementing them whenever an enum featured at least one instance of them. This would allow us to define variants like: ```rust pub trait Enum: Reflect { fn variant(&self) -> Variant; } pub enum Variant<'a> { Unit, Tuple(&'a dyn TupleVariant), Struct(&'a dyn StructVariant), } pub trait TupleVariant { fn field_len(&self) -> usize; // ... } ``` And then do things like: ```rust fn get_tuple_len(foo: &dyn Enum) -> usize { match foo.variant() { Variant::Tuple(tuple) => tuple.field_len(), _ => panic!("not a tuple variant!") } } ``` The reason this PR does not go with this approach is because of the fact that variants are not separate types. In other words, we cannot implement traits on specific variants— these cover the *entire* enum. This means we offer an easy footgun: ```rust let foo: Option<i32> = None; let my_enum = Box::new(foo) as Box<dyn TupleVariant>; ``` Here, `my_enum` contains `foo`, which is a unit variant. However, since we need to implement `TupleVariant` for `Option` as a whole, it's possible to perform such a cast. This is obviously wrong, but could easily go unnoticed. So unfortunately, this makes it not a good candidate for representing variants. ##### 2. Variant Structs To get around the issue of traits necessarily needing to apply to both the enum and its variants, we could instead use structs that are created on a per-variant basis. This was also considered but was ultimately [[removed](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c)](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c) due to concerns about allocations. Each variant struct would probably look something like: ```rust pub trait Enum: Reflect { fn variant_mut(&self) -> VariantMut; } pub enum VariantMut<'a> { Unit, Tuple(TupleVariantMut), Struct(StructVariantMut), } struct StructVariantMut<'a> { fields: Vec<&'a mut dyn Reflect>, field_indices: HashMap<Cow<'static, str>, usize> } ``` This allows us to isolate struct variants into their own defined struct and define methods specifically for their use. It also prevents users from casting to it since it's not a trait. However, this is not an optimal solution. Both `field_indices` and `fields` will require an allocation (remember, a `Box<[T]>` still requires a `Vec<T>` in order to be constructed). This *might* be a problem if called frequently enough. ##### 3. Generated Structs The original design, implemented by @Davier, instead generates structs specific for each variant. So if we had a variant path like `Foo::Bar`, we'd generate a struct named `FooBarWrapper`. This would be newtyped around the original enum and forward tuple or struct methods to the enum with the chosen variant. Because it involved using the `Tuple` and `Struct` traits (which are also both bound on `Reflect`), this meant a bit more code had to be generated. For a single struct variant with one field, the generated code amounted to ~110LoC. However, each new field added to that variant only added ~6 more LoC. In order to work properly, the enum had to be transmuted to the generated struct: ```rust fn variant(&self) -> crate::EnumVariant<'_> { match self { Foo::Bar {value: i32} => { let wrapper_ref = unsafe { std::mem::transmute::<&Self, &FooBarWrapper>(self) }; crate::EnumVariant::Struct(wrapper_ref as &dyn crate::Struct) } } } ``` This works because `FooBarWrapper` is defined as `repr(transparent)`. Out of all the alternatives, this would probably be the one most likely to be used again in the future. The reasons for why this PR did not continue to use it was because: * To reduce generated code (which would hopefully speed up compile times) * To avoid cluttering the code with generated structs not visible to the user * To keep bevy_reflect simple and extensible (these generated structs act as proxies and might not play well with current or future systems) * To avoid additional unsafe blocks * My own misunderstanding of @Davier's code That last point is obviously on me. I misjudged the code to be too unsafe and unable to handle variant swapping (which it probably could) when I was rebasing it. Looking over it again when writing up this whole section, I see that it was actually a pretty clever way of handling variant representation. #### Benefits of All-in-One As stated before, the current implementation uses an all-in-one approach. All variants are capable of containing fields as far as `Enum` is concerned. This provides a few benefits that the alternatives do not (reduced indirection, safer code, etc.). The biggest benefit, though, is direct field access. Rather than forcing users to have to go through pattern matching, we grant direct access to the fields contained by the current variant. The reason we can do this is because all of the pattern matching happens internally. Getting the field at index `2` will automatically return `Some(...)` for the current variant if it has a field at that index or `None` if it doesn't (or can't). This could be useful for scenarios where the variant has already been verified or just set/swapped (or even where the type of variant doesn't matter): ```rust let dyn_enum: &mut dyn Enum = &mut Foo::Bar {value: 123}; // We know it's the `Bar` variant let field = dyn_enum.field("value").unwrap(); ``` Reflection is not a type-safe abstraction— almost every return value is wrapped in `Option<...>`. There are plenty of places to check and recheck that a value is what Reflect says it is. Forcing users to have to go through `match` each time they want to access a field might just be an extra step among dozens of other verification processes. Some might disagree, but ultimately, my view is that the benefit here is an improvement to the ergonomics and usability of reflected enums. </details> --- ## Changelog ### Added * Added `Enum` trait * Added `Enum` impl to `Reflect` derive macro * Added `DynamicEnum` struct * Added `DynamicVariant` * Added `EnumInfo` * Added `VariantInfo` * Added `StructVariantInfo` * Added `TupleVariantInfo` * Added `UnitVariantInfo` * Added serializtion/deserialization support for enums * Added `EnumSerializer` * Added `VariantType` * Added `VariantFieldIter` * Added `VariantField` * Added `enum_partial_eq(...)` * Added `enum_hash(...)` ### Changed * `Option<T>` now implements `Enum` * `bevy_window` now depends on `bevy_reflect` * Implemented `Reflect` and `FromReflect` for `WindowId` * Derive `FromReflect` on `PerspectiveProjection` * Derive `FromReflect` on `OrthographicProjection` * Derive `FromReflect` on `WindowOrigin` * Derive `FromReflect` on `ScalingMode` * Derive `FromReflect` on `DepthCalculation` ## Migration Guide * Enums no longer need to be treated as values and usages of `#[reflect_value(...)]` can be removed or replaced by `#[reflect(...)]` * Enums (including `Option<T>`) now take a different format when serializing. The format is described above, but this may cause issues for existing scenes that make use of enums. --- Also shout out to @nicopap for helping clean up some of the code here! It's a big feature so help like this is really appreciated! Co-authored-by: Gino Valente <gino.valente.code@gmail.com>
2022-08-02 22:14:41 +00:00
use syn::punctuated::Punctuated;
use syn::spanned::Spanned;
reflect: stable type path v2 (#7184) # Objective - Introduce a stable alternative to [`std::any::type_name`](https://doc.rust-lang.org/std/any/fn.type_name.html). - Rewrite of #5805 with heavy inspiration in design. - On the path to #5830. - Part of solving #3327. ## Solution - Add a `TypePath` trait for static stable type path/name information. - Add a `TypePath` derive macro. - Add a `impl_type_path` macro for implementing internal and foreign types in `bevy_reflect`. --- ## Changelog - Added `TypePath` trait. - Added `DynamicTypePath` trait and `get_type_path` method to `Reflect`. - Added a `TypePath` derive macro. - Added a `bevy_reflect::impl_type_path` for implementing `TypePath` on internal and foreign types in `bevy_reflect`. - Changed `bevy_reflect::utility::(Non)GenericTypeInfoCell` to `(Non)GenericTypedCell<T>` which allows us to be generic over both `TypeInfo` and `TypePath`. - `TypePath` is now a supertrait of `Asset`, `Material` and `Material2d`. - `impl_reflect_struct` needs a `#[type_path = "..."]` attribute to be specified. - `impl_reflect_value` needs to either specify path starting with a double colon (`::core::option::Option`) or an `in my_crate::foo` declaration. - Added `bevy_reflect_derive::ReflectTypePath`. - Most uses of `Ident` in `bevy_reflect_derive` changed to use `ReflectTypePath`. ## Migration Guide - Implementors of `Asset`, `Material` and `Material2d` now also need to derive `TypePath`. - Manual implementors of `Reflect` will need to implement the new `get_type_path` method. ## Open Questions - [x] ~This PR currently does not migrate any usages of `std::any::type_name` to use `bevy_reflect::TypePath` to ease the review process. Should it?~ Migration will be left to a follow-up PR. - [ ] This PR adds a lot of `#[derive(TypePath)]` and `T: TypePath` to satisfy new bounds, mostly when deriving `TypeUuid`. Should we make `TypePath` a supertrait of `TypeUuid`? [Should we remove `TypeUuid` in favour of `TypePath`?](https://github.com/bevyengine/bevy/pull/5805/files/2afbd855327c4b68e0a6b6f03118f289988441a4#r961067892)
2023-06-05 20:31:20 +00:00
use syn::{
parse_str, Data, DeriveInput, Field, Fields, GenericParam, Generics, Ident, LitStr, Meta, Path,
PathSegment, Type, TypeParam, Variant,
};
bevy_reflect: Reflect enums (#4761) # Objective > This is a revival of #1347. Credit for the original PR should go to @Davier. Currently, enums are treated as `ReflectRef::Value` types by `bevy_reflect`. Obviously, there needs to be better a better representation for enums using the reflection API. ## Solution Based on prior work from @Davier, an `Enum` trait has been added as well as the ability to automatically implement it via the `Reflect` derive macro. This allows enums to be expressed dynamically: ```rust #[derive(Reflect)] enum Foo { A, B(usize), C { value: f32 }, } let mut foo = Foo::B(123); assert_eq!("B", foo.variant_name()); assert_eq!(1, foo.field_len()); let new_value = DynamicEnum::from(Foo::C { value: 1.23 }); foo.apply(&new_value); assert_eq!(Foo::C{value: 1.23}, foo); ``` ### Features #### Derive Macro Use the `#[derive(Reflect)]` macro to automatically implement the `Enum` trait for enum definitions. Optionally, you can use `#[reflect(ignore)]` with both variants and variant fields, just like you can with structs. These ignored items will not be considered as part of the reflection and cannot be accessed via reflection. ```rust #[derive(Reflect)] enum TestEnum { A, // Uncomment to ignore all of `B` // #[reflect(ignore)] B(usize), C { // Uncomment to ignore only field `foo` of `C` // #[reflect(ignore)] foo: f32, bar: bool, }, } ``` #### Dynamic Enums Enums may be created/represented dynamically via the `DynamicEnum` struct. The main purpose of this struct is to allow enums to be deserialized into a partial state and to allow dynamic patching. In order to ensure conversion from a `DynamicEnum` to a concrete enum type goes smoothly, be sure to add `FromReflect` to your derive macro. ```rust let mut value = TestEnum::A; // Create from a concrete instance let dyn_enum = DynamicEnum::from(TestEnum::B(123)); value.apply(&dyn_enum); assert_eq!(TestEnum::B(123), value); // Create a purely dynamic instance let dyn_enum = DynamicEnum::new("TestEnum", "A", ()); value.apply(&dyn_enum); assert_eq!(TestEnum::A, value); ``` #### Variants An enum value is always represented as one of its variants— never the enum in its entirety. ```rust let value = TestEnum::A; assert_eq!("A", value.variant_name()); // Since we are using the `A` variant, we cannot also be the `B` variant assert_ne!("B", value.variant_name()); ``` All variant types are representable within the `Enum` trait: unit, struct, and tuple. You can get the current type like: ```rust match value.variant_type() { VariantType::Unit => println!("A unit variant!"), VariantType::Struct => println!("A struct variant!"), VariantType::Tuple => println!("A tuple variant!"), } ``` > Notice that they don't contain any values representing the fields. These are purely tags. If a variant has them, you can access the fields as well: ```rust let mut value = TestEnum::C { foo: 1.23, bar: false }; // Read/write specific fields *value.field_mut("bar").unwrap() = true; // Iterate over the entire collection of fields for field in value.iter_fields() { println!("{} = {:?}", field.name(), field.value()); } ``` #### Variant Swapping It might seem odd to group all variant types under a single trait (why allow `iter_fields` on a unit variant?), but the reason this was done ~~is to easily allow *variant swapping*.~~ As I was recently drafting up the **Design Decisions** section, I discovered that other solutions could have been made to work with variant swapping. So while there are reasons to keep the all-in-one approach, variant swapping is _not_ one of them. ```rust let mut value: Box<dyn Enum> = Box::new(TestEnum::A); value.set(Box::new(TestEnum::B(123))).unwrap(); ``` #### Serialization Enums can be serialized and deserialized via reflection without needing to implement `Serialize` or `Deserialize` themselves (which can save thousands of lines of generated code). Below are the ways an enum can be serialized. > Note, like the rest of reflection-based serialization, the order of the keys in these representations is important! ##### Unit ```json { "type": "my_crate::TestEnum", "enum": { "variant": "A" } } ``` ##### Tuple ```json { "type": "my_crate::TestEnum", "enum": { "variant": "B", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` <details> <summary>Effects on Option</summary> This ends up making `Option` look a little ugly: ```json { "type": "core::option::Option<usize>", "enum": { "variant": "Some", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` </details> ##### Struct ```json { "type": "my_crate::TestEnum", "enum": { "variant": "C", "struct": { "foo": { "type": "f32", "value": 1.23 }, "bar": { "type": "bool", "value": false } } } } ``` ## Design Decisions <details> <summary><strong>View Section</strong></summary> This section is here to provide some context for why certain decisions were made for this PR, alternatives that could have been used instead, and what could be improved upon in the future. ### Variant Representation One of the biggest decisions was to decide on how to represent variants. The current design uses a "all-in-one" design where unit, tuple, and struct variants are all simultaneously represented by the `Enum` trait. This is not the only way it could have been done, though. #### Alternatives ##### 1. Variant Traits One way of representing variants would be to define traits for each variant, implementing them whenever an enum featured at least one instance of them. This would allow us to define variants like: ```rust pub trait Enum: Reflect { fn variant(&self) -> Variant; } pub enum Variant<'a> { Unit, Tuple(&'a dyn TupleVariant), Struct(&'a dyn StructVariant), } pub trait TupleVariant { fn field_len(&self) -> usize; // ... } ``` And then do things like: ```rust fn get_tuple_len(foo: &dyn Enum) -> usize { match foo.variant() { Variant::Tuple(tuple) => tuple.field_len(), _ => panic!("not a tuple variant!") } } ``` The reason this PR does not go with this approach is because of the fact that variants are not separate types. In other words, we cannot implement traits on specific variants— these cover the *entire* enum. This means we offer an easy footgun: ```rust let foo: Option<i32> = None; let my_enum = Box::new(foo) as Box<dyn TupleVariant>; ``` Here, `my_enum` contains `foo`, which is a unit variant. However, since we need to implement `TupleVariant` for `Option` as a whole, it's possible to perform such a cast. This is obviously wrong, but could easily go unnoticed. So unfortunately, this makes it not a good candidate for representing variants. ##### 2. Variant Structs To get around the issue of traits necessarily needing to apply to both the enum and its variants, we could instead use structs that are created on a per-variant basis. This was also considered but was ultimately [[removed](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c)](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c) due to concerns about allocations. Each variant struct would probably look something like: ```rust pub trait Enum: Reflect { fn variant_mut(&self) -> VariantMut; } pub enum VariantMut<'a> { Unit, Tuple(TupleVariantMut), Struct(StructVariantMut), } struct StructVariantMut<'a> { fields: Vec<&'a mut dyn Reflect>, field_indices: HashMap<Cow<'static, str>, usize> } ``` This allows us to isolate struct variants into their own defined struct and define methods specifically for their use. It also prevents users from casting to it since it's not a trait. However, this is not an optimal solution. Both `field_indices` and `fields` will require an allocation (remember, a `Box<[T]>` still requires a `Vec<T>` in order to be constructed). This *might* be a problem if called frequently enough. ##### 3. Generated Structs The original design, implemented by @Davier, instead generates structs specific for each variant. So if we had a variant path like `Foo::Bar`, we'd generate a struct named `FooBarWrapper`. This would be newtyped around the original enum and forward tuple or struct methods to the enum with the chosen variant. Because it involved using the `Tuple` and `Struct` traits (which are also both bound on `Reflect`), this meant a bit more code had to be generated. For a single struct variant with one field, the generated code amounted to ~110LoC. However, each new field added to that variant only added ~6 more LoC. In order to work properly, the enum had to be transmuted to the generated struct: ```rust fn variant(&self) -> crate::EnumVariant<'_> { match self { Foo::Bar {value: i32} => { let wrapper_ref = unsafe { std::mem::transmute::<&Self, &FooBarWrapper>(self) }; crate::EnumVariant::Struct(wrapper_ref as &dyn crate::Struct) } } } ``` This works because `FooBarWrapper` is defined as `repr(transparent)`. Out of all the alternatives, this would probably be the one most likely to be used again in the future. The reasons for why this PR did not continue to use it was because: * To reduce generated code (which would hopefully speed up compile times) * To avoid cluttering the code with generated structs not visible to the user * To keep bevy_reflect simple and extensible (these generated structs act as proxies and might not play well with current or future systems) * To avoid additional unsafe blocks * My own misunderstanding of @Davier's code That last point is obviously on me. I misjudged the code to be too unsafe and unable to handle variant swapping (which it probably could) when I was rebasing it. Looking over it again when writing up this whole section, I see that it was actually a pretty clever way of handling variant representation. #### Benefits of All-in-One As stated before, the current implementation uses an all-in-one approach. All variants are capable of containing fields as far as `Enum` is concerned. This provides a few benefits that the alternatives do not (reduced indirection, safer code, etc.). The biggest benefit, though, is direct field access. Rather than forcing users to have to go through pattern matching, we grant direct access to the fields contained by the current variant. The reason we can do this is because all of the pattern matching happens internally. Getting the field at index `2` will automatically return `Some(...)` for the current variant if it has a field at that index or `None` if it doesn't (or can't). This could be useful for scenarios where the variant has already been verified or just set/swapped (or even where the type of variant doesn't matter): ```rust let dyn_enum: &mut dyn Enum = &mut Foo::Bar {value: 123}; // We know it's the `Bar` variant let field = dyn_enum.field("value").unwrap(); ``` Reflection is not a type-safe abstraction— almost every return value is wrapped in `Option<...>`. There are plenty of places to check and recheck that a value is what Reflect says it is. Forcing users to have to go through `match` each time they want to access a field might just be an extra step among dozens of other verification processes. Some might disagree, but ultimately, my view is that the benefit here is an improvement to the ergonomics and usability of reflected enums. </details> --- ## Changelog ### Added * Added `Enum` trait * Added `Enum` impl to `Reflect` derive macro * Added `DynamicEnum` struct * Added `DynamicVariant` * Added `EnumInfo` * Added `VariantInfo` * Added `StructVariantInfo` * Added `TupleVariantInfo` * Added `UnitVariantInfo` * Added serializtion/deserialization support for enums * Added `EnumSerializer` * Added `VariantType` * Added `VariantFieldIter` * Added `VariantField` * Added `enum_partial_eq(...)` * Added `enum_hash(...)` ### Changed * `Option<T>` now implements `Enum` * `bevy_window` now depends on `bevy_reflect` * Implemented `Reflect` and `FromReflect` for `WindowId` * Derive `FromReflect` on `PerspectiveProjection` * Derive `FromReflect` on `OrthographicProjection` * Derive `FromReflect` on `WindowOrigin` * Derive `FromReflect` on `ScalingMode` * Derive `FromReflect` on `DepthCalculation` ## Migration Guide * Enums no longer need to be treated as values and usages of `#[reflect_value(...)]` can be removed or replaced by `#[reflect(...)]` * Enums (including `Option<T>`) now take a different format when serializing. The format is described above, but this may cause issues for existing scenes that make use of enums. --- Also shout out to @nicopap for helping clean up some of the code here! It's a big feature so help like this is really appreciated! Co-authored-by: Gino Valente <gino.valente.code@gmail.com>
2022-08-02 22:14:41 +00:00
pub(crate) enum ReflectDerive<'a> {
Struct(ReflectStruct<'a>),
TupleStruct(ReflectStruct<'a>),
UnitStruct(ReflectStruct<'a>),
Enum(ReflectEnum<'a>),
Value(ReflectMeta<'a>),
bevy_reflect_derive: Tidying up the code (#4712) # Objective The `bevy_reflect_derive` crate is not the cleanest or easiest to follow/maintain. The `lib.rs` file is especially difficult with over 1000 lines of code written in a confusing order. This is just a result of growth within the crate and it would be nice to clean it up for future work. ## Solution Split `bevy_reflect_derive` into many more submodules. The submodules include: * `container_attributes` - Code relating to container attributes * `derive_data` - Code relating to reflection-based derive metadata * `field_attributes` - Code relating to field attributes * `impls` - Code containing actual reflection implementations * `reflect_value` - Code relating to reflection-based value metadata * `registration` - Code relating to type registration * `utility` - General-purpose utility functions This leaves the `lib.rs` file to contain only the public macros, making it much easier to digest (and fewer than 200 lines). By breaking up the code into smaller modules, we make it easier for future contributors to find the code they're looking for or identify which module best fits their own additions. ### Metadata Structs This cleanup also adds two big metadata structs: `ReflectFieldAttr` and `ReflectDeriveData`. The former is used to store all attributes for a struct field (if any). The latter is used to store all metadata for struct-based derive inputs. Both significantly reduce code duplication and make editing these macros much simpler. The tradeoff is that we may collect more metadata than needed. However, this is usually a small thing (such as checking for attributes when they're not really needed or creating a `ReflectFieldAttr` for every field regardless of whether they actually have an attribute). We could try to remove these tradeoffs and squeeze some more performance out, but doing so might come at the cost of developer experience. Personally, I think it's much nicer to create a `ReflectFieldAttr` for every field since it means I don't have to do two `Option` checks. Others may disagree, though, and so we can discuss changing this either in this PR or in a future one. ### Out of Scope _Some_ documentation has been added or improved, but ultimately good docs are probably best saved for a dedicated PR. ## 🔍 Focus Points (for reviewers) I know it's a lot to sift through, so here is a list of **key points for reviewers**: - The following files contain code that was mostly just relocated: - `reflect_value.rs` - `registration.rs` - `container_attributes.rs` was also mostly moved but features some general cleanup (reducing nesting, removing hardcoded strings, etc.) and lots of doc comments - Most impl logic was moved from `lib.rs` to `impls.rs`, but they have been significantly modified to use the new `ReflectDeriveData` metadata struct in order to reduce duplication. - `derive_data.rs` and `field_attributes.rs` contain almost entirely new code and should probably be given the most attention. - Likewise, `from_reflect.rs` saw major changes using `ReflectDeriveData` so it should also be given focus. - There was no change to the `lib.rs` exports so the end-user API should be the same. ## Prior Work This task was initially tackled by @NathanSWard in #2377 (which was closed in favor of this PR), so hats off to them for beating me to the punch by nearly a year! --- ## Changelog * **[INTERNAL]** Split `bevy_reflect_derive` into smaller submodules * **[INTERNAL]** Add `ReflectFieldAttr` * **[INTERNAL]** Add `ReflectDeriveData` * Add `BevyManifest::get_path_direct()` method (`bevy_macro_utils`) Co-authored-by: MrGVSV <49806985+MrGVSV@users.noreply.github.com>
2022-05-12 19:43:23 +00:00
}
bevy_reflect: Reflect enums (#4761) # Objective > This is a revival of #1347. Credit for the original PR should go to @Davier. Currently, enums are treated as `ReflectRef::Value` types by `bevy_reflect`. Obviously, there needs to be better a better representation for enums using the reflection API. ## Solution Based on prior work from @Davier, an `Enum` trait has been added as well as the ability to automatically implement it via the `Reflect` derive macro. This allows enums to be expressed dynamically: ```rust #[derive(Reflect)] enum Foo { A, B(usize), C { value: f32 }, } let mut foo = Foo::B(123); assert_eq!("B", foo.variant_name()); assert_eq!(1, foo.field_len()); let new_value = DynamicEnum::from(Foo::C { value: 1.23 }); foo.apply(&new_value); assert_eq!(Foo::C{value: 1.23}, foo); ``` ### Features #### Derive Macro Use the `#[derive(Reflect)]` macro to automatically implement the `Enum` trait for enum definitions. Optionally, you can use `#[reflect(ignore)]` with both variants and variant fields, just like you can with structs. These ignored items will not be considered as part of the reflection and cannot be accessed via reflection. ```rust #[derive(Reflect)] enum TestEnum { A, // Uncomment to ignore all of `B` // #[reflect(ignore)] B(usize), C { // Uncomment to ignore only field `foo` of `C` // #[reflect(ignore)] foo: f32, bar: bool, }, } ``` #### Dynamic Enums Enums may be created/represented dynamically via the `DynamicEnum` struct. The main purpose of this struct is to allow enums to be deserialized into a partial state and to allow dynamic patching. In order to ensure conversion from a `DynamicEnum` to a concrete enum type goes smoothly, be sure to add `FromReflect` to your derive macro. ```rust let mut value = TestEnum::A; // Create from a concrete instance let dyn_enum = DynamicEnum::from(TestEnum::B(123)); value.apply(&dyn_enum); assert_eq!(TestEnum::B(123), value); // Create a purely dynamic instance let dyn_enum = DynamicEnum::new("TestEnum", "A", ()); value.apply(&dyn_enum); assert_eq!(TestEnum::A, value); ``` #### Variants An enum value is always represented as one of its variants— never the enum in its entirety. ```rust let value = TestEnum::A; assert_eq!("A", value.variant_name()); // Since we are using the `A` variant, we cannot also be the `B` variant assert_ne!("B", value.variant_name()); ``` All variant types are representable within the `Enum` trait: unit, struct, and tuple. You can get the current type like: ```rust match value.variant_type() { VariantType::Unit => println!("A unit variant!"), VariantType::Struct => println!("A struct variant!"), VariantType::Tuple => println!("A tuple variant!"), } ``` > Notice that they don't contain any values representing the fields. These are purely tags. If a variant has them, you can access the fields as well: ```rust let mut value = TestEnum::C { foo: 1.23, bar: false }; // Read/write specific fields *value.field_mut("bar").unwrap() = true; // Iterate over the entire collection of fields for field in value.iter_fields() { println!("{} = {:?}", field.name(), field.value()); } ``` #### Variant Swapping It might seem odd to group all variant types under a single trait (why allow `iter_fields` on a unit variant?), but the reason this was done ~~is to easily allow *variant swapping*.~~ As I was recently drafting up the **Design Decisions** section, I discovered that other solutions could have been made to work with variant swapping. So while there are reasons to keep the all-in-one approach, variant swapping is _not_ one of them. ```rust let mut value: Box<dyn Enum> = Box::new(TestEnum::A); value.set(Box::new(TestEnum::B(123))).unwrap(); ``` #### Serialization Enums can be serialized and deserialized via reflection without needing to implement `Serialize` or `Deserialize` themselves (which can save thousands of lines of generated code). Below are the ways an enum can be serialized. > Note, like the rest of reflection-based serialization, the order of the keys in these representations is important! ##### Unit ```json { "type": "my_crate::TestEnum", "enum": { "variant": "A" } } ``` ##### Tuple ```json { "type": "my_crate::TestEnum", "enum": { "variant": "B", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` <details> <summary>Effects on Option</summary> This ends up making `Option` look a little ugly: ```json { "type": "core::option::Option<usize>", "enum": { "variant": "Some", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` </details> ##### Struct ```json { "type": "my_crate::TestEnum", "enum": { "variant": "C", "struct": { "foo": { "type": "f32", "value": 1.23 }, "bar": { "type": "bool", "value": false } } } } ``` ## Design Decisions <details> <summary><strong>View Section</strong></summary> This section is here to provide some context for why certain decisions were made for this PR, alternatives that could have been used instead, and what could be improved upon in the future. ### Variant Representation One of the biggest decisions was to decide on how to represent variants. The current design uses a "all-in-one" design where unit, tuple, and struct variants are all simultaneously represented by the `Enum` trait. This is not the only way it could have been done, though. #### Alternatives ##### 1. Variant Traits One way of representing variants would be to define traits for each variant, implementing them whenever an enum featured at least one instance of them. This would allow us to define variants like: ```rust pub trait Enum: Reflect { fn variant(&self) -> Variant; } pub enum Variant<'a> { Unit, Tuple(&'a dyn TupleVariant), Struct(&'a dyn StructVariant), } pub trait TupleVariant { fn field_len(&self) -> usize; // ... } ``` And then do things like: ```rust fn get_tuple_len(foo: &dyn Enum) -> usize { match foo.variant() { Variant::Tuple(tuple) => tuple.field_len(), _ => panic!("not a tuple variant!") } } ``` The reason this PR does not go with this approach is because of the fact that variants are not separate types. In other words, we cannot implement traits on specific variants— these cover the *entire* enum. This means we offer an easy footgun: ```rust let foo: Option<i32> = None; let my_enum = Box::new(foo) as Box<dyn TupleVariant>; ``` Here, `my_enum` contains `foo`, which is a unit variant. However, since we need to implement `TupleVariant` for `Option` as a whole, it's possible to perform such a cast. This is obviously wrong, but could easily go unnoticed. So unfortunately, this makes it not a good candidate for representing variants. ##### 2. Variant Structs To get around the issue of traits necessarily needing to apply to both the enum and its variants, we could instead use structs that are created on a per-variant basis. This was also considered but was ultimately [[removed](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c)](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c) due to concerns about allocations. Each variant struct would probably look something like: ```rust pub trait Enum: Reflect { fn variant_mut(&self) -> VariantMut; } pub enum VariantMut<'a> { Unit, Tuple(TupleVariantMut), Struct(StructVariantMut), } struct StructVariantMut<'a> { fields: Vec<&'a mut dyn Reflect>, field_indices: HashMap<Cow<'static, str>, usize> } ``` This allows us to isolate struct variants into their own defined struct and define methods specifically for their use. It also prevents users from casting to it since it's not a trait. However, this is not an optimal solution. Both `field_indices` and `fields` will require an allocation (remember, a `Box<[T]>` still requires a `Vec<T>` in order to be constructed). This *might* be a problem if called frequently enough. ##### 3. Generated Structs The original design, implemented by @Davier, instead generates structs specific for each variant. So if we had a variant path like `Foo::Bar`, we'd generate a struct named `FooBarWrapper`. This would be newtyped around the original enum and forward tuple or struct methods to the enum with the chosen variant. Because it involved using the `Tuple` and `Struct` traits (which are also both bound on `Reflect`), this meant a bit more code had to be generated. For a single struct variant with one field, the generated code amounted to ~110LoC. However, each new field added to that variant only added ~6 more LoC. In order to work properly, the enum had to be transmuted to the generated struct: ```rust fn variant(&self) -> crate::EnumVariant<'_> { match self { Foo::Bar {value: i32} => { let wrapper_ref = unsafe { std::mem::transmute::<&Self, &FooBarWrapper>(self) }; crate::EnumVariant::Struct(wrapper_ref as &dyn crate::Struct) } } } ``` This works because `FooBarWrapper` is defined as `repr(transparent)`. Out of all the alternatives, this would probably be the one most likely to be used again in the future. The reasons for why this PR did not continue to use it was because: * To reduce generated code (which would hopefully speed up compile times) * To avoid cluttering the code with generated structs not visible to the user * To keep bevy_reflect simple and extensible (these generated structs act as proxies and might not play well with current or future systems) * To avoid additional unsafe blocks * My own misunderstanding of @Davier's code That last point is obviously on me. I misjudged the code to be too unsafe and unable to handle variant swapping (which it probably could) when I was rebasing it. Looking over it again when writing up this whole section, I see that it was actually a pretty clever way of handling variant representation. #### Benefits of All-in-One As stated before, the current implementation uses an all-in-one approach. All variants are capable of containing fields as far as `Enum` is concerned. This provides a few benefits that the alternatives do not (reduced indirection, safer code, etc.). The biggest benefit, though, is direct field access. Rather than forcing users to have to go through pattern matching, we grant direct access to the fields contained by the current variant. The reason we can do this is because all of the pattern matching happens internally. Getting the field at index `2` will automatically return `Some(...)` for the current variant if it has a field at that index or `None` if it doesn't (or can't). This could be useful for scenarios where the variant has already been verified or just set/swapped (or even where the type of variant doesn't matter): ```rust let dyn_enum: &mut dyn Enum = &mut Foo::Bar {value: 123}; // We know it's the `Bar` variant let field = dyn_enum.field("value").unwrap(); ``` Reflection is not a type-safe abstraction— almost every return value is wrapped in `Option<...>`. There are plenty of places to check and recheck that a value is what Reflect says it is. Forcing users to have to go through `match` each time they want to access a field might just be an extra step among dozens of other verification processes. Some might disagree, but ultimately, my view is that the benefit here is an improvement to the ergonomics and usability of reflected enums. </details> --- ## Changelog ### Added * Added `Enum` trait * Added `Enum` impl to `Reflect` derive macro * Added `DynamicEnum` struct * Added `DynamicVariant` * Added `EnumInfo` * Added `VariantInfo` * Added `StructVariantInfo` * Added `TupleVariantInfo` * Added `UnitVariantInfo` * Added serializtion/deserialization support for enums * Added `EnumSerializer` * Added `VariantType` * Added `VariantFieldIter` * Added `VariantField` * Added `enum_partial_eq(...)` * Added `enum_hash(...)` ### Changed * `Option<T>` now implements `Enum` * `bevy_window` now depends on `bevy_reflect` * Implemented `Reflect` and `FromReflect` for `WindowId` * Derive `FromReflect` on `PerspectiveProjection` * Derive `FromReflect` on `OrthographicProjection` * Derive `FromReflect` on `WindowOrigin` * Derive `FromReflect` on `ScalingMode` * Derive `FromReflect` on `DepthCalculation` ## Migration Guide * Enums no longer need to be treated as values and usages of `#[reflect_value(...)]` can be removed or replaced by `#[reflect(...)]` * Enums (including `Option<T>`) now take a different format when serializing. The format is described above, but this may cause issues for existing scenes that make use of enums. --- Also shout out to @nicopap for helping clean up some of the code here! It's a big feature so help like this is really appreciated! Co-authored-by: Gino Valente <gino.valente.code@gmail.com>
2022-08-02 22:14:41 +00:00
/// Metadata present on all reflected types, including name, generics, and attributes.
bevy_reflect_derive: Tidying up the code (#4712) # Objective The `bevy_reflect_derive` crate is not the cleanest or easiest to follow/maintain. The `lib.rs` file is especially difficult with over 1000 lines of code written in a confusing order. This is just a result of growth within the crate and it would be nice to clean it up for future work. ## Solution Split `bevy_reflect_derive` into many more submodules. The submodules include: * `container_attributes` - Code relating to container attributes * `derive_data` - Code relating to reflection-based derive metadata * `field_attributes` - Code relating to field attributes * `impls` - Code containing actual reflection implementations * `reflect_value` - Code relating to reflection-based value metadata * `registration` - Code relating to type registration * `utility` - General-purpose utility functions This leaves the `lib.rs` file to contain only the public macros, making it much easier to digest (and fewer than 200 lines). By breaking up the code into smaller modules, we make it easier for future contributors to find the code they're looking for or identify which module best fits their own additions. ### Metadata Structs This cleanup also adds two big metadata structs: `ReflectFieldAttr` and `ReflectDeriveData`. The former is used to store all attributes for a struct field (if any). The latter is used to store all metadata for struct-based derive inputs. Both significantly reduce code duplication and make editing these macros much simpler. The tradeoff is that we may collect more metadata than needed. However, this is usually a small thing (such as checking for attributes when they're not really needed or creating a `ReflectFieldAttr` for every field regardless of whether they actually have an attribute). We could try to remove these tradeoffs and squeeze some more performance out, but doing so might come at the cost of developer experience. Personally, I think it's much nicer to create a `ReflectFieldAttr` for every field since it means I don't have to do two `Option` checks. Others may disagree, though, and so we can discuss changing this either in this PR or in a future one. ### Out of Scope _Some_ documentation has been added or improved, but ultimately good docs are probably best saved for a dedicated PR. ## 🔍 Focus Points (for reviewers) I know it's a lot to sift through, so here is a list of **key points for reviewers**: - The following files contain code that was mostly just relocated: - `reflect_value.rs` - `registration.rs` - `container_attributes.rs` was also mostly moved but features some general cleanup (reducing nesting, removing hardcoded strings, etc.) and lots of doc comments - Most impl logic was moved from `lib.rs` to `impls.rs`, but they have been significantly modified to use the new `ReflectDeriveData` metadata struct in order to reduce duplication. - `derive_data.rs` and `field_attributes.rs` contain almost entirely new code and should probably be given the most attention. - Likewise, `from_reflect.rs` saw major changes using `ReflectDeriveData` so it should also be given focus. - There was no change to the `lib.rs` exports so the end-user API should be the same. ## Prior Work This task was initially tackled by @NathanSWard in #2377 (which was closed in favor of this PR), so hats off to them for beating me to the punch by nearly a year! --- ## Changelog * **[INTERNAL]** Split `bevy_reflect_derive` into smaller submodules * **[INTERNAL]** Add `ReflectFieldAttr` * **[INTERNAL]** Add `ReflectDeriveData` * Add `BevyManifest::get_path_direct()` method (`bevy_macro_utils`) Co-authored-by: MrGVSV <49806985+MrGVSV@users.noreply.github.com>
2022-05-12 19:43:23 +00:00
///
/// # Example
bevy_reflect: Reflect enums (#4761) # Objective > This is a revival of #1347. Credit for the original PR should go to @Davier. Currently, enums are treated as `ReflectRef::Value` types by `bevy_reflect`. Obviously, there needs to be better a better representation for enums using the reflection API. ## Solution Based on prior work from @Davier, an `Enum` trait has been added as well as the ability to automatically implement it via the `Reflect` derive macro. This allows enums to be expressed dynamically: ```rust #[derive(Reflect)] enum Foo { A, B(usize), C { value: f32 }, } let mut foo = Foo::B(123); assert_eq!("B", foo.variant_name()); assert_eq!(1, foo.field_len()); let new_value = DynamicEnum::from(Foo::C { value: 1.23 }); foo.apply(&new_value); assert_eq!(Foo::C{value: 1.23}, foo); ``` ### Features #### Derive Macro Use the `#[derive(Reflect)]` macro to automatically implement the `Enum` trait for enum definitions. Optionally, you can use `#[reflect(ignore)]` with both variants and variant fields, just like you can with structs. These ignored items will not be considered as part of the reflection and cannot be accessed via reflection. ```rust #[derive(Reflect)] enum TestEnum { A, // Uncomment to ignore all of `B` // #[reflect(ignore)] B(usize), C { // Uncomment to ignore only field `foo` of `C` // #[reflect(ignore)] foo: f32, bar: bool, }, } ``` #### Dynamic Enums Enums may be created/represented dynamically via the `DynamicEnum` struct. The main purpose of this struct is to allow enums to be deserialized into a partial state and to allow dynamic patching. In order to ensure conversion from a `DynamicEnum` to a concrete enum type goes smoothly, be sure to add `FromReflect` to your derive macro. ```rust let mut value = TestEnum::A; // Create from a concrete instance let dyn_enum = DynamicEnum::from(TestEnum::B(123)); value.apply(&dyn_enum); assert_eq!(TestEnum::B(123), value); // Create a purely dynamic instance let dyn_enum = DynamicEnum::new("TestEnum", "A", ()); value.apply(&dyn_enum); assert_eq!(TestEnum::A, value); ``` #### Variants An enum value is always represented as one of its variants— never the enum in its entirety. ```rust let value = TestEnum::A; assert_eq!("A", value.variant_name()); // Since we are using the `A` variant, we cannot also be the `B` variant assert_ne!("B", value.variant_name()); ``` All variant types are representable within the `Enum` trait: unit, struct, and tuple. You can get the current type like: ```rust match value.variant_type() { VariantType::Unit => println!("A unit variant!"), VariantType::Struct => println!("A struct variant!"), VariantType::Tuple => println!("A tuple variant!"), } ``` > Notice that they don't contain any values representing the fields. These are purely tags. If a variant has them, you can access the fields as well: ```rust let mut value = TestEnum::C { foo: 1.23, bar: false }; // Read/write specific fields *value.field_mut("bar").unwrap() = true; // Iterate over the entire collection of fields for field in value.iter_fields() { println!("{} = {:?}", field.name(), field.value()); } ``` #### Variant Swapping It might seem odd to group all variant types under a single trait (why allow `iter_fields` on a unit variant?), but the reason this was done ~~is to easily allow *variant swapping*.~~ As I was recently drafting up the **Design Decisions** section, I discovered that other solutions could have been made to work with variant swapping. So while there are reasons to keep the all-in-one approach, variant swapping is _not_ one of them. ```rust let mut value: Box<dyn Enum> = Box::new(TestEnum::A); value.set(Box::new(TestEnum::B(123))).unwrap(); ``` #### Serialization Enums can be serialized and deserialized via reflection without needing to implement `Serialize` or `Deserialize` themselves (which can save thousands of lines of generated code). Below are the ways an enum can be serialized. > Note, like the rest of reflection-based serialization, the order of the keys in these representations is important! ##### Unit ```json { "type": "my_crate::TestEnum", "enum": { "variant": "A" } } ``` ##### Tuple ```json { "type": "my_crate::TestEnum", "enum": { "variant": "B", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` <details> <summary>Effects on Option</summary> This ends up making `Option` look a little ugly: ```json { "type": "core::option::Option<usize>", "enum": { "variant": "Some", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` </details> ##### Struct ```json { "type": "my_crate::TestEnum", "enum": { "variant": "C", "struct": { "foo": { "type": "f32", "value": 1.23 }, "bar": { "type": "bool", "value": false } } } } ``` ## Design Decisions <details> <summary><strong>View Section</strong></summary> This section is here to provide some context for why certain decisions were made for this PR, alternatives that could have been used instead, and what could be improved upon in the future. ### Variant Representation One of the biggest decisions was to decide on how to represent variants. The current design uses a "all-in-one" design where unit, tuple, and struct variants are all simultaneously represented by the `Enum` trait. This is not the only way it could have been done, though. #### Alternatives ##### 1. Variant Traits One way of representing variants would be to define traits for each variant, implementing them whenever an enum featured at least one instance of them. This would allow us to define variants like: ```rust pub trait Enum: Reflect { fn variant(&self) -> Variant; } pub enum Variant<'a> { Unit, Tuple(&'a dyn TupleVariant), Struct(&'a dyn StructVariant), } pub trait TupleVariant { fn field_len(&self) -> usize; // ... } ``` And then do things like: ```rust fn get_tuple_len(foo: &dyn Enum) -> usize { match foo.variant() { Variant::Tuple(tuple) => tuple.field_len(), _ => panic!("not a tuple variant!") } } ``` The reason this PR does not go with this approach is because of the fact that variants are not separate types. In other words, we cannot implement traits on specific variants— these cover the *entire* enum. This means we offer an easy footgun: ```rust let foo: Option<i32> = None; let my_enum = Box::new(foo) as Box<dyn TupleVariant>; ``` Here, `my_enum` contains `foo`, which is a unit variant. However, since we need to implement `TupleVariant` for `Option` as a whole, it's possible to perform such a cast. This is obviously wrong, but could easily go unnoticed. So unfortunately, this makes it not a good candidate for representing variants. ##### 2. Variant Structs To get around the issue of traits necessarily needing to apply to both the enum and its variants, we could instead use structs that are created on a per-variant basis. This was also considered but was ultimately [[removed](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c)](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c) due to concerns about allocations. Each variant struct would probably look something like: ```rust pub trait Enum: Reflect { fn variant_mut(&self) -> VariantMut; } pub enum VariantMut<'a> { Unit, Tuple(TupleVariantMut), Struct(StructVariantMut), } struct StructVariantMut<'a> { fields: Vec<&'a mut dyn Reflect>, field_indices: HashMap<Cow<'static, str>, usize> } ``` This allows us to isolate struct variants into their own defined struct and define methods specifically for their use. It also prevents users from casting to it since it's not a trait. However, this is not an optimal solution. Both `field_indices` and `fields` will require an allocation (remember, a `Box<[T]>` still requires a `Vec<T>` in order to be constructed). This *might* be a problem if called frequently enough. ##### 3. Generated Structs The original design, implemented by @Davier, instead generates structs specific for each variant. So if we had a variant path like `Foo::Bar`, we'd generate a struct named `FooBarWrapper`. This would be newtyped around the original enum and forward tuple or struct methods to the enum with the chosen variant. Because it involved using the `Tuple` and `Struct` traits (which are also both bound on `Reflect`), this meant a bit more code had to be generated. For a single struct variant with one field, the generated code amounted to ~110LoC. However, each new field added to that variant only added ~6 more LoC. In order to work properly, the enum had to be transmuted to the generated struct: ```rust fn variant(&self) -> crate::EnumVariant<'_> { match self { Foo::Bar {value: i32} => { let wrapper_ref = unsafe { std::mem::transmute::<&Self, &FooBarWrapper>(self) }; crate::EnumVariant::Struct(wrapper_ref as &dyn crate::Struct) } } } ``` This works because `FooBarWrapper` is defined as `repr(transparent)`. Out of all the alternatives, this would probably be the one most likely to be used again in the future. The reasons for why this PR did not continue to use it was because: * To reduce generated code (which would hopefully speed up compile times) * To avoid cluttering the code with generated structs not visible to the user * To keep bevy_reflect simple and extensible (these generated structs act as proxies and might not play well with current or future systems) * To avoid additional unsafe blocks * My own misunderstanding of @Davier's code That last point is obviously on me. I misjudged the code to be too unsafe and unable to handle variant swapping (which it probably could) when I was rebasing it. Looking over it again when writing up this whole section, I see that it was actually a pretty clever way of handling variant representation. #### Benefits of All-in-One As stated before, the current implementation uses an all-in-one approach. All variants are capable of containing fields as far as `Enum` is concerned. This provides a few benefits that the alternatives do not (reduced indirection, safer code, etc.). The biggest benefit, though, is direct field access. Rather than forcing users to have to go through pattern matching, we grant direct access to the fields contained by the current variant. The reason we can do this is because all of the pattern matching happens internally. Getting the field at index `2` will automatically return `Some(...)` for the current variant if it has a field at that index or `None` if it doesn't (or can't). This could be useful for scenarios where the variant has already been verified or just set/swapped (or even where the type of variant doesn't matter): ```rust let dyn_enum: &mut dyn Enum = &mut Foo::Bar {value: 123}; // We know it's the `Bar` variant let field = dyn_enum.field("value").unwrap(); ``` Reflection is not a type-safe abstraction— almost every return value is wrapped in `Option<...>`. There are plenty of places to check and recheck that a value is what Reflect says it is. Forcing users to have to go through `match` each time they want to access a field might just be an extra step among dozens of other verification processes. Some might disagree, but ultimately, my view is that the benefit here is an improvement to the ergonomics and usability of reflected enums. </details> --- ## Changelog ### Added * Added `Enum` trait * Added `Enum` impl to `Reflect` derive macro * Added `DynamicEnum` struct * Added `DynamicVariant` * Added `EnumInfo` * Added `VariantInfo` * Added `StructVariantInfo` * Added `TupleVariantInfo` * Added `UnitVariantInfo` * Added serializtion/deserialization support for enums * Added `EnumSerializer` * Added `VariantType` * Added `VariantFieldIter` * Added `VariantField` * Added `enum_partial_eq(...)` * Added `enum_hash(...)` ### Changed * `Option<T>` now implements `Enum` * `bevy_window` now depends on `bevy_reflect` * Implemented `Reflect` and `FromReflect` for `WindowId` * Derive `FromReflect` on `PerspectiveProjection` * Derive `FromReflect` on `OrthographicProjection` * Derive `FromReflect` on `WindowOrigin` * Derive `FromReflect` on `ScalingMode` * Derive `FromReflect` on `DepthCalculation` ## Migration Guide * Enums no longer need to be treated as values and usages of `#[reflect_value(...)]` can be removed or replaced by `#[reflect(...)]` * Enums (including `Option<T>`) now take a different format when serializing. The format is described above, but this may cause issues for existing scenes that make use of enums. --- Also shout out to @nicopap for helping clean up some of the code here! It's a big feature so help like this is really appreciated! Co-authored-by: Gino Valente <gino.valente.code@gmail.com>
2022-08-02 22:14:41 +00:00
///
/// ```ignore (bevy_reflect is not accessible from this crate)
bevy_reflect: Reflect enums (#4761) # Objective > This is a revival of #1347. Credit for the original PR should go to @Davier. Currently, enums are treated as `ReflectRef::Value` types by `bevy_reflect`. Obviously, there needs to be better a better representation for enums using the reflection API. ## Solution Based on prior work from @Davier, an `Enum` trait has been added as well as the ability to automatically implement it via the `Reflect` derive macro. This allows enums to be expressed dynamically: ```rust #[derive(Reflect)] enum Foo { A, B(usize), C { value: f32 }, } let mut foo = Foo::B(123); assert_eq!("B", foo.variant_name()); assert_eq!(1, foo.field_len()); let new_value = DynamicEnum::from(Foo::C { value: 1.23 }); foo.apply(&new_value); assert_eq!(Foo::C{value: 1.23}, foo); ``` ### Features #### Derive Macro Use the `#[derive(Reflect)]` macro to automatically implement the `Enum` trait for enum definitions. Optionally, you can use `#[reflect(ignore)]` with both variants and variant fields, just like you can with structs. These ignored items will not be considered as part of the reflection and cannot be accessed via reflection. ```rust #[derive(Reflect)] enum TestEnum { A, // Uncomment to ignore all of `B` // #[reflect(ignore)] B(usize), C { // Uncomment to ignore only field `foo` of `C` // #[reflect(ignore)] foo: f32, bar: bool, }, } ``` #### Dynamic Enums Enums may be created/represented dynamically via the `DynamicEnum` struct. The main purpose of this struct is to allow enums to be deserialized into a partial state and to allow dynamic patching. In order to ensure conversion from a `DynamicEnum` to a concrete enum type goes smoothly, be sure to add `FromReflect` to your derive macro. ```rust let mut value = TestEnum::A; // Create from a concrete instance let dyn_enum = DynamicEnum::from(TestEnum::B(123)); value.apply(&dyn_enum); assert_eq!(TestEnum::B(123), value); // Create a purely dynamic instance let dyn_enum = DynamicEnum::new("TestEnum", "A", ()); value.apply(&dyn_enum); assert_eq!(TestEnum::A, value); ``` #### Variants An enum value is always represented as one of its variants— never the enum in its entirety. ```rust let value = TestEnum::A; assert_eq!("A", value.variant_name()); // Since we are using the `A` variant, we cannot also be the `B` variant assert_ne!("B", value.variant_name()); ``` All variant types are representable within the `Enum` trait: unit, struct, and tuple. You can get the current type like: ```rust match value.variant_type() { VariantType::Unit => println!("A unit variant!"), VariantType::Struct => println!("A struct variant!"), VariantType::Tuple => println!("A tuple variant!"), } ``` > Notice that they don't contain any values representing the fields. These are purely tags. If a variant has them, you can access the fields as well: ```rust let mut value = TestEnum::C { foo: 1.23, bar: false }; // Read/write specific fields *value.field_mut("bar").unwrap() = true; // Iterate over the entire collection of fields for field in value.iter_fields() { println!("{} = {:?}", field.name(), field.value()); } ``` #### Variant Swapping It might seem odd to group all variant types under a single trait (why allow `iter_fields` on a unit variant?), but the reason this was done ~~is to easily allow *variant swapping*.~~ As I was recently drafting up the **Design Decisions** section, I discovered that other solutions could have been made to work with variant swapping. So while there are reasons to keep the all-in-one approach, variant swapping is _not_ one of them. ```rust let mut value: Box<dyn Enum> = Box::new(TestEnum::A); value.set(Box::new(TestEnum::B(123))).unwrap(); ``` #### Serialization Enums can be serialized and deserialized via reflection without needing to implement `Serialize` or `Deserialize` themselves (which can save thousands of lines of generated code). Below are the ways an enum can be serialized. > Note, like the rest of reflection-based serialization, the order of the keys in these representations is important! ##### Unit ```json { "type": "my_crate::TestEnum", "enum": { "variant": "A" } } ``` ##### Tuple ```json { "type": "my_crate::TestEnum", "enum": { "variant": "B", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` <details> <summary>Effects on Option</summary> This ends up making `Option` look a little ugly: ```json { "type": "core::option::Option<usize>", "enum": { "variant": "Some", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` </details> ##### Struct ```json { "type": "my_crate::TestEnum", "enum": { "variant": "C", "struct": { "foo": { "type": "f32", "value": 1.23 }, "bar": { "type": "bool", "value": false } } } } ``` ## Design Decisions <details> <summary><strong>View Section</strong></summary> This section is here to provide some context for why certain decisions were made for this PR, alternatives that could have been used instead, and what could be improved upon in the future. ### Variant Representation One of the biggest decisions was to decide on how to represent variants. The current design uses a "all-in-one" design where unit, tuple, and struct variants are all simultaneously represented by the `Enum` trait. This is not the only way it could have been done, though. #### Alternatives ##### 1. Variant Traits One way of representing variants would be to define traits for each variant, implementing them whenever an enum featured at least one instance of them. This would allow us to define variants like: ```rust pub trait Enum: Reflect { fn variant(&self) -> Variant; } pub enum Variant<'a> { Unit, Tuple(&'a dyn TupleVariant), Struct(&'a dyn StructVariant), } pub trait TupleVariant { fn field_len(&self) -> usize; // ... } ``` And then do things like: ```rust fn get_tuple_len(foo: &dyn Enum) -> usize { match foo.variant() { Variant::Tuple(tuple) => tuple.field_len(), _ => panic!("not a tuple variant!") } } ``` The reason this PR does not go with this approach is because of the fact that variants are not separate types. In other words, we cannot implement traits on specific variants— these cover the *entire* enum. This means we offer an easy footgun: ```rust let foo: Option<i32> = None; let my_enum = Box::new(foo) as Box<dyn TupleVariant>; ``` Here, `my_enum` contains `foo`, which is a unit variant. However, since we need to implement `TupleVariant` for `Option` as a whole, it's possible to perform such a cast. This is obviously wrong, but could easily go unnoticed. So unfortunately, this makes it not a good candidate for representing variants. ##### 2. Variant Structs To get around the issue of traits necessarily needing to apply to both the enum and its variants, we could instead use structs that are created on a per-variant basis. This was also considered but was ultimately [[removed](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c)](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c) due to concerns about allocations. Each variant struct would probably look something like: ```rust pub trait Enum: Reflect { fn variant_mut(&self) -> VariantMut; } pub enum VariantMut<'a> { Unit, Tuple(TupleVariantMut), Struct(StructVariantMut), } struct StructVariantMut<'a> { fields: Vec<&'a mut dyn Reflect>, field_indices: HashMap<Cow<'static, str>, usize> } ``` This allows us to isolate struct variants into their own defined struct and define methods specifically for their use. It also prevents users from casting to it since it's not a trait. However, this is not an optimal solution. Both `field_indices` and `fields` will require an allocation (remember, a `Box<[T]>` still requires a `Vec<T>` in order to be constructed). This *might* be a problem if called frequently enough. ##### 3. Generated Structs The original design, implemented by @Davier, instead generates structs specific for each variant. So if we had a variant path like `Foo::Bar`, we'd generate a struct named `FooBarWrapper`. This would be newtyped around the original enum and forward tuple or struct methods to the enum with the chosen variant. Because it involved using the `Tuple` and `Struct` traits (which are also both bound on `Reflect`), this meant a bit more code had to be generated. For a single struct variant with one field, the generated code amounted to ~110LoC. However, each new field added to that variant only added ~6 more LoC. In order to work properly, the enum had to be transmuted to the generated struct: ```rust fn variant(&self) -> crate::EnumVariant<'_> { match self { Foo::Bar {value: i32} => { let wrapper_ref = unsafe { std::mem::transmute::<&Self, &FooBarWrapper>(self) }; crate::EnumVariant::Struct(wrapper_ref as &dyn crate::Struct) } } } ``` This works because `FooBarWrapper` is defined as `repr(transparent)`. Out of all the alternatives, this would probably be the one most likely to be used again in the future. The reasons for why this PR did not continue to use it was because: * To reduce generated code (which would hopefully speed up compile times) * To avoid cluttering the code with generated structs not visible to the user * To keep bevy_reflect simple and extensible (these generated structs act as proxies and might not play well with current or future systems) * To avoid additional unsafe blocks * My own misunderstanding of @Davier's code That last point is obviously on me. I misjudged the code to be too unsafe and unable to handle variant swapping (which it probably could) when I was rebasing it. Looking over it again when writing up this whole section, I see that it was actually a pretty clever way of handling variant representation. #### Benefits of All-in-One As stated before, the current implementation uses an all-in-one approach. All variants are capable of containing fields as far as `Enum` is concerned. This provides a few benefits that the alternatives do not (reduced indirection, safer code, etc.). The biggest benefit, though, is direct field access. Rather than forcing users to have to go through pattern matching, we grant direct access to the fields contained by the current variant. The reason we can do this is because all of the pattern matching happens internally. Getting the field at index `2` will automatically return `Some(...)` for the current variant if it has a field at that index or `None` if it doesn't (or can't). This could be useful for scenarios where the variant has already been verified or just set/swapped (or even where the type of variant doesn't matter): ```rust let dyn_enum: &mut dyn Enum = &mut Foo::Bar {value: 123}; // We know it's the `Bar` variant let field = dyn_enum.field("value").unwrap(); ``` Reflection is not a type-safe abstraction— almost every return value is wrapped in `Option<...>`. There are plenty of places to check and recheck that a value is what Reflect says it is. Forcing users to have to go through `match` each time they want to access a field might just be an extra step among dozens of other verification processes. Some might disagree, but ultimately, my view is that the benefit here is an improvement to the ergonomics and usability of reflected enums. </details> --- ## Changelog ### Added * Added `Enum` trait * Added `Enum` impl to `Reflect` derive macro * Added `DynamicEnum` struct * Added `DynamicVariant` * Added `EnumInfo` * Added `VariantInfo` * Added `StructVariantInfo` * Added `TupleVariantInfo` * Added `UnitVariantInfo` * Added serializtion/deserialization support for enums * Added `EnumSerializer` * Added `VariantType` * Added `VariantFieldIter` * Added `VariantField` * Added `enum_partial_eq(...)` * Added `enum_hash(...)` ### Changed * `Option<T>` now implements `Enum` * `bevy_window` now depends on `bevy_reflect` * Implemented `Reflect` and `FromReflect` for `WindowId` * Derive `FromReflect` on `PerspectiveProjection` * Derive `FromReflect` on `OrthographicProjection` * Derive `FromReflect` on `WindowOrigin` * Derive `FromReflect` on `ScalingMode` * Derive `FromReflect` on `DepthCalculation` ## Migration Guide * Enums no longer need to be treated as values and usages of `#[reflect_value(...)]` can be removed or replaced by `#[reflect(...)]` * Enums (including `Option<T>`) now take a different format when serializing. The format is described above, but this may cause issues for existing scenes that make use of enums. --- Also shout out to @nicopap for helping clean up some of the code here! It's a big feature so help like this is really appreciated! Co-authored-by: Gino Valente <gino.valente.code@gmail.com>
2022-08-02 22:14:41 +00:00
/// #[derive(Reflect)]
/// // traits
bevy_reflect_derive: Tidying up the code (#4712) # Objective The `bevy_reflect_derive` crate is not the cleanest or easiest to follow/maintain. The `lib.rs` file is especially difficult with over 1000 lines of code written in a confusing order. This is just a result of growth within the crate and it would be nice to clean it up for future work. ## Solution Split `bevy_reflect_derive` into many more submodules. The submodules include: * `container_attributes` - Code relating to container attributes * `derive_data` - Code relating to reflection-based derive metadata * `field_attributes` - Code relating to field attributes * `impls` - Code containing actual reflection implementations * `reflect_value` - Code relating to reflection-based value metadata * `registration` - Code relating to type registration * `utility` - General-purpose utility functions This leaves the `lib.rs` file to contain only the public macros, making it much easier to digest (and fewer than 200 lines). By breaking up the code into smaller modules, we make it easier for future contributors to find the code they're looking for or identify which module best fits their own additions. ### Metadata Structs This cleanup also adds two big metadata structs: `ReflectFieldAttr` and `ReflectDeriveData`. The former is used to store all attributes for a struct field (if any). The latter is used to store all metadata for struct-based derive inputs. Both significantly reduce code duplication and make editing these macros much simpler. The tradeoff is that we may collect more metadata than needed. However, this is usually a small thing (such as checking for attributes when they're not really needed or creating a `ReflectFieldAttr` for every field regardless of whether they actually have an attribute). We could try to remove these tradeoffs and squeeze some more performance out, but doing so might come at the cost of developer experience. Personally, I think it's much nicer to create a `ReflectFieldAttr` for every field since it means I don't have to do two `Option` checks. Others may disagree, though, and so we can discuss changing this either in this PR or in a future one. ### Out of Scope _Some_ documentation has been added or improved, but ultimately good docs are probably best saved for a dedicated PR. ## 🔍 Focus Points (for reviewers) I know it's a lot to sift through, so here is a list of **key points for reviewers**: - The following files contain code that was mostly just relocated: - `reflect_value.rs` - `registration.rs` - `container_attributes.rs` was also mostly moved but features some general cleanup (reducing nesting, removing hardcoded strings, etc.) and lots of doc comments - Most impl logic was moved from `lib.rs` to `impls.rs`, but they have been significantly modified to use the new `ReflectDeriveData` metadata struct in order to reduce duplication. - `derive_data.rs` and `field_attributes.rs` contain almost entirely new code and should probably be given the most attention. - Likewise, `from_reflect.rs` saw major changes using `ReflectDeriveData` so it should also be given focus. - There was no change to the `lib.rs` exports so the end-user API should be the same. ## Prior Work This task was initially tackled by @NathanSWard in #2377 (which was closed in favor of this PR), so hats off to them for beating me to the punch by nearly a year! --- ## Changelog * **[INTERNAL]** Split `bevy_reflect_derive` into smaller submodules * **[INTERNAL]** Add `ReflectFieldAttr` * **[INTERNAL]** Add `ReflectDeriveData` * Add `BevyManifest::get_path_direct()` method (`bevy_macro_utils`) Co-authored-by: MrGVSV <49806985+MrGVSV@users.noreply.github.com>
2022-05-12 19:43:23 +00:00
/// // |----------------------------------------|
/// #[reflect(PartialEq, Serialize, Deserialize, Default)]
reflect: `TypePath` part 2 (#8768) # Objective - Followup to #7184. - ~Deprecate `TypeUuid` and remove its internal references.~ No longer part of this PR. - Use `TypePath` for the type registry, and (de)serialisation instead of `std::any::type_name`. - Allow accessing type path information behind proxies. ## Solution - Introduce methods on `TypeInfo` and friends for dynamically querying type path. These methods supersede the old `type_name` methods. - Remove `Reflect::type_name` in favor of `DynamicTypePath::type_path` and `TypeInfo::type_path_table`. - Switch all uses of `std::any::type_name` in reflection, non-debugging contexts to use `TypePath`. --- ## Changelog - Added `TypePathTable` for dynamically accessing methods on `TypePath` through `TypeInfo` and the type registry. - Removed `type_name` from all `TypeInfo`-like structs. - Added `type_path` and `type_path_table` methods to all `TypeInfo`-like structs. - Removed `Reflect::type_name` in favor of `DynamicTypePath::reflect_type_path` and `TypeInfo::type_path`. - Changed the signature of all `DynamicTypePath` methods to return strings with a static lifetime. ## Migration Guide - Rely on `TypePath` instead of `std::any::type_name` for all stability guarantees and for use in all reflection contexts, this is used through with one of the following APIs: - `TypePath::type_path` if you have a concrete type and not a value. - `DynamicTypePath::reflect_type_path` if you have an `dyn Reflect` value without a concrete type. - `TypeInfo::type_path` for use through the registry or if you want to work with the represented type of a `DynamicFoo`. - Remove `type_name` from manual `Reflect` implementations. - Use `type_path` and `type_path_table` in place of `type_name` on `TypeInfo`-like structs. - Use `get_with_type_path(_mut)` over `get_with_type_name(_mut)`. ## Note to reviewers I think if anything we were a little overzealous in merging #7184 and we should take that extra care here. In my mind, this is the "point of no return" for `TypePath` and while I think we all agree on the design, we should carefully consider if the finer details and current implementations are actually how we want them moving forward. For example [this incorrect `TypePath` implementation for `String`](https://github.com/soqb/bevy/blob/3fea3c6c0b5719dfbd3d4230f5282ec80d82556a/crates/bevy_reflect/src/impls/std.rs#L90) (note that `String` is in the default Rust prelude) snuck in completely under the radar.
2023-10-09 19:33:03 +00:00
/// // type_path generics
bevy_reflect_derive: Tidying up the code (#4712) # Objective The `bevy_reflect_derive` crate is not the cleanest or easiest to follow/maintain. The `lib.rs` file is especially difficult with over 1000 lines of code written in a confusing order. This is just a result of growth within the crate and it would be nice to clean it up for future work. ## Solution Split `bevy_reflect_derive` into many more submodules. The submodules include: * `container_attributes` - Code relating to container attributes * `derive_data` - Code relating to reflection-based derive metadata * `field_attributes` - Code relating to field attributes * `impls` - Code containing actual reflection implementations * `reflect_value` - Code relating to reflection-based value metadata * `registration` - Code relating to type registration * `utility` - General-purpose utility functions This leaves the `lib.rs` file to contain only the public macros, making it much easier to digest (and fewer than 200 lines). By breaking up the code into smaller modules, we make it easier for future contributors to find the code they're looking for or identify which module best fits their own additions. ### Metadata Structs This cleanup also adds two big metadata structs: `ReflectFieldAttr` and `ReflectDeriveData`. The former is used to store all attributes for a struct field (if any). The latter is used to store all metadata for struct-based derive inputs. Both significantly reduce code duplication and make editing these macros much simpler. The tradeoff is that we may collect more metadata than needed. However, this is usually a small thing (such as checking for attributes when they're not really needed or creating a `ReflectFieldAttr` for every field regardless of whether they actually have an attribute). We could try to remove these tradeoffs and squeeze some more performance out, but doing so might come at the cost of developer experience. Personally, I think it's much nicer to create a `ReflectFieldAttr` for every field since it means I don't have to do two `Option` checks. Others may disagree, though, and so we can discuss changing this either in this PR or in a future one. ### Out of Scope _Some_ documentation has been added or improved, but ultimately good docs are probably best saved for a dedicated PR. ## 🔍 Focus Points (for reviewers) I know it's a lot to sift through, so here is a list of **key points for reviewers**: - The following files contain code that was mostly just relocated: - `reflect_value.rs` - `registration.rs` - `container_attributes.rs` was also mostly moved but features some general cleanup (reducing nesting, removing hardcoded strings, etc.) and lots of doc comments - Most impl logic was moved from `lib.rs` to `impls.rs`, but they have been significantly modified to use the new `ReflectDeriveData` metadata struct in order to reduce duplication. - `derive_data.rs` and `field_attributes.rs` contain almost entirely new code and should probably be given the most attention. - Likewise, `from_reflect.rs` saw major changes using `ReflectDeriveData` so it should also be given focus. - There was no change to the `lib.rs` exports so the end-user API should be the same. ## Prior Work This task was initially tackled by @NathanSWard in #2377 (which was closed in favor of this PR), so hats off to them for beating me to the punch by nearly a year! --- ## Changelog * **[INTERNAL]** Split `bevy_reflect_derive` into smaller submodules * **[INTERNAL]** Add `ReflectFieldAttr` * **[INTERNAL]** Add `ReflectDeriveData` * Add `BevyManifest::get_path_direct()` method (`bevy_macro_utils`) Co-authored-by: MrGVSV <49806985+MrGVSV@users.noreply.github.com>
2022-05-12 19:43:23 +00:00
/// // |-------------------||----------|
bevy_reflect: Reflect enums (#4761) # Objective > This is a revival of #1347. Credit for the original PR should go to @Davier. Currently, enums are treated as `ReflectRef::Value` types by `bevy_reflect`. Obviously, there needs to be better a better representation for enums using the reflection API. ## Solution Based on prior work from @Davier, an `Enum` trait has been added as well as the ability to automatically implement it via the `Reflect` derive macro. This allows enums to be expressed dynamically: ```rust #[derive(Reflect)] enum Foo { A, B(usize), C { value: f32 }, } let mut foo = Foo::B(123); assert_eq!("B", foo.variant_name()); assert_eq!(1, foo.field_len()); let new_value = DynamicEnum::from(Foo::C { value: 1.23 }); foo.apply(&new_value); assert_eq!(Foo::C{value: 1.23}, foo); ``` ### Features #### Derive Macro Use the `#[derive(Reflect)]` macro to automatically implement the `Enum` trait for enum definitions. Optionally, you can use `#[reflect(ignore)]` with both variants and variant fields, just like you can with structs. These ignored items will not be considered as part of the reflection and cannot be accessed via reflection. ```rust #[derive(Reflect)] enum TestEnum { A, // Uncomment to ignore all of `B` // #[reflect(ignore)] B(usize), C { // Uncomment to ignore only field `foo` of `C` // #[reflect(ignore)] foo: f32, bar: bool, }, } ``` #### Dynamic Enums Enums may be created/represented dynamically via the `DynamicEnum` struct. The main purpose of this struct is to allow enums to be deserialized into a partial state and to allow dynamic patching. In order to ensure conversion from a `DynamicEnum` to a concrete enum type goes smoothly, be sure to add `FromReflect` to your derive macro. ```rust let mut value = TestEnum::A; // Create from a concrete instance let dyn_enum = DynamicEnum::from(TestEnum::B(123)); value.apply(&dyn_enum); assert_eq!(TestEnum::B(123), value); // Create a purely dynamic instance let dyn_enum = DynamicEnum::new("TestEnum", "A", ()); value.apply(&dyn_enum); assert_eq!(TestEnum::A, value); ``` #### Variants An enum value is always represented as one of its variants— never the enum in its entirety. ```rust let value = TestEnum::A; assert_eq!("A", value.variant_name()); // Since we are using the `A` variant, we cannot also be the `B` variant assert_ne!("B", value.variant_name()); ``` All variant types are representable within the `Enum` trait: unit, struct, and tuple. You can get the current type like: ```rust match value.variant_type() { VariantType::Unit => println!("A unit variant!"), VariantType::Struct => println!("A struct variant!"), VariantType::Tuple => println!("A tuple variant!"), } ``` > Notice that they don't contain any values representing the fields. These are purely tags. If a variant has them, you can access the fields as well: ```rust let mut value = TestEnum::C { foo: 1.23, bar: false }; // Read/write specific fields *value.field_mut("bar").unwrap() = true; // Iterate over the entire collection of fields for field in value.iter_fields() { println!("{} = {:?}", field.name(), field.value()); } ``` #### Variant Swapping It might seem odd to group all variant types under a single trait (why allow `iter_fields` on a unit variant?), but the reason this was done ~~is to easily allow *variant swapping*.~~ As I was recently drafting up the **Design Decisions** section, I discovered that other solutions could have been made to work with variant swapping. So while there are reasons to keep the all-in-one approach, variant swapping is _not_ one of them. ```rust let mut value: Box<dyn Enum> = Box::new(TestEnum::A); value.set(Box::new(TestEnum::B(123))).unwrap(); ``` #### Serialization Enums can be serialized and deserialized via reflection without needing to implement `Serialize` or `Deserialize` themselves (which can save thousands of lines of generated code). Below are the ways an enum can be serialized. > Note, like the rest of reflection-based serialization, the order of the keys in these representations is important! ##### Unit ```json { "type": "my_crate::TestEnum", "enum": { "variant": "A" } } ``` ##### Tuple ```json { "type": "my_crate::TestEnum", "enum": { "variant": "B", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` <details> <summary>Effects on Option</summary> This ends up making `Option` look a little ugly: ```json { "type": "core::option::Option<usize>", "enum": { "variant": "Some", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` </details> ##### Struct ```json { "type": "my_crate::TestEnum", "enum": { "variant": "C", "struct": { "foo": { "type": "f32", "value": 1.23 }, "bar": { "type": "bool", "value": false } } } } ``` ## Design Decisions <details> <summary><strong>View Section</strong></summary> This section is here to provide some context for why certain decisions were made for this PR, alternatives that could have been used instead, and what could be improved upon in the future. ### Variant Representation One of the biggest decisions was to decide on how to represent variants. The current design uses a "all-in-one" design where unit, tuple, and struct variants are all simultaneously represented by the `Enum` trait. This is not the only way it could have been done, though. #### Alternatives ##### 1. Variant Traits One way of representing variants would be to define traits for each variant, implementing them whenever an enum featured at least one instance of them. This would allow us to define variants like: ```rust pub trait Enum: Reflect { fn variant(&self) -> Variant; } pub enum Variant<'a> { Unit, Tuple(&'a dyn TupleVariant), Struct(&'a dyn StructVariant), } pub trait TupleVariant { fn field_len(&self) -> usize; // ... } ``` And then do things like: ```rust fn get_tuple_len(foo: &dyn Enum) -> usize { match foo.variant() { Variant::Tuple(tuple) => tuple.field_len(), _ => panic!("not a tuple variant!") } } ``` The reason this PR does not go with this approach is because of the fact that variants are not separate types. In other words, we cannot implement traits on specific variants— these cover the *entire* enum. This means we offer an easy footgun: ```rust let foo: Option<i32> = None; let my_enum = Box::new(foo) as Box<dyn TupleVariant>; ``` Here, `my_enum` contains `foo`, which is a unit variant. However, since we need to implement `TupleVariant` for `Option` as a whole, it's possible to perform such a cast. This is obviously wrong, but could easily go unnoticed. So unfortunately, this makes it not a good candidate for representing variants. ##### 2. Variant Structs To get around the issue of traits necessarily needing to apply to both the enum and its variants, we could instead use structs that are created on a per-variant basis. This was also considered but was ultimately [[removed](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c)](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c) due to concerns about allocations. Each variant struct would probably look something like: ```rust pub trait Enum: Reflect { fn variant_mut(&self) -> VariantMut; } pub enum VariantMut<'a> { Unit, Tuple(TupleVariantMut), Struct(StructVariantMut), } struct StructVariantMut<'a> { fields: Vec<&'a mut dyn Reflect>, field_indices: HashMap<Cow<'static, str>, usize> } ``` This allows us to isolate struct variants into their own defined struct and define methods specifically for their use. It also prevents users from casting to it since it's not a trait. However, this is not an optimal solution. Both `field_indices` and `fields` will require an allocation (remember, a `Box<[T]>` still requires a `Vec<T>` in order to be constructed). This *might* be a problem if called frequently enough. ##### 3. Generated Structs The original design, implemented by @Davier, instead generates structs specific for each variant. So if we had a variant path like `Foo::Bar`, we'd generate a struct named `FooBarWrapper`. This would be newtyped around the original enum and forward tuple or struct methods to the enum with the chosen variant. Because it involved using the `Tuple` and `Struct` traits (which are also both bound on `Reflect`), this meant a bit more code had to be generated. For a single struct variant with one field, the generated code amounted to ~110LoC. However, each new field added to that variant only added ~6 more LoC. In order to work properly, the enum had to be transmuted to the generated struct: ```rust fn variant(&self) -> crate::EnumVariant<'_> { match self { Foo::Bar {value: i32} => { let wrapper_ref = unsafe { std::mem::transmute::<&Self, &FooBarWrapper>(self) }; crate::EnumVariant::Struct(wrapper_ref as &dyn crate::Struct) } } } ``` This works because `FooBarWrapper` is defined as `repr(transparent)`. Out of all the alternatives, this would probably be the one most likely to be used again in the future. The reasons for why this PR did not continue to use it was because: * To reduce generated code (which would hopefully speed up compile times) * To avoid cluttering the code with generated structs not visible to the user * To keep bevy_reflect simple and extensible (these generated structs act as proxies and might not play well with current or future systems) * To avoid additional unsafe blocks * My own misunderstanding of @Davier's code That last point is obviously on me. I misjudged the code to be too unsafe and unable to handle variant swapping (which it probably could) when I was rebasing it. Looking over it again when writing up this whole section, I see that it was actually a pretty clever way of handling variant representation. #### Benefits of All-in-One As stated before, the current implementation uses an all-in-one approach. All variants are capable of containing fields as far as `Enum` is concerned. This provides a few benefits that the alternatives do not (reduced indirection, safer code, etc.). The biggest benefit, though, is direct field access. Rather than forcing users to have to go through pattern matching, we grant direct access to the fields contained by the current variant. The reason we can do this is because all of the pattern matching happens internally. Getting the field at index `2` will automatically return `Some(...)` for the current variant if it has a field at that index or `None` if it doesn't (or can't). This could be useful for scenarios where the variant has already been verified or just set/swapped (or even where the type of variant doesn't matter): ```rust let dyn_enum: &mut dyn Enum = &mut Foo::Bar {value: 123}; // We know it's the `Bar` variant let field = dyn_enum.field("value").unwrap(); ``` Reflection is not a type-safe abstraction— almost every return value is wrapped in `Option<...>`. There are plenty of places to check and recheck that a value is what Reflect says it is. Forcing users to have to go through `match` each time they want to access a field might just be an extra step among dozens of other verification processes. Some might disagree, but ultimately, my view is that the benefit here is an improvement to the ergonomics and usability of reflected enums. </details> --- ## Changelog ### Added * Added `Enum` trait * Added `Enum` impl to `Reflect` derive macro * Added `DynamicEnum` struct * Added `DynamicVariant` * Added `EnumInfo` * Added `VariantInfo` * Added `StructVariantInfo` * Added `TupleVariantInfo` * Added `UnitVariantInfo` * Added serializtion/deserialization support for enums * Added `EnumSerializer` * Added `VariantType` * Added `VariantFieldIter` * Added `VariantField` * Added `enum_partial_eq(...)` * Added `enum_hash(...)` ### Changed * `Option<T>` now implements `Enum` * `bevy_window` now depends on `bevy_reflect` * Implemented `Reflect` and `FromReflect` for `WindowId` * Derive `FromReflect` on `PerspectiveProjection` * Derive `FromReflect` on `OrthographicProjection` * Derive `FromReflect` on `WindowOrigin` * Derive `FromReflect` on `ScalingMode` * Derive `FromReflect` on `DepthCalculation` ## Migration Guide * Enums no longer need to be treated as values and usages of `#[reflect_value(...)]` can be removed or replaced by `#[reflect(...)]` * Enums (including `Option<T>`) now take a different format when serializing. The format is described above, but this may cause issues for existing scenes that make use of enums. --- Also shout out to @nicopap for helping clean up some of the code here! It's a big feature so help like this is really appreciated! Co-authored-by: Gino Valente <gino.valente.code@gmail.com>
2022-08-02 22:14:41 +00:00
/// struct ThingThatImReflecting<T1, T2, T3> {/* ... */}
/// ```
pub(crate) struct ReflectMeta<'a> {
/// The registered traits for this type.
bevy_reflect_derive: Clean up attribute logic (#11777) # Objective The code in `bevy_reflect_derive` could use some cleanup. ## Solution Took some of the changes in #11659 to create a dedicated PR for cleaning up the field and container attribute logic. #### Updated Naming I renamed `ReflectTraits` and `ReflectFieldAttr` to `ContainerAttributes` and `FieldAttributes`, respectively. I think these are clearer. #### Updated Parsing ##### Readability The parsing logic wasn't too bad before, but it was getting difficult to read. There was some duplicated logic between `Meta::List` and `Meta::Path` attributes. Additionally, all the logic was kept inside a large method. To simply things, I replaced the nested meta parsing with `ParseStream` parsing. In my opinion, this is easier to follow since it breaks up the large match statement into a small set of single-line if statements, where each if-block contains a single call to the appropriate attribute parsing method. ##### Flexibility On top of the added simplicity, this also makes our attribute parsing much more flexible. It allows us to more elegantly handle custom where clauses (i.e. `#[reflect(where T: Foo)]`) and it opens the door for more non-standard attribute syntax (e.g. #11659). ##### Errors This also allows us to automatically provide certain errors when parsing. For example, since we can use `stream.lookahead1()`, we get errors like the following for free: ``` error: expected one of: `ignore`, `skip_serializing`, `default` --> crates/bevy_reflect/src/lib.rs:1988:23 | 1988 | #[reflect(foo)] | ^^^ ``` --- ## Changelog > [!note] > All changes are internal to `bevy_reflect_derive` and should not affect the public API[^1]. - Renamed `ReflectTraits` to `ContainerAttributes` - Renamed `ReflectMeta::traits` to `ReflectMeta::attrs` - Renamed `ReflectFieldAttr` to `FieldAttributes` - Updated parsing logic for field/container attribute parsing - Now uses a `ParseStream` directly instead of nested meta parsing - General code cleanup of the field/container attribute modules for `bevy_reflect_derive` [^1]: Does not include errors, which may look slightly different. --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
2024-02-12 15:16:27 +00:00
attrs: ContainerAttributes,
reflect: `TypePath` part 2 (#8768) # Objective - Followup to #7184. - ~Deprecate `TypeUuid` and remove its internal references.~ No longer part of this PR. - Use `TypePath` for the type registry, and (de)serialisation instead of `std::any::type_name`. - Allow accessing type path information behind proxies. ## Solution - Introduce methods on `TypeInfo` and friends for dynamically querying type path. These methods supersede the old `type_name` methods. - Remove `Reflect::type_name` in favor of `DynamicTypePath::type_path` and `TypeInfo::type_path_table`. - Switch all uses of `std::any::type_name` in reflection, non-debugging contexts to use `TypePath`. --- ## Changelog - Added `TypePathTable` for dynamically accessing methods on `TypePath` through `TypeInfo` and the type registry. - Removed `type_name` from all `TypeInfo`-like structs. - Added `type_path` and `type_path_table` methods to all `TypeInfo`-like structs. - Removed `Reflect::type_name` in favor of `DynamicTypePath::reflect_type_path` and `TypeInfo::type_path`. - Changed the signature of all `DynamicTypePath` methods to return strings with a static lifetime. ## Migration Guide - Rely on `TypePath` instead of `std::any::type_name` for all stability guarantees and for use in all reflection contexts, this is used through with one of the following APIs: - `TypePath::type_path` if you have a concrete type and not a value. - `DynamicTypePath::reflect_type_path` if you have an `dyn Reflect` value without a concrete type. - `TypeInfo::type_path` for use through the registry or if you want to work with the represented type of a `DynamicFoo`. - Remove `type_name` from manual `Reflect` implementations. - Use `type_path` and `type_path_table` in place of `type_name` on `TypeInfo`-like structs. - Use `get_with_type_path(_mut)` over `get_with_type_name(_mut)`. ## Note to reviewers I think if anything we were a little overzealous in merging #7184 and we should take that extra care here. In my mind, this is the "point of no return" for `TypePath` and while I think we all agree on the design, we should carefully consider if the finer details and current implementations are actually how we want them moving forward. For example [this incorrect `TypePath` implementation for `String`](https://github.com/soqb/bevy/blob/3fea3c6c0b5719dfbd3d4230f5282ec80d82556a/crates/bevy_reflect/src/impls/std.rs#L90) (note that `String` is in the default Rust prelude) snuck in completely under the radar.
2023-10-09 19:33:03 +00:00
/// The path to this type.
reflect: stable type path v2 (#7184) # Objective - Introduce a stable alternative to [`std::any::type_name`](https://doc.rust-lang.org/std/any/fn.type_name.html). - Rewrite of #5805 with heavy inspiration in design. - On the path to #5830. - Part of solving #3327. ## Solution - Add a `TypePath` trait for static stable type path/name information. - Add a `TypePath` derive macro. - Add a `impl_type_path` macro for implementing internal and foreign types in `bevy_reflect`. --- ## Changelog - Added `TypePath` trait. - Added `DynamicTypePath` trait and `get_type_path` method to `Reflect`. - Added a `TypePath` derive macro. - Added a `bevy_reflect::impl_type_path` for implementing `TypePath` on internal and foreign types in `bevy_reflect`. - Changed `bevy_reflect::utility::(Non)GenericTypeInfoCell` to `(Non)GenericTypedCell<T>` which allows us to be generic over both `TypeInfo` and `TypePath`. - `TypePath` is now a supertrait of `Asset`, `Material` and `Material2d`. - `impl_reflect_struct` needs a `#[type_path = "..."]` attribute to be specified. - `impl_reflect_value` needs to either specify path starting with a double colon (`::core::option::Option`) or an `in my_crate::foo` declaration. - Added `bevy_reflect_derive::ReflectTypePath`. - Most uses of `Ident` in `bevy_reflect_derive` changed to use `ReflectTypePath`. ## Migration Guide - Implementors of `Asset`, `Material` and `Material2d` now also need to derive `TypePath`. - Manual implementors of `Reflect` will need to implement the new `get_type_path` method. ## Open Questions - [x] ~This PR currently does not migrate any usages of `std::any::type_name` to use `bevy_reflect::TypePath` to ease the review process. Should it?~ Migration will be left to a follow-up PR. - [ ] This PR adds a lot of `#[derive(TypePath)]` and `T: TypePath` to satisfy new bounds, mostly when deriving `TypeUuid`. Should we make `TypePath` a supertrait of `TypeUuid`? [Should we remove `TypeUuid` in favour of `TypePath`?](https://github.com/bevyengine/bevy/pull/5805/files/2afbd855327c4b68e0a6b6f03118f289988441a4#r961067892)
2023-06-05 20:31:20 +00:00
type_path: ReflectTypePath<'a>,
bevy_reflect: Reflect remote types (#6042) # Objective The goal with this PR is to allow the use of types that don't implement `Reflect` within the reflection API. Rust's [orphan rule](https://doc.rust-lang.org/book/ch10-02-traits.html#implementing-a-trait-on-a-type) prevents implementing a trait on an external type when neither type nor trait are owned by the implementor. This means that if a crate, `cool_rust_lib`, defines a type, `Foo`, then a user cannot use it with reflection. What this means is that we have to ignore it most of the time: ```rust #[derive(Reflect)] struct SomeStruct { #[reflect(ignore)] data: cool_rust_lib::Foo } ``` Obviously, it's impossible to implement `Reflect` on `Foo`. But does it *have* to be? Most of reflection doesn't deal with concrete types— it's almost all using `dyn Reflect`. And being very metadata-driven, it should theoretically be possible. I mean, [`serde`](https://serde.rs/remote-derive.html) does it. ## Solution > Special thanks to @danielhenrymantilla for their help reviewing this PR and offering wisdom wrt safety. Taking a page out of `serde`'s book, this PR adds the ability to easily use "remote types" with reflection. In this context, a "remote type" is the external type for which we have no ability to implement `Reflect`. This adds the `#[reflect_remote(...)]` attribute macro, which is used to generate "remote type wrappers". All you have to do is define the wrapper exactly the same as the remote type's definition: ```rust // Pretend this is our external crate mod cool_rust_lib { #[derive(Default)] struct Foo { pub value: String } } #[reflect_remote(cool_rust_lib::Foo)] struct FooWrapper { pub value: String } ``` > **Note:** All fields in the external type *must* be public. This could be addressed with a separate getter/setter attribute either in this PR or in another one. The macro takes this user-defined item and transforms it into a newtype wrapper around the external type, marking it as `#[repr(transparent)]`. The fields/variants defined by the user are simply used to build out the reflection impls. Additionally, it generates an implementation of the new trait, `ReflectRemote`, which helps prevent accidental misuses of this API. Therefore, the output generated by the macro would look something like: ```rust #[repr(transparent)] struct FooWrapper(pub cool_rust_lib::Foo); impl ReflectRemote for FooWrapper { type Remote = cool_rust_lib::Foo; // transmutation methods... } // reflection impls... // these will acknowledge and make use of the `value` field ``` Internally, the reflection API will pass around the `FooWrapper` and [transmute](https://doc.rust-lang.org/std/mem/fn.transmute.html) it where necessary. All we have to do is then tell `Reflect` to do that. So rather than ignoring the field, we tell `Reflect` to use our wrapper using the `#[reflect(remote = ...)]` field attribute: ```rust #[derive(Reflect)] struct SomeStruct { #[reflect(remote = FooWrapper)] data: cool_rust_lib::Foo } ``` #### Other Macros & Type Data Because this macro consumes the defined item and generates a new one, we can't just put our macros anywhere. All macros that should be passed to the generated struct need to come *below* this macro. For example, to derive `Default` and register its associated type data: ```rust // ✅ GOOD #[reflect_remote(cool_rust_lib::Foo)] #[derive(Default)] #[reflect(Default)] struct FooWrapper { pub value: String } // ❌ BAD #[derive(Default)] #[reflect_remote(cool_rust_lib::Foo)] #[reflect(Default)] struct FooWrapper { pub value: String } ``` #### Generics Generics are forwarded to the generated struct as well. They should also be defined in the same order: ```rust #[reflect_remote(RemoteGeneric<'a, T1, T2>)] struct GenericWrapper<'a, T1, T2> { pub foo: &'a T1, pub bar: &'a T2, } ``` > Naming does *not* need to match the original definition's. Only order matters here. > Also note that the code above is just a demonstration and doesn't actually compile since we'd need to enforce certain bounds (e.g. `T1: Reflect`, `'a: 'static`, etc.) #### Nesting And, yes, you can nest remote types: ```rust #[reflect_remote(RemoteOuter)] struct OuterWrapper { #[reflect(remote = InnerWrapper)] pub inner: RemoteInner } #[reflect_remote(RemoteInner)] struct InnerWrapper(usize); ``` #### Assertions This macro will also generate some compile-time assertions to ensure that the correct types are used. It's important we catch this early so users don't have to wait for something to panic. And it also helps keep our `unsafe` a little safer. For example, a wrapper definition that does not match its corresponding remote type will result in an error: ```rust mod external_crate { pub struct TheirStruct(pub u32); } #[reflect_remote(external_crate::TheirStruct)] struct MyStruct(pub String); // ERROR: expected type `u32` but found `String` ``` <details> <summary>Generated Assertion</summary> ```rust const _: () = { #[allow(non_snake_case)] #[allow(unused_variables)] #[allow(unused_assignments)] #[allow(unreachable_patterns)] #[allow(clippy::multiple_bound_locations)] fn assert_wrapper_definition_matches_remote_type( mut __remote__: external_crate::TheirStruct, ) { __remote__.0 = (|| -> ::core::option::Option<String> { None })().unwrap(); } }; ``` </details> Additionally, using the incorrect type in a `#[reflect(remote = ...)]` attribute should result in an error: ```rust mod external_crate { pub struct TheirFoo(pub u32); pub struct TheirBar(pub i32); } #[reflect_remote(external_crate::TheirFoo)] struct MyFoo(pub u32); #[reflect_remote(external_crate::TheirBar)] struct MyBar(pub i32); #[derive(Reflect)] struct MyStruct { #[reflect(remote = MyBar)] // ERROR: expected type `TheirFoo` but found struct `TheirBar` foo: external_crate::TheirFoo } ``` <details> <summary>Generated Assertion</summary> ```rust const _: () = { struct RemoteFieldAssertions; impl RemoteFieldAssertions { #[allow(non_snake_case)] #[allow(clippy::multiple_bound_locations)] fn assert__foo__is_valid_remote() { let _: <MyBar as bevy_reflect::ReflectRemote>::Remote = (|| -> ::core::option::Option<external_crate::TheirFoo> { None })().unwrap(); } } }; ``` </details> ### Discussion There are a couple points that I think still need discussion or validation. - [x] 1. `Any` shenanigans ~~If we wanted to downcast our remote type from a `dyn Reflect`, we'd have to first downcast to the wrapper then extract the inner type. This PR has a [commit](b840db9f74cb6d357f951cb11b150d46bac89ee2) that addresses this by making all the `Reflect::*any` methods return the inner type rather than the wrapper type. This allows us to downcast directly to our remote type.~~ ~~However, I'm not sure if this is something we want to do. For unknowing users, it could be confusing and seemingly inconsistent. Is it worth keeping? Or should this behavior be removed?~~ I think this should be fine. The remote wrapper is an implementation detail and users should not need to downcast to the wrapper type. Feel free to let me know if there are other opinions on this though! - [x] 2. Implementing `Deref/DerefMut` and `From` ~~We don't currently do this, but should we implement other traits on the generated transparent struct? We could implement `Deref`/`DerefMut` to easily access the inner type. And we could implement `From` for easier conversion between the two types (e.g. `T: Into<Foo>`).~~ As mentioned in the comments, we probably don't need to do this. Again, the remote wrapper is an implementation detail, and should generally not be used directly. - [x] 3. ~~Should we define a getter/setter field attribute in this PR as well or leave it for a future one?~~ I think this should be saved for a future PR - [ ] 4. Any foreseeable issues with this implementation? #### Alternatives One alternative to defining our own `ReflectRemote` would be to use [bytemuck's `TransparentWrapper`](https://docs.rs/bytemuck/1.13.1/bytemuck/trait.TransparentWrapper.html) (as suggested by @danielhenrymantilla). This is definitely a viable option, as `ReflectRemote` is pretty much the same thing as `TransparentWrapper`. However, the cost would be bringing in a new crate— though, it is already in use in a few other sub-crates like bevy_render. I think we're okay just defining `ReflectRemote` ourselves, but we can go the bytemuck route if we'd prefer offloading that work to another crate. --- ## Changelog * Added the `#[reflect_remote(...)]` attribute macro to allow `Reflect` to be used on remote types * Added `ReflectRemote` trait for ensuring proper remote wrapper usage
2024-08-12 19:12:53 +00:00
/// The optional remote type to use instead of the actual type.
remote_ty: Option<RemoteType<'a>>,
bevy_reflect: Reflect enums (#4761) # Objective > This is a revival of #1347. Credit for the original PR should go to @Davier. Currently, enums are treated as `ReflectRef::Value` types by `bevy_reflect`. Obviously, there needs to be better a better representation for enums using the reflection API. ## Solution Based on prior work from @Davier, an `Enum` trait has been added as well as the ability to automatically implement it via the `Reflect` derive macro. This allows enums to be expressed dynamically: ```rust #[derive(Reflect)] enum Foo { A, B(usize), C { value: f32 }, } let mut foo = Foo::B(123); assert_eq!("B", foo.variant_name()); assert_eq!(1, foo.field_len()); let new_value = DynamicEnum::from(Foo::C { value: 1.23 }); foo.apply(&new_value); assert_eq!(Foo::C{value: 1.23}, foo); ``` ### Features #### Derive Macro Use the `#[derive(Reflect)]` macro to automatically implement the `Enum` trait for enum definitions. Optionally, you can use `#[reflect(ignore)]` with both variants and variant fields, just like you can with structs. These ignored items will not be considered as part of the reflection and cannot be accessed via reflection. ```rust #[derive(Reflect)] enum TestEnum { A, // Uncomment to ignore all of `B` // #[reflect(ignore)] B(usize), C { // Uncomment to ignore only field `foo` of `C` // #[reflect(ignore)] foo: f32, bar: bool, }, } ``` #### Dynamic Enums Enums may be created/represented dynamically via the `DynamicEnum` struct. The main purpose of this struct is to allow enums to be deserialized into a partial state and to allow dynamic patching. In order to ensure conversion from a `DynamicEnum` to a concrete enum type goes smoothly, be sure to add `FromReflect` to your derive macro. ```rust let mut value = TestEnum::A; // Create from a concrete instance let dyn_enum = DynamicEnum::from(TestEnum::B(123)); value.apply(&dyn_enum); assert_eq!(TestEnum::B(123), value); // Create a purely dynamic instance let dyn_enum = DynamicEnum::new("TestEnum", "A", ()); value.apply(&dyn_enum); assert_eq!(TestEnum::A, value); ``` #### Variants An enum value is always represented as one of its variants— never the enum in its entirety. ```rust let value = TestEnum::A; assert_eq!("A", value.variant_name()); // Since we are using the `A` variant, we cannot also be the `B` variant assert_ne!("B", value.variant_name()); ``` All variant types are representable within the `Enum` trait: unit, struct, and tuple. You can get the current type like: ```rust match value.variant_type() { VariantType::Unit => println!("A unit variant!"), VariantType::Struct => println!("A struct variant!"), VariantType::Tuple => println!("A tuple variant!"), } ``` > Notice that they don't contain any values representing the fields. These are purely tags. If a variant has them, you can access the fields as well: ```rust let mut value = TestEnum::C { foo: 1.23, bar: false }; // Read/write specific fields *value.field_mut("bar").unwrap() = true; // Iterate over the entire collection of fields for field in value.iter_fields() { println!("{} = {:?}", field.name(), field.value()); } ``` #### Variant Swapping It might seem odd to group all variant types under a single trait (why allow `iter_fields` on a unit variant?), but the reason this was done ~~is to easily allow *variant swapping*.~~ As I was recently drafting up the **Design Decisions** section, I discovered that other solutions could have been made to work with variant swapping. So while there are reasons to keep the all-in-one approach, variant swapping is _not_ one of them. ```rust let mut value: Box<dyn Enum> = Box::new(TestEnum::A); value.set(Box::new(TestEnum::B(123))).unwrap(); ``` #### Serialization Enums can be serialized and deserialized via reflection without needing to implement `Serialize` or `Deserialize` themselves (which can save thousands of lines of generated code). Below are the ways an enum can be serialized. > Note, like the rest of reflection-based serialization, the order of the keys in these representations is important! ##### Unit ```json { "type": "my_crate::TestEnum", "enum": { "variant": "A" } } ``` ##### Tuple ```json { "type": "my_crate::TestEnum", "enum": { "variant": "B", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` <details> <summary>Effects on Option</summary> This ends up making `Option` look a little ugly: ```json { "type": "core::option::Option<usize>", "enum": { "variant": "Some", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` </details> ##### Struct ```json { "type": "my_crate::TestEnum", "enum": { "variant": "C", "struct": { "foo": { "type": "f32", "value": 1.23 }, "bar": { "type": "bool", "value": false } } } } ``` ## Design Decisions <details> <summary><strong>View Section</strong></summary> This section is here to provide some context for why certain decisions were made for this PR, alternatives that could have been used instead, and what could be improved upon in the future. ### Variant Representation One of the biggest decisions was to decide on how to represent variants. The current design uses a "all-in-one" design where unit, tuple, and struct variants are all simultaneously represented by the `Enum` trait. This is not the only way it could have been done, though. #### Alternatives ##### 1. Variant Traits One way of representing variants would be to define traits for each variant, implementing them whenever an enum featured at least one instance of them. This would allow us to define variants like: ```rust pub trait Enum: Reflect { fn variant(&self) -> Variant; } pub enum Variant<'a> { Unit, Tuple(&'a dyn TupleVariant), Struct(&'a dyn StructVariant), } pub trait TupleVariant { fn field_len(&self) -> usize; // ... } ``` And then do things like: ```rust fn get_tuple_len(foo: &dyn Enum) -> usize { match foo.variant() { Variant::Tuple(tuple) => tuple.field_len(), _ => panic!("not a tuple variant!") } } ``` The reason this PR does not go with this approach is because of the fact that variants are not separate types. In other words, we cannot implement traits on specific variants— these cover the *entire* enum. This means we offer an easy footgun: ```rust let foo: Option<i32> = None; let my_enum = Box::new(foo) as Box<dyn TupleVariant>; ``` Here, `my_enum` contains `foo`, which is a unit variant. However, since we need to implement `TupleVariant` for `Option` as a whole, it's possible to perform such a cast. This is obviously wrong, but could easily go unnoticed. So unfortunately, this makes it not a good candidate for representing variants. ##### 2. Variant Structs To get around the issue of traits necessarily needing to apply to both the enum and its variants, we could instead use structs that are created on a per-variant basis. This was also considered but was ultimately [[removed](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c)](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c) due to concerns about allocations. Each variant struct would probably look something like: ```rust pub trait Enum: Reflect { fn variant_mut(&self) -> VariantMut; } pub enum VariantMut<'a> { Unit, Tuple(TupleVariantMut), Struct(StructVariantMut), } struct StructVariantMut<'a> { fields: Vec<&'a mut dyn Reflect>, field_indices: HashMap<Cow<'static, str>, usize> } ``` This allows us to isolate struct variants into their own defined struct and define methods specifically for their use. It also prevents users from casting to it since it's not a trait. However, this is not an optimal solution. Both `field_indices` and `fields` will require an allocation (remember, a `Box<[T]>` still requires a `Vec<T>` in order to be constructed). This *might* be a problem if called frequently enough. ##### 3. Generated Structs The original design, implemented by @Davier, instead generates structs specific for each variant. So if we had a variant path like `Foo::Bar`, we'd generate a struct named `FooBarWrapper`. This would be newtyped around the original enum and forward tuple or struct methods to the enum with the chosen variant. Because it involved using the `Tuple` and `Struct` traits (which are also both bound on `Reflect`), this meant a bit more code had to be generated. For a single struct variant with one field, the generated code amounted to ~110LoC. However, each new field added to that variant only added ~6 more LoC. In order to work properly, the enum had to be transmuted to the generated struct: ```rust fn variant(&self) -> crate::EnumVariant<'_> { match self { Foo::Bar {value: i32} => { let wrapper_ref = unsafe { std::mem::transmute::<&Self, &FooBarWrapper>(self) }; crate::EnumVariant::Struct(wrapper_ref as &dyn crate::Struct) } } } ``` This works because `FooBarWrapper` is defined as `repr(transparent)`. Out of all the alternatives, this would probably be the one most likely to be used again in the future. The reasons for why this PR did not continue to use it was because: * To reduce generated code (which would hopefully speed up compile times) * To avoid cluttering the code with generated structs not visible to the user * To keep bevy_reflect simple and extensible (these generated structs act as proxies and might not play well with current or future systems) * To avoid additional unsafe blocks * My own misunderstanding of @Davier's code That last point is obviously on me. I misjudged the code to be too unsafe and unable to handle variant swapping (which it probably could) when I was rebasing it. Looking over it again when writing up this whole section, I see that it was actually a pretty clever way of handling variant representation. #### Benefits of All-in-One As stated before, the current implementation uses an all-in-one approach. All variants are capable of containing fields as far as `Enum` is concerned. This provides a few benefits that the alternatives do not (reduced indirection, safer code, etc.). The biggest benefit, though, is direct field access. Rather than forcing users to have to go through pattern matching, we grant direct access to the fields contained by the current variant. The reason we can do this is because all of the pattern matching happens internally. Getting the field at index `2` will automatically return `Some(...)` for the current variant if it has a field at that index or `None` if it doesn't (or can't). This could be useful for scenarios where the variant has already been verified or just set/swapped (or even where the type of variant doesn't matter): ```rust let dyn_enum: &mut dyn Enum = &mut Foo::Bar {value: 123}; // We know it's the `Bar` variant let field = dyn_enum.field("value").unwrap(); ``` Reflection is not a type-safe abstraction— almost every return value is wrapped in `Option<...>`. There are plenty of places to check and recheck that a value is what Reflect says it is. Forcing users to have to go through `match` each time they want to access a field might just be an extra step among dozens of other verification processes. Some might disagree, but ultimately, my view is that the benefit here is an improvement to the ergonomics and usability of reflected enums. </details> --- ## Changelog ### Added * Added `Enum` trait * Added `Enum` impl to `Reflect` derive macro * Added `DynamicEnum` struct * Added `DynamicVariant` * Added `EnumInfo` * Added `VariantInfo` * Added `StructVariantInfo` * Added `TupleVariantInfo` * Added `UnitVariantInfo` * Added serializtion/deserialization support for enums * Added `EnumSerializer` * Added `VariantType` * Added `VariantFieldIter` * Added `VariantField` * Added `enum_partial_eq(...)` * Added `enum_hash(...)` ### Changed * `Option<T>` now implements `Enum` * `bevy_window` now depends on `bevy_reflect` * Implemented `Reflect` and `FromReflect` for `WindowId` * Derive `FromReflect` on `PerspectiveProjection` * Derive `FromReflect` on `OrthographicProjection` * Derive `FromReflect` on `WindowOrigin` * Derive `FromReflect` on `ScalingMode` * Derive `FromReflect` on `DepthCalculation` ## Migration Guide * Enums no longer need to be treated as values and usages of `#[reflect_value(...)]` can be removed or replaced by `#[reflect(...)]` * Enums (including `Option<T>`) now take a different format when serializing. The format is described above, but this may cause issues for existing scenes that make use of enums. --- Also shout out to @nicopap for helping clean up some of the code here! It's a big feature so help like this is really appreciated! Co-authored-by: Gino Valente <gino.valente.code@gmail.com>
2022-08-02 22:14:41 +00:00
/// A cached instance of the path to the `bevy_reflect` crate.
bevy_reflect_path: Path,
/// The documentation for this type, if any
#[cfg(feature = "documentation")]
docs: crate::documentation::Documentation,
bevy_reflect: Reflect enums (#4761) # Objective > This is a revival of #1347. Credit for the original PR should go to @Davier. Currently, enums are treated as `ReflectRef::Value` types by `bevy_reflect`. Obviously, there needs to be better a better representation for enums using the reflection API. ## Solution Based on prior work from @Davier, an `Enum` trait has been added as well as the ability to automatically implement it via the `Reflect` derive macro. This allows enums to be expressed dynamically: ```rust #[derive(Reflect)] enum Foo { A, B(usize), C { value: f32 }, } let mut foo = Foo::B(123); assert_eq!("B", foo.variant_name()); assert_eq!(1, foo.field_len()); let new_value = DynamicEnum::from(Foo::C { value: 1.23 }); foo.apply(&new_value); assert_eq!(Foo::C{value: 1.23}, foo); ``` ### Features #### Derive Macro Use the `#[derive(Reflect)]` macro to automatically implement the `Enum` trait for enum definitions. Optionally, you can use `#[reflect(ignore)]` with both variants and variant fields, just like you can with structs. These ignored items will not be considered as part of the reflection and cannot be accessed via reflection. ```rust #[derive(Reflect)] enum TestEnum { A, // Uncomment to ignore all of `B` // #[reflect(ignore)] B(usize), C { // Uncomment to ignore only field `foo` of `C` // #[reflect(ignore)] foo: f32, bar: bool, }, } ``` #### Dynamic Enums Enums may be created/represented dynamically via the `DynamicEnum` struct. The main purpose of this struct is to allow enums to be deserialized into a partial state and to allow dynamic patching. In order to ensure conversion from a `DynamicEnum` to a concrete enum type goes smoothly, be sure to add `FromReflect` to your derive macro. ```rust let mut value = TestEnum::A; // Create from a concrete instance let dyn_enum = DynamicEnum::from(TestEnum::B(123)); value.apply(&dyn_enum); assert_eq!(TestEnum::B(123), value); // Create a purely dynamic instance let dyn_enum = DynamicEnum::new("TestEnum", "A", ()); value.apply(&dyn_enum); assert_eq!(TestEnum::A, value); ``` #### Variants An enum value is always represented as one of its variants— never the enum in its entirety. ```rust let value = TestEnum::A; assert_eq!("A", value.variant_name()); // Since we are using the `A` variant, we cannot also be the `B` variant assert_ne!("B", value.variant_name()); ``` All variant types are representable within the `Enum` trait: unit, struct, and tuple. You can get the current type like: ```rust match value.variant_type() { VariantType::Unit => println!("A unit variant!"), VariantType::Struct => println!("A struct variant!"), VariantType::Tuple => println!("A tuple variant!"), } ``` > Notice that they don't contain any values representing the fields. These are purely tags. If a variant has them, you can access the fields as well: ```rust let mut value = TestEnum::C { foo: 1.23, bar: false }; // Read/write specific fields *value.field_mut("bar").unwrap() = true; // Iterate over the entire collection of fields for field in value.iter_fields() { println!("{} = {:?}", field.name(), field.value()); } ``` #### Variant Swapping It might seem odd to group all variant types under a single trait (why allow `iter_fields` on a unit variant?), but the reason this was done ~~is to easily allow *variant swapping*.~~ As I was recently drafting up the **Design Decisions** section, I discovered that other solutions could have been made to work with variant swapping. So while there are reasons to keep the all-in-one approach, variant swapping is _not_ one of them. ```rust let mut value: Box<dyn Enum> = Box::new(TestEnum::A); value.set(Box::new(TestEnum::B(123))).unwrap(); ``` #### Serialization Enums can be serialized and deserialized via reflection without needing to implement `Serialize` or `Deserialize` themselves (which can save thousands of lines of generated code). Below are the ways an enum can be serialized. > Note, like the rest of reflection-based serialization, the order of the keys in these representations is important! ##### Unit ```json { "type": "my_crate::TestEnum", "enum": { "variant": "A" } } ``` ##### Tuple ```json { "type": "my_crate::TestEnum", "enum": { "variant": "B", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` <details> <summary>Effects on Option</summary> This ends up making `Option` look a little ugly: ```json { "type": "core::option::Option<usize>", "enum": { "variant": "Some", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` </details> ##### Struct ```json { "type": "my_crate::TestEnum", "enum": { "variant": "C", "struct": { "foo": { "type": "f32", "value": 1.23 }, "bar": { "type": "bool", "value": false } } } } ``` ## Design Decisions <details> <summary><strong>View Section</strong></summary> This section is here to provide some context for why certain decisions were made for this PR, alternatives that could have been used instead, and what could be improved upon in the future. ### Variant Representation One of the biggest decisions was to decide on how to represent variants. The current design uses a "all-in-one" design where unit, tuple, and struct variants are all simultaneously represented by the `Enum` trait. This is not the only way it could have been done, though. #### Alternatives ##### 1. Variant Traits One way of representing variants would be to define traits for each variant, implementing them whenever an enum featured at least one instance of them. This would allow us to define variants like: ```rust pub trait Enum: Reflect { fn variant(&self) -> Variant; } pub enum Variant<'a> { Unit, Tuple(&'a dyn TupleVariant), Struct(&'a dyn StructVariant), } pub trait TupleVariant { fn field_len(&self) -> usize; // ... } ``` And then do things like: ```rust fn get_tuple_len(foo: &dyn Enum) -> usize { match foo.variant() { Variant::Tuple(tuple) => tuple.field_len(), _ => panic!("not a tuple variant!") } } ``` The reason this PR does not go with this approach is because of the fact that variants are not separate types. In other words, we cannot implement traits on specific variants— these cover the *entire* enum. This means we offer an easy footgun: ```rust let foo: Option<i32> = None; let my_enum = Box::new(foo) as Box<dyn TupleVariant>; ``` Here, `my_enum` contains `foo`, which is a unit variant. However, since we need to implement `TupleVariant` for `Option` as a whole, it's possible to perform such a cast. This is obviously wrong, but could easily go unnoticed. So unfortunately, this makes it not a good candidate for representing variants. ##### 2. Variant Structs To get around the issue of traits necessarily needing to apply to both the enum and its variants, we could instead use structs that are created on a per-variant basis. This was also considered but was ultimately [[removed](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c)](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c) due to concerns about allocations. Each variant struct would probably look something like: ```rust pub trait Enum: Reflect { fn variant_mut(&self) -> VariantMut; } pub enum VariantMut<'a> { Unit, Tuple(TupleVariantMut), Struct(StructVariantMut), } struct StructVariantMut<'a> { fields: Vec<&'a mut dyn Reflect>, field_indices: HashMap<Cow<'static, str>, usize> } ``` This allows us to isolate struct variants into their own defined struct and define methods specifically for their use. It also prevents users from casting to it since it's not a trait. However, this is not an optimal solution. Both `field_indices` and `fields` will require an allocation (remember, a `Box<[T]>` still requires a `Vec<T>` in order to be constructed). This *might* be a problem if called frequently enough. ##### 3. Generated Structs The original design, implemented by @Davier, instead generates structs specific for each variant. So if we had a variant path like `Foo::Bar`, we'd generate a struct named `FooBarWrapper`. This would be newtyped around the original enum and forward tuple or struct methods to the enum with the chosen variant. Because it involved using the `Tuple` and `Struct` traits (which are also both bound on `Reflect`), this meant a bit more code had to be generated. For a single struct variant with one field, the generated code amounted to ~110LoC. However, each new field added to that variant only added ~6 more LoC. In order to work properly, the enum had to be transmuted to the generated struct: ```rust fn variant(&self) -> crate::EnumVariant<'_> { match self { Foo::Bar {value: i32} => { let wrapper_ref = unsafe { std::mem::transmute::<&Self, &FooBarWrapper>(self) }; crate::EnumVariant::Struct(wrapper_ref as &dyn crate::Struct) } } } ``` This works because `FooBarWrapper` is defined as `repr(transparent)`. Out of all the alternatives, this would probably be the one most likely to be used again in the future. The reasons for why this PR did not continue to use it was because: * To reduce generated code (which would hopefully speed up compile times) * To avoid cluttering the code with generated structs not visible to the user * To keep bevy_reflect simple and extensible (these generated structs act as proxies and might not play well with current or future systems) * To avoid additional unsafe blocks * My own misunderstanding of @Davier's code That last point is obviously on me. I misjudged the code to be too unsafe and unable to handle variant swapping (which it probably could) when I was rebasing it. Looking over it again when writing up this whole section, I see that it was actually a pretty clever way of handling variant representation. #### Benefits of All-in-One As stated before, the current implementation uses an all-in-one approach. All variants are capable of containing fields as far as `Enum` is concerned. This provides a few benefits that the alternatives do not (reduced indirection, safer code, etc.). The biggest benefit, though, is direct field access. Rather than forcing users to have to go through pattern matching, we grant direct access to the fields contained by the current variant. The reason we can do this is because all of the pattern matching happens internally. Getting the field at index `2` will automatically return `Some(...)` for the current variant if it has a field at that index or `None` if it doesn't (or can't). This could be useful for scenarios where the variant has already been verified or just set/swapped (or even where the type of variant doesn't matter): ```rust let dyn_enum: &mut dyn Enum = &mut Foo::Bar {value: 123}; // We know it's the `Bar` variant let field = dyn_enum.field("value").unwrap(); ``` Reflection is not a type-safe abstraction— almost every return value is wrapped in `Option<...>`. There are plenty of places to check and recheck that a value is what Reflect says it is. Forcing users to have to go through `match` each time they want to access a field might just be an extra step among dozens of other verification processes. Some might disagree, but ultimately, my view is that the benefit here is an improvement to the ergonomics and usability of reflected enums. </details> --- ## Changelog ### Added * Added `Enum` trait * Added `Enum` impl to `Reflect` derive macro * Added `DynamicEnum` struct * Added `DynamicVariant` * Added `EnumInfo` * Added `VariantInfo` * Added `StructVariantInfo` * Added `TupleVariantInfo` * Added `UnitVariantInfo` * Added serializtion/deserialization support for enums * Added `EnumSerializer` * Added `VariantType` * Added `VariantFieldIter` * Added `VariantField` * Added `enum_partial_eq(...)` * Added `enum_hash(...)` ### Changed * `Option<T>` now implements `Enum` * `bevy_window` now depends on `bevy_reflect` * Implemented `Reflect` and `FromReflect` for `WindowId` * Derive `FromReflect` on `PerspectiveProjection` * Derive `FromReflect` on `OrthographicProjection` * Derive `FromReflect` on `WindowOrigin` * Derive `FromReflect` on `ScalingMode` * Derive `FromReflect` on `DepthCalculation` ## Migration Guide * Enums no longer need to be treated as values and usages of `#[reflect_value(...)]` can be removed or replaced by `#[reflect(...)]` * Enums (including `Option<T>`) now take a different format when serializing. The format is described above, but this may cause issues for existing scenes that make use of enums. --- Also shout out to @nicopap for helping clean up some of the code here! It's a big feature so help like this is really appreciated! Co-authored-by: Gino Valente <gino.valente.code@gmail.com>
2022-08-02 22:14:41 +00:00
}
/// Struct data used by derive macros for `Reflect` and `FromReflect`.
///
/// # Example
///
/// ```ignore (bevy_reflect is not accessible from this crate)
bevy_reflect: Reflect enums (#4761) # Objective > This is a revival of #1347. Credit for the original PR should go to @Davier. Currently, enums are treated as `ReflectRef::Value` types by `bevy_reflect`. Obviously, there needs to be better a better representation for enums using the reflection API. ## Solution Based on prior work from @Davier, an `Enum` trait has been added as well as the ability to automatically implement it via the `Reflect` derive macro. This allows enums to be expressed dynamically: ```rust #[derive(Reflect)] enum Foo { A, B(usize), C { value: f32 }, } let mut foo = Foo::B(123); assert_eq!("B", foo.variant_name()); assert_eq!(1, foo.field_len()); let new_value = DynamicEnum::from(Foo::C { value: 1.23 }); foo.apply(&new_value); assert_eq!(Foo::C{value: 1.23}, foo); ``` ### Features #### Derive Macro Use the `#[derive(Reflect)]` macro to automatically implement the `Enum` trait for enum definitions. Optionally, you can use `#[reflect(ignore)]` with both variants and variant fields, just like you can with structs. These ignored items will not be considered as part of the reflection and cannot be accessed via reflection. ```rust #[derive(Reflect)] enum TestEnum { A, // Uncomment to ignore all of `B` // #[reflect(ignore)] B(usize), C { // Uncomment to ignore only field `foo` of `C` // #[reflect(ignore)] foo: f32, bar: bool, }, } ``` #### Dynamic Enums Enums may be created/represented dynamically via the `DynamicEnum` struct. The main purpose of this struct is to allow enums to be deserialized into a partial state and to allow dynamic patching. In order to ensure conversion from a `DynamicEnum` to a concrete enum type goes smoothly, be sure to add `FromReflect` to your derive macro. ```rust let mut value = TestEnum::A; // Create from a concrete instance let dyn_enum = DynamicEnum::from(TestEnum::B(123)); value.apply(&dyn_enum); assert_eq!(TestEnum::B(123), value); // Create a purely dynamic instance let dyn_enum = DynamicEnum::new("TestEnum", "A", ()); value.apply(&dyn_enum); assert_eq!(TestEnum::A, value); ``` #### Variants An enum value is always represented as one of its variants— never the enum in its entirety. ```rust let value = TestEnum::A; assert_eq!("A", value.variant_name()); // Since we are using the `A` variant, we cannot also be the `B` variant assert_ne!("B", value.variant_name()); ``` All variant types are representable within the `Enum` trait: unit, struct, and tuple. You can get the current type like: ```rust match value.variant_type() { VariantType::Unit => println!("A unit variant!"), VariantType::Struct => println!("A struct variant!"), VariantType::Tuple => println!("A tuple variant!"), } ``` > Notice that they don't contain any values representing the fields. These are purely tags. If a variant has them, you can access the fields as well: ```rust let mut value = TestEnum::C { foo: 1.23, bar: false }; // Read/write specific fields *value.field_mut("bar").unwrap() = true; // Iterate over the entire collection of fields for field in value.iter_fields() { println!("{} = {:?}", field.name(), field.value()); } ``` #### Variant Swapping It might seem odd to group all variant types under a single trait (why allow `iter_fields` on a unit variant?), but the reason this was done ~~is to easily allow *variant swapping*.~~ As I was recently drafting up the **Design Decisions** section, I discovered that other solutions could have been made to work with variant swapping. So while there are reasons to keep the all-in-one approach, variant swapping is _not_ one of them. ```rust let mut value: Box<dyn Enum> = Box::new(TestEnum::A); value.set(Box::new(TestEnum::B(123))).unwrap(); ``` #### Serialization Enums can be serialized and deserialized via reflection without needing to implement `Serialize` or `Deserialize` themselves (which can save thousands of lines of generated code). Below are the ways an enum can be serialized. > Note, like the rest of reflection-based serialization, the order of the keys in these representations is important! ##### Unit ```json { "type": "my_crate::TestEnum", "enum": { "variant": "A" } } ``` ##### Tuple ```json { "type": "my_crate::TestEnum", "enum": { "variant": "B", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` <details> <summary>Effects on Option</summary> This ends up making `Option` look a little ugly: ```json { "type": "core::option::Option<usize>", "enum": { "variant": "Some", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` </details> ##### Struct ```json { "type": "my_crate::TestEnum", "enum": { "variant": "C", "struct": { "foo": { "type": "f32", "value": 1.23 }, "bar": { "type": "bool", "value": false } } } } ``` ## Design Decisions <details> <summary><strong>View Section</strong></summary> This section is here to provide some context for why certain decisions were made for this PR, alternatives that could have been used instead, and what could be improved upon in the future. ### Variant Representation One of the biggest decisions was to decide on how to represent variants. The current design uses a "all-in-one" design where unit, tuple, and struct variants are all simultaneously represented by the `Enum` trait. This is not the only way it could have been done, though. #### Alternatives ##### 1. Variant Traits One way of representing variants would be to define traits for each variant, implementing them whenever an enum featured at least one instance of them. This would allow us to define variants like: ```rust pub trait Enum: Reflect { fn variant(&self) -> Variant; } pub enum Variant<'a> { Unit, Tuple(&'a dyn TupleVariant), Struct(&'a dyn StructVariant), } pub trait TupleVariant { fn field_len(&self) -> usize; // ... } ``` And then do things like: ```rust fn get_tuple_len(foo: &dyn Enum) -> usize { match foo.variant() { Variant::Tuple(tuple) => tuple.field_len(), _ => panic!("not a tuple variant!") } } ``` The reason this PR does not go with this approach is because of the fact that variants are not separate types. In other words, we cannot implement traits on specific variants— these cover the *entire* enum. This means we offer an easy footgun: ```rust let foo: Option<i32> = None; let my_enum = Box::new(foo) as Box<dyn TupleVariant>; ``` Here, `my_enum` contains `foo`, which is a unit variant. However, since we need to implement `TupleVariant` for `Option` as a whole, it's possible to perform such a cast. This is obviously wrong, but could easily go unnoticed. So unfortunately, this makes it not a good candidate for representing variants. ##### 2. Variant Structs To get around the issue of traits necessarily needing to apply to both the enum and its variants, we could instead use structs that are created on a per-variant basis. This was also considered but was ultimately [[removed](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c)](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c) due to concerns about allocations. Each variant struct would probably look something like: ```rust pub trait Enum: Reflect { fn variant_mut(&self) -> VariantMut; } pub enum VariantMut<'a> { Unit, Tuple(TupleVariantMut), Struct(StructVariantMut), } struct StructVariantMut<'a> { fields: Vec<&'a mut dyn Reflect>, field_indices: HashMap<Cow<'static, str>, usize> } ``` This allows us to isolate struct variants into their own defined struct and define methods specifically for their use. It also prevents users from casting to it since it's not a trait. However, this is not an optimal solution. Both `field_indices` and `fields` will require an allocation (remember, a `Box<[T]>` still requires a `Vec<T>` in order to be constructed). This *might* be a problem if called frequently enough. ##### 3. Generated Structs The original design, implemented by @Davier, instead generates structs specific for each variant. So if we had a variant path like `Foo::Bar`, we'd generate a struct named `FooBarWrapper`. This would be newtyped around the original enum and forward tuple or struct methods to the enum with the chosen variant. Because it involved using the `Tuple` and `Struct` traits (which are also both bound on `Reflect`), this meant a bit more code had to be generated. For a single struct variant with one field, the generated code amounted to ~110LoC. However, each new field added to that variant only added ~6 more LoC. In order to work properly, the enum had to be transmuted to the generated struct: ```rust fn variant(&self) -> crate::EnumVariant<'_> { match self { Foo::Bar {value: i32} => { let wrapper_ref = unsafe { std::mem::transmute::<&Self, &FooBarWrapper>(self) }; crate::EnumVariant::Struct(wrapper_ref as &dyn crate::Struct) } } } ``` This works because `FooBarWrapper` is defined as `repr(transparent)`. Out of all the alternatives, this would probably be the one most likely to be used again in the future. The reasons for why this PR did not continue to use it was because: * To reduce generated code (which would hopefully speed up compile times) * To avoid cluttering the code with generated structs not visible to the user * To keep bevy_reflect simple and extensible (these generated structs act as proxies and might not play well with current or future systems) * To avoid additional unsafe blocks * My own misunderstanding of @Davier's code That last point is obviously on me. I misjudged the code to be too unsafe and unable to handle variant swapping (which it probably could) when I was rebasing it. Looking over it again when writing up this whole section, I see that it was actually a pretty clever way of handling variant representation. #### Benefits of All-in-One As stated before, the current implementation uses an all-in-one approach. All variants are capable of containing fields as far as `Enum` is concerned. This provides a few benefits that the alternatives do not (reduced indirection, safer code, etc.). The biggest benefit, though, is direct field access. Rather than forcing users to have to go through pattern matching, we grant direct access to the fields contained by the current variant. The reason we can do this is because all of the pattern matching happens internally. Getting the field at index `2` will automatically return `Some(...)` for the current variant if it has a field at that index or `None` if it doesn't (or can't). This could be useful for scenarios where the variant has already been verified or just set/swapped (or even where the type of variant doesn't matter): ```rust let dyn_enum: &mut dyn Enum = &mut Foo::Bar {value: 123}; // We know it's the `Bar` variant let field = dyn_enum.field("value").unwrap(); ``` Reflection is not a type-safe abstraction— almost every return value is wrapped in `Option<...>`. There are plenty of places to check and recheck that a value is what Reflect says it is. Forcing users to have to go through `match` each time they want to access a field might just be an extra step among dozens of other verification processes. Some might disagree, but ultimately, my view is that the benefit here is an improvement to the ergonomics and usability of reflected enums. </details> --- ## Changelog ### Added * Added `Enum` trait * Added `Enum` impl to `Reflect` derive macro * Added `DynamicEnum` struct * Added `DynamicVariant` * Added `EnumInfo` * Added `VariantInfo` * Added `StructVariantInfo` * Added `TupleVariantInfo` * Added `UnitVariantInfo` * Added serializtion/deserialization support for enums * Added `EnumSerializer` * Added `VariantType` * Added `VariantFieldIter` * Added `VariantField` * Added `enum_partial_eq(...)` * Added `enum_hash(...)` ### Changed * `Option<T>` now implements `Enum` * `bevy_window` now depends on `bevy_reflect` * Implemented `Reflect` and `FromReflect` for `WindowId` * Derive `FromReflect` on `PerspectiveProjection` * Derive `FromReflect` on `OrthographicProjection` * Derive `FromReflect` on `WindowOrigin` * Derive `FromReflect` on `ScalingMode` * Derive `FromReflect` on `DepthCalculation` ## Migration Guide * Enums no longer need to be treated as values and usages of `#[reflect_value(...)]` can be removed or replaced by `#[reflect(...)]` * Enums (including `Option<T>`) now take a different format when serializing. The format is described above, but this may cause issues for existing scenes that make use of enums. --- Also shout out to @nicopap for helping clean up some of the code here! It's a big feature so help like this is really appreciated! Co-authored-by: Gino Valente <gino.valente.code@gmail.com>
2022-08-02 22:14:41 +00:00
/// #[derive(Reflect)]
/// #[reflect(PartialEq, Serialize, Deserialize, Default)]
bevy_reflect_derive: Tidying up the code (#4712) # Objective The `bevy_reflect_derive` crate is not the cleanest or easiest to follow/maintain. The `lib.rs` file is especially difficult with over 1000 lines of code written in a confusing order. This is just a result of growth within the crate and it would be nice to clean it up for future work. ## Solution Split `bevy_reflect_derive` into many more submodules. The submodules include: * `container_attributes` - Code relating to container attributes * `derive_data` - Code relating to reflection-based derive metadata * `field_attributes` - Code relating to field attributes * `impls` - Code containing actual reflection implementations * `reflect_value` - Code relating to reflection-based value metadata * `registration` - Code relating to type registration * `utility` - General-purpose utility functions This leaves the `lib.rs` file to contain only the public macros, making it much easier to digest (and fewer than 200 lines). By breaking up the code into smaller modules, we make it easier for future contributors to find the code they're looking for or identify which module best fits their own additions. ### Metadata Structs This cleanup also adds two big metadata structs: `ReflectFieldAttr` and `ReflectDeriveData`. The former is used to store all attributes for a struct field (if any). The latter is used to store all metadata for struct-based derive inputs. Both significantly reduce code duplication and make editing these macros much simpler. The tradeoff is that we may collect more metadata than needed. However, this is usually a small thing (such as checking for attributes when they're not really needed or creating a `ReflectFieldAttr` for every field regardless of whether they actually have an attribute). We could try to remove these tradeoffs and squeeze some more performance out, but doing so might come at the cost of developer experience. Personally, I think it's much nicer to create a `ReflectFieldAttr` for every field since it means I don't have to do two `Option` checks. Others may disagree, though, and so we can discuss changing this either in this PR or in a future one. ### Out of Scope _Some_ documentation has been added or improved, but ultimately good docs are probably best saved for a dedicated PR. ## 🔍 Focus Points (for reviewers) I know it's a lot to sift through, so here is a list of **key points for reviewers**: - The following files contain code that was mostly just relocated: - `reflect_value.rs` - `registration.rs` - `container_attributes.rs` was also mostly moved but features some general cleanup (reducing nesting, removing hardcoded strings, etc.) and lots of doc comments - Most impl logic was moved from `lib.rs` to `impls.rs`, but they have been significantly modified to use the new `ReflectDeriveData` metadata struct in order to reduce duplication. - `derive_data.rs` and `field_attributes.rs` contain almost entirely new code and should probably be given the most attention. - Likewise, `from_reflect.rs` saw major changes using `ReflectDeriveData` so it should also be given focus. - There was no change to the `lib.rs` exports so the end-user API should be the same. ## Prior Work This task was initially tackled by @NathanSWard in #2377 (which was closed in favor of this PR), so hats off to them for beating me to the punch by nearly a year! --- ## Changelog * **[INTERNAL]** Split `bevy_reflect_derive` into smaller submodules * **[INTERNAL]** Add `ReflectFieldAttr` * **[INTERNAL]** Add `ReflectDeriveData` * Add `BevyManifest::get_path_direct()` method (`bevy_macro_utils`) Co-authored-by: MrGVSV <49806985+MrGVSV@users.noreply.github.com>
2022-05-12 19:43:23 +00:00
/// struct ThingThatImReflecting<T1, T2, T3> {
/// x: T1, // |
/// y: T2, // |- fields
/// z: T3 // |
/// }
/// ```
bevy_reflect: Reflect enums (#4761) # Objective > This is a revival of #1347. Credit for the original PR should go to @Davier. Currently, enums are treated as `ReflectRef::Value` types by `bevy_reflect`. Obviously, there needs to be better a better representation for enums using the reflection API. ## Solution Based on prior work from @Davier, an `Enum` trait has been added as well as the ability to automatically implement it via the `Reflect` derive macro. This allows enums to be expressed dynamically: ```rust #[derive(Reflect)] enum Foo { A, B(usize), C { value: f32 }, } let mut foo = Foo::B(123); assert_eq!("B", foo.variant_name()); assert_eq!(1, foo.field_len()); let new_value = DynamicEnum::from(Foo::C { value: 1.23 }); foo.apply(&new_value); assert_eq!(Foo::C{value: 1.23}, foo); ``` ### Features #### Derive Macro Use the `#[derive(Reflect)]` macro to automatically implement the `Enum` trait for enum definitions. Optionally, you can use `#[reflect(ignore)]` with both variants and variant fields, just like you can with structs. These ignored items will not be considered as part of the reflection and cannot be accessed via reflection. ```rust #[derive(Reflect)] enum TestEnum { A, // Uncomment to ignore all of `B` // #[reflect(ignore)] B(usize), C { // Uncomment to ignore only field `foo` of `C` // #[reflect(ignore)] foo: f32, bar: bool, }, } ``` #### Dynamic Enums Enums may be created/represented dynamically via the `DynamicEnum` struct. The main purpose of this struct is to allow enums to be deserialized into a partial state and to allow dynamic patching. In order to ensure conversion from a `DynamicEnum` to a concrete enum type goes smoothly, be sure to add `FromReflect` to your derive macro. ```rust let mut value = TestEnum::A; // Create from a concrete instance let dyn_enum = DynamicEnum::from(TestEnum::B(123)); value.apply(&dyn_enum); assert_eq!(TestEnum::B(123), value); // Create a purely dynamic instance let dyn_enum = DynamicEnum::new("TestEnum", "A", ()); value.apply(&dyn_enum); assert_eq!(TestEnum::A, value); ``` #### Variants An enum value is always represented as one of its variants— never the enum in its entirety. ```rust let value = TestEnum::A; assert_eq!("A", value.variant_name()); // Since we are using the `A` variant, we cannot also be the `B` variant assert_ne!("B", value.variant_name()); ``` All variant types are representable within the `Enum` trait: unit, struct, and tuple. You can get the current type like: ```rust match value.variant_type() { VariantType::Unit => println!("A unit variant!"), VariantType::Struct => println!("A struct variant!"), VariantType::Tuple => println!("A tuple variant!"), } ``` > Notice that they don't contain any values representing the fields. These are purely tags. If a variant has them, you can access the fields as well: ```rust let mut value = TestEnum::C { foo: 1.23, bar: false }; // Read/write specific fields *value.field_mut("bar").unwrap() = true; // Iterate over the entire collection of fields for field in value.iter_fields() { println!("{} = {:?}", field.name(), field.value()); } ``` #### Variant Swapping It might seem odd to group all variant types under a single trait (why allow `iter_fields` on a unit variant?), but the reason this was done ~~is to easily allow *variant swapping*.~~ As I was recently drafting up the **Design Decisions** section, I discovered that other solutions could have been made to work with variant swapping. So while there are reasons to keep the all-in-one approach, variant swapping is _not_ one of them. ```rust let mut value: Box<dyn Enum> = Box::new(TestEnum::A); value.set(Box::new(TestEnum::B(123))).unwrap(); ``` #### Serialization Enums can be serialized and deserialized via reflection without needing to implement `Serialize` or `Deserialize` themselves (which can save thousands of lines of generated code). Below are the ways an enum can be serialized. > Note, like the rest of reflection-based serialization, the order of the keys in these representations is important! ##### Unit ```json { "type": "my_crate::TestEnum", "enum": { "variant": "A" } } ``` ##### Tuple ```json { "type": "my_crate::TestEnum", "enum": { "variant": "B", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` <details> <summary>Effects on Option</summary> This ends up making `Option` look a little ugly: ```json { "type": "core::option::Option<usize>", "enum": { "variant": "Some", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` </details> ##### Struct ```json { "type": "my_crate::TestEnum", "enum": { "variant": "C", "struct": { "foo": { "type": "f32", "value": 1.23 }, "bar": { "type": "bool", "value": false } } } } ``` ## Design Decisions <details> <summary><strong>View Section</strong></summary> This section is here to provide some context for why certain decisions were made for this PR, alternatives that could have been used instead, and what could be improved upon in the future. ### Variant Representation One of the biggest decisions was to decide on how to represent variants. The current design uses a "all-in-one" design where unit, tuple, and struct variants are all simultaneously represented by the `Enum` trait. This is not the only way it could have been done, though. #### Alternatives ##### 1. Variant Traits One way of representing variants would be to define traits for each variant, implementing them whenever an enum featured at least one instance of them. This would allow us to define variants like: ```rust pub trait Enum: Reflect { fn variant(&self) -> Variant; } pub enum Variant<'a> { Unit, Tuple(&'a dyn TupleVariant), Struct(&'a dyn StructVariant), } pub trait TupleVariant { fn field_len(&self) -> usize; // ... } ``` And then do things like: ```rust fn get_tuple_len(foo: &dyn Enum) -> usize { match foo.variant() { Variant::Tuple(tuple) => tuple.field_len(), _ => panic!("not a tuple variant!") } } ``` The reason this PR does not go with this approach is because of the fact that variants are not separate types. In other words, we cannot implement traits on specific variants— these cover the *entire* enum. This means we offer an easy footgun: ```rust let foo: Option<i32> = None; let my_enum = Box::new(foo) as Box<dyn TupleVariant>; ``` Here, `my_enum` contains `foo`, which is a unit variant. However, since we need to implement `TupleVariant` for `Option` as a whole, it's possible to perform such a cast. This is obviously wrong, but could easily go unnoticed. So unfortunately, this makes it not a good candidate for representing variants. ##### 2. Variant Structs To get around the issue of traits necessarily needing to apply to both the enum and its variants, we could instead use structs that are created on a per-variant basis. This was also considered but was ultimately [[removed](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c)](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c) due to concerns about allocations. Each variant struct would probably look something like: ```rust pub trait Enum: Reflect { fn variant_mut(&self) -> VariantMut; } pub enum VariantMut<'a> { Unit, Tuple(TupleVariantMut), Struct(StructVariantMut), } struct StructVariantMut<'a> { fields: Vec<&'a mut dyn Reflect>, field_indices: HashMap<Cow<'static, str>, usize> } ``` This allows us to isolate struct variants into their own defined struct and define methods specifically for their use. It also prevents users from casting to it since it's not a trait. However, this is not an optimal solution. Both `field_indices` and `fields` will require an allocation (remember, a `Box<[T]>` still requires a `Vec<T>` in order to be constructed). This *might* be a problem if called frequently enough. ##### 3. Generated Structs The original design, implemented by @Davier, instead generates structs specific for each variant. So if we had a variant path like `Foo::Bar`, we'd generate a struct named `FooBarWrapper`. This would be newtyped around the original enum and forward tuple or struct methods to the enum with the chosen variant. Because it involved using the `Tuple` and `Struct` traits (which are also both bound on `Reflect`), this meant a bit more code had to be generated. For a single struct variant with one field, the generated code amounted to ~110LoC. However, each new field added to that variant only added ~6 more LoC. In order to work properly, the enum had to be transmuted to the generated struct: ```rust fn variant(&self) -> crate::EnumVariant<'_> { match self { Foo::Bar {value: i32} => { let wrapper_ref = unsafe { std::mem::transmute::<&Self, &FooBarWrapper>(self) }; crate::EnumVariant::Struct(wrapper_ref as &dyn crate::Struct) } } } ``` This works because `FooBarWrapper` is defined as `repr(transparent)`. Out of all the alternatives, this would probably be the one most likely to be used again in the future. The reasons for why this PR did not continue to use it was because: * To reduce generated code (which would hopefully speed up compile times) * To avoid cluttering the code with generated structs not visible to the user * To keep bevy_reflect simple and extensible (these generated structs act as proxies and might not play well with current or future systems) * To avoid additional unsafe blocks * My own misunderstanding of @Davier's code That last point is obviously on me. I misjudged the code to be too unsafe and unable to handle variant swapping (which it probably could) when I was rebasing it. Looking over it again when writing up this whole section, I see that it was actually a pretty clever way of handling variant representation. #### Benefits of All-in-One As stated before, the current implementation uses an all-in-one approach. All variants are capable of containing fields as far as `Enum` is concerned. This provides a few benefits that the alternatives do not (reduced indirection, safer code, etc.). The biggest benefit, though, is direct field access. Rather than forcing users to have to go through pattern matching, we grant direct access to the fields contained by the current variant. The reason we can do this is because all of the pattern matching happens internally. Getting the field at index `2` will automatically return `Some(...)` for the current variant if it has a field at that index or `None` if it doesn't (or can't). This could be useful for scenarios where the variant has already been verified or just set/swapped (or even where the type of variant doesn't matter): ```rust let dyn_enum: &mut dyn Enum = &mut Foo::Bar {value: 123}; // We know it's the `Bar` variant let field = dyn_enum.field("value").unwrap(); ``` Reflection is not a type-safe abstraction— almost every return value is wrapped in `Option<...>`. There are plenty of places to check and recheck that a value is what Reflect says it is. Forcing users to have to go through `match` each time they want to access a field might just be an extra step among dozens of other verification processes. Some might disagree, but ultimately, my view is that the benefit here is an improvement to the ergonomics and usability of reflected enums. </details> --- ## Changelog ### Added * Added `Enum` trait * Added `Enum` impl to `Reflect` derive macro * Added `DynamicEnum` struct * Added `DynamicVariant` * Added `EnumInfo` * Added `VariantInfo` * Added `StructVariantInfo` * Added `TupleVariantInfo` * Added `UnitVariantInfo` * Added serializtion/deserialization support for enums * Added `EnumSerializer` * Added `VariantType` * Added `VariantFieldIter` * Added `VariantField` * Added `enum_partial_eq(...)` * Added `enum_hash(...)` ### Changed * `Option<T>` now implements `Enum` * `bevy_window` now depends on `bevy_reflect` * Implemented `Reflect` and `FromReflect` for `WindowId` * Derive `FromReflect` on `PerspectiveProjection` * Derive `FromReflect` on `OrthographicProjection` * Derive `FromReflect` on `WindowOrigin` * Derive `FromReflect` on `ScalingMode` * Derive `FromReflect` on `DepthCalculation` ## Migration Guide * Enums no longer need to be treated as values and usages of `#[reflect_value(...)]` can be removed or replaced by `#[reflect(...)]` * Enums (including `Option<T>`) now take a different format when serializing. The format is described above, but this may cause issues for existing scenes that make use of enums. --- Also shout out to @nicopap for helping clean up some of the code here! It's a big feature so help like this is really appreciated! Co-authored-by: Gino Valente <gino.valente.code@gmail.com>
2022-08-02 22:14:41 +00:00
pub(crate) struct ReflectStruct<'a> {
meta: ReflectMeta<'a>,
bevy_reflect: Fix ignored/skipped field order (#7575) # Objective Fixes #5101 Alternative to #6511 ## Solution Corrected the behavior for ignored fields in `FromReflect`, which was previously using the incorrect field indexes. Similarly, fields marked with `#[reflect(skip_serializing)]` no longer break when using `FromReflect` after deserialization. This was done by modifying `SerializationData` to store a function pointer that can later be used to generate a default instance of the skipped field during deserialization. The function pointer points to a function generated by the derive macro using the behavior designated by `#[reflect(default)]` (or just `Default` if none provided). The entire output of the macro is now wrapped in an [unnamed constant](https://doc.rust-lang.org/stable/reference/items/constant-items.html#unnamed-constant) which keeps this behavior hygienic. #### Rationale The biggest downside to this approach is that it requires fields marked `#[reflect(skip_serializing)]` to provide the ability to create a default instance— either via a `Default` impl or by specifying a custom one. While this isn't great, I think it might be justified by the fact that we really need to create this value when using `FromReflect` on a deserialized object. And we need to do this _during_ deserialization because after that (at least for tuples and tuple structs) we lose information about which field is which: _"is the value at index 1 in this `DynamicTupleStruct` the actual value for index 1 or is it really the value for index 2 since index 1 is skippable...?"_ #### Alternatives An alternative would be to store `Option<Box<dyn Reflect>>` within `DynamicTuple` and `DynamicTupleStruct` instead of just `Box<dyn Reflect>`. This would allow us to insert "empty"/"missing" fields during deserialization, thus saving the positional information of the skipped fields. However, this may require changing the API of `Tuple` and `TupleStruct` such that they can account for their dynamic counterparts returning `None` for a skipped field. In practice this would probably mean exposing the `Option`-ness of the dynamics onto implementors via methods like `Tuple::drain` or `TupleStruct::field`. Personally, I think requiring `Default` would be better than muddying up the API to account for these special cases. But I'm open to trying out this other approach if the community feels that it's better. --- ## Changelog ### Public Changes #### Fixed - The behaviors of `#[reflect(ignore)]` and `#[reflect(skip_serializing)]` are no longer dependent on field order #### Changed - Fields marked with `#[reflect(skip_serializing)]` now need to either implement `Default` or specify a custom default function using `#[reflect(default = "path::to::some_func")]` - Deserializing a type with fields marked `#[reflect(skip_serializing)]` will now include that field initialized to its specified default value - `SerializationData::new` now takes the new `SkippedField` struct along with the skipped field index - Renamed `SerializationData::is_ignored_field` to `SerializationData::is_field_skipped` #### Added - Added `SkippedField` struct - Added methods `SerializationData::generate_default` and `SerializationData::iter_skipped` ### Internal Changes #### Changed - Replaced `members_to_serialization_denylist` and `BitSet<u32>` with `SerializationDataDef` - The `Reflect` derive is more hygienic as it now outputs within an [unnamed constant](https://doc.rust-lang.org/stable/reference/items/constant-items.html#unnamed-constant) - `StructField::index` has been split up into `StructField::declaration_index` and `StructField::reflection_index` #### Removed - Removed `bitset` dependency ## Migration Guide * Fields marked `#[reflect(skip_serializing)]` now must implement `Default` or specify a custom default function with `#[reflect(default = "path::to::some_func")]` ```rust #[derive(Reflect)] struct MyStruct { #[reflect(skip_serializing)] #[reflect(default = "get_foo_default")] foo: Foo, // <- `Foo` does not impl `Default` so requires a custom function #[reflect(skip_serializing)] bar: Bar, // <- `Bar` impls `Default` } #[derive(Reflect)] struct Foo(i32); #[derive(Reflect, Default)] struct Bar(i32); fn get_foo_default() -> Foo { Foo(123) } ``` * `SerializationData::new` has been changed to expect an iterator of `(usize, SkippedField)` rather than one of just `usize` ```rust // BEFORE SerializationData::new([0, 3].into_iter()); // AFTER SerializationData::new([ (0, SkippedField::new(field_0_default_fn)), (3, SkippedField::new(field_3_default_fn)), ].into_iter()); ``` * `Serialization::is_ignored_field` has been renamed to `Serialization::is_field_skipped` * Fields marked `#[reflect(skip_serializing)]` are now included in deserialization output. This may affect logic that expected those fields to be absent.
2023-10-22 12:43:31 +00:00
serialization_data: Option<SerializationDataDef>,
bevy_reflect_derive: Tidying up the code (#4712) # Objective The `bevy_reflect_derive` crate is not the cleanest or easiest to follow/maintain. The `lib.rs` file is especially difficult with over 1000 lines of code written in a confusing order. This is just a result of growth within the crate and it would be nice to clean it up for future work. ## Solution Split `bevy_reflect_derive` into many more submodules. The submodules include: * `container_attributes` - Code relating to container attributes * `derive_data` - Code relating to reflection-based derive metadata * `field_attributes` - Code relating to field attributes * `impls` - Code containing actual reflection implementations * `reflect_value` - Code relating to reflection-based value metadata * `registration` - Code relating to type registration * `utility` - General-purpose utility functions This leaves the `lib.rs` file to contain only the public macros, making it much easier to digest (and fewer than 200 lines). By breaking up the code into smaller modules, we make it easier for future contributors to find the code they're looking for or identify which module best fits their own additions. ### Metadata Structs This cleanup also adds two big metadata structs: `ReflectFieldAttr` and `ReflectDeriveData`. The former is used to store all attributes for a struct field (if any). The latter is used to store all metadata for struct-based derive inputs. Both significantly reduce code duplication and make editing these macros much simpler. The tradeoff is that we may collect more metadata than needed. However, this is usually a small thing (such as checking for attributes when they're not really needed or creating a `ReflectFieldAttr` for every field regardless of whether they actually have an attribute). We could try to remove these tradeoffs and squeeze some more performance out, but doing so might come at the cost of developer experience. Personally, I think it's much nicer to create a `ReflectFieldAttr` for every field since it means I don't have to do two `Option` checks. Others may disagree, though, and so we can discuss changing this either in this PR or in a future one. ### Out of Scope _Some_ documentation has been added or improved, but ultimately good docs are probably best saved for a dedicated PR. ## 🔍 Focus Points (for reviewers) I know it's a lot to sift through, so here is a list of **key points for reviewers**: - The following files contain code that was mostly just relocated: - `reflect_value.rs` - `registration.rs` - `container_attributes.rs` was also mostly moved but features some general cleanup (reducing nesting, removing hardcoded strings, etc.) and lots of doc comments - Most impl logic was moved from `lib.rs` to `impls.rs`, but they have been significantly modified to use the new `ReflectDeriveData` metadata struct in order to reduce duplication. - `derive_data.rs` and `field_attributes.rs` contain almost entirely new code and should probably be given the most attention. - Likewise, `from_reflect.rs` saw major changes using `ReflectDeriveData` so it should also be given focus. - There was no change to the `lib.rs` exports so the end-user API should be the same. ## Prior Work This task was initially tackled by @NathanSWard in #2377 (which was closed in favor of this PR), so hats off to them for beating me to the punch by nearly a year! --- ## Changelog * **[INTERNAL]** Split `bevy_reflect_derive` into smaller submodules * **[INTERNAL]** Add `ReflectFieldAttr` * **[INTERNAL]** Add `ReflectDeriveData` * Add `BevyManifest::get_path_direct()` method (`bevy_macro_utils`) Co-authored-by: MrGVSV <49806985+MrGVSV@users.noreply.github.com>
2022-05-12 19:43:23 +00:00
fields: Vec<StructField<'a>>,
}
bevy_reflect: Reflect enums (#4761) # Objective > This is a revival of #1347. Credit for the original PR should go to @Davier. Currently, enums are treated as `ReflectRef::Value` types by `bevy_reflect`. Obviously, there needs to be better a better representation for enums using the reflection API. ## Solution Based on prior work from @Davier, an `Enum` trait has been added as well as the ability to automatically implement it via the `Reflect` derive macro. This allows enums to be expressed dynamically: ```rust #[derive(Reflect)] enum Foo { A, B(usize), C { value: f32 }, } let mut foo = Foo::B(123); assert_eq!("B", foo.variant_name()); assert_eq!(1, foo.field_len()); let new_value = DynamicEnum::from(Foo::C { value: 1.23 }); foo.apply(&new_value); assert_eq!(Foo::C{value: 1.23}, foo); ``` ### Features #### Derive Macro Use the `#[derive(Reflect)]` macro to automatically implement the `Enum` trait for enum definitions. Optionally, you can use `#[reflect(ignore)]` with both variants and variant fields, just like you can with structs. These ignored items will not be considered as part of the reflection and cannot be accessed via reflection. ```rust #[derive(Reflect)] enum TestEnum { A, // Uncomment to ignore all of `B` // #[reflect(ignore)] B(usize), C { // Uncomment to ignore only field `foo` of `C` // #[reflect(ignore)] foo: f32, bar: bool, }, } ``` #### Dynamic Enums Enums may be created/represented dynamically via the `DynamicEnum` struct. The main purpose of this struct is to allow enums to be deserialized into a partial state and to allow dynamic patching. In order to ensure conversion from a `DynamicEnum` to a concrete enum type goes smoothly, be sure to add `FromReflect` to your derive macro. ```rust let mut value = TestEnum::A; // Create from a concrete instance let dyn_enum = DynamicEnum::from(TestEnum::B(123)); value.apply(&dyn_enum); assert_eq!(TestEnum::B(123), value); // Create a purely dynamic instance let dyn_enum = DynamicEnum::new("TestEnum", "A", ()); value.apply(&dyn_enum); assert_eq!(TestEnum::A, value); ``` #### Variants An enum value is always represented as one of its variants— never the enum in its entirety. ```rust let value = TestEnum::A; assert_eq!("A", value.variant_name()); // Since we are using the `A` variant, we cannot also be the `B` variant assert_ne!("B", value.variant_name()); ``` All variant types are representable within the `Enum` trait: unit, struct, and tuple. You can get the current type like: ```rust match value.variant_type() { VariantType::Unit => println!("A unit variant!"), VariantType::Struct => println!("A struct variant!"), VariantType::Tuple => println!("A tuple variant!"), } ``` > Notice that they don't contain any values representing the fields. These are purely tags. If a variant has them, you can access the fields as well: ```rust let mut value = TestEnum::C { foo: 1.23, bar: false }; // Read/write specific fields *value.field_mut("bar").unwrap() = true; // Iterate over the entire collection of fields for field in value.iter_fields() { println!("{} = {:?}", field.name(), field.value()); } ``` #### Variant Swapping It might seem odd to group all variant types under a single trait (why allow `iter_fields` on a unit variant?), but the reason this was done ~~is to easily allow *variant swapping*.~~ As I was recently drafting up the **Design Decisions** section, I discovered that other solutions could have been made to work with variant swapping. So while there are reasons to keep the all-in-one approach, variant swapping is _not_ one of them. ```rust let mut value: Box<dyn Enum> = Box::new(TestEnum::A); value.set(Box::new(TestEnum::B(123))).unwrap(); ``` #### Serialization Enums can be serialized and deserialized via reflection without needing to implement `Serialize` or `Deserialize` themselves (which can save thousands of lines of generated code). Below are the ways an enum can be serialized. > Note, like the rest of reflection-based serialization, the order of the keys in these representations is important! ##### Unit ```json { "type": "my_crate::TestEnum", "enum": { "variant": "A" } } ``` ##### Tuple ```json { "type": "my_crate::TestEnum", "enum": { "variant": "B", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` <details> <summary>Effects on Option</summary> This ends up making `Option` look a little ugly: ```json { "type": "core::option::Option<usize>", "enum": { "variant": "Some", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` </details> ##### Struct ```json { "type": "my_crate::TestEnum", "enum": { "variant": "C", "struct": { "foo": { "type": "f32", "value": 1.23 }, "bar": { "type": "bool", "value": false } } } } ``` ## Design Decisions <details> <summary><strong>View Section</strong></summary> This section is here to provide some context for why certain decisions were made for this PR, alternatives that could have been used instead, and what could be improved upon in the future. ### Variant Representation One of the biggest decisions was to decide on how to represent variants. The current design uses a "all-in-one" design where unit, tuple, and struct variants are all simultaneously represented by the `Enum` trait. This is not the only way it could have been done, though. #### Alternatives ##### 1. Variant Traits One way of representing variants would be to define traits for each variant, implementing them whenever an enum featured at least one instance of them. This would allow us to define variants like: ```rust pub trait Enum: Reflect { fn variant(&self) -> Variant; } pub enum Variant<'a> { Unit, Tuple(&'a dyn TupleVariant), Struct(&'a dyn StructVariant), } pub trait TupleVariant { fn field_len(&self) -> usize; // ... } ``` And then do things like: ```rust fn get_tuple_len(foo: &dyn Enum) -> usize { match foo.variant() { Variant::Tuple(tuple) => tuple.field_len(), _ => panic!("not a tuple variant!") } } ``` The reason this PR does not go with this approach is because of the fact that variants are not separate types. In other words, we cannot implement traits on specific variants— these cover the *entire* enum. This means we offer an easy footgun: ```rust let foo: Option<i32> = None; let my_enum = Box::new(foo) as Box<dyn TupleVariant>; ``` Here, `my_enum` contains `foo`, which is a unit variant. However, since we need to implement `TupleVariant` for `Option` as a whole, it's possible to perform such a cast. This is obviously wrong, but could easily go unnoticed. So unfortunately, this makes it not a good candidate for representing variants. ##### 2. Variant Structs To get around the issue of traits necessarily needing to apply to both the enum and its variants, we could instead use structs that are created on a per-variant basis. This was also considered but was ultimately [[removed](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c)](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c) due to concerns about allocations. Each variant struct would probably look something like: ```rust pub trait Enum: Reflect { fn variant_mut(&self) -> VariantMut; } pub enum VariantMut<'a> { Unit, Tuple(TupleVariantMut), Struct(StructVariantMut), } struct StructVariantMut<'a> { fields: Vec<&'a mut dyn Reflect>, field_indices: HashMap<Cow<'static, str>, usize> } ``` This allows us to isolate struct variants into their own defined struct and define methods specifically for their use. It also prevents users from casting to it since it's not a trait. However, this is not an optimal solution. Both `field_indices` and `fields` will require an allocation (remember, a `Box<[T]>` still requires a `Vec<T>` in order to be constructed). This *might* be a problem if called frequently enough. ##### 3. Generated Structs The original design, implemented by @Davier, instead generates structs specific for each variant. So if we had a variant path like `Foo::Bar`, we'd generate a struct named `FooBarWrapper`. This would be newtyped around the original enum and forward tuple or struct methods to the enum with the chosen variant. Because it involved using the `Tuple` and `Struct` traits (which are also both bound on `Reflect`), this meant a bit more code had to be generated. For a single struct variant with one field, the generated code amounted to ~110LoC. However, each new field added to that variant only added ~6 more LoC. In order to work properly, the enum had to be transmuted to the generated struct: ```rust fn variant(&self) -> crate::EnumVariant<'_> { match self { Foo::Bar {value: i32} => { let wrapper_ref = unsafe { std::mem::transmute::<&Self, &FooBarWrapper>(self) }; crate::EnumVariant::Struct(wrapper_ref as &dyn crate::Struct) } } } ``` This works because `FooBarWrapper` is defined as `repr(transparent)`. Out of all the alternatives, this would probably be the one most likely to be used again in the future. The reasons for why this PR did not continue to use it was because: * To reduce generated code (which would hopefully speed up compile times) * To avoid cluttering the code with generated structs not visible to the user * To keep bevy_reflect simple and extensible (these generated structs act as proxies and might not play well with current or future systems) * To avoid additional unsafe blocks * My own misunderstanding of @Davier's code That last point is obviously on me. I misjudged the code to be too unsafe and unable to handle variant swapping (which it probably could) when I was rebasing it. Looking over it again when writing up this whole section, I see that it was actually a pretty clever way of handling variant representation. #### Benefits of All-in-One As stated before, the current implementation uses an all-in-one approach. All variants are capable of containing fields as far as `Enum` is concerned. This provides a few benefits that the alternatives do not (reduced indirection, safer code, etc.). The biggest benefit, though, is direct field access. Rather than forcing users to have to go through pattern matching, we grant direct access to the fields contained by the current variant. The reason we can do this is because all of the pattern matching happens internally. Getting the field at index `2` will automatically return `Some(...)` for the current variant if it has a field at that index or `None` if it doesn't (or can't). This could be useful for scenarios where the variant has already been verified or just set/swapped (or even where the type of variant doesn't matter): ```rust let dyn_enum: &mut dyn Enum = &mut Foo::Bar {value: 123}; // We know it's the `Bar` variant let field = dyn_enum.field("value").unwrap(); ``` Reflection is not a type-safe abstraction— almost every return value is wrapped in `Option<...>`. There are plenty of places to check and recheck that a value is what Reflect says it is. Forcing users to have to go through `match` each time they want to access a field might just be an extra step among dozens of other verification processes. Some might disagree, but ultimately, my view is that the benefit here is an improvement to the ergonomics and usability of reflected enums. </details> --- ## Changelog ### Added * Added `Enum` trait * Added `Enum` impl to `Reflect` derive macro * Added `DynamicEnum` struct * Added `DynamicVariant` * Added `EnumInfo` * Added `VariantInfo` * Added `StructVariantInfo` * Added `TupleVariantInfo` * Added `UnitVariantInfo` * Added serializtion/deserialization support for enums * Added `EnumSerializer` * Added `VariantType` * Added `VariantFieldIter` * Added `VariantField` * Added `enum_partial_eq(...)` * Added `enum_hash(...)` ### Changed * `Option<T>` now implements `Enum` * `bevy_window` now depends on `bevy_reflect` * Implemented `Reflect` and `FromReflect` for `WindowId` * Derive `FromReflect` on `PerspectiveProjection` * Derive `FromReflect` on `OrthographicProjection` * Derive `FromReflect` on `WindowOrigin` * Derive `FromReflect` on `ScalingMode` * Derive `FromReflect` on `DepthCalculation` ## Migration Guide * Enums no longer need to be treated as values and usages of `#[reflect_value(...)]` can be removed or replaced by `#[reflect(...)]` * Enums (including `Option<T>`) now take a different format when serializing. The format is described above, but this may cause issues for existing scenes that make use of enums. --- Also shout out to @nicopap for helping clean up some of the code here! It's a big feature so help like this is really appreciated! Co-authored-by: Gino Valente <gino.valente.code@gmail.com>
2022-08-02 22:14:41 +00:00
/// Enum data used by derive macros for `Reflect` and `FromReflect`.
///
/// # Example
///
/// ```ignore (bevy_reflect is not accessible from this crate)
bevy_reflect: Reflect enums (#4761) # Objective > This is a revival of #1347. Credit for the original PR should go to @Davier. Currently, enums are treated as `ReflectRef::Value` types by `bevy_reflect`. Obviously, there needs to be better a better representation for enums using the reflection API. ## Solution Based on prior work from @Davier, an `Enum` trait has been added as well as the ability to automatically implement it via the `Reflect` derive macro. This allows enums to be expressed dynamically: ```rust #[derive(Reflect)] enum Foo { A, B(usize), C { value: f32 }, } let mut foo = Foo::B(123); assert_eq!("B", foo.variant_name()); assert_eq!(1, foo.field_len()); let new_value = DynamicEnum::from(Foo::C { value: 1.23 }); foo.apply(&new_value); assert_eq!(Foo::C{value: 1.23}, foo); ``` ### Features #### Derive Macro Use the `#[derive(Reflect)]` macro to automatically implement the `Enum` trait for enum definitions. Optionally, you can use `#[reflect(ignore)]` with both variants and variant fields, just like you can with structs. These ignored items will not be considered as part of the reflection and cannot be accessed via reflection. ```rust #[derive(Reflect)] enum TestEnum { A, // Uncomment to ignore all of `B` // #[reflect(ignore)] B(usize), C { // Uncomment to ignore only field `foo` of `C` // #[reflect(ignore)] foo: f32, bar: bool, }, } ``` #### Dynamic Enums Enums may be created/represented dynamically via the `DynamicEnum` struct. The main purpose of this struct is to allow enums to be deserialized into a partial state and to allow dynamic patching. In order to ensure conversion from a `DynamicEnum` to a concrete enum type goes smoothly, be sure to add `FromReflect` to your derive macro. ```rust let mut value = TestEnum::A; // Create from a concrete instance let dyn_enum = DynamicEnum::from(TestEnum::B(123)); value.apply(&dyn_enum); assert_eq!(TestEnum::B(123), value); // Create a purely dynamic instance let dyn_enum = DynamicEnum::new("TestEnum", "A", ()); value.apply(&dyn_enum); assert_eq!(TestEnum::A, value); ``` #### Variants An enum value is always represented as one of its variants— never the enum in its entirety. ```rust let value = TestEnum::A; assert_eq!("A", value.variant_name()); // Since we are using the `A` variant, we cannot also be the `B` variant assert_ne!("B", value.variant_name()); ``` All variant types are representable within the `Enum` trait: unit, struct, and tuple. You can get the current type like: ```rust match value.variant_type() { VariantType::Unit => println!("A unit variant!"), VariantType::Struct => println!("A struct variant!"), VariantType::Tuple => println!("A tuple variant!"), } ``` > Notice that they don't contain any values representing the fields. These are purely tags. If a variant has them, you can access the fields as well: ```rust let mut value = TestEnum::C { foo: 1.23, bar: false }; // Read/write specific fields *value.field_mut("bar").unwrap() = true; // Iterate over the entire collection of fields for field in value.iter_fields() { println!("{} = {:?}", field.name(), field.value()); } ``` #### Variant Swapping It might seem odd to group all variant types under a single trait (why allow `iter_fields` on a unit variant?), but the reason this was done ~~is to easily allow *variant swapping*.~~ As I was recently drafting up the **Design Decisions** section, I discovered that other solutions could have been made to work with variant swapping. So while there are reasons to keep the all-in-one approach, variant swapping is _not_ one of them. ```rust let mut value: Box<dyn Enum> = Box::new(TestEnum::A); value.set(Box::new(TestEnum::B(123))).unwrap(); ``` #### Serialization Enums can be serialized and deserialized via reflection without needing to implement `Serialize` or `Deserialize` themselves (which can save thousands of lines of generated code). Below are the ways an enum can be serialized. > Note, like the rest of reflection-based serialization, the order of the keys in these representations is important! ##### Unit ```json { "type": "my_crate::TestEnum", "enum": { "variant": "A" } } ``` ##### Tuple ```json { "type": "my_crate::TestEnum", "enum": { "variant": "B", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` <details> <summary>Effects on Option</summary> This ends up making `Option` look a little ugly: ```json { "type": "core::option::Option<usize>", "enum": { "variant": "Some", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` </details> ##### Struct ```json { "type": "my_crate::TestEnum", "enum": { "variant": "C", "struct": { "foo": { "type": "f32", "value": 1.23 }, "bar": { "type": "bool", "value": false } } } } ``` ## Design Decisions <details> <summary><strong>View Section</strong></summary> This section is here to provide some context for why certain decisions were made for this PR, alternatives that could have been used instead, and what could be improved upon in the future. ### Variant Representation One of the biggest decisions was to decide on how to represent variants. The current design uses a "all-in-one" design where unit, tuple, and struct variants are all simultaneously represented by the `Enum` trait. This is not the only way it could have been done, though. #### Alternatives ##### 1. Variant Traits One way of representing variants would be to define traits for each variant, implementing them whenever an enum featured at least one instance of them. This would allow us to define variants like: ```rust pub trait Enum: Reflect { fn variant(&self) -> Variant; } pub enum Variant<'a> { Unit, Tuple(&'a dyn TupleVariant), Struct(&'a dyn StructVariant), } pub trait TupleVariant { fn field_len(&self) -> usize; // ... } ``` And then do things like: ```rust fn get_tuple_len(foo: &dyn Enum) -> usize { match foo.variant() { Variant::Tuple(tuple) => tuple.field_len(), _ => panic!("not a tuple variant!") } } ``` The reason this PR does not go with this approach is because of the fact that variants are not separate types. In other words, we cannot implement traits on specific variants— these cover the *entire* enum. This means we offer an easy footgun: ```rust let foo: Option<i32> = None; let my_enum = Box::new(foo) as Box<dyn TupleVariant>; ``` Here, `my_enum` contains `foo`, which is a unit variant. However, since we need to implement `TupleVariant` for `Option` as a whole, it's possible to perform such a cast. This is obviously wrong, but could easily go unnoticed. So unfortunately, this makes it not a good candidate for representing variants. ##### 2. Variant Structs To get around the issue of traits necessarily needing to apply to both the enum and its variants, we could instead use structs that are created on a per-variant basis. This was also considered but was ultimately [[removed](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c)](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c) due to concerns about allocations. Each variant struct would probably look something like: ```rust pub trait Enum: Reflect { fn variant_mut(&self) -> VariantMut; } pub enum VariantMut<'a> { Unit, Tuple(TupleVariantMut), Struct(StructVariantMut), } struct StructVariantMut<'a> { fields: Vec<&'a mut dyn Reflect>, field_indices: HashMap<Cow<'static, str>, usize> } ``` This allows us to isolate struct variants into their own defined struct and define methods specifically for their use. It also prevents users from casting to it since it's not a trait. However, this is not an optimal solution. Both `field_indices` and `fields` will require an allocation (remember, a `Box<[T]>` still requires a `Vec<T>` in order to be constructed). This *might* be a problem if called frequently enough. ##### 3. Generated Structs The original design, implemented by @Davier, instead generates structs specific for each variant. So if we had a variant path like `Foo::Bar`, we'd generate a struct named `FooBarWrapper`. This would be newtyped around the original enum and forward tuple or struct methods to the enum with the chosen variant. Because it involved using the `Tuple` and `Struct` traits (which are also both bound on `Reflect`), this meant a bit more code had to be generated. For a single struct variant with one field, the generated code amounted to ~110LoC. However, each new field added to that variant only added ~6 more LoC. In order to work properly, the enum had to be transmuted to the generated struct: ```rust fn variant(&self) -> crate::EnumVariant<'_> { match self { Foo::Bar {value: i32} => { let wrapper_ref = unsafe { std::mem::transmute::<&Self, &FooBarWrapper>(self) }; crate::EnumVariant::Struct(wrapper_ref as &dyn crate::Struct) } } } ``` This works because `FooBarWrapper` is defined as `repr(transparent)`. Out of all the alternatives, this would probably be the one most likely to be used again in the future. The reasons for why this PR did not continue to use it was because: * To reduce generated code (which would hopefully speed up compile times) * To avoid cluttering the code with generated structs not visible to the user * To keep bevy_reflect simple and extensible (these generated structs act as proxies and might not play well with current or future systems) * To avoid additional unsafe blocks * My own misunderstanding of @Davier's code That last point is obviously on me. I misjudged the code to be too unsafe and unable to handle variant swapping (which it probably could) when I was rebasing it. Looking over it again when writing up this whole section, I see that it was actually a pretty clever way of handling variant representation. #### Benefits of All-in-One As stated before, the current implementation uses an all-in-one approach. All variants are capable of containing fields as far as `Enum` is concerned. This provides a few benefits that the alternatives do not (reduced indirection, safer code, etc.). The biggest benefit, though, is direct field access. Rather than forcing users to have to go through pattern matching, we grant direct access to the fields contained by the current variant. The reason we can do this is because all of the pattern matching happens internally. Getting the field at index `2` will automatically return `Some(...)` for the current variant if it has a field at that index or `None` if it doesn't (or can't). This could be useful for scenarios where the variant has already been verified or just set/swapped (or even where the type of variant doesn't matter): ```rust let dyn_enum: &mut dyn Enum = &mut Foo::Bar {value: 123}; // We know it's the `Bar` variant let field = dyn_enum.field("value").unwrap(); ``` Reflection is not a type-safe abstraction— almost every return value is wrapped in `Option<...>`. There are plenty of places to check and recheck that a value is what Reflect says it is. Forcing users to have to go through `match` each time they want to access a field might just be an extra step among dozens of other verification processes. Some might disagree, but ultimately, my view is that the benefit here is an improvement to the ergonomics and usability of reflected enums. </details> --- ## Changelog ### Added * Added `Enum` trait * Added `Enum` impl to `Reflect` derive macro * Added `DynamicEnum` struct * Added `DynamicVariant` * Added `EnumInfo` * Added `VariantInfo` * Added `StructVariantInfo` * Added `TupleVariantInfo` * Added `UnitVariantInfo` * Added serializtion/deserialization support for enums * Added `EnumSerializer` * Added `VariantType` * Added `VariantFieldIter` * Added `VariantField` * Added `enum_partial_eq(...)` * Added `enum_hash(...)` ### Changed * `Option<T>` now implements `Enum` * `bevy_window` now depends on `bevy_reflect` * Implemented `Reflect` and `FromReflect` for `WindowId` * Derive `FromReflect` on `PerspectiveProjection` * Derive `FromReflect` on `OrthographicProjection` * Derive `FromReflect` on `WindowOrigin` * Derive `FromReflect` on `ScalingMode` * Derive `FromReflect` on `DepthCalculation` ## Migration Guide * Enums no longer need to be treated as values and usages of `#[reflect_value(...)]` can be removed or replaced by `#[reflect(...)]` * Enums (including `Option<T>`) now take a different format when serializing. The format is described above, but this may cause issues for existing scenes that make use of enums. --- Also shout out to @nicopap for helping clean up some of the code here! It's a big feature so help like this is really appreciated! Co-authored-by: Gino Valente <gino.valente.code@gmail.com>
2022-08-02 22:14:41 +00:00
/// #[derive(Reflect)]
/// #[reflect(PartialEq, Serialize, Deserialize, Default)]
/// enum ThingThatImReflecting<T1, T2, T3> {
/// A(T1), // |
/// B, // |- variants
/// C { foo: T2, bar: T3 } // |
/// }
/// ```
pub(crate) struct ReflectEnum<'a> {
meta: ReflectMeta<'a>,
variants: Vec<EnumVariant<'a>>,
}
bevy_reflect_derive: Tidying up the code (#4712) # Objective The `bevy_reflect_derive` crate is not the cleanest or easiest to follow/maintain. The `lib.rs` file is especially difficult with over 1000 lines of code written in a confusing order. This is just a result of growth within the crate and it would be nice to clean it up for future work. ## Solution Split `bevy_reflect_derive` into many more submodules. The submodules include: * `container_attributes` - Code relating to container attributes * `derive_data` - Code relating to reflection-based derive metadata * `field_attributes` - Code relating to field attributes * `impls` - Code containing actual reflection implementations * `reflect_value` - Code relating to reflection-based value metadata * `registration` - Code relating to type registration * `utility` - General-purpose utility functions This leaves the `lib.rs` file to contain only the public macros, making it much easier to digest (and fewer than 200 lines). By breaking up the code into smaller modules, we make it easier for future contributors to find the code they're looking for or identify which module best fits their own additions. ### Metadata Structs This cleanup also adds two big metadata structs: `ReflectFieldAttr` and `ReflectDeriveData`. The former is used to store all attributes for a struct field (if any). The latter is used to store all metadata for struct-based derive inputs. Both significantly reduce code duplication and make editing these macros much simpler. The tradeoff is that we may collect more metadata than needed. However, this is usually a small thing (such as checking for attributes when they're not really needed or creating a `ReflectFieldAttr` for every field regardless of whether they actually have an attribute). We could try to remove these tradeoffs and squeeze some more performance out, but doing so might come at the cost of developer experience. Personally, I think it's much nicer to create a `ReflectFieldAttr` for every field since it means I don't have to do two `Option` checks. Others may disagree, though, and so we can discuss changing this either in this PR or in a future one. ### Out of Scope _Some_ documentation has been added or improved, but ultimately good docs are probably best saved for a dedicated PR. ## 🔍 Focus Points (for reviewers) I know it's a lot to sift through, so here is a list of **key points for reviewers**: - The following files contain code that was mostly just relocated: - `reflect_value.rs` - `registration.rs` - `container_attributes.rs` was also mostly moved but features some general cleanup (reducing nesting, removing hardcoded strings, etc.) and lots of doc comments - Most impl logic was moved from `lib.rs` to `impls.rs`, but they have been significantly modified to use the new `ReflectDeriveData` metadata struct in order to reduce duplication. - `derive_data.rs` and `field_attributes.rs` contain almost entirely new code and should probably be given the most attention. - Likewise, `from_reflect.rs` saw major changes using `ReflectDeriveData` so it should also be given focus. - There was no change to the `lib.rs` exports so the end-user API should be the same. ## Prior Work This task was initially tackled by @NathanSWard in #2377 (which was closed in favor of this PR), so hats off to them for beating me to the punch by nearly a year! --- ## Changelog * **[INTERNAL]** Split `bevy_reflect_derive` into smaller submodules * **[INTERNAL]** Add `ReflectFieldAttr` * **[INTERNAL]** Add `ReflectDeriveData` * Add `BevyManifest::get_path_direct()` method (`bevy_macro_utils`) Co-authored-by: MrGVSV <49806985+MrGVSV@users.noreply.github.com>
2022-05-12 19:43:23 +00:00
bevy_reflect: Reflect enums (#4761) # Objective > This is a revival of #1347. Credit for the original PR should go to @Davier. Currently, enums are treated as `ReflectRef::Value` types by `bevy_reflect`. Obviously, there needs to be better a better representation for enums using the reflection API. ## Solution Based on prior work from @Davier, an `Enum` trait has been added as well as the ability to automatically implement it via the `Reflect` derive macro. This allows enums to be expressed dynamically: ```rust #[derive(Reflect)] enum Foo { A, B(usize), C { value: f32 }, } let mut foo = Foo::B(123); assert_eq!("B", foo.variant_name()); assert_eq!(1, foo.field_len()); let new_value = DynamicEnum::from(Foo::C { value: 1.23 }); foo.apply(&new_value); assert_eq!(Foo::C{value: 1.23}, foo); ``` ### Features #### Derive Macro Use the `#[derive(Reflect)]` macro to automatically implement the `Enum` trait for enum definitions. Optionally, you can use `#[reflect(ignore)]` with both variants and variant fields, just like you can with structs. These ignored items will not be considered as part of the reflection and cannot be accessed via reflection. ```rust #[derive(Reflect)] enum TestEnum { A, // Uncomment to ignore all of `B` // #[reflect(ignore)] B(usize), C { // Uncomment to ignore only field `foo` of `C` // #[reflect(ignore)] foo: f32, bar: bool, }, } ``` #### Dynamic Enums Enums may be created/represented dynamically via the `DynamicEnum` struct. The main purpose of this struct is to allow enums to be deserialized into a partial state and to allow dynamic patching. In order to ensure conversion from a `DynamicEnum` to a concrete enum type goes smoothly, be sure to add `FromReflect` to your derive macro. ```rust let mut value = TestEnum::A; // Create from a concrete instance let dyn_enum = DynamicEnum::from(TestEnum::B(123)); value.apply(&dyn_enum); assert_eq!(TestEnum::B(123), value); // Create a purely dynamic instance let dyn_enum = DynamicEnum::new("TestEnum", "A", ()); value.apply(&dyn_enum); assert_eq!(TestEnum::A, value); ``` #### Variants An enum value is always represented as one of its variants— never the enum in its entirety. ```rust let value = TestEnum::A; assert_eq!("A", value.variant_name()); // Since we are using the `A` variant, we cannot also be the `B` variant assert_ne!("B", value.variant_name()); ``` All variant types are representable within the `Enum` trait: unit, struct, and tuple. You can get the current type like: ```rust match value.variant_type() { VariantType::Unit => println!("A unit variant!"), VariantType::Struct => println!("A struct variant!"), VariantType::Tuple => println!("A tuple variant!"), } ``` > Notice that they don't contain any values representing the fields. These are purely tags. If a variant has them, you can access the fields as well: ```rust let mut value = TestEnum::C { foo: 1.23, bar: false }; // Read/write specific fields *value.field_mut("bar").unwrap() = true; // Iterate over the entire collection of fields for field in value.iter_fields() { println!("{} = {:?}", field.name(), field.value()); } ``` #### Variant Swapping It might seem odd to group all variant types under a single trait (why allow `iter_fields` on a unit variant?), but the reason this was done ~~is to easily allow *variant swapping*.~~ As I was recently drafting up the **Design Decisions** section, I discovered that other solutions could have been made to work with variant swapping. So while there are reasons to keep the all-in-one approach, variant swapping is _not_ one of them. ```rust let mut value: Box<dyn Enum> = Box::new(TestEnum::A); value.set(Box::new(TestEnum::B(123))).unwrap(); ``` #### Serialization Enums can be serialized and deserialized via reflection without needing to implement `Serialize` or `Deserialize` themselves (which can save thousands of lines of generated code). Below are the ways an enum can be serialized. > Note, like the rest of reflection-based serialization, the order of the keys in these representations is important! ##### Unit ```json { "type": "my_crate::TestEnum", "enum": { "variant": "A" } } ``` ##### Tuple ```json { "type": "my_crate::TestEnum", "enum": { "variant": "B", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` <details> <summary>Effects on Option</summary> This ends up making `Option` look a little ugly: ```json { "type": "core::option::Option<usize>", "enum": { "variant": "Some", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` </details> ##### Struct ```json { "type": "my_crate::TestEnum", "enum": { "variant": "C", "struct": { "foo": { "type": "f32", "value": 1.23 }, "bar": { "type": "bool", "value": false } } } } ``` ## Design Decisions <details> <summary><strong>View Section</strong></summary> This section is here to provide some context for why certain decisions were made for this PR, alternatives that could have been used instead, and what could be improved upon in the future. ### Variant Representation One of the biggest decisions was to decide on how to represent variants. The current design uses a "all-in-one" design where unit, tuple, and struct variants are all simultaneously represented by the `Enum` trait. This is not the only way it could have been done, though. #### Alternatives ##### 1. Variant Traits One way of representing variants would be to define traits for each variant, implementing them whenever an enum featured at least one instance of them. This would allow us to define variants like: ```rust pub trait Enum: Reflect { fn variant(&self) -> Variant; } pub enum Variant<'a> { Unit, Tuple(&'a dyn TupleVariant), Struct(&'a dyn StructVariant), } pub trait TupleVariant { fn field_len(&self) -> usize; // ... } ``` And then do things like: ```rust fn get_tuple_len(foo: &dyn Enum) -> usize { match foo.variant() { Variant::Tuple(tuple) => tuple.field_len(), _ => panic!("not a tuple variant!") } } ``` The reason this PR does not go with this approach is because of the fact that variants are not separate types. In other words, we cannot implement traits on specific variants— these cover the *entire* enum. This means we offer an easy footgun: ```rust let foo: Option<i32> = None; let my_enum = Box::new(foo) as Box<dyn TupleVariant>; ``` Here, `my_enum` contains `foo`, which is a unit variant. However, since we need to implement `TupleVariant` for `Option` as a whole, it's possible to perform such a cast. This is obviously wrong, but could easily go unnoticed. So unfortunately, this makes it not a good candidate for representing variants. ##### 2. Variant Structs To get around the issue of traits necessarily needing to apply to both the enum and its variants, we could instead use structs that are created on a per-variant basis. This was also considered but was ultimately [[removed](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c)](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c) due to concerns about allocations. Each variant struct would probably look something like: ```rust pub trait Enum: Reflect { fn variant_mut(&self) -> VariantMut; } pub enum VariantMut<'a> { Unit, Tuple(TupleVariantMut), Struct(StructVariantMut), } struct StructVariantMut<'a> { fields: Vec<&'a mut dyn Reflect>, field_indices: HashMap<Cow<'static, str>, usize> } ``` This allows us to isolate struct variants into their own defined struct and define methods specifically for their use. It also prevents users from casting to it since it's not a trait. However, this is not an optimal solution. Both `field_indices` and `fields` will require an allocation (remember, a `Box<[T]>` still requires a `Vec<T>` in order to be constructed). This *might* be a problem if called frequently enough. ##### 3. Generated Structs The original design, implemented by @Davier, instead generates structs specific for each variant. So if we had a variant path like `Foo::Bar`, we'd generate a struct named `FooBarWrapper`. This would be newtyped around the original enum and forward tuple or struct methods to the enum with the chosen variant. Because it involved using the `Tuple` and `Struct` traits (which are also both bound on `Reflect`), this meant a bit more code had to be generated. For a single struct variant with one field, the generated code amounted to ~110LoC. However, each new field added to that variant only added ~6 more LoC. In order to work properly, the enum had to be transmuted to the generated struct: ```rust fn variant(&self) -> crate::EnumVariant<'_> { match self { Foo::Bar {value: i32} => { let wrapper_ref = unsafe { std::mem::transmute::<&Self, &FooBarWrapper>(self) }; crate::EnumVariant::Struct(wrapper_ref as &dyn crate::Struct) } } } ``` This works because `FooBarWrapper` is defined as `repr(transparent)`. Out of all the alternatives, this would probably be the one most likely to be used again in the future. The reasons for why this PR did not continue to use it was because: * To reduce generated code (which would hopefully speed up compile times) * To avoid cluttering the code with generated structs not visible to the user * To keep bevy_reflect simple and extensible (these generated structs act as proxies and might not play well with current or future systems) * To avoid additional unsafe blocks * My own misunderstanding of @Davier's code That last point is obviously on me. I misjudged the code to be too unsafe and unable to handle variant swapping (which it probably could) when I was rebasing it. Looking over it again when writing up this whole section, I see that it was actually a pretty clever way of handling variant representation. #### Benefits of All-in-One As stated before, the current implementation uses an all-in-one approach. All variants are capable of containing fields as far as `Enum` is concerned. This provides a few benefits that the alternatives do not (reduced indirection, safer code, etc.). The biggest benefit, though, is direct field access. Rather than forcing users to have to go through pattern matching, we grant direct access to the fields contained by the current variant. The reason we can do this is because all of the pattern matching happens internally. Getting the field at index `2` will automatically return `Some(...)` for the current variant if it has a field at that index or `None` if it doesn't (or can't). This could be useful for scenarios where the variant has already been verified or just set/swapped (or even where the type of variant doesn't matter): ```rust let dyn_enum: &mut dyn Enum = &mut Foo::Bar {value: 123}; // We know it's the `Bar` variant let field = dyn_enum.field("value").unwrap(); ``` Reflection is not a type-safe abstraction— almost every return value is wrapped in `Option<...>`. There are plenty of places to check and recheck that a value is what Reflect says it is. Forcing users to have to go through `match` each time they want to access a field might just be an extra step among dozens of other verification processes. Some might disagree, but ultimately, my view is that the benefit here is an improvement to the ergonomics and usability of reflected enums. </details> --- ## Changelog ### Added * Added `Enum` trait * Added `Enum` impl to `Reflect` derive macro * Added `DynamicEnum` struct * Added `DynamicVariant` * Added `EnumInfo` * Added `VariantInfo` * Added `StructVariantInfo` * Added `TupleVariantInfo` * Added `UnitVariantInfo` * Added serializtion/deserialization support for enums * Added `EnumSerializer` * Added `VariantType` * Added `VariantFieldIter` * Added `VariantField` * Added `enum_partial_eq(...)` * Added `enum_hash(...)` ### Changed * `Option<T>` now implements `Enum` * `bevy_window` now depends on `bevy_reflect` * Implemented `Reflect` and `FromReflect` for `WindowId` * Derive `FromReflect` on `PerspectiveProjection` * Derive `FromReflect` on `OrthographicProjection` * Derive `FromReflect` on `WindowOrigin` * Derive `FromReflect` on `ScalingMode` * Derive `FromReflect` on `DepthCalculation` ## Migration Guide * Enums no longer need to be treated as values and usages of `#[reflect_value(...)]` can be removed or replaced by `#[reflect(...)]` * Enums (including `Option<T>`) now take a different format when serializing. The format is described above, but this may cause issues for existing scenes that make use of enums. --- Also shout out to @nicopap for helping clean up some of the code here! It's a big feature so help like this is really appreciated! Co-authored-by: Gino Valente <gino.valente.code@gmail.com>
2022-08-02 22:14:41 +00:00
/// Represents a field on a struct or tuple struct.
bevy_reflect: `FromReflect` Ergonomics Implementation (#6056) # Objective **This implementation is based on https://github.com/bevyengine/rfcs/pull/59.** --- Resolves #4597 Full details and motivation can be found in the RFC, but here's a brief summary. `FromReflect` is a very powerful and important trait within the reflection API. It allows Dynamic types (e.g., `DynamicList`, etc.) to be formed into Real ones (e.g., `Vec<i32>`, etc.). This mainly comes into play concerning deserialization, where the reflection deserializers both return a `Box<dyn Reflect>` that almost always contain one of these Dynamic representations of a Real type. To convert this to our Real type, we need to use `FromReflect`. It also sneaks up in other ways. For example, it's a required bound for `T` in `Vec<T>` so that `Vec<T>` as a whole can be made `FromReflect`. It's also required by all fields of an enum as it's used as part of the `Reflect::apply` implementation. So in other words, much like `GetTypeRegistration` and `Typed`, it is very much a core reflection trait. The problem is that it is not currently treated like a core trait and is not automatically derived alongside `Reflect`. This makes using it a bit cumbersome and easy to forget. ## Solution Automatically derive `FromReflect` when deriving `Reflect`. Users can then choose to opt-out if needed using the `#[reflect(from_reflect = false)]` attribute. ```rust #[derive(Reflect)] struct Foo; #[derive(Reflect)] #[reflect(from_reflect = false)] struct Bar; fn test<T: FromReflect>(value: T) {} test(Foo); // <-- OK test(Bar); // <-- Panic! Bar does not implement trait `FromReflect` ``` #### `ReflectFromReflect` This PR also automatically adds the `ReflectFromReflect` (introduced in #6245) registration to the derived `GetTypeRegistration` impl— if the type hasn't opted out of `FromReflect` of course. <details> <summary><h4>Improved Deserialization</h4></summary> > **Warning** > This section includes changes that have since been descoped from this PR. They will likely be implemented again in a followup PR. I am mainly leaving these details in for archival purposes, as well as for reference when implementing this logic again. And since we can do all the above, we might as well improve deserialization. We can now choose to deserialize into a Dynamic type or automatically convert it using `FromReflect` under the hood. `[Un]TypedReflectDeserializer::new` will now perform the conversion and return the `Box`'d Real type. `[Un]TypedReflectDeserializer::new_dynamic` will work like what we have now and simply return the `Box`'d Dynamic type. ```rust // Returns the Real type let reflect_deserializer = UntypedReflectDeserializer::new(&registry); let mut deserializer = ron::de::Deserializer::from_str(input)?; let output: SomeStruct = reflect_deserializer.deserialize(&mut deserializer)?.take()?; // Returns the Dynamic type let reflect_deserializer = UntypedReflectDeserializer::new_dynamic(&registry); let mut deserializer = ron::de::Deserializer::from_str(input)?; let output: DynamicStruct = reflect_deserializer.deserialize(&mut deserializer)?.take()?; ``` </details> --- ## Changelog * `FromReflect` is now automatically derived within the `Reflect` derive macro * This includes auto-registering `ReflectFromReflect` in the derived `GetTypeRegistration` impl * ~~Renamed `TypedReflectDeserializer::new` and `UntypedReflectDeserializer::new` to `TypedReflectDeserializer::new_dynamic` and `UntypedReflectDeserializer::new_dynamic`, respectively~~ **Descoped** * ~~Changed `TypedReflectDeserializer::new` and `UntypedReflectDeserializer::new` to automatically convert the deserialized output using `FromReflect`~~ **Descoped** ## Migration Guide * `FromReflect` is now automatically derived within the `Reflect` derive macro. Items with both derives will need to remove the `FromReflect` one. ```rust // OLD #[derive(Reflect, FromReflect)] struct Foo; // NEW #[derive(Reflect)] struct Foo; ``` If using a manual implementation of `FromReflect` and the `Reflect` derive, users will need to opt-out of the automatic implementation. ```rust // OLD #[derive(Reflect)] struct Foo; impl FromReflect for Foo {/* ... */} // NEW #[derive(Reflect)] #[reflect(from_reflect = false)] struct Foo; impl FromReflect for Foo {/* ... */} ``` <details> <summary><h4>Removed Migrations</h4></summary> > **Warning** > This section includes changes that have since been descoped from this PR. They will likely be implemented again in a followup PR. I am mainly leaving these details in for archival purposes, as well as for reference when implementing this logic again. * The reflect deserializers now perform a `FromReflect` conversion internally. The expected output of `TypedReflectDeserializer::new` and `UntypedReflectDeserializer::new` is no longer a Dynamic (e.g., `DynamicList`), but its Real counterpart (e.g., `Vec<i32>`). ```rust let reflect_deserializer = UntypedReflectDeserializer::new_dynamic(&registry); let mut deserializer = ron::de::Deserializer::from_str(input)?; // OLD let output: DynamicStruct = reflect_deserializer.deserialize(&mut deserializer)?.take()?; // NEW let output: SomeStruct = reflect_deserializer.deserialize(&mut deserializer)?.take()?; ``` Alternatively, if this behavior isn't desired, use the `TypedReflectDeserializer::new_dynamic` and `UntypedReflectDeserializer::new_dynamic` methods instead: ```rust // OLD let reflect_deserializer = UntypedReflectDeserializer::new(&registry); // NEW let reflect_deserializer = UntypedReflectDeserializer::new_dynamic(&registry); ``` </details> --------- Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2023-06-29 01:31:34 +00:00
#[derive(Clone)]
bevy_reflect: Reflect enums (#4761) # Objective > This is a revival of #1347. Credit for the original PR should go to @Davier. Currently, enums are treated as `ReflectRef::Value` types by `bevy_reflect`. Obviously, there needs to be better a better representation for enums using the reflection API. ## Solution Based on prior work from @Davier, an `Enum` trait has been added as well as the ability to automatically implement it via the `Reflect` derive macro. This allows enums to be expressed dynamically: ```rust #[derive(Reflect)] enum Foo { A, B(usize), C { value: f32 }, } let mut foo = Foo::B(123); assert_eq!("B", foo.variant_name()); assert_eq!(1, foo.field_len()); let new_value = DynamicEnum::from(Foo::C { value: 1.23 }); foo.apply(&new_value); assert_eq!(Foo::C{value: 1.23}, foo); ``` ### Features #### Derive Macro Use the `#[derive(Reflect)]` macro to automatically implement the `Enum` trait for enum definitions. Optionally, you can use `#[reflect(ignore)]` with both variants and variant fields, just like you can with structs. These ignored items will not be considered as part of the reflection and cannot be accessed via reflection. ```rust #[derive(Reflect)] enum TestEnum { A, // Uncomment to ignore all of `B` // #[reflect(ignore)] B(usize), C { // Uncomment to ignore only field `foo` of `C` // #[reflect(ignore)] foo: f32, bar: bool, }, } ``` #### Dynamic Enums Enums may be created/represented dynamically via the `DynamicEnum` struct. The main purpose of this struct is to allow enums to be deserialized into a partial state and to allow dynamic patching. In order to ensure conversion from a `DynamicEnum` to a concrete enum type goes smoothly, be sure to add `FromReflect` to your derive macro. ```rust let mut value = TestEnum::A; // Create from a concrete instance let dyn_enum = DynamicEnum::from(TestEnum::B(123)); value.apply(&dyn_enum); assert_eq!(TestEnum::B(123), value); // Create a purely dynamic instance let dyn_enum = DynamicEnum::new("TestEnum", "A", ()); value.apply(&dyn_enum); assert_eq!(TestEnum::A, value); ``` #### Variants An enum value is always represented as one of its variants— never the enum in its entirety. ```rust let value = TestEnum::A; assert_eq!("A", value.variant_name()); // Since we are using the `A` variant, we cannot also be the `B` variant assert_ne!("B", value.variant_name()); ``` All variant types are representable within the `Enum` trait: unit, struct, and tuple. You can get the current type like: ```rust match value.variant_type() { VariantType::Unit => println!("A unit variant!"), VariantType::Struct => println!("A struct variant!"), VariantType::Tuple => println!("A tuple variant!"), } ``` > Notice that they don't contain any values representing the fields. These are purely tags. If a variant has them, you can access the fields as well: ```rust let mut value = TestEnum::C { foo: 1.23, bar: false }; // Read/write specific fields *value.field_mut("bar").unwrap() = true; // Iterate over the entire collection of fields for field in value.iter_fields() { println!("{} = {:?}", field.name(), field.value()); } ``` #### Variant Swapping It might seem odd to group all variant types under a single trait (why allow `iter_fields` on a unit variant?), but the reason this was done ~~is to easily allow *variant swapping*.~~ As I was recently drafting up the **Design Decisions** section, I discovered that other solutions could have been made to work with variant swapping. So while there are reasons to keep the all-in-one approach, variant swapping is _not_ one of them. ```rust let mut value: Box<dyn Enum> = Box::new(TestEnum::A); value.set(Box::new(TestEnum::B(123))).unwrap(); ``` #### Serialization Enums can be serialized and deserialized via reflection without needing to implement `Serialize` or `Deserialize` themselves (which can save thousands of lines of generated code). Below are the ways an enum can be serialized. > Note, like the rest of reflection-based serialization, the order of the keys in these representations is important! ##### Unit ```json { "type": "my_crate::TestEnum", "enum": { "variant": "A" } } ``` ##### Tuple ```json { "type": "my_crate::TestEnum", "enum": { "variant": "B", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` <details> <summary>Effects on Option</summary> This ends up making `Option` look a little ugly: ```json { "type": "core::option::Option<usize>", "enum": { "variant": "Some", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` </details> ##### Struct ```json { "type": "my_crate::TestEnum", "enum": { "variant": "C", "struct": { "foo": { "type": "f32", "value": 1.23 }, "bar": { "type": "bool", "value": false } } } } ``` ## Design Decisions <details> <summary><strong>View Section</strong></summary> This section is here to provide some context for why certain decisions were made for this PR, alternatives that could have been used instead, and what could be improved upon in the future. ### Variant Representation One of the biggest decisions was to decide on how to represent variants. The current design uses a "all-in-one" design where unit, tuple, and struct variants are all simultaneously represented by the `Enum` trait. This is not the only way it could have been done, though. #### Alternatives ##### 1. Variant Traits One way of representing variants would be to define traits for each variant, implementing them whenever an enum featured at least one instance of them. This would allow us to define variants like: ```rust pub trait Enum: Reflect { fn variant(&self) -> Variant; } pub enum Variant<'a> { Unit, Tuple(&'a dyn TupleVariant), Struct(&'a dyn StructVariant), } pub trait TupleVariant { fn field_len(&self) -> usize; // ... } ``` And then do things like: ```rust fn get_tuple_len(foo: &dyn Enum) -> usize { match foo.variant() { Variant::Tuple(tuple) => tuple.field_len(), _ => panic!("not a tuple variant!") } } ``` The reason this PR does not go with this approach is because of the fact that variants are not separate types. In other words, we cannot implement traits on specific variants— these cover the *entire* enum. This means we offer an easy footgun: ```rust let foo: Option<i32> = None; let my_enum = Box::new(foo) as Box<dyn TupleVariant>; ``` Here, `my_enum` contains `foo`, which is a unit variant. However, since we need to implement `TupleVariant` for `Option` as a whole, it's possible to perform such a cast. This is obviously wrong, but could easily go unnoticed. So unfortunately, this makes it not a good candidate for representing variants. ##### 2. Variant Structs To get around the issue of traits necessarily needing to apply to both the enum and its variants, we could instead use structs that are created on a per-variant basis. This was also considered but was ultimately [[removed](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c)](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c) due to concerns about allocations. Each variant struct would probably look something like: ```rust pub trait Enum: Reflect { fn variant_mut(&self) -> VariantMut; } pub enum VariantMut<'a> { Unit, Tuple(TupleVariantMut), Struct(StructVariantMut), } struct StructVariantMut<'a> { fields: Vec<&'a mut dyn Reflect>, field_indices: HashMap<Cow<'static, str>, usize> } ``` This allows us to isolate struct variants into their own defined struct and define methods specifically for their use. It also prevents users from casting to it since it's not a trait. However, this is not an optimal solution. Both `field_indices` and `fields` will require an allocation (remember, a `Box<[T]>` still requires a `Vec<T>` in order to be constructed). This *might* be a problem if called frequently enough. ##### 3. Generated Structs The original design, implemented by @Davier, instead generates structs specific for each variant. So if we had a variant path like `Foo::Bar`, we'd generate a struct named `FooBarWrapper`. This would be newtyped around the original enum and forward tuple or struct methods to the enum with the chosen variant. Because it involved using the `Tuple` and `Struct` traits (which are also both bound on `Reflect`), this meant a bit more code had to be generated. For a single struct variant with one field, the generated code amounted to ~110LoC. However, each new field added to that variant only added ~6 more LoC. In order to work properly, the enum had to be transmuted to the generated struct: ```rust fn variant(&self) -> crate::EnumVariant<'_> { match self { Foo::Bar {value: i32} => { let wrapper_ref = unsafe { std::mem::transmute::<&Self, &FooBarWrapper>(self) }; crate::EnumVariant::Struct(wrapper_ref as &dyn crate::Struct) } } } ``` This works because `FooBarWrapper` is defined as `repr(transparent)`. Out of all the alternatives, this would probably be the one most likely to be used again in the future. The reasons for why this PR did not continue to use it was because: * To reduce generated code (which would hopefully speed up compile times) * To avoid cluttering the code with generated structs not visible to the user * To keep bevy_reflect simple and extensible (these generated structs act as proxies and might not play well with current or future systems) * To avoid additional unsafe blocks * My own misunderstanding of @Davier's code That last point is obviously on me. I misjudged the code to be too unsafe and unable to handle variant swapping (which it probably could) when I was rebasing it. Looking over it again when writing up this whole section, I see that it was actually a pretty clever way of handling variant representation. #### Benefits of All-in-One As stated before, the current implementation uses an all-in-one approach. All variants are capable of containing fields as far as `Enum` is concerned. This provides a few benefits that the alternatives do not (reduced indirection, safer code, etc.). The biggest benefit, though, is direct field access. Rather than forcing users to have to go through pattern matching, we grant direct access to the fields contained by the current variant. The reason we can do this is because all of the pattern matching happens internally. Getting the field at index `2` will automatically return `Some(...)` for the current variant if it has a field at that index or `None` if it doesn't (or can't). This could be useful for scenarios where the variant has already been verified or just set/swapped (or even where the type of variant doesn't matter): ```rust let dyn_enum: &mut dyn Enum = &mut Foo::Bar {value: 123}; // We know it's the `Bar` variant let field = dyn_enum.field("value").unwrap(); ``` Reflection is not a type-safe abstraction— almost every return value is wrapped in `Option<...>`. There are plenty of places to check and recheck that a value is what Reflect says it is. Forcing users to have to go through `match` each time they want to access a field might just be an extra step among dozens of other verification processes. Some might disagree, but ultimately, my view is that the benefit here is an improvement to the ergonomics and usability of reflected enums. </details> --- ## Changelog ### Added * Added `Enum` trait * Added `Enum` impl to `Reflect` derive macro * Added `DynamicEnum` struct * Added `DynamicVariant` * Added `EnumInfo` * Added `VariantInfo` * Added `StructVariantInfo` * Added `TupleVariantInfo` * Added `UnitVariantInfo` * Added serializtion/deserialization support for enums * Added `EnumSerializer` * Added `VariantType` * Added `VariantFieldIter` * Added `VariantField` * Added `enum_partial_eq(...)` * Added `enum_hash(...)` ### Changed * `Option<T>` now implements `Enum` * `bevy_window` now depends on `bevy_reflect` * Implemented `Reflect` and `FromReflect` for `WindowId` * Derive `FromReflect` on `PerspectiveProjection` * Derive `FromReflect` on `OrthographicProjection` * Derive `FromReflect` on `WindowOrigin` * Derive `FromReflect` on `ScalingMode` * Derive `FromReflect` on `DepthCalculation` ## Migration Guide * Enums no longer need to be treated as values and usages of `#[reflect_value(...)]` can be removed or replaced by `#[reflect(...)]` * Enums (including `Option<T>`) now take a different format when serializing. The format is described above, but this may cause issues for existing scenes that make use of enums. --- Also shout out to @nicopap for helping clean up some of the code here! It's a big feature so help like this is really appreciated! Co-authored-by: Gino Valente <gino.valente.code@gmail.com>
2022-08-02 22:14:41 +00:00
pub(crate) struct StructField<'a> {
/// The raw field.
pub data: &'a Field,
/// The reflection-based attributes on the field.
bevy_reflect_derive: Clean up attribute logic (#11777) # Objective The code in `bevy_reflect_derive` could use some cleanup. ## Solution Took some of the changes in #11659 to create a dedicated PR for cleaning up the field and container attribute logic. #### Updated Naming I renamed `ReflectTraits` and `ReflectFieldAttr` to `ContainerAttributes` and `FieldAttributes`, respectively. I think these are clearer. #### Updated Parsing ##### Readability The parsing logic wasn't too bad before, but it was getting difficult to read. There was some duplicated logic between `Meta::List` and `Meta::Path` attributes. Additionally, all the logic was kept inside a large method. To simply things, I replaced the nested meta parsing with `ParseStream` parsing. In my opinion, this is easier to follow since it breaks up the large match statement into a small set of single-line if statements, where each if-block contains a single call to the appropriate attribute parsing method. ##### Flexibility On top of the added simplicity, this also makes our attribute parsing much more flexible. It allows us to more elegantly handle custom where clauses (i.e. `#[reflect(where T: Foo)]`) and it opens the door for more non-standard attribute syntax (e.g. #11659). ##### Errors This also allows us to automatically provide certain errors when parsing. For example, since we can use `stream.lookahead1()`, we get errors like the following for free: ``` error: expected one of: `ignore`, `skip_serializing`, `default` --> crates/bevy_reflect/src/lib.rs:1988:23 | 1988 | #[reflect(foo)] | ^^^ ``` --- ## Changelog > [!note] > All changes are internal to `bevy_reflect_derive` and should not affect the public API[^1]. - Renamed `ReflectTraits` to `ContainerAttributes` - Renamed `ReflectMeta::traits` to `ReflectMeta::attrs` - Renamed `ReflectFieldAttr` to `FieldAttributes` - Updated parsing logic for field/container attribute parsing - Now uses a `ParseStream` directly instead of nested meta parsing - General code cleanup of the field/container attribute modules for `bevy_reflect_derive` [^1]: Does not include errors, which may look slightly different. --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
2024-02-12 15:16:27 +00:00
pub attrs: FieldAttributes,
bevy_reflect: Reflect enums (#4761) # Objective > This is a revival of #1347. Credit for the original PR should go to @Davier. Currently, enums are treated as `ReflectRef::Value` types by `bevy_reflect`. Obviously, there needs to be better a better representation for enums using the reflection API. ## Solution Based on prior work from @Davier, an `Enum` trait has been added as well as the ability to automatically implement it via the `Reflect` derive macro. This allows enums to be expressed dynamically: ```rust #[derive(Reflect)] enum Foo { A, B(usize), C { value: f32 }, } let mut foo = Foo::B(123); assert_eq!("B", foo.variant_name()); assert_eq!(1, foo.field_len()); let new_value = DynamicEnum::from(Foo::C { value: 1.23 }); foo.apply(&new_value); assert_eq!(Foo::C{value: 1.23}, foo); ``` ### Features #### Derive Macro Use the `#[derive(Reflect)]` macro to automatically implement the `Enum` trait for enum definitions. Optionally, you can use `#[reflect(ignore)]` with both variants and variant fields, just like you can with structs. These ignored items will not be considered as part of the reflection and cannot be accessed via reflection. ```rust #[derive(Reflect)] enum TestEnum { A, // Uncomment to ignore all of `B` // #[reflect(ignore)] B(usize), C { // Uncomment to ignore only field `foo` of `C` // #[reflect(ignore)] foo: f32, bar: bool, }, } ``` #### Dynamic Enums Enums may be created/represented dynamically via the `DynamicEnum` struct. The main purpose of this struct is to allow enums to be deserialized into a partial state and to allow dynamic patching. In order to ensure conversion from a `DynamicEnum` to a concrete enum type goes smoothly, be sure to add `FromReflect` to your derive macro. ```rust let mut value = TestEnum::A; // Create from a concrete instance let dyn_enum = DynamicEnum::from(TestEnum::B(123)); value.apply(&dyn_enum); assert_eq!(TestEnum::B(123), value); // Create a purely dynamic instance let dyn_enum = DynamicEnum::new("TestEnum", "A", ()); value.apply(&dyn_enum); assert_eq!(TestEnum::A, value); ``` #### Variants An enum value is always represented as one of its variants— never the enum in its entirety. ```rust let value = TestEnum::A; assert_eq!("A", value.variant_name()); // Since we are using the `A` variant, we cannot also be the `B` variant assert_ne!("B", value.variant_name()); ``` All variant types are representable within the `Enum` trait: unit, struct, and tuple. You can get the current type like: ```rust match value.variant_type() { VariantType::Unit => println!("A unit variant!"), VariantType::Struct => println!("A struct variant!"), VariantType::Tuple => println!("A tuple variant!"), } ``` > Notice that they don't contain any values representing the fields. These are purely tags. If a variant has them, you can access the fields as well: ```rust let mut value = TestEnum::C { foo: 1.23, bar: false }; // Read/write specific fields *value.field_mut("bar").unwrap() = true; // Iterate over the entire collection of fields for field in value.iter_fields() { println!("{} = {:?}", field.name(), field.value()); } ``` #### Variant Swapping It might seem odd to group all variant types under a single trait (why allow `iter_fields` on a unit variant?), but the reason this was done ~~is to easily allow *variant swapping*.~~ As I was recently drafting up the **Design Decisions** section, I discovered that other solutions could have been made to work with variant swapping. So while there are reasons to keep the all-in-one approach, variant swapping is _not_ one of them. ```rust let mut value: Box<dyn Enum> = Box::new(TestEnum::A); value.set(Box::new(TestEnum::B(123))).unwrap(); ``` #### Serialization Enums can be serialized and deserialized via reflection without needing to implement `Serialize` or `Deserialize` themselves (which can save thousands of lines of generated code). Below are the ways an enum can be serialized. > Note, like the rest of reflection-based serialization, the order of the keys in these representations is important! ##### Unit ```json { "type": "my_crate::TestEnum", "enum": { "variant": "A" } } ``` ##### Tuple ```json { "type": "my_crate::TestEnum", "enum": { "variant": "B", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` <details> <summary>Effects on Option</summary> This ends up making `Option` look a little ugly: ```json { "type": "core::option::Option<usize>", "enum": { "variant": "Some", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` </details> ##### Struct ```json { "type": "my_crate::TestEnum", "enum": { "variant": "C", "struct": { "foo": { "type": "f32", "value": 1.23 }, "bar": { "type": "bool", "value": false } } } } ``` ## Design Decisions <details> <summary><strong>View Section</strong></summary> This section is here to provide some context for why certain decisions were made for this PR, alternatives that could have been used instead, and what could be improved upon in the future. ### Variant Representation One of the biggest decisions was to decide on how to represent variants. The current design uses a "all-in-one" design where unit, tuple, and struct variants are all simultaneously represented by the `Enum` trait. This is not the only way it could have been done, though. #### Alternatives ##### 1. Variant Traits One way of representing variants would be to define traits for each variant, implementing them whenever an enum featured at least one instance of them. This would allow us to define variants like: ```rust pub trait Enum: Reflect { fn variant(&self) -> Variant; } pub enum Variant<'a> { Unit, Tuple(&'a dyn TupleVariant), Struct(&'a dyn StructVariant), } pub trait TupleVariant { fn field_len(&self) -> usize; // ... } ``` And then do things like: ```rust fn get_tuple_len(foo: &dyn Enum) -> usize { match foo.variant() { Variant::Tuple(tuple) => tuple.field_len(), _ => panic!("not a tuple variant!") } } ``` The reason this PR does not go with this approach is because of the fact that variants are not separate types. In other words, we cannot implement traits on specific variants— these cover the *entire* enum. This means we offer an easy footgun: ```rust let foo: Option<i32> = None; let my_enum = Box::new(foo) as Box<dyn TupleVariant>; ``` Here, `my_enum` contains `foo`, which is a unit variant. However, since we need to implement `TupleVariant` for `Option` as a whole, it's possible to perform such a cast. This is obviously wrong, but could easily go unnoticed. So unfortunately, this makes it not a good candidate for representing variants. ##### 2. Variant Structs To get around the issue of traits necessarily needing to apply to both the enum and its variants, we could instead use structs that are created on a per-variant basis. This was also considered but was ultimately [[removed](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c)](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c) due to concerns about allocations. Each variant struct would probably look something like: ```rust pub trait Enum: Reflect { fn variant_mut(&self) -> VariantMut; } pub enum VariantMut<'a> { Unit, Tuple(TupleVariantMut), Struct(StructVariantMut), } struct StructVariantMut<'a> { fields: Vec<&'a mut dyn Reflect>, field_indices: HashMap<Cow<'static, str>, usize> } ``` This allows us to isolate struct variants into their own defined struct and define methods specifically for their use. It also prevents users from casting to it since it's not a trait. However, this is not an optimal solution. Both `field_indices` and `fields` will require an allocation (remember, a `Box<[T]>` still requires a `Vec<T>` in order to be constructed). This *might* be a problem if called frequently enough. ##### 3. Generated Structs The original design, implemented by @Davier, instead generates structs specific for each variant. So if we had a variant path like `Foo::Bar`, we'd generate a struct named `FooBarWrapper`. This would be newtyped around the original enum and forward tuple or struct methods to the enum with the chosen variant. Because it involved using the `Tuple` and `Struct` traits (which are also both bound on `Reflect`), this meant a bit more code had to be generated. For a single struct variant with one field, the generated code amounted to ~110LoC. However, each new field added to that variant only added ~6 more LoC. In order to work properly, the enum had to be transmuted to the generated struct: ```rust fn variant(&self) -> crate::EnumVariant<'_> { match self { Foo::Bar {value: i32} => { let wrapper_ref = unsafe { std::mem::transmute::<&Self, &FooBarWrapper>(self) }; crate::EnumVariant::Struct(wrapper_ref as &dyn crate::Struct) } } } ``` This works because `FooBarWrapper` is defined as `repr(transparent)`. Out of all the alternatives, this would probably be the one most likely to be used again in the future. The reasons for why this PR did not continue to use it was because: * To reduce generated code (which would hopefully speed up compile times) * To avoid cluttering the code with generated structs not visible to the user * To keep bevy_reflect simple and extensible (these generated structs act as proxies and might not play well with current or future systems) * To avoid additional unsafe blocks * My own misunderstanding of @Davier's code That last point is obviously on me. I misjudged the code to be too unsafe and unable to handle variant swapping (which it probably could) when I was rebasing it. Looking over it again when writing up this whole section, I see that it was actually a pretty clever way of handling variant representation. #### Benefits of All-in-One As stated before, the current implementation uses an all-in-one approach. All variants are capable of containing fields as far as `Enum` is concerned. This provides a few benefits that the alternatives do not (reduced indirection, safer code, etc.). The biggest benefit, though, is direct field access. Rather than forcing users to have to go through pattern matching, we grant direct access to the fields contained by the current variant. The reason we can do this is because all of the pattern matching happens internally. Getting the field at index `2` will automatically return `Some(...)` for the current variant if it has a field at that index or `None` if it doesn't (or can't). This could be useful for scenarios where the variant has already been verified or just set/swapped (or even where the type of variant doesn't matter): ```rust let dyn_enum: &mut dyn Enum = &mut Foo::Bar {value: 123}; // We know it's the `Bar` variant let field = dyn_enum.field("value").unwrap(); ``` Reflection is not a type-safe abstraction— almost every return value is wrapped in `Option<...>`. There are plenty of places to check and recheck that a value is what Reflect says it is. Forcing users to have to go through `match` each time they want to access a field might just be an extra step among dozens of other verification processes. Some might disagree, but ultimately, my view is that the benefit here is an improvement to the ergonomics and usability of reflected enums. </details> --- ## Changelog ### Added * Added `Enum` trait * Added `Enum` impl to `Reflect` derive macro * Added `DynamicEnum` struct * Added `DynamicVariant` * Added `EnumInfo` * Added `VariantInfo` * Added `StructVariantInfo` * Added `TupleVariantInfo` * Added `UnitVariantInfo` * Added serializtion/deserialization support for enums * Added `EnumSerializer` * Added `VariantType` * Added `VariantFieldIter` * Added `VariantField` * Added `enum_partial_eq(...)` * Added `enum_hash(...)` ### Changed * `Option<T>` now implements `Enum` * `bevy_window` now depends on `bevy_reflect` * Implemented `Reflect` and `FromReflect` for `WindowId` * Derive `FromReflect` on `PerspectiveProjection` * Derive `FromReflect` on `OrthographicProjection` * Derive `FromReflect` on `WindowOrigin` * Derive `FromReflect` on `ScalingMode` * Derive `FromReflect` on `DepthCalculation` ## Migration Guide * Enums no longer need to be treated as values and usages of `#[reflect_value(...)]` can be removed or replaced by `#[reflect(...)]` * Enums (including `Option<T>`) now take a different format when serializing. The format is described above, but this may cause issues for existing scenes that make use of enums. --- Also shout out to @nicopap for helping clean up some of the code here! It's a big feature so help like this is really appreciated! Co-authored-by: Gino Valente <gino.valente.code@gmail.com>
2022-08-02 22:14:41 +00:00
/// The index of this field within the struct.
bevy_reflect: Fix ignored/skipped field order (#7575) # Objective Fixes #5101 Alternative to #6511 ## Solution Corrected the behavior for ignored fields in `FromReflect`, which was previously using the incorrect field indexes. Similarly, fields marked with `#[reflect(skip_serializing)]` no longer break when using `FromReflect` after deserialization. This was done by modifying `SerializationData` to store a function pointer that can later be used to generate a default instance of the skipped field during deserialization. The function pointer points to a function generated by the derive macro using the behavior designated by `#[reflect(default)]` (or just `Default` if none provided). The entire output of the macro is now wrapped in an [unnamed constant](https://doc.rust-lang.org/stable/reference/items/constant-items.html#unnamed-constant) which keeps this behavior hygienic. #### Rationale The biggest downside to this approach is that it requires fields marked `#[reflect(skip_serializing)]` to provide the ability to create a default instance— either via a `Default` impl or by specifying a custom one. While this isn't great, I think it might be justified by the fact that we really need to create this value when using `FromReflect` on a deserialized object. And we need to do this _during_ deserialization because after that (at least for tuples and tuple structs) we lose information about which field is which: _"is the value at index 1 in this `DynamicTupleStruct` the actual value for index 1 or is it really the value for index 2 since index 1 is skippable...?"_ #### Alternatives An alternative would be to store `Option<Box<dyn Reflect>>` within `DynamicTuple` and `DynamicTupleStruct` instead of just `Box<dyn Reflect>`. This would allow us to insert "empty"/"missing" fields during deserialization, thus saving the positional information of the skipped fields. However, this may require changing the API of `Tuple` and `TupleStruct` such that they can account for their dynamic counterparts returning `None` for a skipped field. In practice this would probably mean exposing the `Option`-ness of the dynamics onto implementors via methods like `Tuple::drain` or `TupleStruct::field`. Personally, I think requiring `Default` would be better than muddying up the API to account for these special cases. But I'm open to trying out this other approach if the community feels that it's better. --- ## Changelog ### Public Changes #### Fixed - The behaviors of `#[reflect(ignore)]` and `#[reflect(skip_serializing)]` are no longer dependent on field order #### Changed - Fields marked with `#[reflect(skip_serializing)]` now need to either implement `Default` or specify a custom default function using `#[reflect(default = "path::to::some_func")]` - Deserializing a type with fields marked `#[reflect(skip_serializing)]` will now include that field initialized to its specified default value - `SerializationData::new` now takes the new `SkippedField` struct along with the skipped field index - Renamed `SerializationData::is_ignored_field` to `SerializationData::is_field_skipped` #### Added - Added `SkippedField` struct - Added methods `SerializationData::generate_default` and `SerializationData::iter_skipped` ### Internal Changes #### Changed - Replaced `members_to_serialization_denylist` and `BitSet<u32>` with `SerializationDataDef` - The `Reflect` derive is more hygienic as it now outputs within an [unnamed constant](https://doc.rust-lang.org/stable/reference/items/constant-items.html#unnamed-constant) - `StructField::index` has been split up into `StructField::declaration_index` and `StructField::reflection_index` #### Removed - Removed `bitset` dependency ## Migration Guide * Fields marked `#[reflect(skip_serializing)]` now must implement `Default` or specify a custom default function with `#[reflect(default = "path::to::some_func")]` ```rust #[derive(Reflect)] struct MyStruct { #[reflect(skip_serializing)] #[reflect(default = "get_foo_default")] foo: Foo, // <- `Foo` does not impl `Default` so requires a custom function #[reflect(skip_serializing)] bar: Bar, // <- `Bar` impls `Default` } #[derive(Reflect)] struct Foo(i32); #[derive(Reflect, Default)] struct Bar(i32); fn get_foo_default() -> Foo { Foo(123) } ``` * `SerializationData::new` has been changed to expect an iterator of `(usize, SkippedField)` rather than one of just `usize` ```rust // BEFORE SerializationData::new([0, 3].into_iter()); // AFTER SerializationData::new([ (0, SkippedField::new(field_0_default_fn)), (3, SkippedField::new(field_3_default_fn)), ].into_iter()); ``` * `Serialization::is_ignored_field` has been renamed to `Serialization::is_field_skipped` * Fields marked `#[reflect(skip_serializing)]` are now included in deserialization output. This may affect logic that expected those fields to be absent.
2023-10-22 12:43:31 +00:00
pub declaration_index: usize,
/// The index of this field as seen by the reflection API.
///
/// This index accounts for the removal of [ignored] fields.
/// It will only be `Some(index)` when the field is not ignored.
///
/// [ignored]: crate::field_attributes::ReflectIgnoreBehavior::IgnoreAlways
pub reflection_index: Option<usize>,
/// The documentation for this field, if any
#[cfg(feature = "documentation")]
pub doc: crate::documentation::Documentation,
bevy_reflect: Reflect enums (#4761) # Objective > This is a revival of #1347. Credit for the original PR should go to @Davier. Currently, enums are treated as `ReflectRef::Value` types by `bevy_reflect`. Obviously, there needs to be better a better representation for enums using the reflection API. ## Solution Based on prior work from @Davier, an `Enum` trait has been added as well as the ability to automatically implement it via the `Reflect` derive macro. This allows enums to be expressed dynamically: ```rust #[derive(Reflect)] enum Foo { A, B(usize), C { value: f32 }, } let mut foo = Foo::B(123); assert_eq!("B", foo.variant_name()); assert_eq!(1, foo.field_len()); let new_value = DynamicEnum::from(Foo::C { value: 1.23 }); foo.apply(&new_value); assert_eq!(Foo::C{value: 1.23}, foo); ``` ### Features #### Derive Macro Use the `#[derive(Reflect)]` macro to automatically implement the `Enum` trait for enum definitions. Optionally, you can use `#[reflect(ignore)]` with both variants and variant fields, just like you can with structs. These ignored items will not be considered as part of the reflection and cannot be accessed via reflection. ```rust #[derive(Reflect)] enum TestEnum { A, // Uncomment to ignore all of `B` // #[reflect(ignore)] B(usize), C { // Uncomment to ignore only field `foo` of `C` // #[reflect(ignore)] foo: f32, bar: bool, }, } ``` #### Dynamic Enums Enums may be created/represented dynamically via the `DynamicEnum` struct. The main purpose of this struct is to allow enums to be deserialized into a partial state and to allow dynamic patching. In order to ensure conversion from a `DynamicEnum` to a concrete enum type goes smoothly, be sure to add `FromReflect` to your derive macro. ```rust let mut value = TestEnum::A; // Create from a concrete instance let dyn_enum = DynamicEnum::from(TestEnum::B(123)); value.apply(&dyn_enum); assert_eq!(TestEnum::B(123), value); // Create a purely dynamic instance let dyn_enum = DynamicEnum::new("TestEnum", "A", ()); value.apply(&dyn_enum); assert_eq!(TestEnum::A, value); ``` #### Variants An enum value is always represented as one of its variants— never the enum in its entirety. ```rust let value = TestEnum::A; assert_eq!("A", value.variant_name()); // Since we are using the `A` variant, we cannot also be the `B` variant assert_ne!("B", value.variant_name()); ``` All variant types are representable within the `Enum` trait: unit, struct, and tuple. You can get the current type like: ```rust match value.variant_type() { VariantType::Unit => println!("A unit variant!"), VariantType::Struct => println!("A struct variant!"), VariantType::Tuple => println!("A tuple variant!"), } ``` > Notice that they don't contain any values representing the fields. These are purely tags. If a variant has them, you can access the fields as well: ```rust let mut value = TestEnum::C { foo: 1.23, bar: false }; // Read/write specific fields *value.field_mut("bar").unwrap() = true; // Iterate over the entire collection of fields for field in value.iter_fields() { println!("{} = {:?}", field.name(), field.value()); } ``` #### Variant Swapping It might seem odd to group all variant types under a single trait (why allow `iter_fields` on a unit variant?), but the reason this was done ~~is to easily allow *variant swapping*.~~ As I was recently drafting up the **Design Decisions** section, I discovered that other solutions could have been made to work with variant swapping. So while there are reasons to keep the all-in-one approach, variant swapping is _not_ one of them. ```rust let mut value: Box<dyn Enum> = Box::new(TestEnum::A); value.set(Box::new(TestEnum::B(123))).unwrap(); ``` #### Serialization Enums can be serialized and deserialized via reflection without needing to implement `Serialize` or `Deserialize` themselves (which can save thousands of lines of generated code). Below are the ways an enum can be serialized. > Note, like the rest of reflection-based serialization, the order of the keys in these representations is important! ##### Unit ```json { "type": "my_crate::TestEnum", "enum": { "variant": "A" } } ``` ##### Tuple ```json { "type": "my_crate::TestEnum", "enum": { "variant": "B", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` <details> <summary>Effects on Option</summary> This ends up making `Option` look a little ugly: ```json { "type": "core::option::Option<usize>", "enum": { "variant": "Some", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` </details> ##### Struct ```json { "type": "my_crate::TestEnum", "enum": { "variant": "C", "struct": { "foo": { "type": "f32", "value": 1.23 }, "bar": { "type": "bool", "value": false } } } } ``` ## Design Decisions <details> <summary><strong>View Section</strong></summary> This section is here to provide some context for why certain decisions were made for this PR, alternatives that could have been used instead, and what could be improved upon in the future. ### Variant Representation One of the biggest decisions was to decide on how to represent variants. The current design uses a "all-in-one" design where unit, tuple, and struct variants are all simultaneously represented by the `Enum` trait. This is not the only way it could have been done, though. #### Alternatives ##### 1. Variant Traits One way of representing variants would be to define traits for each variant, implementing them whenever an enum featured at least one instance of them. This would allow us to define variants like: ```rust pub trait Enum: Reflect { fn variant(&self) -> Variant; } pub enum Variant<'a> { Unit, Tuple(&'a dyn TupleVariant), Struct(&'a dyn StructVariant), } pub trait TupleVariant { fn field_len(&self) -> usize; // ... } ``` And then do things like: ```rust fn get_tuple_len(foo: &dyn Enum) -> usize { match foo.variant() { Variant::Tuple(tuple) => tuple.field_len(), _ => panic!("not a tuple variant!") } } ``` The reason this PR does not go with this approach is because of the fact that variants are not separate types. In other words, we cannot implement traits on specific variants— these cover the *entire* enum. This means we offer an easy footgun: ```rust let foo: Option<i32> = None; let my_enum = Box::new(foo) as Box<dyn TupleVariant>; ``` Here, `my_enum` contains `foo`, which is a unit variant. However, since we need to implement `TupleVariant` for `Option` as a whole, it's possible to perform such a cast. This is obviously wrong, but could easily go unnoticed. So unfortunately, this makes it not a good candidate for representing variants. ##### 2. Variant Structs To get around the issue of traits necessarily needing to apply to both the enum and its variants, we could instead use structs that are created on a per-variant basis. This was also considered but was ultimately [[removed](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c)](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c) due to concerns about allocations. Each variant struct would probably look something like: ```rust pub trait Enum: Reflect { fn variant_mut(&self) -> VariantMut; } pub enum VariantMut<'a> { Unit, Tuple(TupleVariantMut), Struct(StructVariantMut), } struct StructVariantMut<'a> { fields: Vec<&'a mut dyn Reflect>, field_indices: HashMap<Cow<'static, str>, usize> } ``` This allows us to isolate struct variants into their own defined struct and define methods specifically for their use. It also prevents users from casting to it since it's not a trait. However, this is not an optimal solution. Both `field_indices` and `fields` will require an allocation (remember, a `Box<[T]>` still requires a `Vec<T>` in order to be constructed). This *might* be a problem if called frequently enough. ##### 3. Generated Structs The original design, implemented by @Davier, instead generates structs specific for each variant. So if we had a variant path like `Foo::Bar`, we'd generate a struct named `FooBarWrapper`. This would be newtyped around the original enum and forward tuple or struct methods to the enum with the chosen variant. Because it involved using the `Tuple` and `Struct` traits (which are also both bound on `Reflect`), this meant a bit more code had to be generated. For a single struct variant with one field, the generated code amounted to ~110LoC. However, each new field added to that variant only added ~6 more LoC. In order to work properly, the enum had to be transmuted to the generated struct: ```rust fn variant(&self) -> crate::EnumVariant<'_> { match self { Foo::Bar {value: i32} => { let wrapper_ref = unsafe { std::mem::transmute::<&Self, &FooBarWrapper>(self) }; crate::EnumVariant::Struct(wrapper_ref as &dyn crate::Struct) } } } ``` This works because `FooBarWrapper` is defined as `repr(transparent)`. Out of all the alternatives, this would probably be the one most likely to be used again in the future. The reasons for why this PR did not continue to use it was because: * To reduce generated code (which would hopefully speed up compile times) * To avoid cluttering the code with generated structs not visible to the user * To keep bevy_reflect simple and extensible (these generated structs act as proxies and might not play well with current or future systems) * To avoid additional unsafe blocks * My own misunderstanding of @Davier's code That last point is obviously on me. I misjudged the code to be too unsafe and unable to handle variant swapping (which it probably could) when I was rebasing it. Looking over it again when writing up this whole section, I see that it was actually a pretty clever way of handling variant representation. #### Benefits of All-in-One As stated before, the current implementation uses an all-in-one approach. All variants are capable of containing fields as far as `Enum` is concerned. This provides a few benefits that the alternatives do not (reduced indirection, safer code, etc.). The biggest benefit, though, is direct field access. Rather than forcing users to have to go through pattern matching, we grant direct access to the fields contained by the current variant. The reason we can do this is because all of the pattern matching happens internally. Getting the field at index `2` will automatically return `Some(...)` for the current variant if it has a field at that index or `None` if it doesn't (or can't). This could be useful for scenarios where the variant has already been verified or just set/swapped (or even where the type of variant doesn't matter): ```rust let dyn_enum: &mut dyn Enum = &mut Foo::Bar {value: 123}; // We know it's the `Bar` variant let field = dyn_enum.field("value").unwrap(); ``` Reflection is not a type-safe abstraction— almost every return value is wrapped in `Option<...>`. There are plenty of places to check and recheck that a value is what Reflect says it is. Forcing users to have to go through `match` each time they want to access a field might just be an extra step among dozens of other verification processes. Some might disagree, but ultimately, my view is that the benefit here is an improvement to the ergonomics and usability of reflected enums. </details> --- ## Changelog ### Added * Added `Enum` trait * Added `Enum` impl to `Reflect` derive macro * Added `DynamicEnum` struct * Added `DynamicVariant` * Added `EnumInfo` * Added `VariantInfo` * Added `StructVariantInfo` * Added `TupleVariantInfo` * Added `UnitVariantInfo` * Added serializtion/deserialization support for enums * Added `EnumSerializer` * Added `VariantType` * Added `VariantFieldIter` * Added `VariantField` * Added `enum_partial_eq(...)` * Added `enum_hash(...)` ### Changed * `Option<T>` now implements `Enum` * `bevy_window` now depends on `bevy_reflect` * Implemented `Reflect` and `FromReflect` for `WindowId` * Derive `FromReflect` on `PerspectiveProjection` * Derive `FromReflect` on `OrthographicProjection` * Derive `FromReflect` on `WindowOrigin` * Derive `FromReflect` on `ScalingMode` * Derive `FromReflect` on `DepthCalculation` ## Migration Guide * Enums no longer need to be treated as values and usages of `#[reflect_value(...)]` can be removed or replaced by `#[reflect(...)]` * Enums (including `Option<T>`) now take a different format when serializing. The format is described above, but this may cause issues for existing scenes that make use of enums. --- Also shout out to @nicopap for helping clean up some of the code here! It's a big feature so help like this is really appreciated! Co-authored-by: Gino Valente <gino.valente.code@gmail.com>
2022-08-02 22:14:41 +00:00
}
/// Represents a variant on an enum.
pub(crate) struct EnumVariant<'a> {
/// The raw variant.
pub data: &'a Variant,
/// The fields within this variant.
pub fields: EnumVariantFields<'a>,
/// The reflection-based attributes on the variant.
#[allow(dead_code)]
bevy_reflect_derive: Clean up attribute logic (#11777) # Objective The code in `bevy_reflect_derive` could use some cleanup. ## Solution Took some of the changes in #11659 to create a dedicated PR for cleaning up the field and container attribute logic. #### Updated Naming I renamed `ReflectTraits` and `ReflectFieldAttr` to `ContainerAttributes` and `FieldAttributes`, respectively. I think these are clearer. #### Updated Parsing ##### Readability The parsing logic wasn't too bad before, but it was getting difficult to read. There was some duplicated logic between `Meta::List` and `Meta::Path` attributes. Additionally, all the logic was kept inside a large method. To simply things, I replaced the nested meta parsing with `ParseStream` parsing. In my opinion, this is easier to follow since it breaks up the large match statement into a small set of single-line if statements, where each if-block contains a single call to the appropriate attribute parsing method. ##### Flexibility On top of the added simplicity, this also makes our attribute parsing much more flexible. It allows us to more elegantly handle custom where clauses (i.e. `#[reflect(where T: Foo)]`) and it opens the door for more non-standard attribute syntax (e.g. #11659). ##### Errors This also allows us to automatically provide certain errors when parsing. For example, since we can use `stream.lookahead1()`, we get errors like the following for free: ``` error: expected one of: `ignore`, `skip_serializing`, `default` --> crates/bevy_reflect/src/lib.rs:1988:23 | 1988 | #[reflect(foo)] | ^^^ ``` --- ## Changelog > [!note] > All changes are internal to `bevy_reflect_derive` and should not affect the public API[^1]. - Renamed `ReflectTraits` to `ContainerAttributes` - Renamed `ReflectMeta::traits` to `ReflectMeta::attrs` - Renamed `ReflectFieldAttr` to `FieldAttributes` - Updated parsing logic for field/container attribute parsing - Now uses a `ParseStream` directly instead of nested meta parsing - General code cleanup of the field/container attribute modules for `bevy_reflect_derive` [^1]: Does not include errors, which may look slightly different. --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
2024-02-12 15:16:27 +00:00
pub attrs: FieldAttributes,
bevy_reflect: Reflect enums (#4761) # Objective > This is a revival of #1347. Credit for the original PR should go to @Davier. Currently, enums are treated as `ReflectRef::Value` types by `bevy_reflect`. Obviously, there needs to be better a better representation for enums using the reflection API. ## Solution Based on prior work from @Davier, an `Enum` trait has been added as well as the ability to automatically implement it via the `Reflect` derive macro. This allows enums to be expressed dynamically: ```rust #[derive(Reflect)] enum Foo { A, B(usize), C { value: f32 }, } let mut foo = Foo::B(123); assert_eq!("B", foo.variant_name()); assert_eq!(1, foo.field_len()); let new_value = DynamicEnum::from(Foo::C { value: 1.23 }); foo.apply(&new_value); assert_eq!(Foo::C{value: 1.23}, foo); ``` ### Features #### Derive Macro Use the `#[derive(Reflect)]` macro to automatically implement the `Enum` trait for enum definitions. Optionally, you can use `#[reflect(ignore)]` with both variants and variant fields, just like you can with structs. These ignored items will not be considered as part of the reflection and cannot be accessed via reflection. ```rust #[derive(Reflect)] enum TestEnum { A, // Uncomment to ignore all of `B` // #[reflect(ignore)] B(usize), C { // Uncomment to ignore only field `foo` of `C` // #[reflect(ignore)] foo: f32, bar: bool, }, } ``` #### Dynamic Enums Enums may be created/represented dynamically via the `DynamicEnum` struct. The main purpose of this struct is to allow enums to be deserialized into a partial state and to allow dynamic patching. In order to ensure conversion from a `DynamicEnum` to a concrete enum type goes smoothly, be sure to add `FromReflect` to your derive macro. ```rust let mut value = TestEnum::A; // Create from a concrete instance let dyn_enum = DynamicEnum::from(TestEnum::B(123)); value.apply(&dyn_enum); assert_eq!(TestEnum::B(123), value); // Create a purely dynamic instance let dyn_enum = DynamicEnum::new("TestEnum", "A", ()); value.apply(&dyn_enum); assert_eq!(TestEnum::A, value); ``` #### Variants An enum value is always represented as one of its variants— never the enum in its entirety. ```rust let value = TestEnum::A; assert_eq!("A", value.variant_name()); // Since we are using the `A` variant, we cannot also be the `B` variant assert_ne!("B", value.variant_name()); ``` All variant types are representable within the `Enum` trait: unit, struct, and tuple. You can get the current type like: ```rust match value.variant_type() { VariantType::Unit => println!("A unit variant!"), VariantType::Struct => println!("A struct variant!"), VariantType::Tuple => println!("A tuple variant!"), } ``` > Notice that they don't contain any values representing the fields. These are purely tags. If a variant has them, you can access the fields as well: ```rust let mut value = TestEnum::C { foo: 1.23, bar: false }; // Read/write specific fields *value.field_mut("bar").unwrap() = true; // Iterate over the entire collection of fields for field in value.iter_fields() { println!("{} = {:?}", field.name(), field.value()); } ``` #### Variant Swapping It might seem odd to group all variant types under a single trait (why allow `iter_fields` on a unit variant?), but the reason this was done ~~is to easily allow *variant swapping*.~~ As I was recently drafting up the **Design Decisions** section, I discovered that other solutions could have been made to work with variant swapping. So while there are reasons to keep the all-in-one approach, variant swapping is _not_ one of them. ```rust let mut value: Box<dyn Enum> = Box::new(TestEnum::A); value.set(Box::new(TestEnum::B(123))).unwrap(); ``` #### Serialization Enums can be serialized and deserialized via reflection without needing to implement `Serialize` or `Deserialize` themselves (which can save thousands of lines of generated code). Below are the ways an enum can be serialized. > Note, like the rest of reflection-based serialization, the order of the keys in these representations is important! ##### Unit ```json { "type": "my_crate::TestEnum", "enum": { "variant": "A" } } ``` ##### Tuple ```json { "type": "my_crate::TestEnum", "enum": { "variant": "B", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` <details> <summary>Effects on Option</summary> This ends up making `Option` look a little ugly: ```json { "type": "core::option::Option<usize>", "enum": { "variant": "Some", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` </details> ##### Struct ```json { "type": "my_crate::TestEnum", "enum": { "variant": "C", "struct": { "foo": { "type": "f32", "value": 1.23 }, "bar": { "type": "bool", "value": false } } } } ``` ## Design Decisions <details> <summary><strong>View Section</strong></summary> This section is here to provide some context for why certain decisions were made for this PR, alternatives that could have been used instead, and what could be improved upon in the future. ### Variant Representation One of the biggest decisions was to decide on how to represent variants. The current design uses a "all-in-one" design where unit, tuple, and struct variants are all simultaneously represented by the `Enum` trait. This is not the only way it could have been done, though. #### Alternatives ##### 1. Variant Traits One way of representing variants would be to define traits for each variant, implementing them whenever an enum featured at least one instance of them. This would allow us to define variants like: ```rust pub trait Enum: Reflect { fn variant(&self) -> Variant; } pub enum Variant<'a> { Unit, Tuple(&'a dyn TupleVariant), Struct(&'a dyn StructVariant), } pub trait TupleVariant { fn field_len(&self) -> usize; // ... } ``` And then do things like: ```rust fn get_tuple_len(foo: &dyn Enum) -> usize { match foo.variant() { Variant::Tuple(tuple) => tuple.field_len(), _ => panic!("not a tuple variant!") } } ``` The reason this PR does not go with this approach is because of the fact that variants are not separate types. In other words, we cannot implement traits on specific variants— these cover the *entire* enum. This means we offer an easy footgun: ```rust let foo: Option<i32> = None; let my_enum = Box::new(foo) as Box<dyn TupleVariant>; ``` Here, `my_enum` contains `foo`, which is a unit variant. However, since we need to implement `TupleVariant` for `Option` as a whole, it's possible to perform such a cast. This is obviously wrong, but could easily go unnoticed. So unfortunately, this makes it not a good candidate for representing variants. ##### 2. Variant Structs To get around the issue of traits necessarily needing to apply to both the enum and its variants, we could instead use structs that are created on a per-variant basis. This was also considered but was ultimately [[removed](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c)](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c) due to concerns about allocations. Each variant struct would probably look something like: ```rust pub trait Enum: Reflect { fn variant_mut(&self) -> VariantMut; } pub enum VariantMut<'a> { Unit, Tuple(TupleVariantMut), Struct(StructVariantMut), } struct StructVariantMut<'a> { fields: Vec<&'a mut dyn Reflect>, field_indices: HashMap<Cow<'static, str>, usize> } ``` This allows us to isolate struct variants into their own defined struct and define methods specifically for their use. It also prevents users from casting to it since it's not a trait. However, this is not an optimal solution. Both `field_indices` and `fields` will require an allocation (remember, a `Box<[T]>` still requires a `Vec<T>` in order to be constructed). This *might* be a problem if called frequently enough. ##### 3. Generated Structs The original design, implemented by @Davier, instead generates structs specific for each variant. So if we had a variant path like `Foo::Bar`, we'd generate a struct named `FooBarWrapper`. This would be newtyped around the original enum and forward tuple or struct methods to the enum with the chosen variant. Because it involved using the `Tuple` and `Struct` traits (which are also both bound on `Reflect`), this meant a bit more code had to be generated. For a single struct variant with one field, the generated code amounted to ~110LoC. However, each new field added to that variant only added ~6 more LoC. In order to work properly, the enum had to be transmuted to the generated struct: ```rust fn variant(&self) -> crate::EnumVariant<'_> { match self { Foo::Bar {value: i32} => { let wrapper_ref = unsafe { std::mem::transmute::<&Self, &FooBarWrapper>(self) }; crate::EnumVariant::Struct(wrapper_ref as &dyn crate::Struct) } } } ``` This works because `FooBarWrapper` is defined as `repr(transparent)`. Out of all the alternatives, this would probably be the one most likely to be used again in the future. The reasons for why this PR did not continue to use it was because: * To reduce generated code (which would hopefully speed up compile times) * To avoid cluttering the code with generated structs not visible to the user * To keep bevy_reflect simple and extensible (these generated structs act as proxies and might not play well with current or future systems) * To avoid additional unsafe blocks * My own misunderstanding of @Davier's code That last point is obviously on me. I misjudged the code to be too unsafe and unable to handle variant swapping (which it probably could) when I was rebasing it. Looking over it again when writing up this whole section, I see that it was actually a pretty clever way of handling variant representation. #### Benefits of All-in-One As stated before, the current implementation uses an all-in-one approach. All variants are capable of containing fields as far as `Enum` is concerned. This provides a few benefits that the alternatives do not (reduced indirection, safer code, etc.). The biggest benefit, though, is direct field access. Rather than forcing users to have to go through pattern matching, we grant direct access to the fields contained by the current variant. The reason we can do this is because all of the pattern matching happens internally. Getting the field at index `2` will automatically return `Some(...)` for the current variant if it has a field at that index or `None` if it doesn't (or can't). This could be useful for scenarios where the variant has already been verified or just set/swapped (or even where the type of variant doesn't matter): ```rust let dyn_enum: &mut dyn Enum = &mut Foo::Bar {value: 123}; // We know it's the `Bar` variant let field = dyn_enum.field("value").unwrap(); ``` Reflection is not a type-safe abstraction— almost every return value is wrapped in `Option<...>`. There are plenty of places to check and recheck that a value is what Reflect says it is. Forcing users to have to go through `match` each time they want to access a field might just be an extra step among dozens of other verification processes. Some might disagree, but ultimately, my view is that the benefit here is an improvement to the ergonomics and usability of reflected enums. </details> --- ## Changelog ### Added * Added `Enum` trait * Added `Enum` impl to `Reflect` derive macro * Added `DynamicEnum` struct * Added `DynamicVariant` * Added `EnumInfo` * Added `VariantInfo` * Added `StructVariantInfo` * Added `TupleVariantInfo` * Added `UnitVariantInfo` * Added serializtion/deserialization support for enums * Added `EnumSerializer` * Added `VariantType` * Added `VariantFieldIter` * Added `VariantField` * Added `enum_partial_eq(...)` * Added `enum_hash(...)` ### Changed * `Option<T>` now implements `Enum` * `bevy_window` now depends on `bevy_reflect` * Implemented `Reflect` and `FromReflect` for `WindowId` * Derive `FromReflect` on `PerspectiveProjection` * Derive `FromReflect` on `OrthographicProjection` * Derive `FromReflect` on `WindowOrigin` * Derive `FromReflect` on `ScalingMode` * Derive `FromReflect` on `DepthCalculation` ## Migration Guide * Enums no longer need to be treated as values and usages of `#[reflect_value(...)]` can be removed or replaced by `#[reflect(...)]` * Enums (including `Option<T>`) now take a different format when serializing. The format is described above, but this may cause issues for existing scenes that make use of enums. --- Also shout out to @nicopap for helping clean up some of the code here! It's a big feature so help like this is really appreciated! Co-authored-by: Gino Valente <gino.valente.code@gmail.com>
2022-08-02 22:14:41 +00:00
/// The index of this variant within the enum.
#[allow(dead_code)]
pub index: usize,
/// The documentation for this variant, if any
#[cfg(feature = "documentation")]
pub doc: crate::documentation::Documentation,
bevy_reflect: Reflect enums (#4761) # Objective > This is a revival of #1347. Credit for the original PR should go to @Davier. Currently, enums are treated as `ReflectRef::Value` types by `bevy_reflect`. Obviously, there needs to be better a better representation for enums using the reflection API. ## Solution Based on prior work from @Davier, an `Enum` trait has been added as well as the ability to automatically implement it via the `Reflect` derive macro. This allows enums to be expressed dynamically: ```rust #[derive(Reflect)] enum Foo { A, B(usize), C { value: f32 }, } let mut foo = Foo::B(123); assert_eq!("B", foo.variant_name()); assert_eq!(1, foo.field_len()); let new_value = DynamicEnum::from(Foo::C { value: 1.23 }); foo.apply(&new_value); assert_eq!(Foo::C{value: 1.23}, foo); ``` ### Features #### Derive Macro Use the `#[derive(Reflect)]` macro to automatically implement the `Enum` trait for enum definitions. Optionally, you can use `#[reflect(ignore)]` with both variants and variant fields, just like you can with structs. These ignored items will not be considered as part of the reflection and cannot be accessed via reflection. ```rust #[derive(Reflect)] enum TestEnum { A, // Uncomment to ignore all of `B` // #[reflect(ignore)] B(usize), C { // Uncomment to ignore only field `foo` of `C` // #[reflect(ignore)] foo: f32, bar: bool, }, } ``` #### Dynamic Enums Enums may be created/represented dynamically via the `DynamicEnum` struct. The main purpose of this struct is to allow enums to be deserialized into a partial state and to allow dynamic patching. In order to ensure conversion from a `DynamicEnum` to a concrete enum type goes smoothly, be sure to add `FromReflect` to your derive macro. ```rust let mut value = TestEnum::A; // Create from a concrete instance let dyn_enum = DynamicEnum::from(TestEnum::B(123)); value.apply(&dyn_enum); assert_eq!(TestEnum::B(123), value); // Create a purely dynamic instance let dyn_enum = DynamicEnum::new("TestEnum", "A", ()); value.apply(&dyn_enum); assert_eq!(TestEnum::A, value); ``` #### Variants An enum value is always represented as one of its variants— never the enum in its entirety. ```rust let value = TestEnum::A; assert_eq!("A", value.variant_name()); // Since we are using the `A` variant, we cannot also be the `B` variant assert_ne!("B", value.variant_name()); ``` All variant types are representable within the `Enum` trait: unit, struct, and tuple. You can get the current type like: ```rust match value.variant_type() { VariantType::Unit => println!("A unit variant!"), VariantType::Struct => println!("A struct variant!"), VariantType::Tuple => println!("A tuple variant!"), } ``` > Notice that they don't contain any values representing the fields. These are purely tags. If a variant has them, you can access the fields as well: ```rust let mut value = TestEnum::C { foo: 1.23, bar: false }; // Read/write specific fields *value.field_mut("bar").unwrap() = true; // Iterate over the entire collection of fields for field in value.iter_fields() { println!("{} = {:?}", field.name(), field.value()); } ``` #### Variant Swapping It might seem odd to group all variant types under a single trait (why allow `iter_fields` on a unit variant?), but the reason this was done ~~is to easily allow *variant swapping*.~~ As I was recently drafting up the **Design Decisions** section, I discovered that other solutions could have been made to work with variant swapping. So while there are reasons to keep the all-in-one approach, variant swapping is _not_ one of them. ```rust let mut value: Box<dyn Enum> = Box::new(TestEnum::A); value.set(Box::new(TestEnum::B(123))).unwrap(); ``` #### Serialization Enums can be serialized and deserialized via reflection without needing to implement `Serialize` or `Deserialize` themselves (which can save thousands of lines of generated code). Below are the ways an enum can be serialized. > Note, like the rest of reflection-based serialization, the order of the keys in these representations is important! ##### Unit ```json { "type": "my_crate::TestEnum", "enum": { "variant": "A" } } ``` ##### Tuple ```json { "type": "my_crate::TestEnum", "enum": { "variant": "B", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` <details> <summary>Effects on Option</summary> This ends up making `Option` look a little ugly: ```json { "type": "core::option::Option<usize>", "enum": { "variant": "Some", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` </details> ##### Struct ```json { "type": "my_crate::TestEnum", "enum": { "variant": "C", "struct": { "foo": { "type": "f32", "value": 1.23 }, "bar": { "type": "bool", "value": false } } } } ``` ## Design Decisions <details> <summary><strong>View Section</strong></summary> This section is here to provide some context for why certain decisions were made for this PR, alternatives that could have been used instead, and what could be improved upon in the future. ### Variant Representation One of the biggest decisions was to decide on how to represent variants. The current design uses a "all-in-one" design where unit, tuple, and struct variants are all simultaneously represented by the `Enum` trait. This is not the only way it could have been done, though. #### Alternatives ##### 1. Variant Traits One way of representing variants would be to define traits for each variant, implementing them whenever an enum featured at least one instance of them. This would allow us to define variants like: ```rust pub trait Enum: Reflect { fn variant(&self) -> Variant; } pub enum Variant<'a> { Unit, Tuple(&'a dyn TupleVariant), Struct(&'a dyn StructVariant), } pub trait TupleVariant { fn field_len(&self) -> usize; // ... } ``` And then do things like: ```rust fn get_tuple_len(foo: &dyn Enum) -> usize { match foo.variant() { Variant::Tuple(tuple) => tuple.field_len(), _ => panic!("not a tuple variant!") } } ``` The reason this PR does not go with this approach is because of the fact that variants are not separate types. In other words, we cannot implement traits on specific variants— these cover the *entire* enum. This means we offer an easy footgun: ```rust let foo: Option<i32> = None; let my_enum = Box::new(foo) as Box<dyn TupleVariant>; ``` Here, `my_enum` contains `foo`, which is a unit variant. However, since we need to implement `TupleVariant` for `Option` as a whole, it's possible to perform such a cast. This is obviously wrong, but could easily go unnoticed. So unfortunately, this makes it not a good candidate for representing variants. ##### 2. Variant Structs To get around the issue of traits necessarily needing to apply to both the enum and its variants, we could instead use structs that are created on a per-variant basis. This was also considered but was ultimately [[removed](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c)](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c) due to concerns about allocations. Each variant struct would probably look something like: ```rust pub trait Enum: Reflect { fn variant_mut(&self) -> VariantMut; } pub enum VariantMut<'a> { Unit, Tuple(TupleVariantMut), Struct(StructVariantMut), } struct StructVariantMut<'a> { fields: Vec<&'a mut dyn Reflect>, field_indices: HashMap<Cow<'static, str>, usize> } ``` This allows us to isolate struct variants into their own defined struct and define methods specifically for their use. It also prevents users from casting to it since it's not a trait. However, this is not an optimal solution. Both `field_indices` and `fields` will require an allocation (remember, a `Box<[T]>` still requires a `Vec<T>` in order to be constructed). This *might* be a problem if called frequently enough. ##### 3. Generated Structs The original design, implemented by @Davier, instead generates structs specific for each variant. So if we had a variant path like `Foo::Bar`, we'd generate a struct named `FooBarWrapper`. This would be newtyped around the original enum and forward tuple or struct methods to the enum with the chosen variant. Because it involved using the `Tuple` and `Struct` traits (which are also both bound on `Reflect`), this meant a bit more code had to be generated. For a single struct variant with one field, the generated code amounted to ~110LoC. However, each new field added to that variant only added ~6 more LoC. In order to work properly, the enum had to be transmuted to the generated struct: ```rust fn variant(&self) -> crate::EnumVariant<'_> { match self { Foo::Bar {value: i32} => { let wrapper_ref = unsafe { std::mem::transmute::<&Self, &FooBarWrapper>(self) }; crate::EnumVariant::Struct(wrapper_ref as &dyn crate::Struct) } } } ``` This works because `FooBarWrapper` is defined as `repr(transparent)`. Out of all the alternatives, this would probably be the one most likely to be used again in the future. The reasons for why this PR did not continue to use it was because: * To reduce generated code (which would hopefully speed up compile times) * To avoid cluttering the code with generated structs not visible to the user * To keep bevy_reflect simple and extensible (these generated structs act as proxies and might not play well with current or future systems) * To avoid additional unsafe blocks * My own misunderstanding of @Davier's code That last point is obviously on me. I misjudged the code to be too unsafe and unable to handle variant swapping (which it probably could) when I was rebasing it. Looking over it again when writing up this whole section, I see that it was actually a pretty clever way of handling variant representation. #### Benefits of All-in-One As stated before, the current implementation uses an all-in-one approach. All variants are capable of containing fields as far as `Enum` is concerned. This provides a few benefits that the alternatives do not (reduced indirection, safer code, etc.). The biggest benefit, though, is direct field access. Rather than forcing users to have to go through pattern matching, we grant direct access to the fields contained by the current variant. The reason we can do this is because all of the pattern matching happens internally. Getting the field at index `2` will automatically return `Some(...)` for the current variant if it has a field at that index or `None` if it doesn't (or can't). This could be useful for scenarios where the variant has already been verified or just set/swapped (or even where the type of variant doesn't matter): ```rust let dyn_enum: &mut dyn Enum = &mut Foo::Bar {value: 123}; // We know it's the `Bar` variant let field = dyn_enum.field("value").unwrap(); ``` Reflection is not a type-safe abstraction— almost every return value is wrapped in `Option<...>`. There are plenty of places to check and recheck that a value is what Reflect says it is. Forcing users to have to go through `match` each time they want to access a field might just be an extra step among dozens of other verification processes. Some might disagree, but ultimately, my view is that the benefit here is an improvement to the ergonomics and usability of reflected enums. </details> --- ## Changelog ### Added * Added `Enum` trait * Added `Enum` impl to `Reflect` derive macro * Added `DynamicEnum` struct * Added `DynamicVariant` * Added `EnumInfo` * Added `VariantInfo` * Added `StructVariantInfo` * Added `TupleVariantInfo` * Added `UnitVariantInfo` * Added serializtion/deserialization support for enums * Added `EnumSerializer` * Added `VariantType` * Added `VariantFieldIter` * Added `VariantField` * Added `enum_partial_eq(...)` * Added `enum_hash(...)` ### Changed * `Option<T>` now implements `Enum` * `bevy_window` now depends on `bevy_reflect` * Implemented `Reflect` and `FromReflect` for `WindowId` * Derive `FromReflect` on `PerspectiveProjection` * Derive `FromReflect` on `OrthographicProjection` * Derive `FromReflect` on `WindowOrigin` * Derive `FromReflect` on `ScalingMode` * Derive `FromReflect` on `DepthCalculation` ## Migration Guide * Enums no longer need to be treated as values and usages of `#[reflect_value(...)]` can be removed or replaced by `#[reflect(...)]` * Enums (including `Option<T>`) now take a different format when serializing. The format is described above, but this may cause issues for existing scenes that make use of enums. --- Also shout out to @nicopap for helping clean up some of the code here! It's a big feature so help like this is really appreciated! Co-authored-by: Gino Valente <gino.valente.code@gmail.com>
2022-08-02 22:14:41 +00:00
}
pub(crate) enum EnumVariantFields<'a> {
Named(Vec<StructField<'a>>),
Unnamed(Vec<StructField<'a>>),
Unit,
}
Support multiple `#[reflect]`/`#[reflect_value]` + improve error messages (#6237) # Objective Currently, surprising behavior happens when specifying `#[reflect(...)]` or `#[reflect_value(...)]` multiple times. Rather than merging the traits lists from all attributes, only the trait list from the last attribute is used. For example, in the following code, only the `Debug` and `Hash` traits are reflected and not `Default` or `PartialEq`: ```rs #[derive(Debug, PartialEq, Hash, Default, Reflect)] #[reflect(PartialEq, Default)] #[reflect(Debug, Hash)] struct Foo; ``` This is especially important when some traits should only be reflected under certain circumstances. For example, this previously had surprisingly behavior when the "serialize" feature is enabled: ```rs #[derive(Debug, Hash, Reflect)] #[reflect(Debug, Hash)] #[cfg_attr( feature = "serialize", derive(Serialize, Deserialize), reflect(Serialize, Deserialize) ] struct Foo; ``` In addition, compile error messages generated from using the derive macro often point to the `#[derive(Reflect)]` rather than to the source of the error. It would be a lot more helpful if the compiler errors pointed to what specifically caused the error rather than just to the derive macro itself. ## Solution Merge the trait lists in all `#[reflect(...)]` and `#[reflect_value(...)]` attributes. Additionally, make `#[reflect]` and `#[reflect_value]` mutually exclusive. Additionally, span information is carried throughout some parts of the code now to ensure that error messages point to more useful places and better indicate what caused those errors. For example, `#[reflect(Hash, Hash)]` points to the second `Hash` as the source of an error. Also, in the following example, the compiler error now points to the `Hash` in `#[reflect(Hash)]` rather than to the derive macro: ```rs #[derive(Reflect)] #[reflect(Hash)] // <-- compiler error points to `Hash` for lack of a `Hash` implementation struct Foo; ``` --- ## Changelog Changed - Using multiple `#[reflect(...)]` or `#[reflect_value(...)]` attributes now merges the trait lists. For example, `#[reflect(Debug, Hash)] #[reflect(PartialEq, Default)]` is equivalent to `#[reflect(Debug, Hash, PartialEq, Default)]`. - Multiple `#[reflect(...)]` and `#[reflect_value(...)]` attributes were previously accepted, but only the last attribute was respected. - Using both `#[reflect(...)]` and `#[reflect_value(...)]` was previously accepted, but had surprising behavior. This is no longer accepted. - Improved error messages for `#[derive(Reflect)]` by propagating useful span information. Many errors should now point to the source of those errors rather than to the derive macro.
2022-10-17 14:38:56 +00:00
/// The method in which the type should be reflected.
#[derive(PartialEq, Eq)]
enum ReflectMode {
/// Reflect the type normally, providing information about all fields/variants.
Normal,
/// Reflect the type as a value.
Value,
}
/// How the macro was invoked.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub(crate) enum ReflectImplSource {
bevy_reflect: Reflect remote types (#6042) # Objective The goal with this PR is to allow the use of types that don't implement `Reflect` within the reflection API. Rust's [orphan rule](https://doc.rust-lang.org/book/ch10-02-traits.html#implementing-a-trait-on-a-type) prevents implementing a trait on an external type when neither type nor trait are owned by the implementor. This means that if a crate, `cool_rust_lib`, defines a type, `Foo`, then a user cannot use it with reflection. What this means is that we have to ignore it most of the time: ```rust #[derive(Reflect)] struct SomeStruct { #[reflect(ignore)] data: cool_rust_lib::Foo } ``` Obviously, it's impossible to implement `Reflect` on `Foo`. But does it *have* to be? Most of reflection doesn't deal with concrete types— it's almost all using `dyn Reflect`. And being very metadata-driven, it should theoretically be possible. I mean, [`serde`](https://serde.rs/remote-derive.html) does it. ## Solution > Special thanks to @danielhenrymantilla for their help reviewing this PR and offering wisdom wrt safety. Taking a page out of `serde`'s book, this PR adds the ability to easily use "remote types" with reflection. In this context, a "remote type" is the external type for which we have no ability to implement `Reflect`. This adds the `#[reflect_remote(...)]` attribute macro, which is used to generate "remote type wrappers". All you have to do is define the wrapper exactly the same as the remote type's definition: ```rust // Pretend this is our external crate mod cool_rust_lib { #[derive(Default)] struct Foo { pub value: String } } #[reflect_remote(cool_rust_lib::Foo)] struct FooWrapper { pub value: String } ``` > **Note:** All fields in the external type *must* be public. This could be addressed with a separate getter/setter attribute either in this PR or in another one. The macro takes this user-defined item and transforms it into a newtype wrapper around the external type, marking it as `#[repr(transparent)]`. The fields/variants defined by the user are simply used to build out the reflection impls. Additionally, it generates an implementation of the new trait, `ReflectRemote`, which helps prevent accidental misuses of this API. Therefore, the output generated by the macro would look something like: ```rust #[repr(transparent)] struct FooWrapper(pub cool_rust_lib::Foo); impl ReflectRemote for FooWrapper { type Remote = cool_rust_lib::Foo; // transmutation methods... } // reflection impls... // these will acknowledge and make use of the `value` field ``` Internally, the reflection API will pass around the `FooWrapper` and [transmute](https://doc.rust-lang.org/std/mem/fn.transmute.html) it where necessary. All we have to do is then tell `Reflect` to do that. So rather than ignoring the field, we tell `Reflect` to use our wrapper using the `#[reflect(remote = ...)]` field attribute: ```rust #[derive(Reflect)] struct SomeStruct { #[reflect(remote = FooWrapper)] data: cool_rust_lib::Foo } ``` #### Other Macros & Type Data Because this macro consumes the defined item and generates a new one, we can't just put our macros anywhere. All macros that should be passed to the generated struct need to come *below* this macro. For example, to derive `Default` and register its associated type data: ```rust // ✅ GOOD #[reflect_remote(cool_rust_lib::Foo)] #[derive(Default)] #[reflect(Default)] struct FooWrapper { pub value: String } // ❌ BAD #[derive(Default)] #[reflect_remote(cool_rust_lib::Foo)] #[reflect(Default)] struct FooWrapper { pub value: String } ``` #### Generics Generics are forwarded to the generated struct as well. They should also be defined in the same order: ```rust #[reflect_remote(RemoteGeneric<'a, T1, T2>)] struct GenericWrapper<'a, T1, T2> { pub foo: &'a T1, pub bar: &'a T2, } ``` > Naming does *not* need to match the original definition's. Only order matters here. > Also note that the code above is just a demonstration and doesn't actually compile since we'd need to enforce certain bounds (e.g. `T1: Reflect`, `'a: 'static`, etc.) #### Nesting And, yes, you can nest remote types: ```rust #[reflect_remote(RemoteOuter)] struct OuterWrapper { #[reflect(remote = InnerWrapper)] pub inner: RemoteInner } #[reflect_remote(RemoteInner)] struct InnerWrapper(usize); ``` #### Assertions This macro will also generate some compile-time assertions to ensure that the correct types are used. It's important we catch this early so users don't have to wait for something to panic. And it also helps keep our `unsafe` a little safer. For example, a wrapper definition that does not match its corresponding remote type will result in an error: ```rust mod external_crate { pub struct TheirStruct(pub u32); } #[reflect_remote(external_crate::TheirStruct)] struct MyStruct(pub String); // ERROR: expected type `u32` but found `String` ``` <details> <summary>Generated Assertion</summary> ```rust const _: () = { #[allow(non_snake_case)] #[allow(unused_variables)] #[allow(unused_assignments)] #[allow(unreachable_patterns)] #[allow(clippy::multiple_bound_locations)] fn assert_wrapper_definition_matches_remote_type( mut __remote__: external_crate::TheirStruct, ) { __remote__.0 = (|| -> ::core::option::Option<String> { None })().unwrap(); } }; ``` </details> Additionally, using the incorrect type in a `#[reflect(remote = ...)]` attribute should result in an error: ```rust mod external_crate { pub struct TheirFoo(pub u32); pub struct TheirBar(pub i32); } #[reflect_remote(external_crate::TheirFoo)] struct MyFoo(pub u32); #[reflect_remote(external_crate::TheirBar)] struct MyBar(pub i32); #[derive(Reflect)] struct MyStruct { #[reflect(remote = MyBar)] // ERROR: expected type `TheirFoo` but found struct `TheirBar` foo: external_crate::TheirFoo } ``` <details> <summary>Generated Assertion</summary> ```rust const _: () = { struct RemoteFieldAssertions; impl RemoteFieldAssertions { #[allow(non_snake_case)] #[allow(clippy::multiple_bound_locations)] fn assert__foo__is_valid_remote() { let _: <MyBar as bevy_reflect::ReflectRemote>::Remote = (|| -> ::core::option::Option<external_crate::TheirFoo> { None })().unwrap(); } } }; ``` </details> ### Discussion There are a couple points that I think still need discussion or validation. - [x] 1. `Any` shenanigans ~~If we wanted to downcast our remote type from a `dyn Reflect`, we'd have to first downcast to the wrapper then extract the inner type. This PR has a [commit](b840db9f74cb6d357f951cb11b150d46bac89ee2) that addresses this by making all the `Reflect::*any` methods return the inner type rather than the wrapper type. This allows us to downcast directly to our remote type.~~ ~~However, I'm not sure if this is something we want to do. For unknowing users, it could be confusing and seemingly inconsistent. Is it worth keeping? Or should this behavior be removed?~~ I think this should be fine. The remote wrapper is an implementation detail and users should not need to downcast to the wrapper type. Feel free to let me know if there are other opinions on this though! - [x] 2. Implementing `Deref/DerefMut` and `From` ~~We don't currently do this, but should we implement other traits on the generated transparent struct? We could implement `Deref`/`DerefMut` to easily access the inner type. And we could implement `From` for easier conversion between the two types (e.g. `T: Into<Foo>`).~~ As mentioned in the comments, we probably don't need to do this. Again, the remote wrapper is an implementation detail, and should generally not be used directly. - [x] 3. ~~Should we define a getter/setter field attribute in this PR as well or leave it for a future one?~~ I think this should be saved for a future PR - [ ] 4. Any foreseeable issues with this implementation? #### Alternatives One alternative to defining our own `ReflectRemote` would be to use [bytemuck's `TransparentWrapper`](https://docs.rs/bytemuck/1.13.1/bytemuck/trait.TransparentWrapper.html) (as suggested by @danielhenrymantilla). This is definitely a viable option, as `ReflectRemote` is pretty much the same thing as `TransparentWrapper`. However, the cost would be bringing in a new crate— though, it is already in use in a few other sub-crates like bevy_render. I think we're okay just defining `ReflectRemote` ourselves, but we can go the bytemuck route if we'd prefer offloading that work to another crate. --- ## Changelog * Added the `#[reflect_remote(...)]` attribute macro to allow `Reflect` to be used on remote types * Added `ReflectRemote` trait for ensuring proper remote wrapper usage
2024-08-12 19:12:53 +00:00
/// Using `impl_reflect!`.
ImplRemoteType,
bevy_reflect: Reflect remote types (#6042) # Objective The goal with this PR is to allow the use of types that don't implement `Reflect` within the reflection API. Rust's [orphan rule](https://doc.rust-lang.org/book/ch10-02-traits.html#implementing-a-trait-on-a-type) prevents implementing a trait on an external type when neither type nor trait are owned by the implementor. This means that if a crate, `cool_rust_lib`, defines a type, `Foo`, then a user cannot use it with reflection. What this means is that we have to ignore it most of the time: ```rust #[derive(Reflect)] struct SomeStruct { #[reflect(ignore)] data: cool_rust_lib::Foo } ``` Obviously, it's impossible to implement `Reflect` on `Foo`. But does it *have* to be? Most of reflection doesn't deal with concrete types— it's almost all using `dyn Reflect`. And being very metadata-driven, it should theoretically be possible. I mean, [`serde`](https://serde.rs/remote-derive.html) does it. ## Solution > Special thanks to @danielhenrymantilla for their help reviewing this PR and offering wisdom wrt safety. Taking a page out of `serde`'s book, this PR adds the ability to easily use "remote types" with reflection. In this context, a "remote type" is the external type for which we have no ability to implement `Reflect`. This adds the `#[reflect_remote(...)]` attribute macro, which is used to generate "remote type wrappers". All you have to do is define the wrapper exactly the same as the remote type's definition: ```rust // Pretend this is our external crate mod cool_rust_lib { #[derive(Default)] struct Foo { pub value: String } } #[reflect_remote(cool_rust_lib::Foo)] struct FooWrapper { pub value: String } ``` > **Note:** All fields in the external type *must* be public. This could be addressed with a separate getter/setter attribute either in this PR or in another one. The macro takes this user-defined item and transforms it into a newtype wrapper around the external type, marking it as `#[repr(transparent)]`. The fields/variants defined by the user are simply used to build out the reflection impls. Additionally, it generates an implementation of the new trait, `ReflectRemote`, which helps prevent accidental misuses of this API. Therefore, the output generated by the macro would look something like: ```rust #[repr(transparent)] struct FooWrapper(pub cool_rust_lib::Foo); impl ReflectRemote for FooWrapper { type Remote = cool_rust_lib::Foo; // transmutation methods... } // reflection impls... // these will acknowledge and make use of the `value` field ``` Internally, the reflection API will pass around the `FooWrapper` and [transmute](https://doc.rust-lang.org/std/mem/fn.transmute.html) it where necessary. All we have to do is then tell `Reflect` to do that. So rather than ignoring the field, we tell `Reflect` to use our wrapper using the `#[reflect(remote = ...)]` field attribute: ```rust #[derive(Reflect)] struct SomeStruct { #[reflect(remote = FooWrapper)] data: cool_rust_lib::Foo } ``` #### Other Macros & Type Data Because this macro consumes the defined item and generates a new one, we can't just put our macros anywhere. All macros that should be passed to the generated struct need to come *below* this macro. For example, to derive `Default` and register its associated type data: ```rust // ✅ GOOD #[reflect_remote(cool_rust_lib::Foo)] #[derive(Default)] #[reflect(Default)] struct FooWrapper { pub value: String } // ❌ BAD #[derive(Default)] #[reflect_remote(cool_rust_lib::Foo)] #[reflect(Default)] struct FooWrapper { pub value: String } ``` #### Generics Generics are forwarded to the generated struct as well. They should also be defined in the same order: ```rust #[reflect_remote(RemoteGeneric<'a, T1, T2>)] struct GenericWrapper<'a, T1, T2> { pub foo: &'a T1, pub bar: &'a T2, } ``` > Naming does *not* need to match the original definition's. Only order matters here. > Also note that the code above is just a demonstration and doesn't actually compile since we'd need to enforce certain bounds (e.g. `T1: Reflect`, `'a: 'static`, etc.) #### Nesting And, yes, you can nest remote types: ```rust #[reflect_remote(RemoteOuter)] struct OuterWrapper { #[reflect(remote = InnerWrapper)] pub inner: RemoteInner } #[reflect_remote(RemoteInner)] struct InnerWrapper(usize); ``` #### Assertions This macro will also generate some compile-time assertions to ensure that the correct types are used. It's important we catch this early so users don't have to wait for something to panic. And it also helps keep our `unsafe` a little safer. For example, a wrapper definition that does not match its corresponding remote type will result in an error: ```rust mod external_crate { pub struct TheirStruct(pub u32); } #[reflect_remote(external_crate::TheirStruct)] struct MyStruct(pub String); // ERROR: expected type `u32` but found `String` ``` <details> <summary>Generated Assertion</summary> ```rust const _: () = { #[allow(non_snake_case)] #[allow(unused_variables)] #[allow(unused_assignments)] #[allow(unreachable_patterns)] #[allow(clippy::multiple_bound_locations)] fn assert_wrapper_definition_matches_remote_type( mut __remote__: external_crate::TheirStruct, ) { __remote__.0 = (|| -> ::core::option::Option<String> { None })().unwrap(); } }; ``` </details> Additionally, using the incorrect type in a `#[reflect(remote = ...)]` attribute should result in an error: ```rust mod external_crate { pub struct TheirFoo(pub u32); pub struct TheirBar(pub i32); } #[reflect_remote(external_crate::TheirFoo)] struct MyFoo(pub u32); #[reflect_remote(external_crate::TheirBar)] struct MyBar(pub i32); #[derive(Reflect)] struct MyStruct { #[reflect(remote = MyBar)] // ERROR: expected type `TheirFoo` but found struct `TheirBar` foo: external_crate::TheirFoo } ``` <details> <summary>Generated Assertion</summary> ```rust const _: () = { struct RemoteFieldAssertions; impl RemoteFieldAssertions { #[allow(non_snake_case)] #[allow(clippy::multiple_bound_locations)] fn assert__foo__is_valid_remote() { let _: <MyBar as bevy_reflect::ReflectRemote>::Remote = (|| -> ::core::option::Option<external_crate::TheirFoo> { None })().unwrap(); } } }; ``` </details> ### Discussion There are a couple points that I think still need discussion or validation. - [x] 1. `Any` shenanigans ~~If we wanted to downcast our remote type from a `dyn Reflect`, we'd have to first downcast to the wrapper then extract the inner type. This PR has a [commit](b840db9f74cb6d357f951cb11b150d46bac89ee2) that addresses this by making all the `Reflect::*any` methods return the inner type rather than the wrapper type. This allows us to downcast directly to our remote type.~~ ~~However, I'm not sure if this is something we want to do. For unknowing users, it could be confusing and seemingly inconsistent. Is it worth keeping? Or should this behavior be removed?~~ I think this should be fine. The remote wrapper is an implementation detail and users should not need to downcast to the wrapper type. Feel free to let me know if there are other opinions on this though! - [x] 2. Implementing `Deref/DerefMut` and `From` ~~We don't currently do this, but should we implement other traits on the generated transparent struct? We could implement `Deref`/`DerefMut` to easily access the inner type. And we could implement `From` for easier conversion between the two types (e.g. `T: Into<Foo>`).~~ As mentioned in the comments, we probably don't need to do this. Again, the remote wrapper is an implementation detail, and should generally not be used directly. - [x] 3. ~~Should we define a getter/setter field attribute in this PR as well or leave it for a future one?~~ I think this should be saved for a future PR - [ ] 4. Any foreseeable issues with this implementation? #### Alternatives One alternative to defining our own `ReflectRemote` would be to use [bytemuck's `TransparentWrapper`](https://docs.rs/bytemuck/1.13.1/bytemuck/trait.TransparentWrapper.html) (as suggested by @danielhenrymantilla). This is definitely a viable option, as `ReflectRemote` is pretty much the same thing as `TransparentWrapper`. However, the cost would be bringing in a new crate— though, it is already in use in a few other sub-crates like bevy_render. I think we're okay just defining `ReflectRemote` ourselves, but we can go the bytemuck route if we'd prefer offloading that work to another crate. --- ## Changelog * Added the `#[reflect_remote(...)]` attribute macro to allow `Reflect` to be used on remote types * Added `ReflectRemote` trait for ensuring proper remote wrapper usage
2024-08-12 19:12:53 +00:00
/// Using `#[derive(...)]`.
DeriveLocalType,
bevy_reflect: Reflect remote types (#6042) # Objective The goal with this PR is to allow the use of types that don't implement `Reflect` within the reflection API. Rust's [orphan rule](https://doc.rust-lang.org/book/ch10-02-traits.html#implementing-a-trait-on-a-type) prevents implementing a trait on an external type when neither type nor trait are owned by the implementor. This means that if a crate, `cool_rust_lib`, defines a type, `Foo`, then a user cannot use it with reflection. What this means is that we have to ignore it most of the time: ```rust #[derive(Reflect)] struct SomeStruct { #[reflect(ignore)] data: cool_rust_lib::Foo } ``` Obviously, it's impossible to implement `Reflect` on `Foo`. But does it *have* to be? Most of reflection doesn't deal with concrete types— it's almost all using `dyn Reflect`. And being very metadata-driven, it should theoretically be possible. I mean, [`serde`](https://serde.rs/remote-derive.html) does it. ## Solution > Special thanks to @danielhenrymantilla for their help reviewing this PR and offering wisdom wrt safety. Taking a page out of `serde`'s book, this PR adds the ability to easily use "remote types" with reflection. In this context, a "remote type" is the external type for which we have no ability to implement `Reflect`. This adds the `#[reflect_remote(...)]` attribute macro, which is used to generate "remote type wrappers". All you have to do is define the wrapper exactly the same as the remote type's definition: ```rust // Pretend this is our external crate mod cool_rust_lib { #[derive(Default)] struct Foo { pub value: String } } #[reflect_remote(cool_rust_lib::Foo)] struct FooWrapper { pub value: String } ``` > **Note:** All fields in the external type *must* be public. This could be addressed with a separate getter/setter attribute either in this PR or in another one. The macro takes this user-defined item and transforms it into a newtype wrapper around the external type, marking it as `#[repr(transparent)]`. The fields/variants defined by the user are simply used to build out the reflection impls. Additionally, it generates an implementation of the new trait, `ReflectRemote`, which helps prevent accidental misuses of this API. Therefore, the output generated by the macro would look something like: ```rust #[repr(transparent)] struct FooWrapper(pub cool_rust_lib::Foo); impl ReflectRemote for FooWrapper { type Remote = cool_rust_lib::Foo; // transmutation methods... } // reflection impls... // these will acknowledge and make use of the `value` field ``` Internally, the reflection API will pass around the `FooWrapper` and [transmute](https://doc.rust-lang.org/std/mem/fn.transmute.html) it where necessary. All we have to do is then tell `Reflect` to do that. So rather than ignoring the field, we tell `Reflect` to use our wrapper using the `#[reflect(remote = ...)]` field attribute: ```rust #[derive(Reflect)] struct SomeStruct { #[reflect(remote = FooWrapper)] data: cool_rust_lib::Foo } ``` #### Other Macros & Type Data Because this macro consumes the defined item and generates a new one, we can't just put our macros anywhere. All macros that should be passed to the generated struct need to come *below* this macro. For example, to derive `Default` and register its associated type data: ```rust // ✅ GOOD #[reflect_remote(cool_rust_lib::Foo)] #[derive(Default)] #[reflect(Default)] struct FooWrapper { pub value: String } // ❌ BAD #[derive(Default)] #[reflect_remote(cool_rust_lib::Foo)] #[reflect(Default)] struct FooWrapper { pub value: String } ``` #### Generics Generics are forwarded to the generated struct as well. They should also be defined in the same order: ```rust #[reflect_remote(RemoteGeneric<'a, T1, T2>)] struct GenericWrapper<'a, T1, T2> { pub foo: &'a T1, pub bar: &'a T2, } ``` > Naming does *not* need to match the original definition's. Only order matters here. > Also note that the code above is just a demonstration and doesn't actually compile since we'd need to enforce certain bounds (e.g. `T1: Reflect`, `'a: 'static`, etc.) #### Nesting And, yes, you can nest remote types: ```rust #[reflect_remote(RemoteOuter)] struct OuterWrapper { #[reflect(remote = InnerWrapper)] pub inner: RemoteInner } #[reflect_remote(RemoteInner)] struct InnerWrapper(usize); ``` #### Assertions This macro will also generate some compile-time assertions to ensure that the correct types are used. It's important we catch this early so users don't have to wait for something to panic. And it also helps keep our `unsafe` a little safer. For example, a wrapper definition that does not match its corresponding remote type will result in an error: ```rust mod external_crate { pub struct TheirStruct(pub u32); } #[reflect_remote(external_crate::TheirStruct)] struct MyStruct(pub String); // ERROR: expected type `u32` but found `String` ``` <details> <summary>Generated Assertion</summary> ```rust const _: () = { #[allow(non_snake_case)] #[allow(unused_variables)] #[allow(unused_assignments)] #[allow(unreachable_patterns)] #[allow(clippy::multiple_bound_locations)] fn assert_wrapper_definition_matches_remote_type( mut __remote__: external_crate::TheirStruct, ) { __remote__.0 = (|| -> ::core::option::Option<String> { None })().unwrap(); } }; ``` </details> Additionally, using the incorrect type in a `#[reflect(remote = ...)]` attribute should result in an error: ```rust mod external_crate { pub struct TheirFoo(pub u32); pub struct TheirBar(pub i32); } #[reflect_remote(external_crate::TheirFoo)] struct MyFoo(pub u32); #[reflect_remote(external_crate::TheirBar)] struct MyBar(pub i32); #[derive(Reflect)] struct MyStruct { #[reflect(remote = MyBar)] // ERROR: expected type `TheirFoo` but found struct `TheirBar` foo: external_crate::TheirFoo } ``` <details> <summary>Generated Assertion</summary> ```rust const _: () = { struct RemoteFieldAssertions; impl RemoteFieldAssertions { #[allow(non_snake_case)] #[allow(clippy::multiple_bound_locations)] fn assert__foo__is_valid_remote() { let _: <MyBar as bevy_reflect::ReflectRemote>::Remote = (|| -> ::core::option::Option<external_crate::TheirFoo> { None })().unwrap(); } } }; ``` </details> ### Discussion There are a couple points that I think still need discussion or validation. - [x] 1. `Any` shenanigans ~~If we wanted to downcast our remote type from a `dyn Reflect`, we'd have to first downcast to the wrapper then extract the inner type. This PR has a [commit](b840db9f74cb6d357f951cb11b150d46bac89ee2) that addresses this by making all the `Reflect::*any` methods return the inner type rather than the wrapper type. This allows us to downcast directly to our remote type.~~ ~~However, I'm not sure if this is something we want to do. For unknowing users, it could be confusing and seemingly inconsistent. Is it worth keeping? Or should this behavior be removed?~~ I think this should be fine. The remote wrapper is an implementation detail and users should not need to downcast to the wrapper type. Feel free to let me know if there are other opinions on this though! - [x] 2. Implementing `Deref/DerefMut` and `From` ~~We don't currently do this, but should we implement other traits on the generated transparent struct? We could implement `Deref`/`DerefMut` to easily access the inner type. And we could implement `From` for easier conversion between the two types (e.g. `T: Into<Foo>`).~~ As mentioned in the comments, we probably don't need to do this. Again, the remote wrapper is an implementation detail, and should generally not be used directly. - [x] 3. ~~Should we define a getter/setter field attribute in this PR as well or leave it for a future one?~~ I think this should be saved for a future PR - [ ] 4. Any foreseeable issues with this implementation? #### Alternatives One alternative to defining our own `ReflectRemote` would be to use [bytemuck's `TransparentWrapper`](https://docs.rs/bytemuck/1.13.1/bytemuck/trait.TransparentWrapper.html) (as suggested by @danielhenrymantilla). This is definitely a viable option, as `ReflectRemote` is pretty much the same thing as `TransparentWrapper`. However, the cost would be bringing in a new crate— though, it is already in use in a few other sub-crates like bevy_render. I think we're okay just defining `ReflectRemote` ourselves, but we can go the bytemuck route if we'd prefer offloading that work to another crate. --- ## Changelog * Added the `#[reflect_remote(...)]` attribute macro to allow `Reflect` to be used on remote types * Added `ReflectRemote` trait for ensuring proper remote wrapper usage
2024-08-12 19:12:53 +00:00
/// Using `#[reflect_remote]`.
RemoteReflect,
}
/// Which trait the macro explicitly implements.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub(crate) enum ReflectTraitToImpl {
Reflect,
FromReflect,
TypePath,
}
/// The provenance of a macro invocation.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub(crate) struct ReflectProvenance {
pub source: ReflectImplSource,
pub trait_: ReflectTraitToImpl,
}
impl fmt::Display for ReflectProvenance {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use self::{ReflectImplSource as S, ReflectTraitToImpl as T};
let str = match (self.source, self.trait_) {
(S::ImplRemoteType, T::Reflect) => "`impl_reflect`",
(S::DeriveLocalType, T::Reflect) => "`#[derive(Reflect)]`",
(S::DeriveLocalType, T::FromReflect) => "`#[derive(FromReflect)]`",
(S::DeriveLocalType, T::TypePath) => "`#[derive(TypePath)]`",
bevy_reflect: Reflect remote types (#6042) # Objective The goal with this PR is to allow the use of types that don't implement `Reflect` within the reflection API. Rust's [orphan rule](https://doc.rust-lang.org/book/ch10-02-traits.html#implementing-a-trait-on-a-type) prevents implementing a trait on an external type when neither type nor trait are owned by the implementor. This means that if a crate, `cool_rust_lib`, defines a type, `Foo`, then a user cannot use it with reflection. What this means is that we have to ignore it most of the time: ```rust #[derive(Reflect)] struct SomeStruct { #[reflect(ignore)] data: cool_rust_lib::Foo } ``` Obviously, it's impossible to implement `Reflect` on `Foo`. But does it *have* to be? Most of reflection doesn't deal with concrete types— it's almost all using `dyn Reflect`. And being very metadata-driven, it should theoretically be possible. I mean, [`serde`](https://serde.rs/remote-derive.html) does it. ## Solution > Special thanks to @danielhenrymantilla for their help reviewing this PR and offering wisdom wrt safety. Taking a page out of `serde`'s book, this PR adds the ability to easily use "remote types" with reflection. In this context, a "remote type" is the external type for which we have no ability to implement `Reflect`. This adds the `#[reflect_remote(...)]` attribute macro, which is used to generate "remote type wrappers". All you have to do is define the wrapper exactly the same as the remote type's definition: ```rust // Pretend this is our external crate mod cool_rust_lib { #[derive(Default)] struct Foo { pub value: String } } #[reflect_remote(cool_rust_lib::Foo)] struct FooWrapper { pub value: String } ``` > **Note:** All fields in the external type *must* be public. This could be addressed with a separate getter/setter attribute either in this PR or in another one. The macro takes this user-defined item and transforms it into a newtype wrapper around the external type, marking it as `#[repr(transparent)]`. The fields/variants defined by the user are simply used to build out the reflection impls. Additionally, it generates an implementation of the new trait, `ReflectRemote`, which helps prevent accidental misuses of this API. Therefore, the output generated by the macro would look something like: ```rust #[repr(transparent)] struct FooWrapper(pub cool_rust_lib::Foo); impl ReflectRemote for FooWrapper { type Remote = cool_rust_lib::Foo; // transmutation methods... } // reflection impls... // these will acknowledge and make use of the `value` field ``` Internally, the reflection API will pass around the `FooWrapper` and [transmute](https://doc.rust-lang.org/std/mem/fn.transmute.html) it where necessary. All we have to do is then tell `Reflect` to do that. So rather than ignoring the field, we tell `Reflect` to use our wrapper using the `#[reflect(remote = ...)]` field attribute: ```rust #[derive(Reflect)] struct SomeStruct { #[reflect(remote = FooWrapper)] data: cool_rust_lib::Foo } ``` #### Other Macros & Type Data Because this macro consumes the defined item and generates a new one, we can't just put our macros anywhere. All macros that should be passed to the generated struct need to come *below* this macro. For example, to derive `Default` and register its associated type data: ```rust // ✅ GOOD #[reflect_remote(cool_rust_lib::Foo)] #[derive(Default)] #[reflect(Default)] struct FooWrapper { pub value: String } // ❌ BAD #[derive(Default)] #[reflect_remote(cool_rust_lib::Foo)] #[reflect(Default)] struct FooWrapper { pub value: String } ``` #### Generics Generics are forwarded to the generated struct as well. They should also be defined in the same order: ```rust #[reflect_remote(RemoteGeneric<'a, T1, T2>)] struct GenericWrapper<'a, T1, T2> { pub foo: &'a T1, pub bar: &'a T2, } ``` > Naming does *not* need to match the original definition's. Only order matters here. > Also note that the code above is just a demonstration and doesn't actually compile since we'd need to enforce certain bounds (e.g. `T1: Reflect`, `'a: 'static`, etc.) #### Nesting And, yes, you can nest remote types: ```rust #[reflect_remote(RemoteOuter)] struct OuterWrapper { #[reflect(remote = InnerWrapper)] pub inner: RemoteInner } #[reflect_remote(RemoteInner)] struct InnerWrapper(usize); ``` #### Assertions This macro will also generate some compile-time assertions to ensure that the correct types are used. It's important we catch this early so users don't have to wait for something to panic. And it also helps keep our `unsafe` a little safer. For example, a wrapper definition that does not match its corresponding remote type will result in an error: ```rust mod external_crate { pub struct TheirStruct(pub u32); } #[reflect_remote(external_crate::TheirStruct)] struct MyStruct(pub String); // ERROR: expected type `u32` but found `String` ``` <details> <summary>Generated Assertion</summary> ```rust const _: () = { #[allow(non_snake_case)] #[allow(unused_variables)] #[allow(unused_assignments)] #[allow(unreachable_patterns)] #[allow(clippy::multiple_bound_locations)] fn assert_wrapper_definition_matches_remote_type( mut __remote__: external_crate::TheirStruct, ) { __remote__.0 = (|| -> ::core::option::Option<String> { None })().unwrap(); } }; ``` </details> Additionally, using the incorrect type in a `#[reflect(remote = ...)]` attribute should result in an error: ```rust mod external_crate { pub struct TheirFoo(pub u32); pub struct TheirBar(pub i32); } #[reflect_remote(external_crate::TheirFoo)] struct MyFoo(pub u32); #[reflect_remote(external_crate::TheirBar)] struct MyBar(pub i32); #[derive(Reflect)] struct MyStruct { #[reflect(remote = MyBar)] // ERROR: expected type `TheirFoo` but found struct `TheirBar` foo: external_crate::TheirFoo } ``` <details> <summary>Generated Assertion</summary> ```rust const _: () = { struct RemoteFieldAssertions; impl RemoteFieldAssertions { #[allow(non_snake_case)] #[allow(clippy::multiple_bound_locations)] fn assert__foo__is_valid_remote() { let _: <MyBar as bevy_reflect::ReflectRemote>::Remote = (|| -> ::core::option::Option<external_crate::TheirFoo> { None })().unwrap(); } } }; ``` </details> ### Discussion There are a couple points that I think still need discussion or validation. - [x] 1. `Any` shenanigans ~~If we wanted to downcast our remote type from a `dyn Reflect`, we'd have to first downcast to the wrapper then extract the inner type. This PR has a [commit](b840db9f74cb6d357f951cb11b150d46bac89ee2) that addresses this by making all the `Reflect::*any` methods return the inner type rather than the wrapper type. This allows us to downcast directly to our remote type.~~ ~~However, I'm not sure if this is something we want to do. For unknowing users, it could be confusing and seemingly inconsistent. Is it worth keeping? Or should this behavior be removed?~~ I think this should be fine. The remote wrapper is an implementation detail and users should not need to downcast to the wrapper type. Feel free to let me know if there are other opinions on this though! - [x] 2. Implementing `Deref/DerefMut` and `From` ~~We don't currently do this, but should we implement other traits on the generated transparent struct? We could implement `Deref`/`DerefMut` to easily access the inner type. And we could implement `From` for easier conversion between the two types (e.g. `T: Into<Foo>`).~~ As mentioned in the comments, we probably don't need to do this. Again, the remote wrapper is an implementation detail, and should generally not be used directly. - [x] 3. ~~Should we define a getter/setter field attribute in this PR as well or leave it for a future one?~~ I think this should be saved for a future PR - [ ] 4. Any foreseeable issues with this implementation? #### Alternatives One alternative to defining our own `ReflectRemote` would be to use [bytemuck's `TransparentWrapper`](https://docs.rs/bytemuck/1.13.1/bytemuck/trait.TransparentWrapper.html) (as suggested by @danielhenrymantilla). This is definitely a viable option, as `ReflectRemote` is pretty much the same thing as `TransparentWrapper`. However, the cost would be bringing in a new crate— though, it is already in use in a few other sub-crates like bevy_render. I think we're okay just defining `ReflectRemote` ourselves, but we can go the bytemuck route if we'd prefer offloading that work to another crate. --- ## Changelog * Added the `#[reflect_remote(...)]` attribute macro to allow `Reflect` to be used on remote types * Added `ReflectRemote` trait for ensuring proper remote wrapper usage
2024-08-12 19:12:53 +00:00
(S::RemoteReflect, T::Reflect) => "`#[reflect_remote]`",
(S::RemoteReflect, T::FromReflect | T::TypePath)
| (S::ImplRemoteType, T::FromReflect | T::TypePath) => unreachable!(),
};
f.write_str(str)
}
}
bevy_reflect: Reflect enums (#4761) # Objective > This is a revival of #1347. Credit for the original PR should go to @Davier. Currently, enums are treated as `ReflectRef::Value` types by `bevy_reflect`. Obviously, there needs to be better a better representation for enums using the reflection API. ## Solution Based on prior work from @Davier, an `Enum` trait has been added as well as the ability to automatically implement it via the `Reflect` derive macro. This allows enums to be expressed dynamically: ```rust #[derive(Reflect)] enum Foo { A, B(usize), C { value: f32 }, } let mut foo = Foo::B(123); assert_eq!("B", foo.variant_name()); assert_eq!(1, foo.field_len()); let new_value = DynamicEnum::from(Foo::C { value: 1.23 }); foo.apply(&new_value); assert_eq!(Foo::C{value: 1.23}, foo); ``` ### Features #### Derive Macro Use the `#[derive(Reflect)]` macro to automatically implement the `Enum` trait for enum definitions. Optionally, you can use `#[reflect(ignore)]` with both variants and variant fields, just like you can with structs. These ignored items will not be considered as part of the reflection and cannot be accessed via reflection. ```rust #[derive(Reflect)] enum TestEnum { A, // Uncomment to ignore all of `B` // #[reflect(ignore)] B(usize), C { // Uncomment to ignore only field `foo` of `C` // #[reflect(ignore)] foo: f32, bar: bool, }, } ``` #### Dynamic Enums Enums may be created/represented dynamically via the `DynamicEnum` struct. The main purpose of this struct is to allow enums to be deserialized into a partial state and to allow dynamic patching. In order to ensure conversion from a `DynamicEnum` to a concrete enum type goes smoothly, be sure to add `FromReflect` to your derive macro. ```rust let mut value = TestEnum::A; // Create from a concrete instance let dyn_enum = DynamicEnum::from(TestEnum::B(123)); value.apply(&dyn_enum); assert_eq!(TestEnum::B(123), value); // Create a purely dynamic instance let dyn_enum = DynamicEnum::new("TestEnum", "A", ()); value.apply(&dyn_enum); assert_eq!(TestEnum::A, value); ``` #### Variants An enum value is always represented as one of its variants— never the enum in its entirety. ```rust let value = TestEnum::A; assert_eq!("A", value.variant_name()); // Since we are using the `A` variant, we cannot also be the `B` variant assert_ne!("B", value.variant_name()); ``` All variant types are representable within the `Enum` trait: unit, struct, and tuple. You can get the current type like: ```rust match value.variant_type() { VariantType::Unit => println!("A unit variant!"), VariantType::Struct => println!("A struct variant!"), VariantType::Tuple => println!("A tuple variant!"), } ``` > Notice that they don't contain any values representing the fields. These are purely tags. If a variant has them, you can access the fields as well: ```rust let mut value = TestEnum::C { foo: 1.23, bar: false }; // Read/write specific fields *value.field_mut("bar").unwrap() = true; // Iterate over the entire collection of fields for field in value.iter_fields() { println!("{} = {:?}", field.name(), field.value()); } ``` #### Variant Swapping It might seem odd to group all variant types under a single trait (why allow `iter_fields` on a unit variant?), but the reason this was done ~~is to easily allow *variant swapping*.~~ As I was recently drafting up the **Design Decisions** section, I discovered that other solutions could have been made to work with variant swapping. So while there are reasons to keep the all-in-one approach, variant swapping is _not_ one of them. ```rust let mut value: Box<dyn Enum> = Box::new(TestEnum::A); value.set(Box::new(TestEnum::B(123))).unwrap(); ``` #### Serialization Enums can be serialized and deserialized via reflection without needing to implement `Serialize` or `Deserialize` themselves (which can save thousands of lines of generated code). Below are the ways an enum can be serialized. > Note, like the rest of reflection-based serialization, the order of the keys in these representations is important! ##### Unit ```json { "type": "my_crate::TestEnum", "enum": { "variant": "A" } } ``` ##### Tuple ```json { "type": "my_crate::TestEnum", "enum": { "variant": "B", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` <details> <summary>Effects on Option</summary> This ends up making `Option` look a little ugly: ```json { "type": "core::option::Option<usize>", "enum": { "variant": "Some", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` </details> ##### Struct ```json { "type": "my_crate::TestEnum", "enum": { "variant": "C", "struct": { "foo": { "type": "f32", "value": 1.23 }, "bar": { "type": "bool", "value": false } } } } ``` ## Design Decisions <details> <summary><strong>View Section</strong></summary> This section is here to provide some context for why certain decisions were made for this PR, alternatives that could have been used instead, and what could be improved upon in the future. ### Variant Representation One of the biggest decisions was to decide on how to represent variants. The current design uses a "all-in-one" design where unit, tuple, and struct variants are all simultaneously represented by the `Enum` trait. This is not the only way it could have been done, though. #### Alternatives ##### 1. Variant Traits One way of representing variants would be to define traits for each variant, implementing them whenever an enum featured at least one instance of them. This would allow us to define variants like: ```rust pub trait Enum: Reflect { fn variant(&self) -> Variant; } pub enum Variant<'a> { Unit, Tuple(&'a dyn TupleVariant), Struct(&'a dyn StructVariant), } pub trait TupleVariant { fn field_len(&self) -> usize; // ... } ``` And then do things like: ```rust fn get_tuple_len(foo: &dyn Enum) -> usize { match foo.variant() { Variant::Tuple(tuple) => tuple.field_len(), _ => panic!("not a tuple variant!") } } ``` The reason this PR does not go with this approach is because of the fact that variants are not separate types. In other words, we cannot implement traits on specific variants— these cover the *entire* enum. This means we offer an easy footgun: ```rust let foo: Option<i32> = None; let my_enum = Box::new(foo) as Box<dyn TupleVariant>; ``` Here, `my_enum` contains `foo`, which is a unit variant. However, since we need to implement `TupleVariant` for `Option` as a whole, it's possible to perform such a cast. This is obviously wrong, but could easily go unnoticed. So unfortunately, this makes it not a good candidate for representing variants. ##### 2. Variant Structs To get around the issue of traits necessarily needing to apply to both the enum and its variants, we could instead use structs that are created on a per-variant basis. This was also considered but was ultimately [[removed](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c)](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c) due to concerns about allocations. Each variant struct would probably look something like: ```rust pub trait Enum: Reflect { fn variant_mut(&self) -> VariantMut; } pub enum VariantMut<'a> { Unit, Tuple(TupleVariantMut), Struct(StructVariantMut), } struct StructVariantMut<'a> { fields: Vec<&'a mut dyn Reflect>, field_indices: HashMap<Cow<'static, str>, usize> } ``` This allows us to isolate struct variants into their own defined struct and define methods specifically for their use. It also prevents users from casting to it since it's not a trait. However, this is not an optimal solution. Both `field_indices` and `fields` will require an allocation (remember, a `Box<[T]>` still requires a `Vec<T>` in order to be constructed). This *might* be a problem if called frequently enough. ##### 3. Generated Structs The original design, implemented by @Davier, instead generates structs specific for each variant. So if we had a variant path like `Foo::Bar`, we'd generate a struct named `FooBarWrapper`. This would be newtyped around the original enum and forward tuple or struct methods to the enum with the chosen variant. Because it involved using the `Tuple` and `Struct` traits (which are also both bound on `Reflect`), this meant a bit more code had to be generated. For a single struct variant with one field, the generated code amounted to ~110LoC. However, each new field added to that variant only added ~6 more LoC. In order to work properly, the enum had to be transmuted to the generated struct: ```rust fn variant(&self) -> crate::EnumVariant<'_> { match self { Foo::Bar {value: i32} => { let wrapper_ref = unsafe { std::mem::transmute::<&Self, &FooBarWrapper>(self) }; crate::EnumVariant::Struct(wrapper_ref as &dyn crate::Struct) } } } ``` This works because `FooBarWrapper` is defined as `repr(transparent)`. Out of all the alternatives, this would probably be the one most likely to be used again in the future. The reasons for why this PR did not continue to use it was because: * To reduce generated code (which would hopefully speed up compile times) * To avoid cluttering the code with generated structs not visible to the user * To keep bevy_reflect simple and extensible (these generated structs act as proxies and might not play well with current or future systems) * To avoid additional unsafe blocks * My own misunderstanding of @Davier's code That last point is obviously on me. I misjudged the code to be too unsafe and unable to handle variant swapping (which it probably could) when I was rebasing it. Looking over it again when writing up this whole section, I see that it was actually a pretty clever way of handling variant representation. #### Benefits of All-in-One As stated before, the current implementation uses an all-in-one approach. All variants are capable of containing fields as far as `Enum` is concerned. This provides a few benefits that the alternatives do not (reduced indirection, safer code, etc.). The biggest benefit, though, is direct field access. Rather than forcing users to have to go through pattern matching, we grant direct access to the fields contained by the current variant. The reason we can do this is because all of the pattern matching happens internally. Getting the field at index `2` will automatically return `Some(...)` for the current variant if it has a field at that index or `None` if it doesn't (or can't). This could be useful for scenarios where the variant has already been verified or just set/swapped (or even where the type of variant doesn't matter): ```rust let dyn_enum: &mut dyn Enum = &mut Foo::Bar {value: 123}; // We know it's the `Bar` variant let field = dyn_enum.field("value").unwrap(); ``` Reflection is not a type-safe abstraction— almost every return value is wrapped in `Option<...>`. There are plenty of places to check and recheck that a value is what Reflect says it is. Forcing users to have to go through `match` each time they want to access a field might just be an extra step among dozens of other verification processes. Some might disagree, but ultimately, my view is that the benefit here is an improvement to the ergonomics and usability of reflected enums. </details> --- ## Changelog ### Added * Added `Enum` trait * Added `Enum` impl to `Reflect` derive macro * Added `DynamicEnum` struct * Added `DynamicVariant` * Added `EnumInfo` * Added `VariantInfo` * Added `StructVariantInfo` * Added `TupleVariantInfo` * Added `UnitVariantInfo` * Added serializtion/deserialization support for enums * Added `EnumSerializer` * Added `VariantType` * Added `VariantFieldIter` * Added `VariantField` * Added `enum_partial_eq(...)` * Added `enum_hash(...)` ### Changed * `Option<T>` now implements `Enum` * `bevy_window` now depends on `bevy_reflect` * Implemented `Reflect` and `FromReflect` for `WindowId` * Derive `FromReflect` on `PerspectiveProjection` * Derive `FromReflect` on `OrthographicProjection` * Derive `FromReflect` on `WindowOrigin` * Derive `FromReflect` on `ScalingMode` * Derive `FromReflect` on `DepthCalculation` ## Migration Guide * Enums no longer need to be treated as values and usages of `#[reflect_value(...)]` can be removed or replaced by `#[reflect(...)]` * Enums (including `Option<T>`) now take a different format when serializing. The format is described above, but this may cause issues for existing scenes that make use of enums. --- Also shout out to @nicopap for helping clean up some of the code here! It's a big feature so help like this is really appreciated! Co-authored-by: Gino Valente <gino.valente.code@gmail.com>
2022-08-02 22:14:41 +00:00
impl<'a> ReflectDerive<'a> {
bevy_reflect: `FromReflect` Ergonomics Implementation (#6056) # Objective **This implementation is based on https://github.com/bevyengine/rfcs/pull/59.** --- Resolves #4597 Full details and motivation can be found in the RFC, but here's a brief summary. `FromReflect` is a very powerful and important trait within the reflection API. It allows Dynamic types (e.g., `DynamicList`, etc.) to be formed into Real ones (e.g., `Vec<i32>`, etc.). This mainly comes into play concerning deserialization, where the reflection deserializers both return a `Box<dyn Reflect>` that almost always contain one of these Dynamic representations of a Real type. To convert this to our Real type, we need to use `FromReflect`. It also sneaks up in other ways. For example, it's a required bound for `T` in `Vec<T>` so that `Vec<T>` as a whole can be made `FromReflect`. It's also required by all fields of an enum as it's used as part of the `Reflect::apply` implementation. So in other words, much like `GetTypeRegistration` and `Typed`, it is very much a core reflection trait. The problem is that it is not currently treated like a core trait and is not automatically derived alongside `Reflect`. This makes using it a bit cumbersome and easy to forget. ## Solution Automatically derive `FromReflect` when deriving `Reflect`. Users can then choose to opt-out if needed using the `#[reflect(from_reflect = false)]` attribute. ```rust #[derive(Reflect)] struct Foo; #[derive(Reflect)] #[reflect(from_reflect = false)] struct Bar; fn test<T: FromReflect>(value: T) {} test(Foo); // <-- OK test(Bar); // <-- Panic! Bar does not implement trait `FromReflect` ``` #### `ReflectFromReflect` This PR also automatically adds the `ReflectFromReflect` (introduced in #6245) registration to the derived `GetTypeRegistration` impl— if the type hasn't opted out of `FromReflect` of course. <details> <summary><h4>Improved Deserialization</h4></summary> > **Warning** > This section includes changes that have since been descoped from this PR. They will likely be implemented again in a followup PR. I am mainly leaving these details in for archival purposes, as well as for reference when implementing this logic again. And since we can do all the above, we might as well improve deserialization. We can now choose to deserialize into a Dynamic type or automatically convert it using `FromReflect` under the hood. `[Un]TypedReflectDeserializer::new` will now perform the conversion and return the `Box`'d Real type. `[Un]TypedReflectDeserializer::new_dynamic` will work like what we have now and simply return the `Box`'d Dynamic type. ```rust // Returns the Real type let reflect_deserializer = UntypedReflectDeserializer::new(&registry); let mut deserializer = ron::de::Deserializer::from_str(input)?; let output: SomeStruct = reflect_deserializer.deserialize(&mut deserializer)?.take()?; // Returns the Dynamic type let reflect_deserializer = UntypedReflectDeserializer::new_dynamic(&registry); let mut deserializer = ron::de::Deserializer::from_str(input)?; let output: DynamicStruct = reflect_deserializer.deserialize(&mut deserializer)?.take()?; ``` </details> --- ## Changelog * `FromReflect` is now automatically derived within the `Reflect` derive macro * This includes auto-registering `ReflectFromReflect` in the derived `GetTypeRegistration` impl * ~~Renamed `TypedReflectDeserializer::new` and `UntypedReflectDeserializer::new` to `TypedReflectDeserializer::new_dynamic` and `UntypedReflectDeserializer::new_dynamic`, respectively~~ **Descoped** * ~~Changed `TypedReflectDeserializer::new` and `UntypedReflectDeserializer::new` to automatically convert the deserialized output using `FromReflect`~~ **Descoped** ## Migration Guide * `FromReflect` is now automatically derived within the `Reflect` derive macro. Items with both derives will need to remove the `FromReflect` one. ```rust // OLD #[derive(Reflect, FromReflect)] struct Foo; // NEW #[derive(Reflect)] struct Foo; ``` If using a manual implementation of `FromReflect` and the `Reflect` derive, users will need to opt-out of the automatic implementation. ```rust // OLD #[derive(Reflect)] struct Foo; impl FromReflect for Foo {/* ... */} // NEW #[derive(Reflect)] #[reflect(from_reflect = false)] struct Foo; impl FromReflect for Foo {/* ... */} ``` <details> <summary><h4>Removed Migrations</h4></summary> > **Warning** > This section includes changes that have since been descoped from this PR. They will likely be implemented again in a followup PR. I am mainly leaving these details in for archival purposes, as well as for reference when implementing this logic again. * The reflect deserializers now perform a `FromReflect` conversion internally. The expected output of `TypedReflectDeserializer::new` and `UntypedReflectDeserializer::new` is no longer a Dynamic (e.g., `DynamicList`), but its Real counterpart (e.g., `Vec<i32>`). ```rust let reflect_deserializer = UntypedReflectDeserializer::new_dynamic(&registry); let mut deserializer = ron::de::Deserializer::from_str(input)?; // OLD let output: DynamicStruct = reflect_deserializer.deserialize(&mut deserializer)?.take()?; // NEW let output: SomeStruct = reflect_deserializer.deserialize(&mut deserializer)?.take()?; ``` Alternatively, if this behavior isn't desired, use the `TypedReflectDeserializer::new_dynamic` and `UntypedReflectDeserializer::new_dynamic` methods instead: ```rust // OLD let reflect_deserializer = UntypedReflectDeserializer::new(&registry); // NEW let reflect_deserializer = UntypedReflectDeserializer::new_dynamic(&registry); ``` </details> --------- Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2023-06-29 01:31:34 +00:00
pub fn from_input(
input: &'a DeriveInput,
provenance: ReflectProvenance,
bevy_reflect: `FromReflect` Ergonomics Implementation (#6056) # Objective **This implementation is based on https://github.com/bevyengine/rfcs/pull/59.** --- Resolves #4597 Full details and motivation can be found in the RFC, but here's a brief summary. `FromReflect` is a very powerful and important trait within the reflection API. It allows Dynamic types (e.g., `DynamicList`, etc.) to be formed into Real ones (e.g., `Vec<i32>`, etc.). This mainly comes into play concerning deserialization, where the reflection deserializers both return a `Box<dyn Reflect>` that almost always contain one of these Dynamic representations of a Real type. To convert this to our Real type, we need to use `FromReflect`. It also sneaks up in other ways. For example, it's a required bound for `T` in `Vec<T>` so that `Vec<T>` as a whole can be made `FromReflect`. It's also required by all fields of an enum as it's used as part of the `Reflect::apply` implementation. So in other words, much like `GetTypeRegistration` and `Typed`, it is very much a core reflection trait. The problem is that it is not currently treated like a core trait and is not automatically derived alongside `Reflect`. This makes using it a bit cumbersome and easy to forget. ## Solution Automatically derive `FromReflect` when deriving `Reflect`. Users can then choose to opt-out if needed using the `#[reflect(from_reflect = false)]` attribute. ```rust #[derive(Reflect)] struct Foo; #[derive(Reflect)] #[reflect(from_reflect = false)] struct Bar; fn test<T: FromReflect>(value: T) {} test(Foo); // <-- OK test(Bar); // <-- Panic! Bar does not implement trait `FromReflect` ``` #### `ReflectFromReflect` This PR also automatically adds the `ReflectFromReflect` (introduced in #6245) registration to the derived `GetTypeRegistration` impl— if the type hasn't opted out of `FromReflect` of course. <details> <summary><h4>Improved Deserialization</h4></summary> > **Warning** > This section includes changes that have since been descoped from this PR. They will likely be implemented again in a followup PR. I am mainly leaving these details in for archival purposes, as well as for reference when implementing this logic again. And since we can do all the above, we might as well improve deserialization. We can now choose to deserialize into a Dynamic type or automatically convert it using `FromReflect` under the hood. `[Un]TypedReflectDeserializer::new` will now perform the conversion and return the `Box`'d Real type. `[Un]TypedReflectDeserializer::new_dynamic` will work like what we have now and simply return the `Box`'d Dynamic type. ```rust // Returns the Real type let reflect_deserializer = UntypedReflectDeserializer::new(&registry); let mut deserializer = ron::de::Deserializer::from_str(input)?; let output: SomeStruct = reflect_deserializer.deserialize(&mut deserializer)?.take()?; // Returns the Dynamic type let reflect_deserializer = UntypedReflectDeserializer::new_dynamic(&registry); let mut deserializer = ron::de::Deserializer::from_str(input)?; let output: DynamicStruct = reflect_deserializer.deserialize(&mut deserializer)?.take()?; ``` </details> --- ## Changelog * `FromReflect` is now automatically derived within the `Reflect` derive macro * This includes auto-registering `ReflectFromReflect` in the derived `GetTypeRegistration` impl * ~~Renamed `TypedReflectDeserializer::new` and `UntypedReflectDeserializer::new` to `TypedReflectDeserializer::new_dynamic` and `UntypedReflectDeserializer::new_dynamic`, respectively~~ **Descoped** * ~~Changed `TypedReflectDeserializer::new` and `UntypedReflectDeserializer::new` to automatically convert the deserialized output using `FromReflect`~~ **Descoped** ## Migration Guide * `FromReflect` is now automatically derived within the `Reflect` derive macro. Items with both derives will need to remove the `FromReflect` one. ```rust // OLD #[derive(Reflect, FromReflect)] struct Foo; // NEW #[derive(Reflect)] struct Foo; ``` If using a manual implementation of `FromReflect` and the `Reflect` derive, users will need to opt-out of the automatic implementation. ```rust // OLD #[derive(Reflect)] struct Foo; impl FromReflect for Foo {/* ... */} // NEW #[derive(Reflect)] #[reflect(from_reflect = false)] struct Foo; impl FromReflect for Foo {/* ... */} ``` <details> <summary><h4>Removed Migrations</h4></summary> > **Warning** > This section includes changes that have since been descoped from this PR. They will likely be implemented again in a followup PR. I am mainly leaving these details in for archival purposes, as well as for reference when implementing this logic again. * The reflect deserializers now perform a `FromReflect` conversion internally. The expected output of `TypedReflectDeserializer::new` and `UntypedReflectDeserializer::new` is no longer a Dynamic (e.g., `DynamicList`), but its Real counterpart (e.g., `Vec<i32>`). ```rust let reflect_deserializer = UntypedReflectDeserializer::new_dynamic(&registry); let mut deserializer = ron::de::Deserializer::from_str(input)?; // OLD let output: DynamicStruct = reflect_deserializer.deserialize(&mut deserializer)?.take()?; // NEW let output: SomeStruct = reflect_deserializer.deserialize(&mut deserializer)?.take()?; ``` Alternatively, if this behavior isn't desired, use the `TypedReflectDeserializer::new_dynamic` and `UntypedReflectDeserializer::new_dynamic` methods instead: ```rust // OLD let reflect_deserializer = UntypedReflectDeserializer::new(&registry); // NEW let reflect_deserializer = UntypedReflectDeserializer::new_dynamic(&registry); ``` </details> --------- Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2023-06-29 01:31:34 +00:00
) -> Result<Self, syn::Error> {
let mut container_attributes = ContainerAttributes::default();
reflect: stable type path v2 (#7184) # Objective - Introduce a stable alternative to [`std::any::type_name`](https://doc.rust-lang.org/std/any/fn.type_name.html). - Rewrite of #5805 with heavy inspiration in design. - On the path to #5830. - Part of solving #3327. ## Solution - Add a `TypePath` trait for static stable type path/name information. - Add a `TypePath` derive macro. - Add a `impl_type_path` macro for implementing internal and foreign types in `bevy_reflect`. --- ## Changelog - Added `TypePath` trait. - Added `DynamicTypePath` trait and `get_type_path` method to `Reflect`. - Added a `TypePath` derive macro. - Added a `bevy_reflect::impl_type_path` for implementing `TypePath` on internal and foreign types in `bevy_reflect`. - Changed `bevy_reflect::utility::(Non)GenericTypeInfoCell` to `(Non)GenericTypedCell<T>` which allows us to be generic over both `TypeInfo` and `TypePath`. - `TypePath` is now a supertrait of `Asset`, `Material` and `Material2d`. - `impl_reflect_struct` needs a `#[type_path = "..."]` attribute to be specified. - `impl_reflect_value` needs to either specify path starting with a double colon (`::core::option::Option`) or an `in my_crate::foo` declaration. - Added `bevy_reflect_derive::ReflectTypePath`. - Most uses of `Ident` in `bevy_reflect_derive` changed to use `ReflectTypePath`. ## Migration Guide - Implementors of `Asset`, `Material` and `Material2d` now also need to derive `TypePath`. - Manual implementors of `Reflect` will need to implement the new `get_type_path` method. ## Open Questions - [x] ~This PR currently does not migrate any usages of `std::any::type_name` to use `bevy_reflect::TypePath` to ease the review process. Should it?~ Migration will be left to a follow-up PR. - [ ] This PR adds a lot of `#[derive(TypePath)]` and `T: TypePath` to satisfy new bounds, mostly when deriving `TypeUuid`. Should we make `TypePath` a supertrait of `TypeUuid`? [Should we remove `TypeUuid` in favour of `TypePath`?](https://github.com/bevyengine/bevy/pull/5805/files/2afbd855327c4b68e0a6b6f03118f289988441a4#r961067892)
2023-06-05 20:31:20 +00:00
// Should indicate whether `#[reflect_value]` was used.
Support multiple `#[reflect]`/`#[reflect_value]` + improve error messages (#6237) # Objective Currently, surprising behavior happens when specifying `#[reflect(...)]` or `#[reflect_value(...)]` multiple times. Rather than merging the traits lists from all attributes, only the trait list from the last attribute is used. For example, in the following code, only the `Debug` and `Hash` traits are reflected and not `Default` or `PartialEq`: ```rs #[derive(Debug, PartialEq, Hash, Default, Reflect)] #[reflect(PartialEq, Default)] #[reflect(Debug, Hash)] struct Foo; ``` This is especially important when some traits should only be reflected under certain circumstances. For example, this previously had surprisingly behavior when the "serialize" feature is enabled: ```rs #[derive(Debug, Hash, Reflect)] #[reflect(Debug, Hash)] #[cfg_attr( feature = "serialize", derive(Serialize, Deserialize), reflect(Serialize, Deserialize) ] struct Foo; ``` In addition, compile error messages generated from using the derive macro often point to the `#[derive(Reflect)]` rather than to the source of the error. It would be a lot more helpful if the compiler errors pointed to what specifically caused the error rather than just to the derive macro itself. ## Solution Merge the trait lists in all `#[reflect(...)]` and `#[reflect_value(...)]` attributes. Additionally, make `#[reflect]` and `#[reflect_value]` mutually exclusive. Additionally, span information is carried throughout some parts of the code now to ensure that error messages point to more useful places and better indicate what caused those errors. For example, `#[reflect(Hash, Hash)]` points to the second `Hash` as the source of an error. Also, in the following example, the compiler error now points to the `Hash` in `#[reflect(Hash)]` rather than to the derive macro: ```rs #[derive(Reflect)] #[reflect(Hash)] // <-- compiler error points to `Hash` for lack of a `Hash` implementation struct Foo; ``` --- ## Changelog Changed - Using multiple `#[reflect(...)]` or `#[reflect_value(...)]` attributes now merges the trait lists. For example, `#[reflect(Debug, Hash)] #[reflect(PartialEq, Default)]` is equivalent to `#[reflect(Debug, Hash, PartialEq, Default)]`. - Multiple `#[reflect(...)]` and `#[reflect_value(...)]` attributes were previously accepted, but only the last attribute was respected. - Using both `#[reflect(...)]` and `#[reflect_value(...)]` was previously accepted, but had surprising behavior. This is no longer accepted. - Improved error messages for `#[derive(Reflect)]` by propagating useful span information. Many errors should now point to the source of those errors rather than to the derive macro.
2022-10-17 14:38:56 +00:00
let mut reflect_mode = None;
reflect: stable type path v2 (#7184) # Objective - Introduce a stable alternative to [`std::any::type_name`](https://doc.rust-lang.org/std/any/fn.type_name.html). - Rewrite of #5805 with heavy inspiration in design. - On the path to #5830. - Part of solving #3327. ## Solution - Add a `TypePath` trait for static stable type path/name information. - Add a `TypePath` derive macro. - Add a `impl_type_path` macro for implementing internal and foreign types in `bevy_reflect`. --- ## Changelog - Added `TypePath` trait. - Added `DynamicTypePath` trait and `get_type_path` method to `Reflect`. - Added a `TypePath` derive macro. - Added a `bevy_reflect::impl_type_path` for implementing `TypePath` on internal and foreign types in `bevy_reflect`. - Changed `bevy_reflect::utility::(Non)GenericTypeInfoCell` to `(Non)GenericTypedCell<T>` which allows us to be generic over both `TypeInfo` and `TypePath`. - `TypePath` is now a supertrait of `Asset`, `Material` and `Material2d`. - `impl_reflect_struct` needs a `#[type_path = "..."]` attribute to be specified. - `impl_reflect_value` needs to either specify path starting with a double colon (`::core::option::Option`) or an `in my_crate::foo` declaration. - Added `bevy_reflect_derive::ReflectTypePath`. - Most uses of `Ident` in `bevy_reflect_derive` changed to use `ReflectTypePath`. ## Migration Guide - Implementors of `Asset`, `Material` and `Material2d` now also need to derive `TypePath`. - Manual implementors of `Reflect` will need to implement the new `get_type_path` method. ## Open Questions - [x] ~This PR currently does not migrate any usages of `std::any::type_name` to use `bevy_reflect::TypePath` to ease the review process. Should it?~ Migration will be left to a follow-up PR. - [ ] This PR adds a lot of `#[derive(TypePath)]` and `T: TypePath` to satisfy new bounds, mostly when deriving `TypeUuid`. Should we make `TypePath` a supertrait of `TypeUuid`? [Should we remove `TypeUuid` in favour of `TypePath`?](https://github.com/bevyengine/bevy/pull/5805/files/2afbd855327c4b68e0a6b6f03118f289988441a4#r961067892)
2023-06-05 20:31:20 +00:00
// Should indicate whether `#[type_path = "..."]` was used.
let mut custom_path: Option<Path> = None;
// Should indicate whether `#[type_name = "..."]` was used.
let mut custom_type_name: Option<Ident> = None;
bevy_reflect_derive: Tidying up the code (#4712) # Objective The `bevy_reflect_derive` crate is not the cleanest or easiest to follow/maintain. The `lib.rs` file is especially difficult with over 1000 lines of code written in a confusing order. This is just a result of growth within the crate and it would be nice to clean it up for future work. ## Solution Split `bevy_reflect_derive` into many more submodules. The submodules include: * `container_attributes` - Code relating to container attributes * `derive_data` - Code relating to reflection-based derive metadata * `field_attributes` - Code relating to field attributes * `impls` - Code containing actual reflection implementations * `reflect_value` - Code relating to reflection-based value metadata * `registration` - Code relating to type registration * `utility` - General-purpose utility functions This leaves the `lib.rs` file to contain only the public macros, making it much easier to digest (and fewer than 200 lines). By breaking up the code into smaller modules, we make it easier for future contributors to find the code they're looking for or identify which module best fits their own additions. ### Metadata Structs This cleanup also adds two big metadata structs: `ReflectFieldAttr` and `ReflectDeriveData`. The former is used to store all attributes for a struct field (if any). The latter is used to store all metadata for struct-based derive inputs. Both significantly reduce code duplication and make editing these macros much simpler. The tradeoff is that we may collect more metadata than needed. However, this is usually a small thing (such as checking for attributes when they're not really needed or creating a `ReflectFieldAttr` for every field regardless of whether they actually have an attribute). We could try to remove these tradeoffs and squeeze some more performance out, but doing so might come at the cost of developer experience. Personally, I think it's much nicer to create a `ReflectFieldAttr` for every field since it means I don't have to do two `Option` checks. Others may disagree, though, and so we can discuss changing this either in this PR or in a future one. ### Out of Scope _Some_ documentation has been added or improved, but ultimately good docs are probably best saved for a dedicated PR. ## 🔍 Focus Points (for reviewers) I know it's a lot to sift through, so here is a list of **key points for reviewers**: - The following files contain code that was mostly just relocated: - `reflect_value.rs` - `registration.rs` - `container_attributes.rs` was also mostly moved but features some general cleanup (reducing nesting, removing hardcoded strings, etc.) and lots of doc comments - Most impl logic was moved from `lib.rs` to `impls.rs`, but they have been significantly modified to use the new `ReflectDeriveData` metadata struct in order to reduce duplication. - `derive_data.rs` and `field_attributes.rs` contain almost entirely new code and should probably be given the most attention. - Likewise, `from_reflect.rs` saw major changes using `ReflectDeriveData` so it should also be given focus. - There was no change to the `lib.rs` exports so the end-user API should be the same. ## Prior Work This task was initially tackled by @NathanSWard in #2377 (which was closed in favor of this PR), so hats off to them for beating me to the punch by nearly a year! --- ## Changelog * **[INTERNAL]** Split `bevy_reflect_derive` into smaller submodules * **[INTERNAL]** Add `ReflectFieldAttr` * **[INTERNAL]** Add `ReflectDeriveData` * Add `BevyManifest::get_path_direct()` method (`bevy_macro_utils`) Co-authored-by: MrGVSV <49806985+MrGVSV@users.noreply.github.com>
2022-05-12 19:43:23 +00:00
#[cfg(feature = "documentation")]
let mut doc = crate::documentation::Documentation::default();
for attribute in &input.attrs {
match &attribute.meta {
Meta::List(meta_list) if meta_list.path.is_ident(REFLECT_ATTRIBUTE_NAME) => {
Support multiple `#[reflect]`/`#[reflect_value]` + improve error messages (#6237) # Objective Currently, surprising behavior happens when specifying `#[reflect(...)]` or `#[reflect_value(...)]` multiple times. Rather than merging the traits lists from all attributes, only the trait list from the last attribute is used. For example, in the following code, only the `Debug` and `Hash` traits are reflected and not `Default` or `PartialEq`: ```rs #[derive(Debug, PartialEq, Hash, Default, Reflect)] #[reflect(PartialEq, Default)] #[reflect(Debug, Hash)] struct Foo; ``` This is especially important when some traits should only be reflected under certain circumstances. For example, this previously had surprisingly behavior when the "serialize" feature is enabled: ```rs #[derive(Debug, Hash, Reflect)] #[reflect(Debug, Hash)] #[cfg_attr( feature = "serialize", derive(Serialize, Deserialize), reflect(Serialize, Deserialize) ] struct Foo; ``` In addition, compile error messages generated from using the derive macro often point to the `#[derive(Reflect)]` rather than to the source of the error. It would be a lot more helpful if the compiler errors pointed to what specifically caused the error rather than just to the derive macro itself. ## Solution Merge the trait lists in all `#[reflect(...)]` and `#[reflect_value(...)]` attributes. Additionally, make `#[reflect]` and `#[reflect_value]` mutually exclusive. Additionally, span information is carried throughout some parts of the code now to ensure that error messages point to more useful places and better indicate what caused those errors. For example, `#[reflect(Hash, Hash)]` points to the second `Hash` as the source of an error. Also, in the following example, the compiler error now points to the `Hash` in `#[reflect(Hash)]` rather than to the derive macro: ```rs #[derive(Reflect)] #[reflect(Hash)] // <-- compiler error points to `Hash` for lack of a `Hash` implementation struct Foo; ``` --- ## Changelog Changed - Using multiple `#[reflect(...)]` or `#[reflect_value(...)]` attributes now merges the trait lists. For example, `#[reflect(Debug, Hash)] #[reflect(PartialEq, Default)]` is equivalent to `#[reflect(Debug, Hash, PartialEq, Default)]`. - Multiple `#[reflect(...)]` and `#[reflect_value(...)]` attributes were previously accepted, but only the last attribute was respected. - Using both `#[reflect(...)]` and `#[reflect_value(...)]` was previously accepted, but had surprising behavior. This is no longer accepted. - Improved error messages for `#[derive(Reflect)]` by propagating useful span information. Many errors should now point to the source of those errors rather than to the derive macro.
2022-10-17 14:38:56 +00:00
if !matches!(reflect_mode, None | Some(ReflectMode::Normal)) {
return Err(syn::Error::new(
meta_list.span(),
format_args!("cannot use both `#[{REFLECT_ATTRIBUTE_NAME}]` and `#[{REFLECT_VALUE_ATTRIBUTE_NAME}]`"),
));
}
reflect_mode = Some(ReflectMode::Normal);
container_attributes.parse_meta_list(meta_list, provenance.trait_)?;
}
Meta::List(meta_list) if meta_list.path.is_ident(REFLECT_VALUE_ATTRIBUTE_NAME) => {
Support multiple `#[reflect]`/`#[reflect_value]` + improve error messages (#6237) # Objective Currently, surprising behavior happens when specifying `#[reflect(...)]` or `#[reflect_value(...)]` multiple times. Rather than merging the traits lists from all attributes, only the trait list from the last attribute is used. For example, in the following code, only the `Debug` and `Hash` traits are reflected and not `Default` or `PartialEq`: ```rs #[derive(Debug, PartialEq, Hash, Default, Reflect)] #[reflect(PartialEq, Default)] #[reflect(Debug, Hash)] struct Foo; ``` This is especially important when some traits should only be reflected under certain circumstances. For example, this previously had surprisingly behavior when the "serialize" feature is enabled: ```rs #[derive(Debug, Hash, Reflect)] #[reflect(Debug, Hash)] #[cfg_attr( feature = "serialize", derive(Serialize, Deserialize), reflect(Serialize, Deserialize) ] struct Foo; ``` In addition, compile error messages generated from using the derive macro often point to the `#[derive(Reflect)]` rather than to the source of the error. It would be a lot more helpful if the compiler errors pointed to what specifically caused the error rather than just to the derive macro itself. ## Solution Merge the trait lists in all `#[reflect(...)]` and `#[reflect_value(...)]` attributes. Additionally, make `#[reflect]` and `#[reflect_value]` mutually exclusive. Additionally, span information is carried throughout some parts of the code now to ensure that error messages point to more useful places and better indicate what caused those errors. For example, `#[reflect(Hash, Hash)]` points to the second `Hash` as the source of an error. Also, in the following example, the compiler error now points to the `Hash` in `#[reflect(Hash)]` rather than to the derive macro: ```rs #[derive(Reflect)] #[reflect(Hash)] // <-- compiler error points to `Hash` for lack of a `Hash` implementation struct Foo; ``` --- ## Changelog Changed - Using multiple `#[reflect(...)]` or `#[reflect_value(...)]` attributes now merges the trait lists. For example, `#[reflect(Debug, Hash)] #[reflect(PartialEq, Default)]` is equivalent to `#[reflect(Debug, Hash, PartialEq, Default)]`. - Multiple `#[reflect(...)]` and `#[reflect_value(...)]` attributes were previously accepted, but only the last attribute was respected. - Using both `#[reflect(...)]` and `#[reflect_value(...)]` was previously accepted, but had surprising behavior. This is no longer accepted. - Improved error messages for `#[derive(Reflect)]` by propagating useful span information. Many errors should now point to the source of those errors rather than to the derive macro.
2022-10-17 14:38:56 +00:00
if !matches!(reflect_mode, None | Some(ReflectMode::Value)) {
return Err(syn::Error::new(
meta_list.span(),
format_args!("cannot use both `#[{REFLECT_ATTRIBUTE_NAME}]` and `#[{REFLECT_VALUE_ATTRIBUTE_NAME}]`"),
));
}
reflect_mode = Some(ReflectMode::Value);
container_attributes.parse_meta_list(meta_list, provenance.trait_)?;
bevy_reflect_derive: Tidying up the code (#4712) # Objective The `bevy_reflect_derive` crate is not the cleanest or easiest to follow/maintain. The `lib.rs` file is especially difficult with over 1000 lines of code written in a confusing order. This is just a result of growth within the crate and it would be nice to clean it up for future work. ## Solution Split `bevy_reflect_derive` into many more submodules. The submodules include: * `container_attributes` - Code relating to container attributes * `derive_data` - Code relating to reflection-based derive metadata * `field_attributes` - Code relating to field attributes * `impls` - Code containing actual reflection implementations * `reflect_value` - Code relating to reflection-based value metadata * `registration` - Code relating to type registration * `utility` - General-purpose utility functions This leaves the `lib.rs` file to contain only the public macros, making it much easier to digest (and fewer than 200 lines). By breaking up the code into smaller modules, we make it easier for future contributors to find the code they're looking for or identify which module best fits their own additions. ### Metadata Structs This cleanup also adds two big metadata structs: `ReflectFieldAttr` and `ReflectDeriveData`. The former is used to store all attributes for a struct field (if any). The latter is used to store all metadata for struct-based derive inputs. Both significantly reduce code duplication and make editing these macros much simpler. The tradeoff is that we may collect more metadata than needed. However, this is usually a small thing (such as checking for attributes when they're not really needed or creating a `ReflectFieldAttr` for every field regardless of whether they actually have an attribute). We could try to remove these tradeoffs and squeeze some more performance out, but doing so might come at the cost of developer experience. Personally, I think it's much nicer to create a `ReflectFieldAttr` for every field since it means I don't have to do two `Option` checks. Others may disagree, though, and so we can discuss changing this either in this PR or in a future one. ### Out of Scope _Some_ documentation has been added or improved, but ultimately good docs are probably best saved for a dedicated PR. ## 🔍 Focus Points (for reviewers) I know it's a lot to sift through, so here is a list of **key points for reviewers**: - The following files contain code that was mostly just relocated: - `reflect_value.rs` - `registration.rs` - `container_attributes.rs` was also mostly moved but features some general cleanup (reducing nesting, removing hardcoded strings, etc.) and lots of doc comments - Most impl logic was moved from `lib.rs` to `impls.rs`, but they have been significantly modified to use the new `ReflectDeriveData` metadata struct in order to reduce duplication. - `derive_data.rs` and `field_attributes.rs` contain almost entirely new code and should probably be given the most attention. - Likewise, `from_reflect.rs` saw major changes using `ReflectDeriveData` so it should also be given focus. - There was no change to the `lib.rs` exports so the end-user API should be the same. ## Prior Work This task was initially tackled by @NathanSWard in #2377 (which was closed in favor of this PR), so hats off to them for beating me to the punch by nearly a year! --- ## Changelog * **[INTERNAL]** Split `bevy_reflect_derive` into smaller submodules * **[INTERNAL]** Add `ReflectFieldAttr` * **[INTERNAL]** Add `ReflectDeriveData` * Add `BevyManifest::get_path_direct()` method (`bevy_macro_utils`) Co-authored-by: MrGVSV <49806985+MrGVSV@users.noreply.github.com>
2022-05-12 19:43:23 +00:00
}
Meta::Path(path) if path.is_ident(REFLECT_VALUE_ATTRIBUTE_NAME) => {
Support multiple `#[reflect]`/`#[reflect_value]` + improve error messages (#6237) # Objective Currently, surprising behavior happens when specifying `#[reflect(...)]` or `#[reflect_value(...)]` multiple times. Rather than merging the traits lists from all attributes, only the trait list from the last attribute is used. For example, in the following code, only the `Debug` and `Hash` traits are reflected and not `Default` or `PartialEq`: ```rs #[derive(Debug, PartialEq, Hash, Default, Reflect)] #[reflect(PartialEq, Default)] #[reflect(Debug, Hash)] struct Foo; ``` This is especially important when some traits should only be reflected under certain circumstances. For example, this previously had surprisingly behavior when the "serialize" feature is enabled: ```rs #[derive(Debug, Hash, Reflect)] #[reflect(Debug, Hash)] #[cfg_attr( feature = "serialize", derive(Serialize, Deserialize), reflect(Serialize, Deserialize) ] struct Foo; ``` In addition, compile error messages generated from using the derive macro often point to the `#[derive(Reflect)]` rather than to the source of the error. It would be a lot more helpful if the compiler errors pointed to what specifically caused the error rather than just to the derive macro itself. ## Solution Merge the trait lists in all `#[reflect(...)]` and `#[reflect_value(...)]` attributes. Additionally, make `#[reflect]` and `#[reflect_value]` mutually exclusive. Additionally, span information is carried throughout some parts of the code now to ensure that error messages point to more useful places and better indicate what caused those errors. For example, `#[reflect(Hash, Hash)]` points to the second `Hash` as the source of an error. Also, in the following example, the compiler error now points to the `Hash` in `#[reflect(Hash)]` rather than to the derive macro: ```rs #[derive(Reflect)] #[reflect(Hash)] // <-- compiler error points to `Hash` for lack of a `Hash` implementation struct Foo; ``` --- ## Changelog Changed - Using multiple `#[reflect(...)]` or `#[reflect_value(...)]` attributes now merges the trait lists. For example, `#[reflect(Debug, Hash)] #[reflect(PartialEq, Default)]` is equivalent to `#[reflect(Debug, Hash, PartialEq, Default)]`. - Multiple `#[reflect(...)]` and `#[reflect_value(...)]` attributes were previously accepted, but only the last attribute was respected. - Using both `#[reflect(...)]` and `#[reflect_value(...)]` was previously accepted, but had surprising behavior. This is no longer accepted. - Improved error messages for `#[derive(Reflect)]` by propagating useful span information. Many errors should now point to the source of those errors rather than to the derive macro.
2022-10-17 14:38:56 +00:00
if !matches!(reflect_mode, None | Some(ReflectMode::Value)) {
return Err(syn::Error::new(
path.span(),
format_args!("cannot use both `#[{REFLECT_ATTRIBUTE_NAME}]` and `#[{REFLECT_VALUE_ATTRIBUTE_NAME}]`"),
));
}
reflect_mode = Some(ReflectMode::Value);
}
reflect: stable type path v2 (#7184) # Objective - Introduce a stable alternative to [`std::any::type_name`](https://doc.rust-lang.org/std/any/fn.type_name.html). - Rewrite of #5805 with heavy inspiration in design. - On the path to #5830. - Part of solving #3327. ## Solution - Add a `TypePath` trait for static stable type path/name information. - Add a `TypePath` derive macro. - Add a `impl_type_path` macro for implementing internal and foreign types in `bevy_reflect`. --- ## Changelog - Added `TypePath` trait. - Added `DynamicTypePath` trait and `get_type_path` method to `Reflect`. - Added a `TypePath` derive macro. - Added a `bevy_reflect::impl_type_path` for implementing `TypePath` on internal and foreign types in `bevy_reflect`. - Changed `bevy_reflect::utility::(Non)GenericTypeInfoCell` to `(Non)GenericTypedCell<T>` which allows us to be generic over both `TypeInfo` and `TypePath`. - `TypePath` is now a supertrait of `Asset`, `Material` and `Material2d`. - `impl_reflect_struct` needs a `#[type_path = "..."]` attribute to be specified. - `impl_reflect_value` needs to either specify path starting with a double colon (`::core::option::Option`) or an `in my_crate::foo` declaration. - Added `bevy_reflect_derive::ReflectTypePath`. - Most uses of `Ident` in `bevy_reflect_derive` changed to use `ReflectTypePath`. ## Migration Guide - Implementors of `Asset`, `Material` and `Material2d` now also need to derive `TypePath`. - Manual implementors of `Reflect` will need to implement the new `get_type_path` method. ## Open Questions - [x] ~This PR currently does not migrate any usages of `std::any::type_name` to use `bevy_reflect::TypePath` to ease the review process. Should it?~ Migration will be left to a follow-up PR. - [ ] This PR adds a lot of `#[derive(TypePath)]` and `T: TypePath` to satisfy new bounds, mostly when deriving `TypeUuid`. Should we make `TypePath` a supertrait of `TypeUuid`? [Should we remove `TypeUuid` in favour of `TypePath`?](https://github.com/bevyengine/bevy/pull/5805/files/2afbd855327c4b68e0a6b6f03118f289988441a4#r961067892)
2023-06-05 20:31:20 +00:00
Meta::NameValue(pair) if pair.path.is_ident(TYPE_PATH_ATTRIBUTE_NAME) => {
let syn::Expr::Lit(syn::ExprLit {
lit: syn::Lit::Str(lit),
..
}) = &pair.value
else {
reflect: stable type path v2 (#7184) # Objective - Introduce a stable alternative to [`std::any::type_name`](https://doc.rust-lang.org/std/any/fn.type_name.html). - Rewrite of #5805 with heavy inspiration in design. - On the path to #5830. - Part of solving #3327. ## Solution - Add a `TypePath` trait for static stable type path/name information. - Add a `TypePath` derive macro. - Add a `impl_type_path` macro for implementing internal and foreign types in `bevy_reflect`. --- ## Changelog - Added `TypePath` trait. - Added `DynamicTypePath` trait and `get_type_path` method to `Reflect`. - Added a `TypePath` derive macro. - Added a `bevy_reflect::impl_type_path` for implementing `TypePath` on internal and foreign types in `bevy_reflect`. - Changed `bevy_reflect::utility::(Non)GenericTypeInfoCell` to `(Non)GenericTypedCell<T>` which allows us to be generic over both `TypeInfo` and `TypePath`. - `TypePath` is now a supertrait of `Asset`, `Material` and `Material2d`. - `impl_reflect_struct` needs a `#[type_path = "..."]` attribute to be specified. - `impl_reflect_value` needs to either specify path starting with a double colon (`::core::option::Option`) or an `in my_crate::foo` declaration. - Added `bevy_reflect_derive::ReflectTypePath`. - Most uses of `Ident` in `bevy_reflect_derive` changed to use `ReflectTypePath`. ## Migration Guide - Implementors of `Asset`, `Material` and `Material2d` now also need to derive `TypePath`. - Manual implementors of `Reflect` will need to implement the new `get_type_path` method. ## Open Questions - [x] ~This PR currently does not migrate any usages of `std::any::type_name` to use `bevy_reflect::TypePath` to ease the review process. Should it?~ Migration will be left to a follow-up PR. - [ ] This PR adds a lot of `#[derive(TypePath)]` and `T: TypePath` to satisfy new bounds, mostly when deriving `TypeUuid`. Should we make `TypePath` a supertrait of `TypeUuid`? [Should we remove `TypeUuid` in favour of `TypePath`?](https://github.com/bevyengine/bevy/pull/5805/files/2afbd855327c4b68e0a6b6f03118f289988441a4#r961067892)
2023-06-05 20:31:20 +00:00
return Err(syn::Error::new(
pair.span(),
format_args!("`#[{TYPE_PATH_ATTRIBUTE_NAME} = \"...\"]` must be a string literal"),
));
};
custom_path = Some(syn::parse::Parser::parse_str(
parse_path_no_leading_colon,
&lit.value(),
)?);
}
Meta::NameValue(pair) if pair.path.is_ident(TYPE_NAME_ATTRIBUTE_NAME) => {
let syn::Expr::Lit(syn::ExprLit {
lit: syn::Lit::Str(lit),
..
}) = &pair.value
else {
reflect: stable type path v2 (#7184) # Objective - Introduce a stable alternative to [`std::any::type_name`](https://doc.rust-lang.org/std/any/fn.type_name.html). - Rewrite of #5805 with heavy inspiration in design. - On the path to #5830. - Part of solving #3327. ## Solution - Add a `TypePath` trait for static stable type path/name information. - Add a `TypePath` derive macro. - Add a `impl_type_path` macro for implementing internal and foreign types in `bevy_reflect`. --- ## Changelog - Added `TypePath` trait. - Added `DynamicTypePath` trait and `get_type_path` method to `Reflect`. - Added a `TypePath` derive macro. - Added a `bevy_reflect::impl_type_path` for implementing `TypePath` on internal and foreign types in `bevy_reflect`. - Changed `bevy_reflect::utility::(Non)GenericTypeInfoCell` to `(Non)GenericTypedCell<T>` which allows us to be generic over both `TypeInfo` and `TypePath`. - `TypePath` is now a supertrait of `Asset`, `Material` and `Material2d`. - `impl_reflect_struct` needs a `#[type_path = "..."]` attribute to be specified. - `impl_reflect_value` needs to either specify path starting with a double colon (`::core::option::Option`) or an `in my_crate::foo` declaration. - Added `bevy_reflect_derive::ReflectTypePath`. - Most uses of `Ident` in `bevy_reflect_derive` changed to use `ReflectTypePath`. ## Migration Guide - Implementors of `Asset`, `Material` and `Material2d` now also need to derive `TypePath`. - Manual implementors of `Reflect` will need to implement the new `get_type_path` method. ## Open Questions - [x] ~This PR currently does not migrate any usages of `std::any::type_name` to use `bevy_reflect::TypePath` to ease the review process. Should it?~ Migration will be left to a follow-up PR. - [ ] This PR adds a lot of `#[derive(TypePath)]` and `T: TypePath` to satisfy new bounds, mostly when deriving `TypeUuid`. Should we make `TypePath` a supertrait of `TypeUuid`? [Should we remove `TypeUuid` in favour of `TypePath`?](https://github.com/bevyengine/bevy/pull/5805/files/2afbd855327c4b68e0a6b6f03118f289988441a4#r961067892)
2023-06-05 20:31:20 +00:00
return Err(syn::Error::new(
pair.span(),
format_args!("`#[{TYPE_NAME_ATTRIBUTE_NAME} = \"...\"]` must be a string literal"),
));
};
custom_type_name = Some(parse_str(&lit.value())?);
}
#[cfg(feature = "documentation")]
Meta::NameValue(pair) if pair.path.is_ident("doc") => {
if let syn::Expr::Lit(syn::ExprLit {
lit: syn::Lit::Str(lit),
..
}) = &pair.value
{
doc.push(lit.value());
}
}
_ => continue,
bevy_reflect_derive: Tidying up the code (#4712) # Objective The `bevy_reflect_derive` crate is not the cleanest or easiest to follow/maintain. The `lib.rs` file is especially difficult with over 1000 lines of code written in a confusing order. This is just a result of growth within the crate and it would be nice to clean it up for future work. ## Solution Split `bevy_reflect_derive` into many more submodules. The submodules include: * `container_attributes` - Code relating to container attributes * `derive_data` - Code relating to reflection-based derive metadata * `field_attributes` - Code relating to field attributes * `impls` - Code containing actual reflection implementations * `reflect_value` - Code relating to reflection-based value metadata * `registration` - Code relating to type registration * `utility` - General-purpose utility functions This leaves the `lib.rs` file to contain only the public macros, making it much easier to digest (and fewer than 200 lines). By breaking up the code into smaller modules, we make it easier for future contributors to find the code they're looking for or identify which module best fits their own additions. ### Metadata Structs This cleanup also adds two big metadata structs: `ReflectFieldAttr` and `ReflectDeriveData`. The former is used to store all attributes for a struct field (if any). The latter is used to store all metadata for struct-based derive inputs. Both significantly reduce code duplication and make editing these macros much simpler. The tradeoff is that we may collect more metadata than needed. However, this is usually a small thing (such as checking for attributes when they're not really needed or creating a `ReflectFieldAttr` for every field regardless of whether they actually have an attribute). We could try to remove these tradeoffs and squeeze some more performance out, but doing so might come at the cost of developer experience. Personally, I think it's much nicer to create a `ReflectFieldAttr` for every field since it means I don't have to do two `Option` checks. Others may disagree, though, and so we can discuss changing this either in this PR or in a future one. ### Out of Scope _Some_ documentation has been added or improved, but ultimately good docs are probably best saved for a dedicated PR. ## 🔍 Focus Points (for reviewers) I know it's a lot to sift through, so here is a list of **key points for reviewers**: - The following files contain code that was mostly just relocated: - `reflect_value.rs` - `registration.rs` - `container_attributes.rs` was also mostly moved but features some general cleanup (reducing nesting, removing hardcoded strings, etc.) and lots of doc comments - Most impl logic was moved from `lib.rs` to `impls.rs`, but they have been significantly modified to use the new `ReflectDeriveData` metadata struct in order to reduce duplication. - `derive_data.rs` and `field_attributes.rs` contain almost entirely new code and should probably be given the most attention. - Likewise, `from_reflect.rs` saw major changes using `ReflectDeriveData` so it should also be given focus. - There was no change to the `lib.rs` exports so the end-user API should be the same. ## Prior Work This task was initially tackled by @NathanSWard in #2377 (which was closed in favor of this PR), so hats off to them for beating me to the punch by nearly a year! --- ## Changelog * **[INTERNAL]** Split `bevy_reflect_derive` into smaller submodules * **[INTERNAL]** Add `ReflectFieldAttr` * **[INTERNAL]** Add `ReflectDeriveData` * Add `BevyManifest::get_path_direct()` method (`bevy_macro_utils`) Co-authored-by: MrGVSV <49806985+MrGVSV@users.noreply.github.com>
2022-05-12 19:43:23 +00:00
}
}
reflect: stable type path v2 (#7184) # Objective - Introduce a stable alternative to [`std::any::type_name`](https://doc.rust-lang.org/std/any/fn.type_name.html). - Rewrite of #5805 with heavy inspiration in design. - On the path to #5830. - Part of solving #3327. ## Solution - Add a `TypePath` trait for static stable type path/name information. - Add a `TypePath` derive macro. - Add a `impl_type_path` macro for implementing internal and foreign types in `bevy_reflect`. --- ## Changelog - Added `TypePath` trait. - Added `DynamicTypePath` trait and `get_type_path` method to `Reflect`. - Added a `TypePath` derive macro. - Added a `bevy_reflect::impl_type_path` for implementing `TypePath` on internal and foreign types in `bevy_reflect`. - Changed `bevy_reflect::utility::(Non)GenericTypeInfoCell` to `(Non)GenericTypedCell<T>` which allows us to be generic over both `TypeInfo` and `TypePath`. - `TypePath` is now a supertrait of `Asset`, `Material` and `Material2d`. - `impl_reflect_struct` needs a `#[type_path = "..."]` attribute to be specified. - `impl_reflect_value` needs to either specify path starting with a double colon (`::core::option::Option`) or an `in my_crate::foo` declaration. - Added `bevy_reflect_derive::ReflectTypePath`. - Most uses of `Ident` in `bevy_reflect_derive` changed to use `ReflectTypePath`. ## Migration Guide - Implementors of `Asset`, `Material` and `Material2d` now also need to derive `TypePath`. - Manual implementors of `Reflect` will need to implement the new `get_type_path` method. ## Open Questions - [x] ~This PR currently does not migrate any usages of `std::any::type_name` to use `bevy_reflect::TypePath` to ease the review process. Should it?~ Migration will be left to a follow-up PR. - [ ] This PR adds a lot of `#[derive(TypePath)]` and `T: TypePath` to satisfy new bounds, mostly when deriving `TypeUuid`. Should we make `TypePath` a supertrait of `TypeUuid`? [Should we remove `TypeUuid` in favour of `TypePath`?](https://github.com/bevyengine/bevy/pull/5805/files/2afbd855327c4b68e0a6b6f03118f289988441a4#r961067892)
2023-06-05 20:31:20 +00:00
match (&mut custom_path, custom_type_name) {
(Some(path), custom_type_name) => {
let ident = custom_type_name.unwrap_or_else(|| input.ident.clone());
path.segments.push(PathSegment::from(ident));
}
(None, Some(name)) => {
return Err(syn::Error::new(
name.span(),
format!("cannot use `#[{TYPE_NAME_ATTRIBUTE_NAME} = \"...\"]` without a `#[{TYPE_PATH_ATTRIBUTE_NAME} = \"...\"]` attribute."),
));
}
_ => (),
}
Support multiple `#[reflect]`/`#[reflect_value]` + improve error messages (#6237) # Objective Currently, surprising behavior happens when specifying `#[reflect(...)]` or `#[reflect_value(...)]` multiple times. Rather than merging the traits lists from all attributes, only the trait list from the last attribute is used. For example, in the following code, only the `Debug` and `Hash` traits are reflected and not `Default` or `PartialEq`: ```rs #[derive(Debug, PartialEq, Hash, Default, Reflect)] #[reflect(PartialEq, Default)] #[reflect(Debug, Hash)] struct Foo; ``` This is especially important when some traits should only be reflected under certain circumstances. For example, this previously had surprisingly behavior when the "serialize" feature is enabled: ```rs #[derive(Debug, Hash, Reflect)] #[reflect(Debug, Hash)] #[cfg_attr( feature = "serialize", derive(Serialize, Deserialize), reflect(Serialize, Deserialize) ] struct Foo; ``` In addition, compile error messages generated from using the derive macro often point to the `#[derive(Reflect)]` rather than to the source of the error. It would be a lot more helpful if the compiler errors pointed to what specifically caused the error rather than just to the derive macro itself. ## Solution Merge the trait lists in all `#[reflect(...)]` and `#[reflect_value(...)]` attributes. Additionally, make `#[reflect]` and `#[reflect_value]` mutually exclusive. Additionally, span information is carried throughout some parts of the code now to ensure that error messages point to more useful places and better indicate what caused those errors. For example, `#[reflect(Hash, Hash)]` points to the second `Hash` as the source of an error. Also, in the following example, the compiler error now points to the `Hash` in `#[reflect(Hash)]` rather than to the derive macro: ```rs #[derive(Reflect)] #[reflect(Hash)] // <-- compiler error points to `Hash` for lack of a `Hash` implementation struct Foo; ``` --- ## Changelog Changed - Using multiple `#[reflect(...)]` or `#[reflect_value(...)]` attributes now merges the trait lists. For example, `#[reflect(Debug, Hash)] #[reflect(PartialEq, Default)]` is equivalent to `#[reflect(Debug, Hash, PartialEq, Default)]`. - Multiple `#[reflect(...)]` and `#[reflect_value(...)]` attributes were previously accepted, but only the last attribute was respected. - Using both `#[reflect(...)]` and `#[reflect_value(...)]` was previously accepted, but had surprising behavior. This is no longer accepted. - Improved error messages for `#[derive(Reflect)]` by propagating useful span information. Many errors should now point to the source of those errors rather than to the derive macro.
2022-10-17 14:38:56 +00:00
reflect: stable type path v2 (#7184) # Objective - Introduce a stable alternative to [`std::any::type_name`](https://doc.rust-lang.org/std/any/fn.type_name.html). - Rewrite of #5805 with heavy inspiration in design. - On the path to #5830. - Part of solving #3327. ## Solution - Add a `TypePath` trait for static stable type path/name information. - Add a `TypePath` derive macro. - Add a `impl_type_path` macro for implementing internal and foreign types in `bevy_reflect`. --- ## Changelog - Added `TypePath` trait. - Added `DynamicTypePath` trait and `get_type_path` method to `Reflect`. - Added a `TypePath` derive macro. - Added a `bevy_reflect::impl_type_path` for implementing `TypePath` on internal and foreign types in `bevy_reflect`. - Changed `bevy_reflect::utility::(Non)GenericTypeInfoCell` to `(Non)GenericTypedCell<T>` which allows us to be generic over both `TypeInfo` and `TypePath`. - `TypePath` is now a supertrait of `Asset`, `Material` and `Material2d`. - `impl_reflect_struct` needs a `#[type_path = "..."]` attribute to be specified. - `impl_reflect_value` needs to either specify path starting with a double colon (`::core::option::Option`) or an `in my_crate::foo` declaration. - Added `bevy_reflect_derive::ReflectTypePath`. - Most uses of `Ident` in `bevy_reflect_derive` changed to use `ReflectTypePath`. ## Migration Guide - Implementors of `Asset`, `Material` and `Material2d` now also need to derive `TypePath`. - Manual implementors of `Reflect` will need to implement the new `get_type_path` method. ## Open Questions - [x] ~This PR currently does not migrate any usages of `std::any::type_name` to use `bevy_reflect::TypePath` to ease the review process. Should it?~ Migration will be left to a follow-up PR. - [ ] This PR adds a lot of `#[derive(TypePath)]` and `T: TypePath` to satisfy new bounds, mostly when deriving `TypeUuid`. Should we make `TypePath` a supertrait of `TypeUuid`? [Should we remove `TypeUuid` in favour of `TypePath`?](https://github.com/bevyengine/bevy/pull/5805/files/2afbd855327c4b68e0a6b6f03118f289988441a4#r961067892)
2023-06-05 20:31:20 +00:00
let type_path = ReflectTypePath::Internal {
ident: &input.ident,
custom_path,
generics: &input.generics,
};
let meta = ReflectMeta::new(type_path, container_attributes);
if provenance.source == ReflectImplSource::ImplRemoteType
&& meta.type_path_attrs().should_auto_derive()
&& !meta.type_path().has_custom_path()
{
return Err(syn::Error::new(
meta.type_path().span(),
format!("a #[{TYPE_PATH_ATTRIBUTE_NAME} = \"...\"] attribute must be specified when using {provenance}")
));
}
#[cfg(feature = "documentation")]
let meta = meta.with_docs(doc);
Support multiple `#[reflect]`/`#[reflect_value]` + improve error messages (#6237) # Objective Currently, surprising behavior happens when specifying `#[reflect(...)]` or `#[reflect_value(...)]` multiple times. Rather than merging the traits lists from all attributes, only the trait list from the last attribute is used. For example, in the following code, only the `Debug` and `Hash` traits are reflected and not `Default` or `PartialEq`: ```rs #[derive(Debug, PartialEq, Hash, Default, Reflect)] #[reflect(PartialEq, Default)] #[reflect(Debug, Hash)] struct Foo; ``` This is especially important when some traits should only be reflected under certain circumstances. For example, this previously had surprisingly behavior when the "serialize" feature is enabled: ```rs #[derive(Debug, Hash, Reflect)] #[reflect(Debug, Hash)] #[cfg_attr( feature = "serialize", derive(Serialize, Deserialize), reflect(Serialize, Deserialize) ] struct Foo; ``` In addition, compile error messages generated from using the derive macro often point to the `#[derive(Reflect)]` rather than to the source of the error. It would be a lot more helpful if the compiler errors pointed to what specifically caused the error rather than just to the derive macro itself. ## Solution Merge the trait lists in all `#[reflect(...)]` and `#[reflect_value(...)]` attributes. Additionally, make `#[reflect]` and `#[reflect_value]` mutually exclusive. Additionally, span information is carried throughout some parts of the code now to ensure that error messages point to more useful places and better indicate what caused those errors. For example, `#[reflect(Hash, Hash)]` points to the second `Hash` as the source of an error. Also, in the following example, the compiler error now points to the `Hash` in `#[reflect(Hash)]` rather than to the derive macro: ```rs #[derive(Reflect)] #[reflect(Hash)] // <-- compiler error points to `Hash` for lack of a `Hash` implementation struct Foo; ``` --- ## Changelog Changed - Using multiple `#[reflect(...)]` or `#[reflect_value(...)]` attributes now merges the trait lists. For example, `#[reflect(Debug, Hash)] #[reflect(PartialEq, Default)]` is equivalent to `#[reflect(Debug, Hash, PartialEq, Default)]`. - Multiple `#[reflect(...)]` and `#[reflect_value(...)]` attributes were previously accepted, but only the last attribute was respected. - Using both `#[reflect(...)]` and `#[reflect_value(...)]` was previously accepted, but had surprising behavior. This is no longer accepted. - Improved error messages for `#[derive(Reflect)]` by propagating useful span information. Many errors should now point to the source of those errors rather than to the derive macro.
2022-10-17 14:38:56 +00:00
// Use normal reflection if unspecified
let reflect_mode = reflect_mode.unwrap_or(ReflectMode::Normal);
if reflect_mode == ReflectMode::Value {
return Ok(Self::Value(meta));
bevy_reflect: Reflect enums (#4761) # Objective > This is a revival of #1347. Credit for the original PR should go to @Davier. Currently, enums are treated as `ReflectRef::Value` types by `bevy_reflect`. Obviously, there needs to be better a better representation for enums using the reflection API. ## Solution Based on prior work from @Davier, an `Enum` trait has been added as well as the ability to automatically implement it via the `Reflect` derive macro. This allows enums to be expressed dynamically: ```rust #[derive(Reflect)] enum Foo { A, B(usize), C { value: f32 }, } let mut foo = Foo::B(123); assert_eq!("B", foo.variant_name()); assert_eq!(1, foo.field_len()); let new_value = DynamicEnum::from(Foo::C { value: 1.23 }); foo.apply(&new_value); assert_eq!(Foo::C{value: 1.23}, foo); ``` ### Features #### Derive Macro Use the `#[derive(Reflect)]` macro to automatically implement the `Enum` trait for enum definitions. Optionally, you can use `#[reflect(ignore)]` with both variants and variant fields, just like you can with structs. These ignored items will not be considered as part of the reflection and cannot be accessed via reflection. ```rust #[derive(Reflect)] enum TestEnum { A, // Uncomment to ignore all of `B` // #[reflect(ignore)] B(usize), C { // Uncomment to ignore only field `foo` of `C` // #[reflect(ignore)] foo: f32, bar: bool, }, } ``` #### Dynamic Enums Enums may be created/represented dynamically via the `DynamicEnum` struct. The main purpose of this struct is to allow enums to be deserialized into a partial state and to allow dynamic patching. In order to ensure conversion from a `DynamicEnum` to a concrete enum type goes smoothly, be sure to add `FromReflect` to your derive macro. ```rust let mut value = TestEnum::A; // Create from a concrete instance let dyn_enum = DynamicEnum::from(TestEnum::B(123)); value.apply(&dyn_enum); assert_eq!(TestEnum::B(123), value); // Create a purely dynamic instance let dyn_enum = DynamicEnum::new("TestEnum", "A", ()); value.apply(&dyn_enum); assert_eq!(TestEnum::A, value); ``` #### Variants An enum value is always represented as one of its variants— never the enum in its entirety. ```rust let value = TestEnum::A; assert_eq!("A", value.variant_name()); // Since we are using the `A` variant, we cannot also be the `B` variant assert_ne!("B", value.variant_name()); ``` All variant types are representable within the `Enum` trait: unit, struct, and tuple. You can get the current type like: ```rust match value.variant_type() { VariantType::Unit => println!("A unit variant!"), VariantType::Struct => println!("A struct variant!"), VariantType::Tuple => println!("A tuple variant!"), } ``` > Notice that they don't contain any values representing the fields. These are purely tags. If a variant has them, you can access the fields as well: ```rust let mut value = TestEnum::C { foo: 1.23, bar: false }; // Read/write specific fields *value.field_mut("bar").unwrap() = true; // Iterate over the entire collection of fields for field in value.iter_fields() { println!("{} = {:?}", field.name(), field.value()); } ``` #### Variant Swapping It might seem odd to group all variant types under a single trait (why allow `iter_fields` on a unit variant?), but the reason this was done ~~is to easily allow *variant swapping*.~~ As I was recently drafting up the **Design Decisions** section, I discovered that other solutions could have been made to work with variant swapping. So while there are reasons to keep the all-in-one approach, variant swapping is _not_ one of them. ```rust let mut value: Box<dyn Enum> = Box::new(TestEnum::A); value.set(Box::new(TestEnum::B(123))).unwrap(); ``` #### Serialization Enums can be serialized and deserialized via reflection without needing to implement `Serialize` or `Deserialize` themselves (which can save thousands of lines of generated code). Below are the ways an enum can be serialized. > Note, like the rest of reflection-based serialization, the order of the keys in these representations is important! ##### Unit ```json { "type": "my_crate::TestEnum", "enum": { "variant": "A" } } ``` ##### Tuple ```json { "type": "my_crate::TestEnum", "enum": { "variant": "B", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` <details> <summary>Effects on Option</summary> This ends up making `Option` look a little ugly: ```json { "type": "core::option::Option<usize>", "enum": { "variant": "Some", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` </details> ##### Struct ```json { "type": "my_crate::TestEnum", "enum": { "variant": "C", "struct": { "foo": { "type": "f32", "value": 1.23 }, "bar": { "type": "bool", "value": false } } } } ``` ## Design Decisions <details> <summary><strong>View Section</strong></summary> This section is here to provide some context for why certain decisions were made for this PR, alternatives that could have been used instead, and what could be improved upon in the future. ### Variant Representation One of the biggest decisions was to decide on how to represent variants. The current design uses a "all-in-one" design where unit, tuple, and struct variants are all simultaneously represented by the `Enum` trait. This is not the only way it could have been done, though. #### Alternatives ##### 1. Variant Traits One way of representing variants would be to define traits for each variant, implementing them whenever an enum featured at least one instance of them. This would allow us to define variants like: ```rust pub trait Enum: Reflect { fn variant(&self) -> Variant; } pub enum Variant<'a> { Unit, Tuple(&'a dyn TupleVariant), Struct(&'a dyn StructVariant), } pub trait TupleVariant { fn field_len(&self) -> usize; // ... } ``` And then do things like: ```rust fn get_tuple_len(foo: &dyn Enum) -> usize { match foo.variant() { Variant::Tuple(tuple) => tuple.field_len(), _ => panic!("not a tuple variant!") } } ``` The reason this PR does not go with this approach is because of the fact that variants are not separate types. In other words, we cannot implement traits on specific variants— these cover the *entire* enum. This means we offer an easy footgun: ```rust let foo: Option<i32> = None; let my_enum = Box::new(foo) as Box<dyn TupleVariant>; ``` Here, `my_enum` contains `foo`, which is a unit variant. However, since we need to implement `TupleVariant` for `Option` as a whole, it's possible to perform such a cast. This is obviously wrong, but could easily go unnoticed. So unfortunately, this makes it not a good candidate for representing variants. ##### 2. Variant Structs To get around the issue of traits necessarily needing to apply to both the enum and its variants, we could instead use structs that are created on a per-variant basis. This was also considered but was ultimately [[removed](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c)](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c) due to concerns about allocations. Each variant struct would probably look something like: ```rust pub trait Enum: Reflect { fn variant_mut(&self) -> VariantMut; } pub enum VariantMut<'a> { Unit, Tuple(TupleVariantMut), Struct(StructVariantMut), } struct StructVariantMut<'a> { fields: Vec<&'a mut dyn Reflect>, field_indices: HashMap<Cow<'static, str>, usize> } ``` This allows us to isolate struct variants into their own defined struct and define methods specifically for their use. It also prevents users from casting to it since it's not a trait. However, this is not an optimal solution. Both `field_indices` and `fields` will require an allocation (remember, a `Box<[T]>` still requires a `Vec<T>` in order to be constructed). This *might* be a problem if called frequently enough. ##### 3. Generated Structs The original design, implemented by @Davier, instead generates structs specific for each variant. So if we had a variant path like `Foo::Bar`, we'd generate a struct named `FooBarWrapper`. This would be newtyped around the original enum and forward tuple or struct methods to the enum with the chosen variant. Because it involved using the `Tuple` and `Struct` traits (which are also both bound on `Reflect`), this meant a bit more code had to be generated. For a single struct variant with one field, the generated code amounted to ~110LoC. However, each new field added to that variant only added ~6 more LoC. In order to work properly, the enum had to be transmuted to the generated struct: ```rust fn variant(&self) -> crate::EnumVariant<'_> { match self { Foo::Bar {value: i32} => { let wrapper_ref = unsafe { std::mem::transmute::<&Self, &FooBarWrapper>(self) }; crate::EnumVariant::Struct(wrapper_ref as &dyn crate::Struct) } } } ``` This works because `FooBarWrapper` is defined as `repr(transparent)`. Out of all the alternatives, this would probably be the one most likely to be used again in the future. The reasons for why this PR did not continue to use it was because: * To reduce generated code (which would hopefully speed up compile times) * To avoid cluttering the code with generated structs not visible to the user * To keep bevy_reflect simple and extensible (these generated structs act as proxies and might not play well with current or future systems) * To avoid additional unsafe blocks * My own misunderstanding of @Davier's code That last point is obviously on me. I misjudged the code to be too unsafe and unable to handle variant swapping (which it probably could) when I was rebasing it. Looking over it again when writing up this whole section, I see that it was actually a pretty clever way of handling variant representation. #### Benefits of All-in-One As stated before, the current implementation uses an all-in-one approach. All variants are capable of containing fields as far as `Enum` is concerned. This provides a few benefits that the alternatives do not (reduced indirection, safer code, etc.). The biggest benefit, though, is direct field access. Rather than forcing users to have to go through pattern matching, we grant direct access to the fields contained by the current variant. The reason we can do this is because all of the pattern matching happens internally. Getting the field at index `2` will automatically return `Some(...)` for the current variant if it has a field at that index or `None` if it doesn't (or can't). This could be useful for scenarios where the variant has already been verified or just set/swapped (or even where the type of variant doesn't matter): ```rust let dyn_enum: &mut dyn Enum = &mut Foo::Bar {value: 123}; // We know it's the `Bar` variant let field = dyn_enum.field("value").unwrap(); ``` Reflection is not a type-safe abstraction— almost every return value is wrapped in `Option<...>`. There are plenty of places to check and recheck that a value is what Reflect says it is. Forcing users to have to go through `match` each time they want to access a field might just be an extra step among dozens of other verification processes. Some might disagree, but ultimately, my view is that the benefit here is an improvement to the ergonomics and usability of reflected enums. </details> --- ## Changelog ### Added * Added `Enum` trait * Added `Enum` impl to `Reflect` derive macro * Added `DynamicEnum` struct * Added `DynamicVariant` * Added `EnumInfo` * Added `VariantInfo` * Added `StructVariantInfo` * Added `TupleVariantInfo` * Added `UnitVariantInfo` * Added serializtion/deserialization support for enums * Added `EnumSerializer` * Added `VariantType` * Added `VariantFieldIter` * Added `VariantField` * Added `enum_partial_eq(...)` * Added `enum_hash(...)` ### Changed * `Option<T>` now implements `Enum` * `bevy_window` now depends on `bevy_reflect` * Implemented `Reflect` and `FromReflect` for `WindowId` * Derive `FromReflect` on `PerspectiveProjection` * Derive `FromReflect` on `OrthographicProjection` * Derive `FromReflect` on `WindowOrigin` * Derive `FromReflect` on `ScalingMode` * Derive `FromReflect` on `DepthCalculation` ## Migration Guide * Enums no longer need to be treated as values and usages of `#[reflect_value(...)]` can be removed or replaced by `#[reflect(...)]` * Enums (including `Option<T>`) now take a different format when serializing. The format is described above, but this may cause issues for existing scenes that make use of enums. --- Also shout out to @nicopap for helping clean up some of the code here! It's a big feature so help like this is really appreciated! Co-authored-by: Gino Valente <gino.valente.code@gmail.com>
2022-08-02 22:14:41 +00:00
}
return match &input.data {
Data::Struct(data) => {
let fields = Self::collect_struct_fields(&data.fields)?;
bevy_reflect: Reflect enums (#4761) # Objective > This is a revival of #1347. Credit for the original PR should go to @Davier. Currently, enums are treated as `ReflectRef::Value` types by `bevy_reflect`. Obviously, there needs to be better a better representation for enums using the reflection API. ## Solution Based on prior work from @Davier, an `Enum` trait has been added as well as the ability to automatically implement it via the `Reflect` derive macro. This allows enums to be expressed dynamically: ```rust #[derive(Reflect)] enum Foo { A, B(usize), C { value: f32 }, } let mut foo = Foo::B(123); assert_eq!("B", foo.variant_name()); assert_eq!(1, foo.field_len()); let new_value = DynamicEnum::from(Foo::C { value: 1.23 }); foo.apply(&new_value); assert_eq!(Foo::C{value: 1.23}, foo); ``` ### Features #### Derive Macro Use the `#[derive(Reflect)]` macro to automatically implement the `Enum` trait for enum definitions. Optionally, you can use `#[reflect(ignore)]` with both variants and variant fields, just like you can with structs. These ignored items will not be considered as part of the reflection and cannot be accessed via reflection. ```rust #[derive(Reflect)] enum TestEnum { A, // Uncomment to ignore all of `B` // #[reflect(ignore)] B(usize), C { // Uncomment to ignore only field `foo` of `C` // #[reflect(ignore)] foo: f32, bar: bool, }, } ``` #### Dynamic Enums Enums may be created/represented dynamically via the `DynamicEnum` struct. The main purpose of this struct is to allow enums to be deserialized into a partial state and to allow dynamic patching. In order to ensure conversion from a `DynamicEnum` to a concrete enum type goes smoothly, be sure to add `FromReflect` to your derive macro. ```rust let mut value = TestEnum::A; // Create from a concrete instance let dyn_enum = DynamicEnum::from(TestEnum::B(123)); value.apply(&dyn_enum); assert_eq!(TestEnum::B(123), value); // Create a purely dynamic instance let dyn_enum = DynamicEnum::new("TestEnum", "A", ()); value.apply(&dyn_enum); assert_eq!(TestEnum::A, value); ``` #### Variants An enum value is always represented as one of its variants— never the enum in its entirety. ```rust let value = TestEnum::A; assert_eq!("A", value.variant_name()); // Since we are using the `A` variant, we cannot also be the `B` variant assert_ne!("B", value.variant_name()); ``` All variant types are representable within the `Enum` trait: unit, struct, and tuple. You can get the current type like: ```rust match value.variant_type() { VariantType::Unit => println!("A unit variant!"), VariantType::Struct => println!("A struct variant!"), VariantType::Tuple => println!("A tuple variant!"), } ``` > Notice that they don't contain any values representing the fields. These are purely tags. If a variant has them, you can access the fields as well: ```rust let mut value = TestEnum::C { foo: 1.23, bar: false }; // Read/write specific fields *value.field_mut("bar").unwrap() = true; // Iterate over the entire collection of fields for field in value.iter_fields() { println!("{} = {:?}", field.name(), field.value()); } ``` #### Variant Swapping It might seem odd to group all variant types under a single trait (why allow `iter_fields` on a unit variant?), but the reason this was done ~~is to easily allow *variant swapping*.~~ As I was recently drafting up the **Design Decisions** section, I discovered that other solutions could have been made to work with variant swapping. So while there are reasons to keep the all-in-one approach, variant swapping is _not_ one of them. ```rust let mut value: Box<dyn Enum> = Box::new(TestEnum::A); value.set(Box::new(TestEnum::B(123))).unwrap(); ``` #### Serialization Enums can be serialized and deserialized via reflection without needing to implement `Serialize` or `Deserialize` themselves (which can save thousands of lines of generated code). Below are the ways an enum can be serialized. > Note, like the rest of reflection-based serialization, the order of the keys in these representations is important! ##### Unit ```json { "type": "my_crate::TestEnum", "enum": { "variant": "A" } } ``` ##### Tuple ```json { "type": "my_crate::TestEnum", "enum": { "variant": "B", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` <details> <summary>Effects on Option</summary> This ends up making `Option` look a little ugly: ```json { "type": "core::option::Option<usize>", "enum": { "variant": "Some", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` </details> ##### Struct ```json { "type": "my_crate::TestEnum", "enum": { "variant": "C", "struct": { "foo": { "type": "f32", "value": 1.23 }, "bar": { "type": "bool", "value": false } } } } ``` ## Design Decisions <details> <summary><strong>View Section</strong></summary> This section is here to provide some context for why certain decisions were made for this PR, alternatives that could have been used instead, and what could be improved upon in the future. ### Variant Representation One of the biggest decisions was to decide on how to represent variants. The current design uses a "all-in-one" design where unit, tuple, and struct variants are all simultaneously represented by the `Enum` trait. This is not the only way it could have been done, though. #### Alternatives ##### 1. Variant Traits One way of representing variants would be to define traits for each variant, implementing them whenever an enum featured at least one instance of them. This would allow us to define variants like: ```rust pub trait Enum: Reflect { fn variant(&self) -> Variant; } pub enum Variant<'a> { Unit, Tuple(&'a dyn TupleVariant), Struct(&'a dyn StructVariant), } pub trait TupleVariant { fn field_len(&self) -> usize; // ... } ``` And then do things like: ```rust fn get_tuple_len(foo: &dyn Enum) -> usize { match foo.variant() { Variant::Tuple(tuple) => tuple.field_len(), _ => panic!("not a tuple variant!") } } ``` The reason this PR does not go with this approach is because of the fact that variants are not separate types. In other words, we cannot implement traits on specific variants— these cover the *entire* enum. This means we offer an easy footgun: ```rust let foo: Option<i32> = None; let my_enum = Box::new(foo) as Box<dyn TupleVariant>; ``` Here, `my_enum` contains `foo`, which is a unit variant. However, since we need to implement `TupleVariant` for `Option` as a whole, it's possible to perform such a cast. This is obviously wrong, but could easily go unnoticed. So unfortunately, this makes it not a good candidate for representing variants. ##### 2. Variant Structs To get around the issue of traits necessarily needing to apply to both the enum and its variants, we could instead use structs that are created on a per-variant basis. This was also considered but was ultimately [[removed](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c)](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c) due to concerns about allocations. Each variant struct would probably look something like: ```rust pub trait Enum: Reflect { fn variant_mut(&self) -> VariantMut; } pub enum VariantMut<'a> { Unit, Tuple(TupleVariantMut), Struct(StructVariantMut), } struct StructVariantMut<'a> { fields: Vec<&'a mut dyn Reflect>, field_indices: HashMap<Cow<'static, str>, usize> } ``` This allows us to isolate struct variants into their own defined struct and define methods specifically for their use. It also prevents users from casting to it since it's not a trait. However, this is not an optimal solution. Both `field_indices` and `fields` will require an allocation (remember, a `Box<[T]>` still requires a `Vec<T>` in order to be constructed). This *might* be a problem if called frequently enough. ##### 3. Generated Structs The original design, implemented by @Davier, instead generates structs specific for each variant. So if we had a variant path like `Foo::Bar`, we'd generate a struct named `FooBarWrapper`. This would be newtyped around the original enum and forward tuple or struct methods to the enum with the chosen variant. Because it involved using the `Tuple` and `Struct` traits (which are also both bound on `Reflect`), this meant a bit more code had to be generated. For a single struct variant with one field, the generated code amounted to ~110LoC. However, each new field added to that variant only added ~6 more LoC. In order to work properly, the enum had to be transmuted to the generated struct: ```rust fn variant(&self) -> crate::EnumVariant<'_> { match self { Foo::Bar {value: i32} => { let wrapper_ref = unsafe { std::mem::transmute::<&Self, &FooBarWrapper>(self) }; crate::EnumVariant::Struct(wrapper_ref as &dyn crate::Struct) } } } ``` This works because `FooBarWrapper` is defined as `repr(transparent)`. Out of all the alternatives, this would probably be the one most likely to be used again in the future. The reasons for why this PR did not continue to use it was because: * To reduce generated code (which would hopefully speed up compile times) * To avoid cluttering the code with generated structs not visible to the user * To keep bevy_reflect simple and extensible (these generated structs act as proxies and might not play well with current or future systems) * To avoid additional unsafe blocks * My own misunderstanding of @Davier's code That last point is obviously on me. I misjudged the code to be too unsafe and unable to handle variant swapping (which it probably could) when I was rebasing it. Looking over it again when writing up this whole section, I see that it was actually a pretty clever way of handling variant representation. #### Benefits of All-in-One As stated before, the current implementation uses an all-in-one approach. All variants are capable of containing fields as far as `Enum` is concerned. This provides a few benefits that the alternatives do not (reduced indirection, safer code, etc.). The biggest benefit, though, is direct field access. Rather than forcing users to have to go through pattern matching, we grant direct access to the fields contained by the current variant. The reason we can do this is because all of the pattern matching happens internally. Getting the field at index `2` will automatically return `Some(...)` for the current variant if it has a field at that index or `None` if it doesn't (or can't). This could be useful for scenarios where the variant has already been verified or just set/swapped (or even where the type of variant doesn't matter): ```rust let dyn_enum: &mut dyn Enum = &mut Foo::Bar {value: 123}; // We know it's the `Bar` variant let field = dyn_enum.field("value").unwrap(); ``` Reflection is not a type-safe abstraction— almost every return value is wrapped in `Option<...>`. There are plenty of places to check and recheck that a value is what Reflect says it is. Forcing users to have to go through `match` each time they want to access a field might just be an extra step among dozens of other verification processes. Some might disagree, but ultimately, my view is that the benefit here is an improvement to the ergonomics and usability of reflected enums. </details> --- ## Changelog ### Added * Added `Enum` trait * Added `Enum` impl to `Reflect` derive macro * Added `DynamicEnum` struct * Added `DynamicVariant` * Added `EnumInfo` * Added `VariantInfo` * Added `StructVariantInfo` * Added `TupleVariantInfo` * Added `UnitVariantInfo` * Added serializtion/deserialization support for enums * Added `EnumSerializer` * Added `VariantType` * Added `VariantFieldIter` * Added `VariantField` * Added `enum_partial_eq(...)` * Added `enum_hash(...)` ### Changed * `Option<T>` now implements `Enum` * `bevy_window` now depends on `bevy_reflect` * Implemented `Reflect` and `FromReflect` for `WindowId` * Derive `FromReflect` on `PerspectiveProjection` * Derive `FromReflect` on `OrthographicProjection` * Derive `FromReflect` on `WindowOrigin` * Derive `FromReflect` on `ScalingMode` * Derive `FromReflect` on `DepthCalculation` ## Migration Guide * Enums no longer need to be treated as values and usages of `#[reflect_value(...)]` can be removed or replaced by `#[reflect(...)]` * Enums (including `Option<T>`) now take a different format when serializing. The format is described above, but this may cause issues for existing scenes that make use of enums. --- Also shout out to @nicopap for helping clean up some of the code here! It's a big feature so help like this is really appreciated! Co-authored-by: Gino Valente <gino.valente.code@gmail.com>
2022-08-02 22:14:41 +00:00
let reflect_struct = ReflectStruct {
meta,
bevy_reflect: Fix ignored/skipped field order (#7575) # Objective Fixes #5101 Alternative to #6511 ## Solution Corrected the behavior for ignored fields in `FromReflect`, which was previously using the incorrect field indexes. Similarly, fields marked with `#[reflect(skip_serializing)]` no longer break when using `FromReflect` after deserialization. This was done by modifying `SerializationData` to store a function pointer that can later be used to generate a default instance of the skipped field during deserialization. The function pointer points to a function generated by the derive macro using the behavior designated by `#[reflect(default)]` (or just `Default` if none provided). The entire output of the macro is now wrapped in an [unnamed constant](https://doc.rust-lang.org/stable/reference/items/constant-items.html#unnamed-constant) which keeps this behavior hygienic. #### Rationale The biggest downside to this approach is that it requires fields marked `#[reflect(skip_serializing)]` to provide the ability to create a default instance— either via a `Default` impl or by specifying a custom one. While this isn't great, I think it might be justified by the fact that we really need to create this value when using `FromReflect` on a deserialized object. And we need to do this _during_ deserialization because after that (at least for tuples and tuple structs) we lose information about which field is which: _"is the value at index 1 in this `DynamicTupleStruct` the actual value for index 1 or is it really the value for index 2 since index 1 is skippable...?"_ #### Alternatives An alternative would be to store `Option<Box<dyn Reflect>>` within `DynamicTuple` and `DynamicTupleStruct` instead of just `Box<dyn Reflect>`. This would allow us to insert "empty"/"missing" fields during deserialization, thus saving the positional information of the skipped fields. However, this may require changing the API of `Tuple` and `TupleStruct` such that they can account for their dynamic counterparts returning `None` for a skipped field. In practice this would probably mean exposing the `Option`-ness of the dynamics onto implementors via methods like `Tuple::drain` or `TupleStruct::field`. Personally, I think requiring `Default` would be better than muddying up the API to account for these special cases. But I'm open to trying out this other approach if the community feels that it's better. --- ## Changelog ### Public Changes #### Fixed - The behaviors of `#[reflect(ignore)]` and `#[reflect(skip_serializing)]` are no longer dependent on field order #### Changed - Fields marked with `#[reflect(skip_serializing)]` now need to either implement `Default` or specify a custom default function using `#[reflect(default = "path::to::some_func")]` - Deserializing a type with fields marked `#[reflect(skip_serializing)]` will now include that field initialized to its specified default value - `SerializationData::new` now takes the new `SkippedField` struct along with the skipped field index - Renamed `SerializationData::is_ignored_field` to `SerializationData::is_field_skipped` #### Added - Added `SkippedField` struct - Added methods `SerializationData::generate_default` and `SerializationData::iter_skipped` ### Internal Changes #### Changed - Replaced `members_to_serialization_denylist` and `BitSet<u32>` with `SerializationDataDef` - The `Reflect` derive is more hygienic as it now outputs within an [unnamed constant](https://doc.rust-lang.org/stable/reference/items/constant-items.html#unnamed-constant) - `StructField::index` has been split up into `StructField::declaration_index` and `StructField::reflection_index` #### Removed - Removed `bitset` dependency ## Migration Guide * Fields marked `#[reflect(skip_serializing)]` now must implement `Default` or specify a custom default function with `#[reflect(default = "path::to::some_func")]` ```rust #[derive(Reflect)] struct MyStruct { #[reflect(skip_serializing)] #[reflect(default = "get_foo_default")] foo: Foo, // <- `Foo` does not impl `Default` so requires a custom function #[reflect(skip_serializing)] bar: Bar, // <- `Bar` impls `Default` } #[derive(Reflect)] struct Foo(i32); #[derive(Reflect, Default)] struct Bar(i32); fn get_foo_default() -> Foo { Foo(123) } ``` * `SerializationData::new` has been changed to expect an iterator of `(usize, SkippedField)` rather than one of just `usize` ```rust // BEFORE SerializationData::new([0, 3].into_iter()); // AFTER SerializationData::new([ (0, SkippedField::new(field_0_default_fn)), (3, SkippedField::new(field_3_default_fn)), ].into_iter()); ``` * `Serialization::is_ignored_field` has been renamed to `Serialization::is_field_skipped` * Fields marked `#[reflect(skip_serializing)]` are now included in deserialization output. This may affect logic that expected those fields to be absent.
2023-10-22 12:43:31 +00:00
serialization_data: SerializationDataDef::new(&fields)?,
fields,
bevy_reflect: Reflect enums (#4761) # Objective > This is a revival of #1347. Credit for the original PR should go to @Davier. Currently, enums are treated as `ReflectRef::Value` types by `bevy_reflect`. Obviously, there needs to be better a better representation for enums using the reflection API. ## Solution Based on prior work from @Davier, an `Enum` trait has been added as well as the ability to automatically implement it via the `Reflect` derive macro. This allows enums to be expressed dynamically: ```rust #[derive(Reflect)] enum Foo { A, B(usize), C { value: f32 }, } let mut foo = Foo::B(123); assert_eq!("B", foo.variant_name()); assert_eq!(1, foo.field_len()); let new_value = DynamicEnum::from(Foo::C { value: 1.23 }); foo.apply(&new_value); assert_eq!(Foo::C{value: 1.23}, foo); ``` ### Features #### Derive Macro Use the `#[derive(Reflect)]` macro to automatically implement the `Enum` trait for enum definitions. Optionally, you can use `#[reflect(ignore)]` with both variants and variant fields, just like you can with structs. These ignored items will not be considered as part of the reflection and cannot be accessed via reflection. ```rust #[derive(Reflect)] enum TestEnum { A, // Uncomment to ignore all of `B` // #[reflect(ignore)] B(usize), C { // Uncomment to ignore only field `foo` of `C` // #[reflect(ignore)] foo: f32, bar: bool, }, } ``` #### Dynamic Enums Enums may be created/represented dynamically via the `DynamicEnum` struct. The main purpose of this struct is to allow enums to be deserialized into a partial state and to allow dynamic patching. In order to ensure conversion from a `DynamicEnum` to a concrete enum type goes smoothly, be sure to add `FromReflect` to your derive macro. ```rust let mut value = TestEnum::A; // Create from a concrete instance let dyn_enum = DynamicEnum::from(TestEnum::B(123)); value.apply(&dyn_enum); assert_eq!(TestEnum::B(123), value); // Create a purely dynamic instance let dyn_enum = DynamicEnum::new("TestEnum", "A", ()); value.apply(&dyn_enum); assert_eq!(TestEnum::A, value); ``` #### Variants An enum value is always represented as one of its variants— never the enum in its entirety. ```rust let value = TestEnum::A; assert_eq!("A", value.variant_name()); // Since we are using the `A` variant, we cannot also be the `B` variant assert_ne!("B", value.variant_name()); ``` All variant types are representable within the `Enum` trait: unit, struct, and tuple. You can get the current type like: ```rust match value.variant_type() { VariantType::Unit => println!("A unit variant!"), VariantType::Struct => println!("A struct variant!"), VariantType::Tuple => println!("A tuple variant!"), } ``` > Notice that they don't contain any values representing the fields. These are purely tags. If a variant has them, you can access the fields as well: ```rust let mut value = TestEnum::C { foo: 1.23, bar: false }; // Read/write specific fields *value.field_mut("bar").unwrap() = true; // Iterate over the entire collection of fields for field in value.iter_fields() { println!("{} = {:?}", field.name(), field.value()); } ``` #### Variant Swapping It might seem odd to group all variant types under a single trait (why allow `iter_fields` on a unit variant?), but the reason this was done ~~is to easily allow *variant swapping*.~~ As I was recently drafting up the **Design Decisions** section, I discovered that other solutions could have been made to work with variant swapping. So while there are reasons to keep the all-in-one approach, variant swapping is _not_ one of them. ```rust let mut value: Box<dyn Enum> = Box::new(TestEnum::A); value.set(Box::new(TestEnum::B(123))).unwrap(); ``` #### Serialization Enums can be serialized and deserialized via reflection without needing to implement `Serialize` or `Deserialize` themselves (which can save thousands of lines of generated code). Below are the ways an enum can be serialized. > Note, like the rest of reflection-based serialization, the order of the keys in these representations is important! ##### Unit ```json { "type": "my_crate::TestEnum", "enum": { "variant": "A" } } ``` ##### Tuple ```json { "type": "my_crate::TestEnum", "enum": { "variant": "B", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` <details> <summary>Effects on Option</summary> This ends up making `Option` look a little ugly: ```json { "type": "core::option::Option<usize>", "enum": { "variant": "Some", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` </details> ##### Struct ```json { "type": "my_crate::TestEnum", "enum": { "variant": "C", "struct": { "foo": { "type": "f32", "value": 1.23 }, "bar": { "type": "bool", "value": false } } } } ``` ## Design Decisions <details> <summary><strong>View Section</strong></summary> This section is here to provide some context for why certain decisions were made for this PR, alternatives that could have been used instead, and what could be improved upon in the future. ### Variant Representation One of the biggest decisions was to decide on how to represent variants. The current design uses a "all-in-one" design where unit, tuple, and struct variants are all simultaneously represented by the `Enum` trait. This is not the only way it could have been done, though. #### Alternatives ##### 1. Variant Traits One way of representing variants would be to define traits for each variant, implementing them whenever an enum featured at least one instance of them. This would allow us to define variants like: ```rust pub trait Enum: Reflect { fn variant(&self) -> Variant; } pub enum Variant<'a> { Unit, Tuple(&'a dyn TupleVariant), Struct(&'a dyn StructVariant), } pub trait TupleVariant { fn field_len(&self) -> usize; // ... } ``` And then do things like: ```rust fn get_tuple_len(foo: &dyn Enum) -> usize { match foo.variant() { Variant::Tuple(tuple) => tuple.field_len(), _ => panic!("not a tuple variant!") } } ``` The reason this PR does not go with this approach is because of the fact that variants are not separate types. In other words, we cannot implement traits on specific variants— these cover the *entire* enum. This means we offer an easy footgun: ```rust let foo: Option<i32> = None; let my_enum = Box::new(foo) as Box<dyn TupleVariant>; ``` Here, `my_enum` contains `foo`, which is a unit variant. However, since we need to implement `TupleVariant` for `Option` as a whole, it's possible to perform such a cast. This is obviously wrong, but could easily go unnoticed. So unfortunately, this makes it not a good candidate for representing variants. ##### 2. Variant Structs To get around the issue of traits necessarily needing to apply to both the enum and its variants, we could instead use structs that are created on a per-variant basis. This was also considered but was ultimately [[removed](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c)](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c) due to concerns about allocations. Each variant struct would probably look something like: ```rust pub trait Enum: Reflect { fn variant_mut(&self) -> VariantMut; } pub enum VariantMut<'a> { Unit, Tuple(TupleVariantMut), Struct(StructVariantMut), } struct StructVariantMut<'a> { fields: Vec<&'a mut dyn Reflect>, field_indices: HashMap<Cow<'static, str>, usize> } ``` This allows us to isolate struct variants into their own defined struct and define methods specifically for their use. It also prevents users from casting to it since it's not a trait. However, this is not an optimal solution. Both `field_indices` and `fields` will require an allocation (remember, a `Box<[T]>` still requires a `Vec<T>` in order to be constructed). This *might* be a problem if called frequently enough. ##### 3. Generated Structs The original design, implemented by @Davier, instead generates structs specific for each variant. So if we had a variant path like `Foo::Bar`, we'd generate a struct named `FooBarWrapper`. This would be newtyped around the original enum and forward tuple or struct methods to the enum with the chosen variant. Because it involved using the `Tuple` and `Struct` traits (which are also both bound on `Reflect`), this meant a bit more code had to be generated. For a single struct variant with one field, the generated code amounted to ~110LoC. However, each new field added to that variant only added ~6 more LoC. In order to work properly, the enum had to be transmuted to the generated struct: ```rust fn variant(&self) -> crate::EnumVariant<'_> { match self { Foo::Bar {value: i32} => { let wrapper_ref = unsafe { std::mem::transmute::<&Self, &FooBarWrapper>(self) }; crate::EnumVariant::Struct(wrapper_ref as &dyn crate::Struct) } } } ``` This works because `FooBarWrapper` is defined as `repr(transparent)`. Out of all the alternatives, this would probably be the one most likely to be used again in the future. The reasons for why this PR did not continue to use it was because: * To reduce generated code (which would hopefully speed up compile times) * To avoid cluttering the code with generated structs not visible to the user * To keep bevy_reflect simple and extensible (these generated structs act as proxies and might not play well with current or future systems) * To avoid additional unsafe blocks * My own misunderstanding of @Davier's code That last point is obviously on me. I misjudged the code to be too unsafe and unable to handle variant swapping (which it probably could) when I was rebasing it. Looking over it again when writing up this whole section, I see that it was actually a pretty clever way of handling variant representation. #### Benefits of All-in-One As stated before, the current implementation uses an all-in-one approach. All variants are capable of containing fields as far as `Enum` is concerned. This provides a few benefits that the alternatives do not (reduced indirection, safer code, etc.). The biggest benefit, though, is direct field access. Rather than forcing users to have to go through pattern matching, we grant direct access to the fields contained by the current variant. The reason we can do this is because all of the pattern matching happens internally. Getting the field at index `2` will automatically return `Some(...)` for the current variant if it has a field at that index or `None` if it doesn't (or can't). This could be useful for scenarios where the variant has already been verified or just set/swapped (or even where the type of variant doesn't matter): ```rust let dyn_enum: &mut dyn Enum = &mut Foo::Bar {value: 123}; // We know it's the `Bar` variant let field = dyn_enum.field("value").unwrap(); ``` Reflection is not a type-safe abstraction— almost every return value is wrapped in `Option<...>`. There are plenty of places to check and recheck that a value is what Reflect says it is. Forcing users to have to go through `match` each time they want to access a field might just be an extra step among dozens of other verification processes. Some might disagree, but ultimately, my view is that the benefit here is an improvement to the ergonomics and usability of reflected enums. </details> --- ## Changelog ### Added * Added `Enum` trait * Added `Enum` impl to `Reflect` derive macro * Added `DynamicEnum` struct * Added `DynamicVariant` * Added `EnumInfo` * Added `VariantInfo` * Added `StructVariantInfo` * Added `TupleVariantInfo` * Added `UnitVariantInfo` * Added serializtion/deserialization support for enums * Added `EnumSerializer` * Added `VariantType` * Added `VariantFieldIter` * Added `VariantField` * Added `enum_partial_eq(...)` * Added `enum_hash(...)` ### Changed * `Option<T>` now implements `Enum` * `bevy_window` now depends on `bevy_reflect` * Implemented `Reflect` and `FromReflect` for `WindowId` * Derive `FromReflect` on `PerspectiveProjection` * Derive `FromReflect` on `OrthographicProjection` * Derive `FromReflect` on `WindowOrigin` * Derive `FromReflect` on `ScalingMode` * Derive `FromReflect` on `DepthCalculation` ## Migration Guide * Enums no longer need to be treated as values and usages of `#[reflect_value(...)]` can be removed or replaced by `#[reflect(...)]` * Enums (including `Option<T>`) now take a different format when serializing. The format is described above, but this may cause issues for existing scenes that make use of enums. --- Also shout out to @nicopap for helping clean up some of the code here! It's a big feature so help like this is really appreciated! Co-authored-by: Gino Valente <gino.valente.code@gmail.com>
2022-08-02 22:14:41 +00:00
};
match data.fields {
Fields::Named(..) => Ok(Self::Struct(reflect_struct)),
Fields::Unnamed(..) => Ok(Self::TupleStruct(reflect_struct)),
Fields::Unit => Ok(Self::UnitStruct(reflect_struct)),
bevy_reflect_derive: Tidying up the code (#4712) # Objective The `bevy_reflect_derive` crate is not the cleanest or easiest to follow/maintain. The `lib.rs` file is especially difficult with over 1000 lines of code written in a confusing order. This is just a result of growth within the crate and it would be nice to clean it up for future work. ## Solution Split `bevy_reflect_derive` into many more submodules. The submodules include: * `container_attributes` - Code relating to container attributes * `derive_data` - Code relating to reflection-based derive metadata * `field_attributes` - Code relating to field attributes * `impls` - Code containing actual reflection implementations * `reflect_value` - Code relating to reflection-based value metadata * `registration` - Code relating to type registration * `utility` - General-purpose utility functions This leaves the `lib.rs` file to contain only the public macros, making it much easier to digest (and fewer than 200 lines). By breaking up the code into smaller modules, we make it easier for future contributors to find the code they're looking for or identify which module best fits their own additions. ### Metadata Structs This cleanup also adds two big metadata structs: `ReflectFieldAttr` and `ReflectDeriveData`. The former is used to store all attributes for a struct field (if any). The latter is used to store all metadata for struct-based derive inputs. Both significantly reduce code duplication and make editing these macros much simpler. The tradeoff is that we may collect more metadata than needed. However, this is usually a small thing (such as checking for attributes when they're not really needed or creating a `ReflectFieldAttr` for every field regardless of whether they actually have an attribute). We could try to remove these tradeoffs and squeeze some more performance out, but doing so might come at the cost of developer experience. Personally, I think it's much nicer to create a `ReflectFieldAttr` for every field since it means I don't have to do two `Option` checks. Others may disagree, though, and so we can discuss changing this either in this PR or in a future one. ### Out of Scope _Some_ documentation has been added or improved, but ultimately good docs are probably best saved for a dedicated PR. ## 🔍 Focus Points (for reviewers) I know it's a lot to sift through, so here is a list of **key points for reviewers**: - The following files contain code that was mostly just relocated: - `reflect_value.rs` - `registration.rs` - `container_attributes.rs` was also mostly moved but features some general cleanup (reducing nesting, removing hardcoded strings, etc.) and lots of doc comments - Most impl logic was moved from `lib.rs` to `impls.rs`, but they have been significantly modified to use the new `ReflectDeriveData` metadata struct in order to reduce duplication. - `derive_data.rs` and `field_attributes.rs` contain almost entirely new code and should probably be given the most attention. - Likewise, `from_reflect.rs` saw major changes using `ReflectDeriveData` so it should also be given focus. - There was no change to the `lib.rs` exports so the end-user API should be the same. ## Prior Work This task was initially tackled by @NathanSWard in #2377 (which was closed in favor of this PR), so hats off to them for beating me to the punch by nearly a year! --- ## Changelog * **[INTERNAL]** Split `bevy_reflect_derive` into smaller submodules * **[INTERNAL]** Add `ReflectFieldAttr` * **[INTERNAL]** Add `ReflectDeriveData` * Add `BevyManifest::get_path_direct()` method (`bevy_macro_utils`) Co-authored-by: MrGVSV <49806985+MrGVSV@users.noreply.github.com>
2022-05-12 19:43:23 +00:00
}
}
bevy_reflect: Reflect enums (#4761) # Objective > This is a revival of #1347. Credit for the original PR should go to @Davier. Currently, enums are treated as `ReflectRef::Value` types by `bevy_reflect`. Obviously, there needs to be better a better representation for enums using the reflection API. ## Solution Based on prior work from @Davier, an `Enum` trait has been added as well as the ability to automatically implement it via the `Reflect` derive macro. This allows enums to be expressed dynamically: ```rust #[derive(Reflect)] enum Foo { A, B(usize), C { value: f32 }, } let mut foo = Foo::B(123); assert_eq!("B", foo.variant_name()); assert_eq!(1, foo.field_len()); let new_value = DynamicEnum::from(Foo::C { value: 1.23 }); foo.apply(&new_value); assert_eq!(Foo::C{value: 1.23}, foo); ``` ### Features #### Derive Macro Use the `#[derive(Reflect)]` macro to automatically implement the `Enum` trait for enum definitions. Optionally, you can use `#[reflect(ignore)]` with both variants and variant fields, just like you can with structs. These ignored items will not be considered as part of the reflection and cannot be accessed via reflection. ```rust #[derive(Reflect)] enum TestEnum { A, // Uncomment to ignore all of `B` // #[reflect(ignore)] B(usize), C { // Uncomment to ignore only field `foo` of `C` // #[reflect(ignore)] foo: f32, bar: bool, }, } ``` #### Dynamic Enums Enums may be created/represented dynamically via the `DynamicEnum` struct. The main purpose of this struct is to allow enums to be deserialized into a partial state and to allow dynamic patching. In order to ensure conversion from a `DynamicEnum` to a concrete enum type goes smoothly, be sure to add `FromReflect` to your derive macro. ```rust let mut value = TestEnum::A; // Create from a concrete instance let dyn_enum = DynamicEnum::from(TestEnum::B(123)); value.apply(&dyn_enum); assert_eq!(TestEnum::B(123), value); // Create a purely dynamic instance let dyn_enum = DynamicEnum::new("TestEnum", "A", ()); value.apply(&dyn_enum); assert_eq!(TestEnum::A, value); ``` #### Variants An enum value is always represented as one of its variants— never the enum in its entirety. ```rust let value = TestEnum::A; assert_eq!("A", value.variant_name()); // Since we are using the `A` variant, we cannot also be the `B` variant assert_ne!("B", value.variant_name()); ``` All variant types are representable within the `Enum` trait: unit, struct, and tuple. You can get the current type like: ```rust match value.variant_type() { VariantType::Unit => println!("A unit variant!"), VariantType::Struct => println!("A struct variant!"), VariantType::Tuple => println!("A tuple variant!"), } ``` > Notice that they don't contain any values representing the fields. These are purely tags. If a variant has them, you can access the fields as well: ```rust let mut value = TestEnum::C { foo: 1.23, bar: false }; // Read/write specific fields *value.field_mut("bar").unwrap() = true; // Iterate over the entire collection of fields for field in value.iter_fields() { println!("{} = {:?}", field.name(), field.value()); } ``` #### Variant Swapping It might seem odd to group all variant types under a single trait (why allow `iter_fields` on a unit variant?), but the reason this was done ~~is to easily allow *variant swapping*.~~ As I was recently drafting up the **Design Decisions** section, I discovered that other solutions could have been made to work with variant swapping. So while there are reasons to keep the all-in-one approach, variant swapping is _not_ one of them. ```rust let mut value: Box<dyn Enum> = Box::new(TestEnum::A); value.set(Box::new(TestEnum::B(123))).unwrap(); ``` #### Serialization Enums can be serialized and deserialized via reflection without needing to implement `Serialize` or `Deserialize` themselves (which can save thousands of lines of generated code). Below are the ways an enum can be serialized. > Note, like the rest of reflection-based serialization, the order of the keys in these representations is important! ##### Unit ```json { "type": "my_crate::TestEnum", "enum": { "variant": "A" } } ``` ##### Tuple ```json { "type": "my_crate::TestEnum", "enum": { "variant": "B", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` <details> <summary>Effects on Option</summary> This ends up making `Option` look a little ugly: ```json { "type": "core::option::Option<usize>", "enum": { "variant": "Some", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` </details> ##### Struct ```json { "type": "my_crate::TestEnum", "enum": { "variant": "C", "struct": { "foo": { "type": "f32", "value": 1.23 }, "bar": { "type": "bool", "value": false } } } } ``` ## Design Decisions <details> <summary><strong>View Section</strong></summary> This section is here to provide some context for why certain decisions were made for this PR, alternatives that could have been used instead, and what could be improved upon in the future. ### Variant Representation One of the biggest decisions was to decide on how to represent variants. The current design uses a "all-in-one" design where unit, tuple, and struct variants are all simultaneously represented by the `Enum` trait. This is not the only way it could have been done, though. #### Alternatives ##### 1. Variant Traits One way of representing variants would be to define traits for each variant, implementing them whenever an enum featured at least one instance of them. This would allow us to define variants like: ```rust pub trait Enum: Reflect { fn variant(&self) -> Variant; } pub enum Variant<'a> { Unit, Tuple(&'a dyn TupleVariant), Struct(&'a dyn StructVariant), } pub trait TupleVariant { fn field_len(&self) -> usize; // ... } ``` And then do things like: ```rust fn get_tuple_len(foo: &dyn Enum) -> usize { match foo.variant() { Variant::Tuple(tuple) => tuple.field_len(), _ => panic!("not a tuple variant!") } } ``` The reason this PR does not go with this approach is because of the fact that variants are not separate types. In other words, we cannot implement traits on specific variants— these cover the *entire* enum. This means we offer an easy footgun: ```rust let foo: Option<i32> = None; let my_enum = Box::new(foo) as Box<dyn TupleVariant>; ``` Here, `my_enum` contains `foo`, which is a unit variant. However, since we need to implement `TupleVariant` for `Option` as a whole, it's possible to perform such a cast. This is obviously wrong, but could easily go unnoticed. So unfortunately, this makes it not a good candidate for representing variants. ##### 2. Variant Structs To get around the issue of traits necessarily needing to apply to both the enum and its variants, we could instead use structs that are created on a per-variant basis. This was also considered but was ultimately [[removed](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c)](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c) due to concerns about allocations. Each variant struct would probably look something like: ```rust pub trait Enum: Reflect { fn variant_mut(&self) -> VariantMut; } pub enum VariantMut<'a> { Unit, Tuple(TupleVariantMut), Struct(StructVariantMut), } struct StructVariantMut<'a> { fields: Vec<&'a mut dyn Reflect>, field_indices: HashMap<Cow<'static, str>, usize> } ``` This allows us to isolate struct variants into their own defined struct and define methods specifically for their use. It also prevents users from casting to it since it's not a trait. However, this is not an optimal solution. Both `field_indices` and `fields` will require an allocation (remember, a `Box<[T]>` still requires a `Vec<T>` in order to be constructed). This *might* be a problem if called frequently enough. ##### 3. Generated Structs The original design, implemented by @Davier, instead generates structs specific for each variant. So if we had a variant path like `Foo::Bar`, we'd generate a struct named `FooBarWrapper`. This would be newtyped around the original enum and forward tuple or struct methods to the enum with the chosen variant. Because it involved using the `Tuple` and `Struct` traits (which are also both bound on `Reflect`), this meant a bit more code had to be generated. For a single struct variant with one field, the generated code amounted to ~110LoC. However, each new field added to that variant only added ~6 more LoC. In order to work properly, the enum had to be transmuted to the generated struct: ```rust fn variant(&self) -> crate::EnumVariant<'_> { match self { Foo::Bar {value: i32} => { let wrapper_ref = unsafe { std::mem::transmute::<&Self, &FooBarWrapper>(self) }; crate::EnumVariant::Struct(wrapper_ref as &dyn crate::Struct) } } } ``` This works because `FooBarWrapper` is defined as `repr(transparent)`. Out of all the alternatives, this would probably be the one most likely to be used again in the future. The reasons for why this PR did not continue to use it was because: * To reduce generated code (which would hopefully speed up compile times) * To avoid cluttering the code with generated structs not visible to the user * To keep bevy_reflect simple and extensible (these generated structs act as proxies and might not play well with current or future systems) * To avoid additional unsafe blocks * My own misunderstanding of @Davier's code That last point is obviously on me. I misjudged the code to be too unsafe and unable to handle variant swapping (which it probably could) when I was rebasing it. Looking over it again when writing up this whole section, I see that it was actually a pretty clever way of handling variant representation. #### Benefits of All-in-One As stated before, the current implementation uses an all-in-one approach. All variants are capable of containing fields as far as `Enum` is concerned. This provides a few benefits that the alternatives do not (reduced indirection, safer code, etc.). The biggest benefit, though, is direct field access. Rather than forcing users to have to go through pattern matching, we grant direct access to the fields contained by the current variant. The reason we can do this is because all of the pattern matching happens internally. Getting the field at index `2` will automatically return `Some(...)` for the current variant if it has a field at that index or `None` if it doesn't (or can't). This could be useful for scenarios where the variant has already been verified or just set/swapped (or even where the type of variant doesn't matter): ```rust let dyn_enum: &mut dyn Enum = &mut Foo::Bar {value: 123}; // We know it's the `Bar` variant let field = dyn_enum.field("value").unwrap(); ``` Reflection is not a type-safe abstraction— almost every return value is wrapped in `Option<...>`. There are plenty of places to check and recheck that a value is what Reflect says it is. Forcing users to have to go through `match` each time they want to access a field might just be an extra step among dozens of other verification processes. Some might disagree, but ultimately, my view is that the benefit here is an improvement to the ergonomics and usability of reflected enums. </details> --- ## Changelog ### Added * Added `Enum` trait * Added `Enum` impl to `Reflect` derive macro * Added `DynamicEnum` struct * Added `DynamicVariant` * Added `EnumInfo` * Added `VariantInfo` * Added `StructVariantInfo` * Added `TupleVariantInfo` * Added `UnitVariantInfo` * Added serializtion/deserialization support for enums * Added `EnumSerializer` * Added `VariantType` * Added `VariantFieldIter` * Added `VariantField` * Added `enum_partial_eq(...)` * Added `enum_hash(...)` ### Changed * `Option<T>` now implements `Enum` * `bevy_window` now depends on `bevy_reflect` * Implemented `Reflect` and `FromReflect` for `WindowId` * Derive `FromReflect` on `PerspectiveProjection` * Derive `FromReflect` on `OrthographicProjection` * Derive `FromReflect` on `WindowOrigin` * Derive `FromReflect` on `ScalingMode` * Derive `FromReflect` on `DepthCalculation` ## Migration Guide * Enums no longer need to be treated as values and usages of `#[reflect_value(...)]` can be removed or replaced by `#[reflect(...)]` * Enums (including `Option<T>`) now take a different format when serializing. The format is described above, but this may cause issues for existing scenes that make use of enums. --- Also shout out to @nicopap for helping clean up some of the code here! It's a big feature so help like this is really appreciated! Co-authored-by: Gino Valente <gino.valente.code@gmail.com>
2022-08-02 22:14:41 +00:00
Data::Enum(data) => {
let variants = Self::collect_enum_variants(&data.variants)?;
let reflect_enum = ReflectEnum { meta, variants };
bevy_reflect: Reflect enums (#4761) # Objective > This is a revival of #1347. Credit for the original PR should go to @Davier. Currently, enums are treated as `ReflectRef::Value` types by `bevy_reflect`. Obviously, there needs to be better a better representation for enums using the reflection API. ## Solution Based on prior work from @Davier, an `Enum` trait has been added as well as the ability to automatically implement it via the `Reflect` derive macro. This allows enums to be expressed dynamically: ```rust #[derive(Reflect)] enum Foo { A, B(usize), C { value: f32 }, } let mut foo = Foo::B(123); assert_eq!("B", foo.variant_name()); assert_eq!(1, foo.field_len()); let new_value = DynamicEnum::from(Foo::C { value: 1.23 }); foo.apply(&new_value); assert_eq!(Foo::C{value: 1.23}, foo); ``` ### Features #### Derive Macro Use the `#[derive(Reflect)]` macro to automatically implement the `Enum` trait for enum definitions. Optionally, you can use `#[reflect(ignore)]` with both variants and variant fields, just like you can with structs. These ignored items will not be considered as part of the reflection and cannot be accessed via reflection. ```rust #[derive(Reflect)] enum TestEnum { A, // Uncomment to ignore all of `B` // #[reflect(ignore)] B(usize), C { // Uncomment to ignore only field `foo` of `C` // #[reflect(ignore)] foo: f32, bar: bool, }, } ``` #### Dynamic Enums Enums may be created/represented dynamically via the `DynamicEnum` struct. The main purpose of this struct is to allow enums to be deserialized into a partial state and to allow dynamic patching. In order to ensure conversion from a `DynamicEnum` to a concrete enum type goes smoothly, be sure to add `FromReflect` to your derive macro. ```rust let mut value = TestEnum::A; // Create from a concrete instance let dyn_enum = DynamicEnum::from(TestEnum::B(123)); value.apply(&dyn_enum); assert_eq!(TestEnum::B(123), value); // Create a purely dynamic instance let dyn_enum = DynamicEnum::new("TestEnum", "A", ()); value.apply(&dyn_enum); assert_eq!(TestEnum::A, value); ``` #### Variants An enum value is always represented as one of its variants— never the enum in its entirety. ```rust let value = TestEnum::A; assert_eq!("A", value.variant_name()); // Since we are using the `A` variant, we cannot also be the `B` variant assert_ne!("B", value.variant_name()); ``` All variant types are representable within the `Enum` trait: unit, struct, and tuple. You can get the current type like: ```rust match value.variant_type() { VariantType::Unit => println!("A unit variant!"), VariantType::Struct => println!("A struct variant!"), VariantType::Tuple => println!("A tuple variant!"), } ``` > Notice that they don't contain any values representing the fields. These are purely tags. If a variant has them, you can access the fields as well: ```rust let mut value = TestEnum::C { foo: 1.23, bar: false }; // Read/write specific fields *value.field_mut("bar").unwrap() = true; // Iterate over the entire collection of fields for field in value.iter_fields() { println!("{} = {:?}", field.name(), field.value()); } ``` #### Variant Swapping It might seem odd to group all variant types under a single trait (why allow `iter_fields` on a unit variant?), but the reason this was done ~~is to easily allow *variant swapping*.~~ As I was recently drafting up the **Design Decisions** section, I discovered that other solutions could have been made to work with variant swapping. So while there are reasons to keep the all-in-one approach, variant swapping is _not_ one of them. ```rust let mut value: Box<dyn Enum> = Box::new(TestEnum::A); value.set(Box::new(TestEnum::B(123))).unwrap(); ``` #### Serialization Enums can be serialized and deserialized via reflection without needing to implement `Serialize` or `Deserialize` themselves (which can save thousands of lines of generated code). Below are the ways an enum can be serialized. > Note, like the rest of reflection-based serialization, the order of the keys in these representations is important! ##### Unit ```json { "type": "my_crate::TestEnum", "enum": { "variant": "A" } } ``` ##### Tuple ```json { "type": "my_crate::TestEnum", "enum": { "variant": "B", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` <details> <summary>Effects on Option</summary> This ends up making `Option` look a little ugly: ```json { "type": "core::option::Option<usize>", "enum": { "variant": "Some", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` </details> ##### Struct ```json { "type": "my_crate::TestEnum", "enum": { "variant": "C", "struct": { "foo": { "type": "f32", "value": 1.23 }, "bar": { "type": "bool", "value": false } } } } ``` ## Design Decisions <details> <summary><strong>View Section</strong></summary> This section is here to provide some context for why certain decisions were made for this PR, alternatives that could have been used instead, and what could be improved upon in the future. ### Variant Representation One of the biggest decisions was to decide on how to represent variants. The current design uses a "all-in-one" design where unit, tuple, and struct variants are all simultaneously represented by the `Enum` trait. This is not the only way it could have been done, though. #### Alternatives ##### 1. Variant Traits One way of representing variants would be to define traits for each variant, implementing them whenever an enum featured at least one instance of them. This would allow us to define variants like: ```rust pub trait Enum: Reflect { fn variant(&self) -> Variant; } pub enum Variant<'a> { Unit, Tuple(&'a dyn TupleVariant), Struct(&'a dyn StructVariant), } pub trait TupleVariant { fn field_len(&self) -> usize; // ... } ``` And then do things like: ```rust fn get_tuple_len(foo: &dyn Enum) -> usize { match foo.variant() { Variant::Tuple(tuple) => tuple.field_len(), _ => panic!("not a tuple variant!") } } ``` The reason this PR does not go with this approach is because of the fact that variants are not separate types. In other words, we cannot implement traits on specific variants— these cover the *entire* enum. This means we offer an easy footgun: ```rust let foo: Option<i32> = None; let my_enum = Box::new(foo) as Box<dyn TupleVariant>; ``` Here, `my_enum` contains `foo`, which is a unit variant. However, since we need to implement `TupleVariant` for `Option` as a whole, it's possible to perform such a cast. This is obviously wrong, but could easily go unnoticed. So unfortunately, this makes it not a good candidate for representing variants. ##### 2. Variant Structs To get around the issue of traits necessarily needing to apply to both the enum and its variants, we could instead use structs that are created on a per-variant basis. This was also considered but was ultimately [[removed](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c)](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c) due to concerns about allocations. Each variant struct would probably look something like: ```rust pub trait Enum: Reflect { fn variant_mut(&self) -> VariantMut; } pub enum VariantMut<'a> { Unit, Tuple(TupleVariantMut), Struct(StructVariantMut), } struct StructVariantMut<'a> { fields: Vec<&'a mut dyn Reflect>, field_indices: HashMap<Cow<'static, str>, usize> } ``` This allows us to isolate struct variants into their own defined struct and define methods specifically for their use. It also prevents users from casting to it since it's not a trait. However, this is not an optimal solution. Both `field_indices` and `fields` will require an allocation (remember, a `Box<[T]>` still requires a `Vec<T>` in order to be constructed). This *might* be a problem if called frequently enough. ##### 3. Generated Structs The original design, implemented by @Davier, instead generates structs specific for each variant. So if we had a variant path like `Foo::Bar`, we'd generate a struct named `FooBarWrapper`. This would be newtyped around the original enum and forward tuple or struct methods to the enum with the chosen variant. Because it involved using the `Tuple` and `Struct` traits (which are also both bound on `Reflect`), this meant a bit more code had to be generated. For a single struct variant with one field, the generated code amounted to ~110LoC. However, each new field added to that variant only added ~6 more LoC. In order to work properly, the enum had to be transmuted to the generated struct: ```rust fn variant(&self) -> crate::EnumVariant<'_> { match self { Foo::Bar {value: i32} => { let wrapper_ref = unsafe { std::mem::transmute::<&Self, &FooBarWrapper>(self) }; crate::EnumVariant::Struct(wrapper_ref as &dyn crate::Struct) } } } ``` This works because `FooBarWrapper` is defined as `repr(transparent)`. Out of all the alternatives, this would probably be the one most likely to be used again in the future. The reasons for why this PR did not continue to use it was because: * To reduce generated code (which would hopefully speed up compile times) * To avoid cluttering the code with generated structs not visible to the user * To keep bevy_reflect simple and extensible (these generated structs act as proxies and might not play well with current or future systems) * To avoid additional unsafe blocks * My own misunderstanding of @Davier's code That last point is obviously on me. I misjudged the code to be too unsafe and unable to handle variant swapping (which it probably could) when I was rebasing it. Looking over it again when writing up this whole section, I see that it was actually a pretty clever way of handling variant representation. #### Benefits of All-in-One As stated before, the current implementation uses an all-in-one approach. All variants are capable of containing fields as far as `Enum` is concerned. This provides a few benefits that the alternatives do not (reduced indirection, safer code, etc.). The biggest benefit, though, is direct field access. Rather than forcing users to have to go through pattern matching, we grant direct access to the fields contained by the current variant. The reason we can do this is because all of the pattern matching happens internally. Getting the field at index `2` will automatically return `Some(...)` for the current variant if it has a field at that index or `None` if it doesn't (or can't). This could be useful for scenarios where the variant has already been verified or just set/swapped (or even where the type of variant doesn't matter): ```rust let dyn_enum: &mut dyn Enum = &mut Foo::Bar {value: 123}; // We know it's the `Bar` variant let field = dyn_enum.field("value").unwrap(); ``` Reflection is not a type-safe abstraction— almost every return value is wrapped in `Option<...>`. There are plenty of places to check and recheck that a value is what Reflect says it is. Forcing users to have to go through `match` each time they want to access a field might just be an extra step among dozens of other verification processes. Some might disagree, but ultimately, my view is that the benefit here is an improvement to the ergonomics and usability of reflected enums. </details> --- ## Changelog ### Added * Added `Enum` trait * Added `Enum` impl to `Reflect` derive macro * Added `DynamicEnum` struct * Added `DynamicVariant` * Added `EnumInfo` * Added `VariantInfo` * Added `StructVariantInfo` * Added `TupleVariantInfo` * Added `UnitVariantInfo` * Added serializtion/deserialization support for enums * Added `EnumSerializer` * Added `VariantType` * Added `VariantFieldIter` * Added `VariantField` * Added `enum_partial_eq(...)` * Added `enum_hash(...)` ### Changed * `Option<T>` now implements `Enum` * `bevy_window` now depends on `bevy_reflect` * Implemented `Reflect` and `FromReflect` for `WindowId` * Derive `FromReflect` on `PerspectiveProjection` * Derive `FromReflect` on `OrthographicProjection` * Derive `FromReflect` on `WindowOrigin` * Derive `FromReflect` on `ScalingMode` * Derive `FromReflect` on `DepthCalculation` ## Migration Guide * Enums no longer need to be treated as values and usages of `#[reflect_value(...)]` can be removed or replaced by `#[reflect(...)]` * Enums (including `Option<T>`) now take a different format when serializing. The format is described above, but this may cause issues for existing scenes that make use of enums. --- Also shout out to @nicopap for helping clean up some of the code here! It's a big feature so help like this is really appreciated! Co-authored-by: Gino Valente <gino.valente.code@gmail.com>
2022-08-02 22:14:41 +00:00
Ok(Self::Enum(reflect_enum))
bevy_reflect_derive: Tidying up the code (#4712) # Objective The `bevy_reflect_derive` crate is not the cleanest or easiest to follow/maintain. The `lib.rs` file is especially difficult with over 1000 lines of code written in a confusing order. This is just a result of growth within the crate and it would be nice to clean it up for future work. ## Solution Split `bevy_reflect_derive` into many more submodules. The submodules include: * `container_attributes` - Code relating to container attributes * `derive_data` - Code relating to reflection-based derive metadata * `field_attributes` - Code relating to field attributes * `impls` - Code containing actual reflection implementations * `reflect_value` - Code relating to reflection-based value metadata * `registration` - Code relating to type registration * `utility` - General-purpose utility functions This leaves the `lib.rs` file to contain only the public macros, making it much easier to digest (and fewer than 200 lines). By breaking up the code into smaller modules, we make it easier for future contributors to find the code they're looking for or identify which module best fits their own additions. ### Metadata Structs This cleanup also adds two big metadata structs: `ReflectFieldAttr` and `ReflectDeriveData`. The former is used to store all attributes for a struct field (if any). The latter is used to store all metadata for struct-based derive inputs. Both significantly reduce code duplication and make editing these macros much simpler. The tradeoff is that we may collect more metadata than needed. However, this is usually a small thing (such as checking for attributes when they're not really needed or creating a `ReflectFieldAttr` for every field regardless of whether they actually have an attribute). We could try to remove these tradeoffs and squeeze some more performance out, but doing so might come at the cost of developer experience. Personally, I think it's much nicer to create a `ReflectFieldAttr` for every field since it means I don't have to do two `Option` checks. Others may disagree, though, and so we can discuss changing this either in this PR or in a future one. ### Out of Scope _Some_ documentation has been added or improved, but ultimately good docs are probably best saved for a dedicated PR. ## 🔍 Focus Points (for reviewers) I know it's a lot to sift through, so here is a list of **key points for reviewers**: - The following files contain code that was mostly just relocated: - `reflect_value.rs` - `registration.rs` - `container_attributes.rs` was also mostly moved but features some general cleanup (reducing nesting, removing hardcoded strings, etc.) and lots of doc comments - Most impl logic was moved from `lib.rs` to `impls.rs`, but they have been significantly modified to use the new `ReflectDeriveData` metadata struct in order to reduce duplication. - `derive_data.rs` and `field_attributes.rs` contain almost entirely new code and should probably be given the most attention. - Likewise, `from_reflect.rs` saw major changes using `ReflectDeriveData` so it should also be given focus. - There was no change to the `lib.rs` exports so the end-user API should be the same. ## Prior Work This task was initially tackled by @NathanSWard in #2377 (which was closed in favor of this PR), so hats off to them for beating me to the punch by nearly a year! --- ## Changelog * **[INTERNAL]** Split `bevy_reflect_derive` into smaller submodules * **[INTERNAL]** Add `ReflectFieldAttr` * **[INTERNAL]** Add `ReflectDeriveData` * Add `BevyManifest::get_path_direct()` method (`bevy_macro_utils`) Co-authored-by: MrGVSV <49806985+MrGVSV@users.noreply.github.com>
2022-05-12 19:43:23 +00:00
}
bevy_reflect: Reflect enums (#4761) # Objective > This is a revival of #1347. Credit for the original PR should go to @Davier. Currently, enums are treated as `ReflectRef::Value` types by `bevy_reflect`. Obviously, there needs to be better a better representation for enums using the reflection API. ## Solution Based on prior work from @Davier, an `Enum` trait has been added as well as the ability to automatically implement it via the `Reflect` derive macro. This allows enums to be expressed dynamically: ```rust #[derive(Reflect)] enum Foo { A, B(usize), C { value: f32 }, } let mut foo = Foo::B(123); assert_eq!("B", foo.variant_name()); assert_eq!(1, foo.field_len()); let new_value = DynamicEnum::from(Foo::C { value: 1.23 }); foo.apply(&new_value); assert_eq!(Foo::C{value: 1.23}, foo); ``` ### Features #### Derive Macro Use the `#[derive(Reflect)]` macro to automatically implement the `Enum` trait for enum definitions. Optionally, you can use `#[reflect(ignore)]` with both variants and variant fields, just like you can with structs. These ignored items will not be considered as part of the reflection and cannot be accessed via reflection. ```rust #[derive(Reflect)] enum TestEnum { A, // Uncomment to ignore all of `B` // #[reflect(ignore)] B(usize), C { // Uncomment to ignore only field `foo` of `C` // #[reflect(ignore)] foo: f32, bar: bool, }, } ``` #### Dynamic Enums Enums may be created/represented dynamically via the `DynamicEnum` struct. The main purpose of this struct is to allow enums to be deserialized into a partial state and to allow dynamic patching. In order to ensure conversion from a `DynamicEnum` to a concrete enum type goes smoothly, be sure to add `FromReflect` to your derive macro. ```rust let mut value = TestEnum::A; // Create from a concrete instance let dyn_enum = DynamicEnum::from(TestEnum::B(123)); value.apply(&dyn_enum); assert_eq!(TestEnum::B(123), value); // Create a purely dynamic instance let dyn_enum = DynamicEnum::new("TestEnum", "A", ()); value.apply(&dyn_enum); assert_eq!(TestEnum::A, value); ``` #### Variants An enum value is always represented as one of its variants— never the enum in its entirety. ```rust let value = TestEnum::A; assert_eq!("A", value.variant_name()); // Since we are using the `A` variant, we cannot also be the `B` variant assert_ne!("B", value.variant_name()); ``` All variant types are representable within the `Enum` trait: unit, struct, and tuple. You can get the current type like: ```rust match value.variant_type() { VariantType::Unit => println!("A unit variant!"), VariantType::Struct => println!("A struct variant!"), VariantType::Tuple => println!("A tuple variant!"), } ``` > Notice that they don't contain any values representing the fields. These are purely tags. If a variant has them, you can access the fields as well: ```rust let mut value = TestEnum::C { foo: 1.23, bar: false }; // Read/write specific fields *value.field_mut("bar").unwrap() = true; // Iterate over the entire collection of fields for field in value.iter_fields() { println!("{} = {:?}", field.name(), field.value()); } ``` #### Variant Swapping It might seem odd to group all variant types under a single trait (why allow `iter_fields` on a unit variant?), but the reason this was done ~~is to easily allow *variant swapping*.~~ As I was recently drafting up the **Design Decisions** section, I discovered that other solutions could have been made to work with variant swapping. So while there are reasons to keep the all-in-one approach, variant swapping is _not_ one of them. ```rust let mut value: Box<dyn Enum> = Box::new(TestEnum::A); value.set(Box::new(TestEnum::B(123))).unwrap(); ``` #### Serialization Enums can be serialized and deserialized via reflection without needing to implement `Serialize` or `Deserialize` themselves (which can save thousands of lines of generated code). Below are the ways an enum can be serialized. > Note, like the rest of reflection-based serialization, the order of the keys in these representations is important! ##### Unit ```json { "type": "my_crate::TestEnum", "enum": { "variant": "A" } } ``` ##### Tuple ```json { "type": "my_crate::TestEnum", "enum": { "variant": "B", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` <details> <summary>Effects on Option</summary> This ends up making `Option` look a little ugly: ```json { "type": "core::option::Option<usize>", "enum": { "variant": "Some", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` </details> ##### Struct ```json { "type": "my_crate::TestEnum", "enum": { "variant": "C", "struct": { "foo": { "type": "f32", "value": 1.23 }, "bar": { "type": "bool", "value": false } } } } ``` ## Design Decisions <details> <summary><strong>View Section</strong></summary> This section is here to provide some context for why certain decisions were made for this PR, alternatives that could have been used instead, and what could be improved upon in the future. ### Variant Representation One of the biggest decisions was to decide on how to represent variants. The current design uses a "all-in-one" design where unit, tuple, and struct variants are all simultaneously represented by the `Enum` trait. This is not the only way it could have been done, though. #### Alternatives ##### 1. Variant Traits One way of representing variants would be to define traits for each variant, implementing them whenever an enum featured at least one instance of them. This would allow us to define variants like: ```rust pub trait Enum: Reflect { fn variant(&self) -> Variant; } pub enum Variant<'a> { Unit, Tuple(&'a dyn TupleVariant), Struct(&'a dyn StructVariant), } pub trait TupleVariant { fn field_len(&self) -> usize; // ... } ``` And then do things like: ```rust fn get_tuple_len(foo: &dyn Enum) -> usize { match foo.variant() { Variant::Tuple(tuple) => tuple.field_len(), _ => panic!("not a tuple variant!") } } ``` The reason this PR does not go with this approach is because of the fact that variants are not separate types. In other words, we cannot implement traits on specific variants— these cover the *entire* enum. This means we offer an easy footgun: ```rust let foo: Option<i32> = None; let my_enum = Box::new(foo) as Box<dyn TupleVariant>; ``` Here, `my_enum` contains `foo`, which is a unit variant. However, since we need to implement `TupleVariant` for `Option` as a whole, it's possible to perform such a cast. This is obviously wrong, but could easily go unnoticed. So unfortunately, this makes it not a good candidate for representing variants. ##### 2. Variant Structs To get around the issue of traits necessarily needing to apply to both the enum and its variants, we could instead use structs that are created on a per-variant basis. This was also considered but was ultimately [[removed](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c)](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c) due to concerns about allocations. Each variant struct would probably look something like: ```rust pub trait Enum: Reflect { fn variant_mut(&self) -> VariantMut; } pub enum VariantMut<'a> { Unit, Tuple(TupleVariantMut), Struct(StructVariantMut), } struct StructVariantMut<'a> { fields: Vec<&'a mut dyn Reflect>, field_indices: HashMap<Cow<'static, str>, usize> } ``` This allows us to isolate struct variants into their own defined struct and define methods specifically for their use. It also prevents users from casting to it since it's not a trait. However, this is not an optimal solution. Both `field_indices` and `fields` will require an allocation (remember, a `Box<[T]>` still requires a `Vec<T>` in order to be constructed). This *might* be a problem if called frequently enough. ##### 3. Generated Structs The original design, implemented by @Davier, instead generates structs specific for each variant. So if we had a variant path like `Foo::Bar`, we'd generate a struct named `FooBarWrapper`. This would be newtyped around the original enum and forward tuple or struct methods to the enum with the chosen variant. Because it involved using the `Tuple` and `Struct` traits (which are also both bound on `Reflect`), this meant a bit more code had to be generated. For a single struct variant with one field, the generated code amounted to ~110LoC. However, each new field added to that variant only added ~6 more LoC. In order to work properly, the enum had to be transmuted to the generated struct: ```rust fn variant(&self) -> crate::EnumVariant<'_> { match self { Foo::Bar {value: i32} => { let wrapper_ref = unsafe { std::mem::transmute::<&Self, &FooBarWrapper>(self) }; crate::EnumVariant::Struct(wrapper_ref as &dyn crate::Struct) } } } ``` This works because `FooBarWrapper` is defined as `repr(transparent)`. Out of all the alternatives, this would probably be the one most likely to be used again in the future. The reasons for why this PR did not continue to use it was because: * To reduce generated code (which would hopefully speed up compile times) * To avoid cluttering the code with generated structs not visible to the user * To keep bevy_reflect simple and extensible (these generated structs act as proxies and might not play well with current or future systems) * To avoid additional unsafe blocks * My own misunderstanding of @Davier's code That last point is obviously on me. I misjudged the code to be too unsafe and unable to handle variant swapping (which it probably could) when I was rebasing it. Looking over it again when writing up this whole section, I see that it was actually a pretty clever way of handling variant representation. #### Benefits of All-in-One As stated before, the current implementation uses an all-in-one approach. All variants are capable of containing fields as far as `Enum` is concerned. This provides a few benefits that the alternatives do not (reduced indirection, safer code, etc.). The biggest benefit, though, is direct field access. Rather than forcing users to have to go through pattern matching, we grant direct access to the fields contained by the current variant. The reason we can do this is because all of the pattern matching happens internally. Getting the field at index `2` will automatically return `Some(...)` for the current variant if it has a field at that index or `None` if it doesn't (or can't). This could be useful for scenarios where the variant has already been verified or just set/swapped (or even where the type of variant doesn't matter): ```rust let dyn_enum: &mut dyn Enum = &mut Foo::Bar {value: 123}; // We know it's the `Bar` variant let field = dyn_enum.field("value").unwrap(); ``` Reflection is not a type-safe abstraction— almost every return value is wrapped in `Option<...>`. There are plenty of places to check and recheck that a value is what Reflect says it is. Forcing users to have to go through `match` each time they want to access a field might just be an extra step among dozens of other verification processes. Some might disagree, but ultimately, my view is that the benefit here is an improvement to the ergonomics and usability of reflected enums. </details> --- ## Changelog ### Added * Added `Enum` trait * Added `Enum` impl to `Reflect` derive macro * Added `DynamicEnum` struct * Added `DynamicVariant` * Added `EnumInfo` * Added `VariantInfo` * Added `StructVariantInfo` * Added `TupleVariantInfo` * Added `UnitVariantInfo` * Added serializtion/deserialization support for enums * Added `EnumSerializer` * Added `VariantType` * Added `VariantFieldIter` * Added `VariantField` * Added `enum_partial_eq(...)` * Added `enum_hash(...)` ### Changed * `Option<T>` now implements `Enum` * `bevy_window` now depends on `bevy_reflect` * Implemented `Reflect` and `FromReflect` for `WindowId` * Derive `FromReflect` on `PerspectiveProjection` * Derive `FromReflect` on `OrthographicProjection` * Derive `FromReflect` on `WindowOrigin` * Derive `FromReflect` on `ScalingMode` * Derive `FromReflect` on `DepthCalculation` ## Migration Guide * Enums no longer need to be treated as values and usages of `#[reflect_value(...)]` can be removed or replaced by `#[reflect(...)]` * Enums (including `Option<T>`) now take a different format when serializing. The format is described above, but this may cause issues for existing scenes that make use of enums. --- Also shout out to @nicopap for helping clean up some of the code here! It's a big feature so help like this is really appreciated! Co-authored-by: Gino Valente <gino.valente.code@gmail.com>
2022-08-02 22:14:41 +00:00
Data::Union(..) => Err(syn::Error::new(
input.span(),
"reflection not supported for unions",
)),
bevy_reflect_derive: Tidying up the code (#4712) # Objective The `bevy_reflect_derive` crate is not the cleanest or easiest to follow/maintain. The `lib.rs` file is especially difficult with over 1000 lines of code written in a confusing order. This is just a result of growth within the crate and it would be nice to clean it up for future work. ## Solution Split `bevy_reflect_derive` into many more submodules. The submodules include: * `container_attributes` - Code relating to container attributes * `derive_data` - Code relating to reflection-based derive metadata * `field_attributes` - Code relating to field attributes * `impls` - Code containing actual reflection implementations * `reflect_value` - Code relating to reflection-based value metadata * `registration` - Code relating to type registration * `utility` - General-purpose utility functions This leaves the `lib.rs` file to contain only the public macros, making it much easier to digest (and fewer than 200 lines). By breaking up the code into smaller modules, we make it easier for future contributors to find the code they're looking for or identify which module best fits their own additions. ### Metadata Structs This cleanup also adds two big metadata structs: `ReflectFieldAttr` and `ReflectDeriveData`. The former is used to store all attributes for a struct field (if any). The latter is used to store all metadata for struct-based derive inputs. Both significantly reduce code duplication and make editing these macros much simpler. The tradeoff is that we may collect more metadata than needed. However, this is usually a small thing (such as checking for attributes when they're not really needed or creating a `ReflectFieldAttr` for every field regardless of whether they actually have an attribute). We could try to remove these tradeoffs and squeeze some more performance out, but doing so might come at the cost of developer experience. Personally, I think it's much nicer to create a `ReflectFieldAttr` for every field since it means I don't have to do two `Option` checks. Others may disagree, though, and so we can discuss changing this either in this PR or in a future one. ### Out of Scope _Some_ documentation has been added or improved, but ultimately good docs are probably best saved for a dedicated PR. ## 🔍 Focus Points (for reviewers) I know it's a lot to sift through, so here is a list of **key points for reviewers**: - The following files contain code that was mostly just relocated: - `reflect_value.rs` - `registration.rs` - `container_attributes.rs` was also mostly moved but features some general cleanup (reducing nesting, removing hardcoded strings, etc.) and lots of doc comments - Most impl logic was moved from `lib.rs` to `impls.rs`, but they have been significantly modified to use the new `ReflectDeriveData` metadata struct in order to reduce duplication. - `derive_data.rs` and `field_attributes.rs` contain almost entirely new code and should probably be given the most attention. - Likewise, `from_reflect.rs` saw major changes using `ReflectDeriveData` so it should also be given focus. - There was no change to the `lib.rs` exports so the end-user API should be the same. ## Prior Work This task was initially tackled by @NathanSWard in #2377 (which was closed in favor of this PR), so hats off to them for beating me to the punch by nearly a year! --- ## Changelog * **[INTERNAL]** Split `bevy_reflect_derive` into smaller submodules * **[INTERNAL]** Add `ReflectFieldAttr` * **[INTERNAL]** Add `ReflectDeriveData` * Add `BevyManifest::get_path_direct()` method (`bevy_macro_utils`) Co-authored-by: MrGVSV <49806985+MrGVSV@users.noreply.github.com>
2022-05-12 19:43:23 +00:00
};
bevy_reflect: Reflect enums (#4761) # Objective > This is a revival of #1347. Credit for the original PR should go to @Davier. Currently, enums are treated as `ReflectRef::Value` types by `bevy_reflect`. Obviously, there needs to be better a better representation for enums using the reflection API. ## Solution Based on prior work from @Davier, an `Enum` trait has been added as well as the ability to automatically implement it via the `Reflect` derive macro. This allows enums to be expressed dynamically: ```rust #[derive(Reflect)] enum Foo { A, B(usize), C { value: f32 }, } let mut foo = Foo::B(123); assert_eq!("B", foo.variant_name()); assert_eq!(1, foo.field_len()); let new_value = DynamicEnum::from(Foo::C { value: 1.23 }); foo.apply(&new_value); assert_eq!(Foo::C{value: 1.23}, foo); ``` ### Features #### Derive Macro Use the `#[derive(Reflect)]` macro to automatically implement the `Enum` trait for enum definitions. Optionally, you can use `#[reflect(ignore)]` with both variants and variant fields, just like you can with structs. These ignored items will not be considered as part of the reflection and cannot be accessed via reflection. ```rust #[derive(Reflect)] enum TestEnum { A, // Uncomment to ignore all of `B` // #[reflect(ignore)] B(usize), C { // Uncomment to ignore only field `foo` of `C` // #[reflect(ignore)] foo: f32, bar: bool, }, } ``` #### Dynamic Enums Enums may be created/represented dynamically via the `DynamicEnum` struct. The main purpose of this struct is to allow enums to be deserialized into a partial state and to allow dynamic patching. In order to ensure conversion from a `DynamicEnum` to a concrete enum type goes smoothly, be sure to add `FromReflect` to your derive macro. ```rust let mut value = TestEnum::A; // Create from a concrete instance let dyn_enum = DynamicEnum::from(TestEnum::B(123)); value.apply(&dyn_enum); assert_eq!(TestEnum::B(123), value); // Create a purely dynamic instance let dyn_enum = DynamicEnum::new("TestEnum", "A", ()); value.apply(&dyn_enum); assert_eq!(TestEnum::A, value); ``` #### Variants An enum value is always represented as one of its variants— never the enum in its entirety. ```rust let value = TestEnum::A; assert_eq!("A", value.variant_name()); // Since we are using the `A` variant, we cannot also be the `B` variant assert_ne!("B", value.variant_name()); ``` All variant types are representable within the `Enum` trait: unit, struct, and tuple. You can get the current type like: ```rust match value.variant_type() { VariantType::Unit => println!("A unit variant!"), VariantType::Struct => println!("A struct variant!"), VariantType::Tuple => println!("A tuple variant!"), } ``` > Notice that they don't contain any values representing the fields. These are purely tags. If a variant has them, you can access the fields as well: ```rust let mut value = TestEnum::C { foo: 1.23, bar: false }; // Read/write specific fields *value.field_mut("bar").unwrap() = true; // Iterate over the entire collection of fields for field in value.iter_fields() { println!("{} = {:?}", field.name(), field.value()); } ``` #### Variant Swapping It might seem odd to group all variant types under a single trait (why allow `iter_fields` on a unit variant?), but the reason this was done ~~is to easily allow *variant swapping*.~~ As I was recently drafting up the **Design Decisions** section, I discovered that other solutions could have been made to work with variant swapping. So while there are reasons to keep the all-in-one approach, variant swapping is _not_ one of them. ```rust let mut value: Box<dyn Enum> = Box::new(TestEnum::A); value.set(Box::new(TestEnum::B(123))).unwrap(); ``` #### Serialization Enums can be serialized and deserialized via reflection without needing to implement `Serialize` or `Deserialize` themselves (which can save thousands of lines of generated code). Below are the ways an enum can be serialized. > Note, like the rest of reflection-based serialization, the order of the keys in these representations is important! ##### Unit ```json { "type": "my_crate::TestEnum", "enum": { "variant": "A" } } ``` ##### Tuple ```json { "type": "my_crate::TestEnum", "enum": { "variant": "B", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` <details> <summary>Effects on Option</summary> This ends up making `Option` look a little ugly: ```json { "type": "core::option::Option<usize>", "enum": { "variant": "Some", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` </details> ##### Struct ```json { "type": "my_crate::TestEnum", "enum": { "variant": "C", "struct": { "foo": { "type": "f32", "value": 1.23 }, "bar": { "type": "bool", "value": false } } } } ``` ## Design Decisions <details> <summary><strong>View Section</strong></summary> This section is here to provide some context for why certain decisions were made for this PR, alternatives that could have been used instead, and what could be improved upon in the future. ### Variant Representation One of the biggest decisions was to decide on how to represent variants. The current design uses a "all-in-one" design where unit, tuple, and struct variants are all simultaneously represented by the `Enum` trait. This is not the only way it could have been done, though. #### Alternatives ##### 1. Variant Traits One way of representing variants would be to define traits for each variant, implementing them whenever an enum featured at least one instance of them. This would allow us to define variants like: ```rust pub trait Enum: Reflect { fn variant(&self) -> Variant; } pub enum Variant<'a> { Unit, Tuple(&'a dyn TupleVariant), Struct(&'a dyn StructVariant), } pub trait TupleVariant { fn field_len(&self) -> usize; // ... } ``` And then do things like: ```rust fn get_tuple_len(foo: &dyn Enum) -> usize { match foo.variant() { Variant::Tuple(tuple) => tuple.field_len(), _ => panic!("not a tuple variant!") } } ``` The reason this PR does not go with this approach is because of the fact that variants are not separate types. In other words, we cannot implement traits on specific variants— these cover the *entire* enum. This means we offer an easy footgun: ```rust let foo: Option<i32> = None; let my_enum = Box::new(foo) as Box<dyn TupleVariant>; ``` Here, `my_enum` contains `foo`, which is a unit variant. However, since we need to implement `TupleVariant` for `Option` as a whole, it's possible to perform such a cast. This is obviously wrong, but could easily go unnoticed. So unfortunately, this makes it not a good candidate for representing variants. ##### 2. Variant Structs To get around the issue of traits necessarily needing to apply to both the enum and its variants, we could instead use structs that are created on a per-variant basis. This was also considered but was ultimately [[removed](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c)](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c) due to concerns about allocations. Each variant struct would probably look something like: ```rust pub trait Enum: Reflect { fn variant_mut(&self) -> VariantMut; } pub enum VariantMut<'a> { Unit, Tuple(TupleVariantMut), Struct(StructVariantMut), } struct StructVariantMut<'a> { fields: Vec<&'a mut dyn Reflect>, field_indices: HashMap<Cow<'static, str>, usize> } ``` This allows us to isolate struct variants into their own defined struct and define methods specifically for their use. It also prevents users from casting to it since it's not a trait. However, this is not an optimal solution. Both `field_indices` and `fields` will require an allocation (remember, a `Box<[T]>` still requires a `Vec<T>` in order to be constructed). This *might* be a problem if called frequently enough. ##### 3. Generated Structs The original design, implemented by @Davier, instead generates structs specific for each variant. So if we had a variant path like `Foo::Bar`, we'd generate a struct named `FooBarWrapper`. This would be newtyped around the original enum and forward tuple or struct methods to the enum with the chosen variant. Because it involved using the `Tuple` and `Struct` traits (which are also both bound on `Reflect`), this meant a bit more code had to be generated. For a single struct variant with one field, the generated code amounted to ~110LoC. However, each new field added to that variant only added ~6 more LoC. In order to work properly, the enum had to be transmuted to the generated struct: ```rust fn variant(&self) -> crate::EnumVariant<'_> { match self { Foo::Bar {value: i32} => { let wrapper_ref = unsafe { std::mem::transmute::<&Self, &FooBarWrapper>(self) }; crate::EnumVariant::Struct(wrapper_ref as &dyn crate::Struct) } } } ``` This works because `FooBarWrapper` is defined as `repr(transparent)`. Out of all the alternatives, this would probably be the one most likely to be used again in the future. The reasons for why this PR did not continue to use it was because: * To reduce generated code (which would hopefully speed up compile times) * To avoid cluttering the code with generated structs not visible to the user * To keep bevy_reflect simple and extensible (these generated structs act as proxies and might not play well with current or future systems) * To avoid additional unsafe blocks * My own misunderstanding of @Davier's code That last point is obviously on me. I misjudged the code to be too unsafe and unable to handle variant swapping (which it probably could) when I was rebasing it. Looking over it again when writing up this whole section, I see that it was actually a pretty clever way of handling variant representation. #### Benefits of All-in-One As stated before, the current implementation uses an all-in-one approach. All variants are capable of containing fields as far as `Enum` is concerned. This provides a few benefits that the alternatives do not (reduced indirection, safer code, etc.). The biggest benefit, though, is direct field access. Rather than forcing users to have to go through pattern matching, we grant direct access to the fields contained by the current variant. The reason we can do this is because all of the pattern matching happens internally. Getting the field at index `2` will automatically return `Some(...)` for the current variant if it has a field at that index or `None` if it doesn't (or can't). This could be useful for scenarios where the variant has already been verified or just set/swapped (or even where the type of variant doesn't matter): ```rust let dyn_enum: &mut dyn Enum = &mut Foo::Bar {value: 123}; // We know it's the `Bar` variant let field = dyn_enum.field("value").unwrap(); ``` Reflection is not a type-safe abstraction— almost every return value is wrapped in `Option<...>`. There are plenty of places to check and recheck that a value is what Reflect says it is. Forcing users to have to go through `match` each time they want to access a field might just be an extra step among dozens of other verification processes. Some might disagree, but ultimately, my view is that the benefit here is an improvement to the ergonomics and usability of reflected enums. </details> --- ## Changelog ### Added * Added `Enum` trait * Added `Enum` impl to `Reflect` derive macro * Added `DynamicEnum` struct * Added `DynamicVariant` * Added `EnumInfo` * Added `VariantInfo` * Added `StructVariantInfo` * Added `TupleVariantInfo` * Added `UnitVariantInfo` * Added serializtion/deserialization support for enums * Added `EnumSerializer` * Added `VariantType` * Added `VariantFieldIter` * Added `VariantField` * Added `enum_partial_eq(...)` * Added `enum_hash(...)` ### Changed * `Option<T>` now implements `Enum` * `bevy_window` now depends on `bevy_reflect` * Implemented `Reflect` and `FromReflect` for `WindowId` * Derive `FromReflect` on `PerspectiveProjection` * Derive `FromReflect` on `OrthographicProjection` * Derive `FromReflect` on `WindowOrigin` * Derive `FromReflect` on `ScalingMode` * Derive `FromReflect` on `DepthCalculation` ## Migration Guide * Enums no longer need to be treated as values and usages of `#[reflect_value(...)]` can be removed or replaced by `#[reflect(...)]` * Enums (including `Option<T>`) now take a different format when serializing. The format is described above, but this may cause issues for existing scenes that make use of enums. --- Also shout out to @nicopap for helping clean up some of the code here! It's a big feature so help like this is really appreciated! Co-authored-by: Gino Valente <gino.valente.code@gmail.com>
2022-08-02 22:14:41 +00:00
}
bevy_reflect_derive: Tidying up the code (#4712) # Objective The `bevy_reflect_derive` crate is not the cleanest or easiest to follow/maintain. The `lib.rs` file is especially difficult with over 1000 lines of code written in a confusing order. This is just a result of growth within the crate and it would be nice to clean it up for future work. ## Solution Split `bevy_reflect_derive` into many more submodules. The submodules include: * `container_attributes` - Code relating to container attributes * `derive_data` - Code relating to reflection-based derive metadata * `field_attributes` - Code relating to field attributes * `impls` - Code containing actual reflection implementations * `reflect_value` - Code relating to reflection-based value metadata * `registration` - Code relating to type registration * `utility` - General-purpose utility functions This leaves the `lib.rs` file to contain only the public macros, making it much easier to digest (and fewer than 200 lines). By breaking up the code into smaller modules, we make it easier for future contributors to find the code they're looking for or identify which module best fits their own additions. ### Metadata Structs This cleanup also adds two big metadata structs: `ReflectFieldAttr` and `ReflectDeriveData`. The former is used to store all attributes for a struct field (if any). The latter is used to store all metadata for struct-based derive inputs. Both significantly reduce code duplication and make editing these macros much simpler. The tradeoff is that we may collect more metadata than needed. However, this is usually a small thing (such as checking for attributes when they're not really needed or creating a `ReflectFieldAttr` for every field regardless of whether they actually have an attribute). We could try to remove these tradeoffs and squeeze some more performance out, but doing so might come at the cost of developer experience. Personally, I think it's much nicer to create a `ReflectFieldAttr` for every field since it means I don't have to do two `Option` checks. Others may disagree, though, and so we can discuss changing this either in this PR or in a future one. ### Out of Scope _Some_ documentation has been added or improved, but ultimately good docs are probably best saved for a dedicated PR. ## 🔍 Focus Points (for reviewers) I know it's a lot to sift through, so here is a list of **key points for reviewers**: - The following files contain code that was mostly just relocated: - `reflect_value.rs` - `registration.rs` - `container_attributes.rs` was also mostly moved but features some general cleanup (reducing nesting, removing hardcoded strings, etc.) and lots of doc comments - Most impl logic was moved from `lib.rs` to `impls.rs`, but they have been significantly modified to use the new `ReflectDeriveData` metadata struct in order to reduce duplication. - `derive_data.rs` and `field_attributes.rs` contain almost entirely new code and should probably be given the most attention. - Likewise, `from_reflect.rs` saw major changes using `ReflectDeriveData` so it should also be given focus. - There was no change to the `lib.rs` exports so the end-user API should be the same. ## Prior Work This task was initially tackled by @NathanSWard in #2377 (which was closed in favor of this PR), so hats off to them for beating me to the punch by nearly a year! --- ## Changelog * **[INTERNAL]** Split `bevy_reflect_derive` into smaller submodules * **[INTERNAL]** Add `ReflectFieldAttr` * **[INTERNAL]** Add `ReflectDeriveData` * Add `BevyManifest::get_path_direct()` method (`bevy_macro_utils`) Co-authored-by: MrGVSV <49806985+MrGVSV@users.noreply.github.com>
2022-05-12 19:43:23 +00:00
bevy_reflect: Reflect remote types (#6042) # Objective The goal with this PR is to allow the use of types that don't implement `Reflect` within the reflection API. Rust's [orphan rule](https://doc.rust-lang.org/book/ch10-02-traits.html#implementing-a-trait-on-a-type) prevents implementing a trait on an external type when neither type nor trait are owned by the implementor. This means that if a crate, `cool_rust_lib`, defines a type, `Foo`, then a user cannot use it with reflection. What this means is that we have to ignore it most of the time: ```rust #[derive(Reflect)] struct SomeStruct { #[reflect(ignore)] data: cool_rust_lib::Foo } ``` Obviously, it's impossible to implement `Reflect` on `Foo`. But does it *have* to be? Most of reflection doesn't deal with concrete types— it's almost all using `dyn Reflect`. And being very metadata-driven, it should theoretically be possible. I mean, [`serde`](https://serde.rs/remote-derive.html) does it. ## Solution > Special thanks to @danielhenrymantilla for their help reviewing this PR and offering wisdom wrt safety. Taking a page out of `serde`'s book, this PR adds the ability to easily use "remote types" with reflection. In this context, a "remote type" is the external type for which we have no ability to implement `Reflect`. This adds the `#[reflect_remote(...)]` attribute macro, which is used to generate "remote type wrappers". All you have to do is define the wrapper exactly the same as the remote type's definition: ```rust // Pretend this is our external crate mod cool_rust_lib { #[derive(Default)] struct Foo { pub value: String } } #[reflect_remote(cool_rust_lib::Foo)] struct FooWrapper { pub value: String } ``` > **Note:** All fields in the external type *must* be public. This could be addressed with a separate getter/setter attribute either in this PR or in another one. The macro takes this user-defined item and transforms it into a newtype wrapper around the external type, marking it as `#[repr(transparent)]`. The fields/variants defined by the user are simply used to build out the reflection impls. Additionally, it generates an implementation of the new trait, `ReflectRemote`, which helps prevent accidental misuses of this API. Therefore, the output generated by the macro would look something like: ```rust #[repr(transparent)] struct FooWrapper(pub cool_rust_lib::Foo); impl ReflectRemote for FooWrapper { type Remote = cool_rust_lib::Foo; // transmutation methods... } // reflection impls... // these will acknowledge and make use of the `value` field ``` Internally, the reflection API will pass around the `FooWrapper` and [transmute](https://doc.rust-lang.org/std/mem/fn.transmute.html) it where necessary. All we have to do is then tell `Reflect` to do that. So rather than ignoring the field, we tell `Reflect` to use our wrapper using the `#[reflect(remote = ...)]` field attribute: ```rust #[derive(Reflect)] struct SomeStruct { #[reflect(remote = FooWrapper)] data: cool_rust_lib::Foo } ``` #### Other Macros & Type Data Because this macro consumes the defined item and generates a new one, we can't just put our macros anywhere. All macros that should be passed to the generated struct need to come *below* this macro. For example, to derive `Default` and register its associated type data: ```rust // ✅ GOOD #[reflect_remote(cool_rust_lib::Foo)] #[derive(Default)] #[reflect(Default)] struct FooWrapper { pub value: String } // ❌ BAD #[derive(Default)] #[reflect_remote(cool_rust_lib::Foo)] #[reflect(Default)] struct FooWrapper { pub value: String } ``` #### Generics Generics are forwarded to the generated struct as well. They should also be defined in the same order: ```rust #[reflect_remote(RemoteGeneric<'a, T1, T2>)] struct GenericWrapper<'a, T1, T2> { pub foo: &'a T1, pub bar: &'a T2, } ``` > Naming does *not* need to match the original definition's. Only order matters here. > Also note that the code above is just a demonstration and doesn't actually compile since we'd need to enforce certain bounds (e.g. `T1: Reflect`, `'a: 'static`, etc.) #### Nesting And, yes, you can nest remote types: ```rust #[reflect_remote(RemoteOuter)] struct OuterWrapper { #[reflect(remote = InnerWrapper)] pub inner: RemoteInner } #[reflect_remote(RemoteInner)] struct InnerWrapper(usize); ``` #### Assertions This macro will also generate some compile-time assertions to ensure that the correct types are used. It's important we catch this early so users don't have to wait for something to panic. And it also helps keep our `unsafe` a little safer. For example, a wrapper definition that does not match its corresponding remote type will result in an error: ```rust mod external_crate { pub struct TheirStruct(pub u32); } #[reflect_remote(external_crate::TheirStruct)] struct MyStruct(pub String); // ERROR: expected type `u32` but found `String` ``` <details> <summary>Generated Assertion</summary> ```rust const _: () = { #[allow(non_snake_case)] #[allow(unused_variables)] #[allow(unused_assignments)] #[allow(unreachable_patterns)] #[allow(clippy::multiple_bound_locations)] fn assert_wrapper_definition_matches_remote_type( mut __remote__: external_crate::TheirStruct, ) { __remote__.0 = (|| -> ::core::option::Option<String> { None })().unwrap(); } }; ``` </details> Additionally, using the incorrect type in a `#[reflect(remote = ...)]` attribute should result in an error: ```rust mod external_crate { pub struct TheirFoo(pub u32); pub struct TheirBar(pub i32); } #[reflect_remote(external_crate::TheirFoo)] struct MyFoo(pub u32); #[reflect_remote(external_crate::TheirBar)] struct MyBar(pub i32); #[derive(Reflect)] struct MyStruct { #[reflect(remote = MyBar)] // ERROR: expected type `TheirFoo` but found struct `TheirBar` foo: external_crate::TheirFoo } ``` <details> <summary>Generated Assertion</summary> ```rust const _: () = { struct RemoteFieldAssertions; impl RemoteFieldAssertions { #[allow(non_snake_case)] #[allow(clippy::multiple_bound_locations)] fn assert__foo__is_valid_remote() { let _: <MyBar as bevy_reflect::ReflectRemote>::Remote = (|| -> ::core::option::Option<external_crate::TheirFoo> { None })().unwrap(); } } }; ``` </details> ### Discussion There are a couple points that I think still need discussion or validation. - [x] 1. `Any` shenanigans ~~If we wanted to downcast our remote type from a `dyn Reflect`, we'd have to first downcast to the wrapper then extract the inner type. This PR has a [commit](b840db9f74cb6d357f951cb11b150d46bac89ee2) that addresses this by making all the `Reflect::*any` methods return the inner type rather than the wrapper type. This allows us to downcast directly to our remote type.~~ ~~However, I'm not sure if this is something we want to do. For unknowing users, it could be confusing and seemingly inconsistent. Is it worth keeping? Or should this behavior be removed?~~ I think this should be fine. The remote wrapper is an implementation detail and users should not need to downcast to the wrapper type. Feel free to let me know if there are other opinions on this though! - [x] 2. Implementing `Deref/DerefMut` and `From` ~~We don't currently do this, but should we implement other traits on the generated transparent struct? We could implement `Deref`/`DerefMut` to easily access the inner type. And we could implement `From` for easier conversion between the two types (e.g. `T: Into<Foo>`).~~ As mentioned in the comments, we probably don't need to do this. Again, the remote wrapper is an implementation detail, and should generally not be used directly. - [x] 3. ~~Should we define a getter/setter field attribute in this PR as well or leave it for a future one?~~ I think this should be saved for a future PR - [ ] 4. Any foreseeable issues with this implementation? #### Alternatives One alternative to defining our own `ReflectRemote` would be to use [bytemuck's `TransparentWrapper`](https://docs.rs/bytemuck/1.13.1/bytemuck/trait.TransparentWrapper.html) (as suggested by @danielhenrymantilla). This is definitely a viable option, as `ReflectRemote` is pretty much the same thing as `TransparentWrapper`. However, the cost would be bringing in a new crate— though, it is already in use in a few other sub-crates like bevy_render. I think we're okay just defining `ReflectRemote` ourselves, but we can go the bytemuck route if we'd prefer offloading that work to another crate. --- ## Changelog * Added the `#[reflect_remote(...)]` attribute macro to allow `Reflect` to be used on remote types * Added `ReflectRemote` trait for ensuring proper remote wrapper usage
2024-08-12 19:12:53 +00:00
/// Set the remote type for this derived type.
///
/// # Panics
///
/// Panics when called on [`ReflectDerive::Value`].
pub fn set_remote(&mut self, remote_ty: Option<RemoteType<'a>>) {
match self {
Self::Struct(data) | Self::TupleStruct(data) | Self::UnitStruct(data) => {
data.meta.remote_ty = remote_ty;
}
Self::Enum(data) => {
data.meta.remote_ty = remote_ty;
}
Self::Value(meta) => {
meta.remote_ty = remote_ty;
}
}
}
/// Get the remote type path, if any.
pub fn remote_ty(&self) -> Option<RemoteType> {
match self {
Self::Struct(data) | Self::TupleStruct(data) | Self::UnitStruct(data) => {
data.meta.remote_ty()
}
Self::Enum(data) => data.meta.remote_ty(),
Self::Value(meta) => meta.remote_ty(),
}
}
/// Get the [`ReflectMeta`] for this derived type.
pub fn meta(&self) -> &ReflectMeta {
reflect: stable type path v2 (#7184) # Objective - Introduce a stable alternative to [`std::any::type_name`](https://doc.rust-lang.org/std/any/fn.type_name.html). - Rewrite of #5805 with heavy inspiration in design. - On the path to #5830. - Part of solving #3327. ## Solution - Add a `TypePath` trait for static stable type path/name information. - Add a `TypePath` derive macro. - Add a `impl_type_path` macro for implementing internal and foreign types in `bevy_reflect`. --- ## Changelog - Added `TypePath` trait. - Added `DynamicTypePath` trait and `get_type_path` method to `Reflect`. - Added a `TypePath` derive macro. - Added a `bevy_reflect::impl_type_path` for implementing `TypePath` on internal and foreign types in `bevy_reflect`. - Changed `bevy_reflect::utility::(Non)GenericTypeInfoCell` to `(Non)GenericTypedCell<T>` which allows us to be generic over both `TypeInfo` and `TypePath`. - `TypePath` is now a supertrait of `Asset`, `Material` and `Material2d`. - `impl_reflect_struct` needs a `#[type_path = "..."]` attribute to be specified. - `impl_reflect_value` needs to either specify path starting with a double colon (`::core::option::Option`) or an `in my_crate::foo` declaration. - Added `bevy_reflect_derive::ReflectTypePath`. - Most uses of `Ident` in `bevy_reflect_derive` changed to use `ReflectTypePath`. ## Migration Guide - Implementors of `Asset`, `Material` and `Material2d` now also need to derive `TypePath`. - Manual implementors of `Reflect` will need to implement the new `get_type_path` method. ## Open Questions - [x] ~This PR currently does not migrate any usages of `std::any::type_name` to use `bevy_reflect::TypePath` to ease the review process. Should it?~ Migration will be left to a follow-up PR. - [ ] This PR adds a lot of `#[derive(TypePath)]` and `T: TypePath` to satisfy new bounds, mostly when deriving `TypeUuid`. Should we make `TypePath` a supertrait of `TypeUuid`? [Should we remove `TypeUuid` in favour of `TypePath`?](https://github.com/bevyengine/bevy/pull/5805/files/2afbd855327c4b68e0a6b6f03118f289988441a4#r961067892)
2023-06-05 20:31:20 +00:00
match self {
bevy_reflect: Reflect remote types (#6042) # Objective The goal with this PR is to allow the use of types that don't implement `Reflect` within the reflection API. Rust's [orphan rule](https://doc.rust-lang.org/book/ch10-02-traits.html#implementing-a-trait-on-a-type) prevents implementing a trait on an external type when neither type nor trait are owned by the implementor. This means that if a crate, `cool_rust_lib`, defines a type, `Foo`, then a user cannot use it with reflection. What this means is that we have to ignore it most of the time: ```rust #[derive(Reflect)] struct SomeStruct { #[reflect(ignore)] data: cool_rust_lib::Foo } ``` Obviously, it's impossible to implement `Reflect` on `Foo`. But does it *have* to be? Most of reflection doesn't deal with concrete types— it's almost all using `dyn Reflect`. And being very metadata-driven, it should theoretically be possible. I mean, [`serde`](https://serde.rs/remote-derive.html) does it. ## Solution > Special thanks to @danielhenrymantilla for their help reviewing this PR and offering wisdom wrt safety. Taking a page out of `serde`'s book, this PR adds the ability to easily use "remote types" with reflection. In this context, a "remote type" is the external type for which we have no ability to implement `Reflect`. This adds the `#[reflect_remote(...)]` attribute macro, which is used to generate "remote type wrappers". All you have to do is define the wrapper exactly the same as the remote type's definition: ```rust // Pretend this is our external crate mod cool_rust_lib { #[derive(Default)] struct Foo { pub value: String } } #[reflect_remote(cool_rust_lib::Foo)] struct FooWrapper { pub value: String } ``` > **Note:** All fields in the external type *must* be public. This could be addressed with a separate getter/setter attribute either in this PR or in another one. The macro takes this user-defined item and transforms it into a newtype wrapper around the external type, marking it as `#[repr(transparent)]`. The fields/variants defined by the user are simply used to build out the reflection impls. Additionally, it generates an implementation of the new trait, `ReflectRemote`, which helps prevent accidental misuses of this API. Therefore, the output generated by the macro would look something like: ```rust #[repr(transparent)] struct FooWrapper(pub cool_rust_lib::Foo); impl ReflectRemote for FooWrapper { type Remote = cool_rust_lib::Foo; // transmutation methods... } // reflection impls... // these will acknowledge and make use of the `value` field ``` Internally, the reflection API will pass around the `FooWrapper` and [transmute](https://doc.rust-lang.org/std/mem/fn.transmute.html) it where necessary. All we have to do is then tell `Reflect` to do that. So rather than ignoring the field, we tell `Reflect` to use our wrapper using the `#[reflect(remote = ...)]` field attribute: ```rust #[derive(Reflect)] struct SomeStruct { #[reflect(remote = FooWrapper)] data: cool_rust_lib::Foo } ``` #### Other Macros & Type Data Because this macro consumes the defined item and generates a new one, we can't just put our macros anywhere. All macros that should be passed to the generated struct need to come *below* this macro. For example, to derive `Default` and register its associated type data: ```rust // ✅ GOOD #[reflect_remote(cool_rust_lib::Foo)] #[derive(Default)] #[reflect(Default)] struct FooWrapper { pub value: String } // ❌ BAD #[derive(Default)] #[reflect_remote(cool_rust_lib::Foo)] #[reflect(Default)] struct FooWrapper { pub value: String } ``` #### Generics Generics are forwarded to the generated struct as well. They should also be defined in the same order: ```rust #[reflect_remote(RemoteGeneric<'a, T1, T2>)] struct GenericWrapper<'a, T1, T2> { pub foo: &'a T1, pub bar: &'a T2, } ``` > Naming does *not* need to match the original definition's. Only order matters here. > Also note that the code above is just a demonstration and doesn't actually compile since we'd need to enforce certain bounds (e.g. `T1: Reflect`, `'a: 'static`, etc.) #### Nesting And, yes, you can nest remote types: ```rust #[reflect_remote(RemoteOuter)] struct OuterWrapper { #[reflect(remote = InnerWrapper)] pub inner: RemoteInner } #[reflect_remote(RemoteInner)] struct InnerWrapper(usize); ``` #### Assertions This macro will also generate some compile-time assertions to ensure that the correct types are used. It's important we catch this early so users don't have to wait for something to panic. And it also helps keep our `unsafe` a little safer. For example, a wrapper definition that does not match its corresponding remote type will result in an error: ```rust mod external_crate { pub struct TheirStruct(pub u32); } #[reflect_remote(external_crate::TheirStruct)] struct MyStruct(pub String); // ERROR: expected type `u32` but found `String` ``` <details> <summary>Generated Assertion</summary> ```rust const _: () = { #[allow(non_snake_case)] #[allow(unused_variables)] #[allow(unused_assignments)] #[allow(unreachable_patterns)] #[allow(clippy::multiple_bound_locations)] fn assert_wrapper_definition_matches_remote_type( mut __remote__: external_crate::TheirStruct, ) { __remote__.0 = (|| -> ::core::option::Option<String> { None })().unwrap(); } }; ``` </details> Additionally, using the incorrect type in a `#[reflect(remote = ...)]` attribute should result in an error: ```rust mod external_crate { pub struct TheirFoo(pub u32); pub struct TheirBar(pub i32); } #[reflect_remote(external_crate::TheirFoo)] struct MyFoo(pub u32); #[reflect_remote(external_crate::TheirBar)] struct MyBar(pub i32); #[derive(Reflect)] struct MyStruct { #[reflect(remote = MyBar)] // ERROR: expected type `TheirFoo` but found struct `TheirBar` foo: external_crate::TheirFoo } ``` <details> <summary>Generated Assertion</summary> ```rust const _: () = { struct RemoteFieldAssertions; impl RemoteFieldAssertions { #[allow(non_snake_case)] #[allow(clippy::multiple_bound_locations)] fn assert__foo__is_valid_remote() { let _: <MyBar as bevy_reflect::ReflectRemote>::Remote = (|| -> ::core::option::Option<external_crate::TheirFoo> { None })().unwrap(); } } }; ``` </details> ### Discussion There are a couple points that I think still need discussion or validation. - [x] 1. `Any` shenanigans ~~If we wanted to downcast our remote type from a `dyn Reflect`, we'd have to first downcast to the wrapper then extract the inner type. This PR has a [commit](b840db9f74cb6d357f951cb11b150d46bac89ee2) that addresses this by making all the `Reflect::*any` methods return the inner type rather than the wrapper type. This allows us to downcast directly to our remote type.~~ ~~However, I'm not sure if this is something we want to do. For unknowing users, it could be confusing and seemingly inconsistent. Is it worth keeping? Or should this behavior be removed?~~ I think this should be fine. The remote wrapper is an implementation detail and users should not need to downcast to the wrapper type. Feel free to let me know if there are other opinions on this though! - [x] 2. Implementing `Deref/DerefMut` and `From` ~~We don't currently do this, but should we implement other traits on the generated transparent struct? We could implement `Deref`/`DerefMut` to easily access the inner type. And we could implement `From` for easier conversion between the two types (e.g. `T: Into<Foo>`).~~ As mentioned in the comments, we probably don't need to do this. Again, the remote wrapper is an implementation detail, and should generally not be used directly. - [x] 3. ~~Should we define a getter/setter field attribute in this PR as well or leave it for a future one?~~ I think this should be saved for a future PR - [ ] 4. Any foreseeable issues with this implementation? #### Alternatives One alternative to defining our own `ReflectRemote` would be to use [bytemuck's `TransparentWrapper`](https://docs.rs/bytemuck/1.13.1/bytemuck/trait.TransparentWrapper.html) (as suggested by @danielhenrymantilla). This is definitely a viable option, as `ReflectRemote` is pretty much the same thing as `TransparentWrapper`. However, the cost would be bringing in a new crate— though, it is already in use in a few other sub-crates like bevy_render. I think we're okay just defining `ReflectRemote` ourselves, but we can go the bytemuck route if we'd prefer offloading that work to another crate. --- ## Changelog * Added the `#[reflect_remote(...)]` attribute macro to allow `Reflect` to be used on remote types * Added `ReflectRemote` trait for ensuring proper remote wrapper usage
2024-08-12 19:12:53 +00:00
Self::Struct(data) | Self::TupleStruct(data) | Self::UnitStruct(data) => data.meta(),
Self::Enum(data) => data.meta(),
Self::Value(meta) => meta,
}
}
pub fn where_clause_options(&self) -> WhereClauseOptions {
match self {
Self::Struct(data) | Self::TupleStruct(data) | Self::UnitStruct(data) => {
data.where_clause_options()
}
Self::Enum(data) => data.where_clause_options(),
Self::Value(meta) => WhereClauseOptions::new(meta),
reflect: stable type path v2 (#7184) # Objective - Introduce a stable alternative to [`std::any::type_name`](https://doc.rust-lang.org/std/any/fn.type_name.html). - Rewrite of #5805 with heavy inspiration in design. - On the path to #5830. - Part of solving #3327. ## Solution - Add a `TypePath` trait for static stable type path/name information. - Add a `TypePath` derive macro. - Add a `impl_type_path` macro for implementing internal and foreign types in `bevy_reflect`. --- ## Changelog - Added `TypePath` trait. - Added `DynamicTypePath` trait and `get_type_path` method to `Reflect`. - Added a `TypePath` derive macro. - Added a `bevy_reflect::impl_type_path` for implementing `TypePath` on internal and foreign types in `bevy_reflect`. - Changed `bevy_reflect::utility::(Non)GenericTypeInfoCell` to `(Non)GenericTypedCell<T>` which allows us to be generic over both `TypeInfo` and `TypePath`. - `TypePath` is now a supertrait of `Asset`, `Material` and `Material2d`. - `impl_reflect_struct` needs a `#[type_path = "..."]` attribute to be specified. - `impl_reflect_value` needs to either specify path starting with a double colon (`::core::option::Option`) or an `in my_crate::foo` declaration. - Added `bevy_reflect_derive::ReflectTypePath`. - Most uses of `Ident` in `bevy_reflect_derive` changed to use `ReflectTypePath`. ## Migration Guide - Implementors of `Asset`, `Material` and `Material2d` now also need to derive `TypePath`. - Manual implementors of `Reflect` will need to implement the new `get_type_path` method. ## Open Questions - [x] ~This PR currently does not migrate any usages of `std::any::type_name` to use `bevy_reflect::TypePath` to ease the review process. Should it?~ Migration will be left to a follow-up PR. - [ ] This PR adds a lot of `#[derive(TypePath)]` and `T: TypePath` to satisfy new bounds, mostly when deriving `TypeUuid`. Should we make `TypePath` a supertrait of `TypeUuid`? [Should we remove `TypeUuid` in favour of `TypePath`?](https://github.com/bevyengine/bevy/pull/5805/files/2afbd855327c4b68e0a6b6f03118f289988441a4#r961067892)
2023-06-05 20:31:20 +00:00
}
}
bevy_reflect: Reflect enums (#4761) # Objective > This is a revival of #1347. Credit for the original PR should go to @Davier. Currently, enums are treated as `ReflectRef::Value` types by `bevy_reflect`. Obviously, there needs to be better a better representation for enums using the reflection API. ## Solution Based on prior work from @Davier, an `Enum` trait has been added as well as the ability to automatically implement it via the `Reflect` derive macro. This allows enums to be expressed dynamically: ```rust #[derive(Reflect)] enum Foo { A, B(usize), C { value: f32 }, } let mut foo = Foo::B(123); assert_eq!("B", foo.variant_name()); assert_eq!(1, foo.field_len()); let new_value = DynamicEnum::from(Foo::C { value: 1.23 }); foo.apply(&new_value); assert_eq!(Foo::C{value: 1.23}, foo); ``` ### Features #### Derive Macro Use the `#[derive(Reflect)]` macro to automatically implement the `Enum` trait for enum definitions. Optionally, you can use `#[reflect(ignore)]` with both variants and variant fields, just like you can with structs. These ignored items will not be considered as part of the reflection and cannot be accessed via reflection. ```rust #[derive(Reflect)] enum TestEnum { A, // Uncomment to ignore all of `B` // #[reflect(ignore)] B(usize), C { // Uncomment to ignore only field `foo` of `C` // #[reflect(ignore)] foo: f32, bar: bool, }, } ``` #### Dynamic Enums Enums may be created/represented dynamically via the `DynamicEnum` struct. The main purpose of this struct is to allow enums to be deserialized into a partial state and to allow dynamic patching. In order to ensure conversion from a `DynamicEnum` to a concrete enum type goes smoothly, be sure to add `FromReflect` to your derive macro. ```rust let mut value = TestEnum::A; // Create from a concrete instance let dyn_enum = DynamicEnum::from(TestEnum::B(123)); value.apply(&dyn_enum); assert_eq!(TestEnum::B(123), value); // Create a purely dynamic instance let dyn_enum = DynamicEnum::new("TestEnum", "A", ()); value.apply(&dyn_enum); assert_eq!(TestEnum::A, value); ``` #### Variants An enum value is always represented as one of its variants— never the enum in its entirety. ```rust let value = TestEnum::A; assert_eq!("A", value.variant_name()); // Since we are using the `A` variant, we cannot also be the `B` variant assert_ne!("B", value.variant_name()); ``` All variant types are representable within the `Enum` trait: unit, struct, and tuple. You can get the current type like: ```rust match value.variant_type() { VariantType::Unit => println!("A unit variant!"), VariantType::Struct => println!("A struct variant!"), VariantType::Tuple => println!("A tuple variant!"), } ``` > Notice that they don't contain any values representing the fields. These are purely tags. If a variant has them, you can access the fields as well: ```rust let mut value = TestEnum::C { foo: 1.23, bar: false }; // Read/write specific fields *value.field_mut("bar").unwrap() = true; // Iterate over the entire collection of fields for field in value.iter_fields() { println!("{} = {:?}", field.name(), field.value()); } ``` #### Variant Swapping It might seem odd to group all variant types under a single trait (why allow `iter_fields` on a unit variant?), but the reason this was done ~~is to easily allow *variant swapping*.~~ As I was recently drafting up the **Design Decisions** section, I discovered that other solutions could have been made to work with variant swapping. So while there are reasons to keep the all-in-one approach, variant swapping is _not_ one of them. ```rust let mut value: Box<dyn Enum> = Box::new(TestEnum::A); value.set(Box::new(TestEnum::B(123))).unwrap(); ``` #### Serialization Enums can be serialized and deserialized via reflection without needing to implement `Serialize` or `Deserialize` themselves (which can save thousands of lines of generated code). Below are the ways an enum can be serialized. > Note, like the rest of reflection-based serialization, the order of the keys in these representations is important! ##### Unit ```json { "type": "my_crate::TestEnum", "enum": { "variant": "A" } } ``` ##### Tuple ```json { "type": "my_crate::TestEnum", "enum": { "variant": "B", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` <details> <summary>Effects on Option</summary> This ends up making `Option` look a little ugly: ```json { "type": "core::option::Option<usize>", "enum": { "variant": "Some", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` </details> ##### Struct ```json { "type": "my_crate::TestEnum", "enum": { "variant": "C", "struct": { "foo": { "type": "f32", "value": 1.23 }, "bar": { "type": "bool", "value": false } } } } ``` ## Design Decisions <details> <summary><strong>View Section</strong></summary> This section is here to provide some context for why certain decisions were made for this PR, alternatives that could have been used instead, and what could be improved upon in the future. ### Variant Representation One of the biggest decisions was to decide on how to represent variants. The current design uses a "all-in-one" design where unit, tuple, and struct variants are all simultaneously represented by the `Enum` trait. This is not the only way it could have been done, though. #### Alternatives ##### 1. Variant Traits One way of representing variants would be to define traits for each variant, implementing them whenever an enum featured at least one instance of them. This would allow us to define variants like: ```rust pub trait Enum: Reflect { fn variant(&self) -> Variant; } pub enum Variant<'a> { Unit, Tuple(&'a dyn TupleVariant), Struct(&'a dyn StructVariant), } pub trait TupleVariant { fn field_len(&self) -> usize; // ... } ``` And then do things like: ```rust fn get_tuple_len(foo: &dyn Enum) -> usize { match foo.variant() { Variant::Tuple(tuple) => tuple.field_len(), _ => panic!("not a tuple variant!") } } ``` The reason this PR does not go with this approach is because of the fact that variants are not separate types. In other words, we cannot implement traits on specific variants— these cover the *entire* enum. This means we offer an easy footgun: ```rust let foo: Option<i32> = None; let my_enum = Box::new(foo) as Box<dyn TupleVariant>; ``` Here, `my_enum` contains `foo`, which is a unit variant. However, since we need to implement `TupleVariant` for `Option` as a whole, it's possible to perform such a cast. This is obviously wrong, but could easily go unnoticed. So unfortunately, this makes it not a good candidate for representing variants. ##### 2. Variant Structs To get around the issue of traits necessarily needing to apply to both the enum and its variants, we could instead use structs that are created on a per-variant basis. This was also considered but was ultimately [[removed](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c)](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c) due to concerns about allocations. Each variant struct would probably look something like: ```rust pub trait Enum: Reflect { fn variant_mut(&self) -> VariantMut; } pub enum VariantMut<'a> { Unit, Tuple(TupleVariantMut), Struct(StructVariantMut), } struct StructVariantMut<'a> { fields: Vec<&'a mut dyn Reflect>, field_indices: HashMap<Cow<'static, str>, usize> } ``` This allows us to isolate struct variants into their own defined struct and define methods specifically for their use. It also prevents users from casting to it since it's not a trait. However, this is not an optimal solution. Both `field_indices` and `fields` will require an allocation (remember, a `Box<[T]>` still requires a `Vec<T>` in order to be constructed). This *might* be a problem if called frequently enough. ##### 3. Generated Structs The original design, implemented by @Davier, instead generates structs specific for each variant. So if we had a variant path like `Foo::Bar`, we'd generate a struct named `FooBarWrapper`. This would be newtyped around the original enum and forward tuple or struct methods to the enum with the chosen variant. Because it involved using the `Tuple` and `Struct` traits (which are also both bound on `Reflect`), this meant a bit more code had to be generated. For a single struct variant with one field, the generated code amounted to ~110LoC. However, each new field added to that variant only added ~6 more LoC. In order to work properly, the enum had to be transmuted to the generated struct: ```rust fn variant(&self) -> crate::EnumVariant<'_> { match self { Foo::Bar {value: i32} => { let wrapper_ref = unsafe { std::mem::transmute::<&Self, &FooBarWrapper>(self) }; crate::EnumVariant::Struct(wrapper_ref as &dyn crate::Struct) } } } ``` This works because `FooBarWrapper` is defined as `repr(transparent)`. Out of all the alternatives, this would probably be the one most likely to be used again in the future. The reasons for why this PR did not continue to use it was because: * To reduce generated code (which would hopefully speed up compile times) * To avoid cluttering the code with generated structs not visible to the user * To keep bevy_reflect simple and extensible (these generated structs act as proxies and might not play well with current or future systems) * To avoid additional unsafe blocks * My own misunderstanding of @Davier's code That last point is obviously on me. I misjudged the code to be too unsafe and unable to handle variant swapping (which it probably could) when I was rebasing it. Looking over it again when writing up this whole section, I see that it was actually a pretty clever way of handling variant representation. #### Benefits of All-in-One As stated before, the current implementation uses an all-in-one approach. All variants are capable of containing fields as far as `Enum` is concerned. This provides a few benefits that the alternatives do not (reduced indirection, safer code, etc.). The biggest benefit, though, is direct field access. Rather than forcing users to have to go through pattern matching, we grant direct access to the fields contained by the current variant. The reason we can do this is because all of the pattern matching happens internally. Getting the field at index `2` will automatically return `Some(...)` for the current variant if it has a field at that index or `None` if it doesn't (or can't). This could be useful for scenarios where the variant has already been verified or just set/swapped (or even where the type of variant doesn't matter): ```rust let dyn_enum: &mut dyn Enum = &mut Foo::Bar {value: 123}; // We know it's the `Bar` variant let field = dyn_enum.field("value").unwrap(); ``` Reflection is not a type-safe abstraction— almost every return value is wrapped in `Option<...>`. There are plenty of places to check and recheck that a value is what Reflect says it is. Forcing users to have to go through `match` each time they want to access a field might just be an extra step among dozens of other verification processes. Some might disagree, but ultimately, my view is that the benefit here is an improvement to the ergonomics and usability of reflected enums. </details> --- ## Changelog ### Added * Added `Enum` trait * Added `Enum` impl to `Reflect` derive macro * Added `DynamicEnum` struct * Added `DynamicVariant` * Added `EnumInfo` * Added `VariantInfo` * Added `StructVariantInfo` * Added `TupleVariantInfo` * Added `UnitVariantInfo` * Added serializtion/deserialization support for enums * Added `EnumSerializer` * Added `VariantType` * Added `VariantFieldIter` * Added `VariantField` * Added `enum_partial_eq(...)` * Added `enum_hash(...)` ### Changed * `Option<T>` now implements `Enum` * `bevy_window` now depends on `bevy_reflect` * Implemented `Reflect` and `FromReflect` for `WindowId` * Derive `FromReflect` on `PerspectiveProjection` * Derive `FromReflect` on `OrthographicProjection` * Derive `FromReflect` on `WindowOrigin` * Derive `FromReflect` on `ScalingMode` * Derive `FromReflect` on `DepthCalculation` ## Migration Guide * Enums no longer need to be treated as values and usages of `#[reflect_value(...)]` can be removed or replaced by `#[reflect(...)]` * Enums (including `Option<T>`) now take a different format when serializing. The format is described above, but this may cause issues for existing scenes that make use of enums. --- Also shout out to @nicopap for helping clean up some of the code here! It's a big feature so help like this is really appreciated! Co-authored-by: Gino Valente <gino.valente.code@gmail.com>
2022-08-02 22:14:41 +00:00
fn collect_struct_fields(fields: &'a Fields) -> Result<Vec<StructField<'a>>, syn::Error> {
bevy_reflect: Fix ignored/skipped field order (#7575) # Objective Fixes #5101 Alternative to #6511 ## Solution Corrected the behavior for ignored fields in `FromReflect`, which was previously using the incorrect field indexes. Similarly, fields marked with `#[reflect(skip_serializing)]` no longer break when using `FromReflect` after deserialization. This was done by modifying `SerializationData` to store a function pointer that can later be used to generate a default instance of the skipped field during deserialization. The function pointer points to a function generated by the derive macro using the behavior designated by `#[reflect(default)]` (or just `Default` if none provided). The entire output of the macro is now wrapped in an [unnamed constant](https://doc.rust-lang.org/stable/reference/items/constant-items.html#unnamed-constant) which keeps this behavior hygienic. #### Rationale The biggest downside to this approach is that it requires fields marked `#[reflect(skip_serializing)]` to provide the ability to create a default instance— either via a `Default` impl or by specifying a custom one. While this isn't great, I think it might be justified by the fact that we really need to create this value when using `FromReflect` on a deserialized object. And we need to do this _during_ deserialization because after that (at least for tuples and tuple structs) we lose information about which field is which: _"is the value at index 1 in this `DynamicTupleStruct` the actual value for index 1 or is it really the value for index 2 since index 1 is skippable...?"_ #### Alternatives An alternative would be to store `Option<Box<dyn Reflect>>` within `DynamicTuple` and `DynamicTupleStruct` instead of just `Box<dyn Reflect>`. This would allow us to insert "empty"/"missing" fields during deserialization, thus saving the positional information of the skipped fields. However, this may require changing the API of `Tuple` and `TupleStruct` such that they can account for their dynamic counterparts returning `None` for a skipped field. In practice this would probably mean exposing the `Option`-ness of the dynamics onto implementors via methods like `Tuple::drain` or `TupleStruct::field`. Personally, I think requiring `Default` would be better than muddying up the API to account for these special cases. But I'm open to trying out this other approach if the community feels that it's better. --- ## Changelog ### Public Changes #### Fixed - The behaviors of `#[reflect(ignore)]` and `#[reflect(skip_serializing)]` are no longer dependent on field order #### Changed - Fields marked with `#[reflect(skip_serializing)]` now need to either implement `Default` or specify a custom default function using `#[reflect(default = "path::to::some_func")]` - Deserializing a type with fields marked `#[reflect(skip_serializing)]` will now include that field initialized to its specified default value - `SerializationData::new` now takes the new `SkippedField` struct along with the skipped field index - Renamed `SerializationData::is_ignored_field` to `SerializationData::is_field_skipped` #### Added - Added `SkippedField` struct - Added methods `SerializationData::generate_default` and `SerializationData::iter_skipped` ### Internal Changes #### Changed - Replaced `members_to_serialization_denylist` and `BitSet<u32>` with `SerializationDataDef` - The `Reflect` derive is more hygienic as it now outputs within an [unnamed constant](https://doc.rust-lang.org/stable/reference/items/constant-items.html#unnamed-constant) - `StructField::index` has been split up into `StructField::declaration_index` and `StructField::reflection_index` #### Removed - Removed `bitset` dependency ## Migration Guide * Fields marked `#[reflect(skip_serializing)]` now must implement `Default` or specify a custom default function with `#[reflect(default = "path::to::some_func")]` ```rust #[derive(Reflect)] struct MyStruct { #[reflect(skip_serializing)] #[reflect(default = "get_foo_default")] foo: Foo, // <- `Foo` does not impl `Default` so requires a custom function #[reflect(skip_serializing)] bar: Bar, // <- `Bar` impls `Default` } #[derive(Reflect)] struct Foo(i32); #[derive(Reflect, Default)] struct Bar(i32); fn get_foo_default() -> Foo { Foo(123) } ``` * `SerializationData::new` has been changed to expect an iterator of `(usize, SkippedField)` rather than one of just `usize` ```rust // BEFORE SerializationData::new([0, 3].into_iter()); // AFTER SerializationData::new([ (0, SkippedField::new(field_0_default_fn)), (3, SkippedField::new(field_3_default_fn)), ].into_iter()); ``` * `Serialization::is_ignored_field` has been renamed to `Serialization::is_field_skipped` * Fields marked `#[reflect(skip_serializing)]` are now included in deserialization output. This may affect logic that expected those fields to be absent.
2023-10-22 12:43:31 +00:00
let mut active_index = 0;
bevy_reflect: Reflect enums (#4761) # Objective > This is a revival of #1347. Credit for the original PR should go to @Davier. Currently, enums are treated as `ReflectRef::Value` types by `bevy_reflect`. Obviously, there needs to be better a better representation for enums using the reflection API. ## Solution Based on prior work from @Davier, an `Enum` trait has been added as well as the ability to automatically implement it via the `Reflect` derive macro. This allows enums to be expressed dynamically: ```rust #[derive(Reflect)] enum Foo { A, B(usize), C { value: f32 }, } let mut foo = Foo::B(123); assert_eq!("B", foo.variant_name()); assert_eq!(1, foo.field_len()); let new_value = DynamicEnum::from(Foo::C { value: 1.23 }); foo.apply(&new_value); assert_eq!(Foo::C{value: 1.23}, foo); ``` ### Features #### Derive Macro Use the `#[derive(Reflect)]` macro to automatically implement the `Enum` trait for enum definitions. Optionally, you can use `#[reflect(ignore)]` with both variants and variant fields, just like you can with structs. These ignored items will not be considered as part of the reflection and cannot be accessed via reflection. ```rust #[derive(Reflect)] enum TestEnum { A, // Uncomment to ignore all of `B` // #[reflect(ignore)] B(usize), C { // Uncomment to ignore only field `foo` of `C` // #[reflect(ignore)] foo: f32, bar: bool, }, } ``` #### Dynamic Enums Enums may be created/represented dynamically via the `DynamicEnum` struct. The main purpose of this struct is to allow enums to be deserialized into a partial state and to allow dynamic patching. In order to ensure conversion from a `DynamicEnum` to a concrete enum type goes smoothly, be sure to add `FromReflect` to your derive macro. ```rust let mut value = TestEnum::A; // Create from a concrete instance let dyn_enum = DynamicEnum::from(TestEnum::B(123)); value.apply(&dyn_enum); assert_eq!(TestEnum::B(123), value); // Create a purely dynamic instance let dyn_enum = DynamicEnum::new("TestEnum", "A", ()); value.apply(&dyn_enum); assert_eq!(TestEnum::A, value); ``` #### Variants An enum value is always represented as one of its variants— never the enum in its entirety. ```rust let value = TestEnum::A; assert_eq!("A", value.variant_name()); // Since we are using the `A` variant, we cannot also be the `B` variant assert_ne!("B", value.variant_name()); ``` All variant types are representable within the `Enum` trait: unit, struct, and tuple. You can get the current type like: ```rust match value.variant_type() { VariantType::Unit => println!("A unit variant!"), VariantType::Struct => println!("A struct variant!"), VariantType::Tuple => println!("A tuple variant!"), } ``` > Notice that they don't contain any values representing the fields. These are purely tags. If a variant has them, you can access the fields as well: ```rust let mut value = TestEnum::C { foo: 1.23, bar: false }; // Read/write specific fields *value.field_mut("bar").unwrap() = true; // Iterate over the entire collection of fields for field in value.iter_fields() { println!("{} = {:?}", field.name(), field.value()); } ``` #### Variant Swapping It might seem odd to group all variant types under a single trait (why allow `iter_fields` on a unit variant?), but the reason this was done ~~is to easily allow *variant swapping*.~~ As I was recently drafting up the **Design Decisions** section, I discovered that other solutions could have been made to work with variant swapping. So while there are reasons to keep the all-in-one approach, variant swapping is _not_ one of them. ```rust let mut value: Box<dyn Enum> = Box::new(TestEnum::A); value.set(Box::new(TestEnum::B(123))).unwrap(); ``` #### Serialization Enums can be serialized and deserialized via reflection without needing to implement `Serialize` or `Deserialize` themselves (which can save thousands of lines of generated code). Below are the ways an enum can be serialized. > Note, like the rest of reflection-based serialization, the order of the keys in these representations is important! ##### Unit ```json { "type": "my_crate::TestEnum", "enum": { "variant": "A" } } ``` ##### Tuple ```json { "type": "my_crate::TestEnum", "enum": { "variant": "B", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` <details> <summary>Effects on Option</summary> This ends up making `Option` look a little ugly: ```json { "type": "core::option::Option<usize>", "enum": { "variant": "Some", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` </details> ##### Struct ```json { "type": "my_crate::TestEnum", "enum": { "variant": "C", "struct": { "foo": { "type": "f32", "value": 1.23 }, "bar": { "type": "bool", "value": false } } } } ``` ## Design Decisions <details> <summary><strong>View Section</strong></summary> This section is here to provide some context for why certain decisions were made for this PR, alternatives that could have been used instead, and what could be improved upon in the future. ### Variant Representation One of the biggest decisions was to decide on how to represent variants. The current design uses a "all-in-one" design where unit, tuple, and struct variants are all simultaneously represented by the `Enum` trait. This is not the only way it could have been done, though. #### Alternatives ##### 1. Variant Traits One way of representing variants would be to define traits for each variant, implementing them whenever an enum featured at least one instance of them. This would allow us to define variants like: ```rust pub trait Enum: Reflect { fn variant(&self) -> Variant; } pub enum Variant<'a> { Unit, Tuple(&'a dyn TupleVariant), Struct(&'a dyn StructVariant), } pub trait TupleVariant { fn field_len(&self) -> usize; // ... } ``` And then do things like: ```rust fn get_tuple_len(foo: &dyn Enum) -> usize { match foo.variant() { Variant::Tuple(tuple) => tuple.field_len(), _ => panic!("not a tuple variant!") } } ``` The reason this PR does not go with this approach is because of the fact that variants are not separate types. In other words, we cannot implement traits on specific variants— these cover the *entire* enum. This means we offer an easy footgun: ```rust let foo: Option<i32> = None; let my_enum = Box::new(foo) as Box<dyn TupleVariant>; ``` Here, `my_enum` contains `foo`, which is a unit variant. However, since we need to implement `TupleVariant` for `Option` as a whole, it's possible to perform such a cast. This is obviously wrong, but could easily go unnoticed. So unfortunately, this makes it not a good candidate for representing variants. ##### 2. Variant Structs To get around the issue of traits necessarily needing to apply to both the enum and its variants, we could instead use structs that are created on a per-variant basis. This was also considered but was ultimately [[removed](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c)](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c) due to concerns about allocations. Each variant struct would probably look something like: ```rust pub trait Enum: Reflect { fn variant_mut(&self) -> VariantMut; } pub enum VariantMut<'a> { Unit, Tuple(TupleVariantMut), Struct(StructVariantMut), } struct StructVariantMut<'a> { fields: Vec<&'a mut dyn Reflect>, field_indices: HashMap<Cow<'static, str>, usize> } ``` This allows us to isolate struct variants into their own defined struct and define methods specifically for their use. It also prevents users from casting to it since it's not a trait. However, this is not an optimal solution. Both `field_indices` and `fields` will require an allocation (remember, a `Box<[T]>` still requires a `Vec<T>` in order to be constructed). This *might* be a problem if called frequently enough. ##### 3. Generated Structs The original design, implemented by @Davier, instead generates structs specific for each variant. So if we had a variant path like `Foo::Bar`, we'd generate a struct named `FooBarWrapper`. This would be newtyped around the original enum and forward tuple or struct methods to the enum with the chosen variant. Because it involved using the `Tuple` and `Struct` traits (which are also both bound on `Reflect`), this meant a bit more code had to be generated. For a single struct variant with one field, the generated code amounted to ~110LoC. However, each new field added to that variant only added ~6 more LoC. In order to work properly, the enum had to be transmuted to the generated struct: ```rust fn variant(&self) -> crate::EnumVariant<'_> { match self { Foo::Bar {value: i32} => { let wrapper_ref = unsafe { std::mem::transmute::<&Self, &FooBarWrapper>(self) }; crate::EnumVariant::Struct(wrapper_ref as &dyn crate::Struct) } } } ``` This works because `FooBarWrapper` is defined as `repr(transparent)`. Out of all the alternatives, this would probably be the one most likely to be used again in the future. The reasons for why this PR did not continue to use it was because: * To reduce generated code (which would hopefully speed up compile times) * To avoid cluttering the code with generated structs not visible to the user * To keep bevy_reflect simple and extensible (these generated structs act as proxies and might not play well with current or future systems) * To avoid additional unsafe blocks * My own misunderstanding of @Davier's code That last point is obviously on me. I misjudged the code to be too unsafe and unable to handle variant swapping (which it probably could) when I was rebasing it. Looking over it again when writing up this whole section, I see that it was actually a pretty clever way of handling variant representation. #### Benefits of All-in-One As stated before, the current implementation uses an all-in-one approach. All variants are capable of containing fields as far as `Enum` is concerned. This provides a few benefits that the alternatives do not (reduced indirection, safer code, etc.). The biggest benefit, though, is direct field access. Rather than forcing users to have to go through pattern matching, we grant direct access to the fields contained by the current variant. The reason we can do this is because all of the pattern matching happens internally. Getting the field at index `2` will automatically return `Some(...)` for the current variant if it has a field at that index or `None` if it doesn't (or can't). This could be useful for scenarios where the variant has already been verified or just set/swapped (or even where the type of variant doesn't matter): ```rust let dyn_enum: &mut dyn Enum = &mut Foo::Bar {value: 123}; // We know it's the `Bar` variant let field = dyn_enum.field("value").unwrap(); ``` Reflection is not a type-safe abstraction— almost every return value is wrapped in `Option<...>`. There are plenty of places to check and recheck that a value is what Reflect says it is. Forcing users to have to go through `match` each time they want to access a field might just be an extra step among dozens of other verification processes. Some might disagree, but ultimately, my view is that the benefit here is an improvement to the ergonomics and usability of reflected enums. </details> --- ## Changelog ### Added * Added `Enum` trait * Added `Enum` impl to `Reflect` derive macro * Added `DynamicEnum` struct * Added `DynamicVariant` * Added `EnumInfo` * Added `VariantInfo` * Added `StructVariantInfo` * Added `TupleVariantInfo` * Added `UnitVariantInfo` * Added serializtion/deserialization support for enums * Added `EnumSerializer` * Added `VariantType` * Added `VariantFieldIter` * Added `VariantField` * Added `enum_partial_eq(...)` * Added `enum_hash(...)` ### Changed * `Option<T>` now implements `Enum` * `bevy_window` now depends on `bevy_reflect` * Implemented `Reflect` and `FromReflect` for `WindowId` * Derive `FromReflect` on `PerspectiveProjection` * Derive `FromReflect` on `OrthographicProjection` * Derive `FromReflect` on `WindowOrigin` * Derive `FromReflect` on `ScalingMode` * Derive `FromReflect` on `DepthCalculation` ## Migration Guide * Enums no longer need to be treated as values and usages of `#[reflect_value(...)]` can be removed or replaced by `#[reflect(...)]` * Enums (including `Option<T>`) now take a different format when serializing. The format is described above, but this may cause issues for existing scenes that make use of enums. --- Also shout out to @nicopap for helping clean up some of the code here! It's a big feature so help like this is really appreciated! Co-authored-by: Gino Valente <gino.valente.code@gmail.com>
2022-08-02 22:14:41 +00:00
let sifter: utility::ResultSifter<StructField<'a>> = fields
bevy_reflect_derive: Tidying up the code (#4712) # Objective The `bevy_reflect_derive` crate is not the cleanest or easiest to follow/maintain. The `lib.rs` file is especially difficult with over 1000 lines of code written in a confusing order. This is just a result of growth within the crate and it would be nice to clean it up for future work. ## Solution Split `bevy_reflect_derive` into many more submodules. The submodules include: * `container_attributes` - Code relating to container attributes * `derive_data` - Code relating to reflection-based derive metadata * `field_attributes` - Code relating to field attributes * `impls` - Code containing actual reflection implementations * `reflect_value` - Code relating to reflection-based value metadata * `registration` - Code relating to type registration * `utility` - General-purpose utility functions This leaves the `lib.rs` file to contain only the public macros, making it much easier to digest (and fewer than 200 lines). By breaking up the code into smaller modules, we make it easier for future contributors to find the code they're looking for or identify which module best fits their own additions. ### Metadata Structs This cleanup also adds two big metadata structs: `ReflectFieldAttr` and `ReflectDeriveData`. The former is used to store all attributes for a struct field (if any). The latter is used to store all metadata for struct-based derive inputs. Both significantly reduce code duplication and make editing these macros much simpler. The tradeoff is that we may collect more metadata than needed. However, this is usually a small thing (such as checking for attributes when they're not really needed or creating a `ReflectFieldAttr` for every field regardless of whether they actually have an attribute). We could try to remove these tradeoffs and squeeze some more performance out, but doing so might come at the cost of developer experience. Personally, I think it's much nicer to create a `ReflectFieldAttr` for every field since it means I don't have to do two `Option` checks. Others may disagree, though, and so we can discuss changing this either in this PR or in a future one. ### Out of Scope _Some_ documentation has been added or improved, but ultimately good docs are probably best saved for a dedicated PR. ## 🔍 Focus Points (for reviewers) I know it's a lot to sift through, so here is a list of **key points for reviewers**: - The following files contain code that was mostly just relocated: - `reflect_value.rs` - `registration.rs` - `container_attributes.rs` was also mostly moved but features some general cleanup (reducing nesting, removing hardcoded strings, etc.) and lots of doc comments - Most impl logic was moved from `lib.rs` to `impls.rs`, but they have been significantly modified to use the new `ReflectDeriveData` metadata struct in order to reduce duplication. - `derive_data.rs` and `field_attributes.rs` contain almost entirely new code and should probably be given the most attention. - Likewise, `from_reflect.rs` saw major changes using `ReflectDeriveData` so it should also be given focus. - There was no change to the `lib.rs` exports so the end-user API should be the same. ## Prior Work This task was initially tackled by @NathanSWard in #2377 (which was closed in favor of this PR), so hats off to them for beating me to the punch by nearly a year! --- ## Changelog * **[INTERNAL]** Split `bevy_reflect_derive` into smaller submodules * **[INTERNAL]** Add `ReflectFieldAttr` * **[INTERNAL]** Add `ReflectDeriveData` * Add `BevyManifest::get_path_direct()` method (`bevy_macro_utils`) Co-authored-by: MrGVSV <49806985+MrGVSV@users.noreply.github.com>
2022-05-12 19:43:23 +00:00
.iter()
.enumerate()
bevy_reflect: Fix ignored/skipped field order (#7575) # Objective Fixes #5101 Alternative to #6511 ## Solution Corrected the behavior for ignored fields in `FromReflect`, which was previously using the incorrect field indexes. Similarly, fields marked with `#[reflect(skip_serializing)]` no longer break when using `FromReflect` after deserialization. This was done by modifying `SerializationData` to store a function pointer that can later be used to generate a default instance of the skipped field during deserialization. The function pointer points to a function generated by the derive macro using the behavior designated by `#[reflect(default)]` (or just `Default` if none provided). The entire output of the macro is now wrapped in an [unnamed constant](https://doc.rust-lang.org/stable/reference/items/constant-items.html#unnamed-constant) which keeps this behavior hygienic. #### Rationale The biggest downside to this approach is that it requires fields marked `#[reflect(skip_serializing)]` to provide the ability to create a default instance— either via a `Default` impl or by specifying a custom one. While this isn't great, I think it might be justified by the fact that we really need to create this value when using `FromReflect` on a deserialized object. And we need to do this _during_ deserialization because after that (at least for tuples and tuple structs) we lose information about which field is which: _"is the value at index 1 in this `DynamicTupleStruct` the actual value for index 1 or is it really the value for index 2 since index 1 is skippable...?"_ #### Alternatives An alternative would be to store `Option<Box<dyn Reflect>>` within `DynamicTuple` and `DynamicTupleStruct` instead of just `Box<dyn Reflect>`. This would allow us to insert "empty"/"missing" fields during deserialization, thus saving the positional information of the skipped fields. However, this may require changing the API of `Tuple` and `TupleStruct` such that they can account for their dynamic counterparts returning `None` for a skipped field. In practice this would probably mean exposing the `Option`-ness of the dynamics onto implementors via methods like `Tuple::drain` or `TupleStruct::field`. Personally, I think requiring `Default` would be better than muddying up the API to account for these special cases. But I'm open to trying out this other approach if the community feels that it's better. --- ## Changelog ### Public Changes #### Fixed - The behaviors of `#[reflect(ignore)]` and `#[reflect(skip_serializing)]` are no longer dependent on field order #### Changed - Fields marked with `#[reflect(skip_serializing)]` now need to either implement `Default` or specify a custom default function using `#[reflect(default = "path::to::some_func")]` - Deserializing a type with fields marked `#[reflect(skip_serializing)]` will now include that field initialized to its specified default value - `SerializationData::new` now takes the new `SkippedField` struct along with the skipped field index - Renamed `SerializationData::is_ignored_field` to `SerializationData::is_field_skipped` #### Added - Added `SkippedField` struct - Added methods `SerializationData::generate_default` and `SerializationData::iter_skipped` ### Internal Changes #### Changed - Replaced `members_to_serialization_denylist` and `BitSet<u32>` with `SerializationDataDef` - The `Reflect` derive is more hygienic as it now outputs within an [unnamed constant](https://doc.rust-lang.org/stable/reference/items/constant-items.html#unnamed-constant) - `StructField::index` has been split up into `StructField::declaration_index` and `StructField::reflection_index` #### Removed - Removed `bitset` dependency ## Migration Guide * Fields marked `#[reflect(skip_serializing)]` now must implement `Default` or specify a custom default function with `#[reflect(default = "path::to::some_func")]` ```rust #[derive(Reflect)] struct MyStruct { #[reflect(skip_serializing)] #[reflect(default = "get_foo_default")] foo: Foo, // <- `Foo` does not impl `Default` so requires a custom function #[reflect(skip_serializing)] bar: Bar, // <- `Bar` impls `Default` } #[derive(Reflect)] struct Foo(i32); #[derive(Reflect, Default)] struct Bar(i32); fn get_foo_default() -> Foo { Foo(123) } ``` * `SerializationData::new` has been changed to expect an iterator of `(usize, SkippedField)` rather than one of just `usize` ```rust // BEFORE SerializationData::new([0, 3].into_iter()); // AFTER SerializationData::new([ (0, SkippedField::new(field_0_default_fn)), (3, SkippedField::new(field_3_default_fn)), ].into_iter()); ``` * `Serialization::is_ignored_field` has been renamed to `Serialization::is_field_skipped` * Fields marked `#[reflect(skip_serializing)]` are now included in deserialization output. This may affect logic that expected those fields to be absent.
2023-10-22 12:43:31 +00:00
.map(
|(declaration_index, field)| -> Result<StructField, syn::Error> {
bevy_reflect_derive: Clean up attribute logic (#11777) # Objective The code in `bevy_reflect_derive` could use some cleanup. ## Solution Took some of the changes in #11659 to create a dedicated PR for cleaning up the field and container attribute logic. #### Updated Naming I renamed `ReflectTraits` and `ReflectFieldAttr` to `ContainerAttributes` and `FieldAttributes`, respectively. I think these are clearer. #### Updated Parsing ##### Readability The parsing logic wasn't too bad before, but it was getting difficult to read. There was some duplicated logic between `Meta::List` and `Meta::Path` attributes. Additionally, all the logic was kept inside a large method. To simply things, I replaced the nested meta parsing with `ParseStream` parsing. In my opinion, this is easier to follow since it breaks up the large match statement into a small set of single-line if statements, where each if-block contains a single call to the appropriate attribute parsing method. ##### Flexibility On top of the added simplicity, this also makes our attribute parsing much more flexible. It allows us to more elegantly handle custom where clauses (i.e. `#[reflect(where T: Foo)]`) and it opens the door for more non-standard attribute syntax (e.g. #11659). ##### Errors This also allows us to automatically provide certain errors when parsing. For example, since we can use `stream.lookahead1()`, we get errors like the following for free: ``` error: expected one of: `ignore`, `skip_serializing`, `default` --> crates/bevy_reflect/src/lib.rs:1988:23 | 1988 | #[reflect(foo)] | ^^^ ``` --- ## Changelog > [!note] > All changes are internal to `bevy_reflect_derive` and should not affect the public API[^1]. - Renamed `ReflectTraits` to `ContainerAttributes` - Renamed `ReflectMeta::traits` to `ReflectMeta::attrs` - Renamed `ReflectFieldAttr` to `FieldAttributes` - Updated parsing logic for field/container attribute parsing - Now uses a `ParseStream` directly instead of nested meta parsing - General code cleanup of the field/container attribute modules for `bevy_reflect_derive` [^1]: Does not include errors, which may look slightly different. --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
2024-02-12 15:16:27 +00:00
let attrs = FieldAttributes::parse_attributes(&field.attrs)?;
bevy_reflect: Fix ignored/skipped field order (#7575) # Objective Fixes #5101 Alternative to #6511 ## Solution Corrected the behavior for ignored fields in `FromReflect`, which was previously using the incorrect field indexes. Similarly, fields marked with `#[reflect(skip_serializing)]` no longer break when using `FromReflect` after deserialization. This was done by modifying `SerializationData` to store a function pointer that can later be used to generate a default instance of the skipped field during deserialization. The function pointer points to a function generated by the derive macro using the behavior designated by `#[reflect(default)]` (or just `Default` if none provided). The entire output of the macro is now wrapped in an [unnamed constant](https://doc.rust-lang.org/stable/reference/items/constant-items.html#unnamed-constant) which keeps this behavior hygienic. #### Rationale The biggest downside to this approach is that it requires fields marked `#[reflect(skip_serializing)]` to provide the ability to create a default instance— either via a `Default` impl or by specifying a custom one. While this isn't great, I think it might be justified by the fact that we really need to create this value when using `FromReflect` on a deserialized object. And we need to do this _during_ deserialization because after that (at least for tuples and tuple structs) we lose information about which field is which: _"is the value at index 1 in this `DynamicTupleStruct` the actual value for index 1 or is it really the value for index 2 since index 1 is skippable...?"_ #### Alternatives An alternative would be to store `Option<Box<dyn Reflect>>` within `DynamicTuple` and `DynamicTupleStruct` instead of just `Box<dyn Reflect>`. This would allow us to insert "empty"/"missing" fields during deserialization, thus saving the positional information of the skipped fields. However, this may require changing the API of `Tuple` and `TupleStruct` such that they can account for their dynamic counterparts returning `None` for a skipped field. In practice this would probably mean exposing the `Option`-ness of the dynamics onto implementors via methods like `Tuple::drain` or `TupleStruct::field`. Personally, I think requiring `Default` would be better than muddying up the API to account for these special cases. But I'm open to trying out this other approach if the community feels that it's better. --- ## Changelog ### Public Changes #### Fixed - The behaviors of `#[reflect(ignore)]` and `#[reflect(skip_serializing)]` are no longer dependent on field order #### Changed - Fields marked with `#[reflect(skip_serializing)]` now need to either implement `Default` or specify a custom default function using `#[reflect(default = "path::to::some_func")]` - Deserializing a type with fields marked `#[reflect(skip_serializing)]` will now include that field initialized to its specified default value - `SerializationData::new` now takes the new `SkippedField` struct along with the skipped field index - Renamed `SerializationData::is_ignored_field` to `SerializationData::is_field_skipped` #### Added - Added `SkippedField` struct - Added methods `SerializationData::generate_default` and `SerializationData::iter_skipped` ### Internal Changes #### Changed - Replaced `members_to_serialization_denylist` and `BitSet<u32>` with `SerializationDataDef` - The `Reflect` derive is more hygienic as it now outputs within an [unnamed constant](https://doc.rust-lang.org/stable/reference/items/constant-items.html#unnamed-constant) - `StructField::index` has been split up into `StructField::declaration_index` and `StructField::reflection_index` #### Removed - Removed `bitset` dependency ## Migration Guide * Fields marked `#[reflect(skip_serializing)]` now must implement `Default` or specify a custom default function with `#[reflect(default = "path::to::some_func")]` ```rust #[derive(Reflect)] struct MyStruct { #[reflect(skip_serializing)] #[reflect(default = "get_foo_default")] foo: Foo, // <- `Foo` does not impl `Default` so requires a custom function #[reflect(skip_serializing)] bar: Bar, // <- `Bar` impls `Default` } #[derive(Reflect)] struct Foo(i32); #[derive(Reflect, Default)] struct Bar(i32); fn get_foo_default() -> Foo { Foo(123) } ``` * `SerializationData::new` has been changed to expect an iterator of `(usize, SkippedField)` rather than one of just `usize` ```rust // BEFORE SerializationData::new([0, 3].into_iter()); // AFTER SerializationData::new([ (0, SkippedField::new(field_0_default_fn)), (3, SkippedField::new(field_3_default_fn)), ].into_iter()); ``` * `Serialization::is_ignored_field` has been renamed to `Serialization::is_field_skipped` * Fields marked `#[reflect(skip_serializing)]` are now included in deserialization output. This may affect logic that expected those fields to be absent.
2023-10-22 12:43:31 +00:00
let reflection_index = if attrs.ignore.is_ignored() {
None
} else {
active_index += 1;
Some(active_index - 1)
};
Ok(StructField {
declaration_index,
reflection_index,
attrs,
data: field,
#[cfg(feature = "documentation")]
doc: crate::documentation::Documentation::from_attributes(&field.attrs),
})
},
)
bevy_reflect: Reflect enums (#4761) # Objective > This is a revival of #1347. Credit for the original PR should go to @Davier. Currently, enums are treated as `ReflectRef::Value` types by `bevy_reflect`. Obviously, there needs to be better a better representation for enums using the reflection API. ## Solution Based on prior work from @Davier, an `Enum` trait has been added as well as the ability to automatically implement it via the `Reflect` derive macro. This allows enums to be expressed dynamically: ```rust #[derive(Reflect)] enum Foo { A, B(usize), C { value: f32 }, } let mut foo = Foo::B(123); assert_eq!("B", foo.variant_name()); assert_eq!(1, foo.field_len()); let new_value = DynamicEnum::from(Foo::C { value: 1.23 }); foo.apply(&new_value); assert_eq!(Foo::C{value: 1.23}, foo); ``` ### Features #### Derive Macro Use the `#[derive(Reflect)]` macro to automatically implement the `Enum` trait for enum definitions. Optionally, you can use `#[reflect(ignore)]` with both variants and variant fields, just like you can with structs. These ignored items will not be considered as part of the reflection and cannot be accessed via reflection. ```rust #[derive(Reflect)] enum TestEnum { A, // Uncomment to ignore all of `B` // #[reflect(ignore)] B(usize), C { // Uncomment to ignore only field `foo` of `C` // #[reflect(ignore)] foo: f32, bar: bool, }, } ``` #### Dynamic Enums Enums may be created/represented dynamically via the `DynamicEnum` struct. The main purpose of this struct is to allow enums to be deserialized into a partial state and to allow dynamic patching. In order to ensure conversion from a `DynamicEnum` to a concrete enum type goes smoothly, be sure to add `FromReflect` to your derive macro. ```rust let mut value = TestEnum::A; // Create from a concrete instance let dyn_enum = DynamicEnum::from(TestEnum::B(123)); value.apply(&dyn_enum); assert_eq!(TestEnum::B(123), value); // Create a purely dynamic instance let dyn_enum = DynamicEnum::new("TestEnum", "A", ()); value.apply(&dyn_enum); assert_eq!(TestEnum::A, value); ``` #### Variants An enum value is always represented as one of its variants— never the enum in its entirety. ```rust let value = TestEnum::A; assert_eq!("A", value.variant_name()); // Since we are using the `A` variant, we cannot also be the `B` variant assert_ne!("B", value.variant_name()); ``` All variant types are representable within the `Enum` trait: unit, struct, and tuple. You can get the current type like: ```rust match value.variant_type() { VariantType::Unit => println!("A unit variant!"), VariantType::Struct => println!("A struct variant!"), VariantType::Tuple => println!("A tuple variant!"), } ``` > Notice that they don't contain any values representing the fields. These are purely tags. If a variant has them, you can access the fields as well: ```rust let mut value = TestEnum::C { foo: 1.23, bar: false }; // Read/write specific fields *value.field_mut("bar").unwrap() = true; // Iterate over the entire collection of fields for field in value.iter_fields() { println!("{} = {:?}", field.name(), field.value()); } ``` #### Variant Swapping It might seem odd to group all variant types under a single trait (why allow `iter_fields` on a unit variant?), but the reason this was done ~~is to easily allow *variant swapping*.~~ As I was recently drafting up the **Design Decisions** section, I discovered that other solutions could have been made to work with variant swapping. So while there are reasons to keep the all-in-one approach, variant swapping is _not_ one of them. ```rust let mut value: Box<dyn Enum> = Box::new(TestEnum::A); value.set(Box::new(TestEnum::B(123))).unwrap(); ``` #### Serialization Enums can be serialized and deserialized via reflection without needing to implement `Serialize` or `Deserialize` themselves (which can save thousands of lines of generated code). Below are the ways an enum can be serialized. > Note, like the rest of reflection-based serialization, the order of the keys in these representations is important! ##### Unit ```json { "type": "my_crate::TestEnum", "enum": { "variant": "A" } } ``` ##### Tuple ```json { "type": "my_crate::TestEnum", "enum": { "variant": "B", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` <details> <summary>Effects on Option</summary> This ends up making `Option` look a little ugly: ```json { "type": "core::option::Option<usize>", "enum": { "variant": "Some", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` </details> ##### Struct ```json { "type": "my_crate::TestEnum", "enum": { "variant": "C", "struct": { "foo": { "type": "f32", "value": 1.23 }, "bar": { "type": "bool", "value": false } } } } ``` ## Design Decisions <details> <summary><strong>View Section</strong></summary> This section is here to provide some context for why certain decisions were made for this PR, alternatives that could have been used instead, and what could be improved upon in the future. ### Variant Representation One of the biggest decisions was to decide on how to represent variants. The current design uses a "all-in-one" design where unit, tuple, and struct variants are all simultaneously represented by the `Enum` trait. This is not the only way it could have been done, though. #### Alternatives ##### 1. Variant Traits One way of representing variants would be to define traits for each variant, implementing them whenever an enum featured at least one instance of them. This would allow us to define variants like: ```rust pub trait Enum: Reflect { fn variant(&self) -> Variant; } pub enum Variant<'a> { Unit, Tuple(&'a dyn TupleVariant), Struct(&'a dyn StructVariant), } pub trait TupleVariant { fn field_len(&self) -> usize; // ... } ``` And then do things like: ```rust fn get_tuple_len(foo: &dyn Enum) -> usize { match foo.variant() { Variant::Tuple(tuple) => tuple.field_len(), _ => panic!("not a tuple variant!") } } ``` The reason this PR does not go with this approach is because of the fact that variants are not separate types. In other words, we cannot implement traits on specific variants— these cover the *entire* enum. This means we offer an easy footgun: ```rust let foo: Option<i32> = None; let my_enum = Box::new(foo) as Box<dyn TupleVariant>; ``` Here, `my_enum` contains `foo`, which is a unit variant. However, since we need to implement `TupleVariant` for `Option` as a whole, it's possible to perform such a cast. This is obviously wrong, but could easily go unnoticed. So unfortunately, this makes it not a good candidate for representing variants. ##### 2. Variant Structs To get around the issue of traits necessarily needing to apply to both the enum and its variants, we could instead use structs that are created on a per-variant basis. This was also considered but was ultimately [[removed](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c)](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c) due to concerns about allocations. Each variant struct would probably look something like: ```rust pub trait Enum: Reflect { fn variant_mut(&self) -> VariantMut; } pub enum VariantMut<'a> { Unit, Tuple(TupleVariantMut), Struct(StructVariantMut), } struct StructVariantMut<'a> { fields: Vec<&'a mut dyn Reflect>, field_indices: HashMap<Cow<'static, str>, usize> } ``` This allows us to isolate struct variants into their own defined struct and define methods specifically for their use. It also prevents users from casting to it since it's not a trait. However, this is not an optimal solution. Both `field_indices` and `fields` will require an allocation (remember, a `Box<[T]>` still requires a `Vec<T>` in order to be constructed). This *might* be a problem if called frequently enough. ##### 3. Generated Structs The original design, implemented by @Davier, instead generates structs specific for each variant. So if we had a variant path like `Foo::Bar`, we'd generate a struct named `FooBarWrapper`. This would be newtyped around the original enum and forward tuple or struct methods to the enum with the chosen variant. Because it involved using the `Tuple` and `Struct` traits (which are also both bound on `Reflect`), this meant a bit more code had to be generated. For a single struct variant with one field, the generated code amounted to ~110LoC. However, each new field added to that variant only added ~6 more LoC. In order to work properly, the enum had to be transmuted to the generated struct: ```rust fn variant(&self) -> crate::EnumVariant<'_> { match self { Foo::Bar {value: i32} => { let wrapper_ref = unsafe { std::mem::transmute::<&Self, &FooBarWrapper>(self) }; crate::EnumVariant::Struct(wrapper_ref as &dyn crate::Struct) } } } ``` This works because `FooBarWrapper` is defined as `repr(transparent)`. Out of all the alternatives, this would probably be the one most likely to be used again in the future. The reasons for why this PR did not continue to use it was because: * To reduce generated code (which would hopefully speed up compile times) * To avoid cluttering the code with generated structs not visible to the user * To keep bevy_reflect simple and extensible (these generated structs act as proxies and might not play well with current or future systems) * To avoid additional unsafe blocks * My own misunderstanding of @Davier's code That last point is obviously on me. I misjudged the code to be too unsafe and unable to handle variant swapping (which it probably could) when I was rebasing it. Looking over it again when writing up this whole section, I see that it was actually a pretty clever way of handling variant representation. #### Benefits of All-in-One As stated before, the current implementation uses an all-in-one approach. All variants are capable of containing fields as far as `Enum` is concerned. This provides a few benefits that the alternatives do not (reduced indirection, safer code, etc.). The biggest benefit, though, is direct field access. Rather than forcing users to have to go through pattern matching, we grant direct access to the fields contained by the current variant. The reason we can do this is because all of the pattern matching happens internally. Getting the field at index `2` will automatically return `Some(...)` for the current variant if it has a field at that index or `None` if it doesn't (or can't). This could be useful for scenarios where the variant has already been verified or just set/swapped (or even where the type of variant doesn't matter): ```rust let dyn_enum: &mut dyn Enum = &mut Foo::Bar {value: 123}; // We know it's the `Bar` variant let field = dyn_enum.field("value").unwrap(); ``` Reflection is not a type-safe abstraction— almost every return value is wrapped in `Option<...>`. There are plenty of places to check and recheck that a value is what Reflect says it is. Forcing users to have to go through `match` each time they want to access a field might just be an extra step among dozens of other verification processes. Some might disagree, but ultimately, my view is that the benefit here is an improvement to the ergonomics and usability of reflected enums. </details> --- ## Changelog ### Added * Added `Enum` trait * Added `Enum` impl to `Reflect` derive macro * Added `DynamicEnum` struct * Added `DynamicVariant` * Added `EnumInfo` * Added `VariantInfo` * Added `StructVariantInfo` * Added `TupleVariantInfo` * Added `UnitVariantInfo` * Added serializtion/deserialization support for enums * Added `EnumSerializer` * Added `VariantType` * Added `VariantFieldIter` * Added `VariantField` * Added `enum_partial_eq(...)` * Added `enum_hash(...)` ### Changed * `Option<T>` now implements `Enum` * `bevy_window` now depends on `bevy_reflect` * Implemented `Reflect` and `FromReflect` for `WindowId` * Derive `FromReflect` on `PerspectiveProjection` * Derive `FromReflect` on `OrthographicProjection` * Derive `FromReflect` on `WindowOrigin` * Derive `FromReflect` on `ScalingMode` * Derive `FromReflect` on `DepthCalculation` ## Migration Guide * Enums no longer need to be treated as values and usages of `#[reflect_value(...)]` can be removed or replaced by `#[reflect(...)]` * Enums (including `Option<T>`) now take a different format when serializing. The format is described above, but this may cause issues for existing scenes that make use of enums. --- Also shout out to @nicopap for helping clean up some of the code here! It's a big feature so help like this is really appreciated! Co-authored-by: Gino Valente <gino.valente.code@gmail.com>
2022-08-02 22:14:41 +00:00
.fold(
utility::ResultSifter::default(),
utility::ResultSifter::fold,
);
bevy_reflect_derive: Tidying up the code (#4712) # Objective The `bevy_reflect_derive` crate is not the cleanest or easiest to follow/maintain. The `lib.rs` file is especially difficult with over 1000 lines of code written in a confusing order. This is just a result of growth within the crate and it would be nice to clean it up for future work. ## Solution Split `bevy_reflect_derive` into many more submodules. The submodules include: * `container_attributes` - Code relating to container attributes * `derive_data` - Code relating to reflection-based derive metadata * `field_attributes` - Code relating to field attributes * `impls` - Code containing actual reflection implementations * `reflect_value` - Code relating to reflection-based value metadata * `registration` - Code relating to type registration * `utility` - General-purpose utility functions This leaves the `lib.rs` file to contain only the public macros, making it much easier to digest (and fewer than 200 lines). By breaking up the code into smaller modules, we make it easier for future contributors to find the code they're looking for or identify which module best fits their own additions. ### Metadata Structs This cleanup also adds two big metadata structs: `ReflectFieldAttr` and `ReflectDeriveData`. The former is used to store all attributes for a struct field (if any). The latter is used to store all metadata for struct-based derive inputs. Both significantly reduce code duplication and make editing these macros much simpler. The tradeoff is that we may collect more metadata than needed. However, this is usually a small thing (such as checking for attributes when they're not really needed or creating a `ReflectFieldAttr` for every field regardless of whether they actually have an attribute). We could try to remove these tradeoffs and squeeze some more performance out, but doing so might come at the cost of developer experience. Personally, I think it's much nicer to create a `ReflectFieldAttr` for every field since it means I don't have to do two `Option` checks. Others may disagree, though, and so we can discuss changing this either in this PR or in a future one. ### Out of Scope _Some_ documentation has been added or improved, but ultimately good docs are probably best saved for a dedicated PR. ## 🔍 Focus Points (for reviewers) I know it's a lot to sift through, so here is a list of **key points for reviewers**: - The following files contain code that was mostly just relocated: - `reflect_value.rs` - `registration.rs` - `container_attributes.rs` was also mostly moved but features some general cleanup (reducing nesting, removing hardcoded strings, etc.) and lots of doc comments - Most impl logic was moved from `lib.rs` to `impls.rs`, but they have been significantly modified to use the new `ReflectDeriveData` metadata struct in order to reduce duplication. - `derive_data.rs` and `field_attributes.rs` contain almost entirely new code and should probably be given the most attention. - Likewise, `from_reflect.rs` saw major changes using `ReflectDeriveData` so it should also be given focus. - There was no change to the `lib.rs` exports so the end-user API should be the same. ## Prior Work This task was initially tackled by @NathanSWard in #2377 (which was closed in favor of this PR), so hats off to them for beating me to the punch by nearly a year! --- ## Changelog * **[INTERNAL]** Split `bevy_reflect_derive` into smaller submodules * **[INTERNAL]** Add `ReflectFieldAttr` * **[INTERNAL]** Add `ReflectDeriveData` * Add `BevyManifest::get_path_direct()` method (`bevy_macro_utils`) Co-authored-by: MrGVSV <49806985+MrGVSV@users.noreply.github.com>
2022-05-12 19:43:23 +00:00
bevy_reflect: Reflect enums (#4761) # Objective > This is a revival of #1347. Credit for the original PR should go to @Davier. Currently, enums are treated as `ReflectRef::Value` types by `bevy_reflect`. Obviously, there needs to be better a better representation for enums using the reflection API. ## Solution Based on prior work from @Davier, an `Enum` trait has been added as well as the ability to automatically implement it via the `Reflect` derive macro. This allows enums to be expressed dynamically: ```rust #[derive(Reflect)] enum Foo { A, B(usize), C { value: f32 }, } let mut foo = Foo::B(123); assert_eq!("B", foo.variant_name()); assert_eq!(1, foo.field_len()); let new_value = DynamicEnum::from(Foo::C { value: 1.23 }); foo.apply(&new_value); assert_eq!(Foo::C{value: 1.23}, foo); ``` ### Features #### Derive Macro Use the `#[derive(Reflect)]` macro to automatically implement the `Enum` trait for enum definitions. Optionally, you can use `#[reflect(ignore)]` with both variants and variant fields, just like you can with structs. These ignored items will not be considered as part of the reflection and cannot be accessed via reflection. ```rust #[derive(Reflect)] enum TestEnum { A, // Uncomment to ignore all of `B` // #[reflect(ignore)] B(usize), C { // Uncomment to ignore only field `foo` of `C` // #[reflect(ignore)] foo: f32, bar: bool, }, } ``` #### Dynamic Enums Enums may be created/represented dynamically via the `DynamicEnum` struct. The main purpose of this struct is to allow enums to be deserialized into a partial state and to allow dynamic patching. In order to ensure conversion from a `DynamicEnum` to a concrete enum type goes smoothly, be sure to add `FromReflect` to your derive macro. ```rust let mut value = TestEnum::A; // Create from a concrete instance let dyn_enum = DynamicEnum::from(TestEnum::B(123)); value.apply(&dyn_enum); assert_eq!(TestEnum::B(123), value); // Create a purely dynamic instance let dyn_enum = DynamicEnum::new("TestEnum", "A", ()); value.apply(&dyn_enum); assert_eq!(TestEnum::A, value); ``` #### Variants An enum value is always represented as one of its variants— never the enum in its entirety. ```rust let value = TestEnum::A; assert_eq!("A", value.variant_name()); // Since we are using the `A` variant, we cannot also be the `B` variant assert_ne!("B", value.variant_name()); ``` All variant types are representable within the `Enum` trait: unit, struct, and tuple. You can get the current type like: ```rust match value.variant_type() { VariantType::Unit => println!("A unit variant!"), VariantType::Struct => println!("A struct variant!"), VariantType::Tuple => println!("A tuple variant!"), } ``` > Notice that they don't contain any values representing the fields. These are purely tags. If a variant has them, you can access the fields as well: ```rust let mut value = TestEnum::C { foo: 1.23, bar: false }; // Read/write specific fields *value.field_mut("bar").unwrap() = true; // Iterate over the entire collection of fields for field in value.iter_fields() { println!("{} = {:?}", field.name(), field.value()); } ``` #### Variant Swapping It might seem odd to group all variant types under a single trait (why allow `iter_fields` on a unit variant?), but the reason this was done ~~is to easily allow *variant swapping*.~~ As I was recently drafting up the **Design Decisions** section, I discovered that other solutions could have been made to work with variant swapping. So while there are reasons to keep the all-in-one approach, variant swapping is _not_ one of them. ```rust let mut value: Box<dyn Enum> = Box::new(TestEnum::A); value.set(Box::new(TestEnum::B(123))).unwrap(); ``` #### Serialization Enums can be serialized and deserialized via reflection without needing to implement `Serialize` or `Deserialize` themselves (which can save thousands of lines of generated code). Below are the ways an enum can be serialized. > Note, like the rest of reflection-based serialization, the order of the keys in these representations is important! ##### Unit ```json { "type": "my_crate::TestEnum", "enum": { "variant": "A" } } ``` ##### Tuple ```json { "type": "my_crate::TestEnum", "enum": { "variant": "B", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` <details> <summary>Effects on Option</summary> This ends up making `Option` look a little ugly: ```json { "type": "core::option::Option<usize>", "enum": { "variant": "Some", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` </details> ##### Struct ```json { "type": "my_crate::TestEnum", "enum": { "variant": "C", "struct": { "foo": { "type": "f32", "value": 1.23 }, "bar": { "type": "bool", "value": false } } } } ``` ## Design Decisions <details> <summary><strong>View Section</strong></summary> This section is here to provide some context for why certain decisions were made for this PR, alternatives that could have been used instead, and what could be improved upon in the future. ### Variant Representation One of the biggest decisions was to decide on how to represent variants. The current design uses a "all-in-one" design where unit, tuple, and struct variants are all simultaneously represented by the `Enum` trait. This is not the only way it could have been done, though. #### Alternatives ##### 1. Variant Traits One way of representing variants would be to define traits for each variant, implementing them whenever an enum featured at least one instance of them. This would allow us to define variants like: ```rust pub trait Enum: Reflect { fn variant(&self) -> Variant; } pub enum Variant<'a> { Unit, Tuple(&'a dyn TupleVariant), Struct(&'a dyn StructVariant), } pub trait TupleVariant { fn field_len(&self) -> usize; // ... } ``` And then do things like: ```rust fn get_tuple_len(foo: &dyn Enum) -> usize { match foo.variant() { Variant::Tuple(tuple) => tuple.field_len(), _ => panic!("not a tuple variant!") } } ``` The reason this PR does not go with this approach is because of the fact that variants are not separate types. In other words, we cannot implement traits on specific variants— these cover the *entire* enum. This means we offer an easy footgun: ```rust let foo: Option<i32> = None; let my_enum = Box::new(foo) as Box<dyn TupleVariant>; ``` Here, `my_enum` contains `foo`, which is a unit variant. However, since we need to implement `TupleVariant` for `Option` as a whole, it's possible to perform such a cast. This is obviously wrong, but could easily go unnoticed. So unfortunately, this makes it not a good candidate for representing variants. ##### 2. Variant Structs To get around the issue of traits necessarily needing to apply to both the enum and its variants, we could instead use structs that are created on a per-variant basis. This was also considered but was ultimately [[removed](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c)](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c) due to concerns about allocations. Each variant struct would probably look something like: ```rust pub trait Enum: Reflect { fn variant_mut(&self) -> VariantMut; } pub enum VariantMut<'a> { Unit, Tuple(TupleVariantMut), Struct(StructVariantMut), } struct StructVariantMut<'a> { fields: Vec<&'a mut dyn Reflect>, field_indices: HashMap<Cow<'static, str>, usize> } ``` This allows us to isolate struct variants into their own defined struct and define methods specifically for their use. It also prevents users from casting to it since it's not a trait. However, this is not an optimal solution. Both `field_indices` and `fields` will require an allocation (remember, a `Box<[T]>` still requires a `Vec<T>` in order to be constructed). This *might* be a problem if called frequently enough. ##### 3. Generated Structs The original design, implemented by @Davier, instead generates structs specific for each variant. So if we had a variant path like `Foo::Bar`, we'd generate a struct named `FooBarWrapper`. This would be newtyped around the original enum and forward tuple or struct methods to the enum with the chosen variant. Because it involved using the `Tuple` and `Struct` traits (which are also both bound on `Reflect`), this meant a bit more code had to be generated. For a single struct variant with one field, the generated code amounted to ~110LoC. However, each new field added to that variant only added ~6 more LoC. In order to work properly, the enum had to be transmuted to the generated struct: ```rust fn variant(&self) -> crate::EnumVariant<'_> { match self { Foo::Bar {value: i32} => { let wrapper_ref = unsafe { std::mem::transmute::<&Self, &FooBarWrapper>(self) }; crate::EnumVariant::Struct(wrapper_ref as &dyn crate::Struct) } } } ``` This works because `FooBarWrapper` is defined as `repr(transparent)`. Out of all the alternatives, this would probably be the one most likely to be used again in the future. The reasons for why this PR did not continue to use it was because: * To reduce generated code (which would hopefully speed up compile times) * To avoid cluttering the code with generated structs not visible to the user * To keep bevy_reflect simple and extensible (these generated structs act as proxies and might not play well with current or future systems) * To avoid additional unsafe blocks * My own misunderstanding of @Davier's code That last point is obviously on me. I misjudged the code to be too unsafe and unable to handle variant swapping (which it probably could) when I was rebasing it. Looking over it again when writing up this whole section, I see that it was actually a pretty clever way of handling variant representation. #### Benefits of All-in-One As stated before, the current implementation uses an all-in-one approach. All variants are capable of containing fields as far as `Enum` is concerned. This provides a few benefits that the alternatives do not (reduced indirection, safer code, etc.). The biggest benefit, though, is direct field access. Rather than forcing users to have to go through pattern matching, we grant direct access to the fields contained by the current variant. The reason we can do this is because all of the pattern matching happens internally. Getting the field at index `2` will automatically return `Some(...)` for the current variant if it has a field at that index or `None` if it doesn't (or can't). This could be useful for scenarios where the variant has already been verified or just set/swapped (or even where the type of variant doesn't matter): ```rust let dyn_enum: &mut dyn Enum = &mut Foo::Bar {value: 123}; // We know it's the `Bar` variant let field = dyn_enum.field("value").unwrap(); ``` Reflection is not a type-safe abstraction— almost every return value is wrapped in `Option<...>`. There are plenty of places to check and recheck that a value is what Reflect says it is. Forcing users to have to go through `match` each time they want to access a field might just be an extra step among dozens of other verification processes. Some might disagree, but ultimately, my view is that the benefit here is an improvement to the ergonomics and usability of reflected enums. </details> --- ## Changelog ### Added * Added `Enum` trait * Added `Enum` impl to `Reflect` derive macro * Added `DynamicEnum` struct * Added `DynamicVariant` * Added `EnumInfo` * Added `VariantInfo` * Added `StructVariantInfo` * Added `TupleVariantInfo` * Added `UnitVariantInfo` * Added serializtion/deserialization support for enums * Added `EnumSerializer` * Added `VariantType` * Added `VariantFieldIter` * Added `VariantField` * Added `enum_partial_eq(...)` * Added `enum_hash(...)` ### Changed * `Option<T>` now implements `Enum` * `bevy_window` now depends on `bevy_reflect` * Implemented `Reflect` and `FromReflect` for `WindowId` * Derive `FromReflect` on `PerspectiveProjection` * Derive `FromReflect` on `OrthographicProjection` * Derive `FromReflect` on `WindowOrigin` * Derive `FromReflect` on `ScalingMode` * Derive `FromReflect` on `DepthCalculation` ## Migration Guide * Enums no longer need to be treated as values and usages of `#[reflect_value(...)]` can be removed or replaced by `#[reflect(...)]` * Enums (including `Option<T>`) now take a different format when serializing. The format is described above, but this may cause issues for existing scenes that make use of enums. --- Also shout out to @nicopap for helping clean up some of the code here! It's a big feature so help like this is really appreciated! Co-authored-by: Gino Valente <gino.valente.code@gmail.com>
2022-08-02 22:14:41 +00:00
sifter.finish()
bevy_reflect_derive: Tidying up the code (#4712) # Objective The `bevy_reflect_derive` crate is not the cleanest or easiest to follow/maintain. The `lib.rs` file is especially difficult with over 1000 lines of code written in a confusing order. This is just a result of growth within the crate and it would be nice to clean it up for future work. ## Solution Split `bevy_reflect_derive` into many more submodules. The submodules include: * `container_attributes` - Code relating to container attributes * `derive_data` - Code relating to reflection-based derive metadata * `field_attributes` - Code relating to field attributes * `impls` - Code containing actual reflection implementations * `reflect_value` - Code relating to reflection-based value metadata * `registration` - Code relating to type registration * `utility` - General-purpose utility functions This leaves the `lib.rs` file to contain only the public macros, making it much easier to digest (and fewer than 200 lines). By breaking up the code into smaller modules, we make it easier for future contributors to find the code they're looking for or identify which module best fits their own additions. ### Metadata Structs This cleanup also adds two big metadata structs: `ReflectFieldAttr` and `ReflectDeriveData`. The former is used to store all attributes for a struct field (if any). The latter is used to store all metadata for struct-based derive inputs. Both significantly reduce code duplication and make editing these macros much simpler. The tradeoff is that we may collect more metadata than needed. However, this is usually a small thing (such as checking for attributes when they're not really needed or creating a `ReflectFieldAttr` for every field regardless of whether they actually have an attribute). We could try to remove these tradeoffs and squeeze some more performance out, but doing so might come at the cost of developer experience. Personally, I think it's much nicer to create a `ReflectFieldAttr` for every field since it means I don't have to do two `Option` checks. Others may disagree, though, and so we can discuss changing this either in this PR or in a future one. ### Out of Scope _Some_ documentation has been added or improved, but ultimately good docs are probably best saved for a dedicated PR. ## 🔍 Focus Points (for reviewers) I know it's a lot to sift through, so here is a list of **key points for reviewers**: - The following files contain code that was mostly just relocated: - `reflect_value.rs` - `registration.rs` - `container_attributes.rs` was also mostly moved but features some general cleanup (reducing nesting, removing hardcoded strings, etc.) and lots of doc comments - Most impl logic was moved from `lib.rs` to `impls.rs`, but they have been significantly modified to use the new `ReflectDeriveData` metadata struct in order to reduce duplication. - `derive_data.rs` and `field_attributes.rs` contain almost entirely new code and should probably be given the most attention. - Likewise, `from_reflect.rs` saw major changes using `ReflectDeriveData` so it should also be given focus. - There was no change to the `lib.rs` exports so the end-user API should be the same. ## Prior Work This task was initially tackled by @NathanSWard in #2377 (which was closed in favor of this PR), so hats off to them for beating me to the punch by nearly a year! --- ## Changelog * **[INTERNAL]** Split `bevy_reflect_derive` into smaller submodules * **[INTERNAL]** Add `ReflectFieldAttr` * **[INTERNAL]** Add `ReflectDeriveData` * Add `BevyManifest::get_path_direct()` method (`bevy_macro_utils`) Co-authored-by: MrGVSV <49806985+MrGVSV@users.noreply.github.com>
2022-05-12 19:43:23 +00:00
}
bevy_reflect: Reflect enums (#4761) # Objective > This is a revival of #1347. Credit for the original PR should go to @Davier. Currently, enums are treated as `ReflectRef::Value` types by `bevy_reflect`. Obviously, there needs to be better a better representation for enums using the reflection API. ## Solution Based on prior work from @Davier, an `Enum` trait has been added as well as the ability to automatically implement it via the `Reflect` derive macro. This allows enums to be expressed dynamically: ```rust #[derive(Reflect)] enum Foo { A, B(usize), C { value: f32 }, } let mut foo = Foo::B(123); assert_eq!("B", foo.variant_name()); assert_eq!(1, foo.field_len()); let new_value = DynamicEnum::from(Foo::C { value: 1.23 }); foo.apply(&new_value); assert_eq!(Foo::C{value: 1.23}, foo); ``` ### Features #### Derive Macro Use the `#[derive(Reflect)]` macro to automatically implement the `Enum` trait for enum definitions. Optionally, you can use `#[reflect(ignore)]` with both variants and variant fields, just like you can with structs. These ignored items will not be considered as part of the reflection and cannot be accessed via reflection. ```rust #[derive(Reflect)] enum TestEnum { A, // Uncomment to ignore all of `B` // #[reflect(ignore)] B(usize), C { // Uncomment to ignore only field `foo` of `C` // #[reflect(ignore)] foo: f32, bar: bool, }, } ``` #### Dynamic Enums Enums may be created/represented dynamically via the `DynamicEnum` struct. The main purpose of this struct is to allow enums to be deserialized into a partial state and to allow dynamic patching. In order to ensure conversion from a `DynamicEnum` to a concrete enum type goes smoothly, be sure to add `FromReflect` to your derive macro. ```rust let mut value = TestEnum::A; // Create from a concrete instance let dyn_enum = DynamicEnum::from(TestEnum::B(123)); value.apply(&dyn_enum); assert_eq!(TestEnum::B(123), value); // Create a purely dynamic instance let dyn_enum = DynamicEnum::new("TestEnum", "A", ()); value.apply(&dyn_enum); assert_eq!(TestEnum::A, value); ``` #### Variants An enum value is always represented as one of its variants— never the enum in its entirety. ```rust let value = TestEnum::A; assert_eq!("A", value.variant_name()); // Since we are using the `A` variant, we cannot also be the `B` variant assert_ne!("B", value.variant_name()); ``` All variant types are representable within the `Enum` trait: unit, struct, and tuple. You can get the current type like: ```rust match value.variant_type() { VariantType::Unit => println!("A unit variant!"), VariantType::Struct => println!("A struct variant!"), VariantType::Tuple => println!("A tuple variant!"), } ``` > Notice that they don't contain any values representing the fields. These are purely tags. If a variant has them, you can access the fields as well: ```rust let mut value = TestEnum::C { foo: 1.23, bar: false }; // Read/write specific fields *value.field_mut("bar").unwrap() = true; // Iterate over the entire collection of fields for field in value.iter_fields() { println!("{} = {:?}", field.name(), field.value()); } ``` #### Variant Swapping It might seem odd to group all variant types under a single trait (why allow `iter_fields` on a unit variant?), but the reason this was done ~~is to easily allow *variant swapping*.~~ As I was recently drafting up the **Design Decisions** section, I discovered that other solutions could have been made to work with variant swapping. So while there are reasons to keep the all-in-one approach, variant swapping is _not_ one of them. ```rust let mut value: Box<dyn Enum> = Box::new(TestEnum::A); value.set(Box::new(TestEnum::B(123))).unwrap(); ``` #### Serialization Enums can be serialized and deserialized via reflection without needing to implement `Serialize` or `Deserialize` themselves (which can save thousands of lines of generated code). Below are the ways an enum can be serialized. > Note, like the rest of reflection-based serialization, the order of the keys in these representations is important! ##### Unit ```json { "type": "my_crate::TestEnum", "enum": { "variant": "A" } } ``` ##### Tuple ```json { "type": "my_crate::TestEnum", "enum": { "variant": "B", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` <details> <summary>Effects on Option</summary> This ends up making `Option` look a little ugly: ```json { "type": "core::option::Option<usize>", "enum": { "variant": "Some", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` </details> ##### Struct ```json { "type": "my_crate::TestEnum", "enum": { "variant": "C", "struct": { "foo": { "type": "f32", "value": 1.23 }, "bar": { "type": "bool", "value": false } } } } ``` ## Design Decisions <details> <summary><strong>View Section</strong></summary> This section is here to provide some context for why certain decisions were made for this PR, alternatives that could have been used instead, and what could be improved upon in the future. ### Variant Representation One of the biggest decisions was to decide on how to represent variants. The current design uses a "all-in-one" design where unit, tuple, and struct variants are all simultaneously represented by the `Enum` trait. This is not the only way it could have been done, though. #### Alternatives ##### 1. Variant Traits One way of representing variants would be to define traits for each variant, implementing them whenever an enum featured at least one instance of them. This would allow us to define variants like: ```rust pub trait Enum: Reflect { fn variant(&self) -> Variant; } pub enum Variant<'a> { Unit, Tuple(&'a dyn TupleVariant), Struct(&'a dyn StructVariant), } pub trait TupleVariant { fn field_len(&self) -> usize; // ... } ``` And then do things like: ```rust fn get_tuple_len(foo: &dyn Enum) -> usize { match foo.variant() { Variant::Tuple(tuple) => tuple.field_len(), _ => panic!("not a tuple variant!") } } ``` The reason this PR does not go with this approach is because of the fact that variants are not separate types. In other words, we cannot implement traits on specific variants— these cover the *entire* enum. This means we offer an easy footgun: ```rust let foo: Option<i32> = None; let my_enum = Box::new(foo) as Box<dyn TupleVariant>; ``` Here, `my_enum` contains `foo`, which is a unit variant. However, since we need to implement `TupleVariant` for `Option` as a whole, it's possible to perform such a cast. This is obviously wrong, but could easily go unnoticed. So unfortunately, this makes it not a good candidate for representing variants. ##### 2. Variant Structs To get around the issue of traits necessarily needing to apply to both the enum and its variants, we could instead use structs that are created on a per-variant basis. This was also considered but was ultimately [[removed](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c)](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c) due to concerns about allocations. Each variant struct would probably look something like: ```rust pub trait Enum: Reflect { fn variant_mut(&self) -> VariantMut; } pub enum VariantMut<'a> { Unit, Tuple(TupleVariantMut), Struct(StructVariantMut), } struct StructVariantMut<'a> { fields: Vec<&'a mut dyn Reflect>, field_indices: HashMap<Cow<'static, str>, usize> } ``` This allows us to isolate struct variants into their own defined struct and define methods specifically for their use. It also prevents users from casting to it since it's not a trait. However, this is not an optimal solution. Both `field_indices` and `fields` will require an allocation (remember, a `Box<[T]>` still requires a `Vec<T>` in order to be constructed). This *might* be a problem if called frequently enough. ##### 3. Generated Structs The original design, implemented by @Davier, instead generates structs specific for each variant. So if we had a variant path like `Foo::Bar`, we'd generate a struct named `FooBarWrapper`. This would be newtyped around the original enum and forward tuple or struct methods to the enum with the chosen variant. Because it involved using the `Tuple` and `Struct` traits (which are also both bound on `Reflect`), this meant a bit more code had to be generated. For a single struct variant with one field, the generated code amounted to ~110LoC. However, each new field added to that variant only added ~6 more LoC. In order to work properly, the enum had to be transmuted to the generated struct: ```rust fn variant(&self) -> crate::EnumVariant<'_> { match self { Foo::Bar {value: i32} => { let wrapper_ref = unsafe { std::mem::transmute::<&Self, &FooBarWrapper>(self) }; crate::EnumVariant::Struct(wrapper_ref as &dyn crate::Struct) } } } ``` This works because `FooBarWrapper` is defined as `repr(transparent)`. Out of all the alternatives, this would probably be the one most likely to be used again in the future. The reasons for why this PR did not continue to use it was because: * To reduce generated code (which would hopefully speed up compile times) * To avoid cluttering the code with generated structs not visible to the user * To keep bevy_reflect simple and extensible (these generated structs act as proxies and might not play well with current or future systems) * To avoid additional unsafe blocks * My own misunderstanding of @Davier's code That last point is obviously on me. I misjudged the code to be too unsafe and unable to handle variant swapping (which it probably could) when I was rebasing it. Looking over it again when writing up this whole section, I see that it was actually a pretty clever way of handling variant representation. #### Benefits of All-in-One As stated before, the current implementation uses an all-in-one approach. All variants are capable of containing fields as far as `Enum` is concerned. This provides a few benefits that the alternatives do not (reduced indirection, safer code, etc.). The biggest benefit, though, is direct field access. Rather than forcing users to have to go through pattern matching, we grant direct access to the fields contained by the current variant. The reason we can do this is because all of the pattern matching happens internally. Getting the field at index `2` will automatically return `Some(...)` for the current variant if it has a field at that index or `None` if it doesn't (or can't). This could be useful for scenarios where the variant has already been verified or just set/swapped (or even where the type of variant doesn't matter): ```rust let dyn_enum: &mut dyn Enum = &mut Foo::Bar {value: 123}; // We know it's the `Bar` variant let field = dyn_enum.field("value").unwrap(); ``` Reflection is not a type-safe abstraction— almost every return value is wrapped in `Option<...>`. There are plenty of places to check and recheck that a value is what Reflect says it is. Forcing users to have to go through `match` each time they want to access a field might just be an extra step among dozens of other verification processes. Some might disagree, but ultimately, my view is that the benefit here is an improvement to the ergonomics and usability of reflected enums. </details> --- ## Changelog ### Added * Added `Enum` trait * Added `Enum` impl to `Reflect` derive macro * Added `DynamicEnum` struct * Added `DynamicVariant` * Added `EnumInfo` * Added `VariantInfo` * Added `StructVariantInfo` * Added `TupleVariantInfo` * Added `UnitVariantInfo` * Added serializtion/deserialization support for enums * Added `EnumSerializer` * Added `VariantType` * Added `VariantFieldIter` * Added `VariantField` * Added `enum_partial_eq(...)` * Added `enum_hash(...)` ### Changed * `Option<T>` now implements `Enum` * `bevy_window` now depends on `bevy_reflect` * Implemented `Reflect` and `FromReflect` for `WindowId` * Derive `FromReflect` on `PerspectiveProjection` * Derive `FromReflect` on `OrthographicProjection` * Derive `FromReflect` on `WindowOrigin` * Derive `FromReflect` on `ScalingMode` * Derive `FromReflect` on `DepthCalculation` ## Migration Guide * Enums no longer need to be treated as values and usages of `#[reflect_value(...)]` can be removed or replaced by `#[reflect(...)]` * Enums (including `Option<T>`) now take a different format when serializing. The format is described above, but this may cause issues for existing scenes that make use of enums. --- Also shout out to @nicopap for helping clean up some of the code here! It's a big feature so help like this is really appreciated! Co-authored-by: Gino Valente <gino.valente.code@gmail.com>
2022-08-02 22:14:41 +00:00
fn collect_enum_variants(
variants: &'a Punctuated<Variant, Comma>,
bevy_reflect: Reflect enums (#4761) # Objective > This is a revival of #1347. Credit for the original PR should go to @Davier. Currently, enums are treated as `ReflectRef::Value` types by `bevy_reflect`. Obviously, there needs to be better a better representation for enums using the reflection API. ## Solution Based on prior work from @Davier, an `Enum` trait has been added as well as the ability to automatically implement it via the `Reflect` derive macro. This allows enums to be expressed dynamically: ```rust #[derive(Reflect)] enum Foo { A, B(usize), C { value: f32 }, } let mut foo = Foo::B(123); assert_eq!("B", foo.variant_name()); assert_eq!(1, foo.field_len()); let new_value = DynamicEnum::from(Foo::C { value: 1.23 }); foo.apply(&new_value); assert_eq!(Foo::C{value: 1.23}, foo); ``` ### Features #### Derive Macro Use the `#[derive(Reflect)]` macro to automatically implement the `Enum` trait for enum definitions. Optionally, you can use `#[reflect(ignore)]` with both variants and variant fields, just like you can with structs. These ignored items will not be considered as part of the reflection and cannot be accessed via reflection. ```rust #[derive(Reflect)] enum TestEnum { A, // Uncomment to ignore all of `B` // #[reflect(ignore)] B(usize), C { // Uncomment to ignore only field `foo` of `C` // #[reflect(ignore)] foo: f32, bar: bool, }, } ``` #### Dynamic Enums Enums may be created/represented dynamically via the `DynamicEnum` struct. The main purpose of this struct is to allow enums to be deserialized into a partial state and to allow dynamic patching. In order to ensure conversion from a `DynamicEnum` to a concrete enum type goes smoothly, be sure to add `FromReflect` to your derive macro. ```rust let mut value = TestEnum::A; // Create from a concrete instance let dyn_enum = DynamicEnum::from(TestEnum::B(123)); value.apply(&dyn_enum); assert_eq!(TestEnum::B(123), value); // Create a purely dynamic instance let dyn_enum = DynamicEnum::new("TestEnum", "A", ()); value.apply(&dyn_enum); assert_eq!(TestEnum::A, value); ``` #### Variants An enum value is always represented as one of its variants— never the enum in its entirety. ```rust let value = TestEnum::A; assert_eq!("A", value.variant_name()); // Since we are using the `A` variant, we cannot also be the `B` variant assert_ne!("B", value.variant_name()); ``` All variant types are representable within the `Enum` trait: unit, struct, and tuple. You can get the current type like: ```rust match value.variant_type() { VariantType::Unit => println!("A unit variant!"), VariantType::Struct => println!("A struct variant!"), VariantType::Tuple => println!("A tuple variant!"), } ``` > Notice that they don't contain any values representing the fields. These are purely tags. If a variant has them, you can access the fields as well: ```rust let mut value = TestEnum::C { foo: 1.23, bar: false }; // Read/write specific fields *value.field_mut("bar").unwrap() = true; // Iterate over the entire collection of fields for field in value.iter_fields() { println!("{} = {:?}", field.name(), field.value()); } ``` #### Variant Swapping It might seem odd to group all variant types under a single trait (why allow `iter_fields` on a unit variant?), but the reason this was done ~~is to easily allow *variant swapping*.~~ As I was recently drafting up the **Design Decisions** section, I discovered that other solutions could have been made to work with variant swapping. So while there are reasons to keep the all-in-one approach, variant swapping is _not_ one of them. ```rust let mut value: Box<dyn Enum> = Box::new(TestEnum::A); value.set(Box::new(TestEnum::B(123))).unwrap(); ``` #### Serialization Enums can be serialized and deserialized via reflection without needing to implement `Serialize` or `Deserialize` themselves (which can save thousands of lines of generated code). Below are the ways an enum can be serialized. > Note, like the rest of reflection-based serialization, the order of the keys in these representations is important! ##### Unit ```json { "type": "my_crate::TestEnum", "enum": { "variant": "A" } } ``` ##### Tuple ```json { "type": "my_crate::TestEnum", "enum": { "variant": "B", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` <details> <summary>Effects on Option</summary> This ends up making `Option` look a little ugly: ```json { "type": "core::option::Option<usize>", "enum": { "variant": "Some", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` </details> ##### Struct ```json { "type": "my_crate::TestEnum", "enum": { "variant": "C", "struct": { "foo": { "type": "f32", "value": 1.23 }, "bar": { "type": "bool", "value": false } } } } ``` ## Design Decisions <details> <summary><strong>View Section</strong></summary> This section is here to provide some context for why certain decisions were made for this PR, alternatives that could have been used instead, and what could be improved upon in the future. ### Variant Representation One of the biggest decisions was to decide on how to represent variants. The current design uses a "all-in-one" design where unit, tuple, and struct variants are all simultaneously represented by the `Enum` trait. This is not the only way it could have been done, though. #### Alternatives ##### 1. Variant Traits One way of representing variants would be to define traits for each variant, implementing them whenever an enum featured at least one instance of them. This would allow us to define variants like: ```rust pub trait Enum: Reflect { fn variant(&self) -> Variant; } pub enum Variant<'a> { Unit, Tuple(&'a dyn TupleVariant), Struct(&'a dyn StructVariant), } pub trait TupleVariant { fn field_len(&self) -> usize; // ... } ``` And then do things like: ```rust fn get_tuple_len(foo: &dyn Enum) -> usize { match foo.variant() { Variant::Tuple(tuple) => tuple.field_len(), _ => panic!("not a tuple variant!") } } ``` The reason this PR does not go with this approach is because of the fact that variants are not separate types. In other words, we cannot implement traits on specific variants— these cover the *entire* enum. This means we offer an easy footgun: ```rust let foo: Option<i32> = None; let my_enum = Box::new(foo) as Box<dyn TupleVariant>; ``` Here, `my_enum` contains `foo`, which is a unit variant. However, since we need to implement `TupleVariant` for `Option` as a whole, it's possible to perform such a cast. This is obviously wrong, but could easily go unnoticed. So unfortunately, this makes it not a good candidate for representing variants. ##### 2. Variant Structs To get around the issue of traits necessarily needing to apply to both the enum and its variants, we could instead use structs that are created on a per-variant basis. This was also considered but was ultimately [[removed](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c)](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c) due to concerns about allocations. Each variant struct would probably look something like: ```rust pub trait Enum: Reflect { fn variant_mut(&self) -> VariantMut; } pub enum VariantMut<'a> { Unit, Tuple(TupleVariantMut), Struct(StructVariantMut), } struct StructVariantMut<'a> { fields: Vec<&'a mut dyn Reflect>, field_indices: HashMap<Cow<'static, str>, usize> } ``` This allows us to isolate struct variants into their own defined struct and define methods specifically for their use. It also prevents users from casting to it since it's not a trait. However, this is not an optimal solution. Both `field_indices` and `fields` will require an allocation (remember, a `Box<[T]>` still requires a `Vec<T>` in order to be constructed). This *might* be a problem if called frequently enough. ##### 3. Generated Structs The original design, implemented by @Davier, instead generates structs specific for each variant. So if we had a variant path like `Foo::Bar`, we'd generate a struct named `FooBarWrapper`. This would be newtyped around the original enum and forward tuple or struct methods to the enum with the chosen variant. Because it involved using the `Tuple` and `Struct` traits (which are also both bound on `Reflect`), this meant a bit more code had to be generated. For a single struct variant with one field, the generated code amounted to ~110LoC. However, each new field added to that variant only added ~6 more LoC. In order to work properly, the enum had to be transmuted to the generated struct: ```rust fn variant(&self) -> crate::EnumVariant<'_> { match self { Foo::Bar {value: i32} => { let wrapper_ref = unsafe { std::mem::transmute::<&Self, &FooBarWrapper>(self) }; crate::EnumVariant::Struct(wrapper_ref as &dyn crate::Struct) } } } ``` This works because `FooBarWrapper` is defined as `repr(transparent)`. Out of all the alternatives, this would probably be the one most likely to be used again in the future. The reasons for why this PR did not continue to use it was because: * To reduce generated code (which would hopefully speed up compile times) * To avoid cluttering the code with generated structs not visible to the user * To keep bevy_reflect simple and extensible (these generated structs act as proxies and might not play well with current or future systems) * To avoid additional unsafe blocks * My own misunderstanding of @Davier's code That last point is obviously on me. I misjudged the code to be too unsafe and unable to handle variant swapping (which it probably could) when I was rebasing it. Looking over it again when writing up this whole section, I see that it was actually a pretty clever way of handling variant representation. #### Benefits of All-in-One As stated before, the current implementation uses an all-in-one approach. All variants are capable of containing fields as far as `Enum` is concerned. This provides a few benefits that the alternatives do not (reduced indirection, safer code, etc.). The biggest benefit, though, is direct field access. Rather than forcing users to have to go through pattern matching, we grant direct access to the fields contained by the current variant. The reason we can do this is because all of the pattern matching happens internally. Getting the field at index `2` will automatically return `Some(...)` for the current variant if it has a field at that index or `None` if it doesn't (or can't). This could be useful for scenarios where the variant has already been verified or just set/swapped (or even where the type of variant doesn't matter): ```rust let dyn_enum: &mut dyn Enum = &mut Foo::Bar {value: 123}; // We know it's the `Bar` variant let field = dyn_enum.field("value").unwrap(); ``` Reflection is not a type-safe abstraction— almost every return value is wrapped in `Option<...>`. There are plenty of places to check and recheck that a value is what Reflect says it is. Forcing users to have to go through `match` each time they want to access a field might just be an extra step among dozens of other verification processes. Some might disagree, but ultimately, my view is that the benefit here is an improvement to the ergonomics and usability of reflected enums. </details> --- ## Changelog ### Added * Added `Enum` trait * Added `Enum` impl to `Reflect` derive macro * Added `DynamicEnum` struct * Added `DynamicVariant` * Added `EnumInfo` * Added `VariantInfo` * Added `StructVariantInfo` * Added `TupleVariantInfo` * Added `UnitVariantInfo` * Added serializtion/deserialization support for enums * Added `EnumSerializer` * Added `VariantType` * Added `VariantFieldIter` * Added `VariantField` * Added `enum_partial_eq(...)` * Added `enum_hash(...)` ### Changed * `Option<T>` now implements `Enum` * `bevy_window` now depends on `bevy_reflect` * Implemented `Reflect` and `FromReflect` for `WindowId` * Derive `FromReflect` on `PerspectiveProjection` * Derive `FromReflect` on `OrthographicProjection` * Derive `FromReflect` on `WindowOrigin` * Derive `FromReflect` on `ScalingMode` * Derive `FromReflect` on `DepthCalculation` ## Migration Guide * Enums no longer need to be treated as values and usages of `#[reflect_value(...)]` can be removed or replaced by `#[reflect(...)]` * Enums (including `Option<T>`) now take a different format when serializing. The format is described above, but this may cause issues for existing scenes that make use of enums. --- Also shout out to @nicopap for helping clean up some of the code here! It's a big feature so help like this is really appreciated! Co-authored-by: Gino Valente <gino.valente.code@gmail.com>
2022-08-02 22:14:41 +00:00
) -> Result<Vec<EnumVariant<'a>>, syn::Error> {
let sifter: utility::ResultSifter<EnumVariant<'a>> = variants
.iter()
.enumerate()
.map(|(index, variant)| -> Result<EnumVariant, syn::Error> {
let fields = Self::collect_struct_fields(&variant.fields)?;
bevy_reflect_derive: Tidying up the code (#4712) # Objective The `bevy_reflect_derive` crate is not the cleanest or easiest to follow/maintain. The `lib.rs` file is especially difficult with over 1000 lines of code written in a confusing order. This is just a result of growth within the crate and it would be nice to clean it up for future work. ## Solution Split `bevy_reflect_derive` into many more submodules. The submodules include: * `container_attributes` - Code relating to container attributes * `derive_data` - Code relating to reflection-based derive metadata * `field_attributes` - Code relating to field attributes * `impls` - Code containing actual reflection implementations * `reflect_value` - Code relating to reflection-based value metadata * `registration` - Code relating to type registration * `utility` - General-purpose utility functions This leaves the `lib.rs` file to contain only the public macros, making it much easier to digest (and fewer than 200 lines). By breaking up the code into smaller modules, we make it easier for future contributors to find the code they're looking for or identify which module best fits their own additions. ### Metadata Structs This cleanup also adds two big metadata structs: `ReflectFieldAttr` and `ReflectDeriveData`. The former is used to store all attributes for a struct field (if any). The latter is used to store all metadata for struct-based derive inputs. Both significantly reduce code duplication and make editing these macros much simpler. The tradeoff is that we may collect more metadata than needed. However, this is usually a small thing (such as checking for attributes when they're not really needed or creating a `ReflectFieldAttr` for every field regardless of whether they actually have an attribute). We could try to remove these tradeoffs and squeeze some more performance out, but doing so might come at the cost of developer experience. Personally, I think it's much nicer to create a `ReflectFieldAttr` for every field since it means I don't have to do two `Option` checks. Others may disagree, though, and so we can discuss changing this either in this PR or in a future one. ### Out of Scope _Some_ documentation has been added or improved, but ultimately good docs are probably best saved for a dedicated PR. ## 🔍 Focus Points (for reviewers) I know it's a lot to sift through, so here is a list of **key points for reviewers**: - The following files contain code that was mostly just relocated: - `reflect_value.rs` - `registration.rs` - `container_attributes.rs` was also mostly moved but features some general cleanup (reducing nesting, removing hardcoded strings, etc.) and lots of doc comments - Most impl logic was moved from `lib.rs` to `impls.rs`, but they have been significantly modified to use the new `ReflectDeriveData` metadata struct in order to reduce duplication. - `derive_data.rs` and `field_attributes.rs` contain almost entirely new code and should probably be given the most attention. - Likewise, `from_reflect.rs` saw major changes using `ReflectDeriveData` so it should also be given focus. - There was no change to the `lib.rs` exports so the end-user API should be the same. ## Prior Work This task was initially tackled by @NathanSWard in #2377 (which was closed in favor of this PR), so hats off to them for beating me to the punch by nearly a year! --- ## Changelog * **[INTERNAL]** Split `bevy_reflect_derive` into smaller submodules * **[INTERNAL]** Add `ReflectFieldAttr` * **[INTERNAL]** Add `ReflectDeriveData` * Add `BevyManifest::get_path_direct()` method (`bevy_macro_utils`) Co-authored-by: MrGVSV <49806985+MrGVSV@users.noreply.github.com>
2022-05-12 19:43:23 +00:00
bevy_reflect: Reflect enums (#4761) # Objective > This is a revival of #1347. Credit for the original PR should go to @Davier. Currently, enums are treated as `ReflectRef::Value` types by `bevy_reflect`. Obviously, there needs to be better a better representation for enums using the reflection API. ## Solution Based on prior work from @Davier, an `Enum` trait has been added as well as the ability to automatically implement it via the `Reflect` derive macro. This allows enums to be expressed dynamically: ```rust #[derive(Reflect)] enum Foo { A, B(usize), C { value: f32 }, } let mut foo = Foo::B(123); assert_eq!("B", foo.variant_name()); assert_eq!(1, foo.field_len()); let new_value = DynamicEnum::from(Foo::C { value: 1.23 }); foo.apply(&new_value); assert_eq!(Foo::C{value: 1.23}, foo); ``` ### Features #### Derive Macro Use the `#[derive(Reflect)]` macro to automatically implement the `Enum` trait for enum definitions. Optionally, you can use `#[reflect(ignore)]` with both variants and variant fields, just like you can with structs. These ignored items will not be considered as part of the reflection and cannot be accessed via reflection. ```rust #[derive(Reflect)] enum TestEnum { A, // Uncomment to ignore all of `B` // #[reflect(ignore)] B(usize), C { // Uncomment to ignore only field `foo` of `C` // #[reflect(ignore)] foo: f32, bar: bool, }, } ``` #### Dynamic Enums Enums may be created/represented dynamically via the `DynamicEnum` struct. The main purpose of this struct is to allow enums to be deserialized into a partial state and to allow dynamic patching. In order to ensure conversion from a `DynamicEnum` to a concrete enum type goes smoothly, be sure to add `FromReflect` to your derive macro. ```rust let mut value = TestEnum::A; // Create from a concrete instance let dyn_enum = DynamicEnum::from(TestEnum::B(123)); value.apply(&dyn_enum); assert_eq!(TestEnum::B(123), value); // Create a purely dynamic instance let dyn_enum = DynamicEnum::new("TestEnum", "A", ()); value.apply(&dyn_enum); assert_eq!(TestEnum::A, value); ``` #### Variants An enum value is always represented as one of its variants— never the enum in its entirety. ```rust let value = TestEnum::A; assert_eq!("A", value.variant_name()); // Since we are using the `A` variant, we cannot also be the `B` variant assert_ne!("B", value.variant_name()); ``` All variant types are representable within the `Enum` trait: unit, struct, and tuple. You can get the current type like: ```rust match value.variant_type() { VariantType::Unit => println!("A unit variant!"), VariantType::Struct => println!("A struct variant!"), VariantType::Tuple => println!("A tuple variant!"), } ``` > Notice that they don't contain any values representing the fields. These are purely tags. If a variant has them, you can access the fields as well: ```rust let mut value = TestEnum::C { foo: 1.23, bar: false }; // Read/write specific fields *value.field_mut("bar").unwrap() = true; // Iterate over the entire collection of fields for field in value.iter_fields() { println!("{} = {:?}", field.name(), field.value()); } ``` #### Variant Swapping It might seem odd to group all variant types under a single trait (why allow `iter_fields` on a unit variant?), but the reason this was done ~~is to easily allow *variant swapping*.~~ As I was recently drafting up the **Design Decisions** section, I discovered that other solutions could have been made to work with variant swapping. So while there are reasons to keep the all-in-one approach, variant swapping is _not_ one of them. ```rust let mut value: Box<dyn Enum> = Box::new(TestEnum::A); value.set(Box::new(TestEnum::B(123))).unwrap(); ``` #### Serialization Enums can be serialized and deserialized via reflection without needing to implement `Serialize` or `Deserialize` themselves (which can save thousands of lines of generated code). Below are the ways an enum can be serialized. > Note, like the rest of reflection-based serialization, the order of the keys in these representations is important! ##### Unit ```json { "type": "my_crate::TestEnum", "enum": { "variant": "A" } } ``` ##### Tuple ```json { "type": "my_crate::TestEnum", "enum": { "variant": "B", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` <details> <summary>Effects on Option</summary> This ends up making `Option` look a little ugly: ```json { "type": "core::option::Option<usize>", "enum": { "variant": "Some", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` </details> ##### Struct ```json { "type": "my_crate::TestEnum", "enum": { "variant": "C", "struct": { "foo": { "type": "f32", "value": 1.23 }, "bar": { "type": "bool", "value": false } } } } ``` ## Design Decisions <details> <summary><strong>View Section</strong></summary> This section is here to provide some context for why certain decisions were made for this PR, alternatives that could have been used instead, and what could be improved upon in the future. ### Variant Representation One of the biggest decisions was to decide on how to represent variants. The current design uses a "all-in-one" design where unit, tuple, and struct variants are all simultaneously represented by the `Enum` trait. This is not the only way it could have been done, though. #### Alternatives ##### 1. Variant Traits One way of representing variants would be to define traits for each variant, implementing them whenever an enum featured at least one instance of them. This would allow us to define variants like: ```rust pub trait Enum: Reflect { fn variant(&self) -> Variant; } pub enum Variant<'a> { Unit, Tuple(&'a dyn TupleVariant), Struct(&'a dyn StructVariant), } pub trait TupleVariant { fn field_len(&self) -> usize; // ... } ``` And then do things like: ```rust fn get_tuple_len(foo: &dyn Enum) -> usize { match foo.variant() { Variant::Tuple(tuple) => tuple.field_len(), _ => panic!("not a tuple variant!") } } ``` The reason this PR does not go with this approach is because of the fact that variants are not separate types. In other words, we cannot implement traits on specific variants— these cover the *entire* enum. This means we offer an easy footgun: ```rust let foo: Option<i32> = None; let my_enum = Box::new(foo) as Box<dyn TupleVariant>; ``` Here, `my_enum` contains `foo`, which is a unit variant. However, since we need to implement `TupleVariant` for `Option` as a whole, it's possible to perform such a cast. This is obviously wrong, but could easily go unnoticed. So unfortunately, this makes it not a good candidate for representing variants. ##### 2. Variant Structs To get around the issue of traits necessarily needing to apply to both the enum and its variants, we could instead use structs that are created on a per-variant basis. This was also considered but was ultimately [[removed](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c)](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c) due to concerns about allocations. Each variant struct would probably look something like: ```rust pub trait Enum: Reflect { fn variant_mut(&self) -> VariantMut; } pub enum VariantMut<'a> { Unit, Tuple(TupleVariantMut), Struct(StructVariantMut), } struct StructVariantMut<'a> { fields: Vec<&'a mut dyn Reflect>, field_indices: HashMap<Cow<'static, str>, usize> } ``` This allows us to isolate struct variants into their own defined struct and define methods specifically for their use. It also prevents users from casting to it since it's not a trait. However, this is not an optimal solution. Both `field_indices` and `fields` will require an allocation (remember, a `Box<[T]>` still requires a `Vec<T>` in order to be constructed). This *might* be a problem if called frequently enough. ##### 3. Generated Structs The original design, implemented by @Davier, instead generates structs specific for each variant. So if we had a variant path like `Foo::Bar`, we'd generate a struct named `FooBarWrapper`. This would be newtyped around the original enum and forward tuple or struct methods to the enum with the chosen variant. Because it involved using the `Tuple` and `Struct` traits (which are also both bound on `Reflect`), this meant a bit more code had to be generated. For a single struct variant with one field, the generated code amounted to ~110LoC. However, each new field added to that variant only added ~6 more LoC. In order to work properly, the enum had to be transmuted to the generated struct: ```rust fn variant(&self) -> crate::EnumVariant<'_> { match self { Foo::Bar {value: i32} => { let wrapper_ref = unsafe { std::mem::transmute::<&Self, &FooBarWrapper>(self) }; crate::EnumVariant::Struct(wrapper_ref as &dyn crate::Struct) } } } ``` This works because `FooBarWrapper` is defined as `repr(transparent)`. Out of all the alternatives, this would probably be the one most likely to be used again in the future. The reasons for why this PR did not continue to use it was because: * To reduce generated code (which would hopefully speed up compile times) * To avoid cluttering the code with generated structs not visible to the user * To keep bevy_reflect simple and extensible (these generated structs act as proxies and might not play well with current or future systems) * To avoid additional unsafe blocks * My own misunderstanding of @Davier's code That last point is obviously on me. I misjudged the code to be too unsafe and unable to handle variant swapping (which it probably could) when I was rebasing it. Looking over it again when writing up this whole section, I see that it was actually a pretty clever way of handling variant representation. #### Benefits of All-in-One As stated before, the current implementation uses an all-in-one approach. All variants are capable of containing fields as far as `Enum` is concerned. This provides a few benefits that the alternatives do not (reduced indirection, safer code, etc.). The biggest benefit, though, is direct field access. Rather than forcing users to have to go through pattern matching, we grant direct access to the fields contained by the current variant. The reason we can do this is because all of the pattern matching happens internally. Getting the field at index `2` will automatically return `Some(...)` for the current variant if it has a field at that index or `None` if it doesn't (or can't). This could be useful for scenarios where the variant has already been verified or just set/swapped (or even where the type of variant doesn't matter): ```rust let dyn_enum: &mut dyn Enum = &mut Foo::Bar {value: 123}; // We know it's the `Bar` variant let field = dyn_enum.field("value").unwrap(); ``` Reflection is not a type-safe abstraction— almost every return value is wrapped in `Option<...>`. There are plenty of places to check and recheck that a value is what Reflect says it is. Forcing users to have to go through `match` each time they want to access a field might just be an extra step among dozens of other verification processes. Some might disagree, but ultimately, my view is that the benefit here is an improvement to the ergonomics and usability of reflected enums. </details> --- ## Changelog ### Added * Added `Enum` trait * Added `Enum` impl to `Reflect` derive macro * Added `DynamicEnum` struct * Added `DynamicVariant` * Added `EnumInfo` * Added `VariantInfo` * Added `StructVariantInfo` * Added `TupleVariantInfo` * Added `UnitVariantInfo` * Added serializtion/deserialization support for enums * Added `EnumSerializer` * Added `VariantType` * Added `VariantFieldIter` * Added `VariantField` * Added `enum_partial_eq(...)` * Added `enum_hash(...)` ### Changed * `Option<T>` now implements `Enum` * `bevy_window` now depends on `bevy_reflect` * Implemented `Reflect` and `FromReflect` for `WindowId` * Derive `FromReflect` on `PerspectiveProjection` * Derive `FromReflect` on `OrthographicProjection` * Derive `FromReflect` on `WindowOrigin` * Derive `FromReflect` on `ScalingMode` * Derive `FromReflect` on `DepthCalculation` ## Migration Guide * Enums no longer need to be treated as values and usages of `#[reflect_value(...)]` can be removed or replaced by `#[reflect(...)]` * Enums (including `Option<T>`) now take a different format when serializing. The format is described above, but this may cause issues for existing scenes that make use of enums. --- Also shout out to @nicopap for helping clean up some of the code here! It's a big feature so help like this is really appreciated! Co-authored-by: Gino Valente <gino.valente.code@gmail.com>
2022-08-02 22:14:41 +00:00
let fields = match variant.fields {
Fields::Named(..) => EnumVariantFields::Named(fields),
Fields::Unnamed(..) => EnumVariantFields::Unnamed(fields),
Fields::Unit => EnumVariantFields::Unit,
};
Ok(EnumVariant {
fields,
bevy_reflect_derive: Clean up attribute logic (#11777) # Objective The code in `bevy_reflect_derive` could use some cleanup. ## Solution Took some of the changes in #11659 to create a dedicated PR for cleaning up the field and container attribute logic. #### Updated Naming I renamed `ReflectTraits` and `ReflectFieldAttr` to `ContainerAttributes` and `FieldAttributes`, respectively. I think these are clearer. #### Updated Parsing ##### Readability The parsing logic wasn't too bad before, but it was getting difficult to read. There was some duplicated logic between `Meta::List` and `Meta::Path` attributes. Additionally, all the logic was kept inside a large method. To simply things, I replaced the nested meta parsing with `ParseStream` parsing. In my opinion, this is easier to follow since it breaks up the large match statement into a small set of single-line if statements, where each if-block contains a single call to the appropriate attribute parsing method. ##### Flexibility On top of the added simplicity, this also makes our attribute parsing much more flexible. It allows us to more elegantly handle custom where clauses (i.e. `#[reflect(where T: Foo)]`) and it opens the door for more non-standard attribute syntax (e.g. #11659). ##### Errors This also allows us to automatically provide certain errors when parsing. For example, since we can use `stream.lookahead1()`, we get errors like the following for free: ``` error: expected one of: `ignore`, `skip_serializing`, `default` --> crates/bevy_reflect/src/lib.rs:1988:23 | 1988 | #[reflect(foo)] | ^^^ ``` --- ## Changelog > [!note] > All changes are internal to `bevy_reflect_derive` and should not affect the public API[^1]. - Renamed `ReflectTraits` to `ContainerAttributes` - Renamed `ReflectMeta::traits` to `ReflectMeta::attrs` - Renamed `ReflectFieldAttr` to `FieldAttributes` - Updated parsing logic for field/container attribute parsing - Now uses a `ParseStream` directly instead of nested meta parsing - General code cleanup of the field/container attribute modules for `bevy_reflect_derive` [^1]: Does not include errors, which may look slightly different. --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
2024-02-12 15:16:27 +00:00
attrs: FieldAttributes::parse_attributes(&variant.attrs)?,
bevy_reflect: Reflect enums (#4761) # Objective > This is a revival of #1347. Credit for the original PR should go to @Davier. Currently, enums are treated as `ReflectRef::Value` types by `bevy_reflect`. Obviously, there needs to be better a better representation for enums using the reflection API. ## Solution Based on prior work from @Davier, an `Enum` trait has been added as well as the ability to automatically implement it via the `Reflect` derive macro. This allows enums to be expressed dynamically: ```rust #[derive(Reflect)] enum Foo { A, B(usize), C { value: f32 }, } let mut foo = Foo::B(123); assert_eq!("B", foo.variant_name()); assert_eq!(1, foo.field_len()); let new_value = DynamicEnum::from(Foo::C { value: 1.23 }); foo.apply(&new_value); assert_eq!(Foo::C{value: 1.23}, foo); ``` ### Features #### Derive Macro Use the `#[derive(Reflect)]` macro to automatically implement the `Enum` trait for enum definitions. Optionally, you can use `#[reflect(ignore)]` with both variants and variant fields, just like you can with structs. These ignored items will not be considered as part of the reflection and cannot be accessed via reflection. ```rust #[derive(Reflect)] enum TestEnum { A, // Uncomment to ignore all of `B` // #[reflect(ignore)] B(usize), C { // Uncomment to ignore only field `foo` of `C` // #[reflect(ignore)] foo: f32, bar: bool, }, } ``` #### Dynamic Enums Enums may be created/represented dynamically via the `DynamicEnum` struct. The main purpose of this struct is to allow enums to be deserialized into a partial state and to allow dynamic patching. In order to ensure conversion from a `DynamicEnum` to a concrete enum type goes smoothly, be sure to add `FromReflect` to your derive macro. ```rust let mut value = TestEnum::A; // Create from a concrete instance let dyn_enum = DynamicEnum::from(TestEnum::B(123)); value.apply(&dyn_enum); assert_eq!(TestEnum::B(123), value); // Create a purely dynamic instance let dyn_enum = DynamicEnum::new("TestEnum", "A", ()); value.apply(&dyn_enum); assert_eq!(TestEnum::A, value); ``` #### Variants An enum value is always represented as one of its variants— never the enum in its entirety. ```rust let value = TestEnum::A; assert_eq!("A", value.variant_name()); // Since we are using the `A` variant, we cannot also be the `B` variant assert_ne!("B", value.variant_name()); ``` All variant types are representable within the `Enum` trait: unit, struct, and tuple. You can get the current type like: ```rust match value.variant_type() { VariantType::Unit => println!("A unit variant!"), VariantType::Struct => println!("A struct variant!"), VariantType::Tuple => println!("A tuple variant!"), } ``` > Notice that they don't contain any values representing the fields. These are purely tags. If a variant has them, you can access the fields as well: ```rust let mut value = TestEnum::C { foo: 1.23, bar: false }; // Read/write specific fields *value.field_mut("bar").unwrap() = true; // Iterate over the entire collection of fields for field in value.iter_fields() { println!("{} = {:?}", field.name(), field.value()); } ``` #### Variant Swapping It might seem odd to group all variant types under a single trait (why allow `iter_fields` on a unit variant?), but the reason this was done ~~is to easily allow *variant swapping*.~~ As I was recently drafting up the **Design Decisions** section, I discovered that other solutions could have been made to work with variant swapping. So while there are reasons to keep the all-in-one approach, variant swapping is _not_ one of them. ```rust let mut value: Box<dyn Enum> = Box::new(TestEnum::A); value.set(Box::new(TestEnum::B(123))).unwrap(); ``` #### Serialization Enums can be serialized and deserialized via reflection without needing to implement `Serialize` or `Deserialize` themselves (which can save thousands of lines of generated code). Below are the ways an enum can be serialized. > Note, like the rest of reflection-based serialization, the order of the keys in these representations is important! ##### Unit ```json { "type": "my_crate::TestEnum", "enum": { "variant": "A" } } ``` ##### Tuple ```json { "type": "my_crate::TestEnum", "enum": { "variant": "B", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` <details> <summary>Effects on Option</summary> This ends up making `Option` look a little ugly: ```json { "type": "core::option::Option<usize>", "enum": { "variant": "Some", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` </details> ##### Struct ```json { "type": "my_crate::TestEnum", "enum": { "variant": "C", "struct": { "foo": { "type": "f32", "value": 1.23 }, "bar": { "type": "bool", "value": false } } } } ``` ## Design Decisions <details> <summary><strong>View Section</strong></summary> This section is here to provide some context for why certain decisions were made for this PR, alternatives that could have been used instead, and what could be improved upon in the future. ### Variant Representation One of the biggest decisions was to decide on how to represent variants. The current design uses a "all-in-one" design where unit, tuple, and struct variants are all simultaneously represented by the `Enum` trait. This is not the only way it could have been done, though. #### Alternatives ##### 1. Variant Traits One way of representing variants would be to define traits for each variant, implementing them whenever an enum featured at least one instance of them. This would allow us to define variants like: ```rust pub trait Enum: Reflect { fn variant(&self) -> Variant; } pub enum Variant<'a> { Unit, Tuple(&'a dyn TupleVariant), Struct(&'a dyn StructVariant), } pub trait TupleVariant { fn field_len(&self) -> usize; // ... } ``` And then do things like: ```rust fn get_tuple_len(foo: &dyn Enum) -> usize { match foo.variant() { Variant::Tuple(tuple) => tuple.field_len(), _ => panic!("not a tuple variant!") } } ``` The reason this PR does not go with this approach is because of the fact that variants are not separate types. In other words, we cannot implement traits on specific variants— these cover the *entire* enum. This means we offer an easy footgun: ```rust let foo: Option<i32> = None; let my_enum = Box::new(foo) as Box<dyn TupleVariant>; ``` Here, `my_enum` contains `foo`, which is a unit variant. However, since we need to implement `TupleVariant` for `Option` as a whole, it's possible to perform such a cast. This is obviously wrong, but could easily go unnoticed. So unfortunately, this makes it not a good candidate for representing variants. ##### 2. Variant Structs To get around the issue of traits necessarily needing to apply to both the enum and its variants, we could instead use structs that are created on a per-variant basis. This was also considered but was ultimately [[removed](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c)](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c) due to concerns about allocations. Each variant struct would probably look something like: ```rust pub trait Enum: Reflect { fn variant_mut(&self) -> VariantMut; } pub enum VariantMut<'a> { Unit, Tuple(TupleVariantMut), Struct(StructVariantMut), } struct StructVariantMut<'a> { fields: Vec<&'a mut dyn Reflect>, field_indices: HashMap<Cow<'static, str>, usize> } ``` This allows us to isolate struct variants into their own defined struct and define methods specifically for their use. It also prevents users from casting to it since it's not a trait. However, this is not an optimal solution. Both `field_indices` and `fields` will require an allocation (remember, a `Box<[T]>` still requires a `Vec<T>` in order to be constructed). This *might* be a problem if called frequently enough. ##### 3. Generated Structs The original design, implemented by @Davier, instead generates structs specific for each variant. So if we had a variant path like `Foo::Bar`, we'd generate a struct named `FooBarWrapper`. This would be newtyped around the original enum and forward tuple or struct methods to the enum with the chosen variant. Because it involved using the `Tuple` and `Struct` traits (which are also both bound on `Reflect`), this meant a bit more code had to be generated. For a single struct variant with one field, the generated code amounted to ~110LoC. However, each new field added to that variant only added ~6 more LoC. In order to work properly, the enum had to be transmuted to the generated struct: ```rust fn variant(&self) -> crate::EnumVariant<'_> { match self { Foo::Bar {value: i32} => { let wrapper_ref = unsafe { std::mem::transmute::<&Self, &FooBarWrapper>(self) }; crate::EnumVariant::Struct(wrapper_ref as &dyn crate::Struct) } } } ``` This works because `FooBarWrapper` is defined as `repr(transparent)`. Out of all the alternatives, this would probably be the one most likely to be used again in the future. The reasons for why this PR did not continue to use it was because: * To reduce generated code (which would hopefully speed up compile times) * To avoid cluttering the code with generated structs not visible to the user * To keep bevy_reflect simple and extensible (these generated structs act as proxies and might not play well with current or future systems) * To avoid additional unsafe blocks * My own misunderstanding of @Davier's code That last point is obviously on me. I misjudged the code to be too unsafe and unable to handle variant swapping (which it probably could) when I was rebasing it. Looking over it again when writing up this whole section, I see that it was actually a pretty clever way of handling variant representation. #### Benefits of All-in-One As stated before, the current implementation uses an all-in-one approach. All variants are capable of containing fields as far as `Enum` is concerned. This provides a few benefits that the alternatives do not (reduced indirection, safer code, etc.). The biggest benefit, though, is direct field access. Rather than forcing users to have to go through pattern matching, we grant direct access to the fields contained by the current variant. The reason we can do this is because all of the pattern matching happens internally. Getting the field at index `2` will automatically return `Some(...)` for the current variant if it has a field at that index or `None` if it doesn't (or can't). This could be useful for scenarios where the variant has already been verified or just set/swapped (or even where the type of variant doesn't matter): ```rust let dyn_enum: &mut dyn Enum = &mut Foo::Bar {value: 123}; // We know it's the `Bar` variant let field = dyn_enum.field("value").unwrap(); ``` Reflection is not a type-safe abstraction— almost every return value is wrapped in `Option<...>`. There are plenty of places to check and recheck that a value is what Reflect says it is. Forcing users to have to go through `match` each time they want to access a field might just be an extra step among dozens of other verification processes. Some might disagree, but ultimately, my view is that the benefit here is an improvement to the ergonomics and usability of reflected enums. </details> --- ## Changelog ### Added * Added `Enum` trait * Added `Enum` impl to `Reflect` derive macro * Added `DynamicEnum` struct * Added `DynamicVariant` * Added `EnumInfo` * Added `VariantInfo` * Added `StructVariantInfo` * Added `TupleVariantInfo` * Added `UnitVariantInfo` * Added serializtion/deserialization support for enums * Added `EnumSerializer` * Added `VariantType` * Added `VariantFieldIter` * Added `VariantField` * Added `enum_partial_eq(...)` * Added `enum_hash(...)` ### Changed * `Option<T>` now implements `Enum` * `bevy_window` now depends on `bevy_reflect` * Implemented `Reflect` and `FromReflect` for `WindowId` * Derive `FromReflect` on `PerspectiveProjection` * Derive `FromReflect` on `OrthographicProjection` * Derive `FromReflect` on `WindowOrigin` * Derive `FromReflect` on `ScalingMode` * Derive `FromReflect` on `DepthCalculation` ## Migration Guide * Enums no longer need to be treated as values and usages of `#[reflect_value(...)]` can be removed or replaced by `#[reflect(...)]` * Enums (including `Option<T>`) now take a different format when serializing. The format is described above, but this may cause issues for existing scenes that make use of enums. --- Also shout out to @nicopap for helping clean up some of the code here! It's a big feature so help like this is really appreciated! Co-authored-by: Gino Valente <gino.valente.code@gmail.com>
2022-08-02 22:14:41 +00:00
data: variant,
index,
#[cfg(feature = "documentation")]
doc: crate::documentation::Documentation::from_attributes(&variant.attrs),
bevy_reflect: Reflect enums (#4761) # Objective > This is a revival of #1347. Credit for the original PR should go to @Davier. Currently, enums are treated as `ReflectRef::Value` types by `bevy_reflect`. Obviously, there needs to be better a better representation for enums using the reflection API. ## Solution Based on prior work from @Davier, an `Enum` trait has been added as well as the ability to automatically implement it via the `Reflect` derive macro. This allows enums to be expressed dynamically: ```rust #[derive(Reflect)] enum Foo { A, B(usize), C { value: f32 }, } let mut foo = Foo::B(123); assert_eq!("B", foo.variant_name()); assert_eq!(1, foo.field_len()); let new_value = DynamicEnum::from(Foo::C { value: 1.23 }); foo.apply(&new_value); assert_eq!(Foo::C{value: 1.23}, foo); ``` ### Features #### Derive Macro Use the `#[derive(Reflect)]` macro to automatically implement the `Enum` trait for enum definitions. Optionally, you can use `#[reflect(ignore)]` with both variants and variant fields, just like you can with structs. These ignored items will not be considered as part of the reflection and cannot be accessed via reflection. ```rust #[derive(Reflect)] enum TestEnum { A, // Uncomment to ignore all of `B` // #[reflect(ignore)] B(usize), C { // Uncomment to ignore only field `foo` of `C` // #[reflect(ignore)] foo: f32, bar: bool, }, } ``` #### Dynamic Enums Enums may be created/represented dynamically via the `DynamicEnum` struct. The main purpose of this struct is to allow enums to be deserialized into a partial state and to allow dynamic patching. In order to ensure conversion from a `DynamicEnum` to a concrete enum type goes smoothly, be sure to add `FromReflect` to your derive macro. ```rust let mut value = TestEnum::A; // Create from a concrete instance let dyn_enum = DynamicEnum::from(TestEnum::B(123)); value.apply(&dyn_enum); assert_eq!(TestEnum::B(123), value); // Create a purely dynamic instance let dyn_enum = DynamicEnum::new("TestEnum", "A", ()); value.apply(&dyn_enum); assert_eq!(TestEnum::A, value); ``` #### Variants An enum value is always represented as one of its variants— never the enum in its entirety. ```rust let value = TestEnum::A; assert_eq!("A", value.variant_name()); // Since we are using the `A` variant, we cannot also be the `B` variant assert_ne!("B", value.variant_name()); ``` All variant types are representable within the `Enum` trait: unit, struct, and tuple. You can get the current type like: ```rust match value.variant_type() { VariantType::Unit => println!("A unit variant!"), VariantType::Struct => println!("A struct variant!"), VariantType::Tuple => println!("A tuple variant!"), } ``` > Notice that they don't contain any values representing the fields. These are purely tags. If a variant has them, you can access the fields as well: ```rust let mut value = TestEnum::C { foo: 1.23, bar: false }; // Read/write specific fields *value.field_mut("bar").unwrap() = true; // Iterate over the entire collection of fields for field in value.iter_fields() { println!("{} = {:?}", field.name(), field.value()); } ``` #### Variant Swapping It might seem odd to group all variant types under a single trait (why allow `iter_fields` on a unit variant?), but the reason this was done ~~is to easily allow *variant swapping*.~~ As I was recently drafting up the **Design Decisions** section, I discovered that other solutions could have been made to work with variant swapping. So while there are reasons to keep the all-in-one approach, variant swapping is _not_ one of them. ```rust let mut value: Box<dyn Enum> = Box::new(TestEnum::A); value.set(Box::new(TestEnum::B(123))).unwrap(); ``` #### Serialization Enums can be serialized and deserialized via reflection without needing to implement `Serialize` or `Deserialize` themselves (which can save thousands of lines of generated code). Below are the ways an enum can be serialized. > Note, like the rest of reflection-based serialization, the order of the keys in these representations is important! ##### Unit ```json { "type": "my_crate::TestEnum", "enum": { "variant": "A" } } ``` ##### Tuple ```json { "type": "my_crate::TestEnum", "enum": { "variant": "B", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` <details> <summary>Effects on Option</summary> This ends up making `Option` look a little ugly: ```json { "type": "core::option::Option<usize>", "enum": { "variant": "Some", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` </details> ##### Struct ```json { "type": "my_crate::TestEnum", "enum": { "variant": "C", "struct": { "foo": { "type": "f32", "value": 1.23 }, "bar": { "type": "bool", "value": false } } } } ``` ## Design Decisions <details> <summary><strong>View Section</strong></summary> This section is here to provide some context for why certain decisions were made for this PR, alternatives that could have been used instead, and what could be improved upon in the future. ### Variant Representation One of the biggest decisions was to decide on how to represent variants. The current design uses a "all-in-one" design where unit, tuple, and struct variants are all simultaneously represented by the `Enum` trait. This is not the only way it could have been done, though. #### Alternatives ##### 1. Variant Traits One way of representing variants would be to define traits for each variant, implementing them whenever an enum featured at least one instance of them. This would allow us to define variants like: ```rust pub trait Enum: Reflect { fn variant(&self) -> Variant; } pub enum Variant<'a> { Unit, Tuple(&'a dyn TupleVariant), Struct(&'a dyn StructVariant), } pub trait TupleVariant { fn field_len(&self) -> usize; // ... } ``` And then do things like: ```rust fn get_tuple_len(foo: &dyn Enum) -> usize { match foo.variant() { Variant::Tuple(tuple) => tuple.field_len(), _ => panic!("not a tuple variant!") } } ``` The reason this PR does not go with this approach is because of the fact that variants are not separate types. In other words, we cannot implement traits on specific variants— these cover the *entire* enum. This means we offer an easy footgun: ```rust let foo: Option<i32> = None; let my_enum = Box::new(foo) as Box<dyn TupleVariant>; ``` Here, `my_enum` contains `foo`, which is a unit variant. However, since we need to implement `TupleVariant` for `Option` as a whole, it's possible to perform such a cast. This is obviously wrong, but could easily go unnoticed. So unfortunately, this makes it not a good candidate for representing variants. ##### 2. Variant Structs To get around the issue of traits necessarily needing to apply to both the enum and its variants, we could instead use structs that are created on a per-variant basis. This was also considered but was ultimately [[removed](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c)](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c) due to concerns about allocations. Each variant struct would probably look something like: ```rust pub trait Enum: Reflect { fn variant_mut(&self) -> VariantMut; } pub enum VariantMut<'a> { Unit, Tuple(TupleVariantMut), Struct(StructVariantMut), } struct StructVariantMut<'a> { fields: Vec<&'a mut dyn Reflect>, field_indices: HashMap<Cow<'static, str>, usize> } ``` This allows us to isolate struct variants into their own defined struct and define methods specifically for their use. It also prevents users from casting to it since it's not a trait. However, this is not an optimal solution. Both `field_indices` and `fields` will require an allocation (remember, a `Box<[T]>` still requires a `Vec<T>` in order to be constructed). This *might* be a problem if called frequently enough. ##### 3. Generated Structs The original design, implemented by @Davier, instead generates structs specific for each variant. So if we had a variant path like `Foo::Bar`, we'd generate a struct named `FooBarWrapper`. This would be newtyped around the original enum and forward tuple or struct methods to the enum with the chosen variant. Because it involved using the `Tuple` and `Struct` traits (which are also both bound on `Reflect`), this meant a bit more code had to be generated. For a single struct variant with one field, the generated code amounted to ~110LoC. However, each new field added to that variant only added ~6 more LoC. In order to work properly, the enum had to be transmuted to the generated struct: ```rust fn variant(&self) -> crate::EnumVariant<'_> { match self { Foo::Bar {value: i32} => { let wrapper_ref = unsafe { std::mem::transmute::<&Self, &FooBarWrapper>(self) }; crate::EnumVariant::Struct(wrapper_ref as &dyn crate::Struct) } } } ``` This works because `FooBarWrapper` is defined as `repr(transparent)`. Out of all the alternatives, this would probably be the one most likely to be used again in the future. The reasons for why this PR did not continue to use it was because: * To reduce generated code (which would hopefully speed up compile times) * To avoid cluttering the code with generated structs not visible to the user * To keep bevy_reflect simple and extensible (these generated structs act as proxies and might not play well with current or future systems) * To avoid additional unsafe blocks * My own misunderstanding of @Davier's code That last point is obviously on me. I misjudged the code to be too unsafe and unable to handle variant swapping (which it probably could) when I was rebasing it. Looking over it again when writing up this whole section, I see that it was actually a pretty clever way of handling variant representation. #### Benefits of All-in-One As stated before, the current implementation uses an all-in-one approach. All variants are capable of containing fields as far as `Enum` is concerned. This provides a few benefits that the alternatives do not (reduced indirection, safer code, etc.). The biggest benefit, though, is direct field access. Rather than forcing users to have to go through pattern matching, we grant direct access to the fields contained by the current variant. The reason we can do this is because all of the pattern matching happens internally. Getting the field at index `2` will automatically return `Some(...)` for the current variant if it has a field at that index or `None` if it doesn't (or can't). This could be useful for scenarios where the variant has already been verified or just set/swapped (or even where the type of variant doesn't matter): ```rust let dyn_enum: &mut dyn Enum = &mut Foo::Bar {value: 123}; // We know it's the `Bar` variant let field = dyn_enum.field("value").unwrap(); ``` Reflection is not a type-safe abstraction— almost every return value is wrapped in `Option<...>`. There are plenty of places to check and recheck that a value is what Reflect says it is. Forcing users to have to go through `match` each time they want to access a field might just be an extra step among dozens of other verification processes. Some might disagree, but ultimately, my view is that the benefit here is an improvement to the ergonomics and usability of reflected enums. </details> --- ## Changelog ### Added * Added `Enum` trait * Added `Enum` impl to `Reflect` derive macro * Added `DynamicEnum` struct * Added `DynamicVariant` * Added `EnumInfo` * Added `VariantInfo` * Added `StructVariantInfo` * Added `TupleVariantInfo` * Added `UnitVariantInfo` * Added serializtion/deserialization support for enums * Added `EnumSerializer` * Added `VariantType` * Added `VariantFieldIter` * Added `VariantField` * Added `enum_partial_eq(...)` * Added `enum_hash(...)` ### Changed * `Option<T>` now implements `Enum` * `bevy_window` now depends on `bevy_reflect` * Implemented `Reflect` and `FromReflect` for `WindowId` * Derive `FromReflect` on `PerspectiveProjection` * Derive `FromReflect` on `OrthographicProjection` * Derive `FromReflect` on `WindowOrigin` * Derive `FromReflect` on `ScalingMode` * Derive `FromReflect` on `DepthCalculation` ## Migration Guide * Enums no longer need to be treated as values and usages of `#[reflect_value(...)]` can be removed or replaced by `#[reflect(...)]` * Enums (including `Option<T>`) now take a different format when serializing. The format is described above, but this may cause issues for existing scenes that make use of enums. --- Also shout out to @nicopap for helping clean up some of the code here! It's a big feature so help like this is really appreciated! Co-authored-by: Gino Valente <gino.valente.code@gmail.com>
2022-08-02 22:14:41 +00:00
})
})
.fold(
utility::ResultSifter::default(),
utility::ResultSifter::fold,
);
bevy_reflect_derive: Tidying up the code (#4712) # Objective The `bevy_reflect_derive` crate is not the cleanest or easiest to follow/maintain. The `lib.rs` file is especially difficult with over 1000 lines of code written in a confusing order. This is just a result of growth within the crate and it would be nice to clean it up for future work. ## Solution Split `bevy_reflect_derive` into many more submodules. The submodules include: * `container_attributes` - Code relating to container attributes * `derive_data` - Code relating to reflection-based derive metadata * `field_attributes` - Code relating to field attributes * `impls` - Code containing actual reflection implementations * `reflect_value` - Code relating to reflection-based value metadata * `registration` - Code relating to type registration * `utility` - General-purpose utility functions This leaves the `lib.rs` file to contain only the public macros, making it much easier to digest (and fewer than 200 lines). By breaking up the code into smaller modules, we make it easier for future contributors to find the code they're looking for or identify which module best fits their own additions. ### Metadata Structs This cleanup also adds two big metadata structs: `ReflectFieldAttr` and `ReflectDeriveData`. The former is used to store all attributes for a struct field (if any). The latter is used to store all metadata for struct-based derive inputs. Both significantly reduce code duplication and make editing these macros much simpler. The tradeoff is that we may collect more metadata than needed. However, this is usually a small thing (such as checking for attributes when they're not really needed or creating a `ReflectFieldAttr` for every field regardless of whether they actually have an attribute). We could try to remove these tradeoffs and squeeze some more performance out, but doing so might come at the cost of developer experience. Personally, I think it's much nicer to create a `ReflectFieldAttr` for every field since it means I don't have to do two `Option` checks. Others may disagree, though, and so we can discuss changing this either in this PR or in a future one. ### Out of Scope _Some_ documentation has been added or improved, but ultimately good docs are probably best saved for a dedicated PR. ## 🔍 Focus Points (for reviewers) I know it's a lot to sift through, so here is a list of **key points for reviewers**: - The following files contain code that was mostly just relocated: - `reflect_value.rs` - `registration.rs` - `container_attributes.rs` was also mostly moved but features some general cleanup (reducing nesting, removing hardcoded strings, etc.) and lots of doc comments - Most impl logic was moved from `lib.rs` to `impls.rs`, but they have been significantly modified to use the new `ReflectDeriveData` metadata struct in order to reduce duplication. - `derive_data.rs` and `field_attributes.rs` contain almost entirely new code and should probably be given the most attention. - Likewise, `from_reflect.rs` saw major changes using `ReflectDeriveData` so it should also be given focus. - There was no change to the `lib.rs` exports so the end-user API should be the same. ## Prior Work This task was initially tackled by @NathanSWard in #2377 (which was closed in favor of this PR), so hats off to them for beating me to the punch by nearly a year! --- ## Changelog * **[INTERNAL]** Split `bevy_reflect_derive` into smaller submodules * **[INTERNAL]** Add `ReflectFieldAttr` * **[INTERNAL]** Add `ReflectDeriveData` * Add `BevyManifest::get_path_direct()` method (`bevy_macro_utils`) Co-authored-by: MrGVSV <49806985+MrGVSV@users.noreply.github.com>
2022-05-12 19:43:23 +00:00
bevy_reflect: Reflect enums (#4761) # Objective > This is a revival of #1347. Credit for the original PR should go to @Davier. Currently, enums are treated as `ReflectRef::Value` types by `bevy_reflect`. Obviously, there needs to be better a better representation for enums using the reflection API. ## Solution Based on prior work from @Davier, an `Enum` trait has been added as well as the ability to automatically implement it via the `Reflect` derive macro. This allows enums to be expressed dynamically: ```rust #[derive(Reflect)] enum Foo { A, B(usize), C { value: f32 }, } let mut foo = Foo::B(123); assert_eq!("B", foo.variant_name()); assert_eq!(1, foo.field_len()); let new_value = DynamicEnum::from(Foo::C { value: 1.23 }); foo.apply(&new_value); assert_eq!(Foo::C{value: 1.23}, foo); ``` ### Features #### Derive Macro Use the `#[derive(Reflect)]` macro to automatically implement the `Enum` trait for enum definitions. Optionally, you can use `#[reflect(ignore)]` with both variants and variant fields, just like you can with structs. These ignored items will not be considered as part of the reflection and cannot be accessed via reflection. ```rust #[derive(Reflect)] enum TestEnum { A, // Uncomment to ignore all of `B` // #[reflect(ignore)] B(usize), C { // Uncomment to ignore only field `foo` of `C` // #[reflect(ignore)] foo: f32, bar: bool, }, } ``` #### Dynamic Enums Enums may be created/represented dynamically via the `DynamicEnum` struct. The main purpose of this struct is to allow enums to be deserialized into a partial state and to allow dynamic patching. In order to ensure conversion from a `DynamicEnum` to a concrete enum type goes smoothly, be sure to add `FromReflect` to your derive macro. ```rust let mut value = TestEnum::A; // Create from a concrete instance let dyn_enum = DynamicEnum::from(TestEnum::B(123)); value.apply(&dyn_enum); assert_eq!(TestEnum::B(123), value); // Create a purely dynamic instance let dyn_enum = DynamicEnum::new("TestEnum", "A", ()); value.apply(&dyn_enum); assert_eq!(TestEnum::A, value); ``` #### Variants An enum value is always represented as one of its variants— never the enum in its entirety. ```rust let value = TestEnum::A; assert_eq!("A", value.variant_name()); // Since we are using the `A` variant, we cannot also be the `B` variant assert_ne!("B", value.variant_name()); ``` All variant types are representable within the `Enum` trait: unit, struct, and tuple. You can get the current type like: ```rust match value.variant_type() { VariantType::Unit => println!("A unit variant!"), VariantType::Struct => println!("A struct variant!"), VariantType::Tuple => println!("A tuple variant!"), } ``` > Notice that they don't contain any values representing the fields. These are purely tags. If a variant has them, you can access the fields as well: ```rust let mut value = TestEnum::C { foo: 1.23, bar: false }; // Read/write specific fields *value.field_mut("bar").unwrap() = true; // Iterate over the entire collection of fields for field in value.iter_fields() { println!("{} = {:?}", field.name(), field.value()); } ``` #### Variant Swapping It might seem odd to group all variant types under a single trait (why allow `iter_fields` on a unit variant?), but the reason this was done ~~is to easily allow *variant swapping*.~~ As I was recently drafting up the **Design Decisions** section, I discovered that other solutions could have been made to work with variant swapping. So while there are reasons to keep the all-in-one approach, variant swapping is _not_ one of them. ```rust let mut value: Box<dyn Enum> = Box::new(TestEnum::A); value.set(Box::new(TestEnum::B(123))).unwrap(); ``` #### Serialization Enums can be serialized and deserialized via reflection without needing to implement `Serialize` or `Deserialize` themselves (which can save thousands of lines of generated code). Below are the ways an enum can be serialized. > Note, like the rest of reflection-based serialization, the order of the keys in these representations is important! ##### Unit ```json { "type": "my_crate::TestEnum", "enum": { "variant": "A" } } ``` ##### Tuple ```json { "type": "my_crate::TestEnum", "enum": { "variant": "B", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` <details> <summary>Effects on Option</summary> This ends up making `Option` look a little ugly: ```json { "type": "core::option::Option<usize>", "enum": { "variant": "Some", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` </details> ##### Struct ```json { "type": "my_crate::TestEnum", "enum": { "variant": "C", "struct": { "foo": { "type": "f32", "value": 1.23 }, "bar": { "type": "bool", "value": false } } } } ``` ## Design Decisions <details> <summary><strong>View Section</strong></summary> This section is here to provide some context for why certain decisions were made for this PR, alternatives that could have been used instead, and what could be improved upon in the future. ### Variant Representation One of the biggest decisions was to decide on how to represent variants. The current design uses a "all-in-one" design where unit, tuple, and struct variants are all simultaneously represented by the `Enum` trait. This is not the only way it could have been done, though. #### Alternatives ##### 1. Variant Traits One way of representing variants would be to define traits for each variant, implementing them whenever an enum featured at least one instance of them. This would allow us to define variants like: ```rust pub trait Enum: Reflect { fn variant(&self) -> Variant; } pub enum Variant<'a> { Unit, Tuple(&'a dyn TupleVariant), Struct(&'a dyn StructVariant), } pub trait TupleVariant { fn field_len(&self) -> usize; // ... } ``` And then do things like: ```rust fn get_tuple_len(foo: &dyn Enum) -> usize { match foo.variant() { Variant::Tuple(tuple) => tuple.field_len(), _ => panic!("not a tuple variant!") } } ``` The reason this PR does not go with this approach is because of the fact that variants are not separate types. In other words, we cannot implement traits on specific variants— these cover the *entire* enum. This means we offer an easy footgun: ```rust let foo: Option<i32> = None; let my_enum = Box::new(foo) as Box<dyn TupleVariant>; ``` Here, `my_enum` contains `foo`, which is a unit variant. However, since we need to implement `TupleVariant` for `Option` as a whole, it's possible to perform such a cast. This is obviously wrong, but could easily go unnoticed. So unfortunately, this makes it not a good candidate for representing variants. ##### 2. Variant Structs To get around the issue of traits necessarily needing to apply to both the enum and its variants, we could instead use structs that are created on a per-variant basis. This was also considered but was ultimately [[removed](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c)](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c) due to concerns about allocations. Each variant struct would probably look something like: ```rust pub trait Enum: Reflect { fn variant_mut(&self) -> VariantMut; } pub enum VariantMut<'a> { Unit, Tuple(TupleVariantMut), Struct(StructVariantMut), } struct StructVariantMut<'a> { fields: Vec<&'a mut dyn Reflect>, field_indices: HashMap<Cow<'static, str>, usize> } ``` This allows us to isolate struct variants into their own defined struct and define methods specifically for their use. It also prevents users from casting to it since it's not a trait. However, this is not an optimal solution. Both `field_indices` and `fields` will require an allocation (remember, a `Box<[T]>` still requires a `Vec<T>` in order to be constructed). This *might* be a problem if called frequently enough. ##### 3. Generated Structs The original design, implemented by @Davier, instead generates structs specific for each variant. So if we had a variant path like `Foo::Bar`, we'd generate a struct named `FooBarWrapper`. This would be newtyped around the original enum and forward tuple or struct methods to the enum with the chosen variant. Because it involved using the `Tuple` and `Struct` traits (which are also both bound on `Reflect`), this meant a bit more code had to be generated. For a single struct variant with one field, the generated code amounted to ~110LoC. However, each new field added to that variant only added ~6 more LoC. In order to work properly, the enum had to be transmuted to the generated struct: ```rust fn variant(&self) -> crate::EnumVariant<'_> { match self { Foo::Bar {value: i32} => { let wrapper_ref = unsafe { std::mem::transmute::<&Self, &FooBarWrapper>(self) }; crate::EnumVariant::Struct(wrapper_ref as &dyn crate::Struct) } } } ``` This works because `FooBarWrapper` is defined as `repr(transparent)`. Out of all the alternatives, this would probably be the one most likely to be used again in the future. The reasons for why this PR did not continue to use it was because: * To reduce generated code (which would hopefully speed up compile times) * To avoid cluttering the code with generated structs not visible to the user * To keep bevy_reflect simple and extensible (these generated structs act as proxies and might not play well with current or future systems) * To avoid additional unsafe blocks * My own misunderstanding of @Davier's code That last point is obviously on me. I misjudged the code to be too unsafe and unable to handle variant swapping (which it probably could) when I was rebasing it. Looking over it again when writing up this whole section, I see that it was actually a pretty clever way of handling variant representation. #### Benefits of All-in-One As stated before, the current implementation uses an all-in-one approach. All variants are capable of containing fields as far as `Enum` is concerned. This provides a few benefits that the alternatives do not (reduced indirection, safer code, etc.). The biggest benefit, though, is direct field access. Rather than forcing users to have to go through pattern matching, we grant direct access to the fields contained by the current variant. The reason we can do this is because all of the pattern matching happens internally. Getting the field at index `2` will automatically return `Some(...)` for the current variant if it has a field at that index or `None` if it doesn't (or can't). This could be useful for scenarios where the variant has already been verified or just set/swapped (or even where the type of variant doesn't matter): ```rust let dyn_enum: &mut dyn Enum = &mut Foo::Bar {value: 123}; // We know it's the `Bar` variant let field = dyn_enum.field("value").unwrap(); ``` Reflection is not a type-safe abstraction— almost every return value is wrapped in `Option<...>`. There are plenty of places to check and recheck that a value is what Reflect says it is. Forcing users to have to go through `match` each time they want to access a field might just be an extra step among dozens of other verification processes. Some might disagree, but ultimately, my view is that the benefit here is an improvement to the ergonomics and usability of reflected enums. </details> --- ## Changelog ### Added * Added `Enum` trait * Added `Enum` impl to `Reflect` derive macro * Added `DynamicEnum` struct * Added `DynamicVariant` * Added `EnumInfo` * Added `VariantInfo` * Added `StructVariantInfo` * Added `TupleVariantInfo` * Added `UnitVariantInfo` * Added serializtion/deserialization support for enums * Added `EnumSerializer` * Added `VariantType` * Added `VariantFieldIter` * Added `VariantField` * Added `enum_partial_eq(...)` * Added `enum_hash(...)` ### Changed * `Option<T>` now implements `Enum` * `bevy_window` now depends on `bevy_reflect` * Implemented `Reflect` and `FromReflect` for `WindowId` * Derive `FromReflect` on `PerspectiveProjection` * Derive `FromReflect` on `OrthographicProjection` * Derive `FromReflect` on `WindowOrigin` * Derive `FromReflect` on `ScalingMode` * Derive `FromReflect` on `DepthCalculation` ## Migration Guide * Enums no longer need to be treated as values and usages of `#[reflect_value(...)]` can be removed or replaced by `#[reflect(...)]` * Enums (including `Option<T>`) now take a different format when serializing. The format is described above, but this may cause issues for existing scenes that make use of enums. --- Also shout out to @nicopap for helping clean up some of the code here! It's a big feature so help like this is really appreciated! Co-authored-by: Gino Valente <gino.valente.code@gmail.com>
2022-08-02 22:14:41 +00:00
sifter.finish()
bevy_reflect_derive: Tidying up the code (#4712) # Objective The `bevy_reflect_derive` crate is not the cleanest or easiest to follow/maintain. The `lib.rs` file is especially difficult with over 1000 lines of code written in a confusing order. This is just a result of growth within the crate and it would be nice to clean it up for future work. ## Solution Split `bevy_reflect_derive` into many more submodules. The submodules include: * `container_attributes` - Code relating to container attributes * `derive_data` - Code relating to reflection-based derive metadata * `field_attributes` - Code relating to field attributes * `impls` - Code containing actual reflection implementations * `reflect_value` - Code relating to reflection-based value metadata * `registration` - Code relating to type registration * `utility` - General-purpose utility functions This leaves the `lib.rs` file to contain only the public macros, making it much easier to digest (and fewer than 200 lines). By breaking up the code into smaller modules, we make it easier for future contributors to find the code they're looking for or identify which module best fits their own additions. ### Metadata Structs This cleanup also adds two big metadata structs: `ReflectFieldAttr` and `ReflectDeriveData`. The former is used to store all attributes for a struct field (if any). The latter is used to store all metadata for struct-based derive inputs. Both significantly reduce code duplication and make editing these macros much simpler. The tradeoff is that we may collect more metadata than needed. However, this is usually a small thing (such as checking for attributes when they're not really needed or creating a `ReflectFieldAttr` for every field regardless of whether they actually have an attribute). We could try to remove these tradeoffs and squeeze some more performance out, but doing so might come at the cost of developer experience. Personally, I think it's much nicer to create a `ReflectFieldAttr` for every field since it means I don't have to do two `Option` checks. Others may disagree, though, and so we can discuss changing this either in this PR or in a future one. ### Out of Scope _Some_ documentation has been added or improved, but ultimately good docs are probably best saved for a dedicated PR. ## 🔍 Focus Points (for reviewers) I know it's a lot to sift through, so here is a list of **key points for reviewers**: - The following files contain code that was mostly just relocated: - `reflect_value.rs` - `registration.rs` - `container_attributes.rs` was also mostly moved but features some general cleanup (reducing nesting, removing hardcoded strings, etc.) and lots of doc comments - Most impl logic was moved from `lib.rs` to `impls.rs`, but they have been significantly modified to use the new `ReflectDeriveData` metadata struct in order to reduce duplication. - `derive_data.rs` and `field_attributes.rs` contain almost entirely new code and should probably be given the most attention. - Likewise, `from_reflect.rs` saw major changes using `ReflectDeriveData` so it should also be given focus. - There was no change to the `lib.rs` exports so the end-user API should be the same. ## Prior Work This task was initially tackled by @NathanSWard in #2377 (which was closed in favor of this PR), so hats off to them for beating me to the punch by nearly a year! --- ## Changelog * **[INTERNAL]** Split `bevy_reflect_derive` into smaller submodules * **[INTERNAL]** Add `ReflectFieldAttr` * **[INTERNAL]** Add `ReflectDeriveData` * Add `BevyManifest::get_path_direct()` method (`bevy_macro_utils`) Co-authored-by: MrGVSV <49806985+MrGVSV@users.noreply.github.com>
2022-05-12 19:43:23 +00:00
}
bevy_reflect: Reflect enums (#4761) # Objective > This is a revival of #1347. Credit for the original PR should go to @Davier. Currently, enums are treated as `ReflectRef::Value` types by `bevy_reflect`. Obviously, there needs to be better a better representation for enums using the reflection API. ## Solution Based on prior work from @Davier, an `Enum` trait has been added as well as the ability to automatically implement it via the `Reflect` derive macro. This allows enums to be expressed dynamically: ```rust #[derive(Reflect)] enum Foo { A, B(usize), C { value: f32 }, } let mut foo = Foo::B(123); assert_eq!("B", foo.variant_name()); assert_eq!(1, foo.field_len()); let new_value = DynamicEnum::from(Foo::C { value: 1.23 }); foo.apply(&new_value); assert_eq!(Foo::C{value: 1.23}, foo); ``` ### Features #### Derive Macro Use the `#[derive(Reflect)]` macro to automatically implement the `Enum` trait for enum definitions. Optionally, you can use `#[reflect(ignore)]` with both variants and variant fields, just like you can with structs. These ignored items will not be considered as part of the reflection and cannot be accessed via reflection. ```rust #[derive(Reflect)] enum TestEnum { A, // Uncomment to ignore all of `B` // #[reflect(ignore)] B(usize), C { // Uncomment to ignore only field `foo` of `C` // #[reflect(ignore)] foo: f32, bar: bool, }, } ``` #### Dynamic Enums Enums may be created/represented dynamically via the `DynamicEnum` struct. The main purpose of this struct is to allow enums to be deserialized into a partial state and to allow dynamic patching. In order to ensure conversion from a `DynamicEnum` to a concrete enum type goes smoothly, be sure to add `FromReflect` to your derive macro. ```rust let mut value = TestEnum::A; // Create from a concrete instance let dyn_enum = DynamicEnum::from(TestEnum::B(123)); value.apply(&dyn_enum); assert_eq!(TestEnum::B(123), value); // Create a purely dynamic instance let dyn_enum = DynamicEnum::new("TestEnum", "A", ()); value.apply(&dyn_enum); assert_eq!(TestEnum::A, value); ``` #### Variants An enum value is always represented as one of its variants— never the enum in its entirety. ```rust let value = TestEnum::A; assert_eq!("A", value.variant_name()); // Since we are using the `A` variant, we cannot also be the `B` variant assert_ne!("B", value.variant_name()); ``` All variant types are representable within the `Enum` trait: unit, struct, and tuple. You can get the current type like: ```rust match value.variant_type() { VariantType::Unit => println!("A unit variant!"), VariantType::Struct => println!("A struct variant!"), VariantType::Tuple => println!("A tuple variant!"), } ``` > Notice that they don't contain any values representing the fields. These are purely tags. If a variant has them, you can access the fields as well: ```rust let mut value = TestEnum::C { foo: 1.23, bar: false }; // Read/write specific fields *value.field_mut("bar").unwrap() = true; // Iterate over the entire collection of fields for field in value.iter_fields() { println!("{} = {:?}", field.name(), field.value()); } ``` #### Variant Swapping It might seem odd to group all variant types under a single trait (why allow `iter_fields` on a unit variant?), but the reason this was done ~~is to easily allow *variant swapping*.~~ As I was recently drafting up the **Design Decisions** section, I discovered that other solutions could have been made to work with variant swapping. So while there are reasons to keep the all-in-one approach, variant swapping is _not_ one of them. ```rust let mut value: Box<dyn Enum> = Box::new(TestEnum::A); value.set(Box::new(TestEnum::B(123))).unwrap(); ``` #### Serialization Enums can be serialized and deserialized via reflection without needing to implement `Serialize` or `Deserialize` themselves (which can save thousands of lines of generated code). Below are the ways an enum can be serialized. > Note, like the rest of reflection-based serialization, the order of the keys in these representations is important! ##### Unit ```json { "type": "my_crate::TestEnum", "enum": { "variant": "A" } } ``` ##### Tuple ```json { "type": "my_crate::TestEnum", "enum": { "variant": "B", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` <details> <summary>Effects on Option</summary> This ends up making `Option` look a little ugly: ```json { "type": "core::option::Option<usize>", "enum": { "variant": "Some", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` </details> ##### Struct ```json { "type": "my_crate::TestEnum", "enum": { "variant": "C", "struct": { "foo": { "type": "f32", "value": 1.23 }, "bar": { "type": "bool", "value": false } } } } ``` ## Design Decisions <details> <summary><strong>View Section</strong></summary> This section is here to provide some context for why certain decisions were made for this PR, alternatives that could have been used instead, and what could be improved upon in the future. ### Variant Representation One of the biggest decisions was to decide on how to represent variants. The current design uses a "all-in-one" design where unit, tuple, and struct variants are all simultaneously represented by the `Enum` trait. This is not the only way it could have been done, though. #### Alternatives ##### 1. Variant Traits One way of representing variants would be to define traits for each variant, implementing them whenever an enum featured at least one instance of them. This would allow us to define variants like: ```rust pub trait Enum: Reflect { fn variant(&self) -> Variant; } pub enum Variant<'a> { Unit, Tuple(&'a dyn TupleVariant), Struct(&'a dyn StructVariant), } pub trait TupleVariant { fn field_len(&self) -> usize; // ... } ``` And then do things like: ```rust fn get_tuple_len(foo: &dyn Enum) -> usize { match foo.variant() { Variant::Tuple(tuple) => tuple.field_len(), _ => panic!("not a tuple variant!") } } ``` The reason this PR does not go with this approach is because of the fact that variants are not separate types. In other words, we cannot implement traits on specific variants— these cover the *entire* enum. This means we offer an easy footgun: ```rust let foo: Option<i32> = None; let my_enum = Box::new(foo) as Box<dyn TupleVariant>; ``` Here, `my_enum` contains `foo`, which is a unit variant. However, since we need to implement `TupleVariant` for `Option` as a whole, it's possible to perform such a cast. This is obviously wrong, but could easily go unnoticed. So unfortunately, this makes it not a good candidate for representing variants. ##### 2. Variant Structs To get around the issue of traits necessarily needing to apply to both the enum and its variants, we could instead use structs that are created on a per-variant basis. This was also considered but was ultimately [[removed](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c)](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c) due to concerns about allocations. Each variant struct would probably look something like: ```rust pub trait Enum: Reflect { fn variant_mut(&self) -> VariantMut; } pub enum VariantMut<'a> { Unit, Tuple(TupleVariantMut), Struct(StructVariantMut), } struct StructVariantMut<'a> { fields: Vec<&'a mut dyn Reflect>, field_indices: HashMap<Cow<'static, str>, usize> } ``` This allows us to isolate struct variants into their own defined struct and define methods specifically for their use. It also prevents users from casting to it since it's not a trait. However, this is not an optimal solution. Both `field_indices` and `fields` will require an allocation (remember, a `Box<[T]>` still requires a `Vec<T>` in order to be constructed). This *might* be a problem if called frequently enough. ##### 3. Generated Structs The original design, implemented by @Davier, instead generates structs specific for each variant. So if we had a variant path like `Foo::Bar`, we'd generate a struct named `FooBarWrapper`. This would be newtyped around the original enum and forward tuple or struct methods to the enum with the chosen variant. Because it involved using the `Tuple` and `Struct` traits (which are also both bound on `Reflect`), this meant a bit more code had to be generated. For a single struct variant with one field, the generated code amounted to ~110LoC. However, each new field added to that variant only added ~6 more LoC. In order to work properly, the enum had to be transmuted to the generated struct: ```rust fn variant(&self) -> crate::EnumVariant<'_> { match self { Foo::Bar {value: i32} => { let wrapper_ref = unsafe { std::mem::transmute::<&Self, &FooBarWrapper>(self) }; crate::EnumVariant::Struct(wrapper_ref as &dyn crate::Struct) } } } ``` This works because `FooBarWrapper` is defined as `repr(transparent)`. Out of all the alternatives, this would probably be the one most likely to be used again in the future. The reasons for why this PR did not continue to use it was because: * To reduce generated code (which would hopefully speed up compile times) * To avoid cluttering the code with generated structs not visible to the user * To keep bevy_reflect simple and extensible (these generated structs act as proxies and might not play well with current or future systems) * To avoid additional unsafe blocks * My own misunderstanding of @Davier's code That last point is obviously on me. I misjudged the code to be too unsafe and unable to handle variant swapping (which it probably could) when I was rebasing it. Looking over it again when writing up this whole section, I see that it was actually a pretty clever way of handling variant representation. #### Benefits of All-in-One As stated before, the current implementation uses an all-in-one approach. All variants are capable of containing fields as far as `Enum` is concerned. This provides a few benefits that the alternatives do not (reduced indirection, safer code, etc.). The biggest benefit, though, is direct field access. Rather than forcing users to have to go through pattern matching, we grant direct access to the fields contained by the current variant. The reason we can do this is because all of the pattern matching happens internally. Getting the field at index `2` will automatically return `Some(...)` for the current variant if it has a field at that index or `None` if it doesn't (or can't). This could be useful for scenarios where the variant has already been verified or just set/swapped (or even where the type of variant doesn't matter): ```rust let dyn_enum: &mut dyn Enum = &mut Foo::Bar {value: 123}; // We know it's the `Bar` variant let field = dyn_enum.field("value").unwrap(); ``` Reflection is not a type-safe abstraction— almost every return value is wrapped in `Option<...>`. There are plenty of places to check and recheck that a value is what Reflect says it is. Forcing users to have to go through `match` each time they want to access a field might just be an extra step among dozens of other verification processes. Some might disagree, but ultimately, my view is that the benefit here is an improvement to the ergonomics and usability of reflected enums. </details> --- ## Changelog ### Added * Added `Enum` trait * Added `Enum` impl to `Reflect` derive macro * Added `DynamicEnum` struct * Added `DynamicVariant` * Added `EnumInfo` * Added `VariantInfo` * Added `StructVariantInfo` * Added `TupleVariantInfo` * Added `UnitVariantInfo` * Added serializtion/deserialization support for enums * Added `EnumSerializer` * Added `VariantType` * Added `VariantFieldIter` * Added `VariantField` * Added `enum_partial_eq(...)` * Added `enum_hash(...)` ### Changed * `Option<T>` now implements `Enum` * `bevy_window` now depends on `bevy_reflect` * Implemented `Reflect` and `FromReflect` for `WindowId` * Derive `FromReflect` on `PerspectiveProjection` * Derive `FromReflect` on `OrthographicProjection` * Derive `FromReflect` on `WindowOrigin` * Derive `FromReflect` on `ScalingMode` * Derive `FromReflect` on `DepthCalculation` ## Migration Guide * Enums no longer need to be treated as values and usages of `#[reflect_value(...)]` can be removed or replaced by `#[reflect(...)]` * Enums (including `Option<T>`) now take a different format when serializing. The format is described above, but this may cause issues for existing scenes that make use of enums. --- Also shout out to @nicopap for helping clean up some of the code here! It's a big feature so help like this is really appreciated! Co-authored-by: Gino Valente <gino.valente.code@gmail.com>
2022-08-02 22:14:41 +00:00
}
bevy_reflect_derive: Tidying up the code (#4712) # Objective The `bevy_reflect_derive` crate is not the cleanest or easiest to follow/maintain. The `lib.rs` file is especially difficult with over 1000 lines of code written in a confusing order. This is just a result of growth within the crate and it would be nice to clean it up for future work. ## Solution Split `bevy_reflect_derive` into many more submodules. The submodules include: * `container_attributes` - Code relating to container attributes * `derive_data` - Code relating to reflection-based derive metadata * `field_attributes` - Code relating to field attributes * `impls` - Code containing actual reflection implementations * `reflect_value` - Code relating to reflection-based value metadata * `registration` - Code relating to type registration * `utility` - General-purpose utility functions This leaves the `lib.rs` file to contain only the public macros, making it much easier to digest (and fewer than 200 lines). By breaking up the code into smaller modules, we make it easier for future contributors to find the code they're looking for or identify which module best fits their own additions. ### Metadata Structs This cleanup also adds two big metadata structs: `ReflectFieldAttr` and `ReflectDeriveData`. The former is used to store all attributes for a struct field (if any). The latter is used to store all metadata for struct-based derive inputs. Both significantly reduce code duplication and make editing these macros much simpler. The tradeoff is that we may collect more metadata than needed. However, this is usually a small thing (such as checking for attributes when they're not really needed or creating a `ReflectFieldAttr` for every field regardless of whether they actually have an attribute). We could try to remove these tradeoffs and squeeze some more performance out, but doing so might come at the cost of developer experience. Personally, I think it's much nicer to create a `ReflectFieldAttr` for every field since it means I don't have to do two `Option` checks. Others may disagree, though, and so we can discuss changing this either in this PR or in a future one. ### Out of Scope _Some_ documentation has been added or improved, but ultimately good docs are probably best saved for a dedicated PR. ## 🔍 Focus Points (for reviewers) I know it's a lot to sift through, so here is a list of **key points for reviewers**: - The following files contain code that was mostly just relocated: - `reflect_value.rs` - `registration.rs` - `container_attributes.rs` was also mostly moved but features some general cleanup (reducing nesting, removing hardcoded strings, etc.) and lots of doc comments - Most impl logic was moved from `lib.rs` to `impls.rs`, but they have been significantly modified to use the new `ReflectDeriveData` metadata struct in order to reduce duplication. - `derive_data.rs` and `field_attributes.rs` contain almost entirely new code and should probably be given the most attention. - Likewise, `from_reflect.rs` saw major changes using `ReflectDeriveData` so it should also be given focus. - There was no change to the `lib.rs` exports so the end-user API should be the same. ## Prior Work This task was initially tackled by @NathanSWard in #2377 (which was closed in favor of this PR), so hats off to them for beating me to the punch by nearly a year! --- ## Changelog * **[INTERNAL]** Split `bevy_reflect_derive` into smaller submodules * **[INTERNAL]** Add `ReflectFieldAttr` * **[INTERNAL]** Add `ReflectDeriveData` * Add `BevyManifest::get_path_direct()` method (`bevy_macro_utils`) Co-authored-by: MrGVSV <49806985+MrGVSV@users.noreply.github.com>
2022-05-12 19:43:23 +00:00
bevy_reflect: Reflect enums (#4761) # Objective > This is a revival of #1347. Credit for the original PR should go to @Davier. Currently, enums are treated as `ReflectRef::Value` types by `bevy_reflect`. Obviously, there needs to be better a better representation for enums using the reflection API. ## Solution Based on prior work from @Davier, an `Enum` trait has been added as well as the ability to automatically implement it via the `Reflect` derive macro. This allows enums to be expressed dynamically: ```rust #[derive(Reflect)] enum Foo { A, B(usize), C { value: f32 }, } let mut foo = Foo::B(123); assert_eq!("B", foo.variant_name()); assert_eq!(1, foo.field_len()); let new_value = DynamicEnum::from(Foo::C { value: 1.23 }); foo.apply(&new_value); assert_eq!(Foo::C{value: 1.23}, foo); ``` ### Features #### Derive Macro Use the `#[derive(Reflect)]` macro to automatically implement the `Enum` trait for enum definitions. Optionally, you can use `#[reflect(ignore)]` with both variants and variant fields, just like you can with structs. These ignored items will not be considered as part of the reflection and cannot be accessed via reflection. ```rust #[derive(Reflect)] enum TestEnum { A, // Uncomment to ignore all of `B` // #[reflect(ignore)] B(usize), C { // Uncomment to ignore only field `foo` of `C` // #[reflect(ignore)] foo: f32, bar: bool, }, } ``` #### Dynamic Enums Enums may be created/represented dynamically via the `DynamicEnum` struct. The main purpose of this struct is to allow enums to be deserialized into a partial state and to allow dynamic patching. In order to ensure conversion from a `DynamicEnum` to a concrete enum type goes smoothly, be sure to add `FromReflect` to your derive macro. ```rust let mut value = TestEnum::A; // Create from a concrete instance let dyn_enum = DynamicEnum::from(TestEnum::B(123)); value.apply(&dyn_enum); assert_eq!(TestEnum::B(123), value); // Create a purely dynamic instance let dyn_enum = DynamicEnum::new("TestEnum", "A", ()); value.apply(&dyn_enum); assert_eq!(TestEnum::A, value); ``` #### Variants An enum value is always represented as one of its variants— never the enum in its entirety. ```rust let value = TestEnum::A; assert_eq!("A", value.variant_name()); // Since we are using the `A` variant, we cannot also be the `B` variant assert_ne!("B", value.variant_name()); ``` All variant types are representable within the `Enum` trait: unit, struct, and tuple. You can get the current type like: ```rust match value.variant_type() { VariantType::Unit => println!("A unit variant!"), VariantType::Struct => println!("A struct variant!"), VariantType::Tuple => println!("A tuple variant!"), } ``` > Notice that they don't contain any values representing the fields. These are purely tags. If a variant has them, you can access the fields as well: ```rust let mut value = TestEnum::C { foo: 1.23, bar: false }; // Read/write specific fields *value.field_mut("bar").unwrap() = true; // Iterate over the entire collection of fields for field in value.iter_fields() { println!("{} = {:?}", field.name(), field.value()); } ``` #### Variant Swapping It might seem odd to group all variant types under a single trait (why allow `iter_fields` on a unit variant?), but the reason this was done ~~is to easily allow *variant swapping*.~~ As I was recently drafting up the **Design Decisions** section, I discovered that other solutions could have been made to work with variant swapping. So while there are reasons to keep the all-in-one approach, variant swapping is _not_ one of them. ```rust let mut value: Box<dyn Enum> = Box::new(TestEnum::A); value.set(Box::new(TestEnum::B(123))).unwrap(); ``` #### Serialization Enums can be serialized and deserialized via reflection without needing to implement `Serialize` or `Deserialize` themselves (which can save thousands of lines of generated code). Below are the ways an enum can be serialized. > Note, like the rest of reflection-based serialization, the order of the keys in these representations is important! ##### Unit ```json { "type": "my_crate::TestEnum", "enum": { "variant": "A" } } ``` ##### Tuple ```json { "type": "my_crate::TestEnum", "enum": { "variant": "B", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` <details> <summary>Effects on Option</summary> This ends up making `Option` look a little ugly: ```json { "type": "core::option::Option<usize>", "enum": { "variant": "Some", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` </details> ##### Struct ```json { "type": "my_crate::TestEnum", "enum": { "variant": "C", "struct": { "foo": { "type": "f32", "value": 1.23 }, "bar": { "type": "bool", "value": false } } } } ``` ## Design Decisions <details> <summary><strong>View Section</strong></summary> This section is here to provide some context for why certain decisions were made for this PR, alternatives that could have been used instead, and what could be improved upon in the future. ### Variant Representation One of the biggest decisions was to decide on how to represent variants. The current design uses a "all-in-one" design where unit, tuple, and struct variants are all simultaneously represented by the `Enum` trait. This is not the only way it could have been done, though. #### Alternatives ##### 1. Variant Traits One way of representing variants would be to define traits for each variant, implementing them whenever an enum featured at least one instance of them. This would allow us to define variants like: ```rust pub trait Enum: Reflect { fn variant(&self) -> Variant; } pub enum Variant<'a> { Unit, Tuple(&'a dyn TupleVariant), Struct(&'a dyn StructVariant), } pub trait TupleVariant { fn field_len(&self) -> usize; // ... } ``` And then do things like: ```rust fn get_tuple_len(foo: &dyn Enum) -> usize { match foo.variant() { Variant::Tuple(tuple) => tuple.field_len(), _ => panic!("not a tuple variant!") } } ``` The reason this PR does not go with this approach is because of the fact that variants are not separate types. In other words, we cannot implement traits on specific variants— these cover the *entire* enum. This means we offer an easy footgun: ```rust let foo: Option<i32> = None; let my_enum = Box::new(foo) as Box<dyn TupleVariant>; ``` Here, `my_enum` contains `foo`, which is a unit variant. However, since we need to implement `TupleVariant` for `Option` as a whole, it's possible to perform such a cast. This is obviously wrong, but could easily go unnoticed. So unfortunately, this makes it not a good candidate for representing variants. ##### 2. Variant Structs To get around the issue of traits necessarily needing to apply to both the enum and its variants, we could instead use structs that are created on a per-variant basis. This was also considered but was ultimately [[removed](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c)](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c) due to concerns about allocations. Each variant struct would probably look something like: ```rust pub trait Enum: Reflect { fn variant_mut(&self) -> VariantMut; } pub enum VariantMut<'a> { Unit, Tuple(TupleVariantMut), Struct(StructVariantMut), } struct StructVariantMut<'a> { fields: Vec<&'a mut dyn Reflect>, field_indices: HashMap<Cow<'static, str>, usize> } ``` This allows us to isolate struct variants into their own defined struct and define methods specifically for their use. It also prevents users from casting to it since it's not a trait. However, this is not an optimal solution. Both `field_indices` and `fields` will require an allocation (remember, a `Box<[T]>` still requires a `Vec<T>` in order to be constructed). This *might* be a problem if called frequently enough. ##### 3. Generated Structs The original design, implemented by @Davier, instead generates structs specific for each variant. So if we had a variant path like `Foo::Bar`, we'd generate a struct named `FooBarWrapper`. This would be newtyped around the original enum and forward tuple or struct methods to the enum with the chosen variant. Because it involved using the `Tuple` and `Struct` traits (which are also both bound on `Reflect`), this meant a bit more code had to be generated. For a single struct variant with one field, the generated code amounted to ~110LoC. However, each new field added to that variant only added ~6 more LoC. In order to work properly, the enum had to be transmuted to the generated struct: ```rust fn variant(&self) -> crate::EnumVariant<'_> { match self { Foo::Bar {value: i32} => { let wrapper_ref = unsafe { std::mem::transmute::<&Self, &FooBarWrapper>(self) }; crate::EnumVariant::Struct(wrapper_ref as &dyn crate::Struct) } } } ``` This works because `FooBarWrapper` is defined as `repr(transparent)`. Out of all the alternatives, this would probably be the one most likely to be used again in the future. The reasons for why this PR did not continue to use it was because: * To reduce generated code (which would hopefully speed up compile times) * To avoid cluttering the code with generated structs not visible to the user * To keep bevy_reflect simple and extensible (these generated structs act as proxies and might not play well with current or future systems) * To avoid additional unsafe blocks * My own misunderstanding of @Davier's code That last point is obviously on me. I misjudged the code to be too unsafe and unable to handle variant swapping (which it probably could) when I was rebasing it. Looking over it again when writing up this whole section, I see that it was actually a pretty clever way of handling variant representation. #### Benefits of All-in-One As stated before, the current implementation uses an all-in-one approach. All variants are capable of containing fields as far as `Enum` is concerned. This provides a few benefits that the alternatives do not (reduced indirection, safer code, etc.). The biggest benefit, though, is direct field access. Rather than forcing users to have to go through pattern matching, we grant direct access to the fields contained by the current variant. The reason we can do this is because all of the pattern matching happens internally. Getting the field at index `2` will automatically return `Some(...)` for the current variant if it has a field at that index or `None` if it doesn't (or can't). This could be useful for scenarios where the variant has already been verified or just set/swapped (or even where the type of variant doesn't matter): ```rust let dyn_enum: &mut dyn Enum = &mut Foo::Bar {value: 123}; // We know it's the `Bar` variant let field = dyn_enum.field("value").unwrap(); ``` Reflection is not a type-safe abstraction— almost every return value is wrapped in `Option<...>`. There are plenty of places to check and recheck that a value is what Reflect says it is. Forcing users to have to go through `match` each time they want to access a field might just be an extra step among dozens of other verification processes. Some might disagree, but ultimately, my view is that the benefit here is an improvement to the ergonomics and usability of reflected enums. </details> --- ## Changelog ### Added * Added `Enum` trait * Added `Enum` impl to `Reflect` derive macro * Added `DynamicEnum` struct * Added `DynamicVariant` * Added `EnumInfo` * Added `VariantInfo` * Added `StructVariantInfo` * Added `TupleVariantInfo` * Added `UnitVariantInfo` * Added serializtion/deserialization support for enums * Added `EnumSerializer` * Added `VariantType` * Added `VariantFieldIter` * Added `VariantField` * Added `enum_partial_eq(...)` * Added `enum_hash(...)` ### Changed * `Option<T>` now implements `Enum` * `bevy_window` now depends on `bevy_reflect` * Implemented `Reflect` and `FromReflect` for `WindowId` * Derive `FromReflect` on `PerspectiveProjection` * Derive `FromReflect` on `OrthographicProjection` * Derive `FromReflect` on `WindowOrigin` * Derive `FromReflect` on `ScalingMode` * Derive `FromReflect` on `DepthCalculation` ## Migration Guide * Enums no longer need to be treated as values and usages of `#[reflect_value(...)]` can be removed or replaced by `#[reflect(...)]` * Enums (including `Option<T>`) now take a different format when serializing. The format is described above, but this may cause issues for existing scenes that make use of enums. --- Also shout out to @nicopap for helping clean up some of the code here! It's a big feature so help like this is really appreciated! Co-authored-by: Gino Valente <gino.valente.code@gmail.com>
2022-08-02 22:14:41 +00:00
impl<'a> ReflectMeta<'a> {
bevy_reflect_derive: Clean up attribute logic (#11777) # Objective The code in `bevy_reflect_derive` could use some cleanup. ## Solution Took some of the changes in #11659 to create a dedicated PR for cleaning up the field and container attribute logic. #### Updated Naming I renamed `ReflectTraits` and `ReflectFieldAttr` to `ContainerAttributes` and `FieldAttributes`, respectively. I think these are clearer. #### Updated Parsing ##### Readability The parsing logic wasn't too bad before, but it was getting difficult to read. There was some duplicated logic between `Meta::List` and `Meta::Path` attributes. Additionally, all the logic was kept inside a large method. To simply things, I replaced the nested meta parsing with `ParseStream` parsing. In my opinion, this is easier to follow since it breaks up the large match statement into a small set of single-line if statements, where each if-block contains a single call to the appropriate attribute parsing method. ##### Flexibility On top of the added simplicity, this also makes our attribute parsing much more flexible. It allows us to more elegantly handle custom where clauses (i.e. `#[reflect(where T: Foo)]`) and it opens the door for more non-standard attribute syntax (e.g. #11659). ##### Errors This also allows us to automatically provide certain errors when parsing. For example, since we can use `stream.lookahead1()`, we get errors like the following for free: ``` error: expected one of: `ignore`, `skip_serializing`, `default` --> crates/bevy_reflect/src/lib.rs:1988:23 | 1988 | #[reflect(foo)] | ^^^ ``` --- ## Changelog > [!note] > All changes are internal to `bevy_reflect_derive` and should not affect the public API[^1]. - Renamed `ReflectTraits` to `ContainerAttributes` - Renamed `ReflectMeta::traits` to `ReflectMeta::attrs` - Renamed `ReflectFieldAttr` to `FieldAttributes` - Updated parsing logic for field/container attribute parsing - Now uses a `ParseStream` directly instead of nested meta parsing - General code cleanup of the field/container attribute modules for `bevy_reflect_derive` [^1]: Does not include errors, which may look slightly different. --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
2024-02-12 15:16:27 +00:00
pub fn new(type_path: ReflectTypePath<'a>, attrs: ContainerAttributes) -> Self {
bevy_reflect: Reflect enums (#4761) # Objective > This is a revival of #1347. Credit for the original PR should go to @Davier. Currently, enums are treated as `ReflectRef::Value` types by `bevy_reflect`. Obviously, there needs to be better a better representation for enums using the reflection API. ## Solution Based on prior work from @Davier, an `Enum` trait has been added as well as the ability to automatically implement it via the `Reflect` derive macro. This allows enums to be expressed dynamically: ```rust #[derive(Reflect)] enum Foo { A, B(usize), C { value: f32 }, } let mut foo = Foo::B(123); assert_eq!("B", foo.variant_name()); assert_eq!(1, foo.field_len()); let new_value = DynamicEnum::from(Foo::C { value: 1.23 }); foo.apply(&new_value); assert_eq!(Foo::C{value: 1.23}, foo); ``` ### Features #### Derive Macro Use the `#[derive(Reflect)]` macro to automatically implement the `Enum` trait for enum definitions. Optionally, you can use `#[reflect(ignore)]` with both variants and variant fields, just like you can with structs. These ignored items will not be considered as part of the reflection and cannot be accessed via reflection. ```rust #[derive(Reflect)] enum TestEnum { A, // Uncomment to ignore all of `B` // #[reflect(ignore)] B(usize), C { // Uncomment to ignore only field `foo` of `C` // #[reflect(ignore)] foo: f32, bar: bool, }, } ``` #### Dynamic Enums Enums may be created/represented dynamically via the `DynamicEnum` struct. The main purpose of this struct is to allow enums to be deserialized into a partial state and to allow dynamic patching. In order to ensure conversion from a `DynamicEnum` to a concrete enum type goes smoothly, be sure to add `FromReflect` to your derive macro. ```rust let mut value = TestEnum::A; // Create from a concrete instance let dyn_enum = DynamicEnum::from(TestEnum::B(123)); value.apply(&dyn_enum); assert_eq!(TestEnum::B(123), value); // Create a purely dynamic instance let dyn_enum = DynamicEnum::new("TestEnum", "A", ()); value.apply(&dyn_enum); assert_eq!(TestEnum::A, value); ``` #### Variants An enum value is always represented as one of its variants— never the enum in its entirety. ```rust let value = TestEnum::A; assert_eq!("A", value.variant_name()); // Since we are using the `A` variant, we cannot also be the `B` variant assert_ne!("B", value.variant_name()); ``` All variant types are representable within the `Enum` trait: unit, struct, and tuple. You can get the current type like: ```rust match value.variant_type() { VariantType::Unit => println!("A unit variant!"), VariantType::Struct => println!("A struct variant!"), VariantType::Tuple => println!("A tuple variant!"), } ``` > Notice that they don't contain any values representing the fields. These are purely tags. If a variant has them, you can access the fields as well: ```rust let mut value = TestEnum::C { foo: 1.23, bar: false }; // Read/write specific fields *value.field_mut("bar").unwrap() = true; // Iterate over the entire collection of fields for field in value.iter_fields() { println!("{} = {:?}", field.name(), field.value()); } ``` #### Variant Swapping It might seem odd to group all variant types under a single trait (why allow `iter_fields` on a unit variant?), but the reason this was done ~~is to easily allow *variant swapping*.~~ As I was recently drafting up the **Design Decisions** section, I discovered that other solutions could have been made to work with variant swapping. So while there are reasons to keep the all-in-one approach, variant swapping is _not_ one of them. ```rust let mut value: Box<dyn Enum> = Box::new(TestEnum::A); value.set(Box::new(TestEnum::B(123))).unwrap(); ``` #### Serialization Enums can be serialized and deserialized via reflection without needing to implement `Serialize` or `Deserialize` themselves (which can save thousands of lines of generated code). Below are the ways an enum can be serialized. > Note, like the rest of reflection-based serialization, the order of the keys in these representations is important! ##### Unit ```json { "type": "my_crate::TestEnum", "enum": { "variant": "A" } } ``` ##### Tuple ```json { "type": "my_crate::TestEnum", "enum": { "variant": "B", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` <details> <summary>Effects on Option</summary> This ends up making `Option` look a little ugly: ```json { "type": "core::option::Option<usize>", "enum": { "variant": "Some", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` </details> ##### Struct ```json { "type": "my_crate::TestEnum", "enum": { "variant": "C", "struct": { "foo": { "type": "f32", "value": 1.23 }, "bar": { "type": "bool", "value": false } } } } ``` ## Design Decisions <details> <summary><strong>View Section</strong></summary> This section is here to provide some context for why certain decisions were made for this PR, alternatives that could have been used instead, and what could be improved upon in the future. ### Variant Representation One of the biggest decisions was to decide on how to represent variants. The current design uses a "all-in-one" design where unit, tuple, and struct variants are all simultaneously represented by the `Enum` trait. This is not the only way it could have been done, though. #### Alternatives ##### 1. Variant Traits One way of representing variants would be to define traits for each variant, implementing them whenever an enum featured at least one instance of them. This would allow us to define variants like: ```rust pub trait Enum: Reflect { fn variant(&self) -> Variant; } pub enum Variant<'a> { Unit, Tuple(&'a dyn TupleVariant), Struct(&'a dyn StructVariant), } pub trait TupleVariant { fn field_len(&self) -> usize; // ... } ``` And then do things like: ```rust fn get_tuple_len(foo: &dyn Enum) -> usize { match foo.variant() { Variant::Tuple(tuple) => tuple.field_len(), _ => panic!("not a tuple variant!") } } ``` The reason this PR does not go with this approach is because of the fact that variants are not separate types. In other words, we cannot implement traits on specific variants— these cover the *entire* enum. This means we offer an easy footgun: ```rust let foo: Option<i32> = None; let my_enum = Box::new(foo) as Box<dyn TupleVariant>; ``` Here, `my_enum` contains `foo`, which is a unit variant. However, since we need to implement `TupleVariant` for `Option` as a whole, it's possible to perform such a cast. This is obviously wrong, but could easily go unnoticed. So unfortunately, this makes it not a good candidate for representing variants. ##### 2. Variant Structs To get around the issue of traits necessarily needing to apply to both the enum and its variants, we could instead use structs that are created on a per-variant basis. This was also considered but was ultimately [[removed](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c)](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c) due to concerns about allocations. Each variant struct would probably look something like: ```rust pub trait Enum: Reflect { fn variant_mut(&self) -> VariantMut; } pub enum VariantMut<'a> { Unit, Tuple(TupleVariantMut), Struct(StructVariantMut), } struct StructVariantMut<'a> { fields: Vec<&'a mut dyn Reflect>, field_indices: HashMap<Cow<'static, str>, usize> } ``` This allows us to isolate struct variants into their own defined struct and define methods specifically for their use. It also prevents users from casting to it since it's not a trait. However, this is not an optimal solution. Both `field_indices` and `fields` will require an allocation (remember, a `Box<[T]>` still requires a `Vec<T>` in order to be constructed). This *might* be a problem if called frequently enough. ##### 3. Generated Structs The original design, implemented by @Davier, instead generates structs specific for each variant. So if we had a variant path like `Foo::Bar`, we'd generate a struct named `FooBarWrapper`. This would be newtyped around the original enum and forward tuple or struct methods to the enum with the chosen variant. Because it involved using the `Tuple` and `Struct` traits (which are also both bound on `Reflect`), this meant a bit more code had to be generated. For a single struct variant with one field, the generated code amounted to ~110LoC. However, each new field added to that variant only added ~6 more LoC. In order to work properly, the enum had to be transmuted to the generated struct: ```rust fn variant(&self) -> crate::EnumVariant<'_> { match self { Foo::Bar {value: i32} => { let wrapper_ref = unsafe { std::mem::transmute::<&Self, &FooBarWrapper>(self) }; crate::EnumVariant::Struct(wrapper_ref as &dyn crate::Struct) } } } ``` This works because `FooBarWrapper` is defined as `repr(transparent)`. Out of all the alternatives, this would probably be the one most likely to be used again in the future. The reasons for why this PR did not continue to use it was because: * To reduce generated code (which would hopefully speed up compile times) * To avoid cluttering the code with generated structs not visible to the user * To keep bevy_reflect simple and extensible (these generated structs act as proxies and might not play well with current or future systems) * To avoid additional unsafe blocks * My own misunderstanding of @Davier's code That last point is obviously on me. I misjudged the code to be too unsafe and unable to handle variant swapping (which it probably could) when I was rebasing it. Looking over it again when writing up this whole section, I see that it was actually a pretty clever way of handling variant representation. #### Benefits of All-in-One As stated before, the current implementation uses an all-in-one approach. All variants are capable of containing fields as far as `Enum` is concerned. This provides a few benefits that the alternatives do not (reduced indirection, safer code, etc.). The biggest benefit, though, is direct field access. Rather than forcing users to have to go through pattern matching, we grant direct access to the fields contained by the current variant. The reason we can do this is because all of the pattern matching happens internally. Getting the field at index `2` will automatically return `Some(...)` for the current variant if it has a field at that index or `None` if it doesn't (or can't). This could be useful for scenarios where the variant has already been verified or just set/swapped (or even where the type of variant doesn't matter): ```rust let dyn_enum: &mut dyn Enum = &mut Foo::Bar {value: 123}; // We know it's the `Bar` variant let field = dyn_enum.field("value").unwrap(); ``` Reflection is not a type-safe abstraction— almost every return value is wrapped in `Option<...>`. There are plenty of places to check and recheck that a value is what Reflect says it is. Forcing users to have to go through `match` each time they want to access a field might just be an extra step among dozens of other verification processes. Some might disagree, but ultimately, my view is that the benefit here is an improvement to the ergonomics and usability of reflected enums. </details> --- ## Changelog ### Added * Added `Enum` trait * Added `Enum` impl to `Reflect` derive macro * Added `DynamicEnum` struct * Added `DynamicVariant` * Added `EnumInfo` * Added `VariantInfo` * Added `StructVariantInfo` * Added `TupleVariantInfo` * Added `UnitVariantInfo` * Added serializtion/deserialization support for enums * Added `EnumSerializer` * Added `VariantType` * Added `VariantFieldIter` * Added `VariantField` * Added `enum_partial_eq(...)` * Added `enum_hash(...)` ### Changed * `Option<T>` now implements `Enum` * `bevy_window` now depends on `bevy_reflect` * Implemented `Reflect` and `FromReflect` for `WindowId` * Derive `FromReflect` on `PerspectiveProjection` * Derive `FromReflect` on `OrthographicProjection` * Derive `FromReflect` on `WindowOrigin` * Derive `FromReflect` on `ScalingMode` * Derive `FromReflect` on `DepthCalculation` ## Migration Guide * Enums no longer need to be treated as values and usages of `#[reflect_value(...)]` can be removed or replaced by `#[reflect(...)]` * Enums (including `Option<T>`) now take a different format when serializing. The format is described above, but this may cause issues for existing scenes that make use of enums. --- Also shout out to @nicopap for helping clean up some of the code here! It's a big feature so help like this is really appreciated! Co-authored-by: Gino Valente <gino.valente.code@gmail.com>
2022-08-02 22:14:41 +00:00
Self {
bevy_reflect_derive: Clean up attribute logic (#11777) # Objective The code in `bevy_reflect_derive` could use some cleanup. ## Solution Took some of the changes in #11659 to create a dedicated PR for cleaning up the field and container attribute logic. #### Updated Naming I renamed `ReflectTraits` and `ReflectFieldAttr` to `ContainerAttributes` and `FieldAttributes`, respectively. I think these are clearer. #### Updated Parsing ##### Readability The parsing logic wasn't too bad before, but it was getting difficult to read. There was some duplicated logic between `Meta::List` and `Meta::Path` attributes. Additionally, all the logic was kept inside a large method. To simply things, I replaced the nested meta parsing with `ParseStream` parsing. In my opinion, this is easier to follow since it breaks up the large match statement into a small set of single-line if statements, where each if-block contains a single call to the appropriate attribute parsing method. ##### Flexibility On top of the added simplicity, this also makes our attribute parsing much more flexible. It allows us to more elegantly handle custom where clauses (i.e. `#[reflect(where T: Foo)]`) and it opens the door for more non-standard attribute syntax (e.g. #11659). ##### Errors This also allows us to automatically provide certain errors when parsing. For example, since we can use `stream.lookahead1()`, we get errors like the following for free: ``` error: expected one of: `ignore`, `skip_serializing`, `default` --> crates/bevy_reflect/src/lib.rs:1988:23 | 1988 | #[reflect(foo)] | ^^^ ``` --- ## Changelog > [!note] > All changes are internal to `bevy_reflect_derive` and should not affect the public API[^1]. - Renamed `ReflectTraits` to `ContainerAttributes` - Renamed `ReflectMeta::traits` to `ReflectMeta::attrs` - Renamed `ReflectFieldAttr` to `FieldAttributes` - Updated parsing logic for field/container attribute parsing - Now uses a `ParseStream` directly instead of nested meta parsing - General code cleanup of the field/container attribute modules for `bevy_reflect_derive` [^1]: Does not include errors, which may look slightly different. --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
2024-02-12 15:16:27 +00:00
attrs,
reflect: stable type path v2 (#7184) # Objective - Introduce a stable alternative to [`std::any::type_name`](https://doc.rust-lang.org/std/any/fn.type_name.html). - Rewrite of #5805 with heavy inspiration in design. - On the path to #5830. - Part of solving #3327. ## Solution - Add a `TypePath` trait for static stable type path/name information. - Add a `TypePath` derive macro. - Add a `impl_type_path` macro for implementing internal and foreign types in `bevy_reflect`. --- ## Changelog - Added `TypePath` trait. - Added `DynamicTypePath` trait and `get_type_path` method to `Reflect`. - Added a `TypePath` derive macro. - Added a `bevy_reflect::impl_type_path` for implementing `TypePath` on internal and foreign types in `bevy_reflect`. - Changed `bevy_reflect::utility::(Non)GenericTypeInfoCell` to `(Non)GenericTypedCell<T>` which allows us to be generic over both `TypeInfo` and `TypePath`. - `TypePath` is now a supertrait of `Asset`, `Material` and `Material2d`. - `impl_reflect_struct` needs a `#[type_path = "..."]` attribute to be specified. - `impl_reflect_value` needs to either specify path starting with a double colon (`::core::option::Option`) or an `in my_crate::foo` declaration. - Added `bevy_reflect_derive::ReflectTypePath`. - Most uses of `Ident` in `bevy_reflect_derive` changed to use `ReflectTypePath`. ## Migration Guide - Implementors of `Asset`, `Material` and `Material2d` now also need to derive `TypePath`. - Manual implementors of `Reflect` will need to implement the new `get_type_path` method. ## Open Questions - [x] ~This PR currently does not migrate any usages of `std::any::type_name` to use `bevy_reflect::TypePath` to ease the review process. Should it?~ Migration will be left to a follow-up PR. - [ ] This PR adds a lot of `#[derive(TypePath)]` and `T: TypePath` to satisfy new bounds, mostly when deriving `TypeUuid`. Should we make `TypePath` a supertrait of `TypeUuid`? [Should we remove `TypeUuid` in favour of `TypePath`?](https://github.com/bevyengine/bevy/pull/5805/files/2afbd855327c4b68e0a6b6f03118f289988441a4#r961067892)
2023-06-05 20:31:20 +00:00
type_path,
bevy_reflect: Reflect remote types (#6042) # Objective The goal with this PR is to allow the use of types that don't implement `Reflect` within the reflection API. Rust's [orphan rule](https://doc.rust-lang.org/book/ch10-02-traits.html#implementing-a-trait-on-a-type) prevents implementing a trait on an external type when neither type nor trait are owned by the implementor. This means that if a crate, `cool_rust_lib`, defines a type, `Foo`, then a user cannot use it with reflection. What this means is that we have to ignore it most of the time: ```rust #[derive(Reflect)] struct SomeStruct { #[reflect(ignore)] data: cool_rust_lib::Foo } ``` Obviously, it's impossible to implement `Reflect` on `Foo`. But does it *have* to be? Most of reflection doesn't deal with concrete types— it's almost all using `dyn Reflect`. And being very metadata-driven, it should theoretically be possible. I mean, [`serde`](https://serde.rs/remote-derive.html) does it. ## Solution > Special thanks to @danielhenrymantilla for their help reviewing this PR and offering wisdom wrt safety. Taking a page out of `serde`'s book, this PR adds the ability to easily use "remote types" with reflection. In this context, a "remote type" is the external type for which we have no ability to implement `Reflect`. This adds the `#[reflect_remote(...)]` attribute macro, which is used to generate "remote type wrappers". All you have to do is define the wrapper exactly the same as the remote type's definition: ```rust // Pretend this is our external crate mod cool_rust_lib { #[derive(Default)] struct Foo { pub value: String } } #[reflect_remote(cool_rust_lib::Foo)] struct FooWrapper { pub value: String } ``` > **Note:** All fields in the external type *must* be public. This could be addressed with a separate getter/setter attribute either in this PR or in another one. The macro takes this user-defined item and transforms it into a newtype wrapper around the external type, marking it as `#[repr(transparent)]`. The fields/variants defined by the user are simply used to build out the reflection impls. Additionally, it generates an implementation of the new trait, `ReflectRemote`, which helps prevent accidental misuses of this API. Therefore, the output generated by the macro would look something like: ```rust #[repr(transparent)] struct FooWrapper(pub cool_rust_lib::Foo); impl ReflectRemote for FooWrapper { type Remote = cool_rust_lib::Foo; // transmutation methods... } // reflection impls... // these will acknowledge and make use of the `value` field ``` Internally, the reflection API will pass around the `FooWrapper` and [transmute](https://doc.rust-lang.org/std/mem/fn.transmute.html) it where necessary. All we have to do is then tell `Reflect` to do that. So rather than ignoring the field, we tell `Reflect` to use our wrapper using the `#[reflect(remote = ...)]` field attribute: ```rust #[derive(Reflect)] struct SomeStruct { #[reflect(remote = FooWrapper)] data: cool_rust_lib::Foo } ``` #### Other Macros & Type Data Because this macro consumes the defined item and generates a new one, we can't just put our macros anywhere. All macros that should be passed to the generated struct need to come *below* this macro. For example, to derive `Default` and register its associated type data: ```rust // ✅ GOOD #[reflect_remote(cool_rust_lib::Foo)] #[derive(Default)] #[reflect(Default)] struct FooWrapper { pub value: String } // ❌ BAD #[derive(Default)] #[reflect_remote(cool_rust_lib::Foo)] #[reflect(Default)] struct FooWrapper { pub value: String } ``` #### Generics Generics are forwarded to the generated struct as well. They should also be defined in the same order: ```rust #[reflect_remote(RemoteGeneric<'a, T1, T2>)] struct GenericWrapper<'a, T1, T2> { pub foo: &'a T1, pub bar: &'a T2, } ``` > Naming does *not* need to match the original definition's. Only order matters here. > Also note that the code above is just a demonstration and doesn't actually compile since we'd need to enforce certain bounds (e.g. `T1: Reflect`, `'a: 'static`, etc.) #### Nesting And, yes, you can nest remote types: ```rust #[reflect_remote(RemoteOuter)] struct OuterWrapper { #[reflect(remote = InnerWrapper)] pub inner: RemoteInner } #[reflect_remote(RemoteInner)] struct InnerWrapper(usize); ``` #### Assertions This macro will also generate some compile-time assertions to ensure that the correct types are used. It's important we catch this early so users don't have to wait for something to panic. And it also helps keep our `unsafe` a little safer. For example, a wrapper definition that does not match its corresponding remote type will result in an error: ```rust mod external_crate { pub struct TheirStruct(pub u32); } #[reflect_remote(external_crate::TheirStruct)] struct MyStruct(pub String); // ERROR: expected type `u32` but found `String` ``` <details> <summary>Generated Assertion</summary> ```rust const _: () = { #[allow(non_snake_case)] #[allow(unused_variables)] #[allow(unused_assignments)] #[allow(unreachable_patterns)] #[allow(clippy::multiple_bound_locations)] fn assert_wrapper_definition_matches_remote_type( mut __remote__: external_crate::TheirStruct, ) { __remote__.0 = (|| -> ::core::option::Option<String> { None })().unwrap(); } }; ``` </details> Additionally, using the incorrect type in a `#[reflect(remote = ...)]` attribute should result in an error: ```rust mod external_crate { pub struct TheirFoo(pub u32); pub struct TheirBar(pub i32); } #[reflect_remote(external_crate::TheirFoo)] struct MyFoo(pub u32); #[reflect_remote(external_crate::TheirBar)] struct MyBar(pub i32); #[derive(Reflect)] struct MyStruct { #[reflect(remote = MyBar)] // ERROR: expected type `TheirFoo` but found struct `TheirBar` foo: external_crate::TheirFoo } ``` <details> <summary>Generated Assertion</summary> ```rust const _: () = { struct RemoteFieldAssertions; impl RemoteFieldAssertions { #[allow(non_snake_case)] #[allow(clippy::multiple_bound_locations)] fn assert__foo__is_valid_remote() { let _: <MyBar as bevy_reflect::ReflectRemote>::Remote = (|| -> ::core::option::Option<external_crate::TheirFoo> { None })().unwrap(); } } }; ``` </details> ### Discussion There are a couple points that I think still need discussion or validation. - [x] 1. `Any` shenanigans ~~If we wanted to downcast our remote type from a `dyn Reflect`, we'd have to first downcast to the wrapper then extract the inner type. This PR has a [commit](b840db9f74cb6d357f951cb11b150d46bac89ee2) that addresses this by making all the `Reflect::*any` methods return the inner type rather than the wrapper type. This allows us to downcast directly to our remote type.~~ ~~However, I'm not sure if this is something we want to do. For unknowing users, it could be confusing and seemingly inconsistent. Is it worth keeping? Or should this behavior be removed?~~ I think this should be fine. The remote wrapper is an implementation detail and users should not need to downcast to the wrapper type. Feel free to let me know if there are other opinions on this though! - [x] 2. Implementing `Deref/DerefMut` and `From` ~~We don't currently do this, but should we implement other traits on the generated transparent struct? We could implement `Deref`/`DerefMut` to easily access the inner type. And we could implement `From` for easier conversion between the two types (e.g. `T: Into<Foo>`).~~ As mentioned in the comments, we probably don't need to do this. Again, the remote wrapper is an implementation detail, and should generally not be used directly. - [x] 3. ~~Should we define a getter/setter field attribute in this PR as well or leave it for a future one?~~ I think this should be saved for a future PR - [ ] 4. Any foreseeable issues with this implementation? #### Alternatives One alternative to defining our own `ReflectRemote` would be to use [bytemuck's `TransparentWrapper`](https://docs.rs/bytemuck/1.13.1/bytemuck/trait.TransparentWrapper.html) (as suggested by @danielhenrymantilla). This is definitely a viable option, as `ReflectRemote` is pretty much the same thing as `TransparentWrapper`. However, the cost would be bringing in a new crate— though, it is already in use in a few other sub-crates like bevy_render. I think we're okay just defining `ReflectRemote` ourselves, but we can go the bytemuck route if we'd prefer offloading that work to another crate. --- ## Changelog * Added the `#[reflect_remote(...)]` attribute macro to allow `Reflect` to be used on remote types * Added `ReflectRemote` trait for ensuring proper remote wrapper usage
2024-08-12 19:12:53 +00:00
remote_ty: None,
bevy_reflect: Reflect enums (#4761) # Objective > This is a revival of #1347. Credit for the original PR should go to @Davier. Currently, enums are treated as `ReflectRef::Value` types by `bevy_reflect`. Obviously, there needs to be better a better representation for enums using the reflection API. ## Solution Based on prior work from @Davier, an `Enum` trait has been added as well as the ability to automatically implement it via the `Reflect` derive macro. This allows enums to be expressed dynamically: ```rust #[derive(Reflect)] enum Foo { A, B(usize), C { value: f32 }, } let mut foo = Foo::B(123); assert_eq!("B", foo.variant_name()); assert_eq!(1, foo.field_len()); let new_value = DynamicEnum::from(Foo::C { value: 1.23 }); foo.apply(&new_value); assert_eq!(Foo::C{value: 1.23}, foo); ``` ### Features #### Derive Macro Use the `#[derive(Reflect)]` macro to automatically implement the `Enum` trait for enum definitions. Optionally, you can use `#[reflect(ignore)]` with both variants and variant fields, just like you can with structs. These ignored items will not be considered as part of the reflection and cannot be accessed via reflection. ```rust #[derive(Reflect)] enum TestEnum { A, // Uncomment to ignore all of `B` // #[reflect(ignore)] B(usize), C { // Uncomment to ignore only field `foo` of `C` // #[reflect(ignore)] foo: f32, bar: bool, }, } ``` #### Dynamic Enums Enums may be created/represented dynamically via the `DynamicEnum` struct. The main purpose of this struct is to allow enums to be deserialized into a partial state and to allow dynamic patching. In order to ensure conversion from a `DynamicEnum` to a concrete enum type goes smoothly, be sure to add `FromReflect` to your derive macro. ```rust let mut value = TestEnum::A; // Create from a concrete instance let dyn_enum = DynamicEnum::from(TestEnum::B(123)); value.apply(&dyn_enum); assert_eq!(TestEnum::B(123), value); // Create a purely dynamic instance let dyn_enum = DynamicEnum::new("TestEnum", "A", ()); value.apply(&dyn_enum); assert_eq!(TestEnum::A, value); ``` #### Variants An enum value is always represented as one of its variants— never the enum in its entirety. ```rust let value = TestEnum::A; assert_eq!("A", value.variant_name()); // Since we are using the `A` variant, we cannot also be the `B` variant assert_ne!("B", value.variant_name()); ``` All variant types are representable within the `Enum` trait: unit, struct, and tuple. You can get the current type like: ```rust match value.variant_type() { VariantType::Unit => println!("A unit variant!"), VariantType::Struct => println!("A struct variant!"), VariantType::Tuple => println!("A tuple variant!"), } ``` > Notice that they don't contain any values representing the fields. These are purely tags. If a variant has them, you can access the fields as well: ```rust let mut value = TestEnum::C { foo: 1.23, bar: false }; // Read/write specific fields *value.field_mut("bar").unwrap() = true; // Iterate over the entire collection of fields for field in value.iter_fields() { println!("{} = {:?}", field.name(), field.value()); } ``` #### Variant Swapping It might seem odd to group all variant types under a single trait (why allow `iter_fields` on a unit variant?), but the reason this was done ~~is to easily allow *variant swapping*.~~ As I was recently drafting up the **Design Decisions** section, I discovered that other solutions could have been made to work with variant swapping. So while there are reasons to keep the all-in-one approach, variant swapping is _not_ one of them. ```rust let mut value: Box<dyn Enum> = Box::new(TestEnum::A); value.set(Box::new(TestEnum::B(123))).unwrap(); ``` #### Serialization Enums can be serialized and deserialized via reflection without needing to implement `Serialize` or `Deserialize` themselves (which can save thousands of lines of generated code). Below are the ways an enum can be serialized. > Note, like the rest of reflection-based serialization, the order of the keys in these representations is important! ##### Unit ```json { "type": "my_crate::TestEnum", "enum": { "variant": "A" } } ``` ##### Tuple ```json { "type": "my_crate::TestEnum", "enum": { "variant": "B", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` <details> <summary>Effects on Option</summary> This ends up making `Option` look a little ugly: ```json { "type": "core::option::Option<usize>", "enum": { "variant": "Some", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` </details> ##### Struct ```json { "type": "my_crate::TestEnum", "enum": { "variant": "C", "struct": { "foo": { "type": "f32", "value": 1.23 }, "bar": { "type": "bool", "value": false } } } } ``` ## Design Decisions <details> <summary><strong>View Section</strong></summary> This section is here to provide some context for why certain decisions were made for this PR, alternatives that could have been used instead, and what could be improved upon in the future. ### Variant Representation One of the biggest decisions was to decide on how to represent variants. The current design uses a "all-in-one" design where unit, tuple, and struct variants are all simultaneously represented by the `Enum` trait. This is not the only way it could have been done, though. #### Alternatives ##### 1. Variant Traits One way of representing variants would be to define traits for each variant, implementing them whenever an enum featured at least one instance of them. This would allow us to define variants like: ```rust pub trait Enum: Reflect { fn variant(&self) -> Variant; } pub enum Variant<'a> { Unit, Tuple(&'a dyn TupleVariant), Struct(&'a dyn StructVariant), } pub trait TupleVariant { fn field_len(&self) -> usize; // ... } ``` And then do things like: ```rust fn get_tuple_len(foo: &dyn Enum) -> usize { match foo.variant() { Variant::Tuple(tuple) => tuple.field_len(), _ => panic!("not a tuple variant!") } } ``` The reason this PR does not go with this approach is because of the fact that variants are not separate types. In other words, we cannot implement traits on specific variants— these cover the *entire* enum. This means we offer an easy footgun: ```rust let foo: Option<i32> = None; let my_enum = Box::new(foo) as Box<dyn TupleVariant>; ``` Here, `my_enum` contains `foo`, which is a unit variant. However, since we need to implement `TupleVariant` for `Option` as a whole, it's possible to perform such a cast. This is obviously wrong, but could easily go unnoticed. So unfortunately, this makes it not a good candidate for representing variants. ##### 2. Variant Structs To get around the issue of traits necessarily needing to apply to both the enum and its variants, we could instead use structs that are created on a per-variant basis. This was also considered but was ultimately [[removed](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c)](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c) due to concerns about allocations. Each variant struct would probably look something like: ```rust pub trait Enum: Reflect { fn variant_mut(&self) -> VariantMut; } pub enum VariantMut<'a> { Unit, Tuple(TupleVariantMut), Struct(StructVariantMut), } struct StructVariantMut<'a> { fields: Vec<&'a mut dyn Reflect>, field_indices: HashMap<Cow<'static, str>, usize> } ``` This allows us to isolate struct variants into their own defined struct and define methods specifically for their use. It also prevents users from casting to it since it's not a trait. However, this is not an optimal solution. Both `field_indices` and `fields` will require an allocation (remember, a `Box<[T]>` still requires a `Vec<T>` in order to be constructed). This *might* be a problem if called frequently enough. ##### 3. Generated Structs The original design, implemented by @Davier, instead generates structs specific for each variant. So if we had a variant path like `Foo::Bar`, we'd generate a struct named `FooBarWrapper`. This would be newtyped around the original enum and forward tuple or struct methods to the enum with the chosen variant. Because it involved using the `Tuple` and `Struct` traits (which are also both bound on `Reflect`), this meant a bit more code had to be generated. For a single struct variant with one field, the generated code amounted to ~110LoC. However, each new field added to that variant only added ~6 more LoC. In order to work properly, the enum had to be transmuted to the generated struct: ```rust fn variant(&self) -> crate::EnumVariant<'_> { match self { Foo::Bar {value: i32} => { let wrapper_ref = unsafe { std::mem::transmute::<&Self, &FooBarWrapper>(self) }; crate::EnumVariant::Struct(wrapper_ref as &dyn crate::Struct) } } } ``` This works because `FooBarWrapper` is defined as `repr(transparent)`. Out of all the alternatives, this would probably be the one most likely to be used again in the future. The reasons for why this PR did not continue to use it was because: * To reduce generated code (which would hopefully speed up compile times) * To avoid cluttering the code with generated structs not visible to the user * To keep bevy_reflect simple and extensible (these generated structs act as proxies and might not play well with current or future systems) * To avoid additional unsafe blocks * My own misunderstanding of @Davier's code That last point is obviously on me. I misjudged the code to be too unsafe and unable to handle variant swapping (which it probably could) when I was rebasing it. Looking over it again when writing up this whole section, I see that it was actually a pretty clever way of handling variant representation. #### Benefits of All-in-One As stated before, the current implementation uses an all-in-one approach. All variants are capable of containing fields as far as `Enum` is concerned. This provides a few benefits that the alternatives do not (reduced indirection, safer code, etc.). The biggest benefit, though, is direct field access. Rather than forcing users to have to go through pattern matching, we grant direct access to the fields contained by the current variant. The reason we can do this is because all of the pattern matching happens internally. Getting the field at index `2` will automatically return `Some(...)` for the current variant if it has a field at that index or `None` if it doesn't (or can't). This could be useful for scenarios where the variant has already been verified or just set/swapped (or even where the type of variant doesn't matter): ```rust let dyn_enum: &mut dyn Enum = &mut Foo::Bar {value: 123}; // We know it's the `Bar` variant let field = dyn_enum.field("value").unwrap(); ``` Reflection is not a type-safe abstraction— almost every return value is wrapped in `Option<...>`. There are plenty of places to check and recheck that a value is what Reflect says it is. Forcing users to have to go through `match` each time they want to access a field might just be an extra step among dozens of other verification processes. Some might disagree, but ultimately, my view is that the benefit here is an improvement to the ergonomics and usability of reflected enums. </details> --- ## Changelog ### Added * Added `Enum` trait * Added `Enum` impl to `Reflect` derive macro * Added `DynamicEnum` struct * Added `DynamicVariant` * Added `EnumInfo` * Added `VariantInfo` * Added `StructVariantInfo` * Added `TupleVariantInfo` * Added `UnitVariantInfo` * Added serializtion/deserialization support for enums * Added `EnumSerializer` * Added `VariantType` * Added `VariantFieldIter` * Added `VariantField` * Added `enum_partial_eq(...)` * Added `enum_hash(...)` ### Changed * `Option<T>` now implements `Enum` * `bevy_window` now depends on `bevy_reflect` * Implemented `Reflect` and `FromReflect` for `WindowId` * Derive `FromReflect` on `PerspectiveProjection` * Derive `FromReflect` on `OrthographicProjection` * Derive `FromReflect` on `WindowOrigin` * Derive `FromReflect` on `ScalingMode` * Derive `FromReflect` on `DepthCalculation` ## Migration Guide * Enums no longer need to be treated as values and usages of `#[reflect_value(...)]` can be removed or replaced by `#[reflect(...)]` * Enums (including `Option<T>`) now take a different format when serializing. The format is described above, but this may cause issues for existing scenes that make use of enums. --- Also shout out to @nicopap for helping clean up some of the code here! It's a big feature so help like this is really appreciated! Co-authored-by: Gino Valente <gino.valente.code@gmail.com>
2022-08-02 22:14:41 +00:00
bevy_reflect_path: utility::get_bevy_reflect_path(),
#[cfg(feature = "documentation")]
docs: Default::default(),
bevy_reflect: Reflect enums (#4761) # Objective > This is a revival of #1347. Credit for the original PR should go to @Davier. Currently, enums are treated as `ReflectRef::Value` types by `bevy_reflect`. Obviously, there needs to be better a better representation for enums using the reflection API. ## Solution Based on prior work from @Davier, an `Enum` trait has been added as well as the ability to automatically implement it via the `Reflect` derive macro. This allows enums to be expressed dynamically: ```rust #[derive(Reflect)] enum Foo { A, B(usize), C { value: f32 }, } let mut foo = Foo::B(123); assert_eq!("B", foo.variant_name()); assert_eq!(1, foo.field_len()); let new_value = DynamicEnum::from(Foo::C { value: 1.23 }); foo.apply(&new_value); assert_eq!(Foo::C{value: 1.23}, foo); ``` ### Features #### Derive Macro Use the `#[derive(Reflect)]` macro to automatically implement the `Enum` trait for enum definitions. Optionally, you can use `#[reflect(ignore)]` with both variants and variant fields, just like you can with structs. These ignored items will not be considered as part of the reflection and cannot be accessed via reflection. ```rust #[derive(Reflect)] enum TestEnum { A, // Uncomment to ignore all of `B` // #[reflect(ignore)] B(usize), C { // Uncomment to ignore only field `foo` of `C` // #[reflect(ignore)] foo: f32, bar: bool, }, } ``` #### Dynamic Enums Enums may be created/represented dynamically via the `DynamicEnum` struct. The main purpose of this struct is to allow enums to be deserialized into a partial state and to allow dynamic patching. In order to ensure conversion from a `DynamicEnum` to a concrete enum type goes smoothly, be sure to add `FromReflect` to your derive macro. ```rust let mut value = TestEnum::A; // Create from a concrete instance let dyn_enum = DynamicEnum::from(TestEnum::B(123)); value.apply(&dyn_enum); assert_eq!(TestEnum::B(123), value); // Create a purely dynamic instance let dyn_enum = DynamicEnum::new("TestEnum", "A", ()); value.apply(&dyn_enum); assert_eq!(TestEnum::A, value); ``` #### Variants An enum value is always represented as one of its variants— never the enum in its entirety. ```rust let value = TestEnum::A; assert_eq!("A", value.variant_name()); // Since we are using the `A` variant, we cannot also be the `B` variant assert_ne!("B", value.variant_name()); ``` All variant types are representable within the `Enum` trait: unit, struct, and tuple. You can get the current type like: ```rust match value.variant_type() { VariantType::Unit => println!("A unit variant!"), VariantType::Struct => println!("A struct variant!"), VariantType::Tuple => println!("A tuple variant!"), } ``` > Notice that they don't contain any values representing the fields. These are purely tags. If a variant has them, you can access the fields as well: ```rust let mut value = TestEnum::C { foo: 1.23, bar: false }; // Read/write specific fields *value.field_mut("bar").unwrap() = true; // Iterate over the entire collection of fields for field in value.iter_fields() { println!("{} = {:?}", field.name(), field.value()); } ``` #### Variant Swapping It might seem odd to group all variant types under a single trait (why allow `iter_fields` on a unit variant?), but the reason this was done ~~is to easily allow *variant swapping*.~~ As I was recently drafting up the **Design Decisions** section, I discovered that other solutions could have been made to work with variant swapping. So while there are reasons to keep the all-in-one approach, variant swapping is _not_ one of them. ```rust let mut value: Box<dyn Enum> = Box::new(TestEnum::A); value.set(Box::new(TestEnum::B(123))).unwrap(); ``` #### Serialization Enums can be serialized and deserialized via reflection without needing to implement `Serialize` or `Deserialize` themselves (which can save thousands of lines of generated code). Below are the ways an enum can be serialized. > Note, like the rest of reflection-based serialization, the order of the keys in these representations is important! ##### Unit ```json { "type": "my_crate::TestEnum", "enum": { "variant": "A" } } ``` ##### Tuple ```json { "type": "my_crate::TestEnum", "enum": { "variant": "B", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` <details> <summary>Effects on Option</summary> This ends up making `Option` look a little ugly: ```json { "type": "core::option::Option<usize>", "enum": { "variant": "Some", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` </details> ##### Struct ```json { "type": "my_crate::TestEnum", "enum": { "variant": "C", "struct": { "foo": { "type": "f32", "value": 1.23 }, "bar": { "type": "bool", "value": false } } } } ``` ## Design Decisions <details> <summary><strong>View Section</strong></summary> This section is here to provide some context for why certain decisions were made for this PR, alternatives that could have been used instead, and what could be improved upon in the future. ### Variant Representation One of the biggest decisions was to decide on how to represent variants. The current design uses a "all-in-one" design where unit, tuple, and struct variants are all simultaneously represented by the `Enum` trait. This is not the only way it could have been done, though. #### Alternatives ##### 1. Variant Traits One way of representing variants would be to define traits for each variant, implementing them whenever an enum featured at least one instance of them. This would allow us to define variants like: ```rust pub trait Enum: Reflect { fn variant(&self) -> Variant; } pub enum Variant<'a> { Unit, Tuple(&'a dyn TupleVariant), Struct(&'a dyn StructVariant), } pub trait TupleVariant { fn field_len(&self) -> usize; // ... } ``` And then do things like: ```rust fn get_tuple_len(foo: &dyn Enum) -> usize { match foo.variant() { Variant::Tuple(tuple) => tuple.field_len(), _ => panic!("not a tuple variant!") } } ``` The reason this PR does not go with this approach is because of the fact that variants are not separate types. In other words, we cannot implement traits on specific variants— these cover the *entire* enum. This means we offer an easy footgun: ```rust let foo: Option<i32> = None; let my_enum = Box::new(foo) as Box<dyn TupleVariant>; ``` Here, `my_enum` contains `foo`, which is a unit variant. However, since we need to implement `TupleVariant` for `Option` as a whole, it's possible to perform such a cast. This is obviously wrong, but could easily go unnoticed. So unfortunately, this makes it not a good candidate for representing variants. ##### 2. Variant Structs To get around the issue of traits necessarily needing to apply to both the enum and its variants, we could instead use structs that are created on a per-variant basis. This was also considered but was ultimately [[removed](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c)](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c) due to concerns about allocations. Each variant struct would probably look something like: ```rust pub trait Enum: Reflect { fn variant_mut(&self) -> VariantMut; } pub enum VariantMut<'a> { Unit, Tuple(TupleVariantMut), Struct(StructVariantMut), } struct StructVariantMut<'a> { fields: Vec<&'a mut dyn Reflect>, field_indices: HashMap<Cow<'static, str>, usize> } ``` This allows us to isolate struct variants into their own defined struct and define methods specifically for their use. It also prevents users from casting to it since it's not a trait. However, this is not an optimal solution. Both `field_indices` and `fields` will require an allocation (remember, a `Box<[T]>` still requires a `Vec<T>` in order to be constructed). This *might* be a problem if called frequently enough. ##### 3. Generated Structs The original design, implemented by @Davier, instead generates structs specific for each variant. So if we had a variant path like `Foo::Bar`, we'd generate a struct named `FooBarWrapper`. This would be newtyped around the original enum and forward tuple or struct methods to the enum with the chosen variant. Because it involved using the `Tuple` and `Struct` traits (which are also both bound on `Reflect`), this meant a bit more code had to be generated. For a single struct variant with one field, the generated code amounted to ~110LoC. However, each new field added to that variant only added ~6 more LoC. In order to work properly, the enum had to be transmuted to the generated struct: ```rust fn variant(&self) -> crate::EnumVariant<'_> { match self { Foo::Bar {value: i32} => { let wrapper_ref = unsafe { std::mem::transmute::<&Self, &FooBarWrapper>(self) }; crate::EnumVariant::Struct(wrapper_ref as &dyn crate::Struct) } } } ``` This works because `FooBarWrapper` is defined as `repr(transparent)`. Out of all the alternatives, this would probably be the one most likely to be used again in the future. The reasons for why this PR did not continue to use it was because: * To reduce generated code (which would hopefully speed up compile times) * To avoid cluttering the code with generated structs not visible to the user * To keep bevy_reflect simple and extensible (these generated structs act as proxies and might not play well with current or future systems) * To avoid additional unsafe blocks * My own misunderstanding of @Davier's code That last point is obviously on me. I misjudged the code to be too unsafe and unable to handle variant swapping (which it probably could) when I was rebasing it. Looking over it again when writing up this whole section, I see that it was actually a pretty clever way of handling variant representation. #### Benefits of All-in-One As stated before, the current implementation uses an all-in-one approach. All variants are capable of containing fields as far as `Enum` is concerned. This provides a few benefits that the alternatives do not (reduced indirection, safer code, etc.). The biggest benefit, though, is direct field access. Rather than forcing users to have to go through pattern matching, we grant direct access to the fields contained by the current variant. The reason we can do this is because all of the pattern matching happens internally. Getting the field at index `2` will automatically return `Some(...)` for the current variant if it has a field at that index or `None` if it doesn't (or can't). This could be useful for scenarios where the variant has already been verified or just set/swapped (or even where the type of variant doesn't matter): ```rust let dyn_enum: &mut dyn Enum = &mut Foo::Bar {value: 123}; // We know it's the `Bar` variant let field = dyn_enum.field("value").unwrap(); ``` Reflection is not a type-safe abstraction— almost every return value is wrapped in `Option<...>`. There are plenty of places to check and recheck that a value is what Reflect says it is. Forcing users to have to go through `match` each time they want to access a field might just be an extra step among dozens of other verification processes. Some might disagree, but ultimately, my view is that the benefit here is an improvement to the ergonomics and usability of reflected enums. </details> --- ## Changelog ### Added * Added `Enum` trait * Added `Enum` impl to `Reflect` derive macro * Added `DynamicEnum` struct * Added `DynamicVariant` * Added `EnumInfo` * Added `VariantInfo` * Added `StructVariantInfo` * Added `TupleVariantInfo` * Added `UnitVariantInfo` * Added serializtion/deserialization support for enums * Added `EnumSerializer` * Added `VariantType` * Added `VariantFieldIter` * Added `VariantField` * Added `enum_partial_eq(...)` * Added `enum_hash(...)` ### Changed * `Option<T>` now implements `Enum` * `bevy_window` now depends on `bevy_reflect` * Implemented `Reflect` and `FromReflect` for `WindowId` * Derive `FromReflect` on `PerspectiveProjection` * Derive `FromReflect` on `OrthographicProjection` * Derive `FromReflect` on `WindowOrigin` * Derive `FromReflect` on `ScalingMode` * Derive `FromReflect` on `DepthCalculation` ## Migration Guide * Enums no longer need to be treated as values and usages of `#[reflect_value(...)]` can be removed or replaced by `#[reflect(...)]` * Enums (including `Option<T>`) now take a different format when serializing. The format is described above, but this may cause issues for existing scenes that make use of enums. --- Also shout out to @nicopap for helping clean up some of the code here! It's a big feature so help like this is really appreciated! Co-authored-by: Gino Valente <gino.valente.code@gmail.com>
2022-08-02 22:14:41 +00:00
}
bevy_reflect_derive: Tidying up the code (#4712) # Objective The `bevy_reflect_derive` crate is not the cleanest or easiest to follow/maintain. The `lib.rs` file is especially difficult with over 1000 lines of code written in a confusing order. This is just a result of growth within the crate and it would be nice to clean it up for future work. ## Solution Split `bevy_reflect_derive` into many more submodules. The submodules include: * `container_attributes` - Code relating to container attributes * `derive_data` - Code relating to reflection-based derive metadata * `field_attributes` - Code relating to field attributes * `impls` - Code containing actual reflection implementations * `reflect_value` - Code relating to reflection-based value metadata * `registration` - Code relating to type registration * `utility` - General-purpose utility functions This leaves the `lib.rs` file to contain only the public macros, making it much easier to digest (and fewer than 200 lines). By breaking up the code into smaller modules, we make it easier for future contributors to find the code they're looking for or identify which module best fits their own additions. ### Metadata Structs This cleanup also adds two big metadata structs: `ReflectFieldAttr` and `ReflectDeriveData`. The former is used to store all attributes for a struct field (if any). The latter is used to store all metadata for struct-based derive inputs. Both significantly reduce code duplication and make editing these macros much simpler. The tradeoff is that we may collect more metadata than needed. However, this is usually a small thing (such as checking for attributes when they're not really needed or creating a `ReflectFieldAttr` for every field regardless of whether they actually have an attribute). We could try to remove these tradeoffs and squeeze some more performance out, but doing so might come at the cost of developer experience. Personally, I think it's much nicer to create a `ReflectFieldAttr` for every field since it means I don't have to do two `Option` checks. Others may disagree, though, and so we can discuss changing this either in this PR or in a future one. ### Out of Scope _Some_ documentation has been added or improved, but ultimately good docs are probably best saved for a dedicated PR. ## 🔍 Focus Points (for reviewers) I know it's a lot to sift through, so here is a list of **key points for reviewers**: - The following files contain code that was mostly just relocated: - `reflect_value.rs` - `registration.rs` - `container_attributes.rs` was also mostly moved but features some general cleanup (reducing nesting, removing hardcoded strings, etc.) and lots of doc comments - Most impl logic was moved from `lib.rs` to `impls.rs`, but they have been significantly modified to use the new `ReflectDeriveData` metadata struct in order to reduce duplication. - `derive_data.rs` and `field_attributes.rs` contain almost entirely new code and should probably be given the most attention. - Likewise, `from_reflect.rs` saw major changes using `ReflectDeriveData` so it should also be given focus. - There was no change to the `lib.rs` exports so the end-user API should be the same. ## Prior Work This task was initially tackled by @NathanSWard in #2377 (which was closed in favor of this PR), so hats off to them for beating me to the punch by nearly a year! --- ## Changelog * **[INTERNAL]** Split `bevy_reflect_derive` into smaller submodules * **[INTERNAL]** Add `ReflectFieldAttr` * **[INTERNAL]** Add `ReflectDeriveData` * Add `BevyManifest::get_path_direct()` method (`bevy_macro_utils`) Co-authored-by: MrGVSV <49806985+MrGVSV@users.noreply.github.com>
2022-05-12 19:43:23 +00:00
}
/// Sets the documentation for this type.
#[cfg(feature = "documentation")]
pub fn with_docs(self, docs: crate::documentation::Documentation) -> Self {
Self { docs, ..self }
}
/// The registered reflect attributes on this struct.
bevy_reflect_derive: Clean up attribute logic (#11777) # Objective The code in `bevy_reflect_derive` could use some cleanup. ## Solution Took some of the changes in #11659 to create a dedicated PR for cleaning up the field and container attribute logic. #### Updated Naming I renamed `ReflectTraits` and `ReflectFieldAttr` to `ContainerAttributes` and `FieldAttributes`, respectively. I think these are clearer. #### Updated Parsing ##### Readability The parsing logic wasn't too bad before, but it was getting difficult to read. There was some duplicated logic between `Meta::List` and `Meta::Path` attributes. Additionally, all the logic was kept inside a large method. To simply things, I replaced the nested meta parsing with `ParseStream` parsing. In my opinion, this is easier to follow since it breaks up the large match statement into a small set of single-line if statements, where each if-block contains a single call to the appropriate attribute parsing method. ##### Flexibility On top of the added simplicity, this also makes our attribute parsing much more flexible. It allows us to more elegantly handle custom where clauses (i.e. `#[reflect(where T: Foo)]`) and it opens the door for more non-standard attribute syntax (e.g. #11659). ##### Errors This also allows us to automatically provide certain errors when parsing. For example, since we can use `stream.lookahead1()`, we get errors like the following for free: ``` error: expected one of: `ignore`, `skip_serializing`, `default` --> crates/bevy_reflect/src/lib.rs:1988:23 | 1988 | #[reflect(foo)] | ^^^ ``` --- ## Changelog > [!note] > All changes are internal to `bevy_reflect_derive` and should not affect the public API[^1]. - Renamed `ReflectTraits` to `ContainerAttributes` - Renamed `ReflectMeta::traits` to `ReflectMeta::attrs` - Renamed `ReflectFieldAttr` to `FieldAttributes` - Updated parsing logic for field/container attribute parsing - Now uses a `ParseStream` directly instead of nested meta parsing - General code cleanup of the field/container attribute modules for `bevy_reflect_derive` [^1]: Does not include errors, which may look slightly different. --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
2024-02-12 15:16:27 +00:00
pub fn attrs(&self) -> &ContainerAttributes {
&self.attrs
bevy_reflect_derive: Tidying up the code (#4712) # Objective The `bevy_reflect_derive` crate is not the cleanest or easiest to follow/maintain. The `lib.rs` file is especially difficult with over 1000 lines of code written in a confusing order. This is just a result of growth within the crate and it would be nice to clean it up for future work. ## Solution Split `bevy_reflect_derive` into many more submodules. The submodules include: * `container_attributes` - Code relating to container attributes * `derive_data` - Code relating to reflection-based derive metadata * `field_attributes` - Code relating to field attributes * `impls` - Code containing actual reflection implementations * `reflect_value` - Code relating to reflection-based value metadata * `registration` - Code relating to type registration * `utility` - General-purpose utility functions This leaves the `lib.rs` file to contain only the public macros, making it much easier to digest (and fewer than 200 lines). By breaking up the code into smaller modules, we make it easier for future contributors to find the code they're looking for or identify which module best fits their own additions. ### Metadata Structs This cleanup also adds two big metadata structs: `ReflectFieldAttr` and `ReflectDeriveData`. The former is used to store all attributes for a struct field (if any). The latter is used to store all metadata for struct-based derive inputs. Both significantly reduce code duplication and make editing these macros much simpler. The tradeoff is that we may collect more metadata than needed. However, this is usually a small thing (such as checking for attributes when they're not really needed or creating a `ReflectFieldAttr` for every field regardless of whether they actually have an attribute). We could try to remove these tradeoffs and squeeze some more performance out, but doing so might come at the cost of developer experience. Personally, I think it's much nicer to create a `ReflectFieldAttr` for every field since it means I don't have to do two `Option` checks. Others may disagree, though, and so we can discuss changing this either in this PR or in a future one. ### Out of Scope _Some_ documentation has been added or improved, but ultimately good docs are probably best saved for a dedicated PR. ## 🔍 Focus Points (for reviewers) I know it's a lot to sift through, so here is a list of **key points for reviewers**: - The following files contain code that was mostly just relocated: - `reflect_value.rs` - `registration.rs` - `container_attributes.rs` was also mostly moved but features some general cleanup (reducing nesting, removing hardcoded strings, etc.) and lots of doc comments - Most impl logic was moved from `lib.rs` to `impls.rs`, but they have been significantly modified to use the new `ReflectDeriveData` metadata struct in order to reduce duplication. - `derive_data.rs` and `field_attributes.rs` contain almost entirely new code and should probably be given the most attention. - Likewise, `from_reflect.rs` saw major changes using `ReflectDeriveData` so it should also be given focus. - There was no change to the `lib.rs` exports so the end-user API should be the same. ## Prior Work This task was initially tackled by @NathanSWard in #2377 (which was closed in favor of this PR), so hats off to them for beating me to the punch by nearly a year! --- ## Changelog * **[INTERNAL]** Split `bevy_reflect_derive` into smaller submodules * **[INTERNAL]** Add `ReflectFieldAttr` * **[INTERNAL]** Add `ReflectDeriveData` * Add `BevyManifest::get_path_direct()` method (`bevy_macro_utils`) Co-authored-by: MrGVSV <49806985+MrGVSV@users.noreply.github.com>
2022-05-12 19:43:23 +00:00
}
bevy_reflect: `FromReflect` Ergonomics Implementation (#6056) # Objective **This implementation is based on https://github.com/bevyengine/rfcs/pull/59.** --- Resolves #4597 Full details and motivation can be found in the RFC, but here's a brief summary. `FromReflect` is a very powerful and important trait within the reflection API. It allows Dynamic types (e.g., `DynamicList`, etc.) to be formed into Real ones (e.g., `Vec<i32>`, etc.). This mainly comes into play concerning deserialization, where the reflection deserializers both return a `Box<dyn Reflect>` that almost always contain one of these Dynamic representations of a Real type. To convert this to our Real type, we need to use `FromReflect`. It also sneaks up in other ways. For example, it's a required bound for `T` in `Vec<T>` so that `Vec<T>` as a whole can be made `FromReflect`. It's also required by all fields of an enum as it's used as part of the `Reflect::apply` implementation. So in other words, much like `GetTypeRegistration` and `Typed`, it is very much a core reflection trait. The problem is that it is not currently treated like a core trait and is not automatically derived alongside `Reflect`. This makes using it a bit cumbersome and easy to forget. ## Solution Automatically derive `FromReflect` when deriving `Reflect`. Users can then choose to opt-out if needed using the `#[reflect(from_reflect = false)]` attribute. ```rust #[derive(Reflect)] struct Foo; #[derive(Reflect)] #[reflect(from_reflect = false)] struct Bar; fn test<T: FromReflect>(value: T) {} test(Foo); // <-- OK test(Bar); // <-- Panic! Bar does not implement trait `FromReflect` ``` #### `ReflectFromReflect` This PR also automatically adds the `ReflectFromReflect` (introduced in #6245) registration to the derived `GetTypeRegistration` impl— if the type hasn't opted out of `FromReflect` of course. <details> <summary><h4>Improved Deserialization</h4></summary> > **Warning** > This section includes changes that have since been descoped from this PR. They will likely be implemented again in a followup PR. I am mainly leaving these details in for archival purposes, as well as for reference when implementing this logic again. And since we can do all the above, we might as well improve deserialization. We can now choose to deserialize into a Dynamic type or automatically convert it using `FromReflect` under the hood. `[Un]TypedReflectDeserializer::new` will now perform the conversion and return the `Box`'d Real type. `[Un]TypedReflectDeserializer::new_dynamic` will work like what we have now and simply return the `Box`'d Dynamic type. ```rust // Returns the Real type let reflect_deserializer = UntypedReflectDeserializer::new(&registry); let mut deserializer = ron::de::Deserializer::from_str(input)?; let output: SomeStruct = reflect_deserializer.deserialize(&mut deserializer)?.take()?; // Returns the Dynamic type let reflect_deserializer = UntypedReflectDeserializer::new_dynamic(&registry); let mut deserializer = ron::de::Deserializer::from_str(input)?; let output: DynamicStruct = reflect_deserializer.deserialize(&mut deserializer)?.take()?; ``` </details> --- ## Changelog * `FromReflect` is now automatically derived within the `Reflect` derive macro * This includes auto-registering `ReflectFromReflect` in the derived `GetTypeRegistration` impl * ~~Renamed `TypedReflectDeserializer::new` and `UntypedReflectDeserializer::new` to `TypedReflectDeserializer::new_dynamic` and `UntypedReflectDeserializer::new_dynamic`, respectively~~ **Descoped** * ~~Changed `TypedReflectDeserializer::new` and `UntypedReflectDeserializer::new` to automatically convert the deserialized output using `FromReflect`~~ **Descoped** ## Migration Guide * `FromReflect` is now automatically derived within the `Reflect` derive macro. Items with both derives will need to remove the `FromReflect` one. ```rust // OLD #[derive(Reflect, FromReflect)] struct Foo; // NEW #[derive(Reflect)] struct Foo; ``` If using a manual implementation of `FromReflect` and the `Reflect` derive, users will need to opt-out of the automatic implementation. ```rust // OLD #[derive(Reflect)] struct Foo; impl FromReflect for Foo {/* ... */} // NEW #[derive(Reflect)] #[reflect(from_reflect = false)] struct Foo; impl FromReflect for Foo {/* ... */} ``` <details> <summary><h4>Removed Migrations</h4></summary> > **Warning** > This section includes changes that have since been descoped from this PR. They will likely be implemented again in a followup PR. I am mainly leaving these details in for archival purposes, as well as for reference when implementing this logic again. * The reflect deserializers now perform a `FromReflect` conversion internally. The expected output of `TypedReflectDeserializer::new` and `UntypedReflectDeserializer::new` is no longer a Dynamic (e.g., `DynamicList`), but its Real counterpart (e.g., `Vec<i32>`). ```rust let reflect_deserializer = UntypedReflectDeserializer::new_dynamic(&registry); let mut deserializer = ron::de::Deserializer::from_str(input)?; // OLD let output: DynamicStruct = reflect_deserializer.deserialize(&mut deserializer)?.take()?; // NEW let output: SomeStruct = reflect_deserializer.deserialize(&mut deserializer)?.take()?; ``` Alternatively, if this behavior isn't desired, use the `TypedReflectDeserializer::new_dynamic` and `UntypedReflectDeserializer::new_dynamic` methods instead: ```rust // OLD let reflect_deserializer = UntypedReflectDeserializer::new(&registry); // NEW let reflect_deserializer = UntypedReflectDeserializer::new_dynamic(&registry); ``` </details> --------- Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2023-06-29 01:31:34 +00:00
/// The `FromReflect` attributes on this type.
#[allow(clippy::wrong_self_convention)]
pub fn from_reflect(&self) -> &FromReflectAttrs {
bevy_reflect_derive: Clean up attribute logic (#11777) # Objective The code in `bevy_reflect_derive` could use some cleanup. ## Solution Took some of the changes in #11659 to create a dedicated PR for cleaning up the field and container attribute logic. #### Updated Naming I renamed `ReflectTraits` and `ReflectFieldAttr` to `ContainerAttributes` and `FieldAttributes`, respectively. I think these are clearer. #### Updated Parsing ##### Readability The parsing logic wasn't too bad before, but it was getting difficult to read. There was some duplicated logic between `Meta::List` and `Meta::Path` attributes. Additionally, all the logic was kept inside a large method. To simply things, I replaced the nested meta parsing with `ParseStream` parsing. In my opinion, this is easier to follow since it breaks up the large match statement into a small set of single-line if statements, where each if-block contains a single call to the appropriate attribute parsing method. ##### Flexibility On top of the added simplicity, this also makes our attribute parsing much more flexible. It allows us to more elegantly handle custom where clauses (i.e. `#[reflect(where T: Foo)]`) and it opens the door for more non-standard attribute syntax (e.g. #11659). ##### Errors This also allows us to automatically provide certain errors when parsing. For example, since we can use `stream.lookahead1()`, we get errors like the following for free: ``` error: expected one of: `ignore`, `skip_serializing`, `default` --> crates/bevy_reflect/src/lib.rs:1988:23 | 1988 | #[reflect(foo)] | ^^^ ``` --- ## Changelog > [!note] > All changes are internal to `bevy_reflect_derive` and should not affect the public API[^1]. - Renamed `ReflectTraits` to `ContainerAttributes` - Renamed `ReflectMeta::traits` to `ReflectMeta::attrs` - Renamed `ReflectFieldAttr` to `FieldAttributes` - Updated parsing logic for field/container attribute parsing - Now uses a `ParseStream` directly instead of nested meta parsing - General code cleanup of the field/container attribute modules for `bevy_reflect_derive` [^1]: Does not include errors, which may look slightly different. --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
2024-02-12 15:16:27 +00:00
self.attrs.from_reflect_attrs()
bevy_reflect: `FromReflect` Ergonomics Implementation (#6056) # Objective **This implementation is based on https://github.com/bevyengine/rfcs/pull/59.** --- Resolves #4597 Full details and motivation can be found in the RFC, but here's a brief summary. `FromReflect` is a very powerful and important trait within the reflection API. It allows Dynamic types (e.g., `DynamicList`, etc.) to be formed into Real ones (e.g., `Vec<i32>`, etc.). This mainly comes into play concerning deserialization, where the reflection deserializers both return a `Box<dyn Reflect>` that almost always contain one of these Dynamic representations of a Real type. To convert this to our Real type, we need to use `FromReflect`. It also sneaks up in other ways. For example, it's a required bound for `T` in `Vec<T>` so that `Vec<T>` as a whole can be made `FromReflect`. It's also required by all fields of an enum as it's used as part of the `Reflect::apply` implementation. So in other words, much like `GetTypeRegistration` and `Typed`, it is very much a core reflection trait. The problem is that it is not currently treated like a core trait and is not automatically derived alongside `Reflect`. This makes using it a bit cumbersome and easy to forget. ## Solution Automatically derive `FromReflect` when deriving `Reflect`. Users can then choose to opt-out if needed using the `#[reflect(from_reflect = false)]` attribute. ```rust #[derive(Reflect)] struct Foo; #[derive(Reflect)] #[reflect(from_reflect = false)] struct Bar; fn test<T: FromReflect>(value: T) {} test(Foo); // <-- OK test(Bar); // <-- Panic! Bar does not implement trait `FromReflect` ``` #### `ReflectFromReflect` This PR also automatically adds the `ReflectFromReflect` (introduced in #6245) registration to the derived `GetTypeRegistration` impl— if the type hasn't opted out of `FromReflect` of course. <details> <summary><h4>Improved Deserialization</h4></summary> > **Warning** > This section includes changes that have since been descoped from this PR. They will likely be implemented again in a followup PR. I am mainly leaving these details in for archival purposes, as well as for reference when implementing this logic again. And since we can do all the above, we might as well improve deserialization. We can now choose to deserialize into a Dynamic type or automatically convert it using `FromReflect` under the hood. `[Un]TypedReflectDeserializer::new` will now perform the conversion and return the `Box`'d Real type. `[Un]TypedReflectDeserializer::new_dynamic` will work like what we have now and simply return the `Box`'d Dynamic type. ```rust // Returns the Real type let reflect_deserializer = UntypedReflectDeserializer::new(&registry); let mut deserializer = ron::de::Deserializer::from_str(input)?; let output: SomeStruct = reflect_deserializer.deserialize(&mut deserializer)?.take()?; // Returns the Dynamic type let reflect_deserializer = UntypedReflectDeserializer::new_dynamic(&registry); let mut deserializer = ron::de::Deserializer::from_str(input)?; let output: DynamicStruct = reflect_deserializer.deserialize(&mut deserializer)?.take()?; ``` </details> --- ## Changelog * `FromReflect` is now automatically derived within the `Reflect` derive macro * This includes auto-registering `ReflectFromReflect` in the derived `GetTypeRegistration` impl * ~~Renamed `TypedReflectDeserializer::new` and `UntypedReflectDeserializer::new` to `TypedReflectDeserializer::new_dynamic` and `UntypedReflectDeserializer::new_dynamic`, respectively~~ **Descoped** * ~~Changed `TypedReflectDeserializer::new` and `UntypedReflectDeserializer::new` to automatically convert the deserialized output using `FromReflect`~~ **Descoped** ## Migration Guide * `FromReflect` is now automatically derived within the `Reflect` derive macro. Items with both derives will need to remove the `FromReflect` one. ```rust // OLD #[derive(Reflect, FromReflect)] struct Foo; // NEW #[derive(Reflect)] struct Foo; ``` If using a manual implementation of `FromReflect` and the `Reflect` derive, users will need to opt-out of the automatic implementation. ```rust // OLD #[derive(Reflect)] struct Foo; impl FromReflect for Foo {/* ... */} // NEW #[derive(Reflect)] #[reflect(from_reflect = false)] struct Foo; impl FromReflect for Foo {/* ... */} ``` <details> <summary><h4>Removed Migrations</h4></summary> > **Warning** > This section includes changes that have since been descoped from this PR. They will likely be implemented again in a followup PR. I am mainly leaving these details in for archival purposes, as well as for reference when implementing this logic again. * The reflect deserializers now perform a `FromReflect` conversion internally. The expected output of `TypedReflectDeserializer::new` and `UntypedReflectDeserializer::new` is no longer a Dynamic (e.g., `DynamicList`), but its Real counterpart (e.g., `Vec<i32>`). ```rust let reflect_deserializer = UntypedReflectDeserializer::new_dynamic(&registry); let mut deserializer = ron::de::Deserializer::from_str(input)?; // OLD let output: DynamicStruct = reflect_deserializer.deserialize(&mut deserializer)?.take()?; // NEW let output: SomeStruct = reflect_deserializer.deserialize(&mut deserializer)?.take()?; ``` Alternatively, if this behavior isn't desired, use the `TypedReflectDeserializer::new_dynamic` and `UntypedReflectDeserializer::new_dynamic` methods instead: ```rust // OLD let reflect_deserializer = UntypedReflectDeserializer::new(&registry); // NEW let reflect_deserializer = UntypedReflectDeserializer::new_dynamic(&registry); ``` </details> --------- Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2023-06-29 01:31:34 +00:00
}
bevy_reflect: Split `#[reflect(where)]` (#11597) # Objective Revert the changes to type parameter bounds introduced in #9046, improves the `#[reflect(where)]` attribute (also from #9046), and adds the ability to opt out of field bounds. This is based on suggestions by @soqb and discussion on [Discord](https://discord.com/channels/691052431525675048/1002362493634629796/1201227833826103427). ## Solution Reverts the changes to type parameter bounds when deriving `Reflect`, introduced in #9046. This was originally done as a means of fixing a recursion issue (#8965). However, as @soqb pointed out, we could achieve the same result by simply making an opt-out attribute instead of messing with the type parameter bounds. This PR has four main changes: 1. Reverts the type parameter bounds from #9046 2. Includes `TypePath` as a default bound for active fields 3. Changes `#reflect(where)]` to be strictly additive 4. Adds `#reflect(no_field_bounds)]` to opt out of field bounds Change 1 means that, like before, type parameters only receive at most the `TypePath` bound (if `#[reflect(type_path = false)]` is not present) and active fields receive the `Reflect` or `FromReflect` bound. And with Change 2, they will also receive `TypePath` (since it's indirectly required by `Typed` to construct `NamedField` and `UnnamedField` instances). Change 3 was made to make room for Change 4. By splitting out the responsibility of `#reflect(where)]`, we can use it with or without `#reflect(no_field_bounds)]` for various use cases. For example, if we hadn't done this, the following would have failed: ```rust // Since we're not using `#reflect(no_field_bounds)]`, // `T::Assoc` is automatically given the required bounds // of `FromReflect + TypePath` #[derive(Reflect)] #[reflect(where T::Assoc: OtherTrait)] struct Foo<T: MyTrait> { value: T::Assoc, } ``` This provides more flexibility to the user while still letting them add or remove most trait bounds. And to solve the original recursion issue, we can do: ```rust #[derive(Reflect)] #[reflect(no_field_bounds)] // <-- Added struct Foo { foo: Vec<Foo> } ``` #### Bounds All in all, we now have four sets of trait bounds: - `Self` gets the bounds `Any + Send + Sync` - Type parameters get the bound `TypePath`. This can be opted out of with `#[reflect(type_path = false)]` - Active fields get the bounds `TypePath` and `FromReflect`/`Reflect` bounds. This can be opted out of with `#reflect(no_field_bounds)]` - Custom bounds can be added with `#[reflect(where)]` --- ## Changelog - Revert some changes #9046 - `#reflect(where)]` is now strictly additive - Added `#reflect(no_field_bounds)]` attribute to opt out of automatic field trait bounds when deriving `Reflect` - Made the `TypePath` requirement on fields when deriving `Reflect` more explicit ## Migration Guide > [!important] > This PR shouldn't be a breaking change relative to the current version of Bevy (v0.12). And since it removes the breaking parts of #9046, that PR also won't need a migration guide.
2024-01-29 17:54:17 +00:00
/// The `TypePath` attributes on this type.
pub fn type_path_attrs(&self) -> &TypePathAttrs {
bevy_reflect_derive: Clean up attribute logic (#11777) # Objective The code in `bevy_reflect_derive` could use some cleanup. ## Solution Took some of the changes in #11659 to create a dedicated PR for cleaning up the field and container attribute logic. #### Updated Naming I renamed `ReflectTraits` and `ReflectFieldAttr` to `ContainerAttributes` and `FieldAttributes`, respectively. I think these are clearer. #### Updated Parsing ##### Readability The parsing logic wasn't too bad before, but it was getting difficult to read. There was some duplicated logic between `Meta::List` and `Meta::Path` attributes. Additionally, all the logic was kept inside a large method. To simply things, I replaced the nested meta parsing with `ParseStream` parsing. In my opinion, this is easier to follow since it breaks up the large match statement into a small set of single-line if statements, where each if-block contains a single call to the appropriate attribute parsing method. ##### Flexibility On top of the added simplicity, this also makes our attribute parsing much more flexible. It allows us to more elegantly handle custom where clauses (i.e. `#[reflect(where T: Foo)]`) and it opens the door for more non-standard attribute syntax (e.g. #11659). ##### Errors This also allows us to automatically provide certain errors when parsing. For example, since we can use `stream.lookahead1()`, we get errors like the following for free: ``` error: expected one of: `ignore`, `skip_serializing`, `default` --> crates/bevy_reflect/src/lib.rs:1988:23 | 1988 | #[reflect(foo)] | ^^^ ``` --- ## Changelog > [!note] > All changes are internal to `bevy_reflect_derive` and should not affect the public API[^1]. - Renamed `ReflectTraits` to `ContainerAttributes` - Renamed `ReflectMeta::traits` to `ReflectMeta::attrs` - Renamed `ReflectFieldAttr` to `FieldAttributes` - Updated parsing logic for field/container attribute parsing - Now uses a `ParseStream` directly instead of nested meta parsing - General code cleanup of the field/container attribute modules for `bevy_reflect_derive` [^1]: Does not include errors, which may look slightly different. --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
2024-02-12 15:16:27 +00:00
self.attrs.type_path_attrs()
bevy_reflect: Split `#[reflect(where)]` (#11597) # Objective Revert the changes to type parameter bounds introduced in #9046, improves the `#[reflect(where)]` attribute (also from #9046), and adds the ability to opt out of field bounds. This is based on suggestions by @soqb and discussion on [Discord](https://discord.com/channels/691052431525675048/1002362493634629796/1201227833826103427). ## Solution Reverts the changes to type parameter bounds when deriving `Reflect`, introduced in #9046. This was originally done as a means of fixing a recursion issue (#8965). However, as @soqb pointed out, we could achieve the same result by simply making an opt-out attribute instead of messing with the type parameter bounds. This PR has four main changes: 1. Reverts the type parameter bounds from #9046 2. Includes `TypePath` as a default bound for active fields 3. Changes `#reflect(where)]` to be strictly additive 4. Adds `#reflect(no_field_bounds)]` to opt out of field bounds Change 1 means that, like before, type parameters only receive at most the `TypePath` bound (if `#[reflect(type_path = false)]` is not present) and active fields receive the `Reflect` or `FromReflect` bound. And with Change 2, they will also receive `TypePath` (since it's indirectly required by `Typed` to construct `NamedField` and `UnnamedField` instances). Change 3 was made to make room for Change 4. By splitting out the responsibility of `#reflect(where)]`, we can use it with or without `#reflect(no_field_bounds)]` for various use cases. For example, if we hadn't done this, the following would have failed: ```rust // Since we're not using `#reflect(no_field_bounds)]`, // `T::Assoc` is automatically given the required bounds // of `FromReflect + TypePath` #[derive(Reflect)] #[reflect(where T::Assoc: OtherTrait)] struct Foo<T: MyTrait> { value: T::Assoc, } ``` This provides more flexibility to the user while still letting them add or remove most trait bounds. And to solve the original recursion issue, we can do: ```rust #[derive(Reflect)] #[reflect(no_field_bounds)] // <-- Added struct Foo { foo: Vec<Foo> } ``` #### Bounds All in all, we now have four sets of trait bounds: - `Self` gets the bounds `Any + Send + Sync` - Type parameters get the bound `TypePath`. This can be opted out of with `#[reflect(type_path = false)]` - Active fields get the bounds `TypePath` and `FromReflect`/`Reflect` bounds. This can be opted out of with `#reflect(no_field_bounds)]` - Custom bounds can be added with `#[reflect(where)]` --- ## Changelog - Revert some changes #9046 - `#reflect(where)]` is now strictly additive - Added `#reflect(no_field_bounds)]` attribute to opt out of automatic field trait bounds when deriving `Reflect` - Made the `TypePath` requirement on fields when deriving `Reflect` more explicit ## Migration Guide > [!important] > This PR shouldn't be a breaking change relative to the current version of Bevy (v0.12). And since it removes the breaking parts of #9046, that PR also won't need a migration guide.
2024-01-29 17:54:17 +00:00
}
reflect: `TypePath` part 2 (#8768) # Objective - Followup to #7184. - ~Deprecate `TypeUuid` and remove its internal references.~ No longer part of this PR. - Use `TypePath` for the type registry, and (de)serialisation instead of `std::any::type_name`. - Allow accessing type path information behind proxies. ## Solution - Introduce methods on `TypeInfo` and friends for dynamically querying type path. These methods supersede the old `type_name` methods. - Remove `Reflect::type_name` in favor of `DynamicTypePath::type_path` and `TypeInfo::type_path_table`. - Switch all uses of `std::any::type_name` in reflection, non-debugging contexts to use `TypePath`. --- ## Changelog - Added `TypePathTable` for dynamically accessing methods on `TypePath` through `TypeInfo` and the type registry. - Removed `type_name` from all `TypeInfo`-like structs. - Added `type_path` and `type_path_table` methods to all `TypeInfo`-like structs. - Removed `Reflect::type_name` in favor of `DynamicTypePath::reflect_type_path` and `TypeInfo::type_path`. - Changed the signature of all `DynamicTypePath` methods to return strings with a static lifetime. ## Migration Guide - Rely on `TypePath` instead of `std::any::type_name` for all stability guarantees and for use in all reflection contexts, this is used through with one of the following APIs: - `TypePath::type_path` if you have a concrete type and not a value. - `DynamicTypePath::reflect_type_path` if you have an `dyn Reflect` value without a concrete type. - `TypeInfo::type_path` for use through the registry or if you want to work with the represented type of a `DynamicFoo`. - Remove `type_name` from manual `Reflect` implementations. - Use `type_path` and `type_path_table` in place of `type_name` on `TypeInfo`-like structs. - Use `get_with_type_path(_mut)` over `get_with_type_name(_mut)`. ## Note to reviewers I think if anything we were a little overzealous in merging #7184 and we should take that extra care here. In my mind, this is the "point of no return" for `TypePath` and while I think we all agree on the design, we should carefully consider if the finer details and current implementations are actually how we want them moving forward. For example [this incorrect `TypePath` implementation for `String`](https://github.com/soqb/bevy/blob/3fea3c6c0b5719dfbd3d4230f5282ec80d82556a/crates/bevy_reflect/src/impls/std.rs#L90) (note that `String` is in the default Rust prelude) snuck in completely under the radar.
2023-10-09 19:33:03 +00:00
/// The path to this type.
reflect: stable type path v2 (#7184) # Objective - Introduce a stable alternative to [`std::any::type_name`](https://doc.rust-lang.org/std/any/fn.type_name.html). - Rewrite of #5805 with heavy inspiration in design. - On the path to #5830. - Part of solving #3327. ## Solution - Add a `TypePath` trait for static stable type path/name information. - Add a `TypePath` derive macro. - Add a `impl_type_path` macro for implementing internal and foreign types in `bevy_reflect`. --- ## Changelog - Added `TypePath` trait. - Added `DynamicTypePath` trait and `get_type_path` method to `Reflect`. - Added a `TypePath` derive macro. - Added a `bevy_reflect::impl_type_path` for implementing `TypePath` on internal and foreign types in `bevy_reflect`. - Changed `bevy_reflect::utility::(Non)GenericTypeInfoCell` to `(Non)GenericTypedCell<T>` which allows us to be generic over both `TypeInfo` and `TypePath`. - `TypePath` is now a supertrait of `Asset`, `Material` and `Material2d`. - `impl_reflect_struct` needs a `#[type_path = "..."]` attribute to be specified. - `impl_reflect_value` needs to either specify path starting with a double colon (`::core::option::Option`) or an `in my_crate::foo` declaration. - Added `bevy_reflect_derive::ReflectTypePath`. - Most uses of `Ident` in `bevy_reflect_derive` changed to use `ReflectTypePath`. ## Migration Guide - Implementors of `Asset`, `Material` and `Material2d` now also need to derive `TypePath`. - Manual implementors of `Reflect` will need to implement the new `get_type_path` method. ## Open Questions - [x] ~This PR currently does not migrate any usages of `std::any::type_name` to use `bevy_reflect::TypePath` to ease the review process. Should it?~ Migration will be left to a follow-up PR. - [ ] This PR adds a lot of `#[derive(TypePath)]` and `T: TypePath` to satisfy new bounds, mostly when deriving `TypeUuid`. Should we make `TypePath` a supertrait of `TypeUuid`? [Should we remove `TypeUuid` in favour of `TypePath`?](https://github.com/bevyengine/bevy/pull/5805/files/2afbd855327c4b68e0a6b6f03118f289988441a4#r961067892)
2023-06-05 20:31:20 +00:00
pub fn type_path(&self) -> &ReflectTypePath<'a> {
&self.type_path
bevy_reflect_derive: Tidying up the code (#4712) # Objective The `bevy_reflect_derive` crate is not the cleanest or easiest to follow/maintain. The `lib.rs` file is especially difficult with over 1000 lines of code written in a confusing order. This is just a result of growth within the crate and it would be nice to clean it up for future work. ## Solution Split `bevy_reflect_derive` into many more submodules. The submodules include: * `container_attributes` - Code relating to container attributes * `derive_data` - Code relating to reflection-based derive metadata * `field_attributes` - Code relating to field attributes * `impls` - Code containing actual reflection implementations * `reflect_value` - Code relating to reflection-based value metadata * `registration` - Code relating to type registration * `utility` - General-purpose utility functions This leaves the `lib.rs` file to contain only the public macros, making it much easier to digest (and fewer than 200 lines). By breaking up the code into smaller modules, we make it easier for future contributors to find the code they're looking for or identify which module best fits their own additions. ### Metadata Structs This cleanup also adds two big metadata structs: `ReflectFieldAttr` and `ReflectDeriveData`. The former is used to store all attributes for a struct field (if any). The latter is used to store all metadata for struct-based derive inputs. Both significantly reduce code duplication and make editing these macros much simpler. The tradeoff is that we may collect more metadata than needed. However, this is usually a small thing (such as checking for attributes when they're not really needed or creating a `ReflectFieldAttr` for every field regardless of whether they actually have an attribute). We could try to remove these tradeoffs and squeeze some more performance out, but doing so might come at the cost of developer experience. Personally, I think it's much nicer to create a `ReflectFieldAttr` for every field since it means I don't have to do two `Option` checks. Others may disagree, though, and so we can discuss changing this either in this PR or in a future one. ### Out of Scope _Some_ documentation has been added or improved, but ultimately good docs are probably best saved for a dedicated PR. ## 🔍 Focus Points (for reviewers) I know it's a lot to sift through, so here is a list of **key points for reviewers**: - The following files contain code that was mostly just relocated: - `reflect_value.rs` - `registration.rs` - `container_attributes.rs` was also mostly moved but features some general cleanup (reducing nesting, removing hardcoded strings, etc.) and lots of doc comments - Most impl logic was moved from `lib.rs` to `impls.rs`, but they have been significantly modified to use the new `ReflectDeriveData` metadata struct in order to reduce duplication. - `derive_data.rs` and `field_attributes.rs` contain almost entirely new code and should probably be given the most attention. - Likewise, `from_reflect.rs` saw major changes using `ReflectDeriveData` so it should also be given focus. - There was no change to the `lib.rs` exports so the end-user API should be the same. ## Prior Work This task was initially tackled by @NathanSWard in #2377 (which was closed in favor of this PR), so hats off to them for beating me to the punch by nearly a year! --- ## Changelog * **[INTERNAL]** Split `bevy_reflect_derive` into smaller submodules * **[INTERNAL]** Add `ReflectFieldAttr` * **[INTERNAL]** Add `ReflectDeriveData` * Add `BevyManifest::get_path_direct()` method (`bevy_macro_utils`) Co-authored-by: MrGVSV <49806985+MrGVSV@users.noreply.github.com>
2022-05-12 19:43:23 +00:00
}
bevy_reflect: Reflect remote types (#6042) # Objective The goal with this PR is to allow the use of types that don't implement `Reflect` within the reflection API. Rust's [orphan rule](https://doc.rust-lang.org/book/ch10-02-traits.html#implementing-a-trait-on-a-type) prevents implementing a trait on an external type when neither type nor trait are owned by the implementor. This means that if a crate, `cool_rust_lib`, defines a type, `Foo`, then a user cannot use it with reflection. What this means is that we have to ignore it most of the time: ```rust #[derive(Reflect)] struct SomeStruct { #[reflect(ignore)] data: cool_rust_lib::Foo } ``` Obviously, it's impossible to implement `Reflect` on `Foo`. But does it *have* to be? Most of reflection doesn't deal with concrete types— it's almost all using `dyn Reflect`. And being very metadata-driven, it should theoretically be possible. I mean, [`serde`](https://serde.rs/remote-derive.html) does it. ## Solution > Special thanks to @danielhenrymantilla for their help reviewing this PR and offering wisdom wrt safety. Taking a page out of `serde`'s book, this PR adds the ability to easily use "remote types" with reflection. In this context, a "remote type" is the external type for which we have no ability to implement `Reflect`. This adds the `#[reflect_remote(...)]` attribute macro, which is used to generate "remote type wrappers". All you have to do is define the wrapper exactly the same as the remote type's definition: ```rust // Pretend this is our external crate mod cool_rust_lib { #[derive(Default)] struct Foo { pub value: String } } #[reflect_remote(cool_rust_lib::Foo)] struct FooWrapper { pub value: String } ``` > **Note:** All fields in the external type *must* be public. This could be addressed with a separate getter/setter attribute either in this PR or in another one. The macro takes this user-defined item and transforms it into a newtype wrapper around the external type, marking it as `#[repr(transparent)]`. The fields/variants defined by the user are simply used to build out the reflection impls. Additionally, it generates an implementation of the new trait, `ReflectRemote`, which helps prevent accidental misuses of this API. Therefore, the output generated by the macro would look something like: ```rust #[repr(transparent)] struct FooWrapper(pub cool_rust_lib::Foo); impl ReflectRemote for FooWrapper { type Remote = cool_rust_lib::Foo; // transmutation methods... } // reflection impls... // these will acknowledge and make use of the `value` field ``` Internally, the reflection API will pass around the `FooWrapper` and [transmute](https://doc.rust-lang.org/std/mem/fn.transmute.html) it where necessary. All we have to do is then tell `Reflect` to do that. So rather than ignoring the field, we tell `Reflect` to use our wrapper using the `#[reflect(remote = ...)]` field attribute: ```rust #[derive(Reflect)] struct SomeStruct { #[reflect(remote = FooWrapper)] data: cool_rust_lib::Foo } ``` #### Other Macros & Type Data Because this macro consumes the defined item and generates a new one, we can't just put our macros anywhere. All macros that should be passed to the generated struct need to come *below* this macro. For example, to derive `Default` and register its associated type data: ```rust // ✅ GOOD #[reflect_remote(cool_rust_lib::Foo)] #[derive(Default)] #[reflect(Default)] struct FooWrapper { pub value: String } // ❌ BAD #[derive(Default)] #[reflect_remote(cool_rust_lib::Foo)] #[reflect(Default)] struct FooWrapper { pub value: String } ``` #### Generics Generics are forwarded to the generated struct as well. They should also be defined in the same order: ```rust #[reflect_remote(RemoteGeneric<'a, T1, T2>)] struct GenericWrapper<'a, T1, T2> { pub foo: &'a T1, pub bar: &'a T2, } ``` > Naming does *not* need to match the original definition's. Only order matters here. > Also note that the code above is just a demonstration and doesn't actually compile since we'd need to enforce certain bounds (e.g. `T1: Reflect`, `'a: 'static`, etc.) #### Nesting And, yes, you can nest remote types: ```rust #[reflect_remote(RemoteOuter)] struct OuterWrapper { #[reflect(remote = InnerWrapper)] pub inner: RemoteInner } #[reflect_remote(RemoteInner)] struct InnerWrapper(usize); ``` #### Assertions This macro will also generate some compile-time assertions to ensure that the correct types are used. It's important we catch this early so users don't have to wait for something to panic. And it also helps keep our `unsafe` a little safer. For example, a wrapper definition that does not match its corresponding remote type will result in an error: ```rust mod external_crate { pub struct TheirStruct(pub u32); } #[reflect_remote(external_crate::TheirStruct)] struct MyStruct(pub String); // ERROR: expected type `u32` but found `String` ``` <details> <summary>Generated Assertion</summary> ```rust const _: () = { #[allow(non_snake_case)] #[allow(unused_variables)] #[allow(unused_assignments)] #[allow(unreachable_patterns)] #[allow(clippy::multiple_bound_locations)] fn assert_wrapper_definition_matches_remote_type( mut __remote__: external_crate::TheirStruct, ) { __remote__.0 = (|| -> ::core::option::Option<String> { None })().unwrap(); } }; ``` </details> Additionally, using the incorrect type in a `#[reflect(remote = ...)]` attribute should result in an error: ```rust mod external_crate { pub struct TheirFoo(pub u32); pub struct TheirBar(pub i32); } #[reflect_remote(external_crate::TheirFoo)] struct MyFoo(pub u32); #[reflect_remote(external_crate::TheirBar)] struct MyBar(pub i32); #[derive(Reflect)] struct MyStruct { #[reflect(remote = MyBar)] // ERROR: expected type `TheirFoo` but found struct `TheirBar` foo: external_crate::TheirFoo } ``` <details> <summary>Generated Assertion</summary> ```rust const _: () = { struct RemoteFieldAssertions; impl RemoteFieldAssertions { #[allow(non_snake_case)] #[allow(clippy::multiple_bound_locations)] fn assert__foo__is_valid_remote() { let _: <MyBar as bevy_reflect::ReflectRemote>::Remote = (|| -> ::core::option::Option<external_crate::TheirFoo> { None })().unwrap(); } } }; ``` </details> ### Discussion There are a couple points that I think still need discussion or validation. - [x] 1. `Any` shenanigans ~~If we wanted to downcast our remote type from a `dyn Reflect`, we'd have to first downcast to the wrapper then extract the inner type. This PR has a [commit](b840db9f74cb6d357f951cb11b150d46bac89ee2) that addresses this by making all the `Reflect::*any` methods return the inner type rather than the wrapper type. This allows us to downcast directly to our remote type.~~ ~~However, I'm not sure if this is something we want to do. For unknowing users, it could be confusing and seemingly inconsistent. Is it worth keeping? Or should this behavior be removed?~~ I think this should be fine. The remote wrapper is an implementation detail and users should not need to downcast to the wrapper type. Feel free to let me know if there are other opinions on this though! - [x] 2. Implementing `Deref/DerefMut` and `From` ~~We don't currently do this, but should we implement other traits on the generated transparent struct? We could implement `Deref`/`DerefMut` to easily access the inner type. And we could implement `From` for easier conversion between the two types (e.g. `T: Into<Foo>`).~~ As mentioned in the comments, we probably don't need to do this. Again, the remote wrapper is an implementation detail, and should generally not be used directly. - [x] 3. ~~Should we define a getter/setter field attribute in this PR as well or leave it for a future one?~~ I think this should be saved for a future PR - [ ] 4. Any foreseeable issues with this implementation? #### Alternatives One alternative to defining our own `ReflectRemote` would be to use [bytemuck's `TransparentWrapper`](https://docs.rs/bytemuck/1.13.1/bytemuck/trait.TransparentWrapper.html) (as suggested by @danielhenrymantilla). This is definitely a viable option, as `ReflectRemote` is pretty much the same thing as `TransparentWrapper`. However, the cost would be bringing in a new crate— though, it is already in use in a few other sub-crates like bevy_render. I think we're okay just defining `ReflectRemote` ourselves, but we can go the bytemuck route if we'd prefer offloading that work to another crate. --- ## Changelog * Added the `#[reflect_remote(...)]` attribute macro to allow `Reflect` to be used on remote types * Added `ReflectRemote` trait for ensuring proper remote wrapper usage
2024-08-12 19:12:53 +00:00
/// Get the remote type path, if any.
pub fn remote_ty(&self) -> Option<RemoteType> {
self.remote_ty
}
/// Whether this reflected type represents a remote type or not.
pub fn is_remote_wrapper(&self) -> bool {
self.remote_ty.is_some()
}
bevy_reflect_derive: Tidying up the code (#4712) # Objective The `bevy_reflect_derive` crate is not the cleanest or easiest to follow/maintain. The `lib.rs` file is especially difficult with over 1000 lines of code written in a confusing order. This is just a result of growth within the crate and it would be nice to clean it up for future work. ## Solution Split `bevy_reflect_derive` into many more submodules. The submodules include: * `container_attributes` - Code relating to container attributes * `derive_data` - Code relating to reflection-based derive metadata * `field_attributes` - Code relating to field attributes * `impls` - Code containing actual reflection implementations * `reflect_value` - Code relating to reflection-based value metadata * `registration` - Code relating to type registration * `utility` - General-purpose utility functions This leaves the `lib.rs` file to contain only the public macros, making it much easier to digest (and fewer than 200 lines). By breaking up the code into smaller modules, we make it easier for future contributors to find the code they're looking for or identify which module best fits their own additions. ### Metadata Structs This cleanup also adds two big metadata structs: `ReflectFieldAttr` and `ReflectDeriveData`. The former is used to store all attributes for a struct field (if any). The latter is used to store all metadata for struct-based derive inputs. Both significantly reduce code duplication and make editing these macros much simpler. The tradeoff is that we may collect more metadata than needed. However, this is usually a small thing (such as checking for attributes when they're not really needed or creating a `ReflectFieldAttr` for every field regardless of whether they actually have an attribute). We could try to remove these tradeoffs and squeeze some more performance out, but doing so might come at the cost of developer experience. Personally, I think it's much nicer to create a `ReflectFieldAttr` for every field since it means I don't have to do two `Option` checks. Others may disagree, though, and so we can discuss changing this either in this PR or in a future one. ### Out of Scope _Some_ documentation has been added or improved, but ultimately good docs are probably best saved for a dedicated PR. ## 🔍 Focus Points (for reviewers) I know it's a lot to sift through, so here is a list of **key points for reviewers**: - The following files contain code that was mostly just relocated: - `reflect_value.rs` - `registration.rs` - `container_attributes.rs` was also mostly moved but features some general cleanup (reducing nesting, removing hardcoded strings, etc.) and lots of doc comments - Most impl logic was moved from `lib.rs` to `impls.rs`, but they have been significantly modified to use the new `ReflectDeriveData` metadata struct in order to reduce duplication. - `derive_data.rs` and `field_attributes.rs` contain almost entirely new code and should probably be given the most attention. - Likewise, `from_reflect.rs` saw major changes using `ReflectDeriveData` so it should also be given focus. - There was no change to the `lib.rs` exports so the end-user API should be the same. ## Prior Work This task was initially tackled by @NathanSWard in #2377 (which was closed in favor of this PR), so hats off to them for beating me to the punch by nearly a year! --- ## Changelog * **[INTERNAL]** Split `bevy_reflect_derive` into smaller submodules * **[INTERNAL]** Add `ReflectFieldAttr` * **[INTERNAL]** Add `ReflectDeriveData` * Add `BevyManifest::get_path_direct()` method (`bevy_macro_utils`) Co-authored-by: MrGVSV <49806985+MrGVSV@users.noreply.github.com>
2022-05-12 19:43:23 +00:00
/// The cached `bevy_reflect` path.
pub fn bevy_reflect_path(&self) -> &Path {
&self.bevy_reflect_path
}
/// Returns the `GetTypeRegistration` impl as a `TokenStream`.
Enable deriving Reflect on structs with generic types (#7364) # Objective I recently had an issue, where I have a struct: ``` struct Property { inner: T } ``` that I use as a wrapper for internal purposes. I don't want to update my struct definition to ``` struct Property<T: Reflect>{ inner: T } ``` because I still want to be able to build `Property<T>` for types `T` that are not `Reflect`. (and also because I don't want to update my whole code base with `<T: Reflect>` bounds) I still wanted to have reflection on it (for `bevy_inspector_egui`), but adding `derive(Reflect)` fails with the error: `T cannot be sent between threads safely. T needs to implement Sync.` I believe that `bevy_reflect` should adopt the model of other derives in the case of generics, which is to add the `Reflect` implementation only if the generics also implement `Reflect`. (That is the behaviour of other macros such as `derive(Clone)` or `derive(Debug)`. It's also the current behavior of `derive(FromReflect)`. Basically doing something like: ``` impl<T> Reflect for Foo<T> where T: Reflect ``` ## Solution - I updated the derive macros for `Structs` and `TupleStructs` to add extra `where` bounds. - Every type that is reflected will need a `T: Reflect` bound - Ignored types will need a `T: 'static + Send + Sync` bound. Here's the reason. For cases like this: ``` #[derive(Reflect)] struct Foo<T, U>{ a: T #[reflect(ignore)] b: U } ``` I had to add the bound `'static + Send + Sync` to ignored generics like `U`. The reason is that we want `Foo<T, U>` to be `Reflect: 'static + Send + Sync`, so `Foo<T, U>` must be able to implement those auto-traits. `Foo<T, U>` will only implement those auto-traits if every generic type implements them, including ignored types. This means that the previously compile-fail case now compiles: ``` #[derive(Reflect)] struct Foo<'a> { #[reflect(ignore)] value: &'a str, } ``` But `Foo<'a>` will only be useable in the cases where `'a: 'static` and panic if we don't have `'a: 'static`, which is what we want (nice bonus from this PR ;) ) --- ## Changelog > This section is optional. If this was a trivial fix, or has no externally-visible impact, you can delete this section. ### Added Possibility to add `derive(Reflect)` to structs and enums that contain generic types, like so: ``` #[derive(Reflect)] struct Foo<T>{ a: T } ``` Reflection will only be available if the generic type T also implements `Reflect`. (previously, this would just return a compiler error)
2023-01-28 00:12:06 +00:00
pub fn get_type_registration(
&self,
where_clause_options: &WhereClauseOptions,
) -> proc_macro2::TokenStream {
bevy_reflect: Recursive registration (#5781) # Objective Resolves #4154 Currently, registration must all be done manually: ```rust #[derive(Reflect)] struct Foo(Bar); #[derive(Reflect)] struct Bar(Baz); #[derive(Reflect)] struct Baz(usize); fn main() { // ... app .register_type::<Foo>() .register_type::<Bar>() .register_type::<Baz>() // .register_type::<usize>() <- This one is handled by Bevy, thankfully // ... } ``` This can grow really quickly and become very annoying to add, remove, and update as types change. It would be great if we could help reduce the number of types that a user must manually implement themselves. ## Solution As suggested in #4154, this PR adds automatic recursive registration. Essentially, when a type is registered, it may now also choose to register additional types along with it using the new `GetTypeRegistration::register_type_dependencies` trait method. The `Reflect` derive macro now automatically does this for all fields in structs, tuple structs, struct variants, and tuple variants. This is also done for tuples, arrays, `Vec<T>`, `HashMap<K, V>`, and `Option<T>`. This allows us to simplify the code above like: ```rust #[derive(Reflect)] struct Foo(Bar); #[derive(Reflect)] struct Bar(Baz); #[derive(Reflect)] struct Baz(usize); fn main() { // ... app.register_type::<Foo>() // ... } ``` This automatic registration only occurs if the type has not yet been registered. If it has been registered, we simply skip it and move to the next one. This reduces the cost of registration and prevents overwriting customized registrations. ## Considerations While this does improve ergonomics on one front, it's important to look at some of the arguments against adopting a PR like this. #### Generic Bounds ~~Since we need to be able to register the fields individually, we need those fields to implement `GetTypeRegistration`. This forces users to then add this trait as a bound on their generic arguments. This annoyance could be relieved with something like #5772.~~ This is no longer a major issue as the `Reflect` derive now adds the `GetTypeRegistration` bound by default. This should technically be okay, since we already add the `Reflect` bound. However, this can also be considered a breaking change for manual implementations that left out a `GetTypeRegistration` impl ~~or for items that contain dynamic types (e.g. `DynamicStruct`) since those also do not implement `GetTypeRegistration`~~. #### Registration Assumptions By automatically registering fields, users might inadvertently be relying on certain types to be automatically registered. If `Foo` auto-registers `Bar`, but `Foo` is later removed from the code, then anywhere that previously used or relied on `Bar`'s registration would now fail. --- ## Changelog - Added recursive type registration to structs, tuple structs, struct variants, tuple variants, tuples, arrays, `Vec<T>`, `HashMap<K, V>`, and `Option<T>` - Added a new trait in the hidden `bevy_reflect::__macro_exports` module called `RegisterForReflection` - Added `GetTypeRegistration` impl for `bevy_render::render_asset::RenderAssetUsages` ## Migration Guide All types that derive `Reflect` will now automatically add `GetTypeRegistration` as a bound on all (unignored) fields. This means that all reflected fields will need to also implement `GetTypeRegistration`. If all fields **derive** `Reflect` or are implemented in `bevy_reflect`, this should not cause any issues. However, manual implementations of `Reflect` that excluded a `GetTypeRegistration` impl for their type will need to add one. ```rust #[derive(Reflect)] struct Foo<T: FromReflect> { data: MyCustomType<T> } // OLD impl<T: FromReflect> Reflect for MyCustomType<T> {/* ... */} // NEW impl<T: FromReflect + GetTypeRegistration> Reflect for MyCustomType<T> {/* ... */} impl<T: FromReflect + GetTypeRegistration> GetTypeRegistration for MyCustomType<T> {/* ... */} ``` --------- Co-authored-by: James Liu <contact@jamessliu.com> Co-authored-by: radiish <cb.setho@gmail.com> Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2024-03-04 19:04:10 +00:00
crate::registration::impl_get_type_registration(
self,
where_clause_options,
None,
Option::<std::iter::Empty<&Type>>::None,
)
bevy_reflect_derive: Tidying up the code (#4712) # Objective The `bevy_reflect_derive` crate is not the cleanest or easiest to follow/maintain. The `lib.rs` file is especially difficult with over 1000 lines of code written in a confusing order. This is just a result of growth within the crate and it would be nice to clean it up for future work. ## Solution Split `bevy_reflect_derive` into many more submodules. The submodules include: * `container_attributes` - Code relating to container attributes * `derive_data` - Code relating to reflection-based derive metadata * `field_attributes` - Code relating to field attributes * `impls` - Code containing actual reflection implementations * `reflect_value` - Code relating to reflection-based value metadata * `registration` - Code relating to type registration * `utility` - General-purpose utility functions This leaves the `lib.rs` file to contain only the public macros, making it much easier to digest (and fewer than 200 lines). By breaking up the code into smaller modules, we make it easier for future contributors to find the code they're looking for or identify which module best fits their own additions. ### Metadata Structs This cleanup also adds two big metadata structs: `ReflectFieldAttr` and `ReflectDeriveData`. The former is used to store all attributes for a struct field (if any). The latter is used to store all metadata for struct-based derive inputs. Both significantly reduce code duplication and make editing these macros much simpler. The tradeoff is that we may collect more metadata than needed. However, this is usually a small thing (such as checking for attributes when they're not really needed or creating a `ReflectFieldAttr` for every field regardless of whether they actually have an attribute). We could try to remove these tradeoffs and squeeze some more performance out, but doing so might come at the cost of developer experience. Personally, I think it's much nicer to create a `ReflectFieldAttr` for every field since it means I don't have to do two `Option` checks. Others may disagree, though, and so we can discuss changing this either in this PR or in a future one. ### Out of Scope _Some_ documentation has been added or improved, but ultimately good docs are probably best saved for a dedicated PR. ## 🔍 Focus Points (for reviewers) I know it's a lot to sift through, so here is a list of **key points for reviewers**: - The following files contain code that was mostly just relocated: - `reflect_value.rs` - `registration.rs` - `container_attributes.rs` was also mostly moved but features some general cleanup (reducing nesting, removing hardcoded strings, etc.) and lots of doc comments - Most impl logic was moved from `lib.rs` to `impls.rs`, but they have been significantly modified to use the new `ReflectDeriveData` metadata struct in order to reduce duplication. - `derive_data.rs` and `field_attributes.rs` contain almost entirely new code and should probably be given the most attention. - Likewise, `from_reflect.rs` saw major changes using `ReflectDeriveData` so it should also be given focus. - There was no change to the `lib.rs` exports so the end-user API should be the same. ## Prior Work This task was initially tackled by @NathanSWard in #2377 (which was closed in favor of this PR), so hats off to them for beating me to the punch by nearly a year! --- ## Changelog * **[INTERNAL]** Split `bevy_reflect_derive` into smaller submodules * **[INTERNAL]** Add `ReflectFieldAttr` * **[INTERNAL]** Add `ReflectDeriveData` * Add `BevyManifest::get_path_direct()` method (`bevy_macro_utils`) Co-authored-by: MrGVSV <49806985+MrGVSV@users.noreply.github.com>
2022-05-12 19:43:23 +00:00
}
/// The collection of docstrings for this type, if any.
#[cfg(feature = "documentation")]
pub fn doc(&self) -> &crate::documentation::Documentation {
&self.docs
}
bevy_reflect_derive: Tidying up the code (#4712) # Objective The `bevy_reflect_derive` crate is not the cleanest or easiest to follow/maintain. The `lib.rs` file is especially difficult with over 1000 lines of code written in a confusing order. This is just a result of growth within the crate and it would be nice to clean it up for future work. ## Solution Split `bevy_reflect_derive` into many more submodules. The submodules include: * `container_attributes` - Code relating to container attributes * `derive_data` - Code relating to reflection-based derive metadata * `field_attributes` - Code relating to field attributes * `impls` - Code containing actual reflection implementations * `reflect_value` - Code relating to reflection-based value metadata * `registration` - Code relating to type registration * `utility` - General-purpose utility functions This leaves the `lib.rs` file to contain only the public macros, making it much easier to digest (and fewer than 200 lines). By breaking up the code into smaller modules, we make it easier for future contributors to find the code they're looking for or identify which module best fits their own additions. ### Metadata Structs This cleanup also adds two big metadata structs: `ReflectFieldAttr` and `ReflectDeriveData`. The former is used to store all attributes for a struct field (if any). The latter is used to store all metadata for struct-based derive inputs. Both significantly reduce code duplication and make editing these macros much simpler. The tradeoff is that we may collect more metadata than needed. However, this is usually a small thing (such as checking for attributes when they're not really needed or creating a `ReflectFieldAttr` for every field regardless of whether they actually have an attribute). We could try to remove these tradeoffs and squeeze some more performance out, but doing so might come at the cost of developer experience. Personally, I think it's much nicer to create a `ReflectFieldAttr` for every field since it means I don't have to do two `Option` checks. Others may disagree, though, and so we can discuss changing this either in this PR or in a future one. ### Out of Scope _Some_ documentation has been added or improved, but ultimately good docs are probably best saved for a dedicated PR. ## 🔍 Focus Points (for reviewers) I know it's a lot to sift through, so here is a list of **key points for reviewers**: - The following files contain code that was mostly just relocated: - `reflect_value.rs` - `registration.rs` - `container_attributes.rs` was also mostly moved but features some general cleanup (reducing nesting, removing hardcoded strings, etc.) and lots of doc comments - Most impl logic was moved from `lib.rs` to `impls.rs`, but they have been significantly modified to use the new `ReflectDeriveData` metadata struct in order to reduce duplication. - `derive_data.rs` and `field_attributes.rs` contain almost entirely new code and should probably be given the most attention. - Likewise, `from_reflect.rs` saw major changes using `ReflectDeriveData` so it should also be given focus. - There was no change to the `lib.rs` exports so the end-user API should be the same. ## Prior Work This task was initially tackled by @NathanSWard in #2377 (which was closed in favor of this PR), so hats off to them for beating me to the punch by nearly a year! --- ## Changelog * **[INTERNAL]** Split `bevy_reflect_derive` into smaller submodules * **[INTERNAL]** Add `ReflectFieldAttr` * **[INTERNAL]** Add `ReflectDeriveData` * Add `BevyManifest::get_path_direct()` method (`bevy_macro_utils`) Co-authored-by: MrGVSV <49806985+MrGVSV@users.noreply.github.com>
2022-05-12 19:43:23 +00:00
}
bevy_reflect: Reflect enums (#4761) # Objective > This is a revival of #1347. Credit for the original PR should go to @Davier. Currently, enums are treated as `ReflectRef::Value` types by `bevy_reflect`. Obviously, there needs to be better a better representation for enums using the reflection API. ## Solution Based on prior work from @Davier, an `Enum` trait has been added as well as the ability to automatically implement it via the `Reflect` derive macro. This allows enums to be expressed dynamically: ```rust #[derive(Reflect)] enum Foo { A, B(usize), C { value: f32 }, } let mut foo = Foo::B(123); assert_eq!("B", foo.variant_name()); assert_eq!(1, foo.field_len()); let new_value = DynamicEnum::from(Foo::C { value: 1.23 }); foo.apply(&new_value); assert_eq!(Foo::C{value: 1.23}, foo); ``` ### Features #### Derive Macro Use the `#[derive(Reflect)]` macro to automatically implement the `Enum` trait for enum definitions. Optionally, you can use `#[reflect(ignore)]` with both variants and variant fields, just like you can with structs. These ignored items will not be considered as part of the reflection and cannot be accessed via reflection. ```rust #[derive(Reflect)] enum TestEnum { A, // Uncomment to ignore all of `B` // #[reflect(ignore)] B(usize), C { // Uncomment to ignore only field `foo` of `C` // #[reflect(ignore)] foo: f32, bar: bool, }, } ``` #### Dynamic Enums Enums may be created/represented dynamically via the `DynamicEnum` struct. The main purpose of this struct is to allow enums to be deserialized into a partial state and to allow dynamic patching. In order to ensure conversion from a `DynamicEnum` to a concrete enum type goes smoothly, be sure to add `FromReflect` to your derive macro. ```rust let mut value = TestEnum::A; // Create from a concrete instance let dyn_enum = DynamicEnum::from(TestEnum::B(123)); value.apply(&dyn_enum); assert_eq!(TestEnum::B(123), value); // Create a purely dynamic instance let dyn_enum = DynamicEnum::new("TestEnum", "A", ()); value.apply(&dyn_enum); assert_eq!(TestEnum::A, value); ``` #### Variants An enum value is always represented as one of its variants— never the enum in its entirety. ```rust let value = TestEnum::A; assert_eq!("A", value.variant_name()); // Since we are using the `A` variant, we cannot also be the `B` variant assert_ne!("B", value.variant_name()); ``` All variant types are representable within the `Enum` trait: unit, struct, and tuple. You can get the current type like: ```rust match value.variant_type() { VariantType::Unit => println!("A unit variant!"), VariantType::Struct => println!("A struct variant!"), VariantType::Tuple => println!("A tuple variant!"), } ``` > Notice that they don't contain any values representing the fields. These are purely tags. If a variant has them, you can access the fields as well: ```rust let mut value = TestEnum::C { foo: 1.23, bar: false }; // Read/write specific fields *value.field_mut("bar").unwrap() = true; // Iterate over the entire collection of fields for field in value.iter_fields() { println!("{} = {:?}", field.name(), field.value()); } ``` #### Variant Swapping It might seem odd to group all variant types under a single trait (why allow `iter_fields` on a unit variant?), but the reason this was done ~~is to easily allow *variant swapping*.~~ As I was recently drafting up the **Design Decisions** section, I discovered that other solutions could have been made to work with variant swapping. So while there are reasons to keep the all-in-one approach, variant swapping is _not_ one of them. ```rust let mut value: Box<dyn Enum> = Box::new(TestEnum::A); value.set(Box::new(TestEnum::B(123))).unwrap(); ``` #### Serialization Enums can be serialized and deserialized via reflection without needing to implement `Serialize` or `Deserialize` themselves (which can save thousands of lines of generated code). Below are the ways an enum can be serialized. > Note, like the rest of reflection-based serialization, the order of the keys in these representations is important! ##### Unit ```json { "type": "my_crate::TestEnum", "enum": { "variant": "A" } } ``` ##### Tuple ```json { "type": "my_crate::TestEnum", "enum": { "variant": "B", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` <details> <summary>Effects on Option</summary> This ends up making `Option` look a little ugly: ```json { "type": "core::option::Option<usize>", "enum": { "variant": "Some", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` </details> ##### Struct ```json { "type": "my_crate::TestEnum", "enum": { "variant": "C", "struct": { "foo": { "type": "f32", "value": 1.23 }, "bar": { "type": "bool", "value": false } } } } ``` ## Design Decisions <details> <summary><strong>View Section</strong></summary> This section is here to provide some context for why certain decisions were made for this PR, alternatives that could have been used instead, and what could be improved upon in the future. ### Variant Representation One of the biggest decisions was to decide on how to represent variants. The current design uses a "all-in-one" design where unit, tuple, and struct variants are all simultaneously represented by the `Enum` trait. This is not the only way it could have been done, though. #### Alternatives ##### 1. Variant Traits One way of representing variants would be to define traits for each variant, implementing them whenever an enum featured at least one instance of them. This would allow us to define variants like: ```rust pub trait Enum: Reflect { fn variant(&self) -> Variant; } pub enum Variant<'a> { Unit, Tuple(&'a dyn TupleVariant), Struct(&'a dyn StructVariant), } pub trait TupleVariant { fn field_len(&self) -> usize; // ... } ``` And then do things like: ```rust fn get_tuple_len(foo: &dyn Enum) -> usize { match foo.variant() { Variant::Tuple(tuple) => tuple.field_len(), _ => panic!("not a tuple variant!") } } ``` The reason this PR does not go with this approach is because of the fact that variants are not separate types. In other words, we cannot implement traits on specific variants— these cover the *entire* enum. This means we offer an easy footgun: ```rust let foo: Option<i32> = None; let my_enum = Box::new(foo) as Box<dyn TupleVariant>; ``` Here, `my_enum` contains `foo`, which is a unit variant. However, since we need to implement `TupleVariant` for `Option` as a whole, it's possible to perform such a cast. This is obviously wrong, but could easily go unnoticed. So unfortunately, this makes it not a good candidate for representing variants. ##### 2. Variant Structs To get around the issue of traits necessarily needing to apply to both the enum and its variants, we could instead use structs that are created on a per-variant basis. This was also considered but was ultimately [[removed](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c)](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c) due to concerns about allocations. Each variant struct would probably look something like: ```rust pub trait Enum: Reflect { fn variant_mut(&self) -> VariantMut; } pub enum VariantMut<'a> { Unit, Tuple(TupleVariantMut), Struct(StructVariantMut), } struct StructVariantMut<'a> { fields: Vec<&'a mut dyn Reflect>, field_indices: HashMap<Cow<'static, str>, usize> } ``` This allows us to isolate struct variants into their own defined struct and define methods specifically for their use. It also prevents users from casting to it since it's not a trait. However, this is not an optimal solution. Both `field_indices` and `fields` will require an allocation (remember, a `Box<[T]>` still requires a `Vec<T>` in order to be constructed). This *might* be a problem if called frequently enough. ##### 3. Generated Structs The original design, implemented by @Davier, instead generates structs specific for each variant. So if we had a variant path like `Foo::Bar`, we'd generate a struct named `FooBarWrapper`. This would be newtyped around the original enum and forward tuple or struct methods to the enum with the chosen variant. Because it involved using the `Tuple` and `Struct` traits (which are also both bound on `Reflect`), this meant a bit more code had to be generated. For a single struct variant with one field, the generated code amounted to ~110LoC. However, each new field added to that variant only added ~6 more LoC. In order to work properly, the enum had to be transmuted to the generated struct: ```rust fn variant(&self) -> crate::EnumVariant<'_> { match self { Foo::Bar {value: i32} => { let wrapper_ref = unsafe { std::mem::transmute::<&Self, &FooBarWrapper>(self) }; crate::EnumVariant::Struct(wrapper_ref as &dyn crate::Struct) } } } ``` This works because `FooBarWrapper` is defined as `repr(transparent)`. Out of all the alternatives, this would probably be the one most likely to be used again in the future. The reasons for why this PR did not continue to use it was because: * To reduce generated code (which would hopefully speed up compile times) * To avoid cluttering the code with generated structs not visible to the user * To keep bevy_reflect simple and extensible (these generated structs act as proxies and might not play well with current or future systems) * To avoid additional unsafe blocks * My own misunderstanding of @Davier's code That last point is obviously on me. I misjudged the code to be too unsafe and unable to handle variant swapping (which it probably could) when I was rebasing it. Looking over it again when writing up this whole section, I see that it was actually a pretty clever way of handling variant representation. #### Benefits of All-in-One As stated before, the current implementation uses an all-in-one approach. All variants are capable of containing fields as far as `Enum` is concerned. This provides a few benefits that the alternatives do not (reduced indirection, safer code, etc.). The biggest benefit, though, is direct field access. Rather than forcing users to have to go through pattern matching, we grant direct access to the fields contained by the current variant. The reason we can do this is because all of the pattern matching happens internally. Getting the field at index `2` will automatically return `Some(...)` for the current variant if it has a field at that index or `None` if it doesn't (or can't). This could be useful for scenarios where the variant has already been verified or just set/swapped (or even where the type of variant doesn't matter): ```rust let dyn_enum: &mut dyn Enum = &mut Foo::Bar {value: 123}; // We know it's the `Bar` variant let field = dyn_enum.field("value").unwrap(); ``` Reflection is not a type-safe abstraction— almost every return value is wrapped in `Option<...>`. There are plenty of places to check and recheck that a value is what Reflect says it is. Forcing users to have to go through `match` each time they want to access a field might just be an extra step among dozens of other verification processes. Some might disagree, but ultimately, my view is that the benefit here is an improvement to the ergonomics and usability of reflected enums. </details> --- ## Changelog ### Added * Added `Enum` trait * Added `Enum` impl to `Reflect` derive macro * Added `DynamicEnum` struct * Added `DynamicVariant` * Added `EnumInfo` * Added `VariantInfo` * Added `StructVariantInfo` * Added `TupleVariantInfo` * Added `UnitVariantInfo` * Added serializtion/deserialization support for enums * Added `EnumSerializer` * Added `VariantType` * Added `VariantFieldIter` * Added `VariantField` * Added `enum_partial_eq(...)` * Added `enum_hash(...)` ### Changed * `Option<T>` now implements `Enum` * `bevy_window` now depends on `bevy_reflect` * Implemented `Reflect` and `FromReflect` for `WindowId` * Derive `FromReflect` on `PerspectiveProjection` * Derive `FromReflect` on `OrthographicProjection` * Derive `FromReflect` on `WindowOrigin` * Derive `FromReflect` on `ScalingMode` * Derive `FromReflect` on `DepthCalculation` ## Migration Guide * Enums no longer need to be treated as values and usages of `#[reflect_value(...)]` can be removed or replaced by `#[reflect(...)]` * Enums (including `Option<T>`) now take a different format when serializing. The format is described above, but this may cause issues for existing scenes that make use of enums. --- Also shout out to @nicopap for helping clean up some of the code here! It's a big feature so help like this is really appreciated! Co-authored-by: Gino Valente <gino.valente.code@gmail.com>
2022-08-02 22:14:41 +00:00
bevy_reflect: Custom attributes (#11659) # Objective As work on the editor starts to ramp up, it might be nice to start allowing types to specify custom attributes. These can be used to provide certain functionality to fields, such as ranges or controlling how data is displayed. A good example of this can be seen in [`bevy-inspector-egui`](https://github.com/jakobhellermann/bevy-inspector-egui) with its [`InspectorOptions`](https://docs.rs/bevy-inspector-egui/0.22.1/bevy_inspector_egui/struct.InspectorOptions.html): ```rust #[derive(Reflect, Default, InspectorOptions)] #[reflect(InspectorOptions)] struct Slider { #[inspector(min = 0.0, max = 1.0)] value: f32, } ``` Normally, as demonstrated in the example above, these attributes are handled by a derive macro and stored in a corresponding `TypeData` struct (i.e. `ReflectInspectorOptions`). Ideally, we would have a good way of defining this directly via reflection so that users don't need to create and manage a whole proc macro just to allow these sorts of attributes. And note that this doesn't have to just be for inspectors and editors. It can be used for things done purely on the code side of things. ## Solution Create a new method for storing attributes on fields via the `Reflect` derive. These custom attributes are stored in type info (e.g. `NamedField`, `StructInfo`, etc.). ```rust #[derive(Reflect)] struct Slider { #[reflect(@0.0..=1.0)] value: f64, } let TypeInfo::Struct(info) = Slider::type_info() else { panic!("expected struct info"); }; let field = info.field("value").unwrap(); let range = field.get_attribute::<RangeInclusive<f64>>().unwrap(); assert_eq!(*range, 0.0..=1.0); ``` ## TODO - [x] ~~Bikeshed syntax~~ Went with a type-based approach, prefixed by `@` for ease of parsing and flexibility - [x] Add support for custom struct/tuple struct field attributes - [x] Add support for custom enum variant field attributes - [x] ~~Add support for custom enum variant attributes (maybe?)~~ ~~Will require a larger refactor. Can be saved for a future PR if we really want it.~~ Actually, we apparently still have support for variant attributes despite not using them, so it was pretty easy to add lol. - [x] Add support for custom container attributes - [x] Allow custom attributes to store any reflectable value (not just `Lit`) - [x] ~~Store attributes in registry~~ This PR used to store these in attributes in the registry, however, it has since switched over to storing them in type info - [x] Add example ## Bikeshedding > [!note] > This section was made for the old method of handling custom attributes, which stored them by name (i.e. `some_attribute = 123`). The PR has shifted away from that, to a more type-safe approach. > > This section has been left for reference. There are a number of ways we can syntactically handle custom attributes. Feel free to leave a comment on your preferred one! Ideally we want one that is clear, readable, and concise since these will potentially see _a lot_ of use. Below is a small, non-exhaustive list of them. Note that the `skip_serializing` reflection attribute is added to demonstrate how each case plays with existing reflection attributes. <details> <summary>List</summary> ##### 1. `@(name = value)` > The `@` was chosen to make them stand out from other attributes and because the "at" symbol is a subtle pneumonic for "attribute". Of course, other symbols could be used (e.g. `$`, `#`, etc.). ```rust #[derive(Reflect)] struct Slider { #[reflect(@(min = 0.0, max = 1.0), skip_serializing)] #[[reflect(@(bevy_editor::hint = "Range: 0.0 to 1.0"))] value: f32, } ``` ##### 2. `@name = value` > This is my personal favorite. ```rust #[derive(Reflect)] struct Slider { #[reflect(@min = 0.0, @max = 1.0, skip_serializing)] #[[reflect(@bevy_editor::hint = "Range: 0.0 to 1.0")] value: f32, } ``` ##### 3. `custom_attr(name = value)` > `custom_attr` can be anything. Other possibilities include `with` or `tag`. ```rust #[derive(Reflect)] struct Slider { #[reflect(custom_attr(min = 0.0, max = 1.0), skip_serializing)] #[[reflect(custom_attr(bevy_editor::hint = "Range: 0.0 to 1.0"))] value: f32, } ``` ##### 4. `reflect_attr(name = value)` ```rust #[derive(Reflect)] struct Slider { #[reflect(skip_serializing)] #[reflect_attr(min = 0.0, max = 1.0)] #[[reflect_attr(bevy_editor::hint = "Range: 0.0 to 1.0")] value: f32, } ``` </details> --- ## Changelog - Added support for custom attributes on reflected types (i.e. `#[reflect(@Foo::new("bar")]`)
2024-05-20 19:30:21 +00:00
impl<'a> StructField<'a> {
/// Generates a `TokenStream` for `NamedField` or `UnnamedField` construction.
pub fn to_info_tokens(&self, bevy_reflect_path: &Path) -> proc_macro2::TokenStream {
let name = match &self.data.ident {
Some(ident) => ident.to_string().to_token_stream(),
None => self.reflection_index.to_token_stream(),
};
let field_info = if self.data.ident.is_some() {
quote! {
#bevy_reflect_path::NamedField
}
} else {
quote! {
#bevy_reflect_path::UnnamedField
}
};
bevy_reflect: Reflect remote types (#6042) # Objective The goal with this PR is to allow the use of types that don't implement `Reflect` within the reflection API. Rust's [orphan rule](https://doc.rust-lang.org/book/ch10-02-traits.html#implementing-a-trait-on-a-type) prevents implementing a trait on an external type when neither type nor trait are owned by the implementor. This means that if a crate, `cool_rust_lib`, defines a type, `Foo`, then a user cannot use it with reflection. What this means is that we have to ignore it most of the time: ```rust #[derive(Reflect)] struct SomeStruct { #[reflect(ignore)] data: cool_rust_lib::Foo } ``` Obviously, it's impossible to implement `Reflect` on `Foo`. But does it *have* to be? Most of reflection doesn't deal with concrete types— it's almost all using `dyn Reflect`. And being very metadata-driven, it should theoretically be possible. I mean, [`serde`](https://serde.rs/remote-derive.html) does it. ## Solution > Special thanks to @danielhenrymantilla for their help reviewing this PR and offering wisdom wrt safety. Taking a page out of `serde`'s book, this PR adds the ability to easily use "remote types" with reflection. In this context, a "remote type" is the external type for which we have no ability to implement `Reflect`. This adds the `#[reflect_remote(...)]` attribute macro, which is used to generate "remote type wrappers". All you have to do is define the wrapper exactly the same as the remote type's definition: ```rust // Pretend this is our external crate mod cool_rust_lib { #[derive(Default)] struct Foo { pub value: String } } #[reflect_remote(cool_rust_lib::Foo)] struct FooWrapper { pub value: String } ``` > **Note:** All fields in the external type *must* be public. This could be addressed with a separate getter/setter attribute either in this PR or in another one. The macro takes this user-defined item and transforms it into a newtype wrapper around the external type, marking it as `#[repr(transparent)]`. The fields/variants defined by the user are simply used to build out the reflection impls. Additionally, it generates an implementation of the new trait, `ReflectRemote`, which helps prevent accidental misuses of this API. Therefore, the output generated by the macro would look something like: ```rust #[repr(transparent)] struct FooWrapper(pub cool_rust_lib::Foo); impl ReflectRemote for FooWrapper { type Remote = cool_rust_lib::Foo; // transmutation methods... } // reflection impls... // these will acknowledge and make use of the `value` field ``` Internally, the reflection API will pass around the `FooWrapper` and [transmute](https://doc.rust-lang.org/std/mem/fn.transmute.html) it where necessary. All we have to do is then tell `Reflect` to do that. So rather than ignoring the field, we tell `Reflect` to use our wrapper using the `#[reflect(remote = ...)]` field attribute: ```rust #[derive(Reflect)] struct SomeStruct { #[reflect(remote = FooWrapper)] data: cool_rust_lib::Foo } ``` #### Other Macros & Type Data Because this macro consumes the defined item and generates a new one, we can't just put our macros anywhere. All macros that should be passed to the generated struct need to come *below* this macro. For example, to derive `Default` and register its associated type data: ```rust // ✅ GOOD #[reflect_remote(cool_rust_lib::Foo)] #[derive(Default)] #[reflect(Default)] struct FooWrapper { pub value: String } // ❌ BAD #[derive(Default)] #[reflect_remote(cool_rust_lib::Foo)] #[reflect(Default)] struct FooWrapper { pub value: String } ``` #### Generics Generics are forwarded to the generated struct as well. They should also be defined in the same order: ```rust #[reflect_remote(RemoteGeneric<'a, T1, T2>)] struct GenericWrapper<'a, T1, T2> { pub foo: &'a T1, pub bar: &'a T2, } ``` > Naming does *not* need to match the original definition's. Only order matters here. > Also note that the code above is just a demonstration and doesn't actually compile since we'd need to enforce certain bounds (e.g. `T1: Reflect`, `'a: 'static`, etc.) #### Nesting And, yes, you can nest remote types: ```rust #[reflect_remote(RemoteOuter)] struct OuterWrapper { #[reflect(remote = InnerWrapper)] pub inner: RemoteInner } #[reflect_remote(RemoteInner)] struct InnerWrapper(usize); ``` #### Assertions This macro will also generate some compile-time assertions to ensure that the correct types are used. It's important we catch this early so users don't have to wait for something to panic. And it also helps keep our `unsafe` a little safer. For example, a wrapper definition that does not match its corresponding remote type will result in an error: ```rust mod external_crate { pub struct TheirStruct(pub u32); } #[reflect_remote(external_crate::TheirStruct)] struct MyStruct(pub String); // ERROR: expected type `u32` but found `String` ``` <details> <summary>Generated Assertion</summary> ```rust const _: () = { #[allow(non_snake_case)] #[allow(unused_variables)] #[allow(unused_assignments)] #[allow(unreachable_patterns)] #[allow(clippy::multiple_bound_locations)] fn assert_wrapper_definition_matches_remote_type( mut __remote__: external_crate::TheirStruct, ) { __remote__.0 = (|| -> ::core::option::Option<String> { None })().unwrap(); } }; ``` </details> Additionally, using the incorrect type in a `#[reflect(remote = ...)]` attribute should result in an error: ```rust mod external_crate { pub struct TheirFoo(pub u32); pub struct TheirBar(pub i32); } #[reflect_remote(external_crate::TheirFoo)] struct MyFoo(pub u32); #[reflect_remote(external_crate::TheirBar)] struct MyBar(pub i32); #[derive(Reflect)] struct MyStruct { #[reflect(remote = MyBar)] // ERROR: expected type `TheirFoo` but found struct `TheirBar` foo: external_crate::TheirFoo } ``` <details> <summary>Generated Assertion</summary> ```rust const _: () = { struct RemoteFieldAssertions; impl RemoteFieldAssertions { #[allow(non_snake_case)] #[allow(clippy::multiple_bound_locations)] fn assert__foo__is_valid_remote() { let _: <MyBar as bevy_reflect::ReflectRemote>::Remote = (|| -> ::core::option::Option<external_crate::TheirFoo> { None })().unwrap(); } } }; ``` </details> ### Discussion There are a couple points that I think still need discussion or validation. - [x] 1. `Any` shenanigans ~~If we wanted to downcast our remote type from a `dyn Reflect`, we'd have to first downcast to the wrapper then extract the inner type. This PR has a [commit](b840db9f74cb6d357f951cb11b150d46bac89ee2) that addresses this by making all the `Reflect::*any` methods return the inner type rather than the wrapper type. This allows us to downcast directly to our remote type.~~ ~~However, I'm not sure if this is something we want to do. For unknowing users, it could be confusing and seemingly inconsistent. Is it worth keeping? Or should this behavior be removed?~~ I think this should be fine. The remote wrapper is an implementation detail and users should not need to downcast to the wrapper type. Feel free to let me know if there are other opinions on this though! - [x] 2. Implementing `Deref/DerefMut` and `From` ~~We don't currently do this, but should we implement other traits on the generated transparent struct? We could implement `Deref`/`DerefMut` to easily access the inner type. And we could implement `From` for easier conversion between the two types (e.g. `T: Into<Foo>`).~~ As mentioned in the comments, we probably don't need to do this. Again, the remote wrapper is an implementation detail, and should generally not be used directly. - [x] 3. ~~Should we define a getter/setter field attribute in this PR as well or leave it for a future one?~~ I think this should be saved for a future PR - [ ] 4. Any foreseeable issues with this implementation? #### Alternatives One alternative to defining our own `ReflectRemote` would be to use [bytemuck's `TransparentWrapper`](https://docs.rs/bytemuck/1.13.1/bytemuck/trait.TransparentWrapper.html) (as suggested by @danielhenrymantilla). This is definitely a viable option, as `ReflectRemote` is pretty much the same thing as `TransparentWrapper`. However, the cost would be bringing in a new crate— though, it is already in use in a few other sub-crates like bevy_render. I think we're okay just defining `ReflectRemote` ourselves, but we can go the bytemuck route if we'd prefer offloading that work to another crate. --- ## Changelog * Added the `#[reflect_remote(...)]` attribute macro to allow `Reflect` to be used on remote types * Added `ReflectRemote` trait for ensuring proper remote wrapper usage
2024-08-12 19:12:53 +00:00
let ty = self.reflected_type();
bevy_reflect: Custom attributes (#11659) # Objective As work on the editor starts to ramp up, it might be nice to start allowing types to specify custom attributes. These can be used to provide certain functionality to fields, such as ranges or controlling how data is displayed. A good example of this can be seen in [`bevy-inspector-egui`](https://github.com/jakobhellermann/bevy-inspector-egui) with its [`InspectorOptions`](https://docs.rs/bevy-inspector-egui/0.22.1/bevy_inspector_egui/struct.InspectorOptions.html): ```rust #[derive(Reflect, Default, InspectorOptions)] #[reflect(InspectorOptions)] struct Slider { #[inspector(min = 0.0, max = 1.0)] value: f32, } ``` Normally, as demonstrated in the example above, these attributes are handled by a derive macro and stored in a corresponding `TypeData` struct (i.e. `ReflectInspectorOptions`). Ideally, we would have a good way of defining this directly via reflection so that users don't need to create and manage a whole proc macro just to allow these sorts of attributes. And note that this doesn't have to just be for inspectors and editors. It can be used for things done purely on the code side of things. ## Solution Create a new method for storing attributes on fields via the `Reflect` derive. These custom attributes are stored in type info (e.g. `NamedField`, `StructInfo`, etc.). ```rust #[derive(Reflect)] struct Slider { #[reflect(@0.0..=1.0)] value: f64, } let TypeInfo::Struct(info) = Slider::type_info() else { panic!("expected struct info"); }; let field = info.field("value").unwrap(); let range = field.get_attribute::<RangeInclusive<f64>>().unwrap(); assert_eq!(*range, 0.0..=1.0); ``` ## TODO - [x] ~~Bikeshed syntax~~ Went with a type-based approach, prefixed by `@` for ease of parsing and flexibility - [x] Add support for custom struct/tuple struct field attributes - [x] Add support for custom enum variant field attributes - [x] ~~Add support for custom enum variant attributes (maybe?)~~ ~~Will require a larger refactor. Can be saved for a future PR if we really want it.~~ Actually, we apparently still have support for variant attributes despite not using them, so it was pretty easy to add lol. - [x] Add support for custom container attributes - [x] Allow custom attributes to store any reflectable value (not just `Lit`) - [x] ~~Store attributes in registry~~ This PR used to store these in attributes in the registry, however, it has since switched over to storing them in type info - [x] Add example ## Bikeshedding > [!note] > This section was made for the old method of handling custom attributes, which stored them by name (i.e. `some_attribute = 123`). The PR has shifted away from that, to a more type-safe approach. > > This section has been left for reference. There are a number of ways we can syntactically handle custom attributes. Feel free to leave a comment on your preferred one! Ideally we want one that is clear, readable, and concise since these will potentially see _a lot_ of use. Below is a small, non-exhaustive list of them. Note that the `skip_serializing` reflection attribute is added to demonstrate how each case plays with existing reflection attributes. <details> <summary>List</summary> ##### 1. `@(name = value)` > The `@` was chosen to make them stand out from other attributes and because the "at" symbol is a subtle pneumonic for "attribute". Of course, other symbols could be used (e.g. `$`, `#`, etc.). ```rust #[derive(Reflect)] struct Slider { #[reflect(@(min = 0.0, max = 1.0), skip_serializing)] #[[reflect(@(bevy_editor::hint = "Range: 0.0 to 1.0"))] value: f32, } ``` ##### 2. `@name = value` > This is my personal favorite. ```rust #[derive(Reflect)] struct Slider { #[reflect(@min = 0.0, @max = 1.0, skip_serializing)] #[[reflect(@bevy_editor::hint = "Range: 0.0 to 1.0")] value: f32, } ``` ##### 3. `custom_attr(name = value)` > `custom_attr` can be anything. Other possibilities include `with` or `tag`. ```rust #[derive(Reflect)] struct Slider { #[reflect(custom_attr(min = 0.0, max = 1.0), skip_serializing)] #[[reflect(custom_attr(bevy_editor::hint = "Range: 0.0 to 1.0"))] value: f32, } ``` ##### 4. `reflect_attr(name = value)` ```rust #[derive(Reflect)] struct Slider { #[reflect(skip_serializing)] #[reflect_attr(min = 0.0, max = 1.0)] #[[reflect_attr(bevy_editor::hint = "Range: 0.0 to 1.0")] value: f32, } ``` </details> --- ## Changelog - Added support for custom attributes on reflected types (i.e. `#[reflect(@Foo::new("bar")]`)
2024-05-20 19:30:21 +00:00
let custom_attributes = self.attrs.custom_attributes.to_tokens(bevy_reflect_path);
#[allow(unused_mut)] // Needs mutability for the feature gate
let mut info = quote! {
#field_info::new::<#ty>(#name).with_custom_attributes(#custom_attributes)
};
#[cfg(feature = "documentation")]
{
let docs = &self.doc;
info.extend(quote! {
.with_docs(#docs)
});
}
info
}
bevy_reflect: Reflect remote types (#6042) # Objective The goal with this PR is to allow the use of types that don't implement `Reflect` within the reflection API. Rust's [orphan rule](https://doc.rust-lang.org/book/ch10-02-traits.html#implementing-a-trait-on-a-type) prevents implementing a trait on an external type when neither type nor trait are owned by the implementor. This means that if a crate, `cool_rust_lib`, defines a type, `Foo`, then a user cannot use it with reflection. What this means is that we have to ignore it most of the time: ```rust #[derive(Reflect)] struct SomeStruct { #[reflect(ignore)] data: cool_rust_lib::Foo } ``` Obviously, it's impossible to implement `Reflect` on `Foo`. But does it *have* to be? Most of reflection doesn't deal with concrete types— it's almost all using `dyn Reflect`. And being very metadata-driven, it should theoretically be possible. I mean, [`serde`](https://serde.rs/remote-derive.html) does it. ## Solution > Special thanks to @danielhenrymantilla for their help reviewing this PR and offering wisdom wrt safety. Taking a page out of `serde`'s book, this PR adds the ability to easily use "remote types" with reflection. In this context, a "remote type" is the external type for which we have no ability to implement `Reflect`. This adds the `#[reflect_remote(...)]` attribute macro, which is used to generate "remote type wrappers". All you have to do is define the wrapper exactly the same as the remote type's definition: ```rust // Pretend this is our external crate mod cool_rust_lib { #[derive(Default)] struct Foo { pub value: String } } #[reflect_remote(cool_rust_lib::Foo)] struct FooWrapper { pub value: String } ``` > **Note:** All fields in the external type *must* be public. This could be addressed with a separate getter/setter attribute either in this PR or in another one. The macro takes this user-defined item and transforms it into a newtype wrapper around the external type, marking it as `#[repr(transparent)]`. The fields/variants defined by the user are simply used to build out the reflection impls. Additionally, it generates an implementation of the new trait, `ReflectRemote`, which helps prevent accidental misuses of this API. Therefore, the output generated by the macro would look something like: ```rust #[repr(transparent)] struct FooWrapper(pub cool_rust_lib::Foo); impl ReflectRemote for FooWrapper { type Remote = cool_rust_lib::Foo; // transmutation methods... } // reflection impls... // these will acknowledge and make use of the `value` field ``` Internally, the reflection API will pass around the `FooWrapper` and [transmute](https://doc.rust-lang.org/std/mem/fn.transmute.html) it where necessary. All we have to do is then tell `Reflect` to do that. So rather than ignoring the field, we tell `Reflect` to use our wrapper using the `#[reflect(remote = ...)]` field attribute: ```rust #[derive(Reflect)] struct SomeStruct { #[reflect(remote = FooWrapper)] data: cool_rust_lib::Foo } ``` #### Other Macros & Type Data Because this macro consumes the defined item and generates a new one, we can't just put our macros anywhere. All macros that should be passed to the generated struct need to come *below* this macro. For example, to derive `Default` and register its associated type data: ```rust // ✅ GOOD #[reflect_remote(cool_rust_lib::Foo)] #[derive(Default)] #[reflect(Default)] struct FooWrapper { pub value: String } // ❌ BAD #[derive(Default)] #[reflect_remote(cool_rust_lib::Foo)] #[reflect(Default)] struct FooWrapper { pub value: String } ``` #### Generics Generics are forwarded to the generated struct as well. They should also be defined in the same order: ```rust #[reflect_remote(RemoteGeneric<'a, T1, T2>)] struct GenericWrapper<'a, T1, T2> { pub foo: &'a T1, pub bar: &'a T2, } ``` > Naming does *not* need to match the original definition's. Only order matters here. > Also note that the code above is just a demonstration and doesn't actually compile since we'd need to enforce certain bounds (e.g. `T1: Reflect`, `'a: 'static`, etc.) #### Nesting And, yes, you can nest remote types: ```rust #[reflect_remote(RemoteOuter)] struct OuterWrapper { #[reflect(remote = InnerWrapper)] pub inner: RemoteInner } #[reflect_remote(RemoteInner)] struct InnerWrapper(usize); ``` #### Assertions This macro will also generate some compile-time assertions to ensure that the correct types are used. It's important we catch this early so users don't have to wait for something to panic. And it also helps keep our `unsafe` a little safer. For example, a wrapper definition that does not match its corresponding remote type will result in an error: ```rust mod external_crate { pub struct TheirStruct(pub u32); } #[reflect_remote(external_crate::TheirStruct)] struct MyStruct(pub String); // ERROR: expected type `u32` but found `String` ``` <details> <summary>Generated Assertion</summary> ```rust const _: () = { #[allow(non_snake_case)] #[allow(unused_variables)] #[allow(unused_assignments)] #[allow(unreachable_patterns)] #[allow(clippy::multiple_bound_locations)] fn assert_wrapper_definition_matches_remote_type( mut __remote__: external_crate::TheirStruct, ) { __remote__.0 = (|| -> ::core::option::Option<String> { None })().unwrap(); } }; ``` </details> Additionally, using the incorrect type in a `#[reflect(remote = ...)]` attribute should result in an error: ```rust mod external_crate { pub struct TheirFoo(pub u32); pub struct TheirBar(pub i32); } #[reflect_remote(external_crate::TheirFoo)] struct MyFoo(pub u32); #[reflect_remote(external_crate::TheirBar)] struct MyBar(pub i32); #[derive(Reflect)] struct MyStruct { #[reflect(remote = MyBar)] // ERROR: expected type `TheirFoo` but found struct `TheirBar` foo: external_crate::TheirFoo } ``` <details> <summary>Generated Assertion</summary> ```rust const _: () = { struct RemoteFieldAssertions; impl RemoteFieldAssertions { #[allow(non_snake_case)] #[allow(clippy::multiple_bound_locations)] fn assert__foo__is_valid_remote() { let _: <MyBar as bevy_reflect::ReflectRemote>::Remote = (|| -> ::core::option::Option<external_crate::TheirFoo> { None })().unwrap(); } } }; ``` </details> ### Discussion There are a couple points that I think still need discussion or validation. - [x] 1. `Any` shenanigans ~~If we wanted to downcast our remote type from a `dyn Reflect`, we'd have to first downcast to the wrapper then extract the inner type. This PR has a [commit](b840db9f74cb6d357f951cb11b150d46bac89ee2) that addresses this by making all the `Reflect::*any` methods return the inner type rather than the wrapper type. This allows us to downcast directly to our remote type.~~ ~~However, I'm not sure if this is something we want to do. For unknowing users, it could be confusing and seemingly inconsistent. Is it worth keeping? Or should this behavior be removed?~~ I think this should be fine. The remote wrapper is an implementation detail and users should not need to downcast to the wrapper type. Feel free to let me know if there are other opinions on this though! - [x] 2. Implementing `Deref/DerefMut` and `From` ~~We don't currently do this, but should we implement other traits on the generated transparent struct? We could implement `Deref`/`DerefMut` to easily access the inner type. And we could implement `From` for easier conversion between the two types (e.g. `T: Into<Foo>`).~~ As mentioned in the comments, we probably don't need to do this. Again, the remote wrapper is an implementation detail, and should generally not be used directly. - [x] 3. ~~Should we define a getter/setter field attribute in this PR as well or leave it for a future one?~~ I think this should be saved for a future PR - [ ] 4. Any foreseeable issues with this implementation? #### Alternatives One alternative to defining our own `ReflectRemote` would be to use [bytemuck's `TransparentWrapper`](https://docs.rs/bytemuck/1.13.1/bytemuck/trait.TransparentWrapper.html) (as suggested by @danielhenrymantilla). This is definitely a viable option, as `ReflectRemote` is pretty much the same thing as `TransparentWrapper`. However, the cost would be bringing in a new crate— though, it is already in use in a few other sub-crates like bevy_render. I think we're okay just defining `ReflectRemote` ourselves, but we can go the bytemuck route if we'd prefer offloading that work to another crate. --- ## Changelog * Added the `#[reflect_remote(...)]` attribute macro to allow `Reflect` to be used on remote types * Added `ReflectRemote` trait for ensuring proper remote wrapper usage
2024-08-12 19:12:53 +00:00
/// Returns the reflected type of this field.
///
/// Normally this is just the field's defined type.
/// However, this can be adjusted to use a different type, like for representing remote types.
/// In those cases, the returned value is the remote wrapper type.
pub fn reflected_type(&self) -> &Type {
self.attrs.remote.as_ref().unwrap_or(&self.data.ty)
}
pub fn attrs(&self) -> &FieldAttributes {
&self.attrs
}
bevy_reflect: Custom attributes (#11659) # Objective As work on the editor starts to ramp up, it might be nice to start allowing types to specify custom attributes. These can be used to provide certain functionality to fields, such as ranges or controlling how data is displayed. A good example of this can be seen in [`bevy-inspector-egui`](https://github.com/jakobhellermann/bevy-inspector-egui) with its [`InspectorOptions`](https://docs.rs/bevy-inspector-egui/0.22.1/bevy_inspector_egui/struct.InspectorOptions.html): ```rust #[derive(Reflect, Default, InspectorOptions)] #[reflect(InspectorOptions)] struct Slider { #[inspector(min = 0.0, max = 1.0)] value: f32, } ``` Normally, as demonstrated in the example above, these attributes are handled by a derive macro and stored in a corresponding `TypeData` struct (i.e. `ReflectInspectorOptions`). Ideally, we would have a good way of defining this directly via reflection so that users don't need to create and manage a whole proc macro just to allow these sorts of attributes. And note that this doesn't have to just be for inspectors and editors. It can be used for things done purely on the code side of things. ## Solution Create a new method for storing attributes on fields via the `Reflect` derive. These custom attributes are stored in type info (e.g. `NamedField`, `StructInfo`, etc.). ```rust #[derive(Reflect)] struct Slider { #[reflect(@0.0..=1.0)] value: f64, } let TypeInfo::Struct(info) = Slider::type_info() else { panic!("expected struct info"); }; let field = info.field("value").unwrap(); let range = field.get_attribute::<RangeInclusive<f64>>().unwrap(); assert_eq!(*range, 0.0..=1.0); ``` ## TODO - [x] ~~Bikeshed syntax~~ Went with a type-based approach, prefixed by `@` for ease of parsing and flexibility - [x] Add support for custom struct/tuple struct field attributes - [x] Add support for custom enum variant field attributes - [x] ~~Add support for custom enum variant attributes (maybe?)~~ ~~Will require a larger refactor. Can be saved for a future PR if we really want it.~~ Actually, we apparently still have support for variant attributes despite not using them, so it was pretty easy to add lol. - [x] Add support for custom container attributes - [x] Allow custom attributes to store any reflectable value (not just `Lit`) - [x] ~~Store attributes in registry~~ This PR used to store these in attributes in the registry, however, it has since switched over to storing them in type info - [x] Add example ## Bikeshedding > [!note] > This section was made for the old method of handling custom attributes, which stored them by name (i.e. `some_attribute = 123`). The PR has shifted away from that, to a more type-safe approach. > > This section has been left for reference. There are a number of ways we can syntactically handle custom attributes. Feel free to leave a comment on your preferred one! Ideally we want one that is clear, readable, and concise since these will potentially see _a lot_ of use. Below is a small, non-exhaustive list of them. Note that the `skip_serializing` reflection attribute is added to demonstrate how each case plays with existing reflection attributes. <details> <summary>List</summary> ##### 1. `@(name = value)` > The `@` was chosen to make them stand out from other attributes and because the "at" symbol is a subtle pneumonic for "attribute". Of course, other symbols could be used (e.g. `$`, `#`, etc.). ```rust #[derive(Reflect)] struct Slider { #[reflect(@(min = 0.0, max = 1.0), skip_serializing)] #[[reflect(@(bevy_editor::hint = "Range: 0.0 to 1.0"))] value: f32, } ``` ##### 2. `@name = value` > This is my personal favorite. ```rust #[derive(Reflect)] struct Slider { #[reflect(@min = 0.0, @max = 1.0, skip_serializing)] #[[reflect(@bevy_editor::hint = "Range: 0.0 to 1.0")] value: f32, } ``` ##### 3. `custom_attr(name = value)` > `custom_attr` can be anything. Other possibilities include `with` or `tag`. ```rust #[derive(Reflect)] struct Slider { #[reflect(custom_attr(min = 0.0, max = 1.0), skip_serializing)] #[[reflect(custom_attr(bevy_editor::hint = "Range: 0.0 to 1.0"))] value: f32, } ``` ##### 4. `reflect_attr(name = value)` ```rust #[derive(Reflect)] struct Slider { #[reflect(skip_serializing)] #[reflect_attr(min = 0.0, max = 1.0)] #[[reflect_attr(bevy_editor::hint = "Range: 0.0 to 1.0")] value: f32, } ``` </details> --- ## Changelog - Added support for custom attributes on reflected types (i.e. `#[reflect(@Foo::new("bar")]`)
2024-05-20 19:30:21 +00:00
}
bevy_reflect: Reflect enums (#4761) # Objective > This is a revival of #1347. Credit for the original PR should go to @Davier. Currently, enums are treated as `ReflectRef::Value` types by `bevy_reflect`. Obviously, there needs to be better a better representation for enums using the reflection API. ## Solution Based on prior work from @Davier, an `Enum` trait has been added as well as the ability to automatically implement it via the `Reflect` derive macro. This allows enums to be expressed dynamically: ```rust #[derive(Reflect)] enum Foo { A, B(usize), C { value: f32 }, } let mut foo = Foo::B(123); assert_eq!("B", foo.variant_name()); assert_eq!(1, foo.field_len()); let new_value = DynamicEnum::from(Foo::C { value: 1.23 }); foo.apply(&new_value); assert_eq!(Foo::C{value: 1.23}, foo); ``` ### Features #### Derive Macro Use the `#[derive(Reflect)]` macro to automatically implement the `Enum` trait for enum definitions. Optionally, you can use `#[reflect(ignore)]` with both variants and variant fields, just like you can with structs. These ignored items will not be considered as part of the reflection and cannot be accessed via reflection. ```rust #[derive(Reflect)] enum TestEnum { A, // Uncomment to ignore all of `B` // #[reflect(ignore)] B(usize), C { // Uncomment to ignore only field `foo` of `C` // #[reflect(ignore)] foo: f32, bar: bool, }, } ``` #### Dynamic Enums Enums may be created/represented dynamically via the `DynamicEnum` struct. The main purpose of this struct is to allow enums to be deserialized into a partial state and to allow dynamic patching. In order to ensure conversion from a `DynamicEnum` to a concrete enum type goes smoothly, be sure to add `FromReflect` to your derive macro. ```rust let mut value = TestEnum::A; // Create from a concrete instance let dyn_enum = DynamicEnum::from(TestEnum::B(123)); value.apply(&dyn_enum); assert_eq!(TestEnum::B(123), value); // Create a purely dynamic instance let dyn_enum = DynamicEnum::new("TestEnum", "A", ()); value.apply(&dyn_enum); assert_eq!(TestEnum::A, value); ``` #### Variants An enum value is always represented as one of its variants— never the enum in its entirety. ```rust let value = TestEnum::A; assert_eq!("A", value.variant_name()); // Since we are using the `A` variant, we cannot also be the `B` variant assert_ne!("B", value.variant_name()); ``` All variant types are representable within the `Enum` trait: unit, struct, and tuple. You can get the current type like: ```rust match value.variant_type() { VariantType::Unit => println!("A unit variant!"), VariantType::Struct => println!("A struct variant!"), VariantType::Tuple => println!("A tuple variant!"), } ``` > Notice that they don't contain any values representing the fields. These are purely tags. If a variant has them, you can access the fields as well: ```rust let mut value = TestEnum::C { foo: 1.23, bar: false }; // Read/write specific fields *value.field_mut("bar").unwrap() = true; // Iterate over the entire collection of fields for field in value.iter_fields() { println!("{} = {:?}", field.name(), field.value()); } ``` #### Variant Swapping It might seem odd to group all variant types under a single trait (why allow `iter_fields` on a unit variant?), but the reason this was done ~~is to easily allow *variant swapping*.~~ As I was recently drafting up the **Design Decisions** section, I discovered that other solutions could have been made to work with variant swapping. So while there are reasons to keep the all-in-one approach, variant swapping is _not_ one of them. ```rust let mut value: Box<dyn Enum> = Box::new(TestEnum::A); value.set(Box::new(TestEnum::B(123))).unwrap(); ``` #### Serialization Enums can be serialized and deserialized via reflection without needing to implement `Serialize` or `Deserialize` themselves (which can save thousands of lines of generated code). Below are the ways an enum can be serialized. > Note, like the rest of reflection-based serialization, the order of the keys in these representations is important! ##### Unit ```json { "type": "my_crate::TestEnum", "enum": { "variant": "A" } } ``` ##### Tuple ```json { "type": "my_crate::TestEnum", "enum": { "variant": "B", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` <details> <summary>Effects on Option</summary> This ends up making `Option` look a little ugly: ```json { "type": "core::option::Option<usize>", "enum": { "variant": "Some", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` </details> ##### Struct ```json { "type": "my_crate::TestEnum", "enum": { "variant": "C", "struct": { "foo": { "type": "f32", "value": 1.23 }, "bar": { "type": "bool", "value": false } } } } ``` ## Design Decisions <details> <summary><strong>View Section</strong></summary> This section is here to provide some context for why certain decisions were made for this PR, alternatives that could have been used instead, and what could be improved upon in the future. ### Variant Representation One of the biggest decisions was to decide on how to represent variants. The current design uses a "all-in-one" design where unit, tuple, and struct variants are all simultaneously represented by the `Enum` trait. This is not the only way it could have been done, though. #### Alternatives ##### 1. Variant Traits One way of representing variants would be to define traits for each variant, implementing them whenever an enum featured at least one instance of them. This would allow us to define variants like: ```rust pub trait Enum: Reflect { fn variant(&self) -> Variant; } pub enum Variant<'a> { Unit, Tuple(&'a dyn TupleVariant), Struct(&'a dyn StructVariant), } pub trait TupleVariant { fn field_len(&self) -> usize; // ... } ``` And then do things like: ```rust fn get_tuple_len(foo: &dyn Enum) -> usize { match foo.variant() { Variant::Tuple(tuple) => tuple.field_len(), _ => panic!("not a tuple variant!") } } ``` The reason this PR does not go with this approach is because of the fact that variants are not separate types. In other words, we cannot implement traits on specific variants— these cover the *entire* enum. This means we offer an easy footgun: ```rust let foo: Option<i32> = None; let my_enum = Box::new(foo) as Box<dyn TupleVariant>; ``` Here, `my_enum` contains `foo`, which is a unit variant. However, since we need to implement `TupleVariant` for `Option` as a whole, it's possible to perform such a cast. This is obviously wrong, but could easily go unnoticed. So unfortunately, this makes it not a good candidate for representing variants. ##### 2. Variant Structs To get around the issue of traits necessarily needing to apply to both the enum and its variants, we could instead use structs that are created on a per-variant basis. This was also considered but was ultimately [[removed](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c)](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c) due to concerns about allocations. Each variant struct would probably look something like: ```rust pub trait Enum: Reflect { fn variant_mut(&self) -> VariantMut; } pub enum VariantMut<'a> { Unit, Tuple(TupleVariantMut), Struct(StructVariantMut), } struct StructVariantMut<'a> { fields: Vec<&'a mut dyn Reflect>, field_indices: HashMap<Cow<'static, str>, usize> } ``` This allows us to isolate struct variants into their own defined struct and define methods specifically for their use. It also prevents users from casting to it since it's not a trait. However, this is not an optimal solution. Both `field_indices` and `fields` will require an allocation (remember, a `Box<[T]>` still requires a `Vec<T>` in order to be constructed). This *might* be a problem if called frequently enough. ##### 3. Generated Structs The original design, implemented by @Davier, instead generates structs specific for each variant. So if we had a variant path like `Foo::Bar`, we'd generate a struct named `FooBarWrapper`. This would be newtyped around the original enum and forward tuple or struct methods to the enum with the chosen variant. Because it involved using the `Tuple` and `Struct` traits (which are also both bound on `Reflect`), this meant a bit more code had to be generated. For a single struct variant with one field, the generated code amounted to ~110LoC. However, each new field added to that variant only added ~6 more LoC. In order to work properly, the enum had to be transmuted to the generated struct: ```rust fn variant(&self) -> crate::EnumVariant<'_> { match self { Foo::Bar {value: i32} => { let wrapper_ref = unsafe { std::mem::transmute::<&Self, &FooBarWrapper>(self) }; crate::EnumVariant::Struct(wrapper_ref as &dyn crate::Struct) } } } ``` This works because `FooBarWrapper` is defined as `repr(transparent)`. Out of all the alternatives, this would probably be the one most likely to be used again in the future. The reasons for why this PR did not continue to use it was because: * To reduce generated code (which would hopefully speed up compile times) * To avoid cluttering the code with generated structs not visible to the user * To keep bevy_reflect simple and extensible (these generated structs act as proxies and might not play well with current or future systems) * To avoid additional unsafe blocks * My own misunderstanding of @Davier's code That last point is obviously on me. I misjudged the code to be too unsafe and unable to handle variant swapping (which it probably could) when I was rebasing it. Looking over it again when writing up this whole section, I see that it was actually a pretty clever way of handling variant representation. #### Benefits of All-in-One As stated before, the current implementation uses an all-in-one approach. All variants are capable of containing fields as far as `Enum` is concerned. This provides a few benefits that the alternatives do not (reduced indirection, safer code, etc.). The biggest benefit, though, is direct field access. Rather than forcing users to have to go through pattern matching, we grant direct access to the fields contained by the current variant. The reason we can do this is because all of the pattern matching happens internally. Getting the field at index `2` will automatically return `Some(...)` for the current variant if it has a field at that index or `None` if it doesn't (or can't). This could be useful for scenarios where the variant has already been verified or just set/swapped (or even where the type of variant doesn't matter): ```rust let dyn_enum: &mut dyn Enum = &mut Foo::Bar {value: 123}; // We know it's the `Bar` variant let field = dyn_enum.field("value").unwrap(); ``` Reflection is not a type-safe abstraction— almost every return value is wrapped in `Option<...>`. There are plenty of places to check and recheck that a value is what Reflect says it is. Forcing users to have to go through `match` each time they want to access a field might just be an extra step among dozens of other verification processes. Some might disagree, but ultimately, my view is that the benefit here is an improvement to the ergonomics and usability of reflected enums. </details> --- ## Changelog ### Added * Added `Enum` trait * Added `Enum` impl to `Reflect` derive macro * Added `DynamicEnum` struct * Added `DynamicVariant` * Added `EnumInfo` * Added `VariantInfo` * Added `StructVariantInfo` * Added `TupleVariantInfo` * Added `UnitVariantInfo` * Added serializtion/deserialization support for enums * Added `EnumSerializer` * Added `VariantType` * Added `VariantFieldIter` * Added `VariantField` * Added `enum_partial_eq(...)` * Added `enum_hash(...)` ### Changed * `Option<T>` now implements `Enum` * `bevy_window` now depends on `bevy_reflect` * Implemented `Reflect` and `FromReflect` for `WindowId` * Derive `FromReflect` on `PerspectiveProjection` * Derive `FromReflect` on `OrthographicProjection` * Derive `FromReflect` on `WindowOrigin` * Derive `FromReflect` on `ScalingMode` * Derive `FromReflect` on `DepthCalculation` ## Migration Guide * Enums no longer need to be treated as values and usages of `#[reflect_value(...)]` can be removed or replaced by `#[reflect(...)]` * Enums (including `Option<T>`) now take a different format when serializing. The format is described above, but this may cause issues for existing scenes that make use of enums. --- Also shout out to @nicopap for helping clean up some of the code here! It's a big feature so help like this is really appreciated! Co-authored-by: Gino Valente <gino.valente.code@gmail.com>
2022-08-02 22:14:41 +00:00
impl<'a> ReflectStruct<'a> {
/// Access the metadata associated with this struct definition.
pub fn meta(&self) -> &ReflectMeta<'a> {
&self.meta
}
bevy_reflect: Fix ignored/skipped field order (#7575) # Objective Fixes #5101 Alternative to #6511 ## Solution Corrected the behavior for ignored fields in `FromReflect`, which was previously using the incorrect field indexes. Similarly, fields marked with `#[reflect(skip_serializing)]` no longer break when using `FromReflect` after deserialization. This was done by modifying `SerializationData` to store a function pointer that can later be used to generate a default instance of the skipped field during deserialization. The function pointer points to a function generated by the derive macro using the behavior designated by `#[reflect(default)]` (or just `Default` if none provided). The entire output of the macro is now wrapped in an [unnamed constant](https://doc.rust-lang.org/stable/reference/items/constant-items.html#unnamed-constant) which keeps this behavior hygienic. #### Rationale The biggest downside to this approach is that it requires fields marked `#[reflect(skip_serializing)]` to provide the ability to create a default instance— either via a `Default` impl or by specifying a custom one. While this isn't great, I think it might be justified by the fact that we really need to create this value when using `FromReflect` on a deserialized object. And we need to do this _during_ deserialization because after that (at least for tuples and tuple structs) we lose information about which field is which: _"is the value at index 1 in this `DynamicTupleStruct` the actual value for index 1 or is it really the value for index 2 since index 1 is skippable...?"_ #### Alternatives An alternative would be to store `Option<Box<dyn Reflect>>` within `DynamicTuple` and `DynamicTupleStruct` instead of just `Box<dyn Reflect>`. This would allow us to insert "empty"/"missing" fields during deserialization, thus saving the positional information of the skipped fields. However, this may require changing the API of `Tuple` and `TupleStruct` such that they can account for their dynamic counterparts returning `None` for a skipped field. In practice this would probably mean exposing the `Option`-ness of the dynamics onto implementors via methods like `Tuple::drain` or `TupleStruct::field`. Personally, I think requiring `Default` would be better than muddying up the API to account for these special cases. But I'm open to trying out this other approach if the community feels that it's better. --- ## Changelog ### Public Changes #### Fixed - The behaviors of `#[reflect(ignore)]` and `#[reflect(skip_serializing)]` are no longer dependent on field order #### Changed - Fields marked with `#[reflect(skip_serializing)]` now need to either implement `Default` or specify a custom default function using `#[reflect(default = "path::to::some_func")]` - Deserializing a type with fields marked `#[reflect(skip_serializing)]` will now include that field initialized to its specified default value - `SerializationData::new` now takes the new `SkippedField` struct along with the skipped field index - Renamed `SerializationData::is_ignored_field` to `SerializationData::is_field_skipped` #### Added - Added `SkippedField` struct - Added methods `SerializationData::generate_default` and `SerializationData::iter_skipped` ### Internal Changes #### Changed - Replaced `members_to_serialization_denylist` and `BitSet<u32>` with `SerializationDataDef` - The `Reflect` derive is more hygienic as it now outputs within an [unnamed constant](https://doc.rust-lang.org/stable/reference/items/constant-items.html#unnamed-constant) - `StructField::index` has been split up into `StructField::declaration_index` and `StructField::reflection_index` #### Removed - Removed `bitset` dependency ## Migration Guide * Fields marked `#[reflect(skip_serializing)]` now must implement `Default` or specify a custom default function with `#[reflect(default = "path::to::some_func")]` ```rust #[derive(Reflect)] struct MyStruct { #[reflect(skip_serializing)] #[reflect(default = "get_foo_default")] foo: Foo, // <- `Foo` does not impl `Default` so requires a custom function #[reflect(skip_serializing)] bar: Bar, // <- `Bar` impls `Default` } #[derive(Reflect)] struct Foo(i32); #[derive(Reflect, Default)] struct Bar(i32); fn get_foo_default() -> Foo { Foo(123) } ``` * `SerializationData::new` has been changed to expect an iterator of `(usize, SkippedField)` rather than one of just `usize` ```rust // BEFORE SerializationData::new([0, 3].into_iter()); // AFTER SerializationData::new([ (0, SkippedField::new(field_0_default_fn)), (3, SkippedField::new(field_3_default_fn)), ].into_iter()); ``` * `Serialization::is_ignored_field` has been renamed to `Serialization::is_field_skipped` * Fields marked `#[reflect(skip_serializing)]` are now included in deserialization output. This may affect logic that expected those fields to be absent.
2023-10-22 12:43:31 +00:00
/// Returns the [`SerializationDataDef`] for this struct.
pub fn serialization_data(&self) -> Option<&SerializationDataDef> {
self.serialization_data.as_ref()
bevy_reflect: Reflect enums (#4761) # Objective > This is a revival of #1347. Credit for the original PR should go to @Davier. Currently, enums are treated as `ReflectRef::Value` types by `bevy_reflect`. Obviously, there needs to be better a better representation for enums using the reflection API. ## Solution Based on prior work from @Davier, an `Enum` trait has been added as well as the ability to automatically implement it via the `Reflect` derive macro. This allows enums to be expressed dynamically: ```rust #[derive(Reflect)] enum Foo { A, B(usize), C { value: f32 }, } let mut foo = Foo::B(123); assert_eq!("B", foo.variant_name()); assert_eq!(1, foo.field_len()); let new_value = DynamicEnum::from(Foo::C { value: 1.23 }); foo.apply(&new_value); assert_eq!(Foo::C{value: 1.23}, foo); ``` ### Features #### Derive Macro Use the `#[derive(Reflect)]` macro to automatically implement the `Enum` trait for enum definitions. Optionally, you can use `#[reflect(ignore)]` with both variants and variant fields, just like you can with structs. These ignored items will not be considered as part of the reflection and cannot be accessed via reflection. ```rust #[derive(Reflect)] enum TestEnum { A, // Uncomment to ignore all of `B` // #[reflect(ignore)] B(usize), C { // Uncomment to ignore only field `foo` of `C` // #[reflect(ignore)] foo: f32, bar: bool, }, } ``` #### Dynamic Enums Enums may be created/represented dynamically via the `DynamicEnum` struct. The main purpose of this struct is to allow enums to be deserialized into a partial state and to allow dynamic patching. In order to ensure conversion from a `DynamicEnum` to a concrete enum type goes smoothly, be sure to add `FromReflect` to your derive macro. ```rust let mut value = TestEnum::A; // Create from a concrete instance let dyn_enum = DynamicEnum::from(TestEnum::B(123)); value.apply(&dyn_enum); assert_eq!(TestEnum::B(123), value); // Create a purely dynamic instance let dyn_enum = DynamicEnum::new("TestEnum", "A", ()); value.apply(&dyn_enum); assert_eq!(TestEnum::A, value); ``` #### Variants An enum value is always represented as one of its variants— never the enum in its entirety. ```rust let value = TestEnum::A; assert_eq!("A", value.variant_name()); // Since we are using the `A` variant, we cannot also be the `B` variant assert_ne!("B", value.variant_name()); ``` All variant types are representable within the `Enum` trait: unit, struct, and tuple. You can get the current type like: ```rust match value.variant_type() { VariantType::Unit => println!("A unit variant!"), VariantType::Struct => println!("A struct variant!"), VariantType::Tuple => println!("A tuple variant!"), } ``` > Notice that they don't contain any values representing the fields. These are purely tags. If a variant has them, you can access the fields as well: ```rust let mut value = TestEnum::C { foo: 1.23, bar: false }; // Read/write specific fields *value.field_mut("bar").unwrap() = true; // Iterate over the entire collection of fields for field in value.iter_fields() { println!("{} = {:?}", field.name(), field.value()); } ``` #### Variant Swapping It might seem odd to group all variant types under a single trait (why allow `iter_fields` on a unit variant?), but the reason this was done ~~is to easily allow *variant swapping*.~~ As I was recently drafting up the **Design Decisions** section, I discovered that other solutions could have been made to work with variant swapping. So while there are reasons to keep the all-in-one approach, variant swapping is _not_ one of them. ```rust let mut value: Box<dyn Enum> = Box::new(TestEnum::A); value.set(Box::new(TestEnum::B(123))).unwrap(); ``` #### Serialization Enums can be serialized and deserialized via reflection without needing to implement `Serialize` or `Deserialize` themselves (which can save thousands of lines of generated code). Below are the ways an enum can be serialized. > Note, like the rest of reflection-based serialization, the order of the keys in these representations is important! ##### Unit ```json { "type": "my_crate::TestEnum", "enum": { "variant": "A" } } ``` ##### Tuple ```json { "type": "my_crate::TestEnum", "enum": { "variant": "B", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` <details> <summary>Effects on Option</summary> This ends up making `Option` look a little ugly: ```json { "type": "core::option::Option<usize>", "enum": { "variant": "Some", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` </details> ##### Struct ```json { "type": "my_crate::TestEnum", "enum": { "variant": "C", "struct": { "foo": { "type": "f32", "value": 1.23 }, "bar": { "type": "bool", "value": false } } } } ``` ## Design Decisions <details> <summary><strong>View Section</strong></summary> This section is here to provide some context for why certain decisions were made for this PR, alternatives that could have been used instead, and what could be improved upon in the future. ### Variant Representation One of the biggest decisions was to decide on how to represent variants. The current design uses a "all-in-one" design where unit, tuple, and struct variants are all simultaneously represented by the `Enum` trait. This is not the only way it could have been done, though. #### Alternatives ##### 1. Variant Traits One way of representing variants would be to define traits for each variant, implementing them whenever an enum featured at least one instance of them. This would allow us to define variants like: ```rust pub trait Enum: Reflect { fn variant(&self) -> Variant; } pub enum Variant<'a> { Unit, Tuple(&'a dyn TupleVariant), Struct(&'a dyn StructVariant), } pub trait TupleVariant { fn field_len(&self) -> usize; // ... } ``` And then do things like: ```rust fn get_tuple_len(foo: &dyn Enum) -> usize { match foo.variant() { Variant::Tuple(tuple) => tuple.field_len(), _ => panic!("not a tuple variant!") } } ``` The reason this PR does not go with this approach is because of the fact that variants are not separate types. In other words, we cannot implement traits on specific variants— these cover the *entire* enum. This means we offer an easy footgun: ```rust let foo: Option<i32> = None; let my_enum = Box::new(foo) as Box<dyn TupleVariant>; ``` Here, `my_enum` contains `foo`, which is a unit variant. However, since we need to implement `TupleVariant` for `Option` as a whole, it's possible to perform such a cast. This is obviously wrong, but could easily go unnoticed. So unfortunately, this makes it not a good candidate for representing variants. ##### 2. Variant Structs To get around the issue of traits necessarily needing to apply to both the enum and its variants, we could instead use structs that are created on a per-variant basis. This was also considered but was ultimately [[removed](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c)](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c) due to concerns about allocations. Each variant struct would probably look something like: ```rust pub trait Enum: Reflect { fn variant_mut(&self) -> VariantMut; } pub enum VariantMut<'a> { Unit, Tuple(TupleVariantMut), Struct(StructVariantMut), } struct StructVariantMut<'a> { fields: Vec<&'a mut dyn Reflect>, field_indices: HashMap<Cow<'static, str>, usize> } ``` This allows us to isolate struct variants into their own defined struct and define methods specifically for their use. It also prevents users from casting to it since it's not a trait. However, this is not an optimal solution. Both `field_indices` and `fields` will require an allocation (remember, a `Box<[T]>` still requires a `Vec<T>` in order to be constructed). This *might* be a problem if called frequently enough. ##### 3. Generated Structs The original design, implemented by @Davier, instead generates structs specific for each variant. So if we had a variant path like `Foo::Bar`, we'd generate a struct named `FooBarWrapper`. This would be newtyped around the original enum and forward tuple or struct methods to the enum with the chosen variant. Because it involved using the `Tuple` and `Struct` traits (which are also both bound on `Reflect`), this meant a bit more code had to be generated. For a single struct variant with one field, the generated code amounted to ~110LoC. However, each new field added to that variant only added ~6 more LoC. In order to work properly, the enum had to be transmuted to the generated struct: ```rust fn variant(&self) -> crate::EnumVariant<'_> { match self { Foo::Bar {value: i32} => { let wrapper_ref = unsafe { std::mem::transmute::<&Self, &FooBarWrapper>(self) }; crate::EnumVariant::Struct(wrapper_ref as &dyn crate::Struct) } } } ``` This works because `FooBarWrapper` is defined as `repr(transparent)`. Out of all the alternatives, this would probably be the one most likely to be used again in the future. The reasons for why this PR did not continue to use it was because: * To reduce generated code (which would hopefully speed up compile times) * To avoid cluttering the code with generated structs not visible to the user * To keep bevy_reflect simple and extensible (these generated structs act as proxies and might not play well with current or future systems) * To avoid additional unsafe blocks * My own misunderstanding of @Davier's code That last point is obviously on me. I misjudged the code to be too unsafe and unable to handle variant swapping (which it probably could) when I was rebasing it. Looking over it again when writing up this whole section, I see that it was actually a pretty clever way of handling variant representation. #### Benefits of All-in-One As stated before, the current implementation uses an all-in-one approach. All variants are capable of containing fields as far as `Enum` is concerned. This provides a few benefits that the alternatives do not (reduced indirection, safer code, etc.). The biggest benefit, though, is direct field access. Rather than forcing users to have to go through pattern matching, we grant direct access to the fields contained by the current variant. The reason we can do this is because all of the pattern matching happens internally. Getting the field at index `2` will automatically return `Some(...)` for the current variant if it has a field at that index or `None` if it doesn't (or can't). This could be useful for scenarios where the variant has already been verified or just set/swapped (or even where the type of variant doesn't matter): ```rust let dyn_enum: &mut dyn Enum = &mut Foo::Bar {value: 123}; // We know it's the `Bar` variant let field = dyn_enum.field("value").unwrap(); ``` Reflection is not a type-safe abstraction— almost every return value is wrapped in `Option<...>`. There are plenty of places to check and recheck that a value is what Reflect says it is. Forcing users to have to go through `match` each time they want to access a field might just be an extra step among dozens of other verification processes. Some might disagree, but ultimately, my view is that the benefit here is an improvement to the ergonomics and usability of reflected enums. </details> --- ## Changelog ### Added * Added `Enum` trait * Added `Enum` impl to `Reflect` derive macro * Added `DynamicEnum` struct * Added `DynamicVariant` * Added `EnumInfo` * Added `VariantInfo` * Added `StructVariantInfo` * Added `TupleVariantInfo` * Added `UnitVariantInfo` * Added serializtion/deserialization support for enums * Added `EnumSerializer` * Added `VariantType` * Added `VariantFieldIter` * Added `VariantField` * Added `enum_partial_eq(...)` * Added `enum_hash(...)` ### Changed * `Option<T>` now implements `Enum` * `bevy_window` now depends on `bevy_reflect` * Implemented `Reflect` and `FromReflect` for `WindowId` * Derive `FromReflect` on `PerspectiveProjection` * Derive `FromReflect` on `OrthographicProjection` * Derive `FromReflect` on `WindowOrigin` * Derive `FromReflect` on `ScalingMode` * Derive `FromReflect` on `DepthCalculation` ## Migration Guide * Enums no longer need to be treated as values and usages of `#[reflect_value(...)]` can be removed or replaced by `#[reflect(...)]` * Enums (including `Option<T>`) now take a different format when serializing. The format is described above, but this may cause issues for existing scenes that make use of enums. --- Also shout out to @nicopap for helping clean up some of the code here! It's a big feature so help like this is really appreciated! Co-authored-by: Gino Valente <gino.valente.code@gmail.com>
2022-08-02 22:14:41 +00:00
}
/// Returns the `GetTypeRegistration` impl as a `TokenStream`.
///
/// Returns a specific implementation for structs and this method should be preferred over the generic [`get_type_registration`](ReflectMeta) method
Enable deriving Reflect on structs with generic types (#7364) # Objective I recently had an issue, where I have a struct: ``` struct Property { inner: T } ``` that I use as a wrapper for internal purposes. I don't want to update my struct definition to ``` struct Property<T: Reflect>{ inner: T } ``` because I still want to be able to build `Property<T>` for types `T` that are not `Reflect`. (and also because I don't want to update my whole code base with `<T: Reflect>` bounds) I still wanted to have reflection on it (for `bevy_inspector_egui`), but adding `derive(Reflect)` fails with the error: `T cannot be sent between threads safely. T needs to implement Sync.` I believe that `bevy_reflect` should adopt the model of other derives in the case of generics, which is to add the `Reflect` implementation only if the generics also implement `Reflect`. (That is the behaviour of other macros such as `derive(Clone)` or `derive(Debug)`. It's also the current behavior of `derive(FromReflect)`. Basically doing something like: ``` impl<T> Reflect for Foo<T> where T: Reflect ``` ## Solution - I updated the derive macros for `Structs` and `TupleStructs` to add extra `where` bounds. - Every type that is reflected will need a `T: Reflect` bound - Ignored types will need a `T: 'static + Send + Sync` bound. Here's the reason. For cases like this: ``` #[derive(Reflect)] struct Foo<T, U>{ a: T #[reflect(ignore)] b: U } ``` I had to add the bound `'static + Send + Sync` to ignored generics like `U`. The reason is that we want `Foo<T, U>` to be `Reflect: 'static + Send + Sync`, so `Foo<T, U>` must be able to implement those auto-traits. `Foo<T, U>` will only implement those auto-traits if every generic type implements them, including ignored types. This means that the previously compile-fail case now compiles: ``` #[derive(Reflect)] struct Foo<'a> { #[reflect(ignore)] value: &'a str, } ``` But `Foo<'a>` will only be useable in the cases where `'a: 'static` and panic if we don't have `'a: 'static`, which is what we want (nice bonus from this PR ;) ) --- ## Changelog > This section is optional. If this was a trivial fix, or has no externally-visible impact, you can delete this section. ### Added Possibility to add `derive(Reflect)` to structs and enums that contain generic types, like so: ``` #[derive(Reflect)] struct Foo<T>{ a: T } ``` Reflection will only be available if the generic type T also implements `Reflect`. (previously, this would just return a compiler error)
2023-01-28 00:12:06 +00:00
pub fn get_type_registration(
&self,
where_clause_options: &WhereClauseOptions,
) -> proc_macro2::TokenStream {
crate::registration::impl_get_type_registration(
reflect: stable type path v2 (#7184) # Objective - Introduce a stable alternative to [`std::any::type_name`](https://doc.rust-lang.org/std/any/fn.type_name.html). - Rewrite of #5805 with heavy inspiration in design. - On the path to #5830. - Part of solving #3327. ## Solution - Add a `TypePath` trait for static stable type path/name information. - Add a `TypePath` derive macro. - Add a `impl_type_path` macro for implementing internal and foreign types in `bevy_reflect`. --- ## Changelog - Added `TypePath` trait. - Added `DynamicTypePath` trait and `get_type_path` method to `Reflect`. - Added a `TypePath` derive macro. - Added a `bevy_reflect::impl_type_path` for implementing `TypePath` on internal and foreign types in `bevy_reflect`. - Changed `bevy_reflect::utility::(Non)GenericTypeInfoCell` to `(Non)GenericTypedCell<T>` which allows us to be generic over both `TypeInfo` and `TypePath`. - `TypePath` is now a supertrait of `Asset`, `Material` and `Material2d`. - `impl_reflect_struct` needs a `#[type_path = "..."]` attribute to be specified. - `impl_reflect_value` needs to either specify path starting with a double colon (`::core::option::Option`) or an `in my_crate::foo` declaration. - Added `bevy_reflect_derive::ReflectTypePath`. - Most uses of `Ident` in `bevy_reflect_derive` changed to use `ReflectTypePath`. ## Migration Guide - Implementors of `Asset`, `Material` and `Material2d` now also need to derive `TypePath`. - Manual implementors of `Reflect` will need to implement the new `get_type_path` method. ## Open Questions - [x] ~This PR currently does not migrate any usages of `std::any::type_name` to use `bevy_reflect::TypePath` to ease the review process. Should it?~ Migration will be left to a follow-up PR. - [ ] This PR adds a lot of `#[derive(TypePath)]` and `T: TypePath` to satisfy new bounds, mostly when deriving `TypeUuid`. Should we make `TypePath` a supertrait of `TypeUuid`? [Should we remove `TypeUuid` in favour of `TypePath`?](https://github.com/bevyengine/bevy/pull/5805/files/2afbd855327c4b68e0a6b6f03118f289988441a4#r961067892)
2023-06-05 20:31:20 +00:00
self.meta(),
Enable deriving Reflect on structs with generic types (#7364) # Objective I recently had an issue, where I have a struct: ``` struct Property { inner: T } ``` that I use as a wrapper for internal purposes. I don't want to update my struct definition to ``` struct Property<T: Reflect>{ inner: T } ``` because I still want to be able to build `Property<T>` for types `T` that are not `Reflect`. (and also because I don't want to update my whole code base with `<T: Reflect>` bounds) I still wanted to have reflection on it (for `bevy_inspector_egui`), but adding `derive(Reflect)` fails with the error: `T cannot be sent between threads safely. T needs to implement Sync.` I believe that `bevy_reflect` should adopt the model of other derives in the case of generics, which is to add the `Reflect` implementation only if the generics also implement `Reflect`. (That is the behaviour of other macros such as `derive(Clone)` or `derive(Debug)`. It's also the current behavior of `derive(FromReflect)`. Basically doing something like: ``` impl<T> Reflect for Foo<T> where T: Reflect ``` ## Solution - I updated the derive macros for `Structs` and `TupleStructs` to add extra `where` bounds. - Every type that is reflected will need a `T: Reflect` bound - Ignored types will need a `T: 'static + Send + Sync` bound. Here's the reason. For cases like this: ``` #[derive(Reflect)] struct Foo<T, U>{ a: T #[reflect(ignore)] b: U } ``` I had to add the bound `'static + Send + Sync` to ignored generics like `U`. The reason is that we want `Foo<T, U>` to be `Reflect: 'static + Send + Sync`, so `Foo<T, U>` must be able to implement those auto-traits. `Foo<T, U>` will only implement those auto-traits if every generic type implements them, including ignored types. This means that the previously compile-fail case now compiles: ``` #[derive(Reflect)] struct Foo<'a> { #[reflect(ignore)] value: &'a str, } ``` But `Foo<'a>` will only be useable in the cases where `'a: 'static` and panic if we don't have `'a: 'static`, which is what we want (nice bonus from this PR ;) ) --- ## Changelog > This section is optional. If this was a trivial fix, or has no externally-visible impact, you can delete this section. ### Added Possibility to add `derive(Reflect)` to structs and enums that contain generic types, like so: ``` #[derive(Reflect)] struct Foo<T>{ a: T } ``` Reflection will only be available if the generic type T also implements `Reflect`. (previously, this would just return a compiler error)
2023-01-28 00:12:06 +00:00
where_clause_options,
bevy_reflect: Fix ignored/skipped field order (#7575) # Objective Fixes #5101 Alternative to #6511 ## Solution Corrected the behavior for ignored fields in `FromReflect`, which was previously using the incorrect field indexes. Similarly, fields marked with `#[reflect(skip_serializing)]` no longer break when using `FromReflect` after deserialization. This was done by modifying `SerializationData` to store a function pointer that can later be used to generate a default instance of the skipped field during deserialization. The function pointer points to a function generated by the derive macro using the behavior designated by `#[reflect(default)]` (or just `Default` if none provided). The entire output of the macro is now wrapped in an [unnamed constant](https://doc.rust-lang.org/stable/reference/items/constant-items.html#unnamed-constant) which keeps this behavior hygienic. #### Rationale The biggest downside to this approach is that it requires fields marked `#[reflect(skip_serializing)]` to provide the ability to create a default instance— either via a `Default` impl or by specifying a custom one. While this isn't great, I think it might be justified by the fact that we really need to create this value when using `FromReflect` on a deserialized object. And we need to do this _during_ deserialization because after that (at least for tuples and tuple structs) we lose information about which field is which: _"is the value at index 1 in this `DynamicTupleStruct` the actual value for index 1 or is it really the value for index 2 since index 1 is skippable...?"_ #### Alternatives An alternative would be to store `Option<Box<dyn Reflect>>` within `DynamicTuple` and `DynamicTupleStruct` instead of just `Box<dyn Reflect>`. This would allow us to insert "empty"/"missing" fields during deserialization, thus saving the positional information of the skipped fields. However, this may require changing the API of `Tuple` and `TupleStruct` such that they can account for their dynamic counterparts returning `None` for a skipped field. In practice this would probably mean exposing the `Option`-ness of the dynamics onto implementors via methods like `Tuple::drain` or `TupleStruct::field`. Personally, I think requiring `Default` would be better than muddying up the API to account for these special cases. But I'm open to trying out this other approach if the community feels that it's better. --- ## Changelog ### Public Changes #### Fixed - The behaviors of `#[reflect(ignore)]` and `#[reflect(skip_serializing)]` are no longer dependent on field order #### Changed - Fields marked with `#[reflect(skip_serializing)]` now need to either implement `Default` or specify a custom default function using `#[reflect(default = "path::to::some_func")]` - Deserializing a type with fields marked `#[reflect(skip_serializing)]` will now include that field initialized to its specified default value - `SerializationData::new` now takes the new `SkippedField` struct along with the skipped field index - Renamed `SerializationData::is_ignored_field` to `SerializationData::is_field_skipped` #### Added - Added `SkippedField` struct - Added methods `SerializationData::generate_default` and `SerializationData::iter_skipped` ### Internal Changes #### Changed - Replaced `members_to_serialization_denylist` and `BitSet<u32>` with `SerializationDataDef` - The `Reflect` derive is more hygienic as it now outputs within an [unnamed constant](https://doc.rust-lang.org/stable/reference/items/constant-items.html#unnamed-constant) - `StructField::index` has been split up into `StructField::declaration_index` and `StructField::reflection_index` #### Removed - Removed `bitset` dependency ## Migration Guide * Fields marked `#[reflect(skip_serializing)]` now must implement `Default` or specify a custom default function with `#[reflect(default = "path::to::some_func")]` ```rust #[derive(Reflect)] struct MyStruct { #[reflect(skip_serializing)] #[reflect(default = "get_foo_default")] foo: Foo, // <- `Foo` does not impl `Default` so requires a custom function #[reflect(skip_serializing)] bar: Bar, // <- `Bar` impls `Default` } #[derive(Reflect)] struct Foo(i32); #[derive(Reflect, Default)] struct Bar(i32); fn get_foo_default() -> Foo { Foo(123) } ``` * `SerializationData::new` has been changed to expect an iterator of `(usize, SkippedField)` rather than one of just `usize` ```rust // BEFORE SerializationData::new([0, 3].into_iter()); // AFTER SerializationData::new([ (0, SkippedField::new(field_0_default_fn)), (3, SkippedField::new(field_3_default_fn)), ].into_iter()); ``` * `Serialization::is_ignored_field` has been renamed to `Serialization::is_field_skipped` * Fields marked `#[reflect(skip_serializing)]` are now included in deserialization output. This may affect logic that expected those fields to be absent.
2023-10-22 12:43:31 +00:00
self.serialization_data(),
bevy_reflect: Recursive registration (#5781) # Objective Resolves #4154 Currently, registration must all be done manually: ```rust #[derive(Reflect)] struct Foo(Bar); #[derive(Reflect)] struct Bar(Baz); #[derive(Reflect)] struct Baz(usize); fn main() { // ... app .register_type::<Foo>() .register_type::<Bar>() .register_type::<Baz>() // .register_type::<usize>() <- This one is handled by Bevy, thankfully // ... } ``` This can grow really quickly and become very annoying to add, remove, and update as types change. It would be great if we could help reduce the number of types that a user must manually implement themselves. ## Solution As suggested in #4154, this PR adds automatic recursive registration. Essentially, when a type is registered, it may now also choose to register additional types along with it using the new `GetTypeRegistration::register_type_dependencies` trait method. The `Reflect` derive macro now automatically does this for all fields in structs, tuple structs, struct variants, and tuple variants. This is also done for tuples, arrays, `Vec<T>`, `HashMap<K, V>`, and `Option<T>`. This allows us to simplify the code above like: ```rust #[derive(Reflect)] struct Foo(Bar); #[derive(Reflect)] struct Bar(Baz); #[derive(Reflect)] struct Baz(usize); fn main() { // ... app.register_type::<Foo>() // ... } ``` This automatic registration only occurs if the type has not yet been registered. If it has been registered, we simply skip it and move to the next one. This reduces the cost of registration and prevents overwriting customized registrations. ## Considerations While this does improve ergonomics on one front, it's important to look at some of the arguments against adopting a PR like this. #### Generic Bounds ~~Since we need to be able to register the fields individually, we need those fields to implement `GetTypeRegistration`. This forces users to then add this trait as a bound on their generic arguments. This annoyance could be relieved with something like #5772.~~ This is no longer a major issue as the `Reflect` derive now adds the `GetTypeRegistration` bound by default. This should technically be okay, since we already add the `Reflect` bound. However, this can also be considered a breaking change for manual implementations that left out a `GetTypeRegistration` impl ~~or for items that contain dynamic types (e.g. `DynamicStruct`) since those also do not implement `GetTypeRegistration`~~. #### Registration Assumptions By automatically registering fields, users might inadvertently be relying on certain types to be automatically registered. If `Foo` auto-registers `Bar`, but `Foo` is later removed from the code, then anywhere that previously used or relied on `Bar`'s registration would now fail. --- ## Changelog - Added recursive type registration to structs, tuple structs, struct variants, tuple variants, tuples, arrays, `Vec<T>`, `HashMap<K, V>`, and `Option<T>` - Added a new trait in the hidden `bevy_reflect::__macro_exports` module called `RegisterForReflection` - Added `GetTypeRegistration` impl for `bevy_render::render_asset::RenderAssetUsages` ## Migration Guide All types that derive `Reflect` will now automatically add `GetTypeRegistration` as a bound on all (unignored) fields. This means that all reflected fields will need to also implement `GetTypeRegistration`. If all fields **derive** `Reflect` or are implemented in `bevy_reflect`, this should not cause any issues. However, manual implementations of `Reflect` that excluded a `GetTypeRegistration` impl for their type will need to add one. ```rust #[derive(Reflect)] struct Foo<T: FromReflect> { data: MyCustomType<T> } // OLD impl<T: FromReflect> Reflect for MyCustomType<T> {/* ... */} // NEW impl<T: FromReflect + GetTypeRegistration> Reflect for MyCustomType<T> {/* ... */} impl<T: FromReflect + GetTypeRegistration> GetTypeRegistration for MyCustomType<T> {/* ... */} ``` --------- Co-authored-by: James Liu <contact@jamessliu.com> Co-authored-by: radiish <cb.setho@gmail.com> Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2024-03-04 19:04:10 +00:00
Some(self.active_types().iter()),
)
bevy_reflect: Reflect enums (#4761) # Objective > This is a revival of #1347. Credit for the original PR should go to @Davier. Currently, enums are treated as `ReflectRef::Value` types by `bevy_reflect`. Obviously, there needs to be better a better representation for enums using the reflection API. ## Solution Based on prior work from @Davier, an `Enum` trait has been added as well as the ability to automatically implement it via the `Reflect` derive macro. This allows enums to be expressed dynamically: ```rust #[derive(Reflect)] enum Foo { A, B(usize), C { value: f32 }, } let mut foo = Foo::B(123); assert_eq!("B", foo.variant_name()); assert_eq!(1, foo.field_len()); let new_value = DynamicEnum::from(Foo::C { value: 1.23 }); foo.apply(&new_value); assert_eq!(Foo::C{value: 1.23}, foo); ``` ### Features #### Derive Macro Use the `#[derive(Reflect)]` macro to automatically implement the `Enum` trait for enum definitions. Optionally, you can use `#[reflect(ignore)]` with both variants and variant fields, just like you can with structs. These ignored items will not be considered as part of the reflection and cannot be accessed via reflection. ```rust #[derive(Reflect)] enum TestEnum { A, // Uncomment to ignore all of `B` // #[reflect(ignore)] B(usize), C { // Uncomment to ignore only field `foo` of `C` // #[reflect(ignore)] foo: f32, bar: bool, }, } ``` #### Dynamic Enums Enums may be created/represented dynamically via the `DynamicEnum` struct. The main purpose of this struct is to allow enums to be deserialized into a partial state and to allow dynamic patching. In order to ensure conversion from a `DynamicEnum` to a concrete enum type goes smoothly, be sure to add `FromReflect` to your derive macro. ```rust let mut value = TestEnum::A; // Create from a concrete instance let dyn_enum = DynamicEnum::from(TestEnum::B(123)); value.apply(&dyn_enum); assert_eq!(TestEnum::B(123), value); // Create a purely dynamic instance let dyn_enum = DynamicEnum::new("TestEnum", "A", ()); value.apply(&dyn_enum); assert_eq!(TestEnum::A, value); ``` #### Variants An enum value is always represented as one of its variants— never the enum in its entirety. ```rust let value = TestEnum::A; assert_eq!("A", value.variant_name()); // Since we are using the `A` variant, we cannot also be the `B` variant assert_ne!("B", value.variant_name()); ``` All variant types are representable within the `Enum` trait: unit, struct, and tuple. You can get the current type like: ```rust match value.variant_type() { VariantType::Unit => println!("A unit variant!"), VariantType::Struct => println!("A struct variant!"), VariantType::Tuple => println!("A tuple variant!"), } ``` > Notice that they don't contain any values representing the fields. These are purely tags. If a variant has them, you can access the fields as well: ```rust let mut value = TestEnum::C { foo: 1.23, bar: false }; // Read/write specific fields *value.field_mut("bar").unwrap() = true; // Iterate over the entire collection of fields for field in value.iter_fields() { println!("{} = {:?}", field.name(), field.value()); } ``` #### Variant Swapping It might seem odd to group all variant types under a single trait (why allow `iter_fields` on a unit variant?), but the reason this was done ~~is to easily allow *variant swapping*.~~ As I was recently drafting up the **Design Decisions** section, I discovered that other solutions could have been made to work with variant swapping. So while there are reasons to keep the all-in-one approach, variant swapping is _not_ one of them. ```rust let mut value: Box<dyn Enum> = Box::new(TestEnum::A); value.set(Box::new(TestEnum::B(123))).unwrap(); ``` #### Serialization Enums can be serialized and deserialized via reflection without needing to implement `Serialize` or `Deserialize` themselves (which can save thousands of lines of generated code). Below are the ways an enum can be serialized. > Note, like the rest of reflection-based serialization, the order of the keys in these representations is important! ##### Unit ```json { "type": "my_crate::TestEnum", "enum": { "variant": "A" } } ``` ##### Tuple ```json { "type": "my_crate::TestEnum", "enum": { "variant": "B", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` <details> <summary>Effects on Option</summary> This ends up making `Option` look a little ugly: ```json { "type": "core::option::Option<usize>", "enum": { "variant": "Some", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` </details> ##### Struct ```json { "type": "my_crate::TestEnum", "enum": { "variant": "C", "struct": { "foo": { "type": "f32", "value": 1.23 }, "bar": { "type": "bool", "value": false } } } } ``` ## Design Decisions <details> <summary><strong>View Section</strong></summary> This section is here to provide some context for why certain decisions were made for this PR, alternatives that could have been used instead, and what could be improved upon in the future. ### Variant Representation One of the biggest decisions was to decide on how to represent variants. The current design uses a "all-in-one" design where unit, tuple, and struct variants are all simultaneously represented by the `Enum` trait. This is not the only way it could have been done, though. #### Alternatives ##### 1. Variant Traits One way of representing variants would be to define traits for each variant, implementing them whenever an enum featured at least one instance of them. This would allow us to define variants like: ```rust pub trait Enum: Reflect { fn variant(&self) -> Variant; } pub enum Variant<'a> { Unit, Tuple(&'a dyn TupleVariant), Struct(&'a dyn StructVariant), } pub trait TupleVariant { fn field_len(&self) -> usize; // ... } ``` And then do things like: ```rust fn get_tuple_len(foo: &dyn Enum) -> usize { match foo.variant() { Variant::Tuple(tuple) => tuple.field_len(), _ => panic!("not a tuple variant!") } } ``` The reason this PR does not go with this approach is because of the fact that variants are not separate types. In other words, we cannot implement traits on specific variants— these cover the *entire* enum. This means we offer an easy footgun: ```rust let foo: Option<i32> = None; let my_enum = Box::new(foo) as Box<dyn TupleVariant>; ``` Here, `my_enum` contains `foo`, which is a unit variant. However, since we need to implement `TupleVariant` for `Option` as a whole, it's possible to perform such a cast. This is obviously wrong, but could easily go unnoticed. So unfortunately, this makes it not a good candidate for representing variants. ##### 2. Variant Structs To get around the issue of traits necessarily needing to apply to both the enum and its variants, we could instead use structs that are created on a per-variant basis. This was also considered but was ultimately [[removed](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c)](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c) due to concerns about allocations. Each variant struct would probably look something like: ```rust pub trait Enum: Reflect { fn variant_mut(&self) -> VariantMut; } pub enum VariantMut<'a> { Unit, Tuple(TupleVariantMut), Struct(StructVariantMut), } struct StructVariantMut<'a> { fields: Vec<&'a mut dyn Reflect>, field_indices: HashMap<Cow<'static, str>, usize> } ``` This allows us to isolate struct variants into their own defined struct and define methods specifically for their use. It also prevents users from casting to it since it's not a trait. However, this is not an optimal solution. Both `field_indices` and `fields` will require an allocation (remember, a `Box<[T]>` still requires a `Vec<T>` in order to be constructed). This *might* be a problem if called frequently enough. ##### 3. Generated Structs The original design, implemented by @Davier, instead generates structs specific for each variant. So if we had a variant path like `Foo::Bar`, we'd generate a struct named `FooBarWrapper`. This would be newtyped around the original enum and forward tuple or struct methods to the enum with the chosen variant. Because it involved using the `Tuple` and `Struct` traits (which are also both bound on `Reflect`), this meant a bit more code had to be generated. For a single struct variant with one field, the generated code amounted to ~110LoC. However, each new field added to that variant only added ~6 more LoC. In order to work properly, the enum had to be transmuted to the generated struct: ```rust fn variant(&self) -> crate::EnumVariant<'_> { match self { Foo::Bar {value: i32} => { let wrapper_ref = unsafe { std::mem::transmute::<&Self, &FooBarWrapper>(self) }; crate::EnumVariant::Struct(wrapper_ref as &dyn crate::Struct) } } } ``` This works because `FooBarWrapper` is defined as `repr(transparent)`. Out of all the alternatives, this would probably be the one most likely to be used again in the future. The reasons for why this PR did not continue to use it was because: * To reduce generated code (which would hopefully speed up compile times) * To avoid cluttering the code with generated structs not visible to the user * To keep bevy_reflect simple and extensible (these generated structs act as proxies and might not play well with current or future systems) * To avoid additional unsafe blocks * My own misunderstanding of @Davier's code That last point is obviously on me. I misjudged the code to be too unsafe and unable to handle variant swapping (which it probably could) when I was rebasing it. Looking over it again when writing up this whole section, I see that it was actually a pretty clever way of handling variant representation. #### Benefits of All-in-One As stated before, the current implementation uses an all-in-one approach. All variants are capable of containing fields as far as `Enum` is concerned. This provides a few benefits that the alternatives do not (reduced indirection, safer code, etc.). The biggest benefit, though, is direct field access. Rather than forcing users to have to go through pattern matching, we grant direct access to the fields contained by the current variant. The reason we can do this is because all of the pattern matching happens internally. Getting the field at index `2` will automatically return `Some(...)` for the current variant if it has a field at that index or `None` if it doesn't (or can't). This could be useful for scenarios where the variant has already been verified or just set/swapped (or even where the type of variant doesn't matter): ```rust let dyn_enum: &mut dyn Enum = &mut Foo::Bar {value: 123}; // We know it's the `Bar` variant let field = dyn_enum.field("value").unwrap(); ``` Reflection is not a type-safe abstraction— almost every return value is wrapped in `Option<...>`. There are plenty of places to check and recheck that a value is what Reflect says it is. Forcing users to have to go through `match` each time they want to access a field might just be an extra step among dozens of other verification processes. Some might disagree, but ultimately, my view is that the benefit here is an improvement to the ergonomics and usability of reflected enums. </details> --- ## Changelog ### Added * Added `Enum` trait * Added `Enum` impl to `Reflect` derive macro * Added `DynamicEnum` struct * Added `DynamicVariant` * Added `EnumInfo` * Added `VariantInfo` * Added `StructVariantInfo` * Added `TupleVariantInfo` * Added `UnitVariantInfo` * Added serializtion/deserialization support for enums * Added `EnumSerializer` * Added `VariantType` * Added `VariantFieldIter` * Added `VariantField` * Added `enum_partial_eq(...)` * Added `enum_hash(...)` ### Changed * `Option<T>` now implements `Enum` * `bevy_window` now depends on `bevy_reflect` * Implemented `Reflect` and `FromReflect` for `WindowId` * Derive `FromReflect` on `PerspectiveProjection` * Derive `FromReflect` on `OrthographicProjection` * Derive `FromReflect` on `WindowOrigin` * Derive `FromReflect` on `ScalingMode` * Derive `FromReflect` on `DepthCalculation` ## Migration Guide * Enums no longer need to be treated as values and usages of `#[reflect_value(...)]` can be removed or replaced by `#[reflect(...)]` * Enums (including `Option<T>`) now take a different format when serializing. The format is described above, but this may cause issues for existing scenes that make use of enums. --- Also shout out to @nicopap for helping clean up some of the code here! It's a big feature so help like this is really appreciated! Co-authored-by: Gino Valente <gino.valente.code@gmail.com>
2022-08-02 22:14:41 +00:00
}
/// Get a collection of types which are exposed to the reflection API
pub fn active_types(&self) -> Vec<Type> {
Enable deriving Reflect on structs with generic types (#7364) # Objective I recently had an issue, where I have a struct: ``` struct Property { inner: T } ``` that I use as a wrapper for internal purposes. I don't want to update my struct definition to ``` struct Property<T: Reflect>{ inner: T } ``` because I still want to be able to build `Property<T>` for types `T` that are not `Reflect`. (and also because I don't want to update my whole code base with `<T: Reflect>` bounds) I still wanted to have reflection on it (for `bevy_inspector_egui`), but adding `derive(Reflect)` fails with the error: `T cannot be sent between threads safely. T needs to implement Sync.` I believe that `bevy_reflect` should adopt the model of other derives in the case of generics, which is to add the `Reflect` implementation only if the generics also implement `Reflect`. (That is the behaviour of other macros such as `derive(Clone)` or `derive(Debug)`. It's also the current behavior of `derive(FromReflect)`. Basically doing something like: ``` impl<T> Reflect for Foo<T> where T: Reflect ``` ## Solution - I updated the derive macros for `Structs` and `TupleStructs` to add extra `where` bounds. - Every type that is reflected will need a `T: Reflect` bound - Ignored types will need a `T: 'static + Send + Sync` bound. Here's the reason. For cases like this: ``` #[derive(Reflect)] struct Foo<T, U>{ a: T #[reflect(ignore)] b: U } ``` I had to add the bound `'static + Send + Sync` to ignored generics like `U`. The reason is that we want `Foo<T, U>` to be `Reflect: 'static + Send + Sync`, so `Foo<T, U>` must be able to implement those auto-traits. `Foo<T, U>` will only implement those auto-traits if every generic type implements them, including ignored types. This means that the previously compile-fail case now compiles: ``` #[derive(Reflect)] struct Foo<'a> { #[reflect(ignore)] value: &'a str, } ``` But `Foo<'a>` will only be useable in the cases where `'a: 'static` and panic if we don't have `'a: 'static`, which is what we want (nice bonus from this PR ;) ) --- ## Changelog > This section is optional. If this was a trivial fix, or has no externally-visible impact, you can delete this section. ### Added Possibility to add `derive(Reflect)` to structs and enums that contain generic types, like so: ``` #[derive(Reflect)] struct Foo<T>{ a: T } ``` Reflection will only be available if the generic type T also implements `Reflect`. (previously, this would just return a compiler error)
2023-01-28 00:12:06 +00:00
self.active_fields()
bevy_reflect: Reflect remote types (#6042) # Objective The goal with this PR is to allow the use of types that don't implement `Reflect` within the reflection API. Rust's [orphan rule](https://doc.rust-lang.org/book/ch10-02-traits.html#implementing-a-trait-on-a-type) prevents implementing a trait on an external type when neither type nor trait are owned by the implementor. This means that if a crate, `cool_rust_lib`, defines a type, `Foo`, then a user cannot use it with reflection. What this means is that we have to ignore it most of the time: ```rust #[derive(Reflect)] struct SomeStruct { #[reflect(ignore)] data: cool_rust_lib::Foo } ``` Obviously, it's impossible to implement `Reflect` on `Foo`. But does it *have* to be? Most of reflection doesn't deal with concrete types— it's almost all using `dyn Reflect`. And being very metadata-driven, it should theoretically be possible. I mean, [`serde`](https://serde.rs/remote-derive.html) does it. ## Solution > Special thanks to @danielhenrymantilla for their help reviewing this PR and offering wisdom wrt safety. Taking a page out of `serde`'s book, this PR adds the ability to easily use "remote types" with reflection. In this context, a "remote type" is the external type for which we have no ability to implement `Reflect`. This adds the `#[reflect_remote(...)]` attribute macro, which is used to generate "remote type wrappers". All you have to do is define the wrapper exactly the same as the remote type's definition: ```rust // Pretend this is our external crate mod cool_rust_lib { #[derive(Default)] struct Foo { pub value: String } } #[reflect_remote(cool_rust_lib::Foo)] struct FooWrapper { pub value: String } ``` > **Note:** All fields in the external type *must* be public. This could be addressed with a separate getter/setter attribute either in this PR or in another one. The macro takes this user-defined item and transforms it into a newtype wrapper around the external type, marking it as `#[repr(transparent)]`. The fields/variants defined by the user are simply used to build out the reflection impls. Additionally, it generates an implementation of the new trait, `ReflectRemote`, which helps prevent accidental misuses of this API. Therefore, the output generated by the macro would look something like: ```rust #[repr(transparent)] struct FooWrapper(pub cool_rust_lib::Foo); impl ReflectRemote for FooWrapper { type Remote = cool_rust_lib::Foo; // transmutation methods... } // reflection impls... // these will acknowledge and make use of the `value` field ``` Internally, the reflection API will pass around the `FooWrapper` and [transmute](https://doc.rust-lang.org/std/mem/fn.transmute.html) it where necessary. All we have to do is then tell `Reflect` to do that. So rather than ignoring the field, we tell `Reflect` to use our wrapper using the `#[reflect(remote = ...)]` field attribute: ```rust #[derive(Reflect)] struct SomeStruct { #[reflect(remote = FooWrapper)] data: cool_rust_lib::Foo } ``` #### Other Macros & Type Data Because this macro consumes the defined item and generates a new one, we can't just put our macros anywhere. All macros that should be passed to the generated struct need to come *below* this macro. For example, to derive `Default` and register its associated type data: ```rust // ✅ GOOD #[reflect_remote(cool_rust_lib::Foo)] #[derive(Default)] #[reflect(Default)] struct FooWrapper { pub value: String } // ❌ BAD #[derive(Default)] #[reflect_remote(cool_rust_lib::Foo)] #[reflect(Default)] struct FooWrapper { pub value: String } ``` #### Generics Generics are forwarded to the generated struct as well. They should also be defined in the same order: ```rust #[reflect_remote(RemoteGeneric<'a, T1, T2>)] struct GenericWrapper<'a, T1, T2> { pub foo: &'a T1, pub bar: &'a T2, } ``` > Naming does *not* need to match the original definition's. Only order matters here. > Also note that the code above is just a demonstration and doesn't actually compile since we'd need to enforce certain bounds (e.g. `T1: Reflect`, `'a: 'static`, etc.) #### Nesting And, yes, you can nest remote types: ```rust #[reflect_remote(RemoteOuter)] struct OuterWrapper { #[reflect(remote = InnerWrapper)] pub inner: RemoteInner } #[reflect_remote(RemoteInner)] struct InnerWrapper(usize); ``` #### Assertions This macro will also generate some compile-time assertions to ensure that the correct types are used. It's important we catch this early so users don't have to wait for something to panic. And it also helps keep our `unsafe` a little safer. For example, a wrapper definition that does not match its corresponding remote type will result in an error: ```rust mod external_crate { pub struct TheirStruct(pub u32); } #[reflect_remote(external_crate::TheirStruct)] struct MyStruct(pub String); // ERROR: expected type `u32` but found `String` ``` <details> <summary>Generated Assertion</summary> ```rust const _: () = { #[allow(non_snake_case)] #[allow(unused_variables)] #[allow(unused_assignments)] #[allow(unreachable_patterns)] #[allow(clippy::multiple_bound_locations)] fn assert_wrapper_definition_matches_remote_type( mut __remote__: external_crate::TheirStruct, ) { __remote__.0 = (|| -> ::core::option::Option<String> { None })().unwrap(); } }; ``` </details> Additionally, using the incorrect type in a `#[reflect(remote = ...)]` attribute should result in an error: ```rust mod external_crate { pub struct TheirFoo(pub u32); pub struct TheirBar(pub i32); } #[reflect_remote(external_crate::TheirFoo)] struct MyFoo(pub u32); #[reflect_remote(external_crate::TheirBar)] struct MyBar(pub i32); #[derive(Reflect)] struct MyStruct { #[reflect(remote = MyBar)] // ERROR: expected type `TheirFoo` but found struct `TheirBar` foo: external_crate::TheirFoo } ``` <details> <summary>Generated Assertion</summary> ```rust const _: () = { struct RemoteFieldAssertions; impl RemoteFieldAssertions { #[allow(non_snake_case)] #[allow(clippy::multiple_bound_locations)] fn assert__foo__is_valid_remote() { let _: <MyBar as bevy_reflect::ReflectRemote>::Remote = (|| -> ::core::option::Option<external_crate::TheirFoo> { None })().unwrap(); } } }; ``` </details> ### Discussion There are a couple points that I think still need discussion or validation. - [x] 1. `Any` shenanigans ~~If we wanted to downcast our remote type from a `dyn Reflect`, we'd have to first downcast to the wrapper then extract the inner type. This PR has a [commit](b840db9f74cb6d357f951cb11b150d46bac89ee2) that addresses this by making all the `Reflect::*any` methods return the inner type rather than the wrapper type. This allows us to downcast directly to our remote type.~~ ~~However, I'm not sure if this is something we want to do. For unknowing users, it could be confusing and seemingly inconsistent. Is it worth keeping? Or should this behavior be removed?~~ I think this should be fine. The remote wrapper is an implementation detail and users should not need to downcast to the wrapper type. Feel free to let me know if there are other opinions on this though! - [x] 2. Implementing `Deref/DerefMut` and `From` ~~We don't currently do this, but should we implement other traits on the generated transparent struct? We could implement `Deref`/`DerefMut` to easily access the inner type. And we could implement `From` for easier conversion between the two types (e.g. `T: Into<Foo>`).~~ As mentioned in the comments, we probably don't need to do this. Again, the remote wrapper is an implementation detail, and should generally not be used directly. - [x] 3. ~~Should we define a getter/setter field attribute in this PR as well or leave it for a future one?~~ I think this should be saved for a future PR - [ ] 4. Any foreseeable issues with this implementation? #### Alternatives One alternative to defining our own `ReflectRemote` would be to use [bytemuck's `TransparentWrapper`](https://docs.rs/bytemuck/1.13.1/bytemuck/trait.TransparentWrapper.html) (as suggested by @danielhenrymantilla). This is definitely a viable option, as `ReflectRemote` is pretty much the same thing as `TransparentWrapper`. However, the cost would be bringing in a new crate— though, it is already in use in a few other sub-crates like bevy_render. I think we're okay just defining `ReflectRemote` ourselves, but we can go the bytemuck route if we'd prefer offloading that work to another crate. --- ## Changelog * Added the `#[reflect_remote(...)]` attribute macro to allow `Reflect` to be used on remote types * Added `ReflectRemote` trait for ensuring proper remote wrapper usage
2024-08-12 19:12:53 +00:00
.map(|field| field.reflected_type().clone())
Enable deriving Reflect on structs with generic types (#7364) # Objective I recently had an issue, where I have a struct: ``` struct Property { inner: T } ``` that I use as a wrapper for internal purposes. I don't want to update my struct definition to ``` struct Property<T: Reflect>{ inner: T } ``` because I still want to be able to build `Property<T>` for types `T` that are not `Reflect`. (and also because I don't want to update my whole code base with `<T: Reflect>` bounds) I still wanted to have reflection on it (for `bevy_inspector_egui`), but adding `derive(Reflect)` fails with the error: `T cannot be sent between threads safely. T needs to implement Sync.` I believe that `bevy_reflect` should adopt the model of other derives in the case of generics, which is to add the `Reflect` implementation only if the generics also implement `Reflect`. (That is the behaviour of other macros such as `derive(Clone)` or `derive(Debug)`. It's also the current behavior of `derive(FromReflect)`. Basically doing something like: ``` impl<T> Reflect for Foo<T> where T: Reflect ``` ## Solution - I updated the derive macros for `Structs` and `TupleStructs` to add extra `where` bounds. - Every type that is reflected will need a `T: Reflect` bound - Ignored types will need a `T: 'static + Send + Sync` bound. Here's the reason. For cases like this: ``` #[derive(Reflect)] struct Foo<T, U>{ a: T #[reflect(ignore)] b: U } ``` I had to add the bound `'static + Send + Sync` to ignored generics like `U`. The reason is that we want `Foo<T, U>` to be `Reflect: 'static + Send + Sync`, so `Foo<T, U>` must be able to implement those auto-traits. `Foo<T, U>` will only implement those auto-traits if every generic type implements them, including ignored types. This means that the previously compile-fail case now compiles: ``` #[derive(Reflect)] struct Foo<'a> { #[reflect(ignore)] value: &'a str, } ``` But `Foo<'a>` will only be useable in the cases where `'a: 'static` and panic if we don't have `'a: 'static`, which is what we want (nice bonus from this PR ;) ) --- ## Changelog > This section is optional. If this was a trivial fix, or has no externally-visible impact, you can delete this section. ### Added Possibility to add `derive(Reflect)` to structs and enums that contain generic types, like so: ``` #[derive(Reflect)] struct Foo<T>{ a: T } ``` Reflection will only be available if the generic type T also implements `Reflect`. (previously, this would just return a compiler error)
2023-01-28 00:12:06 +00:00
.collect()
}
bevy_reflect: Recursive registration (#5781) # Objective Resolves #4154 Currently, registration must all be done manually: ```rust #[derive(Reflect)] struct Foo(Bar); #[derive(Reflect)] struct Bar(Baz); #[derive(Reflect)] struct Baz(usize); fn main() { // ... app .register_type::<Foo>() .register_type::<Bar>() .register_type::<Baz>() // .register_type::<usize>() <- This one is handled by Bevy, thankfully // ... } ``` This can grow really quickly and become very annoying to add, remove, and update as types change. It would be great if we could help reduce the number of types that a user must manually implement themselves. ## Solution As suggested in #4154, this PR adds automatic recursive registration. Essentially, when a type is registered, it may now also choose to register additional types along with it using the new `GetTypeRegistration::register_type_dependencies` trait method. The `Reflect` derive macro now automatically does this for all fields in structs, tuple structs, struct variants, and tuple variants. This is also done for tuples, arrays, `Vec<T>`, `HashMap<K, V>`, and `Option<T>`. This allows us to simplify the code above like: ```rust #[derive(Reflect)] struct Foo(Bar); #[derive(Reflect)] struct Bar(Baz); #[derive(Reflect)] struct Baz(usize); fn main() { // ... app.register_type::<Foo>() // ... } ``` This automatic registration only occurs if the type has not yet been registered. If it has been registered, we simply skip it and move to the next one. This reduces the cost of registration and prevents overwriting customized registrations. ## Considerations While this does improve ergonomics on one front, it's important to look at some of the arguments against adopting a PR like this. #### Generic Bounds ~~Since we need to be able to register the fields individually, we need those fields to implement `GetTypeRegistration`. This forces users to then add this trait as a bound on their generic arguments. This annoyance could be relieved with something like #5772.~~ This is no longer a major issue as the `Reflect` derive now adds the `GetTypeRegistration` bound by default. This should technically be okay, since we already add the `Reflect` bound. However, this can also be considered a breaking change for manual implementations that left out a `GetTypeRegistration` impl ~~or for items that contain dynamic types (e.g. `DynamicStruct`) since those also do not implement `GetTypeRegistration`~~. #### Registration Assumptions By automatically registering fields, users might inadvertently be relying on certain types to be automatically registered. If `Foo` auto-registers `Bar`, but `Foo` is later removed from the code, then anywhere that previously used or relied on `Bar`'s registration would now fail. --- ## Changelog - Added recursive type registration to structs, tuple structs, struct variants, tuple variants, tuples, arrays, `Vec<T>`, `HashMap<K, V>`, and `Option<T>` - Added a new trait in the hidden `bevy_reflect::__macro_exports` module called `RegisterForReflection` - Added `GetTypeRegistration` impl for `bevy_render::render_asset::RenderAssetUsages` ## Migration Guide All types that derive `Reflect` will now automatically add `GetTypeRegistration` as a bound on all (unignored) fields. This means that all reflected fields will need to also implement `GetTypeRegistration`. If all fields **derive** `Reflect` or are implemented in `bevy_reflect`, this should not cause any issues. However, manual implementations of `Reflect` that excluded a `GetTypeRegistration` impl for their type will need to add one. ```rust #[derive(Reflect)] struct Foo<T: FromReflect> { data: MyCustomType<T> } // OLD impl<T: FromReflect> Reflect for MyCustomType<T> {/* ... */} // NEW impl<T: FromReflect + GetTypeRegistration> Reflect for MyCustomType<T> {/* ... */} impl<T: FromReflect + GetTypeRegistration> GetTypeRegistration for MyCustomType<T> {/* ... */} ``` --------- Co-authored-by: James Liu <contact@jamessliu.com> Co-authored-by: radiish <cb.setho@gmail.com> Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2024-03-04 19:04:10 +00:00
/// Get an iterator of fields which are exposed to the reflection API.
pub fn active_fields(&self) -> impl Iterator<Item = &StructField<'a>> {
bevy_reflect: Recursive registration (#5781) # Objective Resolves #4154 Currently, registration must all be done manually: ```rust #[derive(Reflect)] struct Foo(Bar); #[derive(Reflect)] struct Bar(Baz); #[derive(Reflect)] struct Baz(usize); fn main() { // ... app .register_type::<Foo>() .register_type::<Bar>() .register_type::<Baz>() // .register_type::<usize>() <- This one is handled by Bevy, thankfully // ... } ``` This can grow really quickly and become very annoying to add, remove, and update as types change. It would be great if we could help reduce the number of types that a user must manually implement themselves. ## Solution As suggested in #4154, this PR adds automatic recursive registration. Essentially, when a type is registered, it may now also choose to register additional types along with it using the new `GetTypeRegistration::register_type_dependencies` trait method. The `Reflect` derive macro now automatically does this for all fields in structs, tuple structs, struct variants, and tuple variants. This is also done for tuples, arrays, `Vec<T>`, `HashMap<K, V>`, and `Option<T>`. This allows us to simplify the code above like: ```rust #[derive(Reflect)] struct Foo(Bar); #[derive(Reflect)] struct Bar(Baz); #[derive(Reflect)] struct Baz(usize); fn main() { // ... app.register_type::<Foo>() // ... } ``` This automatic registration only occurs if the type has not yet been registered. If it has been registered, we simply skip it and move to the next one. This reduces the cost of registration and prevents overwriting customized registrations. ## Considerations While this does improve ergonomics on one front, it's important to look at some of the arguments against adopting a PR like this. #### Generic Bounds ~~Since we need to be able to register the fields individually, we need those fields to implement `GetTypeRegistration`. This forces users to then add this trait as a bound on their generic arguments. This annoyance could be relieved with something like #5772.~~ This is no longer a major issue as the `Reflect` derive now adds the `GetTypeRegistration` bound by default. This should technically be okay, since we already add the `Reflect` bound. However, this can also be considered a breaking change for manual implementations that left out a `GetTypeRegistration` impl ~~or for items that contain dynamic types (e.g. `DynamicStruct`) since those also do not implement `GetTypeRegistration`~~. #### Registration Assumptions By automatically registering fields, users might inadvertently be relying on certain types to be automatically registered. If `Foo` auto-registers `Bar`, but `Foo` is later removed from the code, then anywhere that previously used or relied on `Bar`'s registration would now fail. --- ## Changelog - Added recursive type registration to structs, tuple structs, struct variants, tuple variants, tuples, arrays, `Vec<T>`, `HashMap<K, V>`, and `Option<T>` - Added a new trait in the hidden `bevy_reflect::__macro_exports` module called `RegisterForReflection` - Added `GetTypeRegistration` impl for `bevy_render::render_asset::RenderAssetUsages` ## Migration Guide All types that derive `Reflect` will now automatically add `GetTypeRegistration` as a bound on all (unignored) fields. This means that all reflected fields will need to also implement `GetTypeRegistration`. If all fields **derive** `Reflect` or are implemented in `bevy_reflect`, this should not cause any issues. However, manual implementations of `Reflect` that excluded a `GetTypeRegistration` impl for their type will need to add one. ```rust #[derive(Reflect)] struct Foo<T: FromReflect> { data: MyCustomType<T> } // OLD impl<T: FromReflect> Reflect for MyCustomType<T> {/* ... */} // NEW impl<T: FromReflect + GetTypeRegistration> Reflect for MyCustomType<T> {/* ... */} impl<T: FromReflect + GetTypeRegistration> GetTypeRegistration for MyCustomType<T> {/* ... */} ``` --------- Co-authored-by: James Liu <contact@jamessliu.com> Co-authored-by: radiish <cb.setho@gmail.com> Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2024-03-04 19:04:10 +00:00
self.fields()
.iter()
Enable deriving Reflect on structs with generic types (#7364) # Objective I recently had an issue, where I have a struct: ``` struct Property { inner: T } ``` that I use as a wrapper for internal purposes. I don't want to update my struct definition to ``` struct Property<T: Reflect>{ inner: T } ``` because I still want to be able to build `Property<T>` for types `T` that are not `Reflect`. (and also because I don't want to update my whole code base with `<T: Reflect>` bounds) I still wanted to have reflection on it (for `bevy_inspector_egui`), but adding `derive(Reflect)` fails with the error: `T cannot be sent between threads safely. T needs to implement Sync.` I believe that `bevy_reflect` should adopt the model of other derives in the case of generics, which is to add the `Reflect` implementation only if the generics also implement `Reflect`. (That is the behaviour of other macros such as `derive(Clone)` or `derive(Debug)`. It's also the current behavior of `derive(FromReflect)`. Basically doing something like: ``` impl<T> Reflect for Foo<T> where T: Reflect ``` ## Solution - I updated the derive macros for `Structs` and `TupleStructs` to add extra `where` bounds. - Every type that is reflected will need a `T: Reflect` bound - Ignored types will need a `T: 'static + Send + Sync` bound. Here's the reason. For cases like this: ``` #[derive(Reflect)] struct Foo<T, U>{ a: T #[reflect(ignore)] b: U } ``` I had to add the bound `'static + Send + Sync` to ignored generics like `U`. The reason is that we want `Foo<T, U>` to be `Reflect: 'static + Send + Sync`, so `Foo<T, U>` must be able to implement those auto-traits. `Foo<T, U>` will only implement those auto-traits if every generic type implements them, including ignored types. This means that the previously compile-fail case now compiles: ``` #[derive(Reflect)] struct Foo<'a> { #[reflect(ignore)] value: &'a str, } ``` But `Foo<'a>` will only be useable in the cases where `'a: 'static` and panic if we don't have `'a: 'static`, which is what we want (nice bonus from this PR ;) ) --- ## Changelog > This section is optional. If this was a trivial fix, or has no externally-visible impact, you can delete this section. ### Added Possibility to add `derive(Reflect)` to structs and enums that contain generic types, like so: ``` #[derive(Reflect)] struct Foo<T>{ a: T } ``` Reflection will only be available if the generic type T also implements `Reflect`. (previously, this would just return a compiler error)
2023-01-28 00:12:06 +00:00
.filter(|field| field.attrs.ignore.is_active())
}
/// Get an iterator of fields which are ignored by the reflection API
pub fn ignored_fields(&self) -> impl Iterator<Item = &StructField<'a>> {
bevy_reflect: Recursive registration (#5781) # Objective Resolves #4154 Currently, registration must all be done manually: ```rust #[derive(Reflect)] struct Foo(Bar); #[derive(Reflect)] struct Bar(Baz); #[derive(Reflect)] struct Baz(usize); fn main() { // ... app .register_type::<Foo>() .register_type::<Bar>() .register_type::<Baz>() // .register_type::<usize>() <- This one is handled by Bevy, thankfully // ... } ``` This can grow really quickly and become very annoying to add, remove, and update as types change. It would be great if we could help reduce the number of types that a user must manually implement themselves. ## Solution As suggested in #4154, this PR adds automatic recursive registration. Essentially, when a type is registered, it may now also choose to register additional types along with it using the new `GetTypeRegistration::register_type_dependencies` trait method. The `Reflect` derive macro now automatically does this for all fields in structs, tuple structs, struct variants, and tuple variants. This is also done for tuples, arrays, `Vec<T>`, `HashMap<K, V>`, and `Option<T>`. This allows us to simplify the code above like: ```rust #[derive(Reflect)] struct Foo(Bar); #[derive(Reflect)] struct Bar(Baz); #[derive(Reflect)] struct Baz(usize); fn main() { // ... app.register_type::<Foo>() // ... } ``` This automatic registration only occurs if the type has not yet been registered. If it has been registered, we simply skip it and move to the next one. This reduces the cost of registration and prevents overwriting customized registrations. ## Considerations While this does improve ergonomics on one front, it's important to look at some of the arguments against adopting a PR like this. #### Generic Bounds ~~Since we need to be able to register the fields individually, we need those fields to implement `GetTypeRegistration`. This forces users to then add this trait as a bound on their generic arguments. This annoyance could be relieved with something like #5772.~~ This is no longer a major issue as the `Reflect` derive now adds the `GetTypeRegistration` bound by default. This should technically be okay, since we already add the `Reflect` bound. However, this can also be considered a breaking change for manual implementations that left out a `GetTypeRegistration` impl ~~or for items that contain dynamic types (e.g. `DynamicStruct`) since those also do not implement `GetTypeRegistration`~~. #### Registration Assumptions By automatically registering fields, users might inadvertently be relying on certain types to be automatically registered. If `Foo` auto-registers `Bar`, but `Foo` is later removed from the code, then anywhere that previously used or relied on `Bar`'s registration would now fail. --- ## Changelog - Added recursive type registration to structs, tuple structs, struct variants, tuple variants, tuples, arrays, `Vec<T>`, `HashMap<K, V>`, and `Option<T>` - Added a new trait in the hidden `bevy_reflect::__macro_exports` module called `RegisterForReflection` - Added `GetTypeRegistration` impl for `bevy_render::render_asset::RenderAssetUsages` ## Migration Guide All types that derive `Reflect` will now automatically add `GetTypeRegistration` as a bound on all (unignored) fields. This means that all reflected fields will need to also implement `GetTypeRegistration`. If all fields **derive** `Reflect` or are implemented in `bevy_reflect`, this should not cause any issues. However, manual implementations of `Reflect` that excluded a `GetTypeRegistration` impl for their type will need to add one. ```rust #[derive(Reflect)] struct Foo<T: FromReflect> { data: MyCustomType<T> } // OLD impl<T: FromReflect> Reflect for MyCustomType<T> {/* ... */} // NEW impl<T: FromReflect + GetTypeRegistration> Reflect for MyCustomType<T> {/* ... */} impl<T: FromReflect + GetTypeRegistration> GetTypeRegistration for MyCustomType<T> {/* ... */} ``` --------- Co-authored-by: James Liu <contact@jamessliu.com> Co-authored-by: radiish <cb.setho@gmail.com> Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2024-03-04 19:04:10 +00:00
self.fields()
.iter()
Enable deriving Reflect on structs with generic types (#7364) # Objective I recently had an issue, where I have a struct: ``` struct Property { inner: T } ``` that I use as a wrapper for internal purposes. I don't want to update my struct definition to ``` struct Property<T: Reflect>{ inner: T } ``` because I still want to be able to build `Property<T>` for types `T` that are not `Reflect`. (and also because I don't want to update my whole code base with `<T: Reflect>` bounds) I still wanted to have reflection on it (for `bevy_inspector_egui`), but adding `derive(Reflect)` fails with the error: `T cannot be sent between threads safely. T needs to implement Sync.` I believe that `bevy_reflect` should adopt the model of other derives in the case of generics, which is to add the `Reflect` implementation only if the generics also implement `Reflect`. (That is the behaviour of other macros such as `derive(Clone)` or `derive(Debug)`. It's also the current behavior of `derive(FromReflect)`. Basically doing something like: ``` impl<T> Reflect for Foo<T> where T: Reflect ``` ## Solution - I updated the derive macros for `Structs` and `TupleStructs` to add extra `where` bounds. - Every type that is reflected will need a `T: Reflect` bound - Ignored types will need a `T: 'static + Send + Sync` bound. Here's the reason. For cases like this: ``` #[derive(Reflect)] struct Foo<T, U>{ a: T #[reflect(ignore)] b: U } ``` I had to add the bound `'static + Send + Sync` to ignored generics like `U`. The reason is that we want `Foo<T, U>` to be `Reflect: 'static + Send + Sync`, so `Foo<T, U>` must be able to implement those auto-traits. `Foo<T, U>` will only implement those auto-traits if every generic type implements them, including ignored types. This means that the previously compile-fail case now compiles: ``` #[derive(Reflect)] struct Foo<'a> { #[reflect(ignore)] value: &'a str, } ``` But `Foo<'a>` will only be useable in the cases where `'a: 'static` and panic if we don't have `'a: 'static`, which is what we want (nice bonus from this PR ;) ) --- ## Changelog > This section is optional. If this was a trivial fix, or has no externally-visible impact, you can delete this section. ### Added Possibility to add `derive(Reflect)` to structs and enums that contain generic types, like so: ``` #[derive(Reflect)] struct Foo<T>{ a: T } ``` Reflection will only be available if the generic type T also implements `Reflect`. (previously, this would just return a compiler error)
2023-01-28 00:12:06 +00:00
.filter(|field| field.attrs.ignore.is_ignored())
bevy_reflect: Reflect enums (#4761) # Objective > This is a revival of #1347. Credit for the original PR should go to @Davier. Currently, enums are treated as `ReflectRef::Value` types by `bevy_reflect`. Obviously, there needs to be better a better representation for enums using the reflection API. ## Solution Based on prior work from @Davier, an `Enum` trait has been added as well as the ability to automatically implement it via the `Reflect` derive macro. This allows enums to be expressed dynamically: ```rust #[derive(Reflect)] enum Foo { A, B(usize), C { value: f32 }, } let mut foo = Foo::B(123); assert_eq!("B", foo.variant_name()); assert_eq!(1, foo.field_len()); let new_value = DynamicEnum::from(Foo::C { value: 1.23 }); foo.apply(&new_value); assert_eq!(Foo::C{value: 1.23}, foo); ``` ### Features #### Derive Macro Use the `#[derive(Reflect)]` macro to automatically implement the `Enum` trait for enum definitions. Optionally, you can use `#[reflect(ignore)]` with both variants and variant fields, just like you can with structs. These ignored items will not be considered as part of the reflection and cannot be accessed via reflection. ```rust #[derive(Reflect)] enum TestEnum { A, // Uncomment to ignore all of `B` // #[reflect(ignore)] B(usize), C { // Uncomment to ignore only field `foo` of `C` // #[reflect(ignore)] foo: f32, bar: bool, }, } ``` #### Dynamic Enums Enums may be created/represented dynamically via the `DynamicEnum` struct. The main purpose of this struct is to allow enums to be deserialized into a partial state and to allow dynamic patching. In order to ensure conversion from a `DynamicEnum` to a concrete enum type goes smoothly, be sure to add `FromReflect` to your derive macro. ```rust let mut value = TestEnum::A; // Create from a concrete instance let dyn_enum = DynamicEnum::from(TestEnum::B(123)); value.apply(&dyn_enum); assert_eq!(TestEnum::B(123), value); // Create a purely dynamic instance let dyn_enum = DynamicEnum::new("TestEnum", "A", ()); value.apply(&dyn_enum); assert_eq!(TestEnum::A, value); ``` #### Variants An enum value is always represented as one of its variants— never the enum in its entirety. ```rust let value = TestEnum::A; assert_eq!("A", value.variant_name()); // Since we are using the `A` variant, we cannot also be the `B` variant assert_ne!("B", value.variant_name()); ``` All variant types are representable within the `Enum` trait: unit, struct, and tuple. You can get the current type like: ```rust match value.variant_type() { VariantType::Unit => println!("A unit variant!"), VariantType::Struct => println!("A struct variant!"), VariantType::Tuple => println!("A tuple variant!"), } ``` > Notice that they don't contain any values representing the fields. These are purely tags. If a variant has them, you can access the fields as well: ```rust let mut value = TestEnum::C { foo: 1.23, bar: false }; // Read/write specific fields *value.field_mut("bar").unwrap() = true; // Iterate over the entire collection of fields for field in value.iter_fields() { println!("{} = {:?}", field.name(), field.value()); } ``` #### Variant Swapping It might seem odd to group all variant types under a single trait (why allow `iter_fields` on a unit variant?), but the reason this was done ~~is to easily allow *variant swapping*.~~ As I was recently drafting up the **Design Decisions** section, I discovered that other solutions could have been made to work with variant swapping. So while there are reasons to keep the all-in-one approach, variant swapping is _not_ one of them. ```rust let mut value: Box<dyn Enum> = Box::new(TestEnum::A); value.set(Box::new(TestEnum::B(123))).unwrap(); ``` #### Serialization Enums can be serialized and deserialized via reflection without needing to implement `Serialize` or `Deserialize` themselves (which can save thousands of lines of generated code). Below are the ways an enum can be serialized. > Note, like the rest of reflection-based serialization, the order of the keys in these representations is important! ##### Unit ```json { "type": "my_crate::TestEnum", "enum": { "variant": "A" } } ``` ##### Tuple ```json { "type": "my_crate::TestEnum", "enum": { "variant": "B", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` <details> <summary>Effects on Option</summary> This ends up making `Option` look a little ugly: ```json { "type": "core::option::Option<usize>", "enum": { "variant": "Some", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` </details> ##### Struct ```json { "type": "my_crate::TestEnum", "enum": { "variant": "C", "struct": { "foo": { "type": "f32", "value": 1.23 }, "bar": { "type": "bool", "value": false } } } } ``` ## Design Decisions <details> <summary><strong>View Section</strong></summary> This section is here to provide some context for why certain decisions were made for this PR, alternatives that could have been used instead, and what could be improved upon in the future. ### Variant Representation One of the biggest decisions was to decide on how to represent variants. The current design uses a "all-in-one" design where unit, tuple, and struct variants are all simultaneously represented by the `Enum` trait. This is not the only way it could have been done, though. #### Alternatives ##### 1. Variant Traits One way of representing variants would be to define traits for each variant, implementing them whenever an enum featured at least one instance of them. This would allow us to define variants like: ```rust pub trait Enum: Reflect { fn variant(&self) -> Variant; } pub enum Variant<'a> { Unit, Tuple(&'a dyn TupleVariant), Struct(&'a dyn StructVariant), } pub trait TupleVariant { fn field_len(&self) -> usize; // ... } ``` And then do things like: ```rust fn get_tuple_len(foo: &dyn Enum) -> usize { match foo.variant() { Variant::Tuple(tuple) => tuple.field_len(), _ => panic!("not a tuple variant!") } } ``` The reason this PR does not go with this approach is because of the fact that variants are not separate types. In other words, we cannot implement traits on specific variants— these cover the *entire* enum. This means we offer an easy footgun: ```rust let foo: Option<i32> = None; let my_enum = Box::new(foo) as Box<dyn TupleVariant>; ``` Here, `my_enum` contains `foo`, which is a unit variant. However, since we need to implement `TupleVariant` for `Option` as a whole, it's possible to perform such a cast. This is obviously wrong, but could easily go unnoticed. So unfortunately, this makes it not a good candidate for representing variants. ##### 2. Variant Structs To get around the issue of traits necessarily needing to apply to both the enum and its variants, we could instead use structs that are created on a per-variant basis. This was also considered but was ultimately [[removed](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c)](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c) due to concerns about allocations. Each variant struct would probably look something like: ```rust pub trait Enum: Reflect { fn variant_mut(&self) -> VariantMut; } pub enum VariantMut<'a> { Unit, Tuple(TupleVariantMut), Struct(StructVariantMut), } struct StructVariantMut<'a> { fields: Vec<&'a mut dyn Reflect>, field_indices: HashMap<Cow<'static, str>, usize> } ``` This allows us to isolate struct variants into their own defined struct and define methods specifically for their use. It also prevents users from casting to it since it's not a trait. However, this is not an optimal solution. Both `field_indices` and `fields` will require an allocation (remember, a `Box<[T]>` still requires a `Vec<T>` in order to be constructed). This *might* be a problem if called frequently enough. ##### 3. Generated Structs The original design, implemented by @Davier, instead generates structs specific for each variant. So if we had a variant path like `Foo::Bar`, we'd generate a struct named `FooBarWrapper`. This would be newtyped around the original enum and forward tuple or struct methods to the enum with the chosen variant. Because it involved using the `Tuple` and `Struct` traits (which are also both bound on `Reflect`), this meant a bit more code had to be generated. For a single struct variant with one field, the generated code amounted to ~110LoC. However, each new field added to that variant only added ~6 more LoC. In order to work properly, the enum had to be transmuted to the generated struct: ```rust fn variant(&self) -> crate::EnumVariant<'_> { match self { Foo::Bar {value: i32} => { let wrapper_ref = unsafe { std::mem::transmute::<&Self, &FooBarWrapper>(self) }; crate::EnumVariant::Struct(wrapper_ref as &dyn crate::Struct) } } } ``` This works because `FooBarWrapper` is defined as `repr(transparent)`. Out of all the alternatives, this would probably be the one most likely to be used again in the future. The reasons for why this PR did not continue to use it was because: * To reduce generated code (which would hopefully speed up compile times) * To avoid cluttering the code with generated structs not visible to the user * To keep bevy_reflect simple and extensible (these generated structs act as proxies and might not play well with current or future systems) * To avoid additional unsafe blocks * My own misunderstanding of @Davier's code That last point is obviously on me. I misjudged the code to be too unsafe and unable to handle variant swapping (which it probably could) when I was rebasing it. Looking over it again when writing up this whole section, I see that it was actually a pretty clever way of handling variant representation. #### Benefits of All-in-One As stated before, the current implementation uses an all-in-one approach. All variants are capable of containing fields as far as `Enum` is concerned. This provides a few benefits that the alternatives do not (reduced indirection, safer code, etc.). The biggest benefit, though, is direct field access. Rather than forcing users to have to go through pattern matching, we grant direct access to the fields contained by the current variant. The reason we can do this is because all of the pattern matching happens internally. Getting the field at index `2` will automatically return `Some(...)` for the current variant if it has a field at that index or `None` if it doesn't (or can't). This could be useful for scenarios where the variant has already been verified or just set/swapped (or even where the type of variant doesn't matter): ```rust let dyn_enum: &mut dyn Enum = &mut Foo::Bar {value: 123}; // We know it's the `Bar` variant let field = dyn_enum.field("value").unwrap(); ``` Reflection is not a type-safe abstraction— almost every return value is wrapped in `Option<...>`. There are plenty of places to check and recheck that a value is what Reflect says it is. Forcing users to have to go through `match` each time they want to access a field might just be an extra step among dozens of other verification processes. Some might disagree, but ultimately, my view is that the benefit here is an improvement to the ergonomics and usability of reflected enums. </details> --- ## Changelog ### Added * Added `Enum` trait * Added `Enum` impl to `Reflect` derive macro * Added `DynamicEnum` struct * Added `DynamicVariant` * Added `EnumInfo` * Added `VariantInfo` * Added `StructVariantInfo` * Added `TupleVariantInfo` * Added `UnitVariantInfo` * Added serializtion/deserialization support for enums * Added `EnumSerializer` * Added `VariantType` * Added `VariantFieldIter` * Added `VariantField` * Added `enum_partial_eq(...)` * Added `enum_hash(...)` ### Changed * `Option<T>` now implements `Enum` * `bevy_window` now depends on `bevy_reflect` * Implemented `Reflect` and `FromReflect` for `WindowId` * Derive `FromReflect` on `PerspectiveProjection` * Derive `FromReflect` on `OrthographicProjection` * Derive `FromReflect` on `WindowOrigin` * Derive `FromReflect` on `ScalingMode` * Derive `FromReflect` on `DepthCalculation` ## Migration Guide * Enums no longer need to be treated as values and usages of `#[reflect_value(...)]` can be removed or replaced by `#[reflect(...)]` * Enums (including `Option<T>`) now take a different format when serializing. The format is described above, but this may cause issues for existing scenes that make use of enums. --- Also shout out to @nicopap for helping clean up some of the code here! It's a big feature so help like this is really appreciated! Co-authored-by: Gino Valente <gino.valente.code@gmail.com>
2022-08-02 22:14:41 +00:00
}
/// The complete set of fields in this struct.
pub fn fields(&self) -> &[StructField<'a>] {
&self.fields
}
Enable deriving Reflect on structs with generic types (#7364) # Objective I recently had an issue, where I have a struct: ``` struct Property { inner: T } ``` that I use as a wrapper for internal purposes. I don't want to update my struct definition to ``` struct Property<T: Reflect>{ inner: T } ``` because I still want to be able to build `Property<T>` for types `T` that are not `Reflect`. (and also because I don't want to update my whole code base with `<T: Reflect>` bounds) I still wanted to have reflection on it (for `bevy_inspector_egui`), but adding `derive(Reflect)` fails with the error: `T cannot be sent between threads safely. T needs to implement Sync.` I believe that `bevy_reflect` should adopt the model of other derives in the case of generics, which is to add the `Reflect` implementation only if the generics also implement `Reflect`. (That is the behaviour of other macros such as `derive(Clone)` or `derive(Debug)`. It's also the current behavior of `derive(FromReflect)`. Basically doing something like: ``` impl<T> Reflect for Foo<T> where T: Reflect ``` ## Solution - I updated the derive macros for `Structs` and `TupleStructs` to add extra `where` bounds. - Every type that is reflected will need a `T: Reflect` bound - Ignored types will need a `T: 'static + Send + Sync` bound. Here's the reason. For cases like this: ``` #[derive(Reflect)] struct Foo<T, U>{ a: T #[reflect(ignore)] b: U } ``` I had to add the bound `'static + Send + Sync` to ignored generics like `U`. The reason is that we want `Foo<T, U>` to be `Reflect: 'static + Send + Sync`, so `Foo<T, U>` must be able to implement those auto-traits. `Foo<T, U>` will only implement those auto-traits if every generic type implements them, including ignored types. This means that the previously compile-fail case now compiles: ``` #[derive(Reflect)] struct Foo<'a> { #[reflect(ignore)] value: &'a str, } ``` But `Foo<'a>` will only be useable in the cases where `'a: 'static` and panic if we don't have `'a: 'static`, which is what we want (nice bonus from this PR ;) ) --- ## Changelog > This section is optional. If this was a trivial fix, or has no externally-visible impact, you can delete this section. ### Added Possibility to add `derive(Reflect)` to structs and enums that contain generic types, like so: ``` #[derive(Reflect)] struct Foo<T>{ a: T } ``` Reflection will only be available if the generic type T also implements `Reflect`. (previously, this would just return a compiler error)
2023-01-28 00:12:06 +00:00
pub fn where_clause_options(&self) -> WhereClauseOptions {
bevy_reflect: Split `#[reflect(where)]` (#11597) # Objective Revert the changes to type parameter bounds introduced in #9046, improves the `#[reflect(where)]` attribute (also from #9046), and adds the ability to opt out of field bounds. This is based on suggestions by @soqb and discussion on [Discord](https://discord.com/channels/691052431525675048/1002362493634629796/1201227833826103427). ## Solution Reverts the changes to type parameter bounds when deriving `Reflect`, introduced in #9046. This was originally done as a means of fixing a recursion issue (#8965). However, as @soqb pointed out, we could achieve the same result by simply making an opt-out attribute instead of messing with the type parameter bounds. This PR has four main changes: 1. Reverts the type parameter bounds from #9046 2. Includes `TypePath` as a default bound for active fields 3. Changes `#reflect(where)]` to be strictly additive 4. Adds `#reflect(no_field_bounds)]` to opt out of field bounds Change 1 means that, like before, type parameters only receive at most the `TypePath` bound (if `#[reflect(type_path = false)]` is not present) and active fields receive the `Reflect` or `FromReflect` bound. And with Change 2, they will also receive `TypePath` (since it's indirectly required by `Typed` to construct `NamedField` and `UnnamedField` instances). Change 3 was made to make room for Change 4. By splitting out the responsibility of `#reflect(where)]`, we can use it with or without `#reflect(no_field_bounds)]` for various use cases. For example, if we hadn't done this, the following would have failed: ```rust // Since we're not using `#reflect(no_field_bounds)]`, // `T::Assoc` is automatically given the required bounds // of `FromReflect + TypePath` #[derive(Reflect)] #[reflect(where T::Assoc: OtherTrait)] struct Foo<T: MyTrait> { value: T::Assoc, } ``` This provides more flexibility to the user while still letting them add or remove most trait bounds. And to solve the original recursion issue, we can do: ```rust #[derive(Reflect)] #[reflect(no_field_bounds)] // <-- Added struct Foo { foo: Vec<Foo> } ``` #### Bounds All in all, we now have four sets of trait bounds: - `Self` gets the bounds `Any + Send + Sync` - Type parameters get the bound `TypePath`. This can be opted out of with `#[reflect(type_path = false)]` - Active fields get the bounds `TypePath` and `FromReflect`/`Reflect` bounds. This can be opted out of with `#reflect(no_field_bounds)]` - Custom bounds can be added with `#[reflect(where)]` --- ## Changelog - Revert some changes #9046 - `#reflect(where)]` is now strictly additive - Added `#reflect(no_field_bounds)]` attribute to opt out of automatic field trait bounds when deriving `Reflect` - Made the `TypePath` requirement on fields when deriving `Reflect` more explicit ## Migration Guide > [!important] > This PR shouldn't be a breaking change relative to the current version of Bevy (v0.12). And since it removes the breaking parts of #9046, that PR also won't need a migration guide.
2024-01-29 17:54:17 +00:00
WhereClauseOptions::new_with_fields(self.meta(), self.active_types().into_boxed_slice())
Enable deriving Reflect on structs with generic types (#7364) # Objective I recently had an issue, where I have a struct: ``` struct Property { inner: T } ``` that I use as a wrapper for internal purposes. I don't want to update my struct definition to ``` struct Property<T: Reflect>{ inner: T } ``` because I still want to be able to build `Property<T>` for types `T` that are not `Reflect`. (and also because I don't want to update my whole code base with `<T: Reflect>` bounds) I still wanted to have reflection on it (for `bevy_inspector_egui`), but adding `derive(Reflect)` fails with the error: `T cannot be sent between threads safely. T needs to implement Sync.` I believe that `bevy_reflect` should adopt the model of other derives in the case of generics, which is to add the `Reflect` implementation only if the generics also implement `Reflect`. (That is the behaviour of other macros such as `derive(Clone)` or `derive(Debug)`. It's also the current behavior of `derive(FromReflect)`. Basically doing something like: ``` impl<T> Reflect for Foo<T> where T: Reflect ``` ## Solution - I updated the derive macros for `Structs` and `TupleStructs` to add extra `where` bounds. - Every type that is reflected will need a `T: Reflect` bound - Ignored types will need a `T: 'static + Send + Sync` bound. Here's the reason. For cases like this: ``` #[derive(Reflect)] struct Foo<T, U>{ a: T #[reflect(ignore)] b: U } ``` I had to add the bound `'static + Send + Sync` to ignored generics like `U`. The reason is that we want `Foo<T, U>` to be `Reflect: 'static + Send + Sync`, so `Foo<T, U>` must be able to implement those auto-traits. `Foo<T, U>` will only implement those auto-traits if every generic type implements them, including ignored types. This means that the previously compile-fail case now compiles: ``` #[derive(Reflect)] struct Foo<'a> { #[reflect(ignore)] value: &'a str, } ``` But `Foo<'a>` will only be useable in the cases where `'a: 'static` and panic if we don't have `'a: 'static`, which is what we want (nice bonus from this PR ;) ) --- ## Changelog > This section is optional. If this was a trivial fix, or has no externally-visible impact, you can delete this section. ### Added Possibility to add `derive(Reflect)` to structs and enums that contain generic types, like so: ``` #[derive(Reflect)] struct Foo<T>{ a: T } ``` Reflection will only be available if the generic type T also implements `Reflect`. (previously, this would just return a compiler error)
2023-01-28 00:12:06 +00:00
}
bevy_reflect: Custom attributes (#11659) # Objective As work on the editor starts to ramp up, it might be nice to start allowing types to specify custom attributes. These can be used to provide certain functionality to fields, such as ranges or controlling how data is displayed. A good example of this can be seen in [`bevy-inspector-egui`](https://github.com/jakobhellermann/bevy-inspector-egui) with its [`InspectorOptions`](https://docs.rs/bevy-inspector-egui/0.22.1/bevy_inspector_egui/struct.InspectorOptions.html): ```rust #[derive(Reflect, Default, InspectorOptions)] #[reflect(InspectorOptions)] struct Slider { #[inspector(min = 0.0, max = 1.0)] value: f32, } ``` Normally, as demonstrated in the example above, these attributes are handled by a derive macro and stored in a corresponding `TypeData` struct (i.e. `ReflectInspectorOptions`). Ideally, we would have a good way of defining this directly via reflection so that users don't need to create and manage a whole proc macro just to allow these sorts of attributes. And note that this doesn't have to just be for inspectors and editors. It can be used for things done purely on the code side of things. ## Solution Create a new method for storing attributes on fields via the `Reflect` derive. These custom attributes are stored in type info (e.g. `NamedField`, `StructInfo`, etc.). ```rust #[derive(Reflect)] struct Slider { #[reflect(@0.0..=1.0)] value: f64, } let TypeInfo::Struct(info) = Slider::type_info() else { panic!("expected struct info"); }; let field = info.field("value").unwrap(); let range = field.get_attribute::<RangeInclusive<f64>>().unwrap(); assert_eq!(*range, 0.0..=1.0); ``` ## TODO - [x] ~~Bikeshed syntax~~ Went with a type-based approach, prefixed by `@` for ease of parsing and flexibility - [x] Add support for custom struct/tuple struct field attributes - [x] Add support for custom enum variant field attributes - [x] ~~Add support for custom enum variant attributes (maybe?)~~ ~~Will require a larger refactor. Can be saved for a future PR if we really want it.~~ Actually, we apparently still have support for variant attributes despite not using them, so it was pretty easy to add lol. - [x] Add support for custom container attributes - [x] Allow custom attributes to store any reflectable value (not just `Lit`) - [x] ~~Store attributes in registry~~ This PR used to store these in attributes in the registry, however, it has since switched over to storing them in type info - [x] Add example ## Bikeshedding > [!note] > This section was made for the old method of handling custom attributes, which stored them by name (i.e. `some_attribute = 123`). The PR has shifted away from that, to a more type-safe approach. > > This section has been left for reference. There are a number of ways we can syntactically handle custom attributes. Feel free to leave a comment on your preferred one! Ideally we want one that is clear, readable, and concise since these will potentially see _a lot_ of use. Below is a small, non-exhaustive list of them. Note that the `skip_serializing` reflection attribute is added to demonstrate how each case plays with existing reflection attributes. <details> <summary>List</summary> ##### 1. `@(name = value)` > The `@` was chosen to make them stand out from other attributes and because the "at" symbol is a subtle pneumonic for "attribute". Of course, other symbols could be used (e.g. `$`, `#`, etc.). ```rust #[derive(Reflect)] struct Slider { #[reflect(@(min = 0.0, max = 1.0), skip_serializing)] #[[reflect(@(bevy_editor::hint = "Range: 0.0 to 1.0"))] value: f32, } ``` ##### 2. `@name = value` > This is my personal favorite. ```rust #[derive(Reflect)] struct Slider { #[reflect(@min = 0.0, @max = 1.0, skip_serializing)] #[[reflect(@bevy_editor::hint = "Range: 0.0 to 1.0")] value: f32, } ``` ##### 3. `custom_attr(name = value)` > `custom_attr` can be anything. Other possibilities include `with` or `tag`. ```rust #[derive(Reflect)] struct Slider { #[reflect(custom_attr(min = 0.0, max = 1.0), skip_serializing)] #[[reflect(custom_attr(bevy_editor::hint = "Range: 0.0 to 1.0"))] value: f32, } ``` ##### 4. `reflect_attr(name = value)` ```rust #[derive(Reflect)] struct Slider { #[reflect(skip_serializing)] #[reflect_attr(min = 0.0, max = 1.0)] #[[reflect_attr(bevy_editor::hint = "Range: 0.0 to 1.0")] value: f32, } ``` </details> --- ## Changelog - Added support for custom attributes on reflected types (i.e. `#[reflect(@Foo::new("bar")]`)
2024-05-20 19:30:21 +00:00
/// Generates a `TokenStream` for `TypeInfo::Struct` or `TypeInfo::TupleStruct` construction.
pub fn to_info_tokens(&self, is_tuple: bool) -> proc_macro2::TokenStream {
let bevy_reflect_path = self.meta().bevy_reflect_path();
let (info_variant, info_struct) = if is_tuple {
(
Ident::new("TupleStruct", Span::call_site()),
Ident::new("TupleStructInfo", Span::call_site()),
)
} else {
(
Ident::new("Struct", Span::call_site()),
Ident::new("StructInfo", Span::call_site()),
)
};
let field_infos = self
.active_fields()
.map(|field| field.to_info_tokens(bevy_reflect_path));
let custom_attributes = self
.meta
.attrs
.custom_attributes()
.to_tokens(bevy_reflect_path);
#[allow(unused_mut)] // Needs mutability for the feature gate
let mut info = quote! {
#bevy_reflect_path::#info_struct::new::<Self>(&[
#(#field_infos),*
])
.with_custom_attributes(#custom_attributes)
};
#[cfg(feature = "documentation")]
{
let docs = self.meta().doc();
info.extend(quote! {
.with_docs(#docs)
});
}
quote! {
#bevy_reflect_path::TypeInfo::#info_variant(#info)
}
}
bevy_reflect: Reflect enums (#4761) # Objective > This is a revival of #1347. Credit for the original PR should go to @Davier. Currently, enums are treated as `ReflectRef::Value` types by `bevy_reflect`. Obviously, there needs to be better a better representation for enums using the reflection API. ## Solution Based on prior work from @Davier, an `Enum` trait has been added as well as the ability to automatically implement it via the `Reflect` derive macro. This allows enums to be expressed dynamically: ```rust #[derive(Reflect)] enum Foo { A, B(usize), C { value: f32 }, } let mut foo = Foo::B(123); assert_eq!("B", foo.variant_name()); assert_eq!(1, foo.field_len()); let new_value = DynamicEnum::from(Foo::C { value: 1.23 }); foo.apply(&new_value); assert_eq!(Foo::C{value: 1.23}, foo); ``` ### Features #### Derive Macro Use the `#[derive(Reflect)]` macro to automatically implement the `Enum` trait for enum definitions. Optionally, you can use `#[reflect(ignore)]` with both variants and variant fields, just like you can with structs. These ignored items will not be considered as part of the reflection and cannot be accessed via reflection. ```rust #[derive(Reflect)] enum TestEnum { A, // Uncomment to ignore all of `B` // #[reflect(ignore)] B(usize), C { // Uncomment to ignore only field `foo` of `C` // #[reflect(ignore)] foo: f32, bar: bool, }, } ``` #### Dynamic Enums Enums may be created/represented dynamically via the `DynamicEnum` struct. The main purpose of this struct is to allow enums to be deserialized into a partial state and to allow dynamic patching. In order to ensure conversion from a `DynamicEnum` to a concrete enum type goes smoothly, be sure to add `FromReflect` to your derive macro. ```rust let mut value = TestEnum::A; // Create from a concrete instance let dyn_enum = DynamicEnum::from(TestEnum::B(123)); value.apply(&dyn_enum); assert_eq!(TestEnum::B(123), value); // Create a purely dynamic instance let dyn_enum = DynamicEnum::new("TestEnum", "A", ()); value.apply(&dyn_enum); assert_eq!(TestEnum::A, value); ``` #### Variants An enum value is always represented as one of its variants— never the enum in its entirety. ```rust let value = TestEnum::A; assert_eq!("A", value.variant_name()); // Since we are using the `A` variant, we cannot also be the `B` variant assert_ne!("B", value.variant_name()); ``` All variant types are representable within the `Enum` trait: unit, struct, and tuple. You can get the current type like: ```rust match value.variant_type() { VariantType::Unit => println!("A unit variant!"), VariantType::Struct => println!("A struct variant!"), VariantType::Tuple => println!("A tuple variant!"), } ``` > Notice that they don't contain any values representing the fields. These are purely tags. If a variant has them, you can access the fields as well: ```rust let mut value = TestEnum::C { foo: 1.23, bar: false }; // Read/write specific fields *value.field_mut("bar").unwrap() = true; // Iterate over the entire collection of fields for field in value.iter_fields() { println!("{} = {:?}", field.name(), field.value()); } ``` #### Variant Swapping It might seem odd to group all variant types under a single trait (why allow `iter_fields` on a unit variant?), but the reason this was done ~~is to easily allow *variant swapping*.~~ As I was recently drafting up the **Design Decisions** section, I discovered that other solutions could have been made to work with variant swapping. So while there are reasons to keep the all-in-one approach, variant swapping is _not_ one of them. ```rust let mut value: Box<dyn Enum> = Box::new(TestEnum::A); value.set(Box::new(TestEnum::B(123))).unwrap(); ``` #### Serialization Enums can be serialized and deserialized via reflection without needing to implement `Serialize` or `Deserialize` themselves (which can save thousands of lines of generated code). Below are the ways an enum can be serialized. > Note, like the rest of reflection-based serialization, the order of the keys in these representations is important! ##### Unit ```json { "type": "my_crate::TestEnum", "enum": { "variant": "A" } } ``` ##### Tuple ```json { "type": "my_crate::TestEnum", "enum": { "variant": "B", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` <details> <summary>Effects on Option</summary> This ends up making `Option` look a little ugly: ```json { "type": "core::option::Option<usize>", "enum": { "variant": "Some", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` </details> ##### Struct ```json { "type": "my_crate::TestEnum", "enum": { "variant": "C", "struct": { "foo": { "type": "f32", "value": 1.23 }, "bar": { "type": "bool", "value": false } } } } ``` ## Design Decisions <details> <summary><strong>View Section</strong></summary> This section is here to provide some context for why certain decisions were made for this PR, alternatives that could have been used instead, and what could be improved upon in the future. ### Variant Representation One of the biggest decisions was to decide on how to represent variants. The current design uses a "all-in-one" design where unit, tuple, and struct variants are all simultaneously represented by the `Enum` trait. This is not the only way it could have been done, though. #### Alternatives ##### 1. Variant Traits One way of representing variants would be to define traits for each variant, implementing them whenever an enum featured at least one instance of them. This would allow us to define variants like: ```rust pub trait Enum: Reflect { fn variant(&self) -> Variant; } pub enum Variant<'a> { Unit, Tuple(&'a dyn TupleVariant), Struct(&'a dyn StructVariant), } pub trait TupleVariant { fn field_len(&self) -> usize; // ... } ``` And then do things like: ```rust fn get_tuple_len(foo: &dyn Enum) -> usize { match foo.variant() { Variant::Tuple(tuple) => tuple.field_len(), _ => panic!("not a tuple variant!") } } ``` The reason this PR does not go with this approach is because of the fact that variants are not separate types. In other words, we cannot implement traits on specific variants— these cover the *entire* enum. This means we offer an easy footgun: ```rust let foo: Option<i32> = None; let my_enum = Box::new(foo) as Box<dyn TupleVariant>; ``` Here, `my_enum` contains `foo`, which is a unit variant. However, since we need to implement `TupleVariant` for `Option` as a whole, it's possible to perform such a cast. This is obviously wrong, but could easily go unnoticed. So unfortunately, this makes it not a good candidate for representing variants. ##### 2. Variant Structs To get around the issue of traits necessarily needing to apply to both the enum and its variants, we could instead use structs that are created on a per-variant basis. This was also considered but was ultimately [[removed](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c)](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c) due to concerns about allocations. Each variant struct would probably look something like: ```rust pub trait Enum: Reflect { fn variant_mut(&self) -> VariantMut; } pub enum VariantMut<'a> { Unit, Tuple(TupleVariantMut), Struct(StructVariantMut), } struct StructVariantMut<'a> { fields: Vec<&'a mut dyn Reflect>, field_indices: HashMap<Cow<'static, str>, usize> } ``` This allows us to isolate struct variants into their own defined struct and define methods specifically for their use. It also prevents users from casting to it since it's not a trait. However, this is not an optimal solution. Both `field_indices` and `fields` will require an allocation (remember, a `Box<[T]>` still requires a `Vec<T>` in order to be constructed). This *might* be a problem if called frequently enough. ##### 3. Generated Structs The original design, implemented by @Davier, instead generates structs specific for each variant. So if we had a variant path like `Foo::Bar`, we'd generate a struct named `FooBarWrapper`. This would be newtyped around the original enum and forward tuple or struct methods to the enum with the chosen variant. Because it involved using the `Tuple` and `Struct` traits (which are also both bound on `Reflect`), this meant a bit more code had to be generated. For a single struct variant with one field, the generated code amounted to ~110LoC. However, each new field added to that variant only added ~6 more LoC. In order to work properly, the enum had to be transmuted to the generated struct: ```rust fn variant(&self) -> crate::EnumVariant<'_> { match self { Foo::Bar {value: i32} => { let wrapper_ref = unsafe { std::mem::transmute::<&Self, &FooBarWrapper>(self) }; crate::EnumVariant::Struct(wrapper_ref as &dyn crate::Struct) } } } ``` This works because `FooBarWrapper` is defined as `repr(transparent)`. Out of all the alternatives, this would probably be the one most likely to be used again in the future. The reasons for why this PR did not continue to use it was because: * To reduce generated code (which would hopefully speed up compile times) * To avoid cluttering the code with generated structs not visible to the user * To keep bevy_reflect simple and extensible (these generated structs act as proxies and might not play well with current or future systems) * To avoid additional unsafe blocks * My own misunderstanding of @Davier's code That last point is obviously on me. I misjudged the code to be too unsafe and unable to handle variant swapping (which it probably could) when I was rebasing it. Looking over it again when writing up this whole section, I see that it was actually a pretty clever way of handling variant representation. #### Benefits of All-in-One As stated before, the current implementation uses an all-in-one approach. All variants are capable of containing fields as far as `Enum` is concerned. This provides a few benefits that the alternatives do not (reduced indirection, safer code, etc.). The biggest benefit, though, is direct field access. Rather than forcing users to have to go through pattern matching, we grant direct access to the fields contained by the current variant. The reason we can do this is because all of the pattern matching happens internally. Getting the field at index `2` will automatically return `Some(...)` for the current variant if it has a field at that index or `None` if it doesn't (or can't). This could be useful for scenarios where the variant has already been verified or just set/swapped (or even where the type of variant doesn't matter): ```rust let dyn_enum: &mut dyn Enum = &mut Foo::Bar {value: 123}; // We know it's the `Bar` variant let field = dyn_enum.field("value").unwrap(); ``` Reflection is not a type-safe abstraction— almost every return value is wrapped in `Option<...>`. There are plenty of places to check and recheck that a value is what Reflect says it is. Forcing users to have to go through `match` each time they want to access a field might just be an extra step among dozens of other verification processes. Some might disagree, but ultimately, my view is that the benefit here is an improvement to the ergonomics and usability of reflected enums. </details> --- ## Changelog ### Added * Added `Enum` trait * Added `Enum` impl to `Reflect` derive macro * Added `DynamicEnum` struct * Added `DynamicVariant` * Added `EnumInfo` * Added `VariantInfo` * Added `StructVariantInfo` * Added `TupleVariantInfo` * Added `UnitVariantInfo` * Added serializtion/deserialization support for enums * Added `EnumSerializer` * Added `VariantType` * Added `VariantFieldIter` * Added `VariantField` * Added `enum_partial_eq(...)` * Added `enum_hash(...)` ### Changed * `Option<T>` now implements `Enum` * `bevy_window` now depends on `bevy_reflect` * Implemented `Reflect` and `FromReflect` for `WindowId` * Derive `FromReflect` on `PerspectiveProjection` * Derive `FromReflect` on `OrthographicProjection` * Derive `FromReflect` on `WindowOrigin` * Derive `FromReflect` on `ScalingMode` * Derive `FromReflect` on `DepthCalculation` ## Migration Guide * Enums no longer need to be treated as values and usages of `#[reflect_value(...)]` can be removed or replaced by `#[reflect(...)]` * Enums (including `Option<T>`) now take a different format when serializing. The format is described above, but this may cause issues for existing scenes that make use of enums. --- Also shout out to @nicopap for helping clean up some of the code here! It's a big feature so help like this is really appreciated! Co-authored-by: Gino Valente <gino.valente.code@gmail.com>
2022-08-02 22:14:41 +00:00
}
impl<'a> ReflectEnum<'a> {
/// Access the metadata associated with this enum definition.
pub fn meta(&self) -> &ReflectMeta<'a> {
&self.meta
}
/// Returns the given ident as a qualified unit variant of this enum.
bevy_reflect: Reflect remote types (#6042) # Objective The goal with this PR is to allow the use of types that don't implement `Reflect` within the reflection API. Rust's [orphan rule](https://doc.rust-lang.org/book/ch10-02-traits.html#implementing-a-trait-on-a-type) prevents implementing a trait on an external type when neither type nor trait are owned by the implementor. This means that if a crate, `cool_rust_lib`, defines a type, `Foo`, then a user cannot use it with reflection. What this means is that we have to ignore it most of the time: ```rust #[derive(Reflect)] struct SomeStruct { #[reflect(ignore)] data: cool_rust_lib::Foo } ``` Obviously, it's impossible to implement `Reflect` on `Foo`. But does it *have* to be? Most of reflection doesn't deal with concrete types— it's almost all using `dyn Reflect`. And being very metadata-driven, it should theoretically be possible. I mean, [`serde`](https://serde.rs/remote-derive.html) does it. ## Solution > Special thanks to @danielhenrymantilla for their help reviewing this PR and offering wisdom wrt safety. Taking a page out of `serde`'s book, this PR adds the ability to easily use "remote types" with reflection. In this context, a "remote type" is the external type for which we have no ability to implement `Reflect`. This adds the `#[reflect_remote(...)]` attribute macro, which is used to generate "remote type wrappers". All you have to do is define the wrapper exactly the same as the remote type's definition: ```rust // Pretend this is our external crate mod cool_rust_lib { #[derive(Default)] struct Foo { pub value: String } } #[reflect_remote(cool_rust_lib::Foo)] struct FooWrapper { pub value: String } ``` > **Note:** All fields in the external type *must* be public. This could be addressed with a separate getter/setter attribute either in this PR or in another one. The macro takes this user-defined item and transforms it into a newtype wrapper around the external type, marking it as `#[repr(transparent)]`. The fields/variants defined by the user are simply used to build out the reflection impls. Additionally, it generates an implementation of the new trait, `ReflectRemote`, which helps prevent accidental misuses of this API. Therefore, the output generated by the macro would look something like: ```rust #[repr(transparent)] struct FooWrapper(pub cool_rust_lib::Foo); impl ReflectRemote for FooWrapper { type Remote = cool_rust_lib::Foo; // transmutation methods... } // reflection impls... // these will acknowledge and make use of the `value` field ``` Internally, the reflection API will pass around the `FooWrapper` and [transmute](https://doc.rust-lang.org/std/mem/fn.transmute.html) it where necessary. All we have to do is then tell `Reflect` to do that. So rather than ignoring the field, we tell `Reflect` to use our wrapper using the `#[reflect(remote = ...)]` field attribute: ```rust #[derive(Reflect)] struct SomeStruct { #[reflect(remote = FooWrapper)] data: cool_rust_lib::Foo } ``` #### Other Macros & Type Data Because this macro consumes the defined item and generates a new one, we can't just put our macros anywhere. All macros that should be passed to the generated struct need to come *below* this macro. For example, to derive `Default` and register its associated type data: ```rust // ✅ GOOD #[reflect_remote(cool_rust_lib::Foo)] #[derive(Default)] #[reflect(Default)] struct FooWrapper { pub value: String } // ❌ BAD #[derive(Default)] #[reflect_remote(cool_rust_lib::Foo)] #[reflect(Default)] struct FooWrapper { pub value: String } ``` #### Generics Generics are forwarded to the generated struct as well. They should also be defined in the same order: ```rust #[reflect_remote(RemoteGeneric<'a, T1, T2>)] struct GenericWrapper<'a, T1, T2> { pub foo: &'a T1, pub bar: &'a T2, } ``` > Naming does *not* need to match the original definition's. Only order matters here. > Also note that the code above is just a demonstration and doesn't actually compile since we'd need to enforce certain bounds (e.g. `T1: Reflect`, `'a: 'static`, etc.) #### Nesting And, yes, you can nest remote types: ```rust #[reflect_remote(RemoteOuter)] struct OuterWrapper { #[reflect(remote = InnerWrapper)] pub inner: RemoteInner } #[reflect_remote(RemoteInner)] struct InnerWrapper(usize); ``` #### Assertions This macro will also generate some compile-time assertions to ensure that the correct types are used. It's important we catch this early so users don't have to wait for something to panic. And it also helps keep our `unsafe` a little safer. For example, a wrapper definition that does not match its corresponding remote type will result in an error: ```rust mod external_crate { pub struct TheirStruct(pub u32); } #[reflect_remote(external_crate::TheirStruct)] struct MyStruct(pub String); // ERROR: expected type `u32` but found `String` ``` <details> <summary>Generated Assertion</summary> ```rust const _: () = { #[allow(non_snake_case)] #[allow(unused_variables)] #[allow(unused_assignments)] #[allow(unreachable_patterns)] #[allow(clippy::multiple_bound_locations)] fn assert_wrapper_definition_matches_remote_type( mut __remote__: external_crate::TheirStruct, ) { __remote__.0 = (|| -> ::core::option::Option<String> { None })().unwrap(); } }; ``` </details> Additionally, using the incorrect type in a `#[reflect(remote = ...)]` attribute should result in an error: ```rust mod external_crate { pub struct TheirFoo(pub u32); pub struct TheirBar(pub i32); } #[reflect_remote(external_crate::TheirFoo)] struct MyFoo(pub u32); #[reflect_remote(external_crate::TheirBar)] struct MyBar(pub i32); #[derive(Reflect)] struct MyStruct { #[reflect(remote = MyBar)] // ERROR: expected type `TheirFoo` but found struct `TheirBar` foo: external_crate::TheirFoo } ``` <details> <summary>Generated Assertion</summary> ```rust const _: () = { struct RemoteFieldAssertions; impl RemoteFieldAssertions { #[allow(non_snake_case)] #[allow(clippy::multiple_bound_locations)] fn assert__foo__is_valid_remote() { let _: <MyBar as bevy_reflect::ReflectRemote>::Remote = (|| -> ::core::option::Option<external_crate::TheirFoo> { None })().unwrap(); } } }; ``` </details> ### Discussion There are a couple points that I think still need discussion or validation. - [x] 1. `Any` shenanigans ~~If we wanted to downcast our remote type from a `dyn Reflect`, we'd have to first downcast to the wrapper then extract the inner type. This PR has a [commit](b840db9f74cb6d357f951cb11b150d46bac89ee2) that addresses this by making all the `Reflect::*any` methods return the inner type rather than the wrapper type. This allows us to downcast directly to our remote type.~~ ~~However, I'm not sure if this is something we want to do. For unknowing users, it could be confusing and seemingly inconsistent. Is it worth keeping? Or should this behavior be removed?~~ I think this should be fine. The remote wrapper is an implementation detail and users should not need to downcast to the wrapper type. Feel free to let me know if there are other opinions on this though! - [x] 2. Implementing `Deref/DerefMut` and `From` ~~We don't currently do this, but should we implement other traits on the generated transparent struct? We could implement `Deref`/`DerefMut` to easily access the inner type. And we could implement `From` for easier conversion between the two types (e.g. `T: Into<Foo>`).~~ As mentioned in the comments, we probably don't need to do this. Again, the remote wrapper is an implementation detail, and should generally not be used directly. - [x] 3. ~~Should we define a getter/setter field attribute in this PR as well or leave it for a future one?~~ I think this should be saved for a future PR - [ ] 4. Any foreseeable issues with this implementation? #### Alternatives One alternative to defining our own `ReflectRemote` would be to use [bytemuck's `TransparentWrapper`](https://docs.rs/bytemuck/1.13.1/bytemuck/trait.TransparentWrapper.html) (as suggested by @danielhenrymantilla). This is definitely a viable option, as `ReflectRemote` is pretty much the same thing as `TransparentWrapper`. However, the cost would be bringing in a new crate— though, it is already in use in a few other sub-crates like bevy_render. I think we're okay just defining `ReflectRemote` ourselves, but we can go the bytemuck route if we'd prefer offloading that work to another crate. --- ## Changelog * Added the `#[reflect_remote(...)]` attribute macro to allow `Reflect` to be used on remote types * Added `ReflectRemote` trait for ensuring proper remote wrapper usage
2024-08-12 19:12:53 +00:00
///
/// This takes into account the remote type, if any.
bevy_reflect: Reflect enums (#4761) # Objective > This is a revival of #1347. Credit for the original PR should go to @Davier. Currently, enums are treated as `ReflectRef::Value` types by `bevy_reflect`. Obviously, there needs to be better a better representation for enums using the reflection API. ## Solution Based on prior work from @Davier, an `Enum` trait has been added as well as the ability to automatically implement it via the `Reflect` derive macro. This allows enums to be expressed dynamically: ```rust #[derive(Reflect)] enum Foo { A, B(usize), C { value: f32 }, } let mut foo = Foo::B(123); assert_eq!("B", foo.variant_name()); assert_eq!(1, foo.field_len()); let new_value = DynamicEnum::from(Foo::C { value: 1.23 }); foo.apply(&new_value); assert_eq!(Foo::C{value: 1.23}, foo); ``` ### Features #### Derive Macro Use the `#[derive(Reflect)]` macro to automatically implement the `Enum` trait for enum definitions. Optionally, you can use `#[reflect(ignore)]` with both variants and variant fields, just like you can with structs. These ignored items will not be considered as part of the reflection and cannot be accessed via reflection. ```rust #[derive(Reflect)] enum TestEnum { A, // Uncomment to ignore all of `B` // #[reflect(ignore)] B(usize), C { // Uncomment to ignore only field `foo` of `C` // #[reflect(ignore)] foo: f32, bar: bool, }, } ``` #### Dynamic Enums Enums may be created/represented dynamically via the `DynamicEnum` struct. The main purpose of this struct is to allow enums to be deserialized into a partial state and to allow dynamic patching. In order to ensure conversion from a `DynamicEnum` to a concrete enum type goes smoothly, be sure to add `FromReflect` to your derive macro. ```rust let mut value = TestEnum::A; // Create from a concrete instance let dyn_enum = DynamicEnum::from(TestEnum::B(123)); value.apply(&dyn_enum); assert_eq!(TestEnum::B(123), value); // Create a purely dynamic instance let dyn_enum = DynamicEnum::new("TestEnum", "A", ()); value.apply(&dyn_enum); assert_eq!(TestEnum::A, value); ``` #### Variants An enum value is always represented as one of its variants— never the enum in its entirety. ```rust let value = TestEnum::A; assert_eq!("A", value.variant_name()); // Since we are using the `A` variant, we cannot also be the `B` variant assert_ne!("B", value.variant_name()); ``` All variant types are representable within the `Enum` trait: unit, struct, and tuple. You can get the current type like: ```rust match value.variant_type() { VariantType::Unit => println!("A unit variant!"), VariantType::Struct => println!("A struct variant!"), VariantType::Tuple => println!("A tuple variant!"), } ``` > Notice that they don't contain any values representing the fields. These are purely tags. If a variant has them, you can access the fields as well: ```rust let mut value = TestEnum::C { foo: 1.23, bar: false }; // Read/write specific fields *value.field_mut("bar").unwrap() = true; // Iterate over the entire collection of fields for field in value.iter_fields() { println!("{} = {:?}", field.name(), field.value()); } ``` #### Variant Swapping It might seem odd to group all variant types under a single trait (why allow `iter_fields` on a unit variant?), but the reason this was done ~~is to easily allow *variant swapping*.~~ As I was recently drafting up the **Design Decisions** section, I discovered that other solutions could have been made to work with variant swapping. So while there are reasons to keep the all-in-one approach, variant swapping is _not_ one of them. ```rust let mut value: Box<dyn Enum> = Box::new(TestEnum::A); value.set(Box::new(TestEnum::B(123))).unwrap(); ``` #### Serialization Enums can be serialized and deserialized via reflection without needing to implement `Serialize` or `Deserialize` themselves (which can save thousands of lines of generated code). Below are the ways an enum can be serialized. > Note, like the rest of reflection-based serialization, the order of the keys in these representations is important! ##### Unit ```json { "type": "my_crate::TestEnum", "enum": { "variant": "A" } } ``` ##### Tuple ```json { "type": "my_crate::TestEnum", "enum": { "variant": "B", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` <details> <summary>Effects on Option</summary> This ends up making `Option` look a little ugly: ```json { "type": "core::option::Option<usize>", "enum": { "variant": "Some", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` </details> ##### Struct ```json { "type": "my_crate::TestEnum", "enum": { "variant": "C", "struct": { "foo": { "type": "f32", "value": 1.23 }, "bar": { "type": "bool", "value": false } } } } ``` ## Design Decisions <details> <summary><strong>View Section</strong></summary> This section is here to provide some context for why certain decisions were made for this PR, alternatives that could have been used instead, and what could be improved upon in the future. ### Variant Representation One of the biggest decisions was to decide on how to represent variants. The current design uses a "all-in-one" design where unit, tuple, and struct variants are all simultaneously represented by the `Enum` trait. This is not the only way it could have been done, though. #### Alternatives ##### 1. Variant Traits One way of representing variants would be to define traits for each variant, implementing them whenever an enum featured at least one instance of them. This would allow us to define variants like: ```rust pub trait Enum: Reflect { fn variant(&self) -> Variant; } pub enum Variant<'a> { Unit, Tuple(&'a dyn TupleVariant), Struct(&'a dyn StructVariant), } pub trait TupleVariant { fn field_len(&self) -> usize; // ... } ``` And then do things like: ```rust fn get_tuple_len(foo: &dyn Enum) -> usize { match foo.variant() { Variant::Tuple(tuple) => tuple.field_len(), _ => panic!("not a tuple variant!") } } ``` The reason this PR does not go with this approach is because of the fact that variants are not separate types. In other words, we cannot implement traits on specific variants— these cover the *entire* enum. This means we offer an easy footgun: ```rust let foo: Option<i32> = None; let my_enum = Box::new(foo) as Box<dyn TupleVariant>; ``` Here, `my_enum` contains `foo`, which is a unit variant. However, since we need to implement `TupleVariant` for `Option` as a whole, it's possible to perform such a cast. This is obviously wrong, but could easily go unnoticed. So unfortunately, this makes it not a good candidate for representing variants. ##### 2. Variant Structs To get around the issue of traits necessarily needing to apply to both the enum and its variants, we could instead use structs that are created on a per-variant basis. This was also considered but was ultimately [[removed](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c)](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c) due to concerns about allocations. Each variant struct would probably look something like: ```rust pub trait Enum: Reflect { fn variant_mut(&self) -> VariantMut; } pub enum VariantMut<'a> { Unit, Tuple(TupleVariantMut), Struct(StructVariantMut), } struct StructVariantMut<'a> { fields: Vec<&'a mut dyn Reflect>, field_indices: HashMap<Cow<'static, str>, usize> } ``` This allows us to isolate struct variants into their own defined struct and define methods specifically for their use. It also prevents users from casting to it since it's not a trait. However, this is not an optimal solution. Both `field_indices` and `fields` will require an allocation (remember, a `Box<[T]>` still requires a `Vec<T>` in order to be constructed). This *might* be a problem if called frequently enough. ##### 3. Generated Structs The original design, implemented by @Davier, instead generates structs specific for each variant. So if we had a variant path like `Foo::Bar`, we'd generate a struct named `FooBarWrapper`. This would be newtyped around the original enum and forward tuple or struct methods to the enum with the chosen variant. Because it involved using the `Tuple` and `Struct` traits (which are also both bound on `Reflect`), this meant a bit more code had to be generated. For a single struct variant with one field, the generated code amounted to ~110LoC. However, each new field added to that variant only added ~6 more LoC. In order to work properly, the enum had to be transmuted to the generated struct: ```rust fn variant(&self) -> crate::EnumVariant<'_> { match self { Foo::Bar {value: i32} => { let wrapper_ref = unsafe { std::mem::transmute::<&Self, &FooBarWrapper>(self) }; crate::EnumVariant::Struct(wrapper_ref as &dyn crate::Struct) } } } ``` This works because `FooBarWrapper` is defined as `repr(transparent)`. Out of all the alternatives, this would probably be the one most likely to be used again in the future. The reasons for why this PR did not continue to use it was because: * To reduce generated code (which would hopefully speed up compile times) * To avoid cluttering the code with generated structs not visible to the user * To keep bevy_reflect simple and extensible (these generated structs act as proxies and might not play well with current or future systems) * To avoid additional unsafe blocks * My own misunderstanding of @Davier's code That last point is obviously on me. I misjudged the code to be too unsafe and unable to handle variant swapping (which it probably could) when I was rebasing it. Looking over it again when writing up this whole section, I see that it was actually a pretty clever way of handling variant representation. #### Benefits of All-in-One As stated before, the current implementation uses an all-in-one approach. All variants are capable of containing fields as far as `Enum` is concerned. This provides a few benefits that the alternatives do not (reduced indirection, safer code, etc.). The biggest benefit, though, is direct field access. Rather than forcing users to have to go through pattern matching, we grant direct access to the fields contained by the current variant. The reason we can do this is because all of the pattern matching happens internally. Getting the field at index `2` will automatically return `Some(...)` for the current variant if it has a field at that index or `None` if it doesn't (or can't). This could be useful for scenarios where the variant has already been verified or just set/swapped (or even where the type of variant doesn't matter): ```rust let dyn_enum: &mut dyn Enum = &mut Foo::Bar {value: 123}; // We know it's the `Bar` variant let field = dyn_enum.field("value").unwrap(); ``` Reflection is not a type-safe abstraction— almost every return value is wrapped in `Option<...>`. There are plenty of places to check and recheck that a value is what Reflect says it is. Forcing users to have to go through `match` each time they want to access a field might just be an extra step among dozens of other verification processes. Some might disagree, but ultimately, my view is that the benefit here is an improvement to the ergonomics and usability of reflected enums. </details> --- ## Changelog ### Added * Added `Enum` trait * Added `Enum` impl to `Reflect` derive macro * Added `DynamicEnum` struct * Added `DynamicVariant` * Added `EnumInfo` * Added `VariantInfo` * Added `StructVariantInfo` * Added `TupleVariantInfo` * Added `UnitVariantInfo` * Added serializtion/deserialization support for enums * Added `EnumSerializer` * Added `VariantType` * Added `VariantFieldIter` * Added `VariantField` * Added `enum_partial_eq(...)` * Added `enum_hash(...)` ### Changed * `Option<T>` now implements `Enum` * `bevy_window` now depends on `bevy_reflect` * Implemented `Reflect` and `FromReflect` for `WindowId` * Derive `FromReflect` on `PerspectiveProjection` * Derive `FromReflect` on `OrthographicProjection` * Derive `FromReflect` on `WindowOrigin` * Derive `FromReflect` on `ScalingMode` * Derive `FromReflect` on `DepthCalculation` ## Migration Guide * Enums no longer need to be treated as values and usages of `#[reflect_value(...)]` can be removed or replaced by `#[reflect(...)]` * Enums (including `Option<T>`) now take a different format when serializing. The format is described above, but this may cause issues for existing scenes that make use of enums. --- Also shout out to @nicopap for helping clean up some of the code here! It's a big feature so help like this is really appreciated! Co-authored-by: Gino Valente <gino.valente.code@gmail.com>
2022-08-02 22:14:41 +00:00
pub fn get_unit(&self, variant: &Ident) -> proc_macro2::TokenStream {
bevy_reflect: Reflect remote types (#6042) # Objective The goal with this PR is to allow the use of types that don't implement `Reflect` within the reflection API. Rust's [orphan rule](https://doc.rust-lang.org/book/ch10-02-traits.html#implementing-a-trait-on-a-type) prevents implementing a trait on an external type when neither type nor trait are owned by the implementor. This means that if a crate, `cool_rust_lib`, defines a type, `Foo`, then a user cannot use it with reflection. What this means is that we have to ignore it most of the time: ```rust #[derive(Reflect)] struct SomeStruct { #[reflect(ignore)] data: cool_rust_lib::Foo } ``` Obviously, it's impossible to implement `Reflect` on `Foo`. But does it *have* to be? Most of reflection doesn't deal with concrete types— it's almost all using `dyn Reflect`. And being very metadata-driven, it should theoretically be possible. I mean, [`serde`](https://serde.rs/remote-derive.html) does it. ## Solution > Special thanks to @danielhenrymantilla for their help reviewing this PR and offering wisdom wrt safety. Taking a page out of `serde`'s book, this PR adds the ability to easily use "remote types" with reflection. In this context, a "remote type" is the external type for which we have no ability to implement `Reflect`. This adds the `#[reflect_remote(...)]` attribute macro, which is used to generate "remote type wrappers". All you have to do is define the wrapper exactly the same as the remote type's definition: ```rust // Pretend this is our external crate mod cool_rust_lib { #[derive(Default)] struct Foo { pub value: String } } #[reflect_remote(cool_rust_lib::Foo)] struct FooWrapper { pub value: String } ``` > **Note:** All fields in the external type *must* be public. This could be addressed with a separate getter/setter attribute either in this PR or in another one. The macro takes this user-defined item and transforms it into a newtype wrapper around the external type, marking it as `#[repr(transparent)]`. The fields/variants defined by the user are simply used to build out the reflection impls. Additionally, it generates an implementation of the new trait, `ReflectRemote`, which helps prevent accidental misuses of this API. Therefore, the output generated by the macro would look something like: ```rust #[repr(transparent)] struct FooWrapper(pub cool_rust_lib::Foo); impl ReflectRemote for FooWrapper { type Remote = cool_rust_lib::Foo; // transmutation methods... } // reflection impls... // these will acknowledge and make use of the `value` field ``` Internally, the reflection API will pass around the `FooWrapper` and [transmute](https://doc.rust-lang.org/std/mem/fn.transmute.html) it where necessary. All we have to do is then tell `Reflect` to do that. So rather than ignoring the field, we tell `Reflect` to use our wrapper using the `#[reflect(remote = ...)]` field attribute: ```rust #[derive(Reflect)] struct SomeStruct { #[reflect(remote = FooWrapper)] data: cool_rust_lib::Foo } ``` #### Other Macros & Type Data Because this macro consumes the defined item and generates a new one, we can't just put our macros anywhere. All macros that should be passed to the generated struct need to come *below* this macro. For example, to derive `Default` and register its associated type data: ```rust // ✅ GOOD #[reflect_remote(cool_rust_lib::Foo)] #[derive(Default)] #[reflect(Default)] struct FooWrapper { pub value: String } // ❌ BAD #[derive(Default)] #[reflect_remote(cool_rust_lib::Foo)] #[reflect(Default)] struct FooWrapper { pub value: String } ``` #### Generics Generics are forwarded to the generated struct as well. They should also be defined in the same order: ```rust #[reflect_remote(RemoteGeneric<'a, T1, T2>)] struct GenericWrapper<'a, T1, T2> { pub foo: &'a T1, pub bar: &'a T2, } ``` > Naming does *not* need to match the original definition's. Only order matters here. > Also note that the code above is just a demonstration and doesn't actually compile since we'd need to enforce certain bounds (e.g. `T1: Reflect`, `'a: 'static`, etc.) #### Nesting And, yes, you can nest remote types: ```rust #[reflect_remote(RemoteOuter)] struct OuterWrapper { #[reflect(remote = InnerWrapper)] pub inner: RemoteInner } #[reflect_remote(RemoteInner)] struct InnerWrapper(usize); ``` #### Assertions This macro will also generate some compile-time assertions to ensure that the correct types are used. It's important we catch this early so users don't have to wait for something to panic. And it also helps keep our `unsafe` a little safer. For example, a wrapper definition that does not match its corresponding remote type will result in an error: ```rust mod external_crate { pub struct TheirStruct(pub u32); } #[reflect_remote(external_crate::TheirStruct)] struct MyStruct(pub String); // ERROR: expected type `u32` but found `String` ``` <details> <summary>Generated Assertion</summary> ```rust const _: () = { #[allow(non_snake_case)] #[allow(unused_variables)] #[allow(unused_assignments)] #[allow(unreachable_patterns)] #[allow(clippy::multiple_bound_locations)] fn assert_wrapper_definition_matches_remote_type( mut __remote__: external_crate::TheirStruct, ) { __remote__.0 = (|| -> ::core::option::Option<String> { None })().unwrap(); } }; ``` </details> Additionally, using the incorrect type in a `#[reflect(remote = ...)]` attribute should result in an error: ```rust mod external_crate { pub struct TheirFoo(pub u32); pub struct TheirBar(pub i32); } #[reflect_remote(external_crate::TheirFoo)] struct MyFoo(pub u32); #[reflect_remote(external_crate::TheirBar)] struct MyBar(pub i32); #[derive(Reflect)] struct MyStruct { #[reflect(remote = MyBar)] // ERROR: expected type `TheirFoo` but found struct `TheirBar` foo: external_crate::TheirFoo } ``` <details> <summary>Generated Assertion</summary> ```rust const _: () = { struct RemoteFieldAssertions; impl RemoteFieldAssertions { #[allow(non_snake_case)] #[allow(clippy::multiple_bound_locations)] fn assert__foo__is_valid_remote() { let _: <MyBar as bevy_reflect::ReflectRemote>::Remote = (|| -> ::core::option::Option<external_crate::TheirFoo> { None })().unwrap(); } } }; ``` </details> ### Discussion There are a couple points that I think still need discussion or validation. - [x] 1. `Any` shenanigans ~~If we wanted to downcast our remote type from a `dyn Reflect`, we'd have to first downcast to the wrapper then extract the inner type. This PR has a [commit](b840db9f74cb6d357f951cb11b150d46bac89ee2) that addresses this by making all the `Reflect::*any` methods return the inner type rather than the wrapper type. This allows us to downcast directly to our remote type.~~ ~~However, I'm not sure if this is something we want to do. For unknowing users, it could be confusing and seemingly inconsistent. Is it worth keeping? Or should this behavior be removed?~~ I think this should be fine. The remote wrapper is an implementation detail and users should not need to downcast to the wrapper type. Feel free to let me know if there are other opinions on this though! - [x] 2. Implementing `Deref/DerefMut` and `From` ~~We don't currently do this, but should we implement other traits on the generated transparent struct? We could implement `Deref`/`DerefMut` to easily access the inner type. And we could implement `From` for easier conversion between the two types (e.g. `T: Into<Foo>`).~~ As mentioned in the comments, we probably don't need to do this. Again, the remote wrapper is an implementation detail, and should generally not be used directly. - [x] 3. ~~Should we define a getter/setter field attribute in this PR as well or leave it for a future one?~~ I think this should be saved for a future PR - [ ] 4. Any foreseeable issues with this implementation? #### Alternatives One alternative to defining our own `ReflectRemote` would be to use [bytemuck's `TransparentWrapper`](https://docs.rs/bytemuck/1.13.1/bytemuck/trait.TransparentWrapper.html) (as suggested by @danielhenrymantilla). This is definitely a viable option, as `ReflectRemote` is pretty much the same thing as `TransparentWrapper`. However, the cost would be bringing in a new crate— though, it is already in use in a few other sub-crates like bevy_render. I think we're okay just defining `ReflectRemote` ourselves, but we can go the bytemuck route if we'd prefer offloading that work to another crate. --- ## Changelog * Added the `#[reflect_remote(...)]` attribute macro to allow `Reflect` to be used on remote types * Added `ReflectRemote` trait for ensuring proper remote wrapper usage
2024-08-12 19:12:53 +00:00
let name = self
.meta
.remote_ty
.map(|path| match path.as_expr_path() {
Ok(path) => path.to_token_stream(),
Err(err) => err.into_compile_error(),
})
.unwrap_or_else(|| self.meta.type_path().to_token_stream());
bevy_reflect: Reflect enums (#4761) # Objective > This is a revival of #1347. Credit for the original PR should go to @Davier. Currently, enums are treated as `ReflectRef::Value` types by `bevy_reflect`. Obviously, there needs to be better a better representation for enums using the reflection API. ## Solution Based on prior work from @Davier, an `Enum` trait has been added as well as the ability to automatically implement it via the `Reflect` derive macro. This allows enums to be expressed dynamically: ```rust #[derive(Reflect)] enum Foo { A, B(usize), C { value: f32 }, } let mut foo = Foo::B(123); assert_eq!("B", foo.variant_name()); assert_eq!(1, foo.field_len()); let new_value = DynamicEnum::from(Foo::C { value: 1.23 }); foo.apply(&new_value); assert_eq!(Foo::C{value: 1.23}, foo); ``` ### Features #### Derive Macro Use the `#[derive(Reflect)]` macro to automatically implement the `Enum` trait for enum definitions. Optionally, you can use `#[reflect(ignore)]` with both variants and variant fields, just like you can with structs. These ignored items will not be considered as part of the reflection and cannot be accessed via reflection. ```rust #[derive(Reflect)] enum TestEnum { A, // Uncomment to ignore all of `B` // #[reflect(ignore)] B(usize), C { // Uncomment to ignore only field `foo` of `C` // #[reflect(ignore)] foo: f32, bar: bool, }, } ``` #### Dynamic Enums Enums may be created/represented dynamically via the `DynamicEnum` struct. The main purpose of this struct is to allow enums to be deserialized into a partial state and to allow dynamic patching. In order to ensure conversion from a `DynamicEnum` to a concrete enum type goes smoothly, be sure to add `FromReflect` to your derive macro. ```rust let mut value = TestEnum::A; // Create from a concrete instance let dyn_enum = DynamicEnum::from(TestEnum::B(123)); value.apply(&dyn_enum); assert_eq!(TestEnum::B(123), value); // Create a purely dynamic instance let dyn_enum = DynamicEnum::new("TestEnum", "A", ()); value.apply(&dyn_enum); assert_eq!(TestEnum::A, value); ``` #### Variants An enum value is always represented as one of its variants— never the enum in its entirety. ```rust let value = TestEnum::A; assert_eq!("A", value.variant_name()); // Since we are using the `A` variant, we cannot also be the `B` variant assert_ne!("B", value.variant_name()); ``` All variant types are representable within the `Enum` trait: unit, struct, and tuple. You can get the current type like: ```rust match value.variant_type() { VariantType::Unit => println!("A unit variant!"), VariantType::Struct => println!("A struct variant!"), VariantType::Tuple => println!("A tuple variant!"), } ``` > Notice that they don't contain any values representing the fields. These are purely tags. If a variant has them, you can access the fields as well: ```rust let mut value = TestEnum::C { foo: 1.23, bar: false }; // Read/write specific fields *value.field_mut("bar").unwrap() = true; // Iterate over the entire collection of fields for field in value.iter_fields() { println!("{} = {:?}", field.name(), field.value()); } ``` #### Variant Swapping It might seem odd to group all variant types under a single trait (why allow `iter_fields` on a unit variant?), but the reason this was done ~~is to easily allow *variant swapping*.~~ As I was recently drafting up the **Design Decisions** section, I discovered that other solutions could have been made to work with variant swapping. So while there are reasons to keep the all-in-one approach, variant swapping is _not_ one of them. ```rust let mut value: Box<dyn Enum> = Box::new(TestEnum::A); value.set(Box::new(TestEnum::B(123))).unwrap(); ``` #### Serialization Enums can be serialized and deserialized via reflection without needing to implement `Serialize` or `Deserialize` themselves (which can save thousands of lines of generated code). Below are the ways an enum can be serialized. > Note, like the rest of reflection-based serialization, the order of the keys in these representations is important! ##### Unit ```json { "type": "my_crate::TestEnum", "enum": { "variant": "A" } } ``` ##### Tuple ```json { "type": "my_crate::TestEnum", "enum": { "variant": "B", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` <details> <summary>Effects on Option</summary> This ends up making `Option` look a little ugly: ```json { "type": "core::option::Option<usize>", "enum": { "variant": "Some", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` </details> ##### Struct ```json { "type": "my_crate::TestEnum", "enum": { "variant": "C", "struct": { "foo": { "type": "f32", "value": 1.23 }, "bar": { "type": "bool", "value": false } } } } ``` ## Design Decisions <details> <summary><strong>View Section</strong></summary> This section is here to provide some context for why certain decisions were made for this PR, alternatives that could have been used instead, and what could be improved upon in the future. ### Variant Representation One of the biggest decisions was to decide on how to represent variants. The current design uses a "all-in-one" design where unit, tuple, and struct variants are all simultaneously represented by the `Enum` trait. This is not the only way it could have been done, though. #### Alternatives ##### 1. Variant Traits One way of representing variants would be to define traits for each variant, implementing them whenever an enum featured at least one instance of them. This would allow us to define variants like: ```rust pub trait Enum: Reflect { fn variant(&self) -> Variant; } pub enum Variant<'a> { Unit, Tuple(&'a dyn TupleVariant), Struct(&'a dyn StructVariant), } pub trait TupleVariant { fn field_len(&self) -> usize; // ... } ``` And then do things like: ```rust fn get_tuple_len(foo: &dyn Enum) -> usize { match foo.variant() { Variant::Tuple(tuple) => tuple.field_len(), _ => panic!("not a tuple variant!") } } ``` The reason this PR does not go with this approach is because of the fact that variants are not separate types. In other words, we cannot implement traits on specific variants— these cover the *entire* enum. This means we offer an easy footgun: ```rust let foo: Option<i32> = None; let my_enum = Box::new(foo) as Box<dyn TupleVariant>; ``` Here, `my_enum` contains `foo`, which is a unit variant. However, since we need to implement `TupleVariant` for `Option` as a whole, it's possible to perform such a cast. This is obviously wrong, but could easily go unnoticed. So unfortunately, this makes it not a good candidate for representing variants. ##### 2. Variant Structs To get around the issue of traits necessarily needing to apply to both the enum and its variants, we could instead use structs that are created on a per-variant basis. This was also considered but was ultimately [[removed](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c)](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c) due to concerns about allocations. Each variant struct would probably look something like: ```rust pub trait Enum: Reflect { fn variant_mut(&self) -> VariantMut; } pub enum VariantMut<'a> { Unit, Tuple(TupleVariantMut), Struct(StructVariantMut), } struct StructVariantMut<'a> { fields: Vec<&'a mut dyn Reflect>, field_indices: HashMap<Cow<'static, str>, usize> } ``` This allows us to isolate struct variants into their own defined struct and define methods specifically for their use. It also prevents users from casting to it since it's not a trait. However, this is not an optimal solution. Both `field_indices` and `fields` will require an allocation (remember, a `Box<[T]>` still requires a `Vec<T>` in order to be constructed). This *might* be a problem if called frequently enough. ##### 3. Generated Structs The original design, implemented by @Davier, instead generates structs specific for each variant. So if we had a variant path like `Foo::Bar`, we'd generate a struct named `FooBarWrapper`. This would be newtyped around the original enum and forward tuple or struct methods to the enum with the chosen variant. Because it involved using the `Tuple` and `Struct` traits (which are also both bound on `Reflect`), this meant a bit more code had to be generated. For a single struct variant with one field, the generated code amounted to ~110LoC. However, each new field added to that variant only added ~6 more LoC. In order to work properly, the enum had to be transmuted to the generated struct: ```rust fn variant(&self) -> crate::EnumVariant<'_> { match self { Foo::Bar {value: i32} => { let wrapper_ref = unsafe { std::mem::transmute::<&Self, &FooBarWrapper>(self) }; crate::EnumVariant::Struct(wrapper_ref as &dyn crate::Struct) } } } ``` This works because `FooBarWrapper` is defined as `repr(transparent)`. Out of all the alternatives, this would probably be the one most likely to be used again in the future. The reasons for why this PR did not continue to use it was because: * To reduce generated code (which would hopefully speed up compile times) * To avoid cluttering the code with generated structs not visible to the user * To keep bevy_reflect simple and extensible (these generated structs act as proxies and might not play well with current or future systems) * To avoid additional unsafe blocks * My own misunderstanding of @Davier's code That last point is obviously on me. I misjudged the code to be too unsafe and unable to handle variant swapping (which it probably could) when I was rebasing it. Looking over it again when writing up this whole section, I see that it was actually a pretty clever way of handling variant representation. #### Benefits of All-in-One As stated before, the current implementation uses an all-in-one approach. All variants are capable of containing fields as far as `Enum` is concerned. This provides a few benefits that the alternatives do not (reduced indirection, safer code, etc.). The biggest benefit, though, is direct field access. Rather than forcing users to have to go through pattern matching, we grant direct access to the fields contained by the current variant. The reason we can do this is because all of the pattern matching happens internally. Getting the field at index `2` will automatically return `Some(...)` for the current variant if it has a field at that index or `None` if it doesn't (or can't). This could be useful for scenarios where the variant has already been verified or just set/swapped (or even where the type of variant doesn't matter): ```rust let dyn_enum: &mut dyn Enum = &mut Foo::Bar {value: 123}; // We know it's the `Bar` variant let field = dyn_enum.field("value").unwrap(); ``` Reflection is not a type-safe abstraction— almost every return value is wrapped in `Option<...>`. There are plenty of places to check and recheck that a value is what Reflect says it is. Forcing users to have to go through `match` each time they want to access a field might just be an extra step among dozens of other verification processes. Some might disagree, but ultimately, my view is that the benefit here is an improvement to the ergonomics and usability of reflected enums. </details> --- ## Changelog ### Added * Added `Enum` trait * Added `Enum` impl to `Reflect` derive macro * Added `DynamicEnum` struct * Added `DynamicVariant` * Added `EnumInfo` * Added `VariantInfo` * Added `StructVariantInfo` * Added `TupleVariantInfo` * Added `UnitVariantInfo` * Added serializtion/deserialization support for enums * Added `EnumSerializer` * Added `VariantType` * Added `VariantFieldIter` * Added `VariantField` * Added `enum_partial_eq(...)` * Added `enum_hash(...)` ### Changed * `Option<T>` now implements `Enum` * `bevy_window` now depends on `bevy_reflect` * Implemented `Reflect` and `FromReflect` for `WindowId` * Derive `FromReflect` on `PerspectiveProjection` * Derive `FromReflect` on `OrthographicProjection` * Derive `FromReflect` on `WindowOrigin` * Derive `FromReflect` on `ScalingMode` * Derive `FromReflect` on `DepthCalculation` ## Migration Guide * Enums no longer need to be treated as values and usages of `#[reflect_value(...)]` can be removed or replaced by `#[reflect(...)]` * Enums (including `Option<T>`) now take a different format when serializing. The format is described above, but this may cause issues for existing scenes that make use of enums. --- Also shout out to @nicopap for helping clean up some of the code here! It's a big feature so help like this is really appreciated! Co-authored-by: Gino Valente <gino.valente.code@gmail.com>
2022-08-02 22:14:41 +00:00
quote! {
#name::#variant
}
}
/// The complete set of variants in this enum.
pub fn variants(&self) -> &[EnumVariant<'a>] {
&self.variants
}
Enable deriving Reflect on structs with generic types (#7364) # Objective I recently had an issue, where I have a struct: ``` struct Property { inner: T } ``` that I use as a wrapper for internal purposes. I don't want to update my struct definition to ``` struct Property<T: Reflect>{ inner: T } ``` because I still want to be able to build `Property<T>` for types `T` that are not `Reflect`. (and also because I don't want to update my whole code base with `<T: Reflect>` bounds) I still wanted to have reflection on it (for `bevy_inspector_egui`), but adding `derive(Reflect)` fails with the error: `T cannot be sent between threads safely. T needs to implement Sync.` I believe that `bevy_reflect` should adopt the model of other derives in the case of generics, which is to add the `Reflect` implementation only if the generics also implement `Reflect`. (That is the behaviour of other macros such as `derive(Clone)` or `derive(Debug)`. It's also the current behavior of `derive(FromReflect)`. Basically doing something like: ``` impl<T> Reflect for Foo<T> where T: Reflect ``` ## Solution - I updated the derive macros for `Structs` and `TupleStructs` to add extra `where` bounds. - Every type that is reflected will need a `T: Reflect` bound - Ignored types will need a `T: 'static + Send + Sync` bound. Here's the reason. For cases like this: ``` #[derive(Reflect)] struct Foo<T, U>{ a: T #[reflect(ignore)] b: U } ``` I had to add the bound `'static + Send + Sync` to ignored generics like `U`. The reason is that we want `Foo<T, U>` to be `Reflect: 'static + Send + Sync`, so `Foo<T, U>` must be able to implement those auto-traits. `Foo<T, U>` will only implement those auto-traits if every generic type implements them, including ignored types. This means that the previously compile-fail case now compiles: ``` #[derive(Reflect)] struct Foo<'a> { #[reflect(ignore)] value: &'a str, } ``` But `Foo<'a>` will only be useable in the cases where `'a: 'static` and panic if we don't have `'a: 'static`, which is what we want (nice bonus from this PR ;) ) --- ## Changelog > This section is optional. If this was a trivial fix, or has no externally-visible impact, you can delete this section. ### Added Possibility to add `derive(Reflect)` to structs and enums that contain generic types, like so: ``` #[derive(Reflect)] struct Foo<T>{ a: T } ``` Reflection will only be available if the generic type T also implements `Reflect`. (previously, this would just return a compiler error)
2023-01-28 00:12:06 +00:00
bevy_reflect: Split `#[reflect(where)]` (#11597) # Objective Revert the changes to type parameter bounds introduced in #9046, improves the `#[reflect(where)]` attribute (also from #9046), and adds the ability to opt out of field bounds. This is based on suggestions by @soqb and discussion on [Discord](https://discord.com/channels/691052431525675048/1002362493634629796/1201227833826103427). ## Solution Reverts the changes to type parameter bounds when deriving `Reflect`, introduced in #9046. This was originally done as a means of fixing a recursion issue (#8965). However, as @soqb pointed out, we could achieve the same result by simply making an opt-out attribute instead of messing with the type parameter bounds. This PR has four main changes: 1. Reverts the type parameter bounds from #9046 2. Includes `TypePath` as a default bound for active fields 3. Changes `#reflect(where)]` to be strictly additive 4. Adds `#reflect(no_field_bounds)]` to opt out of field bounds Change 1 means that, like before, type parameters only receive at most the `TypePath` bound (if `#[reflect(type_path = false)]` is not present) and active fields receive the `Reflect` or `FromReflect` bound. And with Change 2, they will also receive `TypePath` (since it's indirectly required by `Typed` to construct `NamedField` and `UnnamedField` instances). Change 3 was made to make room for Change 4. By splitting out the responsibility of `#reflect(where)]`, we can use it with or without `#reflect(no_field_bounds)]` for various use cases. For example, if we hadn't done this, the following would have failed: ```rust // Since we're not using `#reflect(no_field_bounds)]`, // `T::Assoc` is automatically given the required bounds // of `FromReflect + TypePath` #[derive(Reflect)] #[reflect(where T::Assoc: OtherTrait)] struct Foo<T: MyTrait> { value: T::Assoc, } ``` This provides more flexibility to the user while still letting them add or remove most trait bounds. And to solve the original recursion issue, we can do: ```rust #[derive(Reflect)] #[reflect(no_field_bounds)] // <-- Added struct Foo { foo: Vec<Foo> } ``` #### Bounds All in all, we now have four sets of trait bounds: - `Self` gets the bounds `Any + Send + Sync` - Type parameters get the bound `TypePath`. This can be opted out of with `#[reflect(type_path = false)]` - Active fields get the bounds `TypePath` and `FromReflect`/`Reflect` bounds. This can be opted out of with `#reflect(no_field_bounds)]` - Custom bounds can be added with `#[reflect(where)]` --- ## Changelog - Revert some changes #9046 - `#reflect(where)]` is now strictly additive - Added `#reflect(no_field_bounds)]` attribute to opt out of automatic field trait bounds when deriving `Reflect` - Made the `TypePath` requirement on fields when deriving `Reflect` more explicit ## Migration Guide > [!important] > This PR shouldn't be a breaking change relative to the current version of Bevy (v0.12). And since it removes the breaking parts of #9046, that PR also won't need a migration guide.
2024-01-29 17:54:17 +00:00
/// Get a collection of types which are exposed to the reflection API
pub fn active_types(&self) -> Vec<Type> {
self.active_fields()
bevy_reflect: Reflect remote types (#6042) # Objective The goal with this PR is to allow the use of types that don't implement `Reflect` within the reflection API. Rust's [orphan rule](https://doc.rust-lang.org/book/ch10-02-traits.html#implementing-a-trait-on-a-type) prevents implementing a trait on an external type when neither type nor trait are owned by the implementor. This means that if a crate, `cool_rust_lib`, defines a type, `Foo`, then a user cannot use it with reflection. What this means is that we have to ignore it most of the time: ```rust #[derive(Reflect)] struct SomeStruct { #[reflect(ignore)] data: cool_rust_lib::Foo } ``` Obviously, it's impossible to implement `Reflect` on `Foo`. But does it *have* to be? Most of reflection doesn't deal with concrete types— it's almost all using `dyn Reflect`. And being very metadata-driven, it should theoretically be possible. I mean, [`serde`](https://serde.rs/remote-derive.html) does it. ## Solution > Special thanks to @danielhenrymantilla for their help reviewing this PR and offering wisdom wrt safety. Taking a page out of `serde`'s book, this PR adds the ability to easily use "remote types" with reflection. In this context, a "remote type" is the external type for which we have no ability to implement `Reflect`. This adds the `#[reflect_remote(...)]` attribute macro, which is used to generate "remote type wrappers". All you have to do is define the wrapper exactly the same as the remote type's definition: ```rust // Pretend this is our external crate mod cool_rust_lib { #[derive(Default)] struct Foo { pub value: String } } #[reflect_remote(cool_rust_lib::Foo)] struct FooWrapper { pub value: String } ``` > **Note:** All fields in the external type *must* be public. This could be addressed with a separate getter/setter attribute either in this PR or in another one. The macro takes this user-defined item and transforms it into a newtype wrapper around the external type, marking it as `#[repr(transparent)]`. The fields/variants defined by the user are simply used to build out the reflection impls. Additionally, it generates an implementation of the new trait, `ReflectRemote`, which helps prevent accidental misuses of this API. Therefore, the output generated by the macro would look something like: ```rust #[repr(transparent)] struct FooWrapper(pub cool_rust_lib::Foo); impl ReflectRemote for FooWrapper { type Remote = cool_rust_lib::Foo; // transmutation methods... } // reflection impls... // these will acknowledge and make use of the `value` field ``` Internally, the reflection API will pass around the `FooWrapper` and [transmute](https://doc.rust-lang.org/std/mem/fn.transmute.html) it where necessary. All we have to do is then tell `Reflect` to do that. So rather than ignoring the field, we tell `Reflect` to use our wrapper using the `#[reflect(remote = ...)]` field attribute: ```rust #[derive(Reflect)] struct SomeStruct { #[reflect(remote = FooWrapper)] data: cool_rust_lib::Foo } ``` #### Other Macros & Type Data Because this macro consumes the defined item and generates a new one, we can't just put our macros anywhere. All macros that should be passed to the generated struct need to come *below* this macro. For example, to derive `Default` and register its associated type data: ```rust // ✅ GOOD #[reflect_remote(cool_rust_lib::Foo)] #[derive(Default)] #[reflect(Default)] struct FooWrapper { pub value: String } // ❌ BAD #[derive(Default)] #[reflect_remote(cool_rust_lib::Foo)] #[reflect(Default)] struct FooWrapper { pub value: String } ``` #### Generics Generics are forwarded to the generated struct as well. They should also be defined in the same order: ```rust #[reflect_remote(RemoteGeneric<'a, T1, T2>)] struct GenericWrapper<'a, T1, T2> { pub foo: &'a T1, pub bar: &'a T2, } ``` > Naming does *not* need to match the original definition's. Only order matters here. > Also note that the code above is just a demonstration and doesn't actually compile since we'd need to enforce certain bounds (e.g. `T1: Reflect`, `'a: 'static`, etc.) #### Nesting And, yes, you can nest remote types: ```rust #[reflect_remote(RemoteOuter)] struct OuterWrapper { #[reflect(remote = InnerWrapper)] pub inner: RemoteInner } #[reflect_remote(RemoteInner)] struct InnerWrapper(usize); ``` #### Assertions This macro will also generate some compile-time assertions to ensure that the correct types are used. It's important we catch this early so users don't have to wait for something to panic. And it also helps keep our `unsafe` a little safer. For example, a wrapper definition that does not match its corresponding remote type will result in an error: ```rust mod external_crate { pub struct TheirStruct(pub u32); } #[reflect_remote(external_crate::TheirStruct)] struct MyStruct(pub String); // ERROR: expected type `u32` but found `String` ``` <details> <summary>Generated Assertion</summary> ```rust const _: () = { #[allow(non_snake_case)] #[allow(unused_variables)] #[allow(unused_assignments)] #[allow(unreachable_patterns)] #[allow(clippy::multiple_bound_locations)] fn assert_wrapper_definition_matches_remote_type( mut __remote__: external_crate::TheirStruct, ) { __remote__.0 = (|| -> ::core::option::Option<String> { None })().unwrap(); } }; ``` </details> Additionally, using the incorrect type in a `#[reflect(remote = ...)]` attribute should result in an error: ```rust mod external_crate { pub struct TheirFoo(pub u32); pub struct TheirBar(pub i32); } #[reflect_remote(external_crate::TheirFoo)] struct MyFoo(pub u32); #[reflect_remote(external_crate::TheirBar)] struct MyBar(pub i32); #[derive(Reflect)] struct MyStruct { #[reflect(remote = MyBar)] // ERROR: expected type `TheirFoo` but found struct `TheirBar` foo: external_crate::TheirFoo } ``` <details> <summary>Generated Assertion</summary> ```rust const _: () = { struct RemoteFieldAssertions; impl RemoteFieldAssertions { #[allow(non_snake_case)] #[allow(clippy::multiple_bound_locations)] fn assert__foo__is_valid_remote() { let _: <MyBar as bevy_reflect::ReflectRemote>::Remote = (|| -> ::core::option::Option<external_crate::TheirFoo> { None })().unwrap(); } } }; ``` </details> ### Discussion There are a couple points that I think still need discussion or validation. - [x] 1. `Any` shenanigans ~~If we wanted to downcast our remote type from a `dyn Reflect`, we'd have to first downcast to the wrapper then extract the inner type. This PR has a [commit](b840db9f74cb6d357f951cb11b150d46bac89ee2) that addresses this by making all the `Reflect::*any` methods return the inner type rather than the wrapper type. This allows us to downcast directly to our remote type.~~ ~~However, I'm not sure if this is something we want to do. For unknowing users, it could be confusing and seemingly inconsistent. Is it worth keeping? Or should this behavior be removed?~~ I think this should be fine. The remote wrapper is an implementation detail and users should not need to downcast to the wrapper type. Feel free to let me know if there are other opinions on this though! - [x] 2. Implementing `Deref/DerefMut` and `From` ~~We don't currently do this, but should we implement other traits on the generated transparent struct? We could implement `Deref`/`DerefMut` to easily access the inner type. And we could implement `From` for easier conversion between the two types (e.g. `T: Into<Foo>`).~~ As mentioned in the comments, we probably don't need to do this. Again, the remote wrapper is an implementation detail, and should generally not be used directly. - [x] 3. ~~Should we define a getter/setter field attribute in this PR as well or leave it for a future one?~~ I think this should be saved for a future PR - [ ] 4. Any foreseeable issues with this implementation? #### Alternatives One alternative to defining our own `ReflectRemote` would be to use [bytemuck's `TransparentWrapper`](https://docs.rs/bytemuck/1.13.1/bytemuck/trait.TransparentWrapper.html) (as suggested by @danielhenrymantilla). This is definitely a viable option, as `ReflectRemote` is pretty much the same thing as `TransparentWrapper`. However, the cost would be bringing in a new crate— though, it is already in use in a few other sub-crates like bevy_render. I think we're okay just defining `ReflectRemote` ourselves, but we can go the bytemuck route if we'd prefer offloading that work to another crate. --- ## Changelog * Added the `#[reflect_remote(...)]` attribute macro to allow `Reflect` to be used on remote types * Added `ReflectRemote` trait for ensuring proper remote wrapper usage
2024-08-12 19:12:53 +00:00
.map(|field| field.reflected_type().clone())
bevy_reflect: Split `#[reflect(where)]` (#11597) # Objective Revert the changes to type parameter bounds introduced in #9046, improves the `#[reflect(where)]` attribute (also from #9046), and adds the ability to opt out of field bounds. This is based on suggestions by @soqb and discussion on [Discord](https://discord.com/channels/691052431525675048/1002362493634629796/1201227833826103427). ## Solution Reverts the changes to type parameter bounds when deriving `Reflect`, introduced in #9046. This was originally done as a means of fixing a recursion issue (#8965). However, as @soqb pointed out, we could achieve the same result by simply making an opt-out attribute instead of messing with the type parameter bounds. This PR has four main changes: 1. Reverts the type parameter bounds from #9046 2. Includes `TypePath` as a default bound for active fields 3. Changes `#reflect(where)]` to be strictly additive 4. Adds `#reflect(no_field_bounds)]` to opt out of field bounds Change 1 means that, like before, type parameters only receive at most the `TypePath` bound (if `#[reflect(type_path = false)]` is not present) and active fields receive the `Reflect` or `FromReflect` bound. And with Change 2, they will also receive `TypePath` (since it's indirectly required by `Typed` to construct `NamedField` and `UnnamedField` instances). Change 3 was made to make room for Change 4. By splitting out the responsibility of `#reflect(where)]`, we can use it with or without `#reflect(no_field_bounds)]` for various use cases. For example, if we hadn't done this, the following would have failed: ```rust // Since we're not using `#reflect(no_field_bounds)]`, // `T::Assoc` is automatically given the required bounds // of `FromReflect + TypePath` #[derive(Reflect)] #[reflect(where T::Assoc: OtherTrait)] struct Foo<T: MyTrait> { value: T::Assoc, } ``` This provides more flexibility to the user while still letting them add or remove most trait bounds. And to solve the original recursion issue, we can do: ```rust #[derive(Reflect)] #[reflect(no_field_bounds)] // <-- Added struct Foo { foo: Vec<Foo> } ``` #### Bounds All in all, we now have four sets of trait bounds: - `Self` gets the bounds `Any + Send + Sync` - Type parameters get the bound `TypePath`. This can be opted out of with `#[reflect(type_path = false)]` - Active fields get the bounds `TypePath` and `FromReflect`/`Reflect` bounds. This can be opted out of with `#reflect(no_field_bounds)]` - Custom bounds can be added with `#[reflect(where)]` --- ## Changelog - Revert some changes #9046 - `#reflect(where)]` is now strictly additive - Added `#reflect(no_field_bounds)]` attribute to opt out of automatic field trait bounds when deriving `Reflect` - Made the `TypePath` requirement on fields when deriving `Reflect` more explicit ## Migration Guide > [!important] > This PR shouldn't be a breaking change relative to the current version of Bevy (v0.12). And since it removes the breaking parts of #9046, that PR also won't need a migration guide.
2024-01-29 17:54:17 +00:00
.collect()
}
/// Get an iterator of fields which are exposed to the reflection API
pub fn active_fields(&self) -> impl Iterator<Item = &StructField<'a>> {
self.variants.iter().flat_map(EnumVariant::active_fields)
bevy_reflect: Split `#[reflect(where)]` (#11597) # Objective Revert the changes to type parameter bounds introduced in #9046, improves the `#[reflect(where)]` attribute (also from #9046), and adds the ability to opt out of field bounds. This is based on suggestions by @soqb and discussion on [Discord](https://discord.com/channels/691052431525675048/1002362493634629796/1201227833826103427). ## Solution Reverts the changes to type parameter bounds when deriving `Reflect`, introduced in #9046. This was originally done as a means of fixing a recursion issue (#8965). However, as @soqb pointed out, we could achieve the same result by simply making an opt-out attribute instead of messing with the type parameter bounds. This PR has four main changes: 1. Reverts the type parameter bounds from #9046 2. Includes `TypePath` as a default bound for active fields 3. Changes `#reflect(where)]` to be strictly additive 4. Adds `#reflect(no_field_bounds)]` to opt out of field bounds Change 1 means that, like before, type parameters only receive at most the `TypePath` bound (if `#[reflect(type_path = false)]` is not present) and active fields receive the `Reflect` or `FromReflect` bound. And with Change 2, they will also receive `TypePath` (since it's indirectly required by `Typed` to construct `NamedField` and `UnnamedField` instances). Change 3 was made to make room for Change 4. By splitting out the responsibility of `#reflect(where)]`, we can use it with or without `#reflect(no_field_bounds)]` for various use cases. For example, if we hadn't done this, the following would have failed: ```rust // Since we're not using `#reflect(no_field_bounds)]`, // `T::Assoc` is automatically given the required bounds // of `FromReflect + TypePath` #[derive(Reflect)] #[reflect(where T::Assoc: OtherTrait)] struct Foo<T: MyTrait> { value: T::Assoc, } ``` This provides more flexibility to the user while still letting them add or remove most trait bounds. And to solve the original recursion issue, we can do: ```rust #[derive(Reflect)] #[reflect(no_field_bounds)] // <-- Added struct Foo { foo: Vec<Foo> } ``` #### Bounds All in all, we now have four sets of trait bounds: - `Self` gets the bounds `Any + Send + Sync` - Type parameters get the bound `TypePath`. This can be opted out of with `#[reflect(type_path = false)]` - Active fields get the bounds `TypePath` and `FromReflect`/`Reflect` bounds. This can be opted out of with `#reflect(no_field_bounds)]` - Custom bounds can be added with `#[reflect(where)]` --- ## Changelog - Revert some changes #9046 - `#reflect(where)]` is now strictly additive - Added `#reflect(no_field_bounds)]` attribute to opt out of automatic field trait bounds when deriving `Reflect` - Made the `TypePath` requirement on fields when deriving `Reflect` more explicit ## Migration Guide > [!important] > This PR shouldn't be a breaking change relative to the current version of Bevy (v0.12). And since it removes the breaking parts of #9046, that PR also won't need a migration guide.
2024-01-29 17:54:17 +00:00
}
Enable deriving Reflect on structs with generic types (#7364) # Objective I recently had an issue, where I have a struct: ``` struct Property { inner: T } ``` that I use as a wrapper for internal purposes. I don't want to update my struct definition to ``` struct Property<T: Reflect>{ inner: T } ``` because I still want to be able to build `Property<T>` for types `T` that are not `Reflect`. (and also because I don't want to update my whole code base with `<T: Reflect>` bounds) I still wanted to have reflection on it (for `bevy_inspector_egui`), but adding `derive(Reflect)` fails with the error: `T cannot be sent between threads safely. T needs to implement Sync.` I believe that `bevy_reflect` should adopt the model of other derives in the case of generics, which is to add the `Reflect` implementation only if the generics also implement `Reflect`. (That is the behaviour of other macros such as `derive(Clone)` or `derive(Debug)`. It's also the current behavior of `derive(FromReflect)`. Basically doing something like: ``` impl<T> Reflect for Foo<T> where T: Reflect ``` ## Solution - I updated the derive macros for `Structs` and `TupleStructs` to add extra `where` bounds. - Every type that is reflected will need a `T: Reflect` bound - Ignored types will need a `T: 'static + Send + Sync` bound. Here's the reason. For cases like this: ``` #[derive(Reflect)] struct Foo<T, U>{ a: T #[reflect(ignore)] b: U } ``` I had to add the bound `'static + Send + Sync` to ignored generics like `U`. The reason is that we want `Foo<T, U>` to be `Reflect: 'static + Send + Sync`, so `Foo<T, U>` must be able to implement those auto-traits. `Foo<T, U>` will only implement those auto-traits if every generic type implements them, including ignored types. This means that the previously compile-fail case now compiles: ``` #[derive(Reflect)] struct Foo<'a> { #[reflect(ignore)] value: &'a str, } ``` But `Foo<'a>` will only be useable in the cases where `'a: 'static` and panic if we don't have `'a: 'static`, which is what we want (nice bonus from this PR ;) ) --- ## Changelog > This section is optional. If this was a trivial fix, or has no externally-visible impact, you can delete this section. ### Added Possibility to add `derive(Reflect)` to structs and enums that contain generic types, like so: ``` #[derive(Reflect)] struct Foo<T>{ a: T } ``` Reflection will only be available if the generic type T also implements `Reflect`. (previously, this would just return a compiler error)
2023-01-28 00:12:06 +00:00
pub fn where_clause_options(&self) -> WhereClauseOptions {
bevy_reflect: Split `#[reflect(where)]` (#11597) # Objective Revert the changes to type parameter bounds introduced in #9046, improves the `#[reflect(where)]` attribute (also from #9046), and adds the ability to opt out of field bounds. This is based on suggestions by @soqb and discussion on [Discord](https://discord.com/channels/691052431525675048/1002362493634629796/1201227833826103427). ## Solution Reverts the changes to type parameter bounds when deriving `Reflect`, introduced in #9046. This was originally done as a means of fixing a recursion issue (#8965). However, as @soqb pointed out, we could achieve the same result by simply making an opt-out attribute instead of messing with the type parameter bounds. This PR has four main changes: 1. Reverts the type parameter bounds from #9046 2. Includes `TypePath` as a default bound for active fields 3. Changes `#reflect(where)]` to be strictly additive 4. Adds `#reflect(no_field_bounds)]` to opt out of field bounds Change 1 means that, like before, type parameters only receive at most the `TypePath` bound (if `#[reflect(type_path = false)]` is not present) and active fields receive the `Reflect` or `FromReflect` bound. And with Change 2, they will also receive `TypePath` (since it's indirectly required by `Typed` to construct `NamedField` and `UnnamedField` instances). Change 3 was made to make room for Change 4. By splitting out the responsibility of `#reflect(where)]`, we can use it with or without `#reflect(no_field_bounds)]` for various use cases. For example, if we hadn't done this, the following would have failed: ```rust // Since we're not using `#reflect(no_field_bounds)]`, // `T::Assoc` is automatically given the required bounds // of `FromReflect + TypePath` #[derive(Reflect)] #[reflect(where T::Assoc: OtherTrait)] struct Foo<T: MyTrait> { value: T::Assoc, } ``` This provides more flexibility to the user while still letting them add or remove most trait bounds. And to solve the original recursion issue, we can do: ```rust #[derive(Reflect)] #[reflect(no_field_bounds)] // <-- Added struct Foo { foo: Vec<Foo> } ``` #### Bounds All in all, we now have four sets of trait bounds: - `Self` gets the bounds `Any + Send + Sync` - Type parameters get the bound `TypePath`. This can be opted out of with `#[reflect(type_path = false)]` - Active fields get the bounds `TypePath` and `FromReflect`/`Reflect` bounds. This can be opted out of with `#reflect(no_field_bounds)]` - Custom bounds can be added with `#[reflect(where)]` --- ## Changelog - Revert some changes #9046 - `#reflect(where)]` is now strictly additive - Added `#reflect(no_field_bounds)]` attribute to opt out of automatic field trait bounds when deriving `Reflect` - Made the `TypePath` requirement on fields when deriving `Reflect` more explicit ## Migration Guide > [!important] > This PR shouldn't be a breaking change relative to the current version of Bevy (v0.12). And since it removes the breaking parts of #9046, that PR also won't need a migration guide.
2024-01-29 17:54:17 +00:00
WhereClauseOptions::new_with_fields(self.meta(), self.active_types().into_boxed_slice())
Enable deriving Reflect on structs with generic types (#7364) # Objective I recently had an issue, where I have a struct: ``` struct Property { inner: T } ``` that I use as a wrapper for internal purposes. I don't want to update my struct definition to ``` struct Property<T: Reflect>{ inner: T } ``` because I still want to be able to build `Property<T>` for types `T` that are not `Reflect`. (and also because I don't want to update my whole code base with `<T: Reflect>` bounds) I still wanted to have reflection on it (for `bevy_inspector_egui`), but adding `derive(Reflect)` fails with the error: `T cannot be sent between threads safely. T needs to implement Sync.` I believe that `bevy_reflect` should adopt the model of other derives in the case of generics, which is to add the `Reflect` implementation only if the generics also implement `Reflect`. (That is the behaviour of other macros such as `derive(Clone)` or `derive(Debug)`. It's also the current behavior of `derive(FromReflect)`. Basically doing something like: ``` impl<T> Reflect for Foo<T> where T: Reflect ``` ## Solution - I updated the derive macros for `Structs` and `TupleStructs` to add extra `where` bounds. - Every type that is reflected will need a `T: Reflect` bound - Ignored types will need a `T: 'static + Send + Sync` bound. Here's the reason. For cases like this: ``` #[derive(Reflect)] struct Foo<T, U>{ a: T #[reflect(ignore)] b: U } ``` I had to add the bound `'static + Send + Sync` to ignored generics like `U`. The reason is that we want `Foo<T, U>` to be `Reflect: 'static + Send + Sync`, so `Foo<T, U>` must be able to implement those auto-traits. `Foo<T, U>` will only implement those auto-traits if every generic type implements them, including ignored types. This means that the previously compile-fail case now compiles: ``` #[derive(Reflect)] struct Foo<'a> { #[reflect(ignore)] value: &'a str, } ``` But `Foo<'a>` will only be useable in the cases where `'a: 'static` and panic if we don't have `'a: 'static`, which is what we want (nice bonus from this PR ;) ) --- ## Changelog > This section is optional. If this was a trivial fix, or has no externally-visible impact, you can delete this section. ### Added Possibility to add `derive(Reflect)` to structs and enums that contain generic types, like so: ``` #[derive(Reflect)] struct Foo<T>{ a: T } ``` Reflection will only be available if the generic type T also implements `Reflect`. (previously, this would just return a compiler error)
2023-01-28 00:12:06 +00:00
}
bevy_reflect: Recursive registration (#5781) # Objective Resolves #4154 Currently, registration must all be done manually: ```rust #[derive(Reflect)] struct Foo(Bar); #[derive(Reflect)] struct Bar(Baz); #[derive(Reflect)] struct Baz(usize); fn main() { // ... app .register_type::<Foo>() .register_type::<Bar>() .register_type::<Baz>() // .register_type::<usize>() <- This one is handled by Bevy, thankfully // ... } ``` This can grow really quickly and become very annoying to add, remove, and update as types change. It would be great if we could help reduce the number of types that a user must manually implement themselves. ## Solution As suggested in #4154, this PR adds automatic recursive registration. Essentially, when a type is registered, it may now also choose to register additional types along with it using the new `GetTypeRegistration::register_type_dependencies` trait method. The `Reflect` derive macro now automatically does this for all fields in structs, tuple structs, struct variants, and tuple variants. This is also done for tuples, arrays, `Vec<T>`, `HashMap<K, V>`, and `Option<T>`. This allows us to simplify the code above like: ```rust #[derive(Reflect)] struct Foo(Bar); #[derive(Reflect)] struct Bar(Baz); #[derive(Reflect)] struct Baz(usize); fn main() { // ... app.register_type::<Foo>() // ... } ``` This automatic registration only occurs if the type has not yet been registered. If it has been registered, we simply skip it and move to the next one. This reduces the cost of registration and prevents overwriting customized registrations. ## Considerations While this does improve ergonomics on one front, it's important to look at some of the arguments against adopting a PR like this. #### Generic Bounds ~~Since we need to be able to register the fields individually, we need those fields to implement `GetTypeRegistration`. This forces users to then add this trait as a bound on their generic arguments. This annoyance could be relieved with something like #5772.~~ This is no longer a major issue as the `Reflect` derive now adds the `GetTypeRegistration` bound by default. This should technically be okay, since we already add the `Reflect` bound. However, this can also be considered a breaking change for manual implementations that left out a `GetTypeRegistration` impl ~~or for items that contain dynamic types (e.g. `DynamicStruct`) since those also do not implement `GetTypeRegistration`~~. #### Registration Assumptions By automatically registering fields, users might inadvertently be relying on certain types to be automatically registered. If `Foo` auto-registers `Bar`, but `Foo` is later removed from the code, then anywhere that previously used or relied on `Bar`'s registration would now fail. --- ## Changelog - Added recursive type registration to structs, tuple structs, struct variants, tuple variants, tuples, arrays, `Vec<T>`, `HashMap<K, V>`, and `Option<T>` - Added a new trait in the hidden `bevy_reflect::__macro_exports` module called `RegisterForReflection` - Added `GetTypeRegistration` impl for `bevy_render::render_asset::RenderAssetUsages` ## Migration Guide All types that derive `Reflect` will now automatically add `GetTypeRegistration` as a bound on all (unignored) fields. This means that all reflected fields will need to also implement `GetTypeRegistration`. If all fields **derive** `Reflect` or are implemented in `bevy_reflect`, this should not cause any issues. However, manual implementations of `Reflect` that excluded a `GetTypeRegistration` impl for their type will need to add one. ```rust #[derive(Reflect)] struct Foo<T: FromReflect> { data: MyCustomType<T> } // OLD impl<T: FromReflect> Reflect for MyCustomType<T> {/* ... */} // NEW impl<T: FromReflect + GetTypeRegistration> Reflect for MyCustomType<T> {/* ... */} impl<T: FromReflect + GetTypeRegistration> GetTypeRegistration for MyCustomType<T> {/* ... */} ``` --------- Co-authored-by: James Liu <contact@jamessliu.com> Co-authored-by: radiish <cb.setho@gmail.com> Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2024-03-04 19:04:10 +00:00
/// Returns the `GetTypeRegistration` impl as a `TokenStream`.
///
/// Returns a specific implementation for enums and this method should be preferred over the generic [`get_type_registration`](crate::ReflectMeta) method
pub fn get_type_registration(
&self,
where_clause_options: &WhereClauseOptions,
) -> proc_macro2::TokenStream {
crate::registration::impl_get_type_registration(
self.meta(),
where_clause_options,
None,
bevy_reflect: Reflect remote types (#6042) # Objective The goal with this PR is to allow the use of types that don't implement `Reflect` within the reflection API. Rust's [orphan rule](https://doc.rust-lang.org/book/ch10-02-traits.html#implementing-a-trait-on-a-type) prevents implementing a trait on an external type when neither type nor trait are owned by the implementor. This means that if a crate, `cool_rust_lib`, defines a type, `Foo`, then a user cannot use it with reflection. What this means is that we have to ignore it most of the time: ```rust #[derive(Reflect)] struct SomeStruct { #[reflect(ignore)] data: cool_rust_lib::Foo } ``` Obviously, it's impossible to implement `Reflect` on `Foo`. But does it *have* to be? Most of reflection doesn't deal with concrete types— it's almost all using `dyn Reflect`. And being very metadata-driven, it should theoretically be possible. I mean, [`serde`](https://serde.rs/remote-derive.html) does it. ## Solution > Special thanks to @danielhenrymantilla for their help reviewing this PR and offering wisdom wrt safety. Taking a page out of `serde`'s book, this PR adds the ability to easily use "remote types" with reflection. In this context, a "remote type" is the external type for which we have no ability to implement `Reflect`. This adds the `#[reflect_remote(...)]` attribute macro, which is used to generate "remote type wrappers". All you have to do is define the wrapper exactly the same as the remote type's definition: ```rust // Pretend this is our external crate mod cool_rust_lib { #[derive(Default)] struct Foo { pub value: String } } #[reflect_remote(cool_rust_lib::Foo)] struct FooWrapper { pub value: String } ``` > **Note:** All fields in the external type *must* be public. This could be addressed with a separate getter/setter attribute either in this PR or in another one. The macro takes this user-defined item and transforms it into a newtype wrapper around the external type, marking it as `#[repr(transparent)]`. The fields/variants defined by the user are simply used to build out the reflection impls. Additionally, it generates an implementation of the new trait, `ReflectRemote`, which helps prevent accidental misuses of this API. Therefore, the output generated by the macro would look something like: ```rust #[repr(transparent)] struct FooWrapper(pub cool_rust_lib::Foo); impl ReflectRemote for FooWrapper { type Remote = cool_rust_lib::Foo; // transmutation methods... } // reflection impls... // these will acknowledge and make use of the `value` field ``` Internally, the reflection API will pass around the `FooWrapper` and [transmute](https://doc.rust-lang.org/std/mem/fn.transmute.html) it where necessary. All we have to do is then tell `Reflect` to do that. So rather than ignoring the field, we tell `Reflect` to use our wrapper using the `#[reflect(remote = ...)]` field attribute: ```rust #[derive(Reflect)] struct SomeStruct { #[reflect(remote = FooWrapper)] data: cool_rust_lib::Foo } ``` #### Other Macros & Type Data Because this macro consumes the defined item and generates a new one, we can't just put our macros anywhere. All macros that should be passed to the generated struct need to come *below* this macro. For example, to derive `Default` and register its associated type data: ```rust // ✅ GOOD #[reflect_remote(cool_rust_lib::Foo)] #[derive(Default)] #[reflect(Default)] struct FooWrapper { pub value: String } // ❌ BAD #[derive(Default)] #[reflect_remote(cool_rust_lib::Foo)] #[reflect(Default)] struct FooWrapper { pub value: String } ``` #### Generics Generics are forwarded to the generated struct as well. They should also be defined in the same order: ```rust #[reflect_remote(RemoteGeneric<'a, T1, T2>)] struct GenericWrapper<'a, T1, T2> { pub foo: &'a T1, pub bar: &'a T2, } ``` > Naming does *not* need to match the original definition's. Only order matters here. > Also note that the code above is just a demonstration and doesn't actually compile since we'd need to enforce certain bounds (e.g. `T1: Reflect`, `'a: 'static`, etc.) #### Nesting And, yes, you can nest remote types: ```rust #[reflect_remote(RemoteOuter)] struct OuterWrapper { #[reflect(remote = InnerWrapper)] pub inner: RemoteInner } #[reflect_remote(RemoteInner)] struct InnerWrapper(usize); ``` #### Assertions This macro will also generate some compile-time assertions to ensure that the correct types are used. It's important we catch this early so users don't have to wait for something to panic. And it also helps keep our `unsafe` a little safer. For example, a wrapper definition that does not match its corresponding remote type will result in an error: ```rust mod external_crate { pub struct TheirStruct(pub u32); } #[reflect_remote(external_crate::TheirStruct)] struct MyStruct(pub String); // ERROR: expected type `u32` but found `String` ``` <details> <summary>Generated Assertion</summary> ```rust const _: () = { #[allow(non_snake_case)] #[allow(unused_variables)] #[allow(unused_assignments)] #[allow(unreachable_patterns)] #[allow(clippy::multiple_bound_locations)] fn assert_wrapper_definition_matches_remote_type( mut __remote__: external_crate::TheirStruct, ) { __remote__.0 = (|| -> ::core::option::Option<String> { None })().unwrap(); } }; ``` </details> Additionally, using the incorrect type in a `#[reflect(remote = ...)]` attribute should result in an error: ```rust mod external_crate { pub struct TheirFoo(pub u32); pub struct TheirBar(pub i32); } #[reflect_remote(external_crate::TheirFoo)] struct MyFoo(pub u32); #[reflect_remote(external_crate::TheirBar)] struct MyBar(pub i32); #[derive(Reflect)] struct MyStruct { #[reflect(remote = MyBar)] // ERROR: expected type `TheirFoo` but found struct `TheirBar` foo: external_crate::TheirFoo } ``` <details> <summary>Generated Assertion</summary> ```rust const _: () = { struct RemoteFieldAssertions; impl RemoteFieldAssertions { #[allow(non_snake_case)] #[allow(clippy::multiple_bound_locations)] fn assert__foo__is_valid_remote() { let _: <MyBar as bevy_reflect::ReflectRemote>::Remote = (|| -> ::core::option::Option<external_crate::TheirFoo> { None })().unwrap(); } } }; ``` </details> ### Discussion There are a couple points that I think still need discussion or validation. - [x] 1. `Any` shenanigans ~~If we wanted to downcast our remote type from a `dyn Reflect`, we'd have to first downcast to the wrapper then extract the inner type. This PR has a [commit](b840db9f74cb6d357f951cb11b150d46bac89ee2) that addresses this by making all the `Reflect::*any` methods return the inner type rather than the wrapper type. This allows us to downcast directly to our remote type.~~ ~~However, I'm not sure if this is something we want to do. For unknowing users, it could be confusing and seemingly inconsistent. Is it worth keeping? Or should this behavior be removed?~~ I think this should be fine. The remote wrapper is an implementation detail and users should not need to downcast to the wrapper type. Feel free to let me know if there are other opinions on this though! - [x] 2. Implementing `Deref/DerefMut` and `From` ~~We don't currently do this, but should we implement other traits on the generated transparent struct? We could implement `Deref`/`DerefMut` to easily access the inner type. And we could implement `From` for easier conversion between the two types (e.g. `T: Into<Foo>`).~~ As mentioned in the comments, we probably don't need to do this. Again, the remote wrapper is an implementation detail, and should generally not be used directly. - [x] 3. ~~Should we define a getter/setter field attribute in this PR as well or leave it for a future one?~~ I think this should be saved for a future PR - [ ] 4. Any foreseeable issues with this implementation? #### Alternatives One alternative to defining our own `ReflectRemote` would be to use [bytemuck's `TransparentWrapper`](https://docs.rs/bytemuck/1.13.1/bytemuck/trait.TransparentWrapper.html) (as suggested by @danielhenrymantilla). This is definitely a viable option, as `ReflectRemote` is pretty much the same thing as `TransparentWrapper`. However, the cost would be bringing in a new crate— though, it is already in use in a few other sub-crates like bevy_render. I think we're okay just defining `ReflectRemote` ourselves, but we can go the bytemuck route if we'd prefer offloading that work to another crate. --- ## Changelog * Added the `#[reflect_remote(...)]` attribute macro to allow `Reflect` to be used on remote types * Added `ReflectRemote` trait for ensuring proper remote wrapper usage
2024-08-12 19:12:53 +00:00
Some(self.active_fields().map(StructField::reflected_type)),
bevy_reflect: Recursive registration (#5781) # Objective Resolves #4154 Currently, registration must all be done manually: ```rust #[derive(Reflect)] struct Foo(Bar); #[derive(Reflect)] struct Bar(Baz); #[derive(Reflect)] struct Baz(usize); fn main() { // ... app .register_type::<Foo>() .register_type::<Bar>() .register_type::<Baz>() // .register_type::<usize>() <- This one is handled by Bevy, thankfully // ... } ``` This can grow really quickly and become very annoying to add, remove, and update as types change. It would be great if we could help reduce the number of types that a user must manually implement themselves. ## Solution As suggested in #4154, this PR adds automatic recursive registration. Essentially, when a type is registered, it may now also choose to register additional types along with it using the new `GetTypeRegistration::register_type_dependencies` trait method. The `Reflect` derive macro now automatically does this for all fields in structs, tuple structs, struct variants, and tuple variants. This is also done for tuples, arrays, `Vec<T>`, `HashMap<K, V>`, and `Option<T>`. This allows us to simplify the code above like: ```rust #[derive(Reflect)] struct Foo(Bar); #[derive(Reflect)] struct Bar(Baz); #[derive(Reflect)] struct Baz(usize); fn main() { // ... app.register_type::<Foo>() // ... } ``` This automatic registration only occurs if the type has not yet been registered. If it has been registered, we simply skip it and move to the next one. This reduces the cost of registration and prevents overwriting customized registrations. ## Considerations While this does improve ergonomics on one front, it's important to look at some of the arguments against adopting a PR like this. #### Generic Bounds ~~Since we need to be able to register the fields individually, we need those fields to implement `GetTypeRegistration`. This forces users to then add this trait as a bound on their generic arguments. This annoyance could be relieved with something like #5772.~~ This is no longer a major issue as the `Reflect` derive now adds the `GetTypeRegistration` bound by default. This should technically be okay, since we already add the `Reflect` bound. However, this can also be considered a breaking change for manual implementations that left out a `GetTypeRegistration` impl ~~or for items that contain dynamic types (e.g. `DynamicStruct`) since those also do not implement `GetTypeRegistration`~~. #### Registration Assumptions By automatically registering fields, users might inadvertently be relying on certain types to be automatically registered. If `Foo` auto-registers `Bar`, but `Foo` is later removed from the code, then anywhere that previously used or relied on `Bar`'s registration would now fail. --- ## Changelog - Added recursive type registration to structs, tuple structs, struct variants, tuple variants, tuples, arrays, `Vec<T>`, `HashMap<K, V>`, and `Option<T>` - Added a new trait in the hidden `bevy_reflect::__macro_exports` module called `RegisterForReflection` - Added `GetTypeRegistration` impl for `bevy_render::render_asset::RenderAssetUsages` ## Migration Guide All types that derive `Reflect` will now automatically add `GetTypeRegistration` as a bound on all (unignored) fields. This means that all reflected fields will need to also implement `GetTypeRegistration`. If all fields **derive** `Reflect` or are implemented in `bevy_reflect`, this should not cause any issues. However, manual implementations of `Reflect` that excluded a `GetTypeRegistration` impl for their type will need to add one. ```rust #[derive(Reflect)] struct Foo<T: FromReflect> { data: MyCustomType<T> } // OLD impl<T: FromReflect> Reflect for MyCustomType<T> {/* ... */} // NEW impl<T: FromReflect + GetTypeRegistration> Reflect for MyCustomType<T> {/* ... */} impl<T: FromReflect + GetTypeRegistration> GetTypeRegistration for MyCustomType<T> {/* ... */} ``` --------- Co-authored-by: James Liu <contact@jamessliu.com> Co-authored-by: radiish <cb.setho@gmail.com> Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2024-03-04 19:04:10 +00:00
)
}
bevy_reflect: Custom attributes (#11659) # Objective As work on the editor starts to ramp up, it might be nice to start allowing types to specify custom attributes. These can be used to provide certain functionality to fields, such as ranges or controlling how data is displayed. A good example of this can be seen in [`bevy-inspector-egui`](https://github.com/jakobhellermann/bevy-inspector-egui) with its [`InspectorOptions`](https://docs.rs/bevy-inspector-egui/0.22.1/bevy_inspector_egui/struct.InspectorOptions.html): ```rust #[derive(Reflect, Default, InspectorOptions)] #[reflect(InspectorOptions)] struct Slider { #[inspector(min = 0.0, max = 1.0)] value: f32, } ``` Normally, as demonstrated in the example above, these attributes are handled by a derive macro and stored in a corresponding `TypeData` struct (i.e. `ReflectInspectorOptions`). Ideally, we would have a good way of defining this directly via reflection so that users don't need to create and manage a whole proc macro just to allow these sorts of attributes. And note that this doesn't have to just be for inspectors and editors. It can be used for things done purely on the code side of things. ## Solution Create a new method for storing attributes on fields via the `Reflect` derive. These custom attributes are stored in type info (e.g. `NamedField`, `StructInfo`, etc.). ```rust #[derive(Reflect)] struct Slider { #[reflect(@0.0..=1.0)] value: f64, } let TypeInfo::Struct(info) = Slider::type_info() else { panic!("expected struct info"); }; let field = info.field("value").unwrap(); let range = field.get_attribute::<RangeInclusive<f64>>().unwrap(); assert_eq!(*range, 0.0..=1.0); ``` ## TODO - [x] ~~Bikeshed syntax~~ Went with a type-based approach, prefixed by `@` for ease of parsing and flexibility - [x] Add support for custom struct/tuple struct field attributes - [x] Add support for custom enum variant field attributes - [x] ~~Add support for custom enum variant attributes (maybe?)~~ ~~Will require a larger refactor. Can be saved for a future PR if we really want it.~~ Actually, we apparently still have support for variant attributes despite not using them, so it was pretty easy to add lol. - [x] Add support for custom container attributes - [x] Allow custom attributes to store any reflectable value (not just `Lit`) - [x] ~~Store attributes in registry~~ This PR used to store these in attributes in the registry, however, it has since switched over to storing them in type info - [x] Add example ## Bikeshedding > [!note] > This section was made for the old method of handling custom attributes, which stored them by name (i.e. `some_attribute = 123`). The PR has shifted away from that, to a more type-safe approach. > > This section has been left for reference. There are a number of ways we can syntactically handle custom attributes. Feel free to leave a comment on your preferred one! Ideally we want one that is clear, readable, and concise since these will potentially see _a lot_ of use. Below is a small, non-exhaustive list of them. Note that the `skip_serializing` reflection attribute is added to demonstrate how each case plays with existing reflection attributes. <details> <summary>List</summary> ##### 1. `@(name = value)` > The `@` was chosen to make them stand out from other attributes and because the "at" symbol is a subtle pneumonic for "attribute". Of course, other symbols could be used (e.g. `$`, `#`, etc.). ```rust #[derive(Reflect)] struct Slider { #[reflect(@(min = 0.0, max = 1.0), skip_serializing)] #[[reflect(@(bevy_editor::hint = "Range: 0.0 to 1.0"))] value: f32, } ``` ##### 2. `@name = value` > This is my personal favorite. ```rust #[derive(Reflect)] struct Slider { #[reflect(@min = 0.0, @max = 1.0, skip_serializing)] #[[reflect(@bevy_editor::hint = "Range: 0.0 to 1.0")] value: f32, } ``` ##### 3. `custom_attr(name = value)` > `custom_attr` can be anything. Other possibilities include `with` or `tag`. ```rust #[derive(Reflect)] struct Slider { #[reflect(custom_attr(min = 0.0, max = 1.0), skip_serializing)] #[[reflect(custom_attr(bevy_editor::hint = "Range: 0.0 to 1.0"))] value: f32, } ``` ##### 4. `reflect_attr(name = value)` ```rust #[derive(Reflect)] struct Slider { #[reflect(skip_serializing)] #[reflect_attr(min = 0.0, max = 1.0)] #[[reflect_attr(bevy_editor::hint = "Range: 0.0 to 1.0")] value: f32, } ``` </details> --- ## Changelog - Added support for custom attributes on reflected types (i.e. `#[reflect(@Foo::new("bar")]`)
2024-05-20 19:30:21 +00:00
/// Generates a `TokenStream` for `TypeInfo::Enum` construction.
pub fn to_info_tokens(&self) -> proc_macro2::TokenStream {
let bevy_reflect_path = self.meta().bevy_reflect_path();
let variants = self
.variants
.iter()
.map(|variant| variant.to_info_tokens(bevy_reflect_path));
let custom_attributes = self
.meta
.attrs
.custom_attributes()
.to_tokens(bevy_reflect_path);
#[allow(unused_mut)] // Needs mutability for the feature gate
let mut info = quote! {
#bevy_reflect_path::EnumInfo::new::<Self>(&[
#(#variants),*
])
.with_custom_attributes(#custom_attributes)
};
#[cfg(feature = "documentation")]
{
let docs = self.meta().doc();
info.extend(quote! {
.with_docs(#docs)
});
}
quote! {
#bevy_reflect_path::TypeInfo::Enum(#info)
}
}
Enable deriving Reflect on structs with generic types (#7364) # Objective I recently had an issue, where I have a struct: ``` struct Property { inner: T } ``` that I use as a wrapper for internal purposes. I don't want to update my struct definition to ``` struct Property<T: Reflect>{ inner: T } ``` because I still want to be able to build `Property<T>` for types `T` that are not `Reflect`. (and also because I don't want to update my whole code base with `<T: Reflect>` bounds) I still wanted to have reflection on it (for `bevy_inspector_egui`), but adding `derive(Reflect)` fails with the error: `T cannot be sent between threads safely. T needs to implement Sync.` I believe that `bevy_reflect` should adopt the model of other derives in the case of generics, which is to add the `Reflect` implementation only if the generics also implement `Reflect`. (That is the behaviour of other macros such as `derive(Clone)` or `derive(Debug)`. It's also the current behavior of `derive(FromReflect)`. Basically doing something like: ``` impl<T> Reflect for Foo<T> where T: Reflect ``` ## Solution - I updated the derive macros for `Structs` and `TupleStructs` to add extra `where` bounds. - Every type that is reflected will need a `T: Reflect` bound - Ignored types will need a `T: 'static + Send + Sync` bound. Here's the reason. For cases like this: ``` #[derive(Reflect)] struct Foo<T, U>{ a: T #[reflect(ignore)] b: U } ``` I had to add the bound `'static + Send + Sync` to ignored generics like `U`. The reason is that we want `Foo<T, U>` to be `Reflect: 'static + Send + Sync`, so `Foo<T, U>` must be able to implement those auto-traits. `Foo<T, U>` will only implement those auto-traits if every generic type implements them, including ignored types. This means that the previously compile-fail case now compiles: ``` #[derive(Reflect)] struct Foo<'a> { #[reflect(ignore)] value: &'a str, } ``` But `Foo<'a>` will only be useable in the cases where `'a: 'static` and panic if we don't have `'a: 'static`, which is what we want (nice bonus from this PR ;) ) --- ## Changelog > This section is optional. If this was a trivial fix, or has no externally-visible impact, you can delete this section. ### Added Possibility to add `derive(Reflect)` to structs and enums that contain generic types, like so: ``` #[derive(Reflect)] struct Foo<T>{ a: T } ``` Reflection will only be available if the generic type T also implements `Reflect`. (previously, this would just return a compiler error)
2023-01-28 00:12:06 +00:00
}
impl<'a> EnumVariant<'a> {
/// Get an iterator of fields which are exposed to the reflection API
pub fn active_fields(&self) -> impl Iterator<Item = &StructField<'a>> {
self.fields()
.iter()
.filter(|field| field.attrs.ignore.is_active())
}
/// The complete set of fields in this variant.
#[allow(dead_code)]
pub fn fields(&self) -> &[StructField<'a>] {
match &self.fields {
EnumVariantFields::Named(fields) | EnumVariantFields::Unnamed(fields) => fields,
EnumVariantFields::Unit => &[],
}
}
bevy_reflect: Custom attributes (#11659) # Objective As work on the editor starts to ramp up, it might be nice to start allowing types to specify custom attributes. These can be used to provide certain functionality to fields, such as ranges or controlling how data is displayed. A good example of this can be seen in [`bevy-inspector-egui`](https://github.com/jakobhellermann/bevy-inspector-egui) with its [`InspectorOptions`](https://docs.rs/bevy-inspector-egui/0.22.1/bevy_inspector_egui/struct.InspectorOptions.html): ```rust #[derive(Reflect, Default, InspectorOptions)] #[reflect(InspectorOptions)] struct Slider { #[inspector(min = 0.0, max = 1.0)] value: f32, } ``` Normally, as demonstrated in the example above, these attributes are handled by a derive macro and stored in a corresponding `TypeData` struct (i.e. `ReflectInspectorOptions`). Ideally, we would have a good way of defining this directly via reflection so that users don't need to create and manage a whole proc macro just to allow these sorts of attributes. And note that this doesn't have to just be for inspectors and editors. It can be used for things done purely on the code side of things. ## Solution Create a new method for storing attributes on fields via the `Reflect` derive. These custom attributes are stored in type info (e.g. `NamedField`, `StructInfo`, etc.). ```rust #[derive(Reflect)] struct Slider { #[reflect(@0.0..=1.0)] value: f64, } let TypeInfo::Struct(info) = Slider::type_info() else { panic!("expected struct info"); }; let field = info.field("value").unwrap(); let range = field.get_attribute::<RangeInclusive<f64>>().unwrap(); assert_eq!(*range, 0.0..=1.0); ``` ## TODO - [x] ~~Bikeshed syntax~~ Went with a type-based approach, prefixed by `@` for ease of parsing and flexibility - [x] Add support for custom struct/tuple struct field attributes - [x] Add support for custom enum variant field attributes - [x] ~~Add support for custom enum variant attributes (maybe?)~~ ~~Will require a larger refactor. Can be saved for a future PR if we really want it.~~ Actually, we apparently still have support for variant attributes despite not using them, so it was pretty easy to add lol. - [x] Add support for custom container attributes - [x] Allow custom attributes to store any reflectable value (not just `Lit`) - [x] ~~Store attributes in registry~~ This PR used to store these in attributes in the registry, however, it has since switched over to storing them in type info - [x] Add example ## Bikeshedding > [!note] > This section was made for the old method of handling custom attributes, which stored them by name (i.e. `some_attribute = 123`). The PR has shifted away from that, to a more type-safe approach. > > This section has been left for reference. There are a number of ways we can syntactically handle custom attributes. Feel free to leave a comment on your preferred one! Ideally we want one that is clear, readable, and concise since these will potentially see _a lot_ of use. Below is a small, non-exhaustive list of them. Note that the `skip_serializing` reflection attribute is added to demonstrate how each case plays with existing reflection attributes. <details> <summary>List</summary> ##### 1. `@(name = value)` > The `@` was chosen to make them stand out from other attributes and because the "at" symbol is a subtle pneumonic for "attribute". Of course, other symbols could be used (e.g. `$`, `#`, etc.). ```rust #[derive(Reflect)] struct Slider { #[reflect(@(min = 0.0, max = 1.0), skip_serializing)] #[[reflect(@(bevy_editor::hint = "Range: 0.0 to 1.0"))] value: f32, } ``` ##### 2. `@name = value` > This is my personal favorite. ```rust #[derive(Reflect)] struct Slider { #[reflect(@min = 0.0, @max = 1.0, skip_serializing)] #[[reflect(@bevy_editor::hint = "Range: 0.0 to 1.0")] value: f32, } ``` ##### 3. `custom_attr(name = value)` > `custom_attr` can be anything. Other possibilities include `with` or `tag`. ```rust #[derive(Reflect)] struct Slider { #[reflect(custom_attr(min = 0.0, max = 1.0), skip_serializing)] #[[reflect(custom_attr(bevy_editor::hint = "Range: 0.0 to 1.0"))] value: f32, } ``` ##### 4. `reflect_attr(name = value)` ```rust #[derive(Reflect)] struct Slider { #[reflect(skip_serializing)] #[reflect_attr(min = 0.0, max = 1.0)] #[[reflect_attr(bevy_editor::hint = "Range: 0.0 to 1.0")] value: f32, } ``` </details> --- ## Changelog - Added support for custom attributes on reflected types (i.e. `#[reflect(@Foo::new("bar")]`)
2024-05-20 19:30:21 +00:00
/// Generates a `TokenStream` for `VariantInfo` construction.
pub fn to_info_tokens(&self, bevy_reflect_path: &Path) -> proc_macro2::TokenStream {
let variant_name = &self.data.ident.to_string();
let (info_variant, info_struct) = match &self.fields {
EnumVariantFields::Unit => (
Ident::new("Unit", Span::call_site()),
Ident::new("UnitVariantInfo", Span::call_site()),
),
EnumVariantFields::Unnamed(..) => (
Ident::new("Tuple", Span::call_site()),
Ident::new("TupleVariantInfo", Span::call_site()),
),
EnumVariantFields::Named(..) => (
Ident::new("Struct", Span::call_site()),
Ident::new("StructVariantInfo", Span::call_site()),
),
};
let fields = self
.active_fields()
.map(|field| field.to_info_tokens(bevy_reflect_path));
let args = match &self.fields {
EnumVariantFields::Unit => quote!(#variant_name),
_ => {
quote!( #variant_name , &[#(#fields),*] )
}
};
let custom_attributes = self.attrs.custom_attributes.to_tokens(bevy_reflect_path);
#[allow(unused_mut)] // Needs mutability for the feature gate
let mut info = quote! {
#bevy_reflect_path::#info_struct::new(#args)
.with_custom_attributes(#custom_attributes)
};
#[cfg(feature = "documentation")]
{
let docs = &self.doc;
info.extend(quote! {
.with_docs(#docs)
});
}
quote! {
#bevy_reflect_path::VariantInfo::#info_variant(#info)
}
}
bevy_reflect: Reflect enums (#4761) # Objective > This is a revival of #1347. Credit for the original PR should go to @Davier. Currently, enums are treated as `ReflectRef::Value` types by `bevy_reflect`. Obviously, there needs to be better a better representation for enums using the reflection API. ## Solution Based on prior work from @Davier, an `Enum` trait has been added as well as the ability to automatically implement it via the `Reflect` derive macro. This allows enums to be expressed dynamically: ```rust #[derive(Reflect)] enum Foo { A, B(usize), C { value: f32 }, } let mut foo = Foo::B(123); assert_eq!("B", foo.variant_name()); assert_eq!(1, foo.field_len()); let new_value = DynamicEnum::from(Foo::C { value: 1.23 }); foo.apply(&new_value); assert_eq!(Foo::C{value: 1.23}, foo); ``` ### Features #### Derive Macro Use the `#[derive(Reflect)]` macro to automatically implement the `Enum` trait for enum definitions. Optionally, you can use `#[reflect(ignore)]` with both variants and variant fields, just like you can with structs. These ignored items will not be considered as part of the reflection and cannot be accessed via reflection. ```rust #[derive(Reflect)] enum TestEnum { A, // Uncomment to ignore all of `B` // #[reflect(ignore)] B(usize), C { // Uncomment to ignore only field `foo` of `C` // #[reflect(ignore)] foo: f32, bar: bool, }, } ``` #### Dynamic Enums Enums may be created/represented dynamically via the `DynamicEnum` struct. The main purpose of this struct is to allow enums to be deserialized into a partial state and to allow dynamic patching. In order to ensure conversion from a `DynamicEnum` to a concrete enum type goes smoothly, be sure to add `FromReflect` to your derive macro. ```rust let mut value = TestEnum::A; // Create from a concrete instance let dyn_enum = DynamicEnum::from(TestEnum::B(123)); value.apply(&dyn_enum); assert_eq!(TestEnum::B(123), value); // Create a purely dynamic instance let dyn_enum = DynamicEnum::new("TestEnum", "A", ()); value.apply(&dyn_enum); assert_eq!(TestEnum::A, value); ``` #### Variants An enum value is always represented as one of its variants— never the enum in its entirety. ```rust let value = TestEnum::A; assert_eq!("A", value.variant_name()); // Since we are using the `A` variant, we cannot also be the `B` variant assert_ne!("B", value.variant_name()); ``` All variant types are representable within the `Enum` trait: unit, struct, and tuple. You can get the current type like: ```rust match value.variant_type() { VariantType::Unit => println!("A unit variant!"), VariantType::Struct => println!("A struct variant!"), VariantType::Tuple => println!("A tuple variant!"), } ``` > Notice that they don't contain any values representing the fields. These are purely tags. If a variant has them, you can access the fields as well: ```rust let mut value = TestEnum::C { foo: 1.23, bar: false }; // Read/write specific fields *value.field_mut("bar").unwrap() = true; // Iterate over the entire collection of fields for field in value.iter_fields() { println!("{} = {:?}", field.name(), field.value()); } ``` #### Variant Swapping It might seem odd to group all variant types under a single trait (why allow `iter_fields` on a unit variant?), but the reason this was done ~~is to easily allow *variant swapping*.~~ As I was recently drafting up the **Design Decisions** section, I discovered that other solutions could have been made to work with variant swapping. So while there are reasons to keep the all-in-one approach, variant swapping is _not_ one of them. ```rust let mut value: Box<dyn Enum> = Box::new(TestEnum::A); value.set(Box::new(TestEnum::B(123))).unwrap(); ``` #### Serialization Enums can be serialized and deserialized via reflection without needing to implement `Serialize` or `Deserialize` themselves (which can save thousands of lines of generated code). Below are the ways an enum can be serialized. > Note, like the rest of reflection-based serialization, the order of the keys in these representations is important! ##### Unit ```json { "type": "my_crate::TestEnum", "enum": { "variant": "A" } } ``` ##### Tuple ```json { "type": "my_crate::TestEnum", "enum": { "variant": "B", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` <details> <summary>Effects on Option</summary> This ends up making `Option` look a little ugly: ```json { "type": "core::option::Option<usize>", "enum": { "variant": "Some", "tuple": [ { "type": "usize", "value": 123 } ] } } ``` </details> ##### Struct ```json { "type": "my_crate::TestEnum", "enum": { "variant": "C", "struct": { "foo": { "type": "f32", "value": 1.23 }, "bar": { "type": "bool", "value": false } } } } ``` ## Design Decisions <details> <summary><strong>View Section</strong></summary> This section is here to provide some context for why certain decisions were made for this PR, alternatives that could have been used instead, and what could be improved upon in the future. ### Variant Representation One of the biggest decisions was to decide on how to represent variants. The current design uses a "all-in-one" design where unit, tuple, and struct variants are all simultaneously represented by the `Enum` trait. This is not the only way it could have been done, though. #### Alternatives ##### 1. Variant Traits One way of representing variants would be to define traits for each variant, implementing them whenever an enum featured at least one instance of them. This would allow us to define variants like: ```rust pub trait Enum: Reflect { fn variant(&self) -> Variant; } pub enum Variant<'a> { Unit, Tuple(&'a dyn TupleVariant), Struct(&'a dyn StructVariant), } pub trait TupleVariant { fn field_len(&self) -> usize; // ... } ``` And then do things like: ```rust fn get_tuple_len(foo: &dyn Enum) -> usize { match foo.variant() { Variant::Tuple(tuple) => tuple.field_len(), _ => panic!("not a tuple variant!") } } ``` The reason this PR does not go with this approach is because of the fact that variants are not separate types. In other words, we cannot implement traits on specific variants— these cover the *entire* enum. This means we offer an easy footgun: ```rust let foo: Option<i32> = None; let my_enum = Box::new(foo) as Box<dyn TupleVariant>; ``` Here, `my_enum` contains `foo`, which is a unit variant. However, since we need to implement `TupleVariant` for `Option` as a whole, it's possible to perform such a cast. This is obviously wrong, but could easily go unnoticed. So unfortunately, this makes it not a good candidate for representing variants. ##### 2. Variant Structs To get around the issue of traits necessarily needing to apply to both the enum and its variants, we could instead use structs that are created on a per-variant basis. This was also considered but was ultimately [[removed](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c)](https://github.com/bevyengine/bevy/pull/4761/commits/71d27ab3c6871bb188d8b46512db3b0922a56a0c) due to concerns about allocations. Each variant struct would probably look something like: ```rust pub trait Enum: Reflect { fn variant_mut(&self) -> VariantMut; } pub enum VariantMut<'a> { Unit, Tuple(TupleVariantMut), Struct(StructVariantMut), } struct StructVariantMut<'a> { fields: Vec<&'a mut dyn Reflect>, field_indices: HashMap<Cow<'static, str>, usize> } ``` This allows us to isolate struct variants into their own defined struct and define methods specifically for their use. It also prevents users from casting to it since it's not a trait. However, this is not an optimal solution. Both `field_indices` and `fields` will require an allocation (remember, a `Box<[T]>` still requires a `Vec<T>` in order to be constructed). This *might* be a problem if called frequently enough. ##### 3. Generated Structs The original design, implemented by @Davier, instead generates structs specific for each variant. So if we had a variant path like `Foo::Bar`, we'd generate a struct named `FooBarWrapper`. This would be newtyped around the original enum and forward tuple or struct methods to the enum with the chosen variant. Because it involved using the `Tuple` and `Struct` traits (which are also both bound on `Reflect`), this meant a bit more code had to be generated. For a single struct variant with one field, the generated code amounted to ~110LoC. However, each new field added to that variant only added ~6 more LoC. In order to work properly, the enum had to be transmuted to the generated struct: ```rust fn variant(&self) -> crate::EnumVariant<'_> { match self { Foo::Bar {value: i32} => { let wrapper_ref = unsafe { std::mem::transmute::<&Self, &FooBarWrapper>(self) }; crate::EnumVariant::Struct(wrapper_ref as &dyn crate::Struct) } } } ``` This works because `FooBarWrapper` is defined as `repr(transparent)`. Out of all the alternatives, this would probably be the one most likely to be used again in the future. The reasons for why this PR did not continue to use it was because: * To reduce generated code (which would hopefully speed up compile times) * To avoid cluttering the code with generated structs not visible to the user * To keep bevy_reflect simple and extensible (these generated structs act as proxies and might not play well with current or future systems) * To avoid additional unsafe blocks * My own misunderstanding of @Davier's code That last point is obviously on me. I misjudged the code to be too unsafe and unable to handle variant swapping (which it probably could) when I was rebasing it. Looking over it again when writing up this whole section, I see that it was actually a pretty clever way of handling variant representation. #### Benefits of All-in-One As stated before, the current implementation uses an all-in-one approach. All variants are capable of containing fields as far as `Enum` is concerned. This provides a few benefits that the alternatives do not (reduced indirection, safer code, etc.). The biggest benefit, though, is direct field access. Rather than forcing users to have to go through pattern matching, we grant direct access to the fields contained by the current variant. The reason we can do this is because all of the pattern matching happens internally. Getting the field at index `2` will automatically return `Some(...)` for the current variant if it has a field at that index or `None` if it doesn't (or can't). This could be useful for scenarios where the variant has already been verified or just set/swapped (or even where the type of variant doesn't matter): ```rust let dyn_enum: &mut dyn Enum = &mut Foo::Bar {value: 123}; // We know it's the `Bar` variant let field = dyn_enum.field("value").unwrap(); ``` Reflection is not a type-safe abstraction— almost every return value is wrapped in `Option<...>`. There are plenty of places to check and recheck that a value is what Reflect says it is. Forcing users to have to go through `match` each time they want to access a field might just be an extra step among dozens of other verification processes. Some might disagree, but ultimately, my view is that the benefit here is an improvement to the ergonomics and usability of reflected enums. </details> --- ## Changelog ### Added * Added `Enum` trait * Added `Enum` impl to `Reflect` derive macro * Added `DynamicEnum` struct * Added `DynamicVariant` * Added `EnumInfo` * Added `VariantInfo` * Added `StructVariantInfo` * Added `TupleVariantInfo` * Added `UnitVariantInfo` * Added serializtion/deserialization support for enums * Added `EnumSerializer` * Added `VariantType` * Added `VariantFieldIter` * Added `VariantField` * Added `enum_partial_eq(...)` * Added `enum_hash(...)` ### Changed * `Option<T>` now implements `Enum` * `bevy_window` now depends on `bevy_reflect` * Implemented `Reflect` and `FromReflect` for `WindowId` * Derive `FromReflect` on `PerspectiveProjection` * Derive `FromReflect` on `OrthographicProjection` * Derive `FromReflect` on `WindowOrigin` * Derive `FromReflect` on `ScalingMode` * Derive `FromReflect` on `DepthCalculation` ## Migration Guide * Enums no longer need to be treated as values and usages of `#[reflect_value(...)]` can be removed or replaced by `#[reflect(...)]` * Enums (including `Option<T>`) now take a different format when serializing. The format is described above, but this may cause issues for existing scenes that make use of enums. --- Also shout out to @nicopap for helping clean up some of the code here! It's a big feature so help like this is really appreciated! Co-authored-by: Gino Valente <gino.valente.code@gmail.com>
2022-08-02 22:14:41 +00:00
}
reflect: stable type path v2 (#7184) # Objective - Introduce a stable alternative to [`std::any::type_name`](https://doc.rust-lang.org/std/any/fn.type_name.html). - Rewrite of #5805 with heavy inspiration in design. - On the path to #5830. - Part of solving #3327. ## Solution - Add a `TypePath` trait for static stable type path/name information. - Add a `TypePath` derive macro. - Add a `impl_type_path` macro for implementing internal and foreign types in `bevy_reflect`. --- ## Changelog - Added `TypePath` trait. - Added `DynamicTypePath` trait and `get_type_path` method to `Reflect`. - Added a `TypePath` derive macro. - Added a `bevy_reflect::impl_type_path` for implementing `TypePath` on internal and foreign types in `bevy_reflect`. - Changed `bevy_reflect::utility::(Non)GenericTypeInfoCell` to `(Non)GenericTypedCell<T>` which allows us to be generic over both `TypeInfo` and `TypePath`. - `TypePath` is now a supertrait of `Asset`, `Material` and `Material2d`. - `impl_reflect_struct` needs a `#[type_path = "..."]` attribute to be specified. - `impl_reflect_value` needs to either specify path starting with a double colon (`::core::option::Option`) or an `in my_crate::foo` declaration. - Added `bevy_reflect_derive::ReflectTypePath`. - Most uses of `Ident` in `bevy_reflect_derive` changed to use `ReflectTypePath`. ## Migration Guide - Implementors of `Asset`, `Material` and `Material2d` now also need to derive `TypePath`. - Manual implementors of `Reflect` will need to implement the new `get_type_path` method. ## Open Questions - [x] ~This PR currently does not migrate any usages of `std::any::type_name` to use `bevy_reflect::TypePath` to ease the review process. Should it?~ Migration will be left to a follow-up PR. - [ ] This PR adds a lot of `#[derive(TypePath)]` and `T: TypePath` to satisfy new bounds, mostly when deriving `TypeUuid`. Should we make `TypePath` a supertrait of `TypeUuid`? [Should we remove `TypeUuid` in favour of `TypePath`?](https://github.com/bevyengine/bevy/pull/5805/files/2afbd855327c4b68e0a6b6f03118f289988441a4#r961067892)
2023-06-05 20:31:20 +00:00
/// Represents a path to a type.
///
/// This is used over [`struct@Ident`] or [`Path`]
/// to have the correct semantics for [deriving `TypePath`].
///
/// The type can always be reached with its [`ToTokens`] implementation.
///
/// The [`short_type_path`], [`type_ident`], [`crate_name`], and [`module_path`] methods
/// have corresponding methods on the `TypePath` trait.
/// [`long_type_path`] corresponds to the `type_path` method on `TypePath`.
///
/// [deriving `TypePath`]: crate::derive_type_path
/// [`long_type_path`]: ReflectTypePath::long_type_path
/// [`short_type_path`]: ReflectTypePath::short_type_path
/// [`type_ident`]: ReflectTypePath::type_ident
/// [`crate_name`]: ReflectTypePath::crate_name
/// [`module_path`]: ReflectTypePath::module_path
///
/// # Example
///
/// ```ignore (bevy_reflect is not accessible from this crate)
reflect: stable type path v2 (#7184) # Objective - Introduce a stable alternative to [`std::any::type_name`](https://doc.rust-lang.org/std/any/fn.type_name.html). - Rewrite of #5805 with heavy inspiration in design. - On the path to #5830. - Part of solving #3327. ## Solution - Add a `TypePath` trait for static stable type path/name information. - Add a `TypePath` derive macro. - Add a `impl_type_path` macro for implementing internal and foreign types in `bevy_reflect`. --- ## Changelog - Added `TypePath` trait. - Added `DynamicTypePath` trait and `get_type_path` method to `Reflect`. - Added a `TypePath` derive macro. - Added a `bevy_reflect::impl_type_path` for implementing `TypePath` on internal and foreign types in `bevy_reflect`. - Changed `bevy_reflect::utility::(Non)GenericTypeInfoCell` to `(Non)GenericTypedCell<T>` which allows us to be generic over both `TypeInfo` and `TypePath`. - `TypePath` is now a supertrait of `Asset`, `Material` and `Material2d`. - `impl_reflect_struct` needs a `#[type_path = "..."]` attribute to be specified. - `impl_reflect_value` needs to either specify path starting with a double colon (`::core::option::Option`) or an `in my_crate::foo` declaration. - Added `bevy_reflect_derive::ReflectTypePath`. - Most uses of `Ident` in `bevy_reflect_derive` changed to use `ReflectTypePath`. ## Migration Guide - Implementors of `Asset`, `Material` and `Material2d` now also need to derive `TypePath`. - Manual implementors of `Reflect` will need to implement the new `get_type_path` method. ## Open Questions - [x] ~This PR currently does not migrate any usages of `std::any::type_name` to use `bevy_reflect::TypePath` to ease the review process. Should it?~ Migration will be left to a follow-up PR. - [ ] This PR adds a lot of `#[derive(TypePath)]` and `T: TypePath` to satisfy new bounds, mostly when deriving `TypeUuid`. Should we make `TypePath` a supertrait of `TypeUuid`? [Should we remove `TypeUuid` in favour of `TypePath`?](https://github.com/bevyengine/bevy/pull/5805/files/2afbd855327c4b68e0a6b6f03118f289988441a4#r961067892)
2023-06-05 20:31:20 +00:00
/// # use syn::parse_quote;
/// # use bevy_reflect_derive::ReflectTypePath;
/// let path: syn::Path = parse_quote!(::core::marker::PhantomData)?;
///
/// let type_path = ReflectTypePath::External {
/// path,
/// custom_path: None,
/// };
///
/// // Equivalent to "core::marker".
reflect: stable type path v2 (#7184) # Objective - Introduce a stable alternative to [`std::any::type_name`](https://doc.rust-lang.org/std/any/fn.type_name.html). - Rewrite of #5805 with heavy inspiration in design. - On the path to #5830. - Part of solving #3327. ## Solution - Add a `TypePath` trait for static stable type path/name information. - Add a `TypePath` derive macro. - Add a `impl_type_path` macro for implementing internal and foreign types in `bevy_reflect`. --- ## Changelog - Added `TypePath` trait. - Added `DynamicTypePath` trait and `get_type_path` method to `Reflect`. - Added a `TypePath` derive macro. - Added a `bevy_reflect::impl_type_path` for implementing `TypePath` on internal and foreign types in `bevy_reflect`. - Changed `bevy_reflect::utility::(Non)GenericTypeInfoCell` to `(Non)GenericTypedCell<T>` which allows us to be generic over both `TypeInfo` and `TypePath`. - `TypePath` is now a supertrait of `Asset`, `Material` and `Material2d`. - `impl_reflect_struct` needs a `#[type_path = "..."]` attribute to be specified. - `impl_reflect_value` needs to either specify path starting with a double colon (`::core::option::Option`) or an `in my_crate::foo` declaration. - Added `bevy_reflect_derive::ReflectTypePath`. - Most uses of `Ident` in `bevy_reflect_derive` changed to use `ReflectTypePath`. ## Migration Guide - Implementors of `Asset`, `Material` and `Material2d` now also need to derive `TypePath`. - Manual implementors of `Reflect` will need to implement the new `get_type_path` method. ## Open Questions - [x] ~This PR currently does not migrate any usages of `std::any::type_name` to use `bevy_reflect::TypePath` to ease the review process. Should it?~ Migration will be left to a follow-up PR. - [ ] This PR adds a lot of `#[derive(TypePath)]` and `T: TypePath` to satisfy new bounds, mostly when deriving `TypeUuid`. Should we make `TypePath` a supertrait of `TypeUuid`? [Should we remove `TypeUuid` in favour of `TypePath`?](https://github.com/bevyengine/bevy/pull/5805/files/2afbd855327c4b68e0a6b6f03118f289988441a4#r961067892)
2023-06-05 20:31:20 +00:00
/// let module_path = type_path.module_path();
/// # Ok::<(), syn::Error>(())
/// ```
///
pub(crate) enum ReflectTypePath<'a> {
/// Types without a crate/module that can be named from any scope (e.g. `bool`).
Primitive(&'a Ident),
/// Using `::my_crate::foo::Bar` syntax.
///
/// May have a separate custom path used for the `TypePath` implementation.
reflect: stable type path v2 (#7184) # Objective - Introduce a stable alternative to [`std::any::type_name`](https://doc.rust-lang.org/std/any/fn.type_name.html). - Rewrite of #5805 with heavy inspiration in design. - On the path to #5830. - Part of solving #3327. ## Solution - Add a `TypePath` trait for static stable type path/name information. - Add a `TypePath` derive macro. - Add a `impl_type_path` macro for implementing internal and foreign types in `bevy_reflect`. --- ## Changelog - Added `TypePath` trait. - Added `DynamicTypePath` trait and `get_type_path` method to `Reflect`. - Added a `TypePath` derive macro. - Added a `bevy_reflect::impl_type_path` for implementing `TypePath` on internal and foreign types in `bevy_reflect`. - Changed `bevy_reflect::utility::(Non)GenericTypeInfoCell` to `(Non)GenericTypedCell<T>` which allows us to be generic over both `TypeInfo` and `TypePath`. - `TypePath` is now a supertrait of `Asset`, `Material` and `Material2d`. - `impl_reflect_struct` needs a `#[type_path = "..."]` attribute to be specified. - `impl_reflect_value` needs to either specify path starting with a double colon (`::core::option::Option`) or an `in my_crate::foo` declaration. - Added `bevy_reflect_derive::ReflectTypePath`. - Most uses of `Ident` in `bevy_reflect_derive` changed to use `ReflectTypePath`. ## Migration Guide - Implementors of `Asset`, `Material` and `Material2d` now also need to derive `TypePath`. - Manual implementors of `Reflect` will need to implement the new `get_type_path` method. ## Open Questions - [x] ~This PR currently does not migrate any usages of `std::any::type_name` to use `bevy_reflect::TypePath` to ease the review process. Should it?~ Migration will be left to a follow-up PR. - [ ] This PR adds a lot of `#[derive(TypePath)]` and `T: TypePath` to satisfy new bounds, mostly when deriving `TypeUuid`. Should we make `TypePath` a supertrait of `TypeUuid`? [Should we remove `TypeUuid` in favour of `TypePath`?](https://github.com/bevyengine/bevy/pull/5805/files/2afbd855327c4b68e0a6b6f03118f289988441a4#r961067892)
2023-06-05 20:31:20 +00:00
External {
path: &'a Path,
custom_path: Option<Path>,
generics: &'a Generics,
},
/// The name of a type relative to its scope.
///
/// The type must be able to be reached with just its name.
///
/// May have a separate alias path used for the `TypePath` implementation.
reflect: stable type path v2 (#7184) # Objective - Introduce a stable alternative to [`std::any::type_name`](https://doc.rust-lang.org/std/any/fn.type_name.html). - Rewrite of #5805 with heavy inspiration in design. - On the path to #5830. - Part of solving #3327. ## Solution - Add a `TypePath` trait for static stable type path/name information. - Add a `TypePath` derive macro. - Add a `impl_type_path` macro for implementing internal and foreign types in `bevy_reflect`. --- ## Changelog - Added `TypePath` trait. - Added `DynamicTypePath` trait and `get_type_path` method to `Reflect`. - Added a `TypePath` derive macro. - Added a `bevy_reflect::impl_type_path` for implementing `TypePath` on internal and foreign types in `bevy_reflect`. - Changed `bevy_reflect::utility::(Non)GenericTypeInfoCell` to `(Non)GenericTypedCell<T>` which allows us to be generic over both `TypeInfo` and `TypePath`. - `TypePath` is now a supertrait of `Asset`, `Material` and `Material2d`. - `impl_reflect_struct` needs a `#[type_path = "..."]` attribute to be specified. - `impl_reflect_value` needs to either specify path starting with a double colon (`::core::option::Option`) or an `in my_crate::foo` declaration. - Added `bevy_reflect_derive::ReflectTypePath`. - Most uses of `Ident` in `bevy_reflect_derive` changed to use `ReflectTypePath`. ## Migration Guide - Implementors of `Asset`, `Material` and `Material2d` now also need to derive `TypePath`. - Manual implementors of `Reflect` will need to implement the new `get_type_path` method. ## Open Questions - [x] ~This PR currently does not migrate any usages of `std::any::type_name` to use `bevy_reflect::TypePath` to ease the review process. Should it?~ Migration will be left to a follow-up PR. - [ ] This PR adds a lot of `#[derive(TypePath)]` and `T: TypePath` to satisfy new bounds, mostly when deriving `TypeUuid`. Should we make `TypePath` a supertrait of `TypeUuid`? [Should we remove `TypeUuid` in favour of `TypePath`?](https://github.com/bevyengine/bevy/pull/5805/files/2afbd855327c4b68e0a6b6f03118f289988441a4#r961067892)
2023-06-05 20:31:20 +00:00
///
/// Module and crate are found with [`module_path!()`](module_path),
reflect: stable type path v2 (#7184) # Objective - Introduce a stable alternative to [`std::any::type_name`](https://doc.rust-lang.org/std/any/fn.type_name.html). - Rewrite of #5805 with heavy inspiration in design. - On the path to #5830. - Part of solving #3327. ## Solution - Add a `TypePath` trait for static stable type path/name information. - Add a `TypePath` derive macro. - Add a `impl_type_path` macro for implementing internal and foreign types in `bevy_reflect`. --- ## Changelog - Added `TypePath` trait. - Added `DynamicTypePath` trait and `get_type_path` method to `Reflect`. - Added a `TypePath` derive macro. - Added a `bevy_reflect::impl_type_path` for implementing `TypePath` on internal and foreign types in `bevy_reflect`. - Changed `bevy_reflect::utility::(Non)GenericTypeInfoCell` to `(Non)GenericTypedCell<T>` which allows us to be generic over both `TypeInfo` and `TypePath`. - `TypePath` is now a supertrait of `Asset`, `Material` and `Material2d`. - `impl_reflect_struct` needs a `#[type_path = "..."]` attribute to be specified. - `impl_reflect_value` needs to either specify path starting with a double colon (`::core::option::Option`) or an `in my_crate::foo` declaration. - Added `bevy_reflect_derive::ReflectTypePath`. - Most uses of `Ident` in `bevy_reflect_derive` changed to use `ReflectTypePath`. ## Migration Guide - Implementors of `Asset`, `Material` and `Material2d` now also need to derive `TypePath`. - Manual implementors of `Reflect` will need to implement the new `get_type_path` method. ## Open Questions - [x] ~This PR currently does not migrate any usages of `std::any::type_name` to use `bevy_reflect::TypePath` to ease the review process. Should it?~ Migration will be left to a follow-up PR. - [ ] This PR adds a lot of `#[derive(TypePath)]` and `T: TypePath` to satisfy new bounds, mostly when deriving `TypeUuid`. Should we make `TypePath` a supertrait of `TypeUuid`? [Should we remove `TypeUuid` in favour of `TypePath`?](https://github.com/bevyengine/bevy/pull/5805/files/2afbd855327c4b68e0a6b6f03118f289988441a4#r961067892)
2023-06-05 20:31:20 +00:00
/// if there is no custom path specified.
Internal {
ident: &'a Ident,
custom_path: Option<Path>,
generics: &'a Generics,
},
/// Any [`Type`] with only a defined `type_path` and `short_type_path`.
reflect: stable type path v2 (#7184) # Objective - Introduce a stable alternative to [`std::any::type_name`](https://doc.rust-lang.org/std/any/fn.type_name.html). - Rewrite of #5805 with heavy inspiration in design. - On the path to #5830. - Part of solving #3327. ## Solution - Add a `TypePath` trait for static stable type path/name information. - Add a `TypePath` derive macro. - Add a `impl_type_path` macro for implementing internal and foreign types in `bevy_reflect`. --- ## Changelog - Added `TypePath` trait. - Added `DynamicTypePath` trait and `get_type_path` method to `Reflect`. - Added a `TypePath` derive macro. - Added a `bevy_reflect::impl_type_path` for implementing `TypePath` on internal and foreign types in `bevy_reflect`. - Changed `bevy_reflect::utility::(Non)GenericTypeInfoCell` to `(Non)GenericTypedCell<T>` which allows us to be generic over both `TypeInfo` and `TypePath`. - `TypePath` is now a supertrait of `Asset`, `Material` and `Material2d`. - `impl_reflect_struct` needs a `#[type_path = "..."]` attribute to be specified. - `impl_reflect_value` needs to either specify path starting with a double colon (`::core::option::Option`) or an `in my_crate::foo` declaration. - Added `bevy_reflect_derive::ReflectTypePath`. - Most uses of `Ident` in `bevy_reflect_derive` changed to use `ReflectTypePath`. ## Migration Guide - Implementors of `Asset`, `Material` and `Material2d` now also need to derive `TypePath`. - Manual implementors of `Reflect` will need to implement the new `get_type_path` method. ## Open Questions - [x] ~This PR currently does not migrate any usages of `std::any::type_name` to use `bevy_reflect::TypePath` to ease the review process. Should it?~ Migration will be left to a follow-up PR. - [ ] This PR adds a lot of `#[derive(TypePath)]` and `T: TypePath` to satisfy new bounds, mostly when deriving `TypeUuid`. Should we make `TypePath` a supertrait of `TypeUuid`? [Should we remove `TypeUuid` in favour of `TypePath`?](https://github.com/bevyengine/bevy/pull/5805/files/2afbd855327c4b68e0a6b6f03118f289988441a4#r961067892)
2023-06-05 20:31:20 +00:00
#[allow(dead_code)]
// Not currently used but may be useful in the future due to its generality.
Anonymous {
qualified_type: Type,
long_type_path: StringExpr,
short_type_path: StringExpr,
},
}
impl<'a> ReflectTypePath<'a> {
/// Returns the path interpreted as an [`struct@Ident`].
///
/// Returns [`None`] if [anonymous].
///
/// [anonymous]: ReflectTypePath::Anonymous
pub fn get_ident(&self) -> Option<&Ident> {
match self {
Self::Internal {
ident, custom_path, ..
} => Some(
custom_path
.as_ref()
.map(|path| &path.segments.last().unwrap().ident)
.unwrap_or(ident),
),
Self::External {
path, custom_path, ..
} => Some(
&custom_path
.as_ref()
.unwrap_or(path)
.segments
.last()
.unwrap()
.ident,
),
Self::Primitive(ident) => Some(ident),
_ => None,
}
}
/// The generics associated with the type.
///
/// Empty if [anonymous] or [primitive].
///
/// [primitive]: ReflectTypePath::Primitive
/// [anonymous]: ReflectTypePath::Anonymous
pub fn generics(&self) -> &'a Generics {
// Use a constant because we need to return a reference of at least 'a.
const EMPTY_GENERICS: &Generics = &Generics {
gt_token: None,
lt_token: None,
where_clause: None,
params: Punctuated::new(),
};
bevy_reflect: Type parameter bounds (#9046) # Objective Fixes #8965. #### Background For convenience and to ensure everything is setup properly, we automatically add certain bounds to the derived types. The current implementation does this by taking the types from all active fields and adding them to the where-clause of the generated impls. I believe this method was chosen because it won't add bounds to types that are otherwise ignored. ```rust #[derive(Reflect)] struct Foo<T, U: SomeTrait, V> { t: T, u: U::Assoc, #[reflect(ignore)] v: [V; 2] } // Generates something like: impl<T, U: SomeTrait, V> for Foo<T, U, V> where // Active: T: Reflect, U::Assoc: Reflect, // Ignored: [V; 2]: Send + Sync + Any { // ... } ``` The self-referential type fails because it ends up using _itself_ as a type bound due to being one of its own active fields. ```rust #[derive(Reflect)] struct Foo { foo: Vec<Foo> } // Foo where Vec<Foo>: Reflect -> Vec<T> where T: Reflect -> Foo where Vec<Foo>: Reflect -> ... ``` ## Solution We can't simply parse all field types for the name of our type. That would be both complex and prone to errors and false-positives. And even if it wasn't, what would we replace the bound with? Instead, I opted to go for a solution that only adds the bounds to what really needs it: the type parameters. While the bounds on concrete types make errors a bit cleaner, they aren't strictly necessary. This means we can change our generated where-clause to only add bounds to generic type parameters. Doing this, though, returns us back to the problem of over-bounding parameters that don't need to be bounded. To solve this, I added a new container attribute (based on [this](https://github.com/dtolnay/syn/issues/422#issuecomment-406882925) comment and @nicopap's [comment](https://github.com/bevyengine/bevy/pull/9046#issuecomment-1623593780)) that allows us to pass in a custom where clause to modify what bounds are added to these type parameters. This allows us to do stuff like: ```rust trait Trait { type Assoc; } // We don't need `T` to be reflectable since we only care about `T::Assoc`. #[derive(Reflect)] #[reflect(where T::Assoc: FromReflect)] struct Foo<T: Trait>(T::Assoc); #[derive(TypePath)] struct Bar; impl Trait for Bar { type Assoc = usize; } #[derive(Reflect)] struct Baz { a: Foo<Bar>, } ``` > **Note** > I also [tried](https://github.com/bevyengine/bevy/commit/dc139ea34c4737da3ea7ab7ea2e8701462693d02) allowing `#[reflect(ignore)]` to be used on the type parameters themselves, but that proved problematic since the derive macro does not consume the attribute. This is why I went with the container attribute approach. ### Alternatives One alternative could possibly be to just not add reflection bounds automatically (i.e. only add required bounds like `Send`, `Sync`, `Any`, and `TypePath`). The downside here is we add more friction to using reflection, which already comes with its own set of considerations. This is a potentially viable option, but we really need to consider whether or not the ergonomics hit is worth it. If we did decide to go the more manual route, we should at least consider something like #5772 to make it easier for users to add the right bounds (although, this could still become tricky with `FromReflect` also being automatically derived). ### Open Questions 1. Should we go with this approach or the manual alternative? 2. ~~Should we add a `skip_params` attribute to avoid the `T: 'static` trick?~~ ~~Decided to go with `custom_where()` as it's the simplest~~ Scratch that, went with a normal where clause 3. ~~`custom_where` bikeshedding?~~ No longer needed since we are using a normal where clause ### TODO - [x] Add compile-fail tests --- ## Changelog - Fixed issue preventing recursive types from deriving `Reflect` - Changed how where-clause bounds are generated by the `Reflect` derive macro - They are now only applied to the type parameters, not to all active fields - Added `#[reflect(where T: Trait, U::Assoc: Trait, ...)]` container attribute ## Migration Guide When deriving `Reflect`, generic type params that do not need the automatic reflection bounds (such as `Reflect`) applied to them will need to opt-out using a custom where clause like: `#[reflect(where T: Trait, U::Assoc: Trait, ...)]`. The attribute can define custom bounds only used by the reflection impls. To simply opt-out all the type params, we can pass in an empty where clause: `#[reflect(where)]`. ```rust // BEFORE: #[derive(Reflect)] struct Foo<T>(#[reflect(ignore)] T); // AFTER: #[derive(Reflect)] #[reflect(where)] struct Foo<T>(#[reflect(ignore)] T); ``` --------- Co-authored-by: Nicola Papale <nicopap@users.noreply.github.com>
2024-01-28 16:24:03 +00:00
reflect: stable type path v2 (#7184) # Objective - Introduce a stable alternative to [`std::any::type_name`](https://doc.rust-lang.org/std/any/fn.type_name.html). - Rewrite of #5805 with heavy inspiration in design. - On the path to #5830. - Part of solving #3327. ## Solution - Add a `TypePath` trait for static stable type path/name information. - Add a `TypePath` derive macro. - Add a `impl_type_path` macro for implementing internal and foreign types in `bevy_reflect`. --- ## Changelog - Added `TypePath` trait. - Added `DynamicTypePath` trait and `get_type_path` method to `Reflect`. - Added a `TypePath` derive macro. - Added a `bevy_reflect::impl_type_path` for implementing `TypePath` on internal and foreign types in `bevy_reflect`. - Changed `bevy_reflect::utility::(Non)GenericTypeInfoCell` to `(Non)GenericTypedCell<T>` which allows us to be generic over both `TypeInfo` and `TypePath`. - `TypePath` is now a supertrait of `Asset`, `Material` and `Material2d`. - `impl_reflect_struct` needs a `#[type_path = "..."]` attribute to be specified. - `impl_reflect_value` needs to either specify path starting with a double colon (`::core::option::Option`) or an `in my_crate::foo` declaration. - Added `bevy_reflect_derive::ReflectTypePath`. - Most uses of `Ident` in `bevy_reflect_derive` changed to use `ReflectTypePath`. ## Migration Guide - Implementors of `Asset`, `Material` and `Material2d` now also need to derive `TypePath`. - Manual implementors of `Reflect` will need to implement the new `get_type_path` method. ## Open Questions - [x] ~This PR currently does not migrate any usages of `std::any::type_name` to use `bevy_reflect::TypePath` to ease the review process. Should it?~ Migration will be left to a follow-up PR. - [ ] This PR adds a lot of `#[derive(TypePath)]` and `T: TypePath` to satisfy new bounds, mostly when deriving `TypeUuid`. Should we make `TypePath` a supertrait of `TypeUuid`? [Should we remove `TypeUuid` in favour of `TypePath`?](https://github.com/bevyengine/bevy/pull/5805/files/2afbd855327c4b68e0a6b6f03118f289988441a4#r961067892)
2023-06-05 20:31:20 +00:00
match self {
Self::Internal { generics, .. } | Self::External { generics, .. } => generics,
_ => EMPTY_GENERICS,
}
}
/// Whether an implementation of `Typed` or `TypePath` should be generic.
///
/// Returning true that it should use a `GenericTypeCell` in its implementation.
pub fn impl_is_generic(&self) -> bool {
// Whether to use `GenericTypeCell` is not dependent on lifetimes
// (which all have to be 'static anyway).
!self
.generics()
.params
.iter()
.all(|param| matches!(param, GenericParam::Lifetime(_)))
}
/// Returns the path interpreted as a [`Path`].
///
/// Returns [`None`] if [anonymous], [primitive],
/// or a [`ReflectTypePath::Internal`] without a custom path.
///
/// [primitive]: ReflectTypePath::Primitive
/// [anonymous]: ReflectTypePath::Anonymous
pub fn get_path(&self) -> Option<&Path> {
match self {
Self::Internal { custom_path, .. } => custom_path.as_ref(),
Self::External {
path, custom_path, ..
} => Some(custom_path.as_ref().unwrap_or(path)),
_ => None,
}
}
/// Returns whether this [internal] or [external] path has a custom path.
///
/// [internal]: ReflectTypePath::Internal
/// [external]: ReflectTypePath::External
pub fn has_custom_path(&self) -> bool {
match self {
Self::Internal { custom_path, .. } | Self::External { custom_path, .. } => {
custom_path.is_some()
}
_ => false,
}
}
/// Returns a [`StringExpr`] representing the name of the type's crate.
///
/// Returns [`None`] if the type is [primitive] or [anonymous].
///
/// For non-customised [internal] paths this is created from [`module_path`].
///
/// For `Option<PhantomData>`, this is `"core"`.
///
/// [primitive]: ReflectTypePath::Primitive
/// [anonymous]: ReflectTypePath::Anonymous
/// [internal]: ReflectTypePath::Internal
pub fn crate_name(&self) -> Option<StringExpr> {
if let Some(path) = self.get_path() {
let crate_name = &path.segments.first().unwrap().ident;
return Some(StringExpr::from(crate_name));
}
match self {
Self::Internal { .. } => Some(StringExpr::Borrowed(quote! {
::core::module_path!()
.split(':')
.next()
.unwrap()
})),
Self::External { .. } => unreachable!(),
_ => None,
}
}
/// Combines type generics and const generics into one [`StringExpr`].
///
/// This string can be used with a `GenericTypePathCell` in a `TypePath` implementation.
///
/// The `ty_generic_fn` param maps [`TypeParam`]s to [`StringExpr`]s.
fn reduce_generics(
generics: &Generics,
mut ty_generic_fn: impl FnMut(&TypeParam) -> StringExpr,
) -> StringExpr {
let mut params = generics.params.iter().filter_map(|param| match param {
GenericParam::Type(type_param) => Some(ty_generic_fn(type_param)),
GenericParam::Const(const_param) => {
let ident = &const_param.ident;
let ty = &const_param.ty;
Some(StringExpr::Owned(quote! {
<#ty as ::std::string::ToString>::to_string(&#ident)
}))
}
GenericParam::Lifetime(_) => None,
});
params
.next()
.into_iter()
.chain(params.flat_map(|x| [StringExpr::from_str(", "), x]))
.collect()
}
/// Returns a [`StringExpr`] representing the "type path" of the type.
///
/// For `Option<PhantomData>`, this is `"core::option::Option<core::marker::PhantomData>"`.
pub fn long_type_path(&self, bevy_reflect_path: &Path) -> StringExpr {
match self {
Self::Primitive(ident) => StringExpr::from(ident),
Self::Anonymous { long_type_path, .. } => long_type_path.clone(),
Self::Internal { generics, .. } | Self::External { generics, .. } => {
let ident = self.type_ident().unwrap();
let module_path = self.module_path().unwrap();
if self.impl_is_generic() {
let generics = ReflectTypePath::reduce_generics(
generics,
|TypeParam { ident, .. }| {
StringExpr::Borrowed(quote! {
<#ident as #bevy_reflect_path::TypePath>::type_path()
})
},
);
StringExpr::from_iter([
module_path,
StringExpr::from_str("::"),
ident,
StringExpr::from_str("<"),
generics,
StringExpr::from_str(">"),
])
} else {
StringExpr::from_iter([module_path, StringExpr::from_str("::"), ident])
}
}
}
}
/// Returns a [`StringExpr`] representing the "short path" of the type.
///
/// For `Option<PhantomData>`, this is `"Option<PhantomData>"`.
pub fn short_type_path(&self, bevy_reflect_path: &Path) -> StringExpr {
match self {
Self::Anonymous {
short_type_path, ..
} => short_type_path.clone(),
Self::Primitive(ident) => StringExpr::from(ident),
Self::External { generics, .. } | Self::Internal { generics, .. } => {
let ident = self.type_ident().unwrap();
if self.impl_is_generic() {
let generics = ReflectTypePath::reduce_generics(
generics,
|TypeParam { ident, .. }| {
StringExpr::Borrowed(quote! {
<#ident as #bevy_reflect_path::TypePath>::short_type_path()
})
},
);
StringExpr::from_iter([
ident,
StringExpr::from_str("<"),
generics,
StringExpr::from_str(">"),
])
} else {
ident
}
}
}
}
/// Returns a [`StringExpr`] representing the path to the module
/// that the type is in.
///
/// Returns [`None`] if the type is [primitive] or [anonymous].
///
/// For non-customised [internal] paths this is created from [`module_path`].
///
/// For `Option<PhantomData>`, this is `"core::option"`.
///
/// [primitive]: ReflectTypePath::Primitive
/// [anonymous]: ReflectTypePath::Anonymous
/// [internal]: ReflectTypePath::Internal
pub fn module_path(&self) -> Option<StringExpr> {
if let Some(path) = self.get_path() {
let path_string = path
.segments
.pairs()
.take(path.segments.len() - 1)
.map(|pair| pair.value().ident.to_string())
.reduce(|path, ident| path + "::" + &ident)
.unwrap();
let path_lit = LitStr::new(&path_string, path.span());
return Some(StringExpr::from_lit(&path_lit));
}
match self {
Self::Internal { .. } => Some(StringExpr::Const(quote! {
::core::module_path!()
})),
Self::External { .. } => unreachable!(),
_ => None,
}
}
/// Returns a [`StringExpr`] representing the type's final ident.
///
/// Returns [`None`] if the type is [anonymous].
///
/// This is not necessarily a valid qualified path to the type.
///
/// For `Option<PhantomData>`, this is `"Option"`.
///
/// [anonymous]: ReflectTypePath::Anonymous
pub fn type_ident(&self) -> Option<StringExpr> {
self.get_ident().map(StringExpr::from)
}
bevy_reflect: Reflect remote types (#6042) # Objective The goal with this PR is to allow the use of types that don't implement `Reflect` within the reflection API. Rust's [orphan rule](https://doc.rust-lang.org/book/ch10-02-traits.html#implementing-a-trait-on-a-type) prevents implementing a trait on an external type when neither type nor trait are owned by the implementor. This means that if a crate, `cool_rust_lib`, defines a type, `Foo`, then a user cannot use it with reflection. What this means is that we have to ignore it most of the time: ```rust #[derive(Reflect)] struct SomeStruct { #[reflect(ignore)] data: cool_rust_lib::Foo } ``` Obviously, it's impossible to implement `Reflect` on `Foo`. But does it *have* to be? Most of reflection doesn't deal with concrete types— it's almost all using `dyn Reflect`. And being very metadata-driven, it should theoretically be possible. I mean, [`serde`](https://serde.rs/remote-derive.html) does it. ## Solution > Special thanks to @danielhenrymantilla for their help reviewing this PR and offering wisdom wrt safety. Taking a page out of `serde`'s book, this PR adds the ability to easily use "remote types" with reflection. In this context, a "remote type" is the external type for which we have no ability to implement `Reflect`. This adds the `#[reflect_remote(...)]` attribute macro, which is used to generate "remote type wrappers". All you have to do is define the wrapper exactly the same as the remote type's definition: ```rust // Pretend this is our external crate mod cool_rust_lib { #[derive(Default)] struct Foo { pub value: String } } #[reflect_remote(cool_rust_lib::Foo)] struct FooWrapper { pub value: String } ``` > **Note:** All fields in the external type *must* be public. This could be addressed with a separate getter/setter attribute either in this PR or in another one. The macro takes this user-defined item and transforms it into a newtype wrapper around the external type, marking it as `#[repr(transparent)]`. The fields/variants defined by the user are simply used to build out the reflection impls. Additionally, it generates an implementation of the new trait, `ReflectRemote`, which helps prevent accidental misuses of this API. Therefore, the output generated by the macro would look something like: ```rust #[repr(transparent)] struct FooWrapper(pub cool_rust_lib::Foo); impl ReflectRemote for FooWrapper { type Remote = cool_rust_lib::Foo; // transmutation methods... } // reflection impls... // these will acknowledge and make use of the `value` field ``` Internally, the reflection API will pass around the `FooWrapper` and [transmute](https://doc.rust-lang.org/std/mem/fn.transmute.html) it where necessary. All we have to do is then tell `Reflect` to do that. So rather than ignoring the field, we tell `Reflect` to use our wrapper using the `#[reflect(remote = ...)]` field attribute: ```rust #[derive(Reflect)] struct SomeStruct { #[reflect(remote = FooWrapper)] data: cool_rust_lib::Foo } ``` #### Other Macros & Type Data Because this macro consumes the defined item and generates a new one, we can't just put our macros anywhere. All macros that should be passed to the generated struct need to come *below* this macro. For example, to derive `Default` and register its associated type data: ```rust // ✅ GOOD #[reflect_remote(cool_rust_lib::Foo)] #[derive(Default)] #[reflect(Default)] struct FooWrapper { pub value: String } // ❌ BAD #[derive(Default)] #[reflect_remote(cool_rust_lib::Foo)] #[reflect(Default)] struct FooWrapper { pub value: String } ``` #### Generics Generics are forwarded to the generated struct as well. They should also be defined in the same order: ```rust #[reflect_remote(RemoteGeneric<'a, T1, T2>)] struct GenericWrapper<'a, T1, T2> { pub foo: &'a T1, pub bar: &'a T2, } ``` > Naming does *not* need to match the original definition's. Only order matters here. > Also note that the code above is just a demonstration and doesn't actually compile since we'd need to enforce certain bounds (e.g. `T1: Reflect`, `'a: 'static`, etc.) #### Nesting And, yes, you can nest remote types: ```rust #[reflect_remote(RemoteOuter)] struct OuterWrapper { #[reflect(remote = InnerWrapper)] pub inner: RemoteInner } #[reflect_remote(RemoteInner)] struct InnerWrapper(usize); ``` #### Assertions This macro will also generate some compile-time assertions to ensure that the correct types are used. It's important we catch this early so users don't have to wait for something to panic. And it also helps keep our `unsafe` a little safer. For example, a wrapper definition that does not match its corresponding remote type will result in an error: ```rust mod external_crate { pub struct TheirStruct(pub u32); } #[reflect_remote(external_crate::TheirStruct)] struct MyStruct(pub String); // ERROR: expected type `u32` but found `String` ``` <details> <summary>Generated Assertion</summary> ```rust const _: () = { #[allow(non_snake_case)] #[allow(unused_variables)] #[allow(unused_assignments)] #[allow(unreachable_patterns)] #[allow(clippy::multiple_bound_locations)] fn assert_wrapper_definition_matches_remote_type( mut __remote__: external_crate::TheirStruct, ) { __remote__.0 = (|| -> ::core::option::Option<String> { None })().unwrap(); } }; ``` </details> Additionally, using the incorrect type in a `#[reflect(remote = ...)]` attribute should result in an error: ```rust mod external_crate { pub struct TheirFoo(pub u32); pub struct TheirBar(pub i32); } #[reflect_remote(external_crate::TheirFoo)] struct MyFoo(pub u32); #[reflect_remote(external_crate::TheirBar)] struct MyBar(pub i32); #[derive(Reflect)] struct MyStruct { #[reflect(remote = MyBar)] // ERROR: expected type `TheirFoo` but found struct `TheirBar` foo: external_crate::TheirFoo } ``` <details> <summary>Generated Assertion</summary> ```rust const _: () = { struct RemoteFieldAssertions; impl RemoteFieldAssertions { #[allow(non_snake_case)] #[allow(clippy::multiple_bound_locations)] fn assert__foo__is_valid_remote() { let _: <MyBar as bevy_reflect::ReflectRemote>::Remote = (|| -> ::core::option::Option<external_crate::TheirFoo> { None })().unwrap(); } } }; ``` </details> ### Discussion There are a couple points that I think still need discussion or validation. - [x] 1. `Any` shenanigans ~~If we wanted to downcast our remote type from a `dyn Reflect`, we'd have to first downcast to the wrapper then extract the inner type. This PR has a [commit](b840db9f74cb6d357f951cb11b150d46bac89ee2) that addresses this by making all the `Reflect::*any` methods return the inner type rather than the wrapper type. This allows us to downcast directly to our remote type.~~ ~~However, I'm not sure if this is something we want to do. For unknowing users, it could be confusing and seemingly inconsistent. Is it worth keeping? Or should this behavior be removed?~~ I think this should be fine. The remote wrapper is an implementation detail and users should not need to downcast to the wrapper type. Feel free to let me know if there are other opinions on this though! - [x] 2. Implementing `Deref/DerefMut` and `From` ~~We don't currently do this, but should we implement other traits on the generated transparent struct? We could implement `Deref`/`DerefMut` to easily access the inner type. And we could implement `From` for easier conversion between the two types (e.g. `T: Into<Foo>`).~~ As mentioned in the comments, we probably don't need to do this. Again, the remote wrapper is an implementation detail, and should generally not be used directly. - [x] 3. ~~Should we define a getter/setter field attribute in this PR as well or leave it for a future one?~~ I think this should be saved for a future PR - [ ] 4. Any foreseeable issues with this implementation? #### Alternatives One alternative to defining our own `ReflectRemote` would be to use [bytemuck's `TransparentWrapper`](https://docs.rs/bytemuck/1.13.1/bytemuck/trait.TransparentWrapper.html) (as suggested by @danielhenrymantilla). This is definitely a viable option, as `ReflectRemote` is pretty much the same thing as `TransparentWrapper`. However, the cost would be bringing in a new crate— though, it is already in use in a few other sub-crates like bevy_render. I think we're okay just defining `ReflectRemote` ourselves, but we can go the bytemuck route if we'd prefer offloading that work to another crate. --- ## Changelog * Added the `#[reflect_remote(...)]` attribute macro to allow `Reflect` to be used on remote types * Added `ReflectRemote` trait for ensuring proper remote wrapper usage
2024-08-12 19:12:53 +00:00
/// Returns the true type regardless of whether a custom path is specified.
///
/// To get the custom path if there is one, use [`Self::get_path`].
///
/// For example, the type `Foo<T: Debug>` would return `Foo<T>`.
pub fn true_type(&self) -> proc_macro2::TokenStream {
match self {
Self::Primitive(ident) => quote!(#ident),
Self::Internal {
ident, generics, ..
} => {
let (_, ty_generics, _) = generics.split_for_impl();
quote!(#ident #ty_generics)
}
Self::External { path, generics, .. } => {
let (_, ty_generics, _) = generics.split_for_impl();
quote!(#path #ty_generics)
}
Self::Anonymous { qualified_type, .. } => qualified_type.to_token_stream(),
}
}
reflect: stable type path v2 (#7184) # Objective - Introduce a stable alternative to [`std::any::type_name`](https://doc.rust-lang.org/std/any/fn.type_name.html). - Rewrite of #5805 with heavy inspiration in design. - On the path to #5830. - Part of solving #3327. ## Solution - Add a `TypePath` trait for static stable type path/name information. - Add a `TypePath` derive macro. - Add a `impl_type_path` macro for implementing internal and foreign types in `bevy_reflect`. --- ## Changelog - Added `TypePath` trait. - Added `DynamicTypePath` trait and `get_type_path` method to `Reflect`. - Added a `TypePath` derive macro. - Added a `bevy_reflect::impl_type_path` for implementing `TypePath` on internal and foreign types in `bevy_reflect`. - Changed `bevy_reflect::utility::(Non)GenericTypeInfoCell` to `(Non)GenericTypedCell<T>` which allows us to be generic over both `TypeInfo` and `TypePath`. - `TypePath` is now a supertrait of `Asset`, `Material` and `Material2d`. - `impl_reflect_struct` needs a `#[type_path = "..."]` attribute to be specified. - `impl_reflect_value` needs to either specify path starting with a double colon (`::core::option::Option`) or an `in my_crate::foo` declaration. - Added `bevy_reflect_derive::ReflectTypePath`. - Most uses of `Ident` in `bevy_reflect_derive` changed to use `ReflectTypePath`. ## Migration Guide - Implementors of `Asset`, `Material` and `Material2d` now also need to derive `TypePath`. - Manual implementors of `Reflect` will need to implement the new `get_type_path` method. ## Open Questions - [x] ~This PR currently does not migrate any usages of `std::any::type_name` to use `bevy_reflect::TypePath` to ease the review process. Should it?~ Migration will be left to a follow-up PR. - [ ] This PR adds a lot of `#[derive(TypePath)]` and `T: TypePath` to satisfy new bounds, mostly when deriving `TypeUuid`. Should we make `TypePath` a supertrait of `TypeUuid`? [Should we remove `TypeUuid` in favour of `TypePath`?](https://github.com/bevyengine/bevy/pull/5805/files/2afbd855327c4b68e0a6b6f03118f289988441a4#r961067892)
2023-06-05 20:31:20 +00:00
}
impl<'a> ToTokens for ReflectTypePath<'a> {
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
match self {
Self::Internal { ident, .. } | Self::Primitive(ident) => ident.to_tokens(tokens),
Self::External { path, .. } => path.to_tokens(tokens),
Self::Anonymous { qualified_type, .. } => qualified_type.to_tokens(tokens),
}
}
}