mirror of
https://github.com/bevyengine/bevy
synced 2025-01-06 10:18:59 +00:00
70 commits
Author | SHA1 | Message | Date | |
---|---|---|---|---|
David Cosby
|
42b737878f
|
Re-export smallvec crate from bevy_utils (#11006)
Matches versioning & features from other Cargo.toml files in the project. # Objective Resolves #10932 ## Solution Added smallvec to the bevy_utils cargo.toml and added a line to re-export the crate. Target version and features set to match what's used in the other bevy crates. |
||
davier
|
55402bdf2e
|
Fix debug printing for dynamic types (#10740)
# Objective Printing `DynamicStruct` with a debug format does not show the contained type anymore. For instance, in `examples/reflection/reflection.rs`, adding `dbg!(&reflect_value);` to line 96 will print: ```rust [examples/reflection/reflection.rs:96] &reflect_value = DynamicStruct(bevy_reflect::DynamicStruct { a: 4, nested: DynamicStruct(bevy_reflect::DynamicStruct { b: 8, }), }) ``` ## Solution Show the represented type instead (`reflection::Foo` and `reflection::Bar` in this case): ```rust [examples/reflection/reflection.rs:96] &reflect_value = DynamicStruct(reflection::Foo { a: 4, nested: DynamicStruct(reflection::Bar { b: 8, }), }) ``` --------- Co-authored-by: Gino Valente <49806985+MrGVSV@users.noreply.github.com> |
||
Stepan Koltsov
|
506bdc5e68
|
Remove pointless trait implementation exports in bevy_reflect (#10771)
Trait implementations do not need to be reexported to be used. ``` warning: unused import: `self::std::*` --> crates/bevy_reflect/src/lib.rs:502:13 | 502 | pub use self::std::*; | ^^^^^^^^^^^^ | = note: `#[warn(unused_imports)]` on by default warning: unused import: `self::uuid::*` --> crates/bevy_reflect/src/lib.rs:503:13 | 503 | pub use self::uuid::*; | ^^^^^^^^^^^^^ warning: unused import: `impls::*` --> crates/bevy_reflect/src/lib.rs:525:9 | 525 | pub use impls::*; | ^^^^^^^^ ``` |
||
tygyh
|
fd308571c4
|
Remove unnecessary path prefixes (#10749)
# Objective - Shorten paths by removing unnecessary prefixes ## Solution - Remove the prefixes from many paths which do not need them. Finding the paths was done automatically using built-in refactoring tools in Jetbrains RustRover. |
||
Gino Valente
|
13f2749021
|
bevy_utils: Export generate_composite_uuid utility function (#10496)
# Objective The `generate_composite_uuid` utility function hidden in `bevy_reflect::__macro_exports` could be generally useful to users. For example, I previously relied on `Hash` to generate a `u64` to create a deterministic `HandleId`. In v0.12, `HandleId` has been replaced by `AssetId` which now requires a `Uuid`, which I could generate with this function. ## Solution Relocate `generate_composite_uuid` from `bevy_reflect::__macro_exports` to `bevy_utils::uuid`. It is still re-exported under `bevy_reflect::__macro_exports` so there should not be any breaking changes (although, users should generally not rely on pseudo-private/hidden modules like `__macro_exports`). I chose to keep it in `bevy_reflect::__macro_exports` so as to not clutter up our public API and to reduce the number of changes in this PR. We could have also marked the export as `#[doc(hidden)]`, but personally I like that we have a dedicated module for this (makes it clear what is public and what isn't when just looking at the macro code). --- ## Changelog - Moved `generate_composite_uuid` to `bevy_utils::uuid` and made it public - Note: it was technically already public, just hidden |
||
Ame
|
951c9bb1a2
|
Add [lints] table, fix adding #![allow(clippy::type_complexity)] everywhere (#10011)
# Objective - Fix adding `#![allow(clippy::type_complexity)]` everywhere. like #9796 ## Solution - Use the new [lints] table that will land in 1.74 (https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#lints) - inherit lint to the workspace, crates and examples. ``` [lints] workspace = true ``` ## Changelog - Bump rust version to 1.74 - Enable lints table for the workspace ```toml [workspace.lints.clippy] type_complexity = "allow" ``` - Allow type complexity for all crates and examples ```toml [lints] workspace = true ``` --------- Co-authored-by: Martín Maita <47983254+mnmaita@users.noreply.github.com> |
||
Gino Valente
|
60773e6787
|
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. |
||
Trashtalk217
|
e5f5ce5e97
|
Migrate Quat reflection strategy from "value" to "struct" (#10068)
Adopted from #8954, co-authored by @pyrotechnick # Objective The Bevy ecosystem currently reflects `Quat` via "value" rather than the more appropriate "struct" strategy. This behaviour is inconsistent to that of similar types, i.e. `Vec3`. Additionally, employing the "value" strategy causes instances of `Quat` to be serialised as a sequence `[x, y, z, w]` rather than structures of shape `{ x, y, z, w }`. The [comments surrounding the applicable code]( |
||
radiish
|
262846e702
|
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`](
|
||
Christian Hughes
|
f8fd93f418
|
Add TypePath to the prelude (#9963)
# Objective In order to derive `Asset`s (v2), `TypePath` must also be implemented. `TypePath` is not currently in the prelude, but given it is *required* when deriving something that *is* in the prelude, I think it deserves to be added. ## Solution Add `TypePath` to `bevy_reflect::prelude`. |
||
Nicola Papale
|
7083f0d593
|
Make it so ParsedPath can be passed to GetPath (#9373)
# Objective - Unify the `ParsedPath` and `GetPath` APIs. They weirdly didn't play well together. - Make `ParsedPath` and `GetPath` API easier to use ## Solution - Add the `ReflectPath` trait. - `GetPath` methods now accept an `impl ReflectPath<'a>` instead of a `&'a str`, this mean it also can accepts a `&ParsedPath` - Make `GetPath: Reflect` and use default impl for `Reflect` types. - Add `GetPath` and `ReflectPath` to the `bevy_reflect` prelude --- ## Changelog - Add the `ReflectPath` trait. - `GetPath` methods now accept an `impl ReflectPath<'a>` instead of a `&'a str`, this mean it also can accept a `&ParsedPath` - Make `GetPath: Reflect` and use default impl for `Reflect` types. - Add `GetPath` and `ReflectPath` to the `bevy_reflect` prelude ## Migration Guide `GetPath` now requires `Reflect`. This reduces a lot of boilerplate on bevy's side. If you were implementing manually `GetPath` on your own type, please get in touch! `ParsedPath::element[_mut]` isn't an inherent method of `ParsedPath`, you must now import `ReflectPath`. This is only relevant if you weren't importing the bevy prelude. ```diff -use bevy::reflect::ParsedPath; +use bevy::reflect::{ParsedPath, ReflectPath}; parsed_path.element(reflect_type).unwrap() parsed_path.element(reflect_type).unwrap() |
||
Gino Valente
|
f96cd758cd
|
bevy_reflect: Opt-out attribute for TypePath (#9140)
# Objective Fixes #9094 ## Solution Takes a bit from [this](https://github.com/bevyengine/bevy/issues/9094#issuecomment-1629333851) comment as well as a [comment](https://discord.com/channels/691052431525675048/1002362493634629796/1128024873260810271) from @soqb. This allows users to opt-out of the `TypePath` implementation that is automatically generated by the `Reflect` derive macro, allowing custom `TypePath` implementations. ```rust #[derive(Reflect)] #[reflect(type_path = false)] struct Foo<T> { #[reflect(ignore)] _marker: PhantomData<T>, } struct NotTypePath; impl<T: 'static> TypePath for Foo<T> { fn type_path() -> &'static str { std::any::type_name::<Self>() } fn short_type_path() -> &'static str { static CELL: GenericTypePathCell = GenericTypePathCell::new(); CELL.get_or_insert::<Self, _>(|| { bevy_utils::get_short_name(std::any::type_name::<Self>()) }) } fn crate_name() -> Option<&'static str> { Some("my_crate") } fn module_path() -> Option<&'static str> { Some("my_crate::foo") } fn type_ident() -> Option<&'static str> { Some("Foo") } } // Can use `TypePath` let _ = <Foo<NotTypePath> as TypePath>::type_path(); // Can register the type let mut registry = TypeRegistry::default(); registry.register::<Foo<NotTypePath>>(); ``` #### Type Path Stability The stability of type paths mainly come into play during serialization. If a type is moved between builds, an unstable type path may become invalid. Users that opt-out of `TypePath` and rely on something like `std::any::type_name` as in the example above, should be aware that this solution removes the stability guarantees. Deserialization thus expects that type to never move. If it does, then the serialized type paths will need to be updated accordingly. If a user depends on stability, they will need to implement that stability logic manually (probably by looking at the expanded output of a typical `Reflect`/`TypePath` derive). This could be difficult for type parameters that don't/can't implement `TypePath`, and will need to make heavy use of string parsing and manipulation to achieve the same effect (alternatively, they can choose to simply exclude any type parameter that doesn't implement `TypePath`). --- ## Changelog - Added the `#[reflect(type_path = false)]` attribute to opt out of the `TypePath` impl when deriving `Reflect` --------- Co-authored-by: Carter Anderson <mcanders1@gmail.com> |
||
Gino Valente
|
84f6b879ae
|
bevy_reflect: Fix combined field attributes (#9322)
# Objective It seems the behavior of field attributes was accidentally broken at some point. Take the following code: ```rust #[derive(Reflect)] struct Foo { #[reflect(ignore, default)] value: usize } ``` The above code should simply mark `value` as ignored and specify a default behavior. However, what this actually does is discard both. That's especially a problem when we don't want the field to be be given a `Reflect` or `FromReflect` bound (which is why we ignore it in the first place). This only happens when the attributes are combined into one. The following code works properly: ```rust #[derive(Reflect)] struct Foo { #[reflect(ignore)] #[reflect(default)] value: usize } ``` ## Solution Cleaned up the field attribute parsing logic to support combined field attributes. --- ## Changelog - Fixed a bug where `Reflect` derive attributes on fields are not able to be combined into a single attribute |
||
Nicola Papale
|
08ea1d18ae
|
Refactor path module of bevy_reflect (#8887)
# Objective - The `path` module was getting fairly large. - The code in `AccessRef::read_element` and mut equivalent was very complex and difficult to understand. - The `ReflectPathError` had a lot of variants, and was difficult to read. ## Solution - Split the file in two, `access` now has its own module - Rewrite the `read_element` methods, they were ~200 lines long, they are now ~70 lines long — I didn't change any of the logic. It's really just the same code, but error handling is separated. - Split the `ReflectPathError` error - Merge `AccessRef` and `Access` - A few other changes that aim to reduce code complexity ### Fully detailed change list - `Display` impl of `ParsedPath` now includes prefix dots — this allows simplifying its implementation, and IMO `.path.to.field` is a better way to express a "path" than `path.to.field` which could suggest we are reading the `to` field of a variable named `path` - Add a test to check that dot prefixes and other are correctly parsed — Until now, no test contained a prefixing dot - Merge `Access` and `AccessRef`, using a `Cow<'a, str>`. Generated code seems to agree with this decision (`ParsedPath::parse` sheds 5% of instructions) - Remove `Access::as_ref` since there is no such thing as an `AccessRef` anymore. - Rename `AccessRef::to_owned` into `AccessRef::into_owned()` since it takes ownership of `self` now. - Add a `parse_static` that doesn't allocate new strings for named fields! - Add a section about path reflection in the `bevy_reflect` crate root doc — I saw a few people that weren't aware of path reflection, so I thought it was pertinent to add it to the root doc - a lot of nits - rename `index` to `offset` when it refers to offset in the path string — There is no more confusion with the other kind of indices in this context, also it's a common naming convention for parsing. - Make a dedicated enum for parsing errors - rename the `read_element` methods to `element` — shorter, but also `read_element_mut` was a fairly poor name - The error values now not only contain the expected type but also the actual type. - Remove lifetimes that could be inferred from the `GetPath` trait methods. --- ## Change log - Added the `ParsedPath::parse_static` method, avoids allocating when parsing `&'static str`. ## Migration Guide If you were matching on the `Err(ReflectPathError)` value returned by `GetPath` and `ParsedPath` methods, now only the parse-related errors and the offset are publicly accessible. You can always use the `fmt::Display` to get a clear error message, but if you need programmatic access to the error types, please open an issue. --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> Co-authored-by: Gino Valente <49806985+MrGVSV@users.noreply.github.com> |
||
Gino Valente
|
aeeb20ec4c
|
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(®istry); let mut deserializer = ron:🇩🇪:Deserializer::from_str(input)?; let output: SomeStruct = reflect_deserializer.deserialize(&mut deserializer)?.take()?; // Returns the Dynamic type let reflect_deserializer = UntypedReflectDeserializer::new_dynamic(®istry); let mut deserializer = ron:🇩🇪: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(®istry); let mut deserializer = ron:🇩🇪: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(®istry); // NEW let reflect_deserializer = UntypedReflectDeserializer::new_dynamic(®istry); ``` </details> --------- Co-authored-by: Carter Anderson <mcanders1@gmail.com> |
||
radiish
|
e17fc53aa1
|
reflect: avoid deadlock in GenericTypeCell (#8957)
# Objective - There was a deadlock discovered in the implementation of `bevy_reflect::utility::GenericTypeCell`, when called on a recursive type, e.g. `Vec<Vec<VariableCurve>>` ## Solution - Drop the lock before calling the initialisation function, and then pick it up again afterwards. ## Additional Context - [Discussed on Discord](https://discord.com/channels/691052431525675048/1002362493634629796/1122706835284185108) |
||
Joaquín León
|
af4336c501
|
Reflect UUID (#8905)
For those who wish to be able to `#[reflect]` stuff using the `Uuid` type I'm very unfamiliar with the codebase, so please tell me if I'm missing something |
||
EliasPrescott
|
e6b655fb25
|
adding reflection for Cow<'static, [T]> (#7454)
# Objective - Implementing reflection for Cow<'static, [T]> - Hopefully fixes #7429 ## Solution - Implementing Reflect, Typed, GetTypeRegistration, and FromReflect for Cow<'static, [T]> --- ## Notes I have not used bevy_reflection much yet, so I may not fully understand all the use cases. This is also my first attempt at contributing, so I would appreciate any feedback or recommendations for changes. I tried to add cases for using Cow<'static, str> and Cow<'static, [u8]> to some of the bevy_reflect tests, but I can't guarantee those tests are comprehensive enough. --------- Co-authored-by: MinerSebas <66798382+MinerSebas@users.noreply.github.com> Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> |
||
Thierry Berger
|
b559e9b6b4
|
bevy_reflect: implement Reflect for SmolStr (#8771)
# Objective To upgrade winit's dependency, it's useful to reuse SmolStr, which replaces/improves the too restrictive Key letter enums. As Input<Key> is a resource it should implement Reflect through all its fields. ## Solution Add smol_str to bevy_reflect supported types, behind a feature flag. This PR blocks winit's upgrade PR: https://github.com/bevyengine/bevy/pull/8745. # Current state - I'm discovering bevy_reflect, I appreciate all feedbacks, and send me your nitpicks! - Lacking more tests --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> Co-authored-by: Gino Valente <49806985+MrGVSV@users.noreply.github.com> |
||
Jamie Ridding
|
1e97c79ec1
|
bevy_reflect: Disambiguate type bounds in where clauses. (#8761)
# Objective It was accidentally found that rustc is unable to parse certain constructs in `where` clauses properly. `bevy_reflect::Reflect`'s habit of copying and pasting the field types in a type's definition to its `where` clauses made it very easy to accidentally run into this behaviour - particularly with the construct ```rust where for<'a> fn(&'a T) -> &'a T: Trait1 + Trait2 ``` which was incorrectly parsed as ```rust where for<'a> (fn(&'a T) -> &'a T: Trait1 + Trait2) ^ ^ incorrect syntax grouping ``` instead of ```rust where (for<'a> fn(&'a T) -> &'a T): Trait1 + Trait2 ^ ^ correct syntax grouping ``` Fixes #8759 ## Solution This commit fixes the issue by inserting explicit parentheses to disambiguate types from their bound lists. |
||
radiish
|
1efc762924
|
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`?](
|
||
Gino Valente
|
6b292d4263
|
bevy_reflect: Allow #[reflect(default)] on enum variant fields (#8514)
# Objective When using `FromReflect`, fields can be optionally left out if they are marked with `#[reflect(default)]`. This is very handy for working with serialized data as giant structs only need to list a subset of defined fields in order to be constructed. <details> <summary>Example</summary> Take the following struct: ```rust #[derive(Reflect, FromReflect)] struct Foo { #[reflect(default)] a: usize, #[reflect(default)] b: usize, #[reflect(default)] c: usize, #[reflect(default)] d: usize, } ``` Since all the fields are default-able, we can successfully call `FromReflect` on deserialized data like: ```rust ( "foo::Foo": ( // Only set `b` and default the rest b: 123 ) ) ``` </details> Unfortunately, this does not work with fields in enum variants. Marking a variant field as `#[reflect(default)]` does nothing when calling `FromReflect`. ## Solution Allow enum variant fields to define a default value using `#[reflect(default)]`. ### `#[reflect(Default)]` One thing that structs and tuple structs can do is use their `Default` implementation when calling `FromReflect`. Adding `#[reflect(Default)]` to the struct or tuple struct both registers `ReflectDefault` and alters the `FromReflect` implementation to use `Default` to generate any missing fields. This works well enough for structs and tuple structs, but for enums it's not as simple. Since the `Default` implementation for an enum only covers a single variant, it's not as intuitive as to what the behavior will be. And (imo) it feels weird that we would be able to specify default values in this way for one variant but not the others. Because of this, I chose to not implement that behavior here. However, I'm open to adding it in if anyone feels otherwise. --- ## Changelog - Allow enum variant fields to define a default value using `#[reflect(default)]` |
||
François
|
0736195a1e
|
update syn, encase, glam and hexasphere (#8573)
# Objective - Fixes #8282 - Update `syn` to 2.0, `encase` to 0.6, `glam` to 0.24 and `hexasphere` to 9.0 Blocked ~~on https://github.com/teoxoy/encase/pull/42~~ and ~~on https://github.com/OptimisticPeach/hexasphere/pull/17~~ --------- Co-authored-by: Nicola Papale <nicopap@users.noreply.github.com> Co-authored-by: JoJoJet <21144246+JoJoJet@users.noreply.github.com> |
||
Gino Valente
|
75130bd5ec
|
bevy_reflect: Better proxies (#6971)
# Objective > This PR is based on discussion from #6601 The Dynamic types (e.g. `DynamicStruct`, `DynamicList`, etc.) act as both: 1. Dynamic containers which may hold any arbitrary data 2. Proxy types which may represent any other type Currently, the only way we can represent the proxy-ness of a Dynamic is by giving it a name. ```rust // This is just a dynamic container let mut data = DynamicStruct::default(); // This is a "proxy" data.set_name(std::any::type_name::<Foo>()); ``` This type name is the only way we check that the given Dynamic is a proxy of some other type. When we need to "assert the type" of a `dyn Reflect`, we call `Reflect::type_name` on it. However, because we're only using a string to denote the type, we run into a few gotchas and limitations. For example, hashing a Dynamic proxy may work differently than the type it proxies: ```rust #[derive(Reflect, Hash)] #[reflect(Hash)] struct Foo(i32); let concrete = Foo(123); let dynamic = concrete.clone_dynamic(); let concrete_hash = concrete.reflect_hash(); let dynamic_hash = dynamic.reflect_hash(); // The hashes are not equal because `concrete` uses its own `Hash` impl // while `dynamic` uses a reflection-based hashing algorithm assert_ne!(concrete_hash, dynamic_hash); ``` Because the Dynamic proxy only knows about the name of the type, it's unaware of any other information about it. This means it also differs on `Reflect::reflect_partial_eq`, and may include ignored or skipped fields in places the concrete type wouldn't. ## Solution Rather than having Dynamics pass along just the type name of proxied types, we can instead have them pass around the `TypeInfo`. Now all Dynamic types contain an `Option<&'static TypeInfo>` rather than a `String`: ```diff pub struct DynamicTupleStruct { - type_name: String, + represented_type: Option<&'static TypeInfo>, fields: Vec<Box<dyn Reflect>>, } ``` By changing `Reflect::get_type_info` to `Reflect::represented_type_info`, hopefully we make this behavior a little clearer. And to account for `None` values on these dynamic types, `Reflect::represented_type_info` now returns `Option<&'static TypeInfo>`. ```rust let mut data = DynamicTupleStruct::default(); // Not proxying any specific type assert!(dyn_tuple_struct.represented_type_info().is_none()); let type_info = <Foo as Typed>::type_info(); dyn_tuple_struct.set_represented_type(Some(type_info)); // Alternatively: // let dyn_tuple_struct = foo.clone_dynamic(); // Now we're proxying `Foo` assert!(dyn_tuple_struct.represented_type_info().is_some()); ``` This means that we can have full access to all the static type information for the proxied type. Future work would include transitioning more static type information (trait impls, attributes, etc.) over to the `TypeInfo` so it can actually be utilized by Dynamic proxies. ### Alternatives & Rationale > **Note** > These alternatives were written when this PR was first made using a `Proxy` trait. This trait has since been removed. <details> <summary>View</summary> #### Alternative: The `Proxy<T>` Approach I had considered adding something like a `Proxy<T>` type where `T` would be the Dynamic and would contain the proxied type information. This was nice in that it allows us to explicitly determine whether something is a proxy or not at a type level. `Proxy<DynamicStruct>` proxies a struct. Makes sense. The reason I didn't go with this approach is because (1) tuples, (2) complexity, and (3) `PartialReflect`. The `DynamicTuple` struct allows us to represent tuples at runtime. It also allows us to do something you normally can't with tuples: add new fields. Because of this, adding a field immediately invalidates the proxy (e.g. our info for `(i32, i32)` doesn't apply to `(i32, i32, NewField)`). By going with this PR's approach, we can just remove the type info on `DynamicTuple` when that happens. However, with the `Proxy<T>` approach, it becomes difficult to represent this behavior— we'd have to completely control how we access data for `T` for each `T`. Secondly, it introduces some added complexities (aside from the manual impls for each `T`). Does `Proxy<T>` impl `Reflect`? Likely yes, if we want to represent it as `dyn Reflect`. What `TypeInfo` do we give it? How would we forward reflection methods to the inner type (remember, we don't have specialization)? How do we separate this from Dynamic types? And finally, how do all this in a way that's both logical and intuitive for users? Lastly, introducing a `Proxy` trait rather than a `Proxy<T>` struct is actually more inline with the [Unique Reflect RFC](https://github.com/bevyengine/rfcs/pull/56). In a way, the `Proxy` trait is really one part of the `PartialReflect` trait introduced in that RFC (it's technically not in that RFC but it fits well with it), where the `PartialReflect` serves as a way for proxies to work _like_ concrete types without having full access to everything a concrete `Reflect` type can do. This would help bridge the gap between the current state of the crate and the implementation of that RFC. All that said, this is still a viable solution. If the community believes this is the better path forward, then we can do that instead. These were just my reasons for not initially going with it in this PR. #### Alternative: The Type Registry Approach The `Proxy` trait is great and all, but how does it solve the original problem? Well, it doesn't— yet! The goal would be to start moving information from the derive macro and its attributes to the generated `TypeInfo` since these are known statically and shouldn't change. For example, adding `ignored: bool` to `[Un]NamedField` or a list of impls. However, there is another way of storing this information. This is, of course, one of the uses of the `TypeRegistry`. If we're worried about Dynamic proxies not aligning with their concrete counterparts, we could move more type information to the registry and require its usage. For example, we could replace `Reflect::reflect_hash(&self)` with `Reflect::reflect_hash(&self, registry: &TypeRegistry)`. That's not the _worst_ thing in the world, but it is an ergonomics loss. Additionally, other attributes may have their own requirements, further restricting what's possible without the registry. The `Reflect::apply` method will require the registry as well now. Why? Well because the `map_apply` function used for the `Reflect::apply` impls on `Map` types depends on `Map::insert_boxed`, which (at least for `DynamicMap`) requires `Reflect::reflect_hash`. The same would apply when adding support for reflection-based diffing, which will require `Reflect::reflect_partial_eq`. Again, this is a totally viable alternative. I just chose not to go with it for the reasons above. If we want to go with it, then we can close this PR and we can pursue this alternative instead. #### Downsides Just to highlight a quick potential downside (likely needs more investigation): retrieving the `TypeInfo` requires acquiring a lock on the `GenericTypeInfoCell` used by the `Typed` impls for generic types (non-generic types use a `OnceBox which should be faster). I am not sure how much of a performance hit that is and will need to run some benchmarks to compare against. </details> ### Open Questions 1. Should we use `Cow<'static, TypeInfo>` instead? I think that might be easier for modding? Perhaps, in that case, we need to update `Typed::type_info` and friends as well? 2. Are the alternatives better than the approach this PR takes? Are there other alternatives? --- ## Changelog ### Changed - `Reflect::get_type_info` has been renamed to `Reflect::represented_type_info` - This method now returns `Option<&'static TypeInfo>` rather than just `&'static TypeInfo` ### Added - Added `Reflect::is_dynamic` method to indicate when a type is dynamic - Added a `set_represented_type` method on all dynamic types ### Removed - Removed `TypeInfo::Dynamic` (use `Reflect::is_dynamic` instead) - Removed `Typed` impls for all dynamic types ## Migration Guide - The Dynamic types no longer take a string type name. Instead, they require a static reference to `TypeInfo`: ```rust #[derive(Reflect)] struct MyTupleStruct(f32, f32); let mut dyn_tuple_struct = DynamicTupleStruct::default(); dyn_tuple_struct.insert(1.23_f32); dyn_tuple_struct.insert(3.21_f32); // BEFORE: let type_name = std::any::type_name::<MyTupleStruct>(); dyn_tuple_struct.set_name(type_name); // AFTER: let type_info = <MyTupleStruct as Typed>::type_info(); dyn_tuple_struct.set_represented_type(Some(type_info)); ``` - `Reflect::get_type_info` has been renamed to `Reflect::represented_type_info` and now also returns an `Option<&'static TypeInfo>` (instead of just `&'static TypeInfo`): ```rust // BEFORE: let info: &'static TypeInfo = value.get_type_info(); // AFTER: let info: &'static TypeInfo = value.represented_type_info().unwrap(); ``` - `TypeInfo::Dynamic` and `DynamicInfo` has been removed. Use `Reflect::is_dynamic` instead: ```rust // BEFORE: if matches!(value.get_type_info(), TypeInfo::Dynamic) { // ... } // AFTER: if value.is_dynamic() { // ... } ``` --------- Co-authored-by: radiish <cb.setho@gmail.com> |
||
Gino Valente
|
74d425263a
|
bevy_reflect: Add ReflectFromReflect to the prelude (#8496)
# Objective Considering that `FromReflect` is a very common trait to derive, it would make sense to include `ReflectFromReflect` in the `bevy_reflect` prelude so users don't need to import it separately. ## Solution Add `ReflectFromReflect` to the prelude. |
||
JoJoJet
|
3ead10a3e0
|
Suppress the clippy::type_complexity lint (#8313)
# Objective The clippy lint `type_complexity` is known not to play well with bevy. It frequently triggers when writing complex queries, and taking the lint's advice of using a type alias almost always just obfuscates the code with no benefit. Because of this, this lint is currently ignored in CI, but unfortunately it still shows up when viewing bevy code in an IDE. As someone who's made a fair amount of pull requests to this repo, I will say that this issue has been a consistent thorn in my side. Since bevy code is filled with spurious, ignorable warnings, it can be very difficult to spot the *real* warnings that must be fixed -- most of the time I just ignore all warnings, only to later find out that one of them was real after I'm done when CI runs. ## Solution Suppress this lint in all bevy crates. This was previously attempted in #7050, but the review process ended up making it more complicated than it needs to be and landed on a subpar solution. The discussion in https://github.com/rust-lang/rust-clippy/pull/10571 explores some better long-term solutions to this problem. Since there is no timeline on when these solutions may land, we should resolve this issue in the meantime by locally suppressing these lints. ### Unresolved issues Currently, these lints are not suppressed in our examples, since that would require suppressing the lint in every single source file. They are still ignored in CI. |
||
Gino Valente
|
cd1737ecca |
bevy_reflect: Improved documentation (#7148)
# Objective
`bevy_reflect` can be a moderately complex crate to try and understand. It has many moving parts, a handful of gotchas, and a few subtle contracts that aren't immediately obvious to users and even other contributors.
The current README does an okay job demonstrating how the crate can be used. However, the crate's actual documentation should give a better overview of the crate, its inner-workings, and show some of its own examples.
## Solution
Added crate-level documentation that attempts to summarize the main parts of `bevy_reflect` into small sections.
This PR also updates the documentation for:
- `Reflect`
- `FromReflect`
- The reflection subtraits
- Other important types and traits
- The reflection macros (including the derive macros)
- Crate features
### Open Questions
1. ~~Should I update the docs for the Dynamic types? I was originally going to, but I'm getting a little concerned about the size of this PR 😅~~ Decided to not do this in this PR. It'll be better served from its own PR.
2. Should derive macro documentation be moved to the trait itself? This could improve visibility and allow for better doc links, but could also clutter up the trait's documentation (as well as not being on the actual derive macro's documentation).
### TODO
- [ ] ~~Document Dynamic types (?)~~ I think this should be done in a separate PR.
- [x] Document crate features
- [x] Update docs for `GetTypeRegistration`
- [x] Update docs for `TypeRegistration`
- [x] Update docs for `derive_from_reflect`
- [x] Document `reflect_trait`
- [x] Document `impl_reflect_value`
- [x] Document `impl_from_reflect_value`
---
## Changelog
- Updated documentation across the `bevy_reflect` crate
- Removed `#[module]` helper attribute for `Reflect` derives (this is not currently used)
## Migration Guide
- Removed `#[module]` helper attribute for `Reflect` derives. If your code is relying on this attribute, please replace it with either `#[reflect]` or `#[reflect_value]` (dependent on use-case).
Co-authored-by: Gino Valente <49806985+MrGVSV@users.noreply.github.com>
|
||
dis-da-moe
|
8853bef6df |
implement TypeUuid for primitives and fix multiple-parameter generics having the same TypeUuid (#6633)
# Objective - Fixes #5432 - Fixes #6680 ## Solution - move code responsible for generating the `impl TypeUuid` from `type_uuid_derive` into a new function, `gen_impl_type_uuid`. - this allows the new proc macro, `impl_type_uuid`, to call the code for generation. - added struct `TypeUuidDef` and implemented `syn::Parse` to allow parsing of the input for the new macro. - finally, used the new macro `impl_type_uuid` to implement `TypeUuid` for the standard library (in `crates/bevy_reflect/src/type_uuid_impl.rs`). - fixes #6680 by doing a wrapping add of the param's index to its `TYPE_UUID` Co-authored-by: dis-da-moe <84386186+dis-da-moe@users.noreply.github.com> |
||
Gino Valente
|
724b36289c |
bevy_reflect: Decouple List and Array traits (#7467)
# Objective Resolves #7121 ## Solution Decouples `List` and `Array` by removing `Array` as a supertrait of `List`. Additionally, similar methods from `Array` have been added to `List` so that their usages can remain largely unchanged. #### Possible Alternatives ##### `Sequence` My guess for why we originally made `List` a subtrait of `Array` is that they share a lot of common operations. We could potentially move these overlapping methods to a `Sequence` (name taken from #7059) trait and make that a supertrait of both. This would allow functions to contain logic that simply operates on a sequence rather than "list vs array". However, this means that we'd need to add methods for converting to a `dyn Sequence`. It also might be confusing since we wouldn't add a `ReflectRef::Sequence` or anything like that. Is such a trait worth adding (either in this PR or a followup one)? --- ## Changelog - Removed `Array` as supertrait of `List` - Added methods to `List` that were previously provided by `Array` ## Migration Guide The `List` trait is no longer dependent on `Array`. Implementors of `List` can remove the `Array` impl and move its methods into the `List` impl (with only a couple tweaks). ```rust // BEFORE impl Array for Foo { fn get(&self, index: usize) -> Option<&dyn Reflect> {/* ... */} fn get_mut(&mut self, index: usize) -> Option<&mut dyn Reflect> {/* ... */} fn len(&self) -> usize {/* ... */} fn is_empty(&self) -> bool {/* ... */} fn iter(&self) -> ArrayIter {/* ... */} fn drain(self: Box<Self>) -> Vec<Box<dyn Reflect>> {/* ... */} fn clone_dynamic(&self) -> DynamicArray {/* ... */} } impl List for Foo { fn insert(&mut self, index: usize, element: Box<dyn Reflect>) {/* ... */} fn remove(&mut self, index: usize) -> Box<dyn Reflect> {/* ... */} fn push(&mut self, value: Box<dyn Reflect>) {/* ... */} fn pop(&mut self) -> Option<Box<dyn Reflect>> {/* ... */} fn clone_dynamic(&self) -> DynamicList {/* ... */} } // AFTER impl List for Foo { fn get(&self, index: usize) -> Option<&dyn Reflect> {/* ... */} fn get_mut(&mut self, index: usize) -> Option<&mut dyn Reflect> {/* ... */} fn insert(&mut self, index: usize, element: Box<dyn Reflect>) {/* ... */} fn remove(&mut self, index: usize) -> Box<dyn Reflect> {/* ... */} fn push(&mut self, value: Box<dyn Reflect>) {/* ... */} fn pop(&mut self) -> Option<Box<dyn Reflect>> {/* ... */} fn len(&self) -> usize {/* ... */} fn is_empty(&self) -> bool {/* ... */} fn iter(&self) -> ListIter {/* ... */} fn drain(self: Box<Self>) -> Vec<Box<dyn Reflect>> {/* ... */} fn clone_dynamic(&self) -> DynamicList {/* ... */} } ``` Some other small tweaks that will need to be made include: - Use `ListIter` for `List::iter` instead of `ArrayIter` (the return type from `Array::iter`) - Replace `array_hash` with `list_hash` in `Reflect::reflect_hash` for implementors of `List` |
||
Gino Valente
|
8cd59b6a03 |
bevy_reflect: Pre-parsed paths (#7321)
# Objective > ℹ️ **This is an adoption of #4081 by @james7132** Fixes #4080. Provide a way to pre-parse reflection paths so as to avoid having to parse at each call to `GetPath::path` (or similar method). ## Solution Adds the `ParsedPath` struct (named `FieldPath` in the original PR) that parses and caches the sequence of accesses to a reflected element. This is functionally similar to the `GetPath` trait, but removes the need to parse an unchanged path more than once. ### Additional Changes Included in this PR from the original is cleaner code as well as the introduction of a new pathing operation: field access by index. This allows struct and struct variant fields to be accessed in a more performant (albeit more fragile) way if needed. This operation is faster due to not having to perform string matching. As an example, if we wanted the third field on a struct, we'd write `#2`—where `#` denotes indexed access and `2` denotes the desired field index. This PR also contains improved documentation for `GetPath` and friends, including renaming some of the methods to be more clear to the end-user with a reduced risk of getting them mixed up. ### Future Work There are a few things that could be done as a separate PR (order doesn't matter— they could be followup PRs or done in parallel). These are: - [x] ~~Add support for `Tuple`. Currently, we hint that they work but they do not.~~ See #7324 - [ ] Cleanup `ReflectPathError`. I think it would be nicer to give `ReflectPathError` two variants: `ReflectPathError::ParseError` and `ReflectPathError::AccessError`, with all current variants placed within one of those two. It's not obvious when one might expect to receive one type of error over the other, so we can help by explicitly categorizing them. --- ## Changelog - Cleaned up `GetPath` logic - Added `ParsedPath` for cached reflection paths - Added new reflection path syntax: struct field access by index (example syntax: `foo#1`) - Renamed methods on `GetPath`: - `path` -> `reflect_path` - `path_mut` -> `reflect_path_mut` - `get_path` -> `path` - `get_path_mut` -> `path_mut` ## Migration Guide `GetPath` methods have been renamed according to the following: - `path` -> `reflect_path` - `path_mut` -> `reflect_path_mut` - `get_path` -> `path` - `get_path_mut` -> `path_mut` Co-authored-by: Gino Valente <gino.valente.code@gmail.com> |
||
Gino Valente
|
63f1a9dec8 |
bevy_reflect: Add ReflectFromReflect (v2) (#6245)
# Objective Resolves #4597 (based on the work from #6056 and a refresh of #4147) When using reflection, we may often end up in a scenario where we have a Dynamic representing a certain type. Unfortunately, we can't just call `MyType::from_reflect` as we do not have knowledge of the concrete type (`MyType`) at runtime. Such scenarios happen when we call `Reflect::clone_value`, use the reflection deserializers, or create the Dynamic type ourselves. ## Solution Add a `ReflectFromReflect` type data struct. This struct allows us to easily convert Dynamic representations of our types into their respective concrete instances. ```rust #[derive(Reflect, FromReflect)] #[reflect(FromReflect)] // <- Register `ReflectFromReflect` struct MyStruct(String); let type_id = TypeId::of::<MyStruct>(); // Register our type let mut registry = TypeRegistry::default(); registry.register::<MyStruct>(); // Create a concrete instance let my_struct = MyStruct("Hello world".to_string()); // `Reflect::clone_value` will generate a `DynamicTupleStruct` for tuple struct types let dynamic_value: Box<dyn Reflect> = my_struct.clone_value(); assert!(!dynamic_value.is::<MyStruct>()); // Get the `ReflectFromReflect` type data from the registry let rfr: &ReflectFromReflect = registry .get_type_data::<ReflectFromReflect>(type_id) .unwrap(); // Call `FromReflect::from_reflect` on our Dynamic value let concrete_value: Box<dyn Reflect> = rfr.from_reflect(&dynamic_value); assert!(concrete_value.is::<MyStruct>()); ``` ### Why this PR? ###### Why now? The three main reasons I closed #4147 were that: 1. Registering `ReflectFromReflect` is clunky (deriving `FromReflect` *and* registering `ReflectFromReflect`) 2. The ecosystem and Bevy itself didn't seem to pay much attention to deriving `FromReflect` 3. I didn't see a lot of desire from the community for such a feature However, as time has passed it seems 2 and 3 are not really true anymore. Bevy is internally adding lots more `FromReflect` derives, which should make this feature all the more useful. Additionally, I have seen a growing number of people look for something like `ReflectFromReflect`. I think 1 is still an issue, but not a horrible one. Plus it could be made much, much better using #6056. And I think splitting this feature out of #6056 could lead to #6056 being adopted sooner (or at least make the need more clear to users). ###### Why not just re-open #4147? The main reason is so that this PR can garner more attention than simply re-opening the old one. This helps bring fresh eyes to the PR for potentially more perspectives/reviews. --- ## Changelog * Added `ReflectFromReflect` Co-authored-by: Gino Valente <49806985+MrGVSV@users.noreply.github.com> |
||
Gino Valente
|
02fbf16c80 |
bevy_reflect: Add Reflect::into_reflect (#6502)
# Objective Using `Reflect` we can easily switch between a specific reflection trait object, such as a `dyn Struct`, to a `dyn Reflect` object via `Reflect::as_reflect` or `Reflect::as_reflect_mut`. ```rust fn do_something(value: &dyn Reflect) {/* ... */} let foo: Box<dyn Struct> = Box::new(Foo::default()); do_something(foo.as_reflect()); ``` However, there is no way to convert a _boxed_ reflection trait object to a `Box<dyn Reflect>`. ## Solution Add a `Reflect::into_reflect` method which allows converting a boxed reflection trait object back into a boxed `Reflect` trait object. ```rust fn do_something(value: Box<dyn Reflect>) {/* ... */} let foo: Box<dyn Struct> = Box::new(Foo::default()); do_something(foo.into_reflect()); ``` --- ## Changelog - Added `Reflect::into_reflect` |
||
Alice Cecile
|
334e09892b |
Revert "Show prelude re-exports in docs (#6448)" (#6449)
This reverts commit
|
||
Alejandro Pascual
|
53d387f340 |
Show prelude re-exports in docs (#6448)
# Objective - Right now re-exports are completely hidden in prelude docs. - Fixes #6433 ## Solution - We could show the re-exports without inlining their documentation. |
||
Jakob Hellermann
|
e71c4d2802 |
fix nightly clippy warnings (#6395)
# Objective - fix new clippy lints before they get stable and break CI ## Solution - run `clippy --fix` to auto-fix machine-applicable lints - silence `clippy::should_implement_trait` for `fn HandleId::default<T: Asset>` ## Changes - always prefer `format!("{inline}")` over `format!("{}", not_inline)` - prefer `Box::default` (or `Box::<T>::default` if necessary) over `Box::new(T::default())` |
||
Gino Valente
|
a658bfef19 |
bevy_reflect: Reflect doc comments (#6234)
# Objective Resolves #6197 Make it so that doc comments can be retrieved via reflection. ## Solution Adds the new `documentation` feature to `bevy_reflect` (disabled by default). When enabled, documentation can be found using `TypeInfo::doc` for reflected types: ```rust /// Some struct. /// /// # Example /// /// ```ignore /// let some_struct = SomeStruct; /// ``` #[derive(Reflect)] struct SomeStruct; let info = <SomeStruct as Typed>::type_info(); assert_eq!( Some(" Some struct.\n\n # Example\n\n ```ignore\n let some_struct = SomeStruct;\n ```"), info.docs() ); ``` ### Notes for Reviewers The bulk of the files simply added the same 16 lines of code (with slightly different documentation). Most of the real changes occur in the `bevy_reflect_derive` files as well as in the added tests. --- ## Changelog * Added `documentation` feature to `bevy_reflect` * Added `TypeInfo::docs` method (and similar methods for all info types) |
||
TehPers
|
132e8fb382 |
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. |
||
Gino Valente
|
d30d3e752a |
bevy_reflect: Improve serialization format even more (#5723)
> Note: This is rebased off #4561 and can be viewed as a competitor to that PR. See `Comparison with #4561` section for details. # Objective The current serialization format used by `bevy_reflect` is both verbose and error-prone. Taking the following structs[^1] for example: ```rust // -- src/inventory.rs #[derive(Reflect)] struct Inventory { id: String, max_storage: usize, items: Vec<Item> } #[derive(Reflect)] struct Item { name: String } ``` Given an inventory of a single item, this would serialize to something like: ```rust // -- assets/inventory.ron { "type": "my_game::inventory::Inventory", "struct": { "id": { "type": "alloc::string::String", "value": "inv001", }, "max_storage": { "type": "usize", "value": 10 }, "items": { "type": "alloc::vec::Vec<alloc::string::String>", "list": [ { "type": "my_game::inventory::Item", "struct": { "name": { "type": "alloc::string::String", "value": "Pickaxe" }, }, }, ], }, }, } ``` Aside from being really long and difficult to read, it also has a few "gotchas" that users need to be aware of if they want to edit the file manually. A major one is the requirement that you use the proper keys for a given type. For structs, you need `"struct"`. For lists, `"list"`. For tuple structs, `"tuple_struct"`. And so on. It also ***requires*** that the `"type"` entry come before the actual data. Despite being a map— which in programming is almost always orderless by default— the entries need to be in a particular order. Failure to follow the ordering convention results in a failure to deserialize the data. This makes it very prone to errors and annoyances. ## Solution Using #4042, we can remove a lot of the boilerplate and metadata needed by this older system. Since we now have static access to type information, we can simplify our serialized data to look like: ```rust // -- assets/inventory.ron { "my_game::inventory::Inventory": ( id: "inv001", max_storage: 10, items: [ ( name: "Pickaxe" ), ], ), } ``` This is much more digestible and a lot less error-prone (no more key requirements and no more extra type names). Additionally, it is a lot more familiar to users as it follows conventional serde mechanics. For example, the struct is represented with `(...)` when serialized to RON. #### Custom Serialization Additionally, this PR adds the opt-in ability to specify a custom serde implementation to be used rather than the one created via reflection. For example[^1]: ```rust // -- src/inventory.rs #[derive(Reflect, Serialize)] #[reflect(Serialize)] struct Item { #[serde(alias = "id")] name: String } ``` ```rust // -- assets/inventory.ron { "my_game::inventory::Inventory": ( id: "inv001", max_storage: 10, items: [ ( id: "Pickaxe" ), ], ), }, ``` By allowing users to define their own serialization methods, we do two things: 1. We give more control over how data is serialized/deserialized to the end user 2. We avoid having to re-define serde's attributes and forcing users to apply both (e.g. we don't need a `#[reflect(alias)]` attribute). ### Improved Formats One of the improvements this PR provides is the ability to represent data in ways that are more conventional and/or familiar to users. Many users are familiar with RON so here are some of the ways we can now represent data in RON: ###### Structs ```js { "my_crate::Foo": ( bar: 123 ) } // OR { "my_crate::Foo": Foo( bar: 123 ) } ``` <details> <summary>Old Format</summary> ```js { "type": "my_crate::Foo", "struct": { "bar": { "type": "usize", "value": 123 } } } ``` </details> ###### Tuples ```js { "(f32, f32)": (1.0, 2.0) } ``` <details> <summary>Old Format</summary> ```js { "type": "(f32, f32)", "tuple": [ { "type": "f32", "value": 1.0 }, { "type": "f32", "value": 2.0 } ] } ``` </details> ###### Tuple Structs ```js { "my_crate::Bar": ("Hello World!") } // OR { "my_crate::Bar": Bar("Hello World!") } ``` <details> <summary>Old Format</summary> ```js { "type": "my_crate::Bar", "tuple_struct": [ { "type": "alloc::string::String", "value": "Hello World!" } ] } ``` </details> ###### Arrays It may be a bit surprising to some, but arrays now also use the tuple format. This is because they essentially _are_ tuples (a sequence of values with a fixed size), but only allow for homogenous types. Additionally, this is how RON handles them and is probably a result of the 32-capacity limit imposed on them (both by [serde](https://docs.rs/serde/latest/serde/trait.Serialize.html#impl-Serialize-for-%5BT%3B%2032%5D) and by [bevy_reflect](https://docs.rs/bevy/latest/bevy/reflect/trait.GetTypeRegistration.html#impl-GetTypeRegistration-for-%5BT%3B%2032%5D)). ```js { "[i32; 3]": (1, 2, 3) } ``` <details> <summary>Old Format</summary> ```js { "type": "[i32; 3]", "array": [ { "type": "i32", "value": 1 }, { "type": "i32", "value": 2 }, { "type": "i32", "value": 3 } ] } ``` </details> ###### Enums To make things simple, I'll just put a struct variant here, but the style applies to all variant types: ```js { "my_crate::ItemType": Consumable( name: "Healing potion" ) } ``` <details> <summary>Old Format</summary> ```js { "type": "my_crate::ItemType", "enum": { "variant": "Consumable", "struct": { "name": { "type": "alloc::string::String", "value": "Healing potion" } } } } ``` </details> ### Comparison with #4561 This PR is a rebased version of #4561. The reason for the split between the two is because this PR creates a _very_ different scene format. You may notice that the PR descriptions for either PR are pretty similar. This was done to better convey the changes depending on which (if any) gets merged first. If #4561 makes it in first, I will update this PR description accordingly. --- ## Changelog * Re-worked serialization/deserialization for reflected types * Added `TypedReflectDeserializer` for deserializing data with known `TypeInfo` * Renamed `ReflectDeserializer` to `UntypedReflectDeserializer` * ~~Replaced usages of `deserialize_any` with `deserialize_map` for non-self-describing formats~~ Reverted this change since there are still some issues that need to be sorted out (in a separate PR). By reverting this, crates like `bincode` can throw an error when attempting to deserialize non-self-describing formats (`bincode` results in `DeserializeAnyNotSupported`) * Structs, tuples, tuple structs, arrays, and enums are now all de/serialized using conventional serde methods ## Migration Guide * This PR reduces the verbosity of the scene format. Scenes will need to be updated accordingly: ```js // Old format { "type": "my_game::item::Item", "struct": { "id": { "type": "alloc::string::String", "value": "bevycraft:stone", }, "tags": { "type": "alloc::vec::Vec<alloc::string::String>", "list": [ { "type": "alloc::string::String", "value": "material" }, ], }, } // New format { "my_game::item::Item": ( id: "bevycraft:stone", tags: ["material"] ) } ``` [^1]: Some derives omitted for brevity. |
||
targrub
|
d0e294c86b |
Query filter types must be ReadOnlyWorldQuery (#6008)
# Objective Fixes Issue #6005. ## Solution Replaced WorldQuery with ReadOnlyWorldQuery on F generic in Query filters and QueryState to restrict its trait bound. ## Migration Guide Query filter (`F`) generics are now bound by `ReadOnlyWorldQuery`, rather than `WorldQuery`. If for some reason you were requesting `Query<&A, &mut B>`, please use `Query<&A, With<B>>` instead. |
||
Jerome Humbert
|
8b7b44d839 |
Move sprite::Rect into bevy_math (#5686)
# Objective Promote the `Rect` utility of `sprite::Rect`, which defines a rectangle by its minimum and maximum corners, to the `bevy_math` crate to make it available as a general math type to all crates without the need to depend on the `bevy_sprite` crate. Fixes #5575 ## Solution Move `sprite::Rect` into `bevy_math` and fix all uses. Implement `Reflect` for `Rect` directly into the `bevy_reflect` crate by having `bevy_reflect` depend on `bevy_math`. This looks like a new dependency, but the `bevy_reflect` was "cheating" for other math types by directly depending on `glam` to reflect other math types, thereby giving the illusion that there was no dependency on `bevy_math`. In practice conceptually Bevy's math types are reflected into the `bevy_reflect` crate to avoid a dependency of that crate to a "lower level" utility crate like `bevy_math` (which in turn would make `bevy_reflect` be a dependency of most other crates, and increase the risk of circular dependencies). So this change simply formalizes that dependency in `Cargo.toml`. The `Rect` struct is also augmented in this change with a collection of utility methods to improve its usability. A few uses cases are updated to use those new methods, resulting is more clear and concise syntax. --- ## Changelog ### Changed - Moved the `sprite::Rect` type into `bevy_math`. ### Added - Added several utility methods to the `math::Rect` type. ## Migration Guide The `bevy::sprite::Rect` type moved to the math utility crate as `bevy::math::Rect`. You should change your imports from `use bevy::sprite::Rect` to `use bevy::math::Rect`. |
||
Gino Valente
|
ecc584ff23 |
bevy_reflect: Get owned fields (#5728)
# Objective Sometimes it's useful to be able to retrieve all the fields of a container type so that they may be processed separately. With reflection, however, we typically only have access to references. The only alternative is to "clone" the value using `Reflect::clone_value`. This, however, returns a Dynamic type in most cases. The solution there would be to use `FromReflect` instead, but this also has a problem in that it means we need to add `FromReflect` as an additional bound. ## Solution Add a `drain` method to all container traits. This returns a `Vec<Box<dyn Reflect>>` (except for `Map` which returns `Vec<(Box<dyn Reflect>, Box<dyn Reflect>)>`). This allows us to do things a lot simpler. For example, if we finished processing a struct and just need a particular value: ```rust // === OLD === // /// May or may not return a Dynamic*** value (even if `container` wasn't a `DynamicStruct`) fn get_output(container: Box<dyn Struct>, output_index: usize) -> Box<dyn Reflect> { container.field_at(output_index).unwrap().clone_value() } // === NEW === // /// Returns _exactly_ whatever was in the given struct fn get_output(container: Box<dyn Struct>, output_index: usize) -> Box<dyn Reflect> { container.drain().remove(output_index).unwrap() } ``` ### Discussion * Is `drain` the best method name? It makes sense that it "drains" all the fields and that it consumes the container in the process, but I'm open to alternatives. --- ## Changelog * Added a `drain` method to the following traits: * `Struct` * `TupleStruct` * `Tuple` * `Array` * `List` * `Map` * `Enum` |
||
Gino Valente
|
00508d110a |
bevy_reflect: Add FromReflect to the prelude (#5720)
# Objective `FromReflect` is a commonly used component to the Reflect API. It's required as a bound for reflecting things like `Vec<T>` and `HashMap<K, V>` and is generally useful (if not necessary) to derive on most structs or enums. Currently, however, it is not exported in `bevy_reflect`'s prelude. This means a module that uses `bevy_reflect` might have the following two lines: ```rust use bevy_reflect::prelude::*; use bevy_reflect::FromReflect; ``` Additionally, users of the full engine might need to put: ```rust use bevy::prelude::*; use bevy::reflect::FromReflect; ``` ## Solution Add `FromReflect` to the prelude of `bevy_reflect`. --- ## Changelog - Added `FromReflect` to the prelude of `bevy_reflect` |
||
Gino Valente
|
15826d6019 |
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](
|
||
Jakob Hellermann
|
4d05eb19be |
bevy_reflect: remove glam from a test which is active without the glam feature (#5195)
# Objective `glam` is an optional feature in `bevy_reflect` and there is a separate `mod test { #[cfg(feature = "glam")] mod glam { .. }}`. The `reflect_downcast` test is not in that module and doesn't depend on glam, which breaks `cargo test -p bevy_reflect` without the `glam` feature. ## Solution - Remove the glam types from the test, they're not relevant to it |
||
James Liu
|
5498ef81fb |
bevy_reflect: support map insertion (#5173)
# Objective This is a rebase of #3701 which is currently scheduled for 0.8 but is marked for adoption. > Fixes https://github.com/bevyengine/bevy/discussions/3609 ## Solution > - add an `insert_boxed()` method on the `Map` trait > - implement it for `HashMap` using a new `FromReflect` generic bound > - add a `map_apply()` helper method to implement `Map::apply()`, that inserts new values instead of ignoring them --- ## Changelog TODO Co-authored-by: james7132 <contact@jamessliu.com> |
||
CGMossa
|
33f9b3940d |
Updated glam to 0.21 . (#5142)
Removed `const_vec2`/`const_vec3` and replaced with equivalent `.from_array`. # Objective Fixes #5112 ## Solution - `encase` needs to update to `glam` as well. See teoxoy/encase#4 on progress on that. - `hexasphere` also needs to be updated, see OptimisticPeach/hexasphere#12. |
||
PROMETHIA-27
|
c27a3cff6d |
Make Reflect safe to implement (#5010)
# Objective Currently, `Reflect` is unsafe to implement because of a contract in which `any` and `any_mut` must return `self`, or `downcast` will cause UB. This PR makes `Reflect` safe, makes `downcast` not use unsafe, and eliminates this contract. ## Solution This PR adds a method to `Reflect`, `any`. It also renames the old `any` to `as_any`. `any` now takes a `Box<Self>` and returns a `Box<dyn Any>`. --- ## Changelog ### Added: - `any()` method - `represents()` method ### Changed: - `Reflect` is now a safe trait - `downcast()` is now safe - The old `any` is now called `as_any`, and `any_mut` is now `as_mut_any` ## Migration Guide - Reflect derives should not have to change anything - Manual reflect impls will need to remove the `unsafe` keyword, add `any()` implementations, and rename the old `any` and `any_mut` to `as_any` and `as_mut_any`. - Calls to `any`/`any_mut` must be changed to `as_any`/`as_mut_any` ## Points of discussion: - Should renaming `any` be avoided and instead name the new method `any_box`? - ~~Could there be a performance regression from avoiding the unsafe? I doubt it, but this change does seem to introduce redundant checks.~~ - ~~Could/should `is` and `type_id()` be implemented differently? For example, moving `is` onto `Reflect` as an `fn(&self, TypeId) -> bool`~~ Co-authored-by: PROMETHIA-27 <42193387+PROMETHIA-27@users.noreply.github.com> |
||
Jakob Hellermann
|
218b0fd3b6 |
bevy_reflect : put serialize into external ReflectSerialize type (#4782)
builds on top of #4780 # Objective `Reflect` and `Serialize` are currently very tied together because `Reflect` has a `fn serialize(&self) -> Option<Serializable<'_>>` method. Because of that, we can either implement `Reflect` for types like `Option<T>` with `T: Serialize` and have `fn serialize` be implemented, or without the bound but having `fn serialize` return `None`. By separating `ReflectSerialize` into a separate type (like how it already is for `ReflectDeserialize`, `ReflectDefault`), we could separately `.register::<Option<T>>()` and `.register_data::<Option<T>, ReflectSerialize>()` only if the type `T: Serialize`. This PR does not change the registration but allows it to be changed in a future PR. ## Solution - add the type ```rust struct ReflectSerialize { .. } impl<T: Reflect + Serialize> FromType<T> for ReflectSerialize { .. } ``` - remove `#[reflect(Serialize)]` special casing. - when serializing reflect value types, look for `ReflectSerialize` in the `TypeRegistry` instead of calling `value.serialize()` |
||
François
|
ab72c8368f |
Fix ron deprecation (#5021)
# Objective - Update to fix `ron` deprecation |
||
Gino Valente
|
e6f34ba47f |
bevy_reflect: Add statically available type info for reflected types (#4042)
# Objective > Resolves #4504 It can be helpful to have access to type information without requiring an instance of that type. Especially for `Reflect`, a lot of the gathered type information is known at compile-time and should not necessarily require an instance. ## Solution Created a dedicated `TypeInfo` enum to store static type information. All types that derive `Reflect` now also implement the newly created `Typed` trait: ```rust pub trait Typed: Reflect { fn type_info() -> &'static TypeInfo; } ``` > Note: This trait was made separate from `Reflect` due to `Sized` restrictions. If you only have access to a `dyn Reflect`, just call `.get_type_info()` on it. This new trait method on `Reflect` should return the same value as if you had called it statically. If all you have is a `TypeId` or type name, you can get the `TypeInfo` directly from the registry using the `TypeRegistry::get_type_info` method (assuming it was registered). ### Usage Below is an example of working with `TypeInfo`. As you can see, we don't have to generate an instance of `MyTupleStruct` in order to get this information. ```rust #[derive(Reflect)] struct MyTupleStruct(usize, i32, MyStruct); let info = MyTupleStruct::type_info(); if let TypeInfo::TupleStruct(info) = info { assert!(info.is::<MyTupleStruct>()); assert_eq!(std::any::type_name::<MyTupleStruct>(), info.type_name()); assert!(info.field_at(1).unwrap().is::<i32>()); } else { panic!("Expected `TypeInfo::TupleStruct`"); } ``` ### Manual Implementations It's not recommended to manually implement `Typed` yourself, but if you must, you can use the `TypeInfoCell` to automatically create and manage the static `TypeInfo`s for you (which is very helpful for blanket/generic impls): ```rust use bevy_reflect::{Reflect, TupleStructInfo, TypeInfo, UnnamedField}; use bevy_reflect::utility::TypeInfoCell; struct Foo<T: Reflect>(T); impl<T: Reflect> Typed for Foo<T> { fn type_info() -> &'static TypeInfo { static CELL: TypeInfoCell = TypeInfoCell::generic(); CELL.get_or_insert::<Self, _>(|| { let fields = [UnnamedField:🆕:<T>()]; let info = TupleStructInfo:🆕:<Self>(&fields); TypeInfo::TupleStruct(info) }) } } ``` ## Benefits One major benefit is that this opens the door to other serialization methods. Since we can get all the type info at compile time, we can know how to properly deserialize something like: ```rust #[derive(Reflect)] struct MyType { foo: usize, bar: Vec<String> } // RON to be deserialized: ( type: "my_crate::MyType", // <- We now know how to deserialize the rest of this object value: { // "foo" is a value type matching "usize" "foo": 123, // "bar" is a list type matching "Vec<String>" with item type "String" "bar": ["a", "b", "c"] } ) ``` Not only is this more compact, but it has better compatibility (we can change the type of `"foo"` to `i32` without having to update our serialized data). Of course, serialization/deserialization strategies like this may need to be discussed and fully considered before possibly making a change. However, we will be better equipped to do that now that we can access type information right from the registry. ## Discussion Some items to discuss: 1. Duplication. There's a bit of overlap with the existing traits/structs since they require an instance of the type while the type info structs do not (for example, `Struct::field_at(&self, index: usize)` and `StructInfo::field_at(&self, index: usize)`, though only `StructInfo` is accessible without an instance object). Is this okay, or do we want to handle it in another way? 2. Should `TypeInfo::Dynamic` be removed? Since the dynamic types don't have type information available at runtime, we could consider them `TypeInfo::Value`s (or just even just `TypeInfo::Struct`). The intention with `TypeInfo::Dynamic` was to keep the distinction from these dynamic types and actual structs/values since users might incorrectly believe the methods of the dynamic type's info struct would map to some contained data (which isn't possible statically). 4. General usefulness of this change, including missing/unnecessary parts. 5. Possible changes to the scene format? (One possible issue with changing it like in the example above might be that we'd have to be careful when handling generic or trait object types.) ## Compile Tests I ran a few tests to compare compile times (as suggested [here](https://github.com/bevyengine/bevy/pull/4042#discussion_r876408143)). I toggled `Reflect` and `FromReflect` derive macros using `cfg_attr` for both this PR ( |