reflect: remove manual Reflect impls which could be handled by macros (#12596)

# Objective

* Adopted #12025 to fix merge conflicts
* In some cases we used manual impls for certain types, though they are
(at least, now) unnecessary.

## Solution

* Use macros and reflecting-by-value to avoid this clutter.
* Though there were linker issues with Reflect and the CowArc in
AssetPath (see https://github.com/bevyengine/bevy/issues/9747), I
checked these are resolved by using #[reflect_value].

---------

Co-authored-by: soqb <cb.setho@gmail.com>
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: James Liu <contact@jamessliu.com>
This commit is contained in:
Jacques Schutte 2024-03-23 03:45:00 +02:00 committed by GitHub
parent 037f9d414b
commit fdf2ea7cc5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 17 additions and 488 deletions

View File

@ -1,15 +1,10 @@
use crate::io::AssetSourceId;
use bevy_reflect::{
std_traits::ReflectDefault, utility::NonGenericTypeInfoCell, FromReflect, FromType,
GetTypeRegistration, Reflect, ReflectDeserialize, ReflectFromPtr, ReflectFromReflect,
ReflectKind, ReflectMut, ReflectOwned, ReflectRef, ReflectSerialize, TypeInfo, TypePath,
TypeRegistration, Typed, ValueInfo,
};
use bevy_reflect::{Reflect, ReflectDeserialize, ReflectSerialize};
use bevy_utils::CowArc;
use serde::{de::Visitor, Deserialize, Serialize};
use std::{
fmt::{Debug, Display},
hash::{Hash, Hasher},
hash::Hash,
ops::Deref,
path::{Path, PathBuf},
};
@ -52,7 +47,8 @@ use thiserror::Error;
/// This means that the common case of `asset_server.load("my_scene.scn")` when it creates and
/// clones internal owned [`AssetPaths`](AssetPath).
/// This also means that you should use [`AssetPath::parse`] in cases where `&str` is the explicit type.
#[derive(Eq, PartialEq, Hash, Clone, Default)]
#[derive(Eq, PartialEq, Hash, Clone, Default, Reflect)]
#[reflect_value(Debug, PartialEq, Hash, Serialize, Deserialize)]
pub struct AssetPath<'a> {
source: AssetSourceId<'a>,
path: CowArc<'a, Path>,
@ -572,136 +568,6 @@ impl<'de> Visitor<'de> for AssetPathVisitor {
}
}
// NOTE: We manually implement "reflect value" because deriving Reflect on `AssetPath` breaks dynamic linking
// See https://github.com/bevyengine/bevy/issues/9747
// NOTE: This could use `impl_reflect_value` if it supported static lifetimes.
impl GetTypeRegistration for AssetPath<'static> {
fn get_type_registration() -> TypeRegistration {
let mut registration = TypeRegistration::of::<Self>();
registration.insert::<ReflectFromPtr>(FromType::<Self>::from_type());
registration.insert::<ReflectFromReflect>(FromType::<Self>::from_type());
registration.insert::<ReflectSerialize>(FromType::<Self>::from_type());
registration.insert::<ReflectDeserialize>(FromType::<Self>::from_type());
registration.insert::<ReflectDefault>(FromType::<Self>::from_type());
registration
}
}
impl TypePath for AssetPath<'static> {
fn type_path() -> &'static str {
"bevy_asset::path::AssetPath<'static>"
}
fn short_type_path() -> &'static str {
"AssetPath<'static>"
}
fn type_ident() -> Option<&'static str> {
Some("AssetPath<'static>")
}
fn crate_name() -> Option<&'static str> {
None
}
fn module_path() -> Option<&'static str> {
None
}
}
impl Typed for AssetPath<'static> {
fn type_info() -> &'static TypeInfo {
static CELL: NonGenericTypeInfoCell = NonGenericTypeInfoCell::new();
CELL.get_or_set(|| {
let info = ValueInfo::new::<Self>();
TypeInfo::Value(info)
})
}
}
impl Reflect for AssetPath<'static> {
#[inline]
fn get_represented_type_info(&self) -> Option<&'static TypeInfo> {
Some(<Self as Typed>::type_info())
}
#[inline]
fn into_any(self: Box<Self>) -> Box<dyn core::any::Any> {
self
}
#[inline]
fn as_any(&self) -> &dyn core::any::Any {
self
}
#[inline]
fn as_any_mut(&mut self) -> &mut dyn core::any::Any {
self
}
#[inline]
fn into_reflect(self: Box<Self>) -> Box<dyn Reflect> {
self
}
#[inline]
fn as_reflect(&self) -> &dyn Reflect {
self
}
#[inline]
fn as_reflect_mut(&mut self) -> &mut dyn Reflect {
self
}
#[inline]
fn apply(&mut self, value: &dyn Reflect) {
let value = Reflect::as_any(value);
if let Some(value) = value.downcast_ref::<Self>() {
*self = value.clone();
} else {
panic!("Value is not {}.", std::any::type_name::<Self>());
}
}
#[inline]
fn set(
&mut self,
value: Box<dyn bevy_reflect::Reflect>,
) -> Result<(), Box<dyn bevy_reflect::Reflect>> {
*self = <dyn bevy_reflect::Reflect>::take(value)?;
Ok(())
}
fn reflect_kind(&self) -> ReflectKind {
ReflectKind::Value
}
fn reflect_ref(&self) -> ReflectRef {
ReflectRef::Value(self)
}
fn reflect_mut(&mut self) -> ReflectMut {
ReflectMut::Value(self)
}
fn reflect_owned(self: Box<Self>) -> ReflectOwned {
ReflectOwned::Value(self)
}
#[inline]
fn clone_value(&self) -> Box<dyn Reflect> {
Box::new(self.clone())
}
fn reflect_hash(&self) -> Option<u64> {
let mut hasher = bevy_reflect::utility::reflect_hasher();
Hash::hash(&::core::any::Any::type_id(self), &mut hasher);
Hash::hash(self, &mut hasher);
Some(Hasher::finish(&hasher))
}
fn reflect_partial_eq(&self, value: &dyn Reflect) -> Option<bool> {
let value = <dyn Reflect>::as_any(value);
if let Some(value) = <dyn core::any::Any>::downcast_ref::<Self>(value) {
Some(PartialEq::eq(self, value))
} else {
Some(false)
}
}
fn debug(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
::core::fmt::Debug::fmt(self, f)
}
}
impl FromReflect for AssetPath<'static> {
fn from_reflect(reflect: &dyn Reflect) -> Option<Self> {
Some(Clone::clone(<dyn core::any::Any>::downcast_ref::<
AssetPath<'static>,
>(<dyn Reflect>::as_any(reflect))?))
}
}
/// Normalizes the path by collapsing all occurrences of '.' and '..' dot-segments where possible
/// as per [RFC 1808](https://datatracker.ietf.org/doc/html/rfc1808)
pub(crate) fn normalize_path(path: &Path) -> PathBuf {

View File

@ -1,17 +1,16 @@
use crate::std_traits::ReflectDefault;
use crate::{self as bevy_reflect, ReflectFromPtr, ReflectFromReflect, ReflectOwned, TypeRegistry};
use crate::{
impl_type_path, map_apply, map_partial_eq, Array, ArrayInfo, ArrayIter, DynamicEnum,
DynamicMap, Enum, EnumInfo, FromReflect, FromType, GetTypeRegistration, List, ListInfo,
ListIter, Map, MapInfo, MapIter, Reflect, ReflectDeserialize, ReflectKind, ReflectMut,
ReflectRef, ReflectSerialize, TupleVariantInfo, TypeInfo, TypePath, TypeRegistration, Typed,
UnitVariantInfo, UnnamedField, ValueInfo, VariantFieldIter, VariantInfo, VariantType,
impl_type_path, map_apply, map_partial_eq, Array, ArrayInfo, ArrayIter, DynamicMap,
FromReflect, FromType, GetTypeRegistration, List, ListInfo, ListIter, Map, MapInfo, MapIter,
Reflect, ReflectDeserialize, ReflectKind, ReflectMut, ReflectRef, ReflectSerialize, TypeInfo,
TypePath, TypeRegistration, Typed, ValueInfo,
};
use crate::utility::{
reflect_hasher, GenericTypeInfoCell, GenericTypePathCell, NonGenericTypeInfoCell,
};
use bevy_reflect_derive::impl_reflect_value;
use bevy_reflect_derive::{impl_reflect, impl_reflect_value};
use std::fmt;
use std::{
any::Any,
@ -996,252 +995,14 @@ impl<T: Reflect + TypePath + GetTypeRegistration, const N: usize> GetTypeRegistr
}
}
impl<T: FromReflect + TypePath + GetTypeRegistration> GetTypeRegistration for Option<T> {
fn get_type_registration() -> TypeRegistration {
TypeRegistration::of::<Option<T>>()
}
fn register_type_dependencies(registry: &mut TypeRegistry) {
registry.register::<T>();
impl_reflect! {
#[type_path = "core::option"]
enum Option<T> {
None,
Some(T),
}
}
impl<T: FromReflect + TypePath + GetTypeRegistration> Enum for Option<T> {
fn field(&self, _name: &str) -> Option<&dyn Reflect> {
None
}
fn field_at(&self, index: usize) -> Option<&dyn Reflect> {
match self {
Some(value) if index == 0 => Some(value),
_ => None,
}
}
fn field_mut(&mut self, _name: &str) -> Option<&mut dyn Reflect> {
None
}
fn field_at_mut(&mut self, index: usize) -> Option<&mut dyn Reflect> {
match self {
Some(value) if index == 0 => Some(value),
_ => None,
}
}
fn index_of(&self, _name: &str) -> Option<usize> {
None
}
fn name_at(&self, _index: usize) -> Option<&str> {
None
}
fn iter_fields(&self) -> VariantFieldIter {
VariantFieldIter::new(self)
}
#[inline]
fn field_len(&self) -> usize {
match self {
Some(..) => 1,
None => 0,
}
}
#[inline]
fn variant_name(&self) -> &str {
match self {
Some(..) => "Some",
None => "None",
}
}
fn variant_index(&self) -> usize {
match self {
None => 0,
Some(..) => 1,
}
}
#[inline]
fn variant_type(&self) -> VariantType {
match self {
Some(..) => VariantType::Tuple,
None => VariantType::Unit,
}
}
fn clone_dynamic(&self) -> DynamicEnum {
DynamicEnum::from_ref::<Self>(self)
}
}
impl<T: FromReflect + TypePath + GetTypeRegistration> Reflect for Option<T> {
#[inline]
fn get_represented_type_info(&self) -> Option<&'static TypeInfo> {
Some(<Self as Typed>::type_info())
}
#[inline]
fn into_any(self: Box<Self>) -> Box<dyn Any> {
self
}
#[inline]
fn as_any(&self) -> &dyn Any {
self
}
#[inline]
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
#[inline]
fn into_reflect(self: Box<Self>) -> Box<dyn Reflect> {
self
}
fn as_reflect(&self) -> &dyn Reflect {
self
}
fn as_reflect_mut(&mut self) -> &mut dyn Reflect {
self
}
#[inline]
fn apply(&mut self, value: &dyn Reflect) {
if let ReflectRef::Enum(value) = value.reflect_ref() {
if self.variant_name() == value.variant_name() {
// Same variant -> just update fields
for (index, field) in value.iter_fields().enumerate() {
if let Some(v) = self.field_at_mut(index) {
v.apply(field.value());
}
}
} else {
// New variant -> perform a switch
match value.variant_name() {
"Some" => {
let field = T::take_from_reflect(
value
.field_at(0)
.unwrap_or_else(|| {
panic!(
"Field in `Some` variant of {} should exist",
Self::type_path()
)
})
.clone_value(),
)
.unwrap_or_else(|_| {
panic!(
"Field in `Some` variant of {} should be of type {}",
Self::type_path(),
T::type_path()
)
});
*self = Some(field);
}
"None" => {
*self = None;
}
_ => panic!("Enum is not a {}.", Self::type_path()),
}
}
}
}
#[inline]
fn set(&mut self, value: Box<dyn Reflect>) -> Result<(), Box<dyn Reflect>> {
*self = value.take()?;
Ok(())
}
fn reflect_kind(&self) -> ReflectKind {
ReflectKind::Enum
}
fn reflect_ref(&self) -> ReflectRef {
ReflectRef::Enum(self)
}
fn reflect_mut(&mut self) -> ReflectMut {
ReflectMut::Enum(self)
}
fn reflect_owned(self: Box<Self>) -> ReflectOwned {
ReflectOwned::Enum(self)
}
#[inline]
fn clone_value(&self) -> Box<dyn Reflect> {
Box::new(Enum::clone_dynamic(self))
}
fn reflect_hash(&self) -> Option<u64> {
crate::enum_hash(self)
}
fn reflect_partial_eq(&self, value: &dyn Reflect) -> Option<bool> {
crate::enum_partial_eq(self, value)
}
}
impl<T: FromReflect + TypePath + GetTypeRegistration> FromReflect for Option<T> {
fn from_reflect(reflect: &dyn Reflect) -> Option<Self> {
if let ReflectRef::Enum(dyn_enum) = reflect.reflect_ref() {
match dyn_enum.variant_name() {
"Some" => {
let field = T::take_from_reflect(
dyn_enum
.field_at(0)
.unwrap_or_else(|| {
panic!(
"Field in `Some` variant of {} should exist",
Option::<T>::type_path()
)
})
.clone_value(),
)
.unwrap_or_else(|_| {
panic!(
"Field in `Some` variant of {} should be of type {}",
Option::<T>::type_path(),
T::type_path()
)
});
Some(Some(field))
}
"None" => Some(None),
name => panic!(
"variant with name `{}` does not exist on enum `{}`",
name,
Self::type_path()
),
}
} else {
None
}
}
}
impl<T: FromReflect + TypePath + GetTypeRegistration> Typed for Option<T> {
fn type_info() -> &'static TypeInfo {
static CELL: GenericTypeInfoCell = GenericTypeInfoCell::new();
CELL.get_or_insert::<Self, _>(|| {
let none_variant = VariantInfo::Unit(UnitVariantInfo::new("None"));
let some_variant =
VariantInfo::Tuple(TupleVariantInfo::new("Some", &[UnnamedField::new::<T>(0)]));
TypeInfo::Enum(EnumInfo::new::<Self>(&[none_variant, some_variant]))
})
}
}
impl_type_path!(::core::option::Option<T>);
impl<T: TypePath + ?Sized> TypePath for &'static T {
fn type_path() -> &'static str {
static CELL: GenericTypePathCell = GenericTypePathCell::new();

View File

@ -7,13 +7,7 @@ use bevy_ecs::{
system::{StaticSystemParam, SystemParam, SystemParamItem, SystemState},
world::{FromWorld, Mut},
};
use bevy_reflect::std_traits::ReflectDefault;
use bevy_reflect::{
utility::{reflect_hasher, NonGenericTypeInfoCell},
FromReflect, FromType, GetTypeRegistration, Reflect, ReflectDeserialize, ReflectFromPtr,
ReflectFromReflect, ReflectKind, ReflectMut, ReflectOwned, ReflectRef, ReflectSerialize,
TypeInfo, TypePath, TypeRegistration, Typed, ValueInfo,
};
use bevy_reflect::{Reflect, ReflectDeserialize, ReflectSerialize};
use bevy_utils::{HashMap, HashSet};
use serde::{Deserialize, Serialize};
use std::marker::PhantomData;
@ -77,7 +71,8 @@ bitflags::bitflags! {
/// [discussion about memory management](https://github.com/WebAssembly/design/issues/1397) for more
/// details.
#[repr(transparent)]
#[derive(Serialize, TypePath, Deserialize, Hash, Clone, Copy, PartialEq, Eq, Debug)]
#[derive(Serialize, Deserialize, Hash, Clone, Copy, PartialEq, Eq, Debug, Reflect)]
#[reflect_value(Serialize, Deserialize, Hash, PartialEq, Debug)]
pub struct RenderAssetUsages: u8 {
const MAIN_WORLD = 1 << 0;
const RENDER_WORLD = 1 << 1;
@ -98,99 +93,6 @@ impl Default for RenderAssetUsages {
}
}
impl Reflect for RenderAssetUsages {
fn get_represented_type_info(&self) -> Option<&'static bevy_reflect::TypeInfo> {
Some(<Self as Typed>::type_info())
}
fn into_any(self: Box<Self>) -> Box<dyn std::any::Any> {
self
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
self
}
fn into_reflect(self: Box<Self>) -> Box<dyn Reflect> {
self
}
fn as_reflect(&self) -> &dyn Reflect {
self
}
fn as_reflect_mut(&mut self) -> &mut dyn Reflect {
self
}
fn apply(&mut self, value: &dyn Reflect) {
let value = value.as_any();
if let Some(&value) = value.downcast_ref::<Self>() {
*self = value;
} else {
panic!("Value is not a {}.", Self::type_path());
}
}
fn set(&mut self, value: Box<dyn Reflect>) -> Result<(), Box<dyn Reflect>> {
*self = value.take()?;
Ok(())
}
fn reflect_kind(&self) -> bevy_reflect::ReflectKind {
ReflectKind::Value
}
fn reflect_ref(&self) -> bevy_reflect::ReflectRef {
ReflectRef::Value(self)
}
fn reflect_mut(&mut self) -> bevy_reflect::ReflectMut {
ReflectMut::Value(self)
}
fn reflect_owned(self: Box<Self>) -> bevy_reflect::ReflectOwned {
ReflectOwned::Value(self)
}
fn clone_value(&self) -> Box<dyn Reflect> {
Box::new(*self)
}
fn reflect_hash(&self) -> Option<u64> {
use std::hash::Hash;
use std::hash::Hasher;
let mut hasher = reflect_hasher();
Hash::hash(&std::any::Any::type_id(self), &mut hasher);
Hash::hash(self, &mut hasher);
Some(hasher.finish())
}
fn reflect_partial_eq(&self, value: &dyn Reflect) -> Option<bool> {
let value = value.as_any();
if let Some(value) = value.downcast_ref::<Self>() {
Some(std::cmp::PartialEq::eq(self, value))
} else {
Some(false)
}
}
}
impl GetTypeRegistration for RenderAssetUsages {
fn get_type_registration() -> TypeRegistration {
let mut registration = TypeRegistration::of::<Self>();
registration.insert::<ReflectSerialize>(FromType::<Self>::from_type());
registration.insert::<ReflectDeserialize>(FromType::<Self>::from_type());
registration.insert::<ReflectDefault>(FromType::<Self>::from_type());
registration.insert::<ReflectFromReflect>(FromType::<Self>::from_type());
registration.insert::<ReflectFromPtr>(FromType::<Self>::from_type());
registration
}
}
impl FromReflect for RenderAssetUsages {
fn from_reflect(reflect: &dyn Reflect) -> Option<Self> {
let raw_value = *reflect.as_any().downcast_ref::<u8>()?;
Self::from_bits(raw_value)
}
}
impl Typed for RenderAssetUsages {
fn type_info() -> &'static TypeInfo {
static CELL: NonGenericTypeInfoCell = NonGenericTypeInfoCell::new();
CELL.get_or_set(|| TypeInfo::Value(ValueInfo::new::<Self>()))
}
}
/// This plugin extracts the changed assets from the "app world" into the "render world"
/// and prepares them for the GPU. They can then be accessed from the [`RenderAssets`] resource.
///