From 1efc76292445d106ffbfddf9faaa7eab2fddf0c6 Mon Sep 17 00:00:00 2001 From: radiish Date: Mon, 5 Jun 2023 21:31:20 +0100 Subject: [PATCH] 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` which allows us to be generic over both `TypeInfo` and `TypePath`. - `TypePath` is now a supertrait of `Asset`, `Material` and `Material2d`. - `impl_reflect_struct` needs a `#[type_path = "..."]` attribute to be specified. - `impl_reflect_value` needs to either specify path starting with a double colon (`::core::option::Option`) or an `in my_crate::foo` declaration. - Added `bevy_reflect_derive::ReflectTypePath`. - Most uses of `Ident` in `bevy_reflect_derive` changed to use `ReflectTypePath`. ## Migration Guide - Implementors of `Asset`, `Material` and `Material2d` now also need to derive `TypePath`. - Manual implementors of `Reflect` will need to implement the new `get_type_path` method. ## Open Questions - [x] ~This PR currently does not migrate any usages of `std::any::type_name` to use `bevy_reflect::TypePath` to ease the review process. Should it?~ Migration will be left to a follow-up PR. - [ ] This PR adds a lot of `#[derive(TypePath)]` and `T: TypePath` to satisfy new bounds, mostly when deriving `TypeUuid`. Should we make `TypePath` a supertrait of `TypeUuid`? [Should we remove `TypeUuid` in favour of `TypePath`?](https://github.com/bevyengine/bevy/pull/5805/files/2afbd855327c4b68e0a6b6f03118f289988441a4#r961067892) --- crates/bevy_asset/src/asset_server.rs | 6 +- crates/bevy_asset/src/assets.rs | 2 +- crates/bevy_asset/src/loader.rs | 5 +- crates/bevy_audio/src/audio_source.rs | 4 +- crates/bevy_audio/src/sinks.rs | 6 +- crates/bevy_ecs/src/reflect.rs | 2 +- crates/bevy_gltf/src/lib.rs | 10 +- crates/bevy_input/src/input.rs | 4 +- crates/bevy_pbr/src/material.rs | 8 +- .../bevy_reflect_derive/src/derive_data.rs | 471 ++++++++++++++++-- .../bevy_reflect_derive/src/from_reflect.rs | 28 +- .../bevy_reflect_derive/src/impls/enums.rs | 26 +- .../bevy_reflect_derive/src/impls/mod.rs | 1 + .../bevy_reflect_derive/src/impls/structs.rs | 31 +- .../src/impls/tuple_structs.rs | 30 +- .../bevy_reflect_derive/src/impls/typed.rs | 140 +++++- .../bevy_reflect_derive/src/impls/values.rs | 26 +- .../bevy_reflect_derive/src/lib.rs | 182 ++++++- .../bevy_reflect_derive/src/reflect_value.rs | 45 +- .../bevy_reflect_derive/src/registration.rs | 22 +- .../bevy_reflect_derive/src/type_path.rs | 102 ++++ .../bevy_reflect_derive/src/utility.rs | 152 +++++- crates/bevy_reflect/src/array.rs | 13 +- crates/bevy_reflect/src/enums/dynamic_enum.rs | 14 +- crates/bevy_reflect/src/impls/glam.rs | 50 +- crates/bevy_reflect/src/impls/rect.rs | 1 + crates/bevy_reflect/src/impls/smallvec.rs | 23 +- crates/bevy_reflect/src/impls/std.rs | 346 ++++++++++--- crates/bevy_reflect/src/lib.rs | 102 +++- crates/bevy_reflect/src/list.rs | 14 +- crates/bevy_reflect/src/map.rs | 12 +- crates/bevy_reflect/src/reflect.rs | 12 +- crates/bevy_reflect/src/struct_trait.rs | 13 +- crates/bevy_reflect/src/tuple.rs | 44 +- crates/bevy_reflect/src/tuple_struct.rs | 14 +- crates/bevy_reflect/src/type_info.rs | 3 +- crates/bevy_reflect/src/type_path.rs | 167 +++++++ crates/bevy_reflect/src/utility.rs | 230 ++++++--- .../tests/reflect_derive/generics.fail.rs | 5 +- .../tests/reflect_derive/generics.fail.stderr | 8 +- crates/bevy_render/src/mesh/mesh/mod.rs | 4 +- crates/bevy_render/src/mesh/mesh/skinning.rs | 4 +- .../bevy_render/src/render_resource/shader.rs | 4 +- crates/bevy_render/src/view/visibility/mod.rs | 2 +- crates/bevy_scene/src/dynamic_scene.rs | 4 +- crates/bevy_scene/src/scene.rs | 4 +- crates/bevy_sprite/src/mesh2d/material.rs | 8 +- crates/bevy_text/src/font.rs | 4 +- crates/bevy_text/src/font_atlas_set.rs | 3 +- examples/2d/custom_gltf_vertex_attribute.rs | 4 +- examples/3d/lines.rs | 4 +- examples/3d/tonemapping.rs | 4 +- examples/asset/custom_asset.rs | 4 +- examples/audio/decodable.rs | 4 +- examples/shader/animate_shader.rs | 8 +- examples/shader/array_texture.rs | 4 +- examples/shader/custom_vertex_attribute.rs | 4 +- examples/shader/shader_defs.rs | 4 +- examples/shader/shader_material.rs | 4 +- examples/shader/shader_material_glsl.rs | 4 +- .../shader_material_screenspace_texture.rs | 4 +- examples/shader/shader_prepass.rs | 6 +- examples/shader/texture_binding_array.rs | 4 +- 63 files changed, 2062 insertions(+), 411 deletions(-) create mode 100644 crates/bevy_reflect/bevy_reflect_derive/src/type_path.rs create mode 100644 crates/bevy_reflect/src/type_path.rs diff --git a/crates/bevy_asset/src/asset_server.rs b/crates/bevy_asset/src/asset_server.rs index 08cd8349298f7..e7e830cd1d90a 100644 --- a/crates/bevy_asset/src/asset_server.rs +++ b/crates/bevy_asset/src/asset_server.rs @@ -98,7 +98,7 @@ pub struct AssetServerInternal { /// use bevy_asset::{AssetServer, Handle}; /// use bevy_ecs::prelude::{Commands, Res}; /// -/// # #[derive(Debug, bevy_reflect::TypeUuid)] +/// # #[derive(Debug, bevy_reflect::TypeUuid, bevy_reflect::TypePath)] /// # #[uuid = "00000000-0000-0000-0000-000000000000"] /// # struct Image; /// @@ -647,10 +647,10 @@ mod test { use crate::{loader::LoadedAsset, update_asset_storage_system}; use bevy_app::{App, Update}; use bevy_ecs::prelude::*; - use bevy_reflect::TypeUuid; + use bevy_reflect::{TypePath, TypeUuid}; use bevy_utils::BoxedFuture; - #[derive(Debug, TypeUuid)] + #[derive(Debug, TypeUuid, TypePath)] #[uuid = "a5189b72-0572-4290-a2e0-96f73a491c44"] struct PngAsset; diff --git a/crates/bevy_asset/src/assets.rs b/crates/bevy_asset/src/assets.rs index 74fabdc7c83e5..7b4d488ca98a3 100644 --- a/crates/bevy_asset/src/assets.rs +++ b/crates/bevy_asset/src/assets.rs @@ -488,7 +488,7 @@ mod tests { #[test] fn asset_overwriting() { - #[derive(bevy_reflect::TypeUuid)] + #[derive(bevy_reflect::TypeUuid, bevy_reflect::TypePath)] #[uuid = "44115972-f31b-46e5-be5c-2b9aece6a52f"] struct MyAsset; let mut app = App::new(); diff --git a/crates/bevy_asset/src/loader.rs b/crates/bevy_asset/src/loader.rs index 55611b6a1803a..a494fd0a2cc01 100644 --- a/crates/bevy_asset/src/loader.rs +++ b/crates/bevy_asset/src/loader.rs @@ -5,6 +5,7 @@ use crate::{ use anyhow::Error; use anyhow::Result; use bevy_ecs::system::{Res, ResMut}; +use bevy_reflect::TypePath; use bevy_reflect::{TypeUuid, TypeUuidDynamic}; use bevy_utils::{BoxedFuture, HashMap}; use crossbeam_channel::{Receiver, Sender}; @@ -47,13 +48,13 @@ pub trait AssetLoader: Send + Sync + 'static { /// /// In order to load assets into your game you must either add them manually to an asset storage /// with [`Assets::add`] or load them from the filesystem with [`AssetServer::load`]. -pub trait Asset: TypeUuid + AssetDynamic {} +pub trait Asset: TypeUuid + TypePath + AssetDynamic {} /// An untyped version of the [`Asset`] trait. pub trait AssetDynamic: Downcast + TypeUuidDynamic + Send + Sync + 'static {} impl_downcast!(AssetDynamic); -impl Asset for T where T: TypeUuid + AssetDynamic + TypeUuidDynamic {} +impl Asset for T where T: TypeUuid + TypePath + AssetDynamic + TypeUuidDynamic {} impl AssetDynamic for T where T: Send + Sync + 'static + TypeUuidDynamic {} diff --git a/crates/bevy_audio/src/audio_source.rs b/crates/bevy_audio/src/audio_source.rs index afbf445350526..93b951e9f7b5e 100644 --- a/crates/bevy_audio/src/audio_source.rs +++ b/crates/bevy_audio/src/audio_source.rs @@ -1,11 +1,11 @@ use anyhow::Result; use bevy_asset::{Asset, AssetLoader, LoadContext, LoadedAsset}; -use bevy_reflect::TypeUuid; +use bevy_reflect::{TypePath, TypeUuid}; use bevy_utils::BoxedFuture; use std::{io::Cursor, sync::Arc}; /// A source of audio data -#[derive(Debug, Clone, TypeUuid)] +#[derive(Debug, Clone, TypeUuid, TypePath)] #[uuid = "7a14806a-672b-443b-8d16-4f18afefa463"] pub struct AudioSource { /// Raw data of the audio source. diff --git a/crates/bevy_audio/src/sinks.rs b/crates/bevy_audio/src/sinks.rs index c84ae6fbd60bd..2746eaf195da2 100644 --- a/crates/bevy_audio/src/sinks.rs +++ b/crates/bevy_audio/src/sinks.rs @@ -1,5 +1,5 @@ use bevy_math::Vec3; -use bevy_reflect::TypeUuid; +use bevy_reflect::{TypePath, TypeUuid}; use bevy_transform::prelude::Transform; use rodio::{Sink, SpatialSink}; @@ -87,7 +87,7 @@ pub trait AudioSinkPlayback { /// } /// ``` /// -#[derive(TypeUuid)] +#[derive(TypePath, TypeUuid)] #[uuid = "8BEE570C-57C2-4FC0-8CFB-983A22F7D981"] pub struct AudioSink { // This field is an Option in order to allow us to have a safe drop that will detach the sink. @@ -158,7 +158,7 @@ impl AudioSinkPlayback for AudioSink { /// } /// ``` /// -#[derive(TypeUuid)] +#[derive(TypePath, TypeUuid)] #[uuid = "F3CA4C47-595E-453B-96A7-31C3DDF2A177"] pub struct SpatialAudioSink { // This field is an Option in order to allow us to have a safe drop that will detach the sink. diff --git a/crates/bevy_ecs/src/reflect.rs b/crates/bevy_ecs/src/reflect.rs index 62be9c9ae4198..3f556c0700c3a 100644 --- a/crates/bevy_ecs/src/reflect.rs +++ b/crates/bevy_ecs/src/reflect.rs @@ -407,7 +407,7 @@ impl FromType for ReflectResource { } } -impl_reflect_value!(Entity(Hash, PartialEq, Serialize, Deserialize)); +impl_reflect_value!((in bevy_ecs) Entity(Hash, PartialEq, Serialize, Deserialize)); impl_from_reflect_value!(Entity); #[derive(Clone)] diff --git a/crates/bevy_gltf/src/lib.rs b/crates/bevy_gltf/src/lib.rs index 934c0dae237b8..7277a906269f5 100644 --- a/crates/bevy_gltf/src/lib.rs +++ b/crates/bevy_gltf/src/lib.rs @@ -12,7 +12,7 @@ use bevy_app::prelude::*; use bevy_asset::{AddAsset, Handle}; use bevy_ecs::{prelude::Component, reflect::ReflectComponent}; use bevy_pbr::StandardMaterial; -use bevy_reflect::{Reflect, TypeUuid}; +use bevy_reflect::{Reflect, TypePath, TypeUuid}; use bevy_render::{ mesh::{Mesh, MeshVertexAttribute}, renderer::RenderDevice, @@ -58,7 +58,7 @@ impl Plugin for GltfPlugin { } /// Representation of a loaded glTF file. -#[derive(Debug, TypeUuid)] +#[derive(Debug, TypeUuid, TypePath)] #[uuid = "5c7d5f8a-f7b0-4e45-a09e-406c0372fea2"] pub struct Gltf { pub scenes: Vec>, @@ -78,7 +78,7 @@ pub struct Gltf { /// A glTF node with all of its child nodes, its [`GltfMesh`], /// [`Transform`](bevy_transform::prelude::Transform) and an optional [`GltfExtras`]. -#[derive(Debug, Clone, TypeUuid)] +#[derive(Debug, Clone, TypeUuid, TypePath)] #[uuid = "dad74750-1fd6-460f-ac51-0a7937563865"] pub struct GltfNode { pub children: Vec, @@ -89,7 +89,7 @@ pub struct GltfNode { /// A glTF mesh, which may consist of multiple [`GltfPrimitives`](GltfPrimitive) /// and an optional [`GltfExtras`]. -#[derive(Debug, Clone, TypeUuid)] +#[derive(Debug, Clone, TypeUuid, TypePath)] #[uuid = "8ceaec9a-926a-4f29-8ee3-578a69f42315"] pub struct GltfMesh { pub primitives: Vec, @@ -97,7 +97,7 @@ pub struct GltfMesh { } /// Part of a [`GltfMesh`] that consists of a [`Mesh`], an optional [`StandardMaterial`] and [`GltfExtras`]. -#[derive(Debug, Clone, TypeUuid)] +#[derive(Debug, Clone, TypeUuid, TypePath)] #[uuid = "cbfca302-82fd-41cb-af77-cab6b3d50af1"] pub struct GltfPrimitive { pub mesh: Handle, diff --git a/crates/bevy_input/src/input.rs b/crates/bevy_input/src/input.rs index a22145ea64f5d..3758840fecd65 100644 --- a/crates/bevy_input/src/input.rs +++ b/crates/bevy_input/src/input.rs @@ -174,10 +174,12 @@ where #[cfg(test)] mod test { + use bevy_reflect::TypePath; + use crate::Input; /// Used for testing the functionality of [`Input`]. - #[derive(Copy, Clone, Eq, PartialEq, Hash)] + #[derive(TypePath, Copy, Clone, Eq, PartialEq, Hash)] enum DummyInput { Input1, Input2, diff --git a/crates/bevy_pbr/src/material.rs b/crates/bevy_pbr/src/material.rs index e5db9bee18739..0016634533eeb 100644 --- a/crates/bevy_pbr/src/material.rs +++ b/crates/bevy_pbr/src/material.rs @@ -19,7 +19,7 @@ use bevy_ecs::{ SystemParamItem, }, }; -use bevy_reflect::TypeUuid; +use bevy_reflect::{TypePath, TypeUuid}; use bevy_render::{ extract_component::ExtractComponentPlugin, mesh::{Mesh, MeshVertexBufferLayout}, @@ -59,11 +59,11 @@ use std::marker::PhantomData; /// ``` /// # use bevy_pbr::{Material, MaterialMeshBundle}; /// # use bevy_ecs::prelude::*; -/// # use bevy_reflect::TypeUuid; +/// # use bevy_reflect::{TypeUuid, TypePath}; /// # use bevy_render::{render_resource::{AsBindGroup, ShaderRef}, texture::Image, color::Color}; /// # use bevy_asset::{Handle, AssetServer, Assets}; /// -/// #[derive(AsBindGroup, TypeUuid, Debug, Clone)] +/// #[derive(AsBindGroup, TypeUuid, TypePath, Debug, Clone)] /// #[uuid = "f690fdae-d598-45ab-8225-97e2a3f056e0"] /// pub struct CustomMaterial { /// // Uniform bindings must implement `ShaderType`, which will be used to convert the value to @@ -106,7 +106,7 @@ use std::marker::PhantomData; /// @group(1) @binding(2) /// var color_sampler: sampler; /// ``` -pub trait Material: AsBindGroup + Send + Sync + Clone + TypeUuid + Sized + 'static { +pub trait Material: AsBindGroup + Send + Sync + Clone + TypeUuid + TypePath + Sized { /// Returns this material's vertex shader. If [`ShaderRef::Default`] is returned, the default mesh vertex shader /// will be used. fn vertex_shader() -> ShaderRef { diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/derive_data.rs b/crates/bevy_reflect/bevy_reflect_derive/src/derive_data.rs index 132dc36dbaf88..3dc9db462d707 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/derive_data.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/derive_data.rs @@ -1,15 +1,22 @@ use crate::container_attributes::ReflectTraits; use crate::field_attributes::{parse_field_attrs, ReflectFieldAttr}; use crate::fq_std::{FQAny, FQDefault, FQSend, FQSync}; -use crate::utility::{members_to_serialization_denylist, WhereClauseOptions}; +use crate::type_path::parse_path_no_leading_colon; +use crate::utility::{members_to_serialization_denylist, StringExpr, WhereClauseOptions}; use bit_set::BitSet; -use quote::quote; +use quote::{quote, ToTokens}; use syn::token::Comma; -use crate::{utility, REFLECT_ATTRIBUTE_NAME, REFLECT_VALUE_ATTRIBUTE_NAME}; +use crate::{ + utility, REFLECT_ATTRIBUTE_NAME, REFLECT_VALUE_ATTRIBUTE_NAME, TYPE_NAME_ATTRIBUTE_NAME, + TYPE_PATH_ATTRIBUTE_NAME, +}; use syn::punctuated::Punctuated; use syn::spanned::Spanned; -use syn::{Data, DeriveInput, Field, Fields, Generics, Ident, Meta, Path, Variant}; +use syn::{ + parse_str, Data, DeriveInput, Field, Fields, GenericParam, Generics, Ident, LitStr, Meta, Path, + PathSegment, Type, TypeParam, Variant, +}; pub(crate) enum ReflectDerive<'a> { Struct(ReflectStruct<'a>), @@ -36,9 +43,7 @@ pub(crate) struct ReflectMeta<'a> { /// The registered traits for this type. traits: ReflectTraits, /// The name of this type. - type_name: &'a Ident, - /// The generics defined on this type. - generics: &'a Generics, + type_path: ReflectTypePath<'a>, /// A cached instance of the path to the `bevy_reflect` crate. bevy_reflect_path: Path, /// The documentation for this type, if any @@ -131,8 +136,12 @@ enum ReflectMode { impl<'a> ReflectDerive<'a> { pub fn from_input(input: &'a DeriveInput) -> Result { let mut traits = ReflectTraits::default(); - // Should indicate whether `#[reflect_value]` was used + // Should indicate whether `#[reflect_value]` was used. let mut reflect_mode = None; + // Should indicate whether `#[type_path = "..."]` was used. + let mut custom_path: Option = None; + // Should indicate whether `#[type_name = "..."]` was used. + let mut custom_type_name: Option = None; #[cfg(feature = "documentation")] let mut doc = crate::documentation::Documentation::default(); @@ -177,6 +186,35 @@ impl<'a> ReflectDerive<'a> { reflect_mode = Some(ReflectMode::Value); } + Meta::NameValue(pair) if pair.path.is_ident(TYPE_PATH_ATTRIBUTE_NAME) => { + let syn::Expr::Lit(syn::ExprLit { + lit: syn::Lit::Str(lit), + .. + }) = &pair.value else { + return Err(syn::Error::new( + pair.span(), + format_args!("`#[{TYPE_PATH_ATTRIBUTE_NAME} = \"...\"]` must be a string literal"), + )); + }; + + custom_path = Some(syn::parse::Parser::parse_str( + parse_path_no_leading_colon, + &lit.value(), + )?); + } + Meta::NameValue(pair) if pair.path.is_ident(TYPE_NAME_ATTRIBUTE_NAME) => { + let syn::Expr::Lit(syn::ExprLit { + lit: syn::Lit::Str(lit), + .. + }) = &pair.value else { + return Err(syn::Error::new( + pair.span(), + format_args!("`#[{TYPE_NAME_ATTRIBUTE_NAME} = \"...\"]` must be a string literal"), + )); + }; + + custom_type_name = Some(parse_str(&lit.value())?); + } #[cfg(feature = "documentation")] Meta::NameValue(pair) if pair.path.is_ident("doc") => { if let syn::Expr::Lit(syn::ExprLit { @@ -190,8 +228,27 @@ impl<'a> ReflectDerive<'a> { _ => continue, } } + match (&mut custom_path, custom_type_name) { + (Some(path), custom_type_name) => { + let ident = custom_type_name.unwrap_or_else(|| input.ident.clone()); + path.segments.push(PathSegment::from(ident)); + } + (None, Some(name)) => { + return Err(syn::Error::new( + name.span(), + format!("cannot use `#[{TYPE_NAME_ATTRIBUTE_NAME} = \"...\"]` without a `#[{TYPE_PATH_ATTRIBUTE_NAME} = \"...\"]` attribute."), + )); + } + _ => (), + } - let meta = ReflectMeta::new(&input.ident, &input.generics, traits); + let type_path = ReflectTypePath::Internal { + ident: &input.ident, + custom_path, + generics: &input.generics, + }; + + let meta = ReflectMeta::new(type_path, traits); #[cfg(feature = "documentation")] let meta = meta.with_docs(doc); @@ -233,6 +290,16 @@ impl<'a> ReflectDerive<'a> { }; } + pub fn meta(&self) -> &ReflectMeta<'a> { + match self { + ReflectDerive::Struct(data) + | ReflectDerive::TupleStruct(data) + | ReflectDerive::UnitStruct(data) => data.meta(), + ReflectDerive::Enum(data) => data.meta(), + ReflectDerive::Value(meta) => meta, + } + } + fn collect_struct_fields(fields: &'a Fields) -> Result>, syn::Error> { let sifter: utility::ResultSifter> = fields .iter() @@ -288,11 +355,10 @@ impl<'a> ReflectDerive<'a> { } impl<'a> ReflectMeta<'a> { - pub fn new(type_name: &'a Ident, generics: &'a Generics, traits: ReflectTraits) -> Self { + pub fn new(type_path: ReflectTypePath<'a>, traits: ReflectTraits) -> Self { Self { traits, - type_name, - generics, + type_path, bevy_reflect_path: utility::get_bevy_reflect_path(), #[cfg(feature = "documentation")] docs: Default::default(), @@ -311,13 +377,8 @@ impl<'a> ReflectMeta<'a> { } /// The name of this struct. - pub fn type_name(&self) -> &'a Ident { - self.type_name - } - - /// The generics associated with this struct. - pub fn generics(&self) -> &'a Generics { - self.generics + pub fn type_path(&self) -> &ReflectTypePath<'a> { + &self.type_path } /// The cached `bevy_reflect` path. @@ -330,14 +391,7 @@ impl<'a> ReflectMeta<'a> { &self, where_clause_options: &WhereClauseOptions, ) -> proc_macro2::TokenStream { - crate::registration::impl_get_type_registration( - self.type_name, - &self.bevy_reflect_path, - self.traits.idents(), - self.generics, - where_clause_options, - None, - ) + crate::registration::impl_get_type_registration(self, where_clause_options, None) } /// The collection of docstrings for this type, if any. @@ -368,13 +422,8 @@ impl<'a> ReflectStruct<'a> { &self, where_clause_options: &WhereClauseOptions, ) -> proc_macro2::TokenStream { - let reflect_path = self.meta.bevy_reflect_path(); - crate::registration::impl_get_type_registration( - self.meta.type_name(), - reflect_path, - self.meta.traits().idents(), - self.meta.generics(), + self.meta(), where_clause_options, Some(&self.serialization_denylist), ) @@ -421,6 +470,7 @@ impl<'a> ReflectStruct<'a> { active_trait_bounds: quote! { #bevy_reflect_path::Reflect }, ignored_types: self.ignored_types().into(), ignored_trait_bounds: quote! { #FQAny + #FQSend + #FQSync }, + ..WhereClauseOptions::type_path_bounds(self.meta()) } } } @@ -433,7 +483,7 @@ impl<'a> ReflectEnum<'a> { /// Returns the given ident as a qualified unit variant of this enum. pub fn get_unit(&self, variant: &Ident) -> proc_macro2::TokenStream { - let name = self.meta.type_name; + let name = self.meta.type_path(); quote! { #name::#variant } @@ -479,6 +529,7 @@ impl<'a> ReflectEnum<'a> { active_trait_bounds: quote! { #bevy_reflect_path::FromReflect }, ignored_types: self.ignored_types().into(), ignored_trait_bounds: quote! { #FQAny + #FQSend + #FQSync + #FQDefault }, + ..WhereClauseOptions::type_path_bounds(self.meta()) } } } @@ -509,3 +560,355 @@ impl<'a> EnumVariant<'a> { } } } + +/// Represents a path to a type. +/// +/// This is used over [`struct@Ident`] or [`Path`] +/// to have the correct semantics for [deriving `TypePath`]. +/// +/// The type can always be reached with its [`ToTokens`] implementation. +/// +/// The [`short_type_path`], [`type_ident`], [`crate_name`], and [`module_path`] methods +/// have corresponding methods on the `TypePath` trait. +/// [`long_type_path`] corresponds to the `type_path` method on `TypePath`. +/// +/// [deriving `TypePath`]: crate::derive_type_path +/// [`long_type_path`]: ReflectTypePath::long_type_path +/// [`short_type_path`]: ReflectTypePath::short_type_path +/// [`type_ident`]: ReflectTypePath::type_ident +/// [`crate_name`]: ReflectTypePath::crate_name +/// [`module_path`]: ReflectTypePath::module_path +/// +/// # Example +/// +/// ```rust,ignore +/// # use syn::parse_quote; +/// # use bevy_reflect_derive::ReflectTypePath; +/// let path: syn::Path = parse_quote!(::core::marker::PhantomData)?; +/// +/// let type_path = ReflectTypePath::External { +/// path, +/// custom_path: None, +/// }; +/// +/// // Eqivalent to "core::marker". +/// let module_path = type_path.module_path(); +/// # Ok::<(), syn::Error>(()) +/// ``` +/// +pub(crate) enum ReflectTypePath<'a> { + /// Types without a crate/module that can be named from any scope (e.g. `bool`). + Primitive(&'a Ident), + /// Using `::my_crate::foo::Bar` syntax. + /// + /// May have a seperate custom path used for the `TypePath` implementation. + External { + path: &'a Path, + custom_path: Option, + generics: &'a Generics, + }, + /// The name of a type relative to its scope. + /// + /// The type must be able to be reached with just its name. + /// + /// May have a seperate alias path used for the `TypePath` implementation. + /// + /// Module and crate are found with [`module_path!()`](core::module_path), + /// if there is no custom path specified. + Internal { + ident: &'a Ident, + custom_path: Option, + generics: &'a Generics, + }, + /// Any [`syn::Type`] with only a defined `type_path` and `short_type_path`. + #[allow(dead_code)] + // Not currently used but may be useful in the future due to its generality. + Anonymous { + qualified_type: Type, + long_type_path: StringExpr, + short_type_path: StringExpr, + }, +} + +impl<'a> ReflectTypePath<'a> { + /// Returns the path interpreted as an [`struct@Ident`]. + /// + /// Returns [`None`] if [anonymous]. + /// + /// [anonymous]: ReflectTypePath::Anonymous + pub fn get_ident(&self) -> Option<&Ident> { + match self { + Self::Internal { + ident, custom_path, .. + } => Some( + custom_path + .as_ref() + .map(|path| &path.segments.last().unwrap().ident) + .unwrap_or(ident), + ), + Self::External { + path, custom_path, .. + } => Some( + &custom_path + .as_ref() + .unwrap_or(path) + .segments + .last() + .unwrap() + .ident, + ), + Self::Primitive(ident) => Some(ident), + _ => None, + } + } + + /// The generics associated with the type. + /// + /// Empty if [anonymous] or [primitive]. + /// + /// [primitive]: ReflectTypePath::Primitive + /// [anonymous]: ReflectTypePath::Anonymous + pub fn generics(&self) -> &'a Generics { + // Use a constant because we need to return a reference of at least 'a. + const EMPTY_GENERICS: &Generics = &Generics { + gt_token: None, + lt_token: None, + where_clause: None, + params: Punctuated::new(), + }; + match self { + Self::Internal { generics, .. } | Self::External { generics, .. } => generics, + _ => EMPTY_GENERICS, + } + } + + /// Whether an implementation of `Typed` or `TypePath` should be generic. + /// + /// Returning true that it should use a `GenericTypeCell` in its implementation. + pub fn impl_is_generic(&self) -> bool { + // Whether to use `GenericTypeCell` is not dependent on lifetimes + // (which all have to be 'static anyway). + !self + .generics() + .params + .iter() + .all(|param| matches!(param, GenericParam::Lifetime(_))) + } + + /// Returns the path interpreted as a [`Path`]. + /// + /// Returns [`None`] if [anonymous], [primitive], + /// or a [`ReflectTypePath::Internal`] without a custom path. + /// + /// [primitive]: ReflectTypePath::Primitive + /// [anonymous]: ReflectTypePath::Anonymous + pub fn get_path(&self) -> Option<&Path> { + match self { + Self::Internal { custom_path, .. } => custom_path.as_ref(), + Self::External { + path, custom_path, .. + } => Some(custom_path.as_ref().unwrap_or(path)), + _ => None, + } + } + + /// Returns whether this [internal] or [external] path has a custom path. + /// + /// [internal]: ReflectTypePath::Internal + /// [external]: ReflectTypePath::External + pub fn has_custom_path(&self) -> bool { + match self { + Self::Internal { custom_path, .. } | Self::External { custom_path, .. } => { + custom_path.is_some() + } + _ => false, + } + } + + /// Returns a [`StringExpr`] representing the name of the type's crate. + /// + /// Returns [`None`] if the type is [primitive] or [anonymous]. + /// + /// For non-customised [internal] paths this is created from [`module_path`]. + /// + /// For `Option`, this is `"core"`. + /// + /// [primitive]: ReflectTypePath::Primitive + /// [anonymous]: ReflectTypePath::Anonymous + /// [internal]: ReflectTypePath::Internal + pub fn crate_name(&self) -> Option { + if let Some(path) = self.get_path() { + let crate_name = &path.segments.first().unwrap().ident; + return Some(StringExpr::from(crate_name)); + } + + match self { + Self::Internal { .. } => Some(StringExpr::Borrowed(quote! { + ::core::module_path!() + .split(':') + .next() + .unwrap() + })), + Self::External { .. } => unreachable!(), + _ => None, + } + } + + /// Combines type generics and const generics into one [`StringExpr`]. + /// + /// This string can be used with a `GenericTypePathCell` in a `TypePath` implementation. + /// + /// The `ty_generic_fn` param maps [`TypeParam`]s to [`StringExpr`]s. + fn reduce_generics( + generics: &Generics, + mut ty_generic_fn: impl FnMut(&TypeParam) -> StringExpr, + ) -> StringExpr { + let mut params = generics.params.iter().filter_map(|param| match param { + GenericParam::Type(type_param) => Some(ty_generic_fn(type_param)), + GenericParam::Const(const_param) => { + let ident = &const_param.ident; + let ty = &const_param.ty; + + Some(StringExpr::Owned(quote! { + <#ty as ::std::string::ToString>::to_string(&#ident) + })) + } + GenericParam::Lifetime(_) => None, + }); + + params + .next() + .into_iter() + .chain(params.flat_map(|x| [StringExpr::from_str(", "), x])) + .collect() + } + + /// Returns a [`StringExpr`] representing the "type path" of the type. + /// + /// For `Option`, this is `"core::option::Option"`. + pub fn long_type_path(&self, bevy_reflect_path: &Path) -> StringExpr { + match self { + Self::Primitive(ident) => StringExpr::from(ident), + Self::Anonymous { long_type_path, .. } => long_type_path.clone(), + Self::Internal { generics, .. } | Self::External { generics, .. } => { + let ident = self.type_ident().unwrap(); + let module_path = self.module_path().unwrap(); + + if self.impl_is_generic() { + let generics = ReflectTypePath::reduce_generics( + generics, + |TypeParam { ident, .. }| { + StringExpr::Borrowed(quote! { + <#ident as #bevy_reflect_path::TypePath>::type_path() + }) + }, + ); + + StringExpr::from_iter([ + module_path, + StringExpr::from_str("::"), + ident, + StringExpr::from_str("<"), + generics, + StringExpr::from_str(">"), + ]) + } else { + StringExpr::from_iter([module_path, StringExpr::from_str("::"), ident]) + } + } + } + } + + /// Returns a [`StringExpr`] representing the "short path" of the type. + /// + /// For `Option`, this is `"Option"`. + pub fn short_type_path(&self, bevy_reflect_path: &Path) -> StringExpr { + match self { + Self::Anonymous { + short_type_path, .. + } => short_type_path.clone(), + Self::Primitive(ident) => StringExpr::from(ident), + Self::External { generics, .. } | Self::Internal { generics, .. } => { + let ident = self.type_ident().unwrap(); + + if self.impl_is_generic() { + let generics = ReflectTypePath::reduce_generics( + generics, + |TypeParam { ident, .. }| { + StringExpr::Borrowed(quote! { + <#ident as #bevy_reflect_path::TypePath>::short_type_path() + }) + }, + ); + + StringExpr::from_iter([ + ident, + StringExpr::from_str("<"), + generics, + StringExpr::from_str(">"), + ]) + } else { + ident + } + } + } + } + + /// Returns a [`StringExpr`] representing the path to the module + /// that the type is in. + /// + /// Returns [`None`] if the type is [primitive] or [anonymous]. + /// + /// For non-customised [internal] paths this is created from [`module_path`]. + /// + /// For `Option`, this is `"core::option"`. + /// + /// [primitive]: ReflectTypePath::Primitive + /// [anonymous]: ReflectTypePath::Anonymous + /// [internal]: ReflectTypePath::Internal + pub fn module_path(&self) -> Option { + if let Some(path) = self.get_path() { + let path_string = path + .segments + .pairs() + .take(path.segments.len() - 1) + .map(|pair| pair.value().ident.to_string()) + .reduce(|path, ident| path + "::" + &ident) + .unwrap(); + + let path_lit = LitStr::new(&path_string, path.span()); + return Some(StringExpr::from_lit(&path_lit)); + } + + match self { + Self::Internal { .. } => Some(StringExpr::Const(quote! { + ::core::module_path!() + })), + Self::External { .. } => unreachable!(), + _ => None, + } + } + + /// Returns a [`StringExpr`] representing the type's final ident. + /// + /// Returns [`None`] if the type is [anonymous]. + /// + /// This is not necessarily a valid qualified path to the type. + /// + /// For `Option`, this is `"Option"`. + /// + /// [anonymous]: ReflectTypePath::Anonymous + pub fn type_ident(&self) -> Option { + self.get_ident().map(StringExpr::from) + } +} + +impl<'a> ToTokens for ReflectTypePath<'a> { + fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { + match self { + Self::Internal { ident, .. } | Self::Primitive(ident) => ident.to_tokens(tokens), + Self::External { path, .. } => path.to_tokens(tokens), + Self::Anonymous { qualified_type, .. } => qualified_type.to_tokens(tokens), + } + } +} diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/from_reflect.rs b/crates/bevy_reflect/bevy_reflect_derive/src/from_reflect.rs index 20523b8c5ba6b..2473dd356f7a6 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/from_reflect.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/from_reflect.rs @@ -22,13 +22,13 @@ pub(crate) fn impl_tuple_struct(reflect_struct: &ReflectStruct) -> TokenStream { /// Implements `FromReflect` for the given value type pub(crate) fn impl_value(meta: &ReflectMeta) -> TokenStream { - let type_name = meta.type_name(); + let type_path = meta.type_path(); let bevy_reflect_path = meta.bevy_reflect_path(); - let (impl_generics, ty_generics, where_clause) = meta.generics().split_for_impl(); + let (impl_generics, ty_generics, where_clause) = type_path.generics().split_for_impl(); TokenStream::from(quote! { - impl #impl_generics #bevy_reflect_path::FromReflect for #type_name #ty_generics #where_clause { + impl #impl_generics #bevy_reflect_path::FromReflect for #type_path #ty_generics #where_clause { fn from_reflect(reflect: &dyn #bevy_reflect_path::Reflect) -> #FQOption { - #FQOption::Some(#FQClone::clone(::downcast_ref::<#type_name #ty_generics>(::as_any(reflect))?)) + #FQOption::Some(#FQClone::clone(::downcast_ref::<#type_path #ty_generics>(::as_any(reflect))?)) } } }) @@ -38,7 +38,7 @@ pub(crate) fn impl_value(meta: &ReflectMeta) -> TokenStream { pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> TokenStream { let fqoption = FQOption.into_token_stream(); - let type_name = reflect_enum.meta().type_name(); + let enum_path = reflect_enum.meta().type_path(); let bevy_reflect_path = reflect_enum.meta().bevy_reflect_path(); let ref_value = Ident::new("__param0", Span::call_site()); @@ -47,8 +47,7 @@ pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> TokenStream { variant_constructors, } = get_variant_constructors(reflect_enum, &ref_value, false); - let (impl_generics, ty_generics, where_clause) = - reflect_enum.meta().generics().split_for_impl(); + let (impl_generics, ty_generics, where_clause) = enum_path.generics().split_for_impl(); // Add FromReflect bound for each active field let where_from_reflect_clause = extend_where_clause( @@ -58,11 +57,12 @@ pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> TokenStream { ignored_types: reflect_enum.ignored_types().into_boxed_slice(), active_trait_bounds: quote!(#bevy_reflect_path::FromReflect), ignored_trait_bounds: quote!(#FQDefault), + ..WhereClauseOptions::type_path_bounds(reflect_enum.meta()) }, ); TokenStream::from(quote! { - impl #impl_generics #bevy_reflect_path::FromReflect for #type_name #ty_generics #where_from_reflect_clause { + impl #impl_generics #bevy_reflect_path::FromReflect for #enum_path #ty_generics #where_from_reflect_clause { fn from_reflect(#ref_value: &dyn #bevy_reflect_path::Reflect) -> #FQOption { if let #bevy_reflect_path::ReflectRef::Enum(#ref_value) = #bevy_reflect_path::Reflect::reflect_ref(#ref_value) { match #bevy_reflect_path::Enum::variant_name(#ref_value) { @@ -90,8 +90,7 @@ impl MemberValuePair { fn impl_struct_internal(reflect_struct: &ReflectStruct, is_tuple: bool) -> TokenStream { let fqoption = FQOption.into_token_stream(); - let struct_name = reflect_struct.meta().type_name(); - let generics = reflect_struct.meta().generics(); + let struct_path = reflect_struct.meta().type_path(); let bevy_reflect_path = reflect_struct.meta().bevy_reflect_path(); let ref_struct = Ident::new("__ref_struct", Span::call_site()); @@ -129,7 +128,11 @@ fn impl_struct_internal(reflect_struct: &ReflectStruct, is_tuple: bool) -> Token ) }; - let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); + let (impl_generics, ty_generics, where_clause) = reflect_struct + .meta() + .type_path() + .generics() + .split_for_impl(); // Add FromReflect bound for each active field let where_from_reflect_clause = extend_where_clause( @@ -143,11 +146,12 @@ fn impl_struct_internal(reflect_struct: &ReflectStruct, is_tuple: bool) -> Token } else { quote!(#FQDefault) }, + ..WhereClauseOptions::type_path_bounds(reflect_struct.meta()) }, ); TokenStream::from(quote! { - impl #impl_generics #bevy_reflect_path::FromReflect for #struct_name #ty_generics #where_from_reflect_clause + impl #impl_generics #bevy_reflect_path::FromReflect for #struct_path #ty_generics #where_from_reflect_clause { fn from_reflect(reflect: &dyn #bevy_reflect_path::Reflect) -> #FQOption { if let #bevy_reflect_path::ReflectRef::#ref_struct_type(#ref_struct) = #bevy_reflect_path::Reflect::reflect_ref(reflect) { diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs b/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs index 2f2f5cf5e3582..365061df4f2ff 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/impls/enums.rs @@ -1,7 +1,7 @@ use crate::derive_data::{EnumVariant, EnumVariantFields, ReflectEnum, StructField}; use crate::enum_utility::{get_variant_constructors, EnumVariantConstructors}; use crate::fq_std::{FQAny, FQBox, FQOption, FQResult}; -use crate::impls::impl_typed; +use crate::impls::{impl_type_path, impl_typed}; use crate::utility::extend_where_clause; use proc_macro::TokenStream; use proc_macro2::{Ident, Span}; @@ -10,7 +10,7 @@ use syn::Fields; pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> TokenStream { let bevy_reflect_path = reflect_enum.meta().bevy_reflect_path(); - let enum_name = reflect_enum.meta().type_name(); + let enum_path = reflect_enum.meta().type_path(); let ref_name = Ident::new("__name_param", Span::call_site()); let ref_index = Ident::new("__index_param", Span::call_site()); @@ -59,7 +59,7 @@ pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> TokenStream { } }); - let string_name = enum_name.to_string(); + let string_name = enum_path.get_ident().unwrap().to_string(); #[cfg(feature = "documentation")] let info_generator = { @@ -77,22 +77,23 @@ pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> TokenStream { }; let typed_impl = impl_typed( - enum_name, - reflect_enum.meta().generics(), + reflect_enum.meta(), &where_clause_options, quote! { let variants = [#(#variant_info),*]; let info = #info_generator; #bevy_reflect_path::TypeInfo::Enum(info) }, - bevy_reflect_path, ); + let type_path_impl = impl_type_path(reflect_enum.meta(), &where_clause_options); + let get_type_registration_impl = reflect_enum .meta() .get_type_registration(&where_clause_options); + let (impl_generics, ty_generics, where_clause) = - reflect_enum.meta().generics().split_for_impl(); + reflect_enum.meta().type_path().generics().split_for_impl(); let where_reflect_clause = extend_where_clause(where_clause, &where_clause_options); @@ -101,7 +102,9 @@ pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> TokenStream { #typed_impl - impl #impl_generics #bevy_reflect_path::Enum for #enum_name #ty_generics #where_reflect_clause { + #type_path_impl + + impl #impl_generics #bevy_reflect_path::Enum for #enum_path #ty_generics #where_reflect_clause { fn field(&self, #ref_name: &str) -> #FQOption<&dyn #bevy_reflect_path::Reflect> { match self { #(#enum_field,)* @@ -185,7 +188,7 @@ pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> TokenStream { } } - impl #impl_generics #bevy_reflect_path::Reflect for #enum_name #ty_generics #where_reflect_clause { + impl #impl_generics #bevy_reflect_path::Reflect for #enum_path #ty_generics #where_reflect_clause { #[inline] fn type_name(&self) -> &str { ::core::any::type_name::() @@ -196,6 +199,11 @@ pub(crate) fn impl_enum(reflect_enum: &ReflectEnum) -> TokenStream { #FQOption::Some(::type_info()) } + #[inline] + fn get_type_path(&self) -> &dyn #bevy_reflect_path::DynamicTypePath { + self + } + #[inline] fn into_any(self: #FQBox) -> #FQBox { self diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/impls/mod.rs b/crates/bevy_reflect/bevy_reflect_derive/src/impls/mod.rs index 19523fbf806ba..db5418b446348 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/impls/mod.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/impls/mod.rs @@ -7,5 +7,6 @@ mod values; pub(crate) use enums::impl_enum; pub(crate) use structs::impl_struct; pub(crate) use tuple_structs::impl_tuple_struct; +pub(crate) use typed::impl_type_path; pub(crate) use typed::impl_typed; pub(crate) use values::impl_value; diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/impls/structs.rs b/crates/bevy_reflect/bevy_reflect_derive/src/impls/structs.rs index 2b7fd1815c607..9c5d30ebad8d3 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/impls/structs.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/impls/structs.rs @@ -1,5 +1,5 @@ use crate::fq_std::{FQAny, FQBox, FQDefault, FQOption, FQResult}; -use crate::impls::impl_typed; +use crate::impls::{impl_type_path, impl_typed}; use crate::utility::{extend_where_clause, ident_or_index}; use crate::ReflectStruct; use proc_macro::TokenStream; @@ -10,7 +10,7 @@ pub(crate) fn impl_struct(reflect_struct: &ReflectStruct) -> TokenStream { let fqoption = FQOption.into_token_stream(); let bevy_reflect_path = reflect_struct.meta().bevy_reflect_path(); - let struct_name = reflect_struct.meta().type_name(); + let struct_path = reflect_struct.meta().type_path(); let field_names = reflect_struct .active_fields() @@ -64,7 +64,7 @@ pub(crate) fn impl_struct(reflect_struct: &ReflectStruct) -> TokenStream { } }; - let string_name = struct_name.to_string(); + let string_name = struct_path.get_ident().unwrap().to_string(); #[cfg(feature = "documentation")] let info_generator = { @@ -83,20 +83,24 @@ pub(crate) fn impl_struct(reflect_struct: &ReflectStruct) -> TokenStream { let where_clause_options = reflect_struct.where_clause_options(); let typed_impl = impl_typed( - struct_name, - reflect_struct.meta().generics(), + reflect_struct.meta(), &where_clause_options, quote! { let fields = [#field_generator]; let info = #info_generator; #bevy_reflect_path::TypeInfo::Struct(info) }, - bevy_reflect_path, ); + let type_path_impl = impl_type_path(reflect_struct.meta(), &where_clause_options); + let get_type_registration_impl = reflect_struct.get_type_registration(&where_clause_options); - let (impl_generics, ty_generics, where_clause) = - reflect_struct.meta().generics().split_for_impl(); + + let (impl_generics, ty_generics, where_clause) = reflect_struct + .meta() + .type_path() + .generics() + .split_for_impl(); let where_reflect_clause = extend_where_clause(where_clause, &where_clause_options); @@ -105,7 +109,9 @@ pub(crate) fn impl_struct(reflect_struct: &ReflectStruct) -> TokenStream { #typed_impl - impl #impl_generics #bevy_reflect_path::Struct for #struct_name #ty_generics #where_reflect_clause { + #type_path_impl + + impl #impl_generics #bevy_reflect_path::Struct for #struct_path #ty_generics #where_reflect_clause { fn field(&self, name: &str) -> #FQOption<&dyn #bevy_reflect_path::Reflect> { match name { #(#field_names => #fqoption::Some(&self.#field_idents),)* @@ -157,7 +163,7 @@ pub(crate) fn impl_struct(reflect_struct: &ReflectStruct) -> TokenStream { } } - impl #impl_generics #bevy_reflect_path::Reflect for #struct_name #ty_generics #where_reflect_clause { + impl #impl_generics #bevy_reflect_path::Reflect for #struct_path #ty_generics #where_reflect_clause { #[inline] fn type_name(&self) -> &str { ::core::any::type_name::() @@ -168,6 +174,11 @@ pub(crate) fn impl_struct(reflect_struct: &ReflectStruct) -> TokenStream { #FQOption::Some(::type_info()) } + #[inline] + fn get_type_path(&self) -> &dyn #bevy_reflect_path::DynamicTypePath { + self + } + #[inline] fn into_any(self: #FQBox) -> #FQBox { self diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/impls/tuple_structs.rs b/crates/bevy_reflect/bevy_reflect_derive/src/impls/tuple_structs.rs index c74d4d9064f80..db4b839baa49e 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/impls/tuple_structs.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/impls/tuple_structs.rs @@ -1,5 +1,5 @@ use crate::fq_std::{FQAny, FQBox, FQDefault, FQOption, FQResult}; -use crate::impls::impl_typed; +use crate::impls::{impl_type_path, impl_typed}; use crate::utility::extend_where_clause; use crate::ReflectStruct; use proc_macro::TokenStream; @@ -11,7 +11,7 @@ pub(crate) fn impl_tuple_struct(reflect_struct: &ReflectStruct) -> TokenStream { let fqoption = FQOption.into_token_stream(); let bevy_reflect_path = reflect_struct.meta().bevy_reflect_path(); - let struct_name = reflect_struct.meta().type_name(); + let struct_path = reflect_struct.meta().type_path(); let field_idents = reflect_struct .active_fields() @@ -58,7 +58,7 @@ pub(crate) fn impl_tuple_struct(reflect_struct: &ReflectStruct) -> TokenStream { } }; - let string_name = struct_name.to_string(); + let string_name = struct_path.get_ident().unwrap().to_string(); #[cfg(feature = "documentation")] let info_generator = { @@ -76,19 +76,22 @@ pub(crate) fn impl_tuple_struct(reflect_struct: &ReflectStruct) -> TokenStream { }; let typed_impl = impl_typed( - struct_name, - reflect_struct.meta().generics(), + reflect_struct.meta(), &where_clause_options, quote! { let fields = [#field_generator]; let info = #info_generator; #bevy_reflect_path::TypeInfo::TupleStruct(info) }, - bevy_reflect_path, ); - let (impl_generics, ty_generics, where_clause) = - reflect_struct.meta().generics().split_for_impl(); + let type_path_impl = impl_type_path(reflect_struct.meta(), &where_clause_options); + + let (impl_generics, ty_generics, where_clause) = reflect_struct + .meta() + .type_path() + .generics() + .split_for_impl(); let where_reflect_clause = extend_where_clause(where_clause, &where_clause_options); @@ -97,7 +100,9 @@ pub(crate) fn impl_tuple_struct(reflect_struct: &ReflectStruct) -> TokenStream { #typed_impl - impl #impl_generics #bevy_reflect_path::TupleStruct for #struct_name #ty_generics #where_reflect_clause { + #type_path_impl + + impl #impl_generics #bevy_reflect_path::TupleStruct for #struct_path #ty_generics #where_reflect_clause { fn field(&self, index: usize) -> #FQOption<&dyn #bevy_reflect_path::Reflect> { match index { #(#field_indices => #fqoption::Some(&self.#field_idents),)* @@ -128,7 +133,7 @@ pub(crate) fn impl_tuple_struct(reflect_struct: &ReflectStruct) -> TokenStream { } } - impl #impl_generics #bevy_reflect_path::Reflect for #struct_name #ty_generics #where_reflect_clause { + impl #impl_generics #bevy_reflect_path::Reflect for #struct_path #ty_generics #where_reflect_clause { #[inline] fn type_name(&self) -> &str { ::core::any::type_name::() @@ -139,6 +144,11 @@ pub(crate) fn impl_tuple_struct(reflect_struct: &ReflectStruct) -> TokenStream { #FQOption::Some(::type_info()) } + #[inline] + fn get_type_path(&self) -> &dyn #bevy_reflect_path::DynamicTypePath { + self + } + #[inline] fn into_any(self: #FQBox) -> #FQBox { self diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/impls/typed.rs b/crates/bevy_reflect/bevy_reflect_derive/src/impls/typed.rs index e0b88c92e44d4..290154692c496 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/impls/typed.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/impls/typed.rs @@ -1,43 +1,149 @@ -use crate::utility::{extend_where_clause, WhereClauseOptions}; -use proc_macro2::Ident; -use quote::quote; -use syn::{Generics, Path}; +use crate::utility::{extend_where_clause, StringExpr, WhereClauseOptions}; +use quote::{quote, ToTokens}; -#[allow(clippy::too_many_arguments)] -pub(crate) fn impl_typed( - type_name: &Ident, - generics: &Generics, - where_clause_options: &WhereClauseOptions, +use crate::{ + derive_data::{ReflectMeta, ReflectTypePath}, + utility::wrap_in_option, +}; + +/// Returns an expression for a `NonGenericTypeCell` or `GenericTypeCell` to generate `'static` references. +fn static_type_cell( + meta: &ReflectMeta, + property: TypedProperty, generator: proc_macro2::TokenStream, - bevy_reflect_path: &Path, ) -> proc_macro2::TokenStream { - let is_generic = !generics.params.is_empty(); + let bevy_reflect_path = meta.bevy_reflect_path(); + if meta.type_path().impl_is_generic() { + let cell_type = match property { + TypedProperty::TypePath => quote!(GenericTypePathCell), + TypedProperty::TypeInfo => quote!(GenericTypeInfoCell), + }; - let static_generator = if is_generic { quote! { - static CELL: #bevy_reflect_path::utility::GenericTypeInfoCell = #bevy_reflect_path::utility::GenericTypeInfoCell::new(); + static CELL: #bevy_reflect_path::utility::#cell_type = #bevy_reflect_path::utility::#cell_type::new(); CELL.get_or_insert::(|| { #generator }) } } else { + let cell_type = match property { + TypedProperty::TypePath => unreachable!( + "Cannot have a non-generic type path cell. Use string literals and core::concat instead." + ), + TypedProperty::TypeInfo => quote!(NonGenericTypeInfoCell), + }; + quote! { - static CELL: #bevy_reflect_path::utility::NonGenericTypeInfoCell = #bevy_reflect_path::utility::NonGenericTypeInfoCell::new(); + static CELL: #bevy_reflect_path::utility::#cell_type = #bevy_reflect_path::utility::#cell_type::new(); CELL.get_or_set(|| { #generator }) } + } +} + +#[derive(Clone, Copy)] +pub(crate) enum TypedProperty { + TypeInfo, + TypePath, +} + +pub(crate) fn impl_type_path( + meta: &ReflectMeta, + where_clause_options: &WhereClauseOptions, +) -> proc_macro2::TokenStream { + let type_path = meta.type_path(); + let bevy_reflect_path = meta.bevy_reflect_path(); + + let (long_type_path, short_type_path) = if type_path.impl_is_generic() { + let long_path_cell = static_type_cell( + meta, + TypedProperty::TypePath, + type_path.long_type_path(bevy_reflect_path).into_owned(), + ); + let short_path_cell = static_type_cell( + meta, + TypedProperty::TypePath, + type_path.short_type_path(bevy_reflect_path).into_owned(), + ); + ( + long_path_cell.to_token_stream(), + short_path_cell.to_token_stream(), + ) + } else { + ( + type_path.long_type_path(bevy_reflect_path).into_borrowed(), + type_path.short_type_path(bevy_reflect_path).into_borrowed(), + ) + }; + + let type_ident = wrap_in_option(type_path.type_ident().map(StringExpr::into_borrowed)); + let module_path = wrap_in_option(type_path.module_path().map(StringExpr::into_borrowed)); + let crate_name = wrap_in_option(type_path.crate_name().map(StringExpr::into_borrowed)); + + let primitive_assert = if let ReflectTypePath::Primitive(_) = type_path { + Some(quote! { + const _: () = { + mod private_scope { + // Compiles if it can be named when there are no imports. + type AssertIsPrimitive = #type_path; + } + }; + }) + } else { + None }; - let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); + let (impl_generics, ty_generics, where_clause) = type_path.generics().split_for_impl(); // Add Typed bound for each active field let where_reflect_clause = extend_where_clause(where_clause, where_clause_options); quote! { - impl #impl_generics #bevy_reflect_path::Typed for #type_name #ty_generics #where_reflect_clause { + #primitive_assert + + impl #impl_generics #bevy_reflect_path::TypePath for #type_path #ty_generics #where_reflect_clause { + fn type_path() -> &'static str { + #long_type_path + } + + fn short_type_path() -> &'static str { + #short_type_path + } + + fn type_ident() -> Option<&'static str> { + #type_ident + } + + fn crate_name() -> Option<&'static str> { + #crate_name + } + + fn module_path() -> Option<&'static str> { + #module_path + } + } + } +} + +pub(crate) fn impl_typed( + meta: &ReflectMeta, + where_clause_options: &WhereClauseOptions, + type_info_generator: proc_macro2::TokenStream, +) -> proc_macro2::TokenStream { + let type_path = meta.type_path(); + let bevy_reflect_path = meta.bevy_reflect_path(); + + let type_info_cell = static_type_cell(meta, TypedProperty::TypeInfo, type_info_generator); + + let (impl_generics, ty_generics, where_clause) = type_path.generics().split_for_impl(); + + let where_reflect_clause = extend_where_clause(where_clause, where_clause_options); + + quote! { + impl #impl_generics #bevy_reflect_path::Typed for #type_path #ty_generics #where_reflect_clause { fn type_info() -> &'static #bevy_reflect_path::TypeInfo { - #static_generator + #type_info_cell } } } diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/impls/values.rs b/crates/bevy_reflect/bevy_reflect_derive/src/impls/values.rs index 964ef1f2ce8a8..b61b1ca9baf0b 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/impls/values.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/impls/values.rs @@ -1,6 +1,6 @@ use crate::fq_std::{FQAny, FQBox, FQClone, FQOption, FQResult}; -use crate::impls::impl_typed; -use crate::utility::WhereClauseOptions; +use crate::impls::{impl_type_path, impl_typed}; +use crate::utility::{extend_where_clause, WhereClauseOptions}; use crate::ReflectMeta; use proc_macro::TokenStream; use quote::quote; @@ -8,7 +8,7 @@ use quote::quote; /// Implements `GetTypeRegistration` and `Reflect` for the given type data. pub(crate) fn impl_value(meta: &ReflectMeta) -> TokenStream { let bevy_reflect_path = meta.bevy_reflect_path(); - let type_name = meta.type_name(); + let type_path = meta.type_path(); let hash_fn = meta.traits().get_hash_impl(bevy_reflect_path); let partial_eq_fn = meta.traits().get_partial_eq_impl(bevy_reflect_path); @@ -22,27 +22,30 @@ pub(crate) fn impl_value(meta: &ReflectMeta) -> TokenStream { #[cfg(not(feature = "documentation"))] let with_docs: Option = None; - let where_clause_options = WhereClauseOptions::default(); + let where_clause_options = WhereClauseOptions::type_path_bounds(meta); let typed_impl = impl_typed( - type_name, - meta.generics(), + meta, &where_clause_options, quote! { let info = #bevy_reflect_path::ValueInfo::new::() #with_docs; #bevy_reflect_path::TypeInfo::Value(info) }, - bevy_reflect_path, ); - let (impl_generics, ty_generics, where_clause) = meta.generics().split_for_impl(); + let type_path_impl = impl_type_path(meta, &where_clause_options); + + let (impl_generics, ty_generics, where_clause) = type_path.generics().split_for_impl(); + let where_reflect_clause = extend_where_clause(where_clause, &where_clause_options); let get_type_registration_impl = meta.get_type_registration(&where_clause_options); TokenStream::from(quote! { #get_type_registration_impl + #type_path_impl + #typed_impl - impl #impl_generics #bevy_reflect_path::Reflect for #type_name #ty_generics #where_clause { + impl #impl_generics #bevy_reflect_path::Reflect for #type_path #ty_generics #where_reflect_clause { #[inline] fn type_name(&self) -> &str { ::core::any::type_name::() @@ -53,6 +56,11 @@ pub(crate) fn impl_value(meta: &ReflectMeta) -> TokenStream { #FQOption::Some(::type_info()) } + #[inline] + fn get_type_path(&self) -> &dyn #bevy_reflect_path::DynamicTypePath { + self + } + #[inline] fn into_any(self: #FQBox) -> #FQBox { self diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs b/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs index f7f2859d10734..0956fe785ecaf 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/lib.rs @@ -26,20 +26,27 @@ mod impls; mod reflect_value; mod registration; mod trait_reflection; +mod type_path; mod type_uuid; mod utility; use crate::derive_data::{ReflectDerive, ReflectMeta, ReflectStruct}; use crate::type_uuid::gen_impl_type_uuid; +use container_attributes::ReflectTraits; +use derive_data::ReflectTypePath; use proc_macro::TokenStream; use quote::quote; use reflect_value::ReflectValueDef; use syn::spanned::Spanned; use syn::{parse_macro_input, DeriveInput}; +use type_path::NamedTypePathDef; use type_uuid::TypeUuidDef; +use utility::WhereClauseOptions; pub(crate) static REFLECT_ATTRIBUTE_NAME: &str = "reflect"; pub(crate) static REFLECT_VALUE_ATTRIBUTE_NAME: &str = "reflect_value"; +pub(crate) static TYPE_PATH_ATTRIBUTE_NAME: &str = "type_path"; +pub(crate) static TYPE_NAME_ATTRIBUTE_NAME: &str = "type_name"; /// The main derive macro used by `bevy_reflect` for deriving its `Reflect` trait. /// @@ -52,6 +59,8 @@ pub(crate) static REFLECT_VALUE_ATTRIBUTE_NAME: &str = "reflect_value"; /// This macro comes with some helper attributes that can be added to the container item /// in order to provide additional functionality or alter the generated implementations. /// +/// In addition to those listed, this macro can also use the attributes for [`TypePath`] derives. +/// /// ## `#[reflect(Ident)]` /// /// The `#[reflect(Ident)]` attribute is used to add type data registrations to the `GetTypeRegistration` @@ -124,7 +133,7 @@ pub(crate) static REFLECT_VALUE_ATTRIBUTE_NAME: &str = "reflect_value"; /// which will be used by the reflection serializers to determine whether or not the field is serializable. /// /// [`reflect_trait`]: macro@reflect_trait -#[proc_macro_derive(Reflect, attributes(reflect, reflect_value))] +#[proc_macro_derive(Reflect, attributes(reflect, reflect_value, type_path, type_name))] pub fn derive_reflect(input: TokenStream) -> TokenStream { let ast = parse_macro_input!(input as DeriveInput); @@ -188,6 +197,36 @@ pub fn derive_from_reflect(input: TokenStream) -> TokenStream { } } +/// Derives the `TypePath` trait, providing a stable alternative to [`std::any::type_name`]. +/// +/// # Container Attributes +/// +/// ## `#[type_path = "my_crate::foo"]` +/// +/// Optionally specifies a custom module path to use instead of [`module_path`]. +/// +/// This path does not include the final identifier. +/// +/// ## `#[type_name = "RenamedType"]` +/// +/// Optionally specifies a new terminating identifier for `TypePath`. +/// +/// To use this attribute, `#[type_path = "..."]` must also be specified. +#[proc_macro_derive(TypePath, attributes(type_path, type_name))] +pub fn derive_type_path(input: TokenStream) -> TokenStream { + let ast = parse_macro_input!(input as DeriveInput); + let derive_data = match ReflectDerive::from_input(&ast) { + Ok(data) => data, + Err(err) => return err.into_compile_error().into(), + }; + + impls::impl_type_path( + derive_data.meta(), + &WhereClauseOptions::type_path_bounds(derive_data.meta()), + ) + .into() +} + // From https://github.com/randomPoison/type-uuid #[proc_macro_derive(TypeUuid, attributes(uuid))] pub fn derive_type_uuid(input: TokenStream) -> TokenStream { @@ -254,30 +293,47 @@ pub fn reflect_trait(args: TokenStream, input: TokenStream) -> TokenStream { /// The only reason for this macro's existence is so that `bevy_reflect` can easily implement the reflection traits /// on primitives and other Rust types internally. /// +/// Since this macro also implements `TypePath`, the type path must be explicit. +/// See [`impl_type_path!`] for the exact syntax. +/// /// # Examples /// /// Types can be passed with or without registering type data: /// /// ```ignore -/// impl_reflect_value!(foo); -/// impl_reflect_value!(bar(Debug, Default, Serialize, Deserialize)); +/// impl_reflect_value!(::my_crate::Foo); +/// impl_reflect_value!(::my_crate::Bar(Debug, Default, Serialize, Deserialize)); /// ``` /// /// Generic types can also specify their parameters and bounds: /// /// ```ignore -/// impl_reflect_value!(foo where T1: Bar (Default, Serialize, Deserialize)); +/// impl_reflect_value!(::my_crate::Foo where T1: Bar (Default, Serialize, Deserialize)); +/// ``` +/// +/// Custom type paths can be specified: +/// +/// ```ignore +/// impl_reflect_value!((in not_my_crate as NotFoo) Foo(Debug, Default)); /// ``` /// /// [deriving `Reflect`]: Reflect #[proc_macro] pub fn impl_reflect_value(input: TokenStream) -> TokenStream { let def = parse_macro_input!(input as ReflectValueDef); - let meta = ReflectMeta::new( - &def.type_name, - &def.generics, - def.traits.unwrap_or_default(), - ); + + let default_name = &def.type_path.segments.last().unwrap().ident; + let type_path = if def.type_path.leading_colon.is_none() && def.custom_path.is_none() { + ReflectTypePath::Primitive(default_name) + } else { + ReflectTypePath::External { + path: &def.type_path, + custom_path: def.custom_path.map(|path| path.into_path(default_name)), + generics: &def.generics, + } + }; + + let meta = ReflectMeta::new(type_path, def.traits.unwrap_or_default()); #[cfg(feature = "documentation")] let meta = meta.with_docs(documentation::Documentation::from_attributes(&def.attrs)); @@ -294,23 +350,27 @@ pub fn impl_reflect_value(input: TokenStream) -> TokenStream { /// which have greater functionality. The type being reflected must be in scope, as you cannot /// qualify it in the macro as e.g. `bevy::prelude::Vec3`. /// +/// It is necessary to add a `#[type_path = "my_crate::foo"]` attribute to all types. +/// /// It may be necessary to add `#[reflect(Default)]` for some types, specifically non-constructible /// foreign types. Without `Default` reflected for such types, you will usually get an arcane /// error message and fail to compile. If the type does not implement `Default`, it may not /// be possible to reflect without extending the macro. /// +/// /// # Example /// Implementing `Reflect` for `bevy::prelude::Vec3` as a struct type: /// ```ignore /// use bevy::prelude::Vec3; /// /// impl_reflect_struct!( -/// #[reflect(PartialEq, Serialize, Deserialize, Default)] -/// struct Vec3 { -/// x: f32, -/// y: f32, -/// z: f32 -/// } +/// #[reflect(PartialEq, Serialize, Deserialize, Default)] +/// #[type_path = "bevy::prelude"] +/// struct Vec3 { +/// x: f32, +/// y: f32, +/// z: f32 +/// } /// ); /// ``` #[proc_macro] @@ -323,6 +383,15 @@ pub fn impl_reflect_struct(input: TokenStream) -> TokenStream { match derive_data { ReflectDerive::Struct(struct_data) => { + if !struct_data.meta().type_path().has_custom_path() { + return syn::Error::new( + struct_data.meta().type_path().span(), + format!("a #[{TYPE_PATH_ATTRIBUTE_NAME} = \"...\"] attribute must be specified when using `impl_reflect_struct`") + ) + .into_compile_error() + .into(); + } + let impl_struct: proc_macro2::TokenStream = impls::impl_struct(&struct_data).into(); let impl_from_struct: proc_macro2::TokenStream = from_reflect::impl_struct(&struct_data).into(); @@ -370,11 +439,84 @@ pub fn impl_reflect_struct(input: TokenStream) -> TokenStream { #[proc_macro] pub fn impl_from_reflect_value(input: TokenStream) -> TokenStream { let def = parse_macro_input!(input as ReflectValueDef); - from_reflect::impl_value(&ReflectMeta::new( - &def.type_name, - &def.generics, - def.traits.unwrap_or_default(), - )) + + let default_name = &def.type_path.segments.last().unwrap().ident; + let type_path = if def.type_path.leading_colon.is_none() + && def.custom_path.is_none() + && def.generics.params.is_empty() + { + ReflectTypePath::Primitive(default_name) + } else { + ReflectTypePath::External { + path: &def.type_path, + custom_path: def.custom_path.map(|alias| alias.into_path(default_name)), + generics: &def.generics, + } + }; + + from_reflect::impl_value(&ReflectMeta::new(type_path, def.traits.unwrap_or_default())) +} + +/// A replacement for [deriving `TypePath`] for use on foreign types. +/// +/// Since (unlike the derive) this macro may be invoked in a different module to where the type is defined, +/// it requires an 'absolute' path definition. +/// +/// Specifically, a leading `::` denoting a global path must be specified +/// or a preceeding `(in my_crate::foo)` to specify the custom path must be used. +/// +/// # Examples +/// +/// Implementing `TypePath` on a foreign type: +/// ```rust,ignore +/// impl_type_path!(::foreign_crate::foo::bar::Baz); +/// ``` +/// +/// On a generic type: +/// ```rust,ignore +/// impl_type_path!(::foreign_crate::Foo); +/// ``` +/// +/// On a primitive (note this will not compile for a non-primitive type): +/// ```rust,ignore +/// impl_type_path!(bool); +/// ``` +/// +/// With a custom type path: +/// ```rust,ignore +/// impl_type_path!((in other_crate::foo::bar) Baz); +/// ``` +/// +/// With a custom type path and a custom type name: +/// ```rust,ignore +/// impl_type_path!((in other_crate::foo as Baz) Bar); +/// ``` +/// +/// [deriving `TypePath`]: TypePath +#[proc_macro] +pub fn impl_type_path(input: TokenStream) -> TokenStream { + let def = parse_macro_input!(input as NamedTypePathDef); + + let type_path = match def { + NamedTypePathDef::External { + ref path, + custom_path, + ref generics, + } => { + let default_name = &path.segments.last().unwrap().ident; + + ReflectTypePath::External { + path, + custom_path: custom_path.map(|path| path.into_path(default_name)), + generics, + } + } + NamedTypePathDef::Primtive(ref ident) => ReflectTypePath::Primitive(ident), + }; + + let meta = ReflectMeta::new(type_path, ReflectTraits::default()); + + impls::impl_type_path(&meta, &WhereClauseOptions::type_path_bounds(&meta)).into() } /// Derives `TypeUuid` for the given type. This is used internally to implement `TypeUuid` on foreign types, such as those in the std. This macro should be used in the format of `<[Generic Params]> [Type (Path)], [Uuid (String Literal)]`. diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/reflect_value.rs b/crates/bevy_reflect/bevy_reflect_derive/src/reflect_value.rs index 0e71413758dd7..b3d0f663a3a44 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/reflect_value.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/reflect_value.rs @@ -1,58 +1,59 @@ use crate::container_attributes::ReflectTraits; -use proc_macro2::Ident; +use crate::type_path::CustomPathDef; use syn::parse::{Parse, ParseStream}; -use syn::token::{Paren, Where}; -use syn::{parenthesized, Attribute, Generics}; +use syn::token::Paren; +use syn::{parenthesized, Attribute, Generics, Path}; /// A struct used to define a simple reflected value type (such as primitives). /// +/// +/// /// This takes the form: /// /// ```ignore /// // Standard -/// foo(TraitA, TraitB) +/// ::my_crate::foo::Bar(TraitA, TraitB) /// /// // With generics -/// foo(TraitA, TraitB) +/// ::my_crate::foo::Bar(TraitA, TraitB) /// /// // With generics and where clause -/// foo where T1: Bar (TraitA, TraitB) +/// ::my_crate::foo::Bar where T1: Bar (TraitA, TraitB) +/// +/// // With a custom path (not with impl_from_reflect_value) +/// (in my_crate::bar) Bar(TraitA, TraitB) /// ``` pub(crate) struct ReflectValueDef { #[allow(dead_code)] pub attrs: Vec, - pub type_name: Ident, + pub type_path: Path, pub generics: Generics, pub traits: Option, + pub custom_path: Option, } impl Parse for ReflectValueDef { fn parse(input: ParseStream) -> syn::Result { let attrs = input.call(Attribute::parse_outer)?; - let type_ident = input.parse::()?; - let generics = input.parse::()?; - let mut lookahead = input.lookahead1(); - let mut where_clause = None; - if lookahead.peek(Where) { - where_clause = Some(input.parse()?); - lookahead = input.lookahead1(); - } + + let custom_path = CustomPathDef::parse_parenthesized(input)?; + + let type_path = Path::parse_mod_style(input)?; + let mut generics = input.parse::()?; + generics.where_clause = input.parse()?; let mut traits = None; - if lookahead.peek(Paren) { + if input.peek(Paren) { let content; parenthesized!(content in input); traits = Some(content.parse::()?); } - Ok(ReflectValueDef { attrs, - type_name: type_ident, - generics: Generics { - where_clause, - ..generics - }, + type_path, + generics, traits, + custom_path, }) } } diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/registration.rs b/crates/bevy_reflect/bevy_reflect_derive/src/registration.rs index 256fd82c55886..09bc88518dae0 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/registration.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/registration.rs @@ -2,21 +2,21 @@ use crate::utility::{extend_where_clause, WhereClauseOptions}; use bit_set::BitSet; -use proc_macro2::Ident; use quote::quote; -use syn::{Generics, Path}; + +use crate::derive_data::ReflectMeta; /// Creates the `GetTypeRegistration` impl for the given type data. #[allow(clippy::too_many_arguments)] pub(crate) fn impl_get_type_registration( - type_name: &Ident, - bevy_reflect_path: &Path, - registration_data: &[Ident], - generics: &Generics, + meta: &ReflectMeta, where_clause_options: &WhereClauseOptions, serialization_denylist: Option<&BitSet>, ) -> proc_macro2::TokenStream { - let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); + let type_path = meta.type_path(); + let bevy_reflect_path = meta.bevy_reflect_path(); + let registration_data = meta.traits().idents(); + let (impl_generics, ty_generics, where_clause) = type_path.generics().split_for_impl(); let serialization_data = serialization_denylist.map(|denylist| { let denylist = denylist.into_iter(); quote! { @@ -29,12 +29,12 @@ pub(crate) fn impl_get_type_registration( quote! { #[allow(unused_mut)] - impl #impl_generics #bevy_reflect_path::GetTypeRegistration for #type_name #ty_generics #where_reflect_clause { + impl #impl_generics #bevy_reflect_path::GetTypeRegistration for #type_path #ty_generics #where_reflect_clause { fn get_type_registration() -> #bevy_reflect_path::TypeRegistration { - let mut registration = #bevy_reflect_path::TypeRegistration::of::<#type_name #ty_generics>(); - registration.insert::<#bevy_reflect_path::ReflectFromPtr>(#bevy_reflect_path::FromType::<#type_name #ty_generics>::from_type()); + let mut registration = #bevy_reflect_path::TypeRegistration::of::(); + registration.insert::<#bevy_reflect_path::ReflectFromPtr>(#bevy_reflect_path::FromType::::from_type()); #serialization_data - #(registration.insert::<#registration_data>(#bevy_reflect_path::FromType::<#type_name #ty_generics>::from_type());)* + #(registration.insert::<#registration_data>(#bevy_reflect_path::FromType::::from_type());)* registration } } diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/type_path.rs b/crates/bevy_reflect/bevy_reflect_derive/src/type_path.rs new file mode 100644 index 0000000000000..5a2f8c9fae190 --- /dev/null +++ b/crates/bevy_reflect/bevy_reflect_derive/src/type_path.rs @@ -0,0 +1,102 @@ +use proc_macro2::Ident; +use syn::{ + parenthesized, + parse::{Parse, ParseStream}, + token::Paren, + Generics, Path, PathSegment, Token, +}; + +pub(crate) fn parse_path_no_leading_colon(input: ParseStream) -> syn::Result { + if input.peek(Token![::]) { + return Err(input.error("did not expect a leading double colon (`::`)")); + } + + let path = Path::parse_mod_style(input)?; + + if path.segments.is_empty() { + Err(input.error("expected a path")) + } else { + Ok(path) + } +} + +/// An alias for a `TypePath`. +/// +/// This is the parenthesized part of `(in my_crate::foo as MyType) SomeType`. +pub(crate) struct CustomPathDef { + path: Path, + name: Option, +} + +impl CustomPathDef { + pub fn into_path(mut self, default_name: &Ident) -> Path { + let name = PathSegment::from(self.name.unwrap_or_else(|| default_name.clone())); + self.path.segments.push(name); + self.path + } + + pub fn parse_parenthesized(input: ParseStream) -> syn::Result> { + if input.peek(Paren) { + let path; + parenthesized!(path in input); + Ok(Some(path.call(Self::parse)?)) + } else { + Ok(None) + } + } + + fn parse(input: ParseStream) -> syn::Result { + input.parse::()?; + + let custom_path = parse_path_no_leading_colon(input)?; + + if !input.peek(Token![as]) { + return Ok(Self { + path: custom_path, + name: None, + }); + } + + input.parse::()?; + let custom_name: Ident = input.parse()?; + + Ok(Self { + path: custom_path, + name: Some(custom_name), + }) + } +} + +pub(crate) enum NamedTypePathDef { + External { + path: Path, + generics: Generics, + custom_path: Option, + }, + Primtive(Ident), +} + +impl Parse for NamedTypePathDef { + fn parse(input: ParseStream) -> syn::Result { + let custom_path = CustomPathDef::parse_parenthesized(input)?; + + let path = Path::parse_mod_style(input)?; + let mut generics = input.parse::()?; + generics.where_clause = input.parse()?; + + if path.leading_colon.is_none() && custom_path.is_none() { + if path.segments.len() == 1 { + let ident = path.segments.into_iter().next().unwrap().ident; + Ok(NamedTypePathDef::Primtive(ident)) + } else { + Err(input.error("non-customized paths must start with a double colon (`::`)")) + } + } else { + Ok(NamedTypePathDef::External { + path, + generics, + custom_path, + }) + } + } +} diff --git a/crates/bevy_reflect/bevy_reflect_derive/src/utility.rs b/crates/bevy_reflect/bevy_reflect_derive/src/utility.rs index 76d45f39e24a5..b8efa3f530e62 100644 --- a/crates/bevy_reflect/bevy_reflect_derive/src/utility.rs +++ b/crates/bevy_reflect/bevy_reflect_derive/src/utility.rs @@ -1,11 +1,11 @@ //! General-purpose utility functions for internal usage within this crate. -use crate::field_attributes::ReflectIgnoreBehavior; +use crate::{derive_data::ReflectMeta, field_attributes::ReflectIgnoreBehavior, fq_std::FQOption}; use bevy_macro_utils::BevyManifest; use bit_set::BitSet; use proc_macro2::{Ident, Span}; -use quote::quote; -use syn::{Member, Path, Type, WhereClause}; +use quote::{quote, ToTokens}; +use syn::{spanned::Spanned, LitStr, Member, Path, Type, WhereClause}; /// Returns the correct path for `bevy_reflect`. pub(crate) fn get_bevy_reflect_path() -> Path { @@ -60,6 +60,10 @@ pub(crate) fn ident_or_index(ident: Option<&Ident>, index: usize) -> Member { /// Options defining how to extend the `where` clause in reflection with any additional bounds needed. pub(crate) struct WhereClauseOptions { + /// Type parameters that need extra trait bounds. + pub(crate) parameter_types: Box<[Ident]>, + /// Trait bounds to add to the type parameters. + pub(crate) parameter_trait_bounds: proc_macro2::TokenStream, /// Any types that will be reflected and need an extra trait bound pub(crate) active_types: Box<[Type]>, /// Trait bounds to add to the active types @@ -70,12 +74,31 @@ pub(crate) struct WhereClauseOptions { pub(crate) ignored_trait_bounds: proc_macro2::TokenStream, } +impl WhereClauseOptions { + /// Extends a where clause, adding a `TypePath` bound to each type parameter. + pub fn type_path_bounds(meta: &ReflectMeta) -> Self { + let bevy_reflect_path = meta.bevy_reflect_path(); + Self { + parameter_types: meta + .type_path() + .generics() + .type_params() + .map(|ty| ty.ident.clone()) + .collect(), + parameter_trait_bounds: quote! { #bevy_reflect_path::TypePath }, + ..Default::default() + } + } +} + impl Default for WhereClauseOptions { /// By default, don't add any additional bounds to the `where` clause fn default() -> Self { Self { + parameter_types: Box::new([]), active_types: Box::new([]), ignored_types: Box::new([]), + parameter_trait_bounds: quote! {}, active_trait_bounds: quote! {}, ignored_trait_bounds: quote! {}, } @@ -117,22 +140,26 @@ pub(crate) fn extend_where_clause( where_clause: Option<&WhereClause>, where_clause_options: &WhereClauseOptions, ) -> proc_macro2::TokenStream { + let parameter_types = &where_clause_options.parameter_types; let active_types = &where_clause_options.active_types; let ignored_types = &where_clause_options.ignored_types; + let parameter_trait_bounds = &where_clause_options.parameter_trait_bounds; let active_trait_bounds = &where_clause_options.active_trait_bounds; let ignored_trait_bounds = &where_clause_options.ignored_trait_bounds; let mut generic_where_clause = if let Some(where_clause) = where_clause { let predicates = where_clause.predicates.iter(); quote! {where #(#predicates,)*} - } else if !(active_types.is_empty() && ignored_types.is_empty()) { + } else if !(parameter_types.is_empty() && active_types.is_empty() && ignored_types.is_empty()) { quote! {where} } else { - quote! {} + quote!() }; generic_where_clause.extend(quote! { #(#active_types: #active_trait_bounds,)* #(#ignored_types: #ignored_trait_bounds,)* + // Leave parameter bounds to the end for more sane error messages. + #(#parameter_types: #parameter_trait_bounds,)* }); generic_where_clause } @@ -212,3 +239,118 @@ where bitset } + +/// Turns an `Option` into a `TokenStream` for an `Option`. +pub(crate) fn wrap_in_option(tokens: Option) -> proc_macro2::TokenStream { + match tokens { + Some(tokens) => quote! { + #FQOption::Some(#tokens) + }, + None => quote! { + #FQOption::None + }, + } +} + +/// Contains tokens representing different kinds of string. +#[derive(Clone)] +pub(crate) enum StringExpr { + /// A string that is valid at compile time. + /// + /// This is either a string literal like `"mystring"`, + /// or a string created by a macro like [`module_path`] + /// or [`concat`]. + Const(proc_macro2::TokenStream), + /// A [string slice](str) that is borrowed for a `'static` lifetime. + Borrowed(proc_macro2::TokenStream), + /// An [owned string](String). + Owned(proc_macro2::TokenStream), +} + +impl From for StringExpr { + fn from(value: T) -> Self { + Self::from_lit(&LitStr::new(&value.to_string(), value.span())) + } +} + +impl StringExpr { + /// Creates a [constant] [`StringExpr`] from a [`struct@LitStr`]. + /// + /// [constant]: StringExpr::Const + pub fn from_lit(lit: &LitStr) -> Self { + Self::Const(lit.to_token_stream()) + } + + /// Creates a [constant] [`StringExpr`] by interpreting a [string slice][str] as a [`struct@LitStr`]. + /// + /// [constant]: StringExpr::Const + pub fn from_str(string: &str) -> Self { + Self::Const(string.into_token_stream()) + } + + /// Returns tokens for an [owned string](String). + /// + /// The returned expression will allocate unless the [`StringExpr`] is [already owned]. + /// + /// [already owned]: StringExpr::Owned + pub fn into_owned(self) -> proc_macro2::TokenStream { + match self { + Self::Const(tokens) | Self::Borrowed(tokens) => quote! { + ::std::string::ToString::to_string(#tokens) + }, + Self::Owned(owned) => owned, + } + } + + /// Returns tokens for a statically borrowed [string slice](str). + pub fn into_borrowed(self) -> proc_macro2::TokenStream { + match self { + Self::Const(tokens) | Self::Borrowed(tokens) => tokens, + Self::Owned(owned) => quote! { + &#owned + }, + } + } + + /// Appends a [`StringExpr`] to another. + /// + /// If both expressions are [`StringExpr::Const`] this will use [`concat`] to merge them. + pub fn appended_by(mut self, other: StringExpr) -> Self { + if let Self::Const(tokens) = self { + if let Self::Const(more) = other { + return Self::Const(quote! { + ::core::concat!(#tokens, #more) + }); + } + self = Self::Const(tokens); + } + + let owned = self.into_owned(); + let borrowed = other.into_borrowed(); + Self::Owned(quote! { + #owned + #borrowed + }) + } +} + +impl Default for StringExpr { + fn default() -> Self { + StringExpr::from_str("") + } +} + +impl FromIterator for StringExpr { + fn from_iter>(iter: T) -> Self { + let mut iter = iter.into_iter(); + match iter.next() { + Some(mut expr) => { + for next in iter { + expr = expr.appended_by(next); + } + + expr + } + None => Default::default(), + } + } +} diff --git a/crates/bevy_reflect/src/array.rs b/crates/bevy_reflect/src/array.rs index 0367e1add4f79..7a4375f83a6a0 100644 --- a/crates/bevy_reflect/src/array.rs +++ b/crates/bevy_reflect/src/array.rs @@ -1,4 +1,9 @@ -use crate::{utility::reflect_hasher, Reflect, ReflectMut, ReflectOwned, ReflectRef, TypeInfo}; +use bevy_reflect_derive::impl_type_path; + +use crate::{ + self as bevy_reflect, utility::reflect_hasher, DynamicTypePath, Reflect, ReflectMut, + ReflectOwned, ReflectRef, TypeInfo, +}; use std::{ any::{Any, TypeId}, fmt::Debug, @@ -221,6 +226,11 @@ impl Reflect for DynamicArray { self.represented_type } + #[inline] + fn get_type_path(&self) -> &dyn DynamicTypePath { + self + } + #[inline] fn into_any(self: Box) -> Box { self @@ -335,6 +345,7 @@ impl Array for DynamicArray { } } +impl_type_path!((in bevy_reflect) DynamicArray); /// An iterator over an [`Array`]. pub struct ArrayIter<'a> { array: &'a dyn Array, diff --git a/crates/bevy_reflect/src/enums/dynamic_enum.rs b/crates/bevy_reflect/src/enums/dynamic_enum.rs index 1e4cce12780d7..6edc256c68439 100644 --- a/crates/bevy_reflect/src/enums/dynamic_enum.rs +++ b/crates/bevy_reflect/src/enums/dynamic_enum.rs @@ -1,6 +1,9 @@ +use bevy_reflect_derive::impl_type_path; + use crate::{ - enum_debug, enum_hash, enum_partial_eq, DynamicStruct, DynamicTuple, Enum, Reflect, ReflectMut, - ReflectOwned, ReflectRef, Struct, Tuple, TypeInfo, VariantFieldIter, VariantType, + self as bevy_reflect, enum_debug, enum_hash, enum_partial_eq, DynamicStruct, DynamicTuple, + DynamicTypePath, Enum, Reflect, ReflectMut, ReflectOwned, ReflectRef, Struct, Tuple, TypeInfo, + VariantFieldIter, VariantType, }; use std::any::Any; use std::fmt::Formatter; @@ -297,6 +300,11 @@ impl Reflect for DynamicEnum { self.represented_type } + #[inline] + fn get_type_path(&self) -> &dyn DynamicTypePath { + self + } + #[inline] fn into_any(self: Box) -> Box { self @@ -420,3 +428,5 @@ impl Reflect for DynamicEnum { write!(f, ")") } } + +impl_type_path!((in bevy_reflect) DynamicEnum); diff --git a/crates/bevy_reflect/src/impls/glam.rs b/crates/bevy_reflect/src/impls/glam.rs index bb3ab96e3a365..4a3f50cc62b6f 100644 --- a/crates/bevy_reflect/src/impls/glam.rs +++ b/crates/bevy_reflect/src/impls/glam.rs @@ -6,6 +6,7 @@ use glam::*; impl_reflect_struct!( #[reflect(Debug, Hash, PartialEq, Default)] + #[type_path = "glam"] struct IVec2 { x: i32, y: i32, @@ -13,6 +14,7 @@ impl_reflect_struct!( ); impl_reflect_struct!( #[reflect(Debug, Hash, PartialEq, Default)] + #[type_path = "glam"] struct IVec3 { x: i32, y: i32, @@ -21,6 +23,7 @@ impl_reflect_struct!( ); impl_reflect_struct!( #[reflect(Debug, Hash, PartialEq, Default)] + #[type_path = "glam"] struct IVec4 { x: i32, y: i32, @@ -31,6 +34,7 @@ impl_reflect_struct!( impl_reflect_struct!( #[reflect(Debug, Hash, PartialEq, Default)] + #[type_path = "glam"] struct UVec2 { x: u32, y: u32, @@ -38,6 +42,7 @@ impl_reflect_struct!( ); impl_reflect_struct!( #[reflect(Debug, Hash, PartialEq, Default)] + #[type_path = "glam"] struct UVec3 { x: u32, y: u32, @@ -46,6 +51,7 @@ impl_reflect_struct!( ); impl_reflect_struct!( #[reflect(Debug, Hash, PartialEq, Default)] + #[type_path = "glam"] struct UVec4 { x: u32, y: u32, @@ -53,9 +59,9 @@ impl_reflect_struct!( w: u32, } ); - impl_reflect_struct!( #[reflect(Debug, PartialEq, Default)] + #[type_path = "glam"] struct Vec2 { x: f32, y: f32, @@ -63,6 +69,7 @@ impl_reflect_struct!( ); impl_reflect_struct!( #[reflect(Debug, PartialEq, Default)] + #[type_path = "glam"] struct Vec3 { x: f32, y: f32, @@ -71,6 +78,7 @@ impl_reflect_struct!( ); impl_reflect_struct!( #[reflect(Debug, PartialEq, Default)] + #[type_path = "glam"] struct Vec3A { x: f32, y: f32, @@ -79,6 +87,7 @@ impl_reflect_struct!( ); impl_reflect_struct!( #[reflect(Debug, PartialEq, Default)] + #[type_path = "glam"] struct Vec4 { x: f32, y: f32, @@ -89,6 +98,7 @@ impl_reflect_struct!( impl_reflect_struct!( #[reflect(Debug, PartialEq, Default)] + #[type_path = "glam"] struct BVec2 { x: bool, y: bool, @@ -96,6 +106,7 @@ impl_reflect_struct!( ); impl_reflect_struct!( #[reflect(Debug, PartialEq, Default)] + #[type_path = "glam"] struct BVec3 { x: bool, y: bool, @@ -104,6 +115,7 @@ impl_reflect_struct!( ); impl_reflect_struct!( #[reflect(Debug, PartialEq, Default)] + #[type_path = "glam"] struct BVec4 { x: bool, y: bool, @@ -114,6 +126,7 @@ impl_reflect_struct!( impl_reflect_struct!( #[reflect(Debug, PartialEq, Default)] + #[type_path = "glam"] struct DVec2 { x: f64, y: f64, @@ -121,6 +134,7 @@ impl_reflect_struct!( ); impl_reflect_struct!( #[reflect(Debug, PartialEq, Default)] + #[type_path = "glam"] struct DVec3 { x: f64, y: f64, @@ -129,6 +143,7 @@ impl_reflect_struct!( ); impl_reflect_struct!( #[reflect(Debug, PartialEq, Default)] + #[type_path = "glam"] struct DVec4 { x: f64, y: f64, @@ -139,6 +154,7 @@ impl_reflect_struct!( impl_reflect_struct!( #[reflect(Debug, PartialEq, Default)] + #[type_path = "glam"] struct Mat2 { x_axis: Vec2, y_axis: Vec2, @@ -146,6 +162,7 @@ impl_reflect_struct!( ); impl_reflect_struct!( #[reflect(Debug, PartialEq, Default)] + #[type_path = "glam"] struct Mat3 { x_axis: Vec3, y_axis: Vec3, @@ -154,6 +171,7 @@ impl_reflect_struct!( ); impl_reflect_struct!( #[reflect(Debug, PartialEq, Default)] + #[type_path = "glam"] struct Mat3A { x_axis: Vec3A, y_axis: Vec3A, @@ -162,6 +180,7 @@ impl_reflect_struct!( ); impl_reflect_struct!( #[reflect(Debug, PartialEq, Default)] + #[type_path = "glam"] struct Mat4 { x_axis: Vec4, y_axis: Vec4, @@ -172,6 +191,7 @@ impl_reflect_struct!( impl_reflect_struct!( #[reflect(Debug, PartialEq, Default)] + #[type_path = "glam"] struct DMat2 { x_axis: DVec2, y_axis: DVec2, @@ -179,6 +199,7 @@ impl_reflect_struct!( ); impl_reflect_struct!( #[reflect(Debug, PartialEq, Default)] + #[type_path = "glam"] struct DMat3 { x_axis: DVec3, y_axis: DVec3, @@ -187,6 +208,7 @@ impl_reflect_struct!( ); impl_reflect_struct!( #[reflect(Debug, PartialEq, Default)] + #[type_path = "glam"] struct DMat4 { x_axis: DVec4, y_axis: DVec4, @@ -197,6 +219,7 @@ impl_reflect_struct!( impl_reflect_struct!( #[reflect(Debug, PartialEq, Default)] + #[type_path = "glam"] struct Affine2 { matrix2: Mat2, translation: Vec2, @@ -204,6 +227,7 @@ impl_reflect_struct!( ); impl_reflect_struct!( #[reflect(Debug, PartialEq, Default)] + #[type_path = "glam"] struct Affine3A { matrix3: Mat3A, translation: Vec3A, @@ -212,6 +236,7 @@ impl_reflect_struct!( impl_reflect_struct!( #[reflect(Debug, PartialEq, Default)] + #[type_path = "glam"] struct DAffine2 { matrix2: DMat2, translation: DVec2, @@ -219,6 +244,7 @@ impl_reflect_struct!( ); impl_reflect_struct!( #[reflect(Debug, PartialEq, Default)] + #[type_path = "glam"] struct DAffine3 { matrix3: DMat3, translation: DVec3, @@ -229,12 +255,24 @@ impl_reflect_struct!( // mechanisms for read-only fields. I doubt those mechanisms would be added, // so for now quaternions will remain as values. They are represented identically // to Vec4 and DVec4, so you may use those instead and convert between. -impl_reflect_value!(Quat(Debug, PartialEq, Serialize, Deserialize, Default)); -impl_reflect_value!(DQuat(Debug, PartialEq, Serialize, Deserialize, Default)); +impl_reflect_value!(::glam::Quat( + Debug, + PartialEq, + Serialize, + Deserialize, + Default +)); +impl_reflect_value!(::glam::DQuat( + Debug, + PartialEq, + Serialize, + Deserialize, + Default +)); impl_from_reflect_value!(Quat); impl_from_reflect_value!(DQuat); -impl_reflect_value!(EulerRot(Debug, Default)); -impl_reflect_value!(BVec3A(Debug, Default)); -impl_reflect_value!(BVec4A(Debug, Default)); +impl_reflect_value!(::glam::EulerRot(Debug, Default)); +impl_reflect_value!(::glam::BVec3A(Debug, Default)); +impl_reflect_value!(::glam::BVec4A(Debug, Default)); diff --git a/crates/bevy_reflect/src/impls/rect.rs b/crates/bevy_reflect/src/impls/rect.rs index 9db5e1cd85196..b215a58599d20 100644 --- a/crates/bevy_reflect/src/impls/rect.rs +++ b/crates/bevy_reflect/src/impls/rect.rs @@ -6,6 +6,7 @@ use bevy_reflect_derive::impl_reflect_struct; impl_reflect_struct!( #[reflect(Debug, PartialEq, Serialize, Deserialize, Default)] + #[type_path = "bevy_math"] struct Rect { min: Vec2, max: Vec2, diff --git a/crates/bevy_reflect/src/impls/smallvec.rs b/crates/bevy_reflect/src/impls/smallvec.rs index 918dc849b62eb..eb6a4b1c025ad 100644 --- a/crates/bevy_reflect/src/impls/smallvec.rs +++ b/crates/bevy_reflect/src/impls/smallvec.rs @@ -1,13 +1,15 @@ +use bevy_reflect_derive::impl_type_path; use smallvec::SmallVec; use std::any::Any; use crate::utility::GenericTypeInfoCell; use crate::{ - FromReflect, FromType, GetTypeRegistration, List, ListInfo, ListIter, Reflect, ReflectFromPtr, - ReflectMut, ReflectOwned, ReflectRef, TypeInfo, TypeRegistration, Typed, + self as bevy_reflect, DynamicTypePath, FromReflect, FromType, GetTypeRegistration, List, + ListInfo, ListIter, Reflect, ReflectFromPtr, ReflectMut, ReflectOwned, ReflectRef, TypeInfo, + TypePath, TypeRegistration, Typed, }; -impl List for SmallVec +impl List for SmallVec where T::Item: FromReflect, { @@ -74,7 +76,7 @@ where } } -impl Reflect for SmallVec +impl Reflect for SmallVec where T::Item: FromReflect, { @@ -86,6 +88,11 @@ where Some(::type_info()) } + #[inline] + fn get_type_path(&self) -> &dyn DynamicTypePath { + self + } + fn into_any(self: Box) -> Box { self } @@ -140,7 +147,7 @@ where } } -impl Typed for SmallVec +impl Typed for SmallVec where T::Item: FromReflect, { @@ -150,7 +157,9 @@ where } } -impl FromReflect for SmallVec +impl_type_path!(::smallvec::SmallVec); + +impl FromReflect for SmallVec where T::Item: FromReflect, { @@ -167,7 +176,7 @@ where } } -impl GetTypeRegistration for SmallVec +impl GetTypeRegistration for SmallVec where T::Item: FromReflect, { diff --git a/crates/bevy_reflect/src/impls/std.rs b/crates/bevy_reflect/src/impls/std.rs index 4637d481691f5..806aff9fa8af0 100644 --- a/crates/bevy_reflect/src/impls/std.rs +++ b/crates/bevy_reflect/src/impls/std.rs @@ -1,14 +1,16 @@ use crate::std_traits::ReflectDefault; use crate::{self as bevy_reflect, ReflectFromPtr, ReflectFromReflect, ReflectOwned}; use crate::{ - map_apply, map_partial_eq, Array, ArrayInfo, ArrayIter, DynamicEnum, DynamicMap, Enum, - EnumInfo, FromReflect, FromType, GetTypeRegistration, List, ListInfo, Map, MapInfo, MapIter, - Reflect, ReflectDeserialize, ReflectMut, ReflectRef, ReflectSerialize, TupleVariantInfo, - TypeInfo, TypeRegistration, Typed, UnitVariantInfo, UnnamedField, ValueInfo, VariantFieldIter, - VariantInfo, VariantType, + impl_type_path, map_apply, map_partial_eq, Array, ArrayInfo, ArrayIter, DynamicEnum, + DynamicMap, DynamicTypePath, Enum, EnumInfo, FromReflect, FromType, GetTypeRegistration, List, + ListInfo, ListIter, Map, MapInfo, MapIter, Reflect, ReflectDeserialize, ReflectMut, ReflectRef, + ReflectSerialize, TupleVariantInfo, TypeInfo, TypePath, TypeRegistration, Typed, + UnitVariantInfo, UnnamedField, ValueInfo, VariantFieldIter, VariantInfo, VariantType, }; -use crate::utility::{reflect_hasher, GenericTypeInfoCell, NonGenericTypeInfoCell}; +use crate::utility::{ + reflect_hasher, GenericTypeInfoCell, GenericTypePathCell, NonGenericTypeInfoCell, +}; use bevy_reflect_derive::{impl_from_reflect_value, impl_reflect_value}; use bevy_utils::HashSet; use bevy_utils::{Duration, Instant}; @@ -93,7 +95,7 @@ impl_reflect_value!(String( Deserialize, Default )); -impl_reflect_value!(PathBuf( +impl_reflect_value!(::std::path::PathBuf( Debug, Hash, PartialEq, @@ -101,15 +103,18 @@ impl_reflect_value!(PathBuf( Deserialize, Default )); -impl_reflect_value!(Result()); -impl_reflect_value!(HashSet()); -impl_reflect_value!(Range()); -impl_reflect_value!(RangeInclusive()); -impl_reflect_value!(RangeFrom()); -impl_reflect_value!(RangeTo()); -impl_reflect_value!(RangeToInclusive()); -impl_reflect_value!(RangeFull()); -impl_reflect_value!(Duration( +impl_reflect_value!( + ::core::result::Result < T: Clone + Reflect + TypePath, + E: Clone + Reflect + TypePath > () +); +impl_reflect_value!(::bevy_utils::HashSet()); +impl_reflect_value!(::core::ops::Range()); +impl_reflect_value!(::core::ops::RangeInclusive()); +impl_reflect_value!(::core::ops::RangeFrom()); +impl_reflect_value!(::core::ops::RangeTo()); +impl_reflect_value!(::core::ops::RangeToInclusive()); +impl_reflect_value!(::core::ops::RangeFull()); +impl_reflect_value!(::bevy_utils::Duration( Debug, Hash, PartialEq, @@ -117,26 +122,104 @@ impl_reflect_value!(Duration( Deserialize, Default )); -impl_reflect_value!(Instant(Debug, Hash, PartialEq)); -impl_reflect_value!(NonZeroI128(Debug, Hash, PartialEq, Serialize, Deserialize)); -impl_reflect_value!(NonZeroU128(Debug, Hash, PartialEq, Serialize, Deserialize)); -impl_reflect_value!(NonZeroIsize(Debug, Hash, PartialEq, Serialize, Deserialize)); -impl_reflect_value!(NonZeroUsize(Debug, Hash, PartialEq, Serialize, Deserialize)); -impl_reflect_value!(NonZeroI64(Debug, Hash, PartialEq, Serialize, Deserialize)); -impl_reflect_value!(NonZeroU64(Debug, Hash, PartialEq, Serialize, Deserialize)); -impl_reflect_value!(NonZeroU32(Debug, Hash, PartialEq, Serialize, Deserialize)); -impl_reflect_value!(NonZeroI32(Debug, Hash, PartialEq, Serialize, Deserialize)); -impl_reflect_value!(NonZeroI16(Debug, Hash, PartialEq, Serialize, Deserialize)); -impl_reflect_value!(NonZeroU16(Debug, Hash, PartialEq, Serialize, Deserialize)); -impl_reflect_value!(NonZeroU8(Debug, Hash, PartialEq, Serialize, Deserialize)); -impl_reflect_value!(NonZeroI8(Debug, Hash, PartialEq, Serialize, Deserialize)); +impl_reflect_value!(::bevy_utils::Instant(Debug, Hash, PartialEq)); +impl_reflect_value!(::core::num::NonZeroI128( + Debug, + Hash, + PartialEq, + Serialize, + Deserialize +)); +impl_reflect_value!(::core::num::NonZeroU128( + Debug, + Hash, + PartialEq, + Serialize, + Deserialize +)); +impl_reflect_value!(::core::num::NonZeroIsize( + Debug, + Hash, + PartialEq, + Serialize, + Deserialize +)); +impl_reflect_value!(::core::num::NonZeroUsize( + Debug, + Hash, + PartialEq, + Serialize, + Deserialize +)); +impl_reflect_value!(::core::num::NonZeroI64( + Debug, + Hash, + PartialEq, + Serialize, + Deserialize +)); +impl_reflect_value!(::core::num::NonZeroU64( + Debug, + Hash, + PartialEq, + Serialize, + Deserialize +)); +impl_reflect_value!(::core::num::NonZeroU32( + Debug, + Hash, + PartialEq, + Serialize, + Deserialize +)); +impl_reflect_value!(::core::num::NonZeroI32( + Debug, + Hash, + PartialEq, + Serialize, + Deserialize +)); +impl_reflect_value!(::core::num::NonZeroI16( + Debug, + Hash, + PartialEq, + Serialize, + Deserialize +)); +impl_reflect_value!(::core::num::NonZeroU16( + Debug, + Hash, + PartialEq, + Serialize, + Deserialize +)); +impl_reflect_value!(::core::num::NonZeroU8( + Debug, + Hash, + PartialEq, + Serialize, + Deserialize +)); +impl_reflect_value!(::core::num::NonZeroI8( + Debug, + Hash, + PartialEq, + Serialize, + Deserialize +)); // `Serialize` and `Deserialize` only for platforms supported by serde: // https://github.com/serde-rs/serde/blob/3ffb86fc70efd3d329519e2dddfa306cc04f167c/serde/src/de/impls.rs#L1732 #[cfg(any(unix, windows))] -impl_reflect_value!(OsString(Debug, Hash, PartialEq, Serialize, Deserialize)); +impl_reflect_value!(::std::ffi::OsString( + Debug, + Hash, + PartialEq, + Serialize, + Deserialize +)); #[cfg(not(any(unix, windows)))] -impl_reflect_value!(OsString(Debug, Hash, PartialEq)); +impl_reflect_value!(::std::ffi::OsString(Debug, Hash, PartialEq)); impl_from_reflect_value!(bool); impl_from_reflect_value!(char); @@ -157,12 +240,12 @@ impl_from_reflect_value!(f64); impl_from_reflect_value!(String); impl_from_reflect_value!(PathBuf); impl_from_reflect_value!(OsString); -impl_from_reflect_value!(HashSet); -impl_from_reflect_value!(Range); -impl_from_reflect_value!(RangeInclusive); -impl_from_reflect_value!(RangeFrom); -impl_from_reflect_value!(RangeTo); -impl_from_reflect_value!(RangeToInclusive); +impl_from_reflect_value!(HashSet); +impl_from_reflect_value!(Range); +impl_from_reflect_value!(RangeInclusive); +impl_from_reflect_value!(RangeFrom); +impl_from_reflect_value!(RangeTo); +impl_from_reflect_value!(RangeToInclusive); impl_from_reflect_value!(RangeFull); impl_from_reflect_value!(Duration); impl_from_reflect_value!(Instant); @@ -180,8 +263,8 @@ impl_from_reflect_value!(NonZeroU8); impl_from_reflect_value!(NonZeroI8); macro_rules! impl_reflect_for_veclike { - ($ty:ty, $insert:expr, $remove:expr, $push:expr, $pop:expr, $sub:ty) => { - impl List for $ty { + ($ty:path, $insert:expr, $remove:expr, $push:expr, $pop:expr, $sub:ty) => { + impl List for $ty { #[inline] fn get(&self, index: usize) -> Option<&dyn Reflect> { <$sub>::get(self, index).map(|value| value as &dyn Reflect) @@ -228,8 +311,8 @@ macro_rules! impl_reflect_for_veclike { } #[inline] - fn iter(&self) -> $crate::ListIter { - $crate::ListIter::new(self) + fn iter(&self) -> ListIter { + ListIter::new(self) } #[inline] @@ -240,7 +323,7 @@ macro_rules! impl_reflect_for_veclike { } } - impl Reflect for $ty { + impl Reflect for $ty { fn type_name(&self) -> &str { std::any::type_name::() } @@ -249,6 +332,11 @@ macro_rules! impl_reflect_for_veclike { Some(::type_info()) } + #[inline] + fn get_type_path(&self) -> &dyn DynamicTypePath { + self + } + fn into_any(self: Box) -> Box { self } @@ -307,14 +395,16 @@ macro_rules! impl_reflect_for_veclike { } } - impl Typed for $ty { + impl Typed for $ty { fn type_info() -> &'static TypeInfo { static CELL: GenericTypeInfoCell = GenericTypeInfoCell::new(); CELL.get_or_insert::(|| TypeInfo::List(ListInfo::new::())) } } - impl GetTypeRegistration for $ty { + impl_type_path!($ty where T: FromReflect); + + impl GetTypeRegistration for $ty { fn get_type_registration() -> TypeRegistration { let mut registration = TypeRegistration::of::<$ty>(); registration.insert::(FromType::<$ty>::from_type()); @@ -322,7 +412,7 @@ macro_rules! impl_reflect_for_veclike { } } - impl FromReflect for $ty { + impl FromReflect for $ty { fn from_reflect(reflect: &dyn Reflect) -> Option { if let ReflectRef::List(ref_list) = reflect.reflect_ref() { let mut new_list = Self::with_capacity(ref_list.len()); @@ -338,9 +428,16 @@ macro_rules! impl_reflect_for_veclike { }; } -impl_reflect_for_veclike!(Vec, Vec::insert, Vec::remove, Vec::push, Vec::pop, [T]); impl_reflect_for_veclike!( - VecDeque, + ::alloc::vec::Vec, + Vec::insert, + Vec::remove, + Vec::push, + Vec::pop, + [T] +); +impl_reflect_for_veclike!( + ::alloc::collections::VecDeque, VecDeque::insert, VecDeque::remove, VecDeque::push_back, @@ -349,12 +446,12 @@ impl_reflect_for_veclike!( ); macro_rules! impl_reflect_for_hashmap { - ($ty:ty) => { + ($ty:path) => { impl Map for $ty where - K: FromReflect + Eq + Hash, - V: FromReflect, - S: BuildHasher + Send + Sync + 'static, + K: FromReflect + TypePath + Eq + Hash, + V: FromReflect + TypePath, + S: TypePath + BuildHasher + Send + Sync, { fn get(&self, key: &dyn Reflect) -> Option<&dyn Reflect> { key.downcast_ref::() @@ -446,9 +543,9 @@ macro_rules! impl_reflect_for_hashmap { impl Reflect for $ty where - K: FromReflect + Eq + Hash, - V: FromReflect, - S: BuildHasher + Send + Sync + 'static, + K: FromReflect + TypePath + Eq + Hash, + V: FromReflect + TypePath, + S: TypePath + BuildHasher + Send + Sync, { fn type_name(&self) -> &str { std::any::type_name::() @@ -458,6 +555,10 @@ macro_rules! impl_reflect_for_hashmap { Some(::type_info()) } + fn get_type_path(&self) -> &dyn DynamicTypePath { + self + } + fn into_any(self: Box) -> Box { self } @@ -515,9 +616,9 @@ macro_rules! impl_reflect_for_hashmap { impl Typed for $ty where - K: FromReflect + Eq + Hash, - V: FromReflect, - S: BuildHasher + Send + Sync + 'static, + K: FromReflect + TypePath + Eq + Hash, + V: FromReflect + TypePath, + S: TypePath + BuildHasher + Send + Sync, { fn type_info() -> &'static TypeInfo { static CELL: GenericTypeInfoCell = GenericTypeInfoCell::new(); @@ -527,9 +628,9 @@ macro_rules! impl_reflect_for_hashmap { impl GetTypeRegistration for $ty where - K: FromReflect + Eq + Hash, - V: FromReflect, - S: BuildHasher + Send + Sync + 'static, + K: FromReflect + TypePath + Eq + Hash, + V: FromReflect + TypePath, + S: TypePath + BuildHasher + Send + Sync, { fn get_type_registration() -> TypeRegistration { let mut registration = TypeRegistration::of::(); @@ -540,9 +641,9 @@ macro_rules! impl_reflect_for_hashmap { impl FromReflect for $ty where - K: FromReflect + Eq + Hash, - V: FromReflect, - S: BuildHasher + Default + Send + Sync + 'static, + K: FromReflect + TypePath + Eq + Hash, + V: FromReflect + TypePath, + S: TypePath + BuildHasher + Default + Send + Sync, { fn from_reflect(reflect: &dyn Reflect) -> Option { if let ReflectRef::Map(ref_map) = reflect.reflect_ref() { @@ -561,10 +662,27 @@ macro_rules! impl_reflect_for_hashmap { }; } +impl_reflect_for_hashmap!(::std::collections::HashMap); +impl_type_path!(::std::collections::hash_map::RandomState); +impl_type_path!( + ::std::collections::HashMap + where + K: FromReflect + Eq + Hash + ?Sized, + V: FromReflect + ?Sized, + S: BuildHasher + Send + Sync + 'static, +); + impl_reflect_for_hashmap!(bevy_utils::hashbrown::HashMap); -impl_reflect_for_hashmap!(std::collections::HashMap); +impl_type_path!(::bevy_utils::hashbrown::hash_map::DefaultHashBuilder); +impl_type_path!( + ::bevy_utils::hashbrown::HashMap + where + K: FromReflect + Eq + Hash + ?Sized, + V: FromReflect + ?Sized, + S: BuildHasher + Send + Sync + 'static, +); -impl Array for [T; N] { +impl Array for [T; N] { #[inline] fn get(&self, index: usize) -> Option<&dyn Reflect> { <[T]>::get(self, index).map(|value| value as &dyn Reflect) @@ -593,7 +711,7 @@ impl Array for [T; N] { } } -impl Reflect for [T; N] { +impl Reflect for [T; N] { #[inline] fn type_name(&self) -> &str { std::any::type_name::() @@ -603,6 +721,11 @@ impl Reflect for [T; N] { Some(::type_info()) } + #[inline] + fn get_type_path(&self) -> &dyn DynamicTypePath { + self + } + #[inline] fn into_any(self: Box) -> Box { self @@ -675,7 +798,7 @@ impl Reflect for [T; N] { } } -impl FromReflect for [T; N] { +impl FromReflect for [T; N] { fn from_reflect(reflect: &dyn Reflect) -> Option { if let ReflectRef::Array(ref_array) = reflect.reflect_ref() { let mut temp_vec = Vec::with_capacity(ref_array.len()); @@ -689,13 +812,25 @@ impl FromReflect for [T; N] { } } -impl Typed for [T; N] { +impl Typed for [T; N] { fn type_info() -> &'static TypeInfo { static CELL: GenericTypeInfoCell = GenericTypeInfoCell::new(); CELL.get_or_insert::(|| TypeInfo::Array(ArrayInfo::new::(N))) } } +impl TypePath for [T; N] { + fn type_path() -> &'static str { + static CELL: GenericTypePathCell = GenericTypePathCell::new(); + CELL.get_or_insert::(|| format!("[{t}; {N}]", t = T::type_path())) + } + + fn short_type_path() -> &'static str { + static CELL: GenericTypePathCell = GenericTypePathCell::new(); + CELL.get_or_insert::(|| format!("[{t}; {N}]", t = T::short_type_path())) + } +} + // TODO: // `FromType::from_type` requires `Deserialize<'de>` to be implemented for `T`. // Currently serde only supports `Deserialize<'de>` for arrays up to size 32. @@ -704,7 +839,7 @@ impl Typed for [T; N] { macro_rules! impl_array_get_type_registration { ($($N:expr)+) => { $( - impl GetTypeRegistration for [T; $N] { + impl GetTypeRegistration for [T; $N] { fn get_type_registration() -> TypeRegistration { TypeRegistration::of::<[T; $N]>() } @@ -720,13 +855,13 @@ impl_array_get_type_registration! { 30 31 32 } -impl GetTypeRegistration for Option { +impl GetTypeRegistration for Option { fn get_type_registration() -> TypeRegistration { TypeRegistration::of::>() } } -impl Enum for Option { +impl Enum for Option { fn field(&self, _name: &str) -> Option<&dyn Reflect> { None } @@ -797,7 +932,7 @@ impl Enum for Option { } } -impl Reflect for Option { +impl Reflect for Option { #[inline] fn type_name(&self) -> &str { std::any::type_name::() @@ -808,6 +943,11 @@ impl Reflect for Option { Some(::type_info()) } + #[inline] + fn get_type_path(&self) -> &dyn DynamicTypePath { + self + } + #[inline] fn into_any(self: Box) -> Box { self @@ -911,7 +1051,7 @@ impl Reflect for Option { } } -impl FromReflect for Option { +impl FromReflect for Option { fn from_reflect(reflect: &dyn Reflect) -> Option { if let ReflectRef::Enum(dyn_enum) = reflect.reflect_ref() { match dyn_enum.variant_name() { @@ -949,7 +1089,7 @@ impl FromReflect for Option { } } -impl Typed for Option { +impl Typed for Option { fn type_info() -> &'static TypeInfo { static CELL: GenericTypeInfoCell = GenericTypeInfoCell::new(); CELL.get_or_insert::(|| { @@ -964,6 +1104,8 @@ impl Typed for Option { } } +impl_type_path!(::core::option::Option); + impl Reflect for Cow<'static, str> { fn type_name(&self) -> &str { std::any::type_name::() @@ -973,6 +1115,11 @@ impl Reflect for Cow<'static, str> { Some(::type_info()) } + #[inline] + fn get_type_path(&self) -> &dyn DynamicTypePath { + self + } + fn into_any(self: Box) -> Box { self } @@ -1051,6 +1198,30 @@ impl Typed for Cow<'static, str> { } } +impl TypePath for Cow<'static, str> { + fn type_path() -> &'static str { + static CELL: GenericTypePathCell = GenericTypePathCell::new(); + CELL.get_or_insert::(|| "std::borrow::Cow::".to_owned()) + } + + fn short_type_path() -> &'static str { + static CELL: GenericTypePathCell = GenericTypePathCell::new(); + CELL.get_or_insert::(|| "Cow".to_owned()) + } + + fn type_ident() -> Option<&'static str> { + Some("Cow") + } + + fn crate_name() -> Option<&'static str> { + Some("std") + } + + fn module_path() -> Option<&'static str> { + Some("std::borrow") + } +} + impl GetTypeRegistration for Cow<'static, str> { fn get_type_registration() -> TypeRegistration { let mut registration = TypeRegistration::of::>(); @@ -1081,6 +1252,11 @@ impl Reflect for &'static Path { Some(::type_info()) } + #[inline] + fn get_type_path(&self) -> &dyn DynamicTypePath { + self + } + fn into_any(self: Box) -> Box { self } @@ -1159,6 +1335,18 @@ impl Typed for &'static Path { } } +impl TypePath for &'static Path { + fn type_path() -> &'static str { + static CELL: GenericTypePathCell = GenericTypePathCell::new(); + CELL.get_or_insert::(|| "&std::path::Path".to_owned()) + } + + fn short_type_path() -> &'static str { + static CELL: GenericTypePathCell = GenericTypePathCell::new(); + CELL.get_or_insert::(|| "&Path".to_owned()) + } +} + impl GetTypeRegistration for &'static Path { fn get_type_registration() -> TypeRegistration { let mut registration = TypeRegistration::of::(); @@ -1178,6 +1366,11 @@ impl Reflect for Cow<'static, Path> { std::any::type_name::() } + #[inline] + fn get_type_path(&self) -> &dyn DynamicTypePath { + self + } + fn get_represented_type_info(&self) -> Option<&'static TypeInfo> { Some(::type_info()) } @@ -1264,6 +1457,11 @@ impl Typed for Cow<'static, Path> { } } +trait PathOnly: ToOwned {} +impl PathOnly for Path {} +impl_type_path!(::alloc::borrow::Cow<'a: 'static, T: PathOnly + ?Sized>); +impl_type_path!(::std::path::Path); + impl FromReflect for Cow<'static, Path> { fn from_reflect(reflect: &dyn Reflect) -> Option { Some(reflect.as_any().downcast_ref::()?.clone()) diff --git a/crates/bevy_reflect/src/lib.rs b/crates/bevy_reflect/src/lib.rs index 40a20a40fe3dd..c00b91e626ff1 100644 --- a/crates/bevy_reflect/src/lib.rs +++ b/crates/bevy_reflect/src/lib.rs @@ -448,6 +448,7 @@ mod struct_trait; mod tuple; mod tuple_struct; mod type_info; +mod type_path; mod type_registry; mod type_uuid; mod type_uuid_impl; @@ -496,12 +497,15 @@ pub use struct_trait::*; pub use tuple::*; pub use tuple_struct::*; pub use type_info::*; +pub use type_path::*; pub use type_registry::*; pub use type_uuid::*; pub use bevy_reflect_derive::*; pub use erased_serde; +extern crate alloc; + #[doc(hidden)] pub mod __macro_exports { use crate::Uuid; @@ -543,8 +547,8 @@ mod tests { ser::{to_string_pretty, PrettyConfig}, Deserializer, }; - use std::any::TypeId; use std::fmt::{Debug, Formatter}; + use std::{any::TypeId, marker::PhantomData}; use super::prelude::*; use super::*; @@ -1118,6 +1122,98 @@ mod tests { ); } + #[test] + fn reflect_type_path() { + #[derive(TypePath)] + struct Param; + + #[derive(TypePath)] + struct Derive; + + #[derive(TypePath)] + #[type_path = "my_alias"] + struct DerivePath; + + #[derive(TypePath)] + #[type_path = "my_alias"] + #[type_name = "MyDerivePathName"] + struct DerivePathName; + + #[derive(TypePath)] + struct DeriveG(PhantomData); + + #[derive(TypePath)] + #[type_path = "my_alias"] + struct DerivePathG(PhantomData); + + #[derive(TypePath)] + #[type_path = "my_alias"] + #[type_name = "MyDerivePathNameG"] + struct DerivePathNameG(PhantomData); + + struct Macro; + impl_type_path!((in my_alias) Macro); + + struct MacroName; + impl_type_path!((in my_alias as MyMacroName) MacroName); + + struct MacroG(PhantomData); + impl_type_path!((in my_alias) MacroG); + + struct MacroNameG(PhantomData); + impl_type_path!((in my_alias as MyMacroNameG) MacroNameG); + + assert_eq!(Derive::type_path(), "bevy_reflect::tests::Derive"); + assert_eq!(DerivePath::type_path(), "my_alias::DerivePath"); + assert_eq!(DerivePathName::type_path(), "my_alias::MyDerivePathName"); + + assert_eq!( + DeriveG::::type_path(), + "bevy_reflect::tests::DeriveG" + ); + assert_eq!( + DerivePathG::::type_path(), + "my_alias::DerivePathG" + ); + assert_eq!( + DerivePathNameG::::type_path(), + "my_alias::MyDerivePathNameG" + ); + + assert_eq!(Macro::type_path(), "my_alias::Macro"); + assert_eq!(MacroName::type_path(), "my_alias::MyMacroName"); + assert_eq!( + MacroG::::type_path(), + "my_alias::MacroG" + ); + assert_eq!( + MacroNameG::::type_path(), + "my_alias::MyMacroNameG" + ); + + assert_eq!(Derive::short_type_path(), "Derive"); + assert_eq!(DerivePath::short_type_path(), "DerivePath"); + assert_eq!(DerivePathName::short_type_path(), "MyDerivePathName"); + + assert_eq!(DeriveG::::short_type_path(), "DeriveG"); + assert_eq!( + DerivePathG::::short_type_path(), + "DerivePathG" + ); + assert_eq!( + DerivePathNameG::::short_type_path(), + "MyDerivePathNameG" + ); + + assert_eq!(Macro::short_type_path(), "Macro"); + assert_eq!(MacroName::short_type_path(), "MyMacroName"); + assert_eq!(MacroG::::short_type_path(), "MacroG"); + assert_eq!( + MacroNameG::::short_type_path(), + "MyMacroNameG" + ); + } + #[test] fn reflect_type_info() { // TypeInfo @@ -1171,7 +1267,7 @@ mod tests { // Struct (generic) #[derive(Reflect)] - struct MyGenericStruct { + struct MyGenericStruct { foo: T, bar: usize, } @@ -1420,7 +1516,7 @@ mod tests { struct SomePrimitive; impl_reflect_value!( /// Some primitive for which we have attributed custom documentation. - SomePrimitive + (in bevy_reflect::tests) SomePrimitive ); let info = ::type_info(); diff --git a/crates/bevy_reflect/src/list.rs b/crates/bevy_reflect/src/list.rs index 6b17527302f02..b040464ec33fa 100644 --- a/crates/bevy_reflect/src/list.rs +++ b/crates/bevy_reflect/src/list.rs @@ -2,8 +2,13 @@ use std::any::{Any, TypeId}; use std::fmt::{Debug, Formatter}; use std::hash::{Hash, Hasher}; +use bevy_reflect_derive::impl_type_path; + use crate::utility::reflect_hasher; -use crate::{FromReflect, Reflect, ReflectMut, ReflectOwned, ReflectRef, TypeInfo}; +use crate::{ + self as bevy_reflect, DynamicTypePath, FromReflect, Reflect, ReflectMut, ReflectOwned, + ReflectRef, TypeInfo, +}; /// A trait used to power [list-like] operations via [reflection]. /// @@ -271,6 +276,11 @@ impl Reflect for DynamicList { self.represented_type } + #[inline] + fn get_type_path(&self) -> &dyn DynamicTypePath { + self + } + #[inline] fn into_any(self: Box) -> Box { self @@ -352,6 +362,8 @@ impl Reflect for DynamicList { } } +impl_type_path!((in bevy_reflect) DynamicList); + impl Debug for DynamicList { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { self.debug(f) diff --git a/crates/bevy_reflect/src/map.rs b/crates/bevy_reflect/src/map.rs index 4dfedd2dbc1b1..3b9c0c751bc27 100644 --- a/crates/bevy_reflect/src/map.rs +++ b/crates/bevy_reflect/src/map.rs @@ -2,9 +2,12 @@ use std::any::{Any, TypeId}; use std::fmt::{Debug, Formatter}; use std::hash::Hash; +use bevy_reflect_derive::impl_type_path; use bevy_utils::{Entry, HashMap}; -use crate::{Reflect, ReflectMut, ReflectOwned, ReflectRef, TypeInfo}; +use crate::{ + self as bevy_reflect, DynamicTypePath, Reflect, ReflectMut, ReflectOwned, ReflectRef, TypeInfo, +}; /// A trait used to power [map-like] operations via [reflection]. /// @@ -307,6 +310,11 @@ impl Reflect for DynamicMap { self.represented_type } + #[inline] + fn get_type_path(&self) -> &dyn DynamicTypePath { + self + } + fn into_any(self: Box) -> Box { self } @@ -375,6 +383,8 @@ impl Reflect for DynamicMap { } } +impl_type_path!((in bevy_reflect) DynamicMap); + impl Debug for DynamicMap { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { self.debug(f) diff --git a/crates/bevy_reflect/src/reflect.rs b/crates/bevy_reflect/src/reflect.rs index c0749f5067caa..53bf3aca1a0e5 100644 --- a/crates/bevy_reflect/src/reflect.rs +++ b/crates/bevy_reflect/src/reflect.rs @@ -1,7 +1,7 @@ use crate::{ array_debug, enum_debug, list_debug, map_debug, serde::Serializable, struct_debug, tuple_debug, - tuple_struct_debug, Array, Enum, List, Map, Struct, Tuple, TupleStruct, TypeInfo, Typed, - ValueInfo, + tuple_struct_debug, Array, DynamicTypePath, Enum, List, Map, Struct, Tuple, TupleStruct, + TypeInfo, Typed, ValueInfo, }; use std::{ any::{self, Any, TypeId}, @@ -93,6 +93,14 @@ pub trait Reflect: Any + Send + Sync { /// [`TypeRegistry::get_type_info`]: crate::TypeRegistry::get_type_info fn get_represented_type_info(&self) -> Option<&'static TypeInfo>; + /// Returns the [`TypePath`] implementation for the underlying type. + /// + /// Methods on [`DynamicTypePath`] suffer the same performance concerns as [`get_represented_type_info`]. + /// + /// [`TypePath`]: crate::TypePath + /// [`get_represented_type_info`]: Reflect::get_represented_type_info + fn get_type_path(&self) -> &dyn DynamicTypePath; + /// Returns the value as a [`Box`][std::any::Any]. fn into_any(self: Box) -> Box; diff --git a/crates/bevy_reflect/src/struct_trait.rs b/crates/bevy_reflect/src/struct_trait.rs index ede4ddec4d04d..fce88b78dd47c 100644 --- a/crates/bevy_reflect/src/struct_trait.rs +++ b/crates/bevy_reflect/src/struct_trait.rs @@ -1,4 +1,8 @@ -use crate::{NamedField, Reflect, ReflectMut, ReflectOwned, ReflectRef, TypeInfo}; +use crate::{ + self as bevy_reflect, DynamicTypePath, NamedField, Reflect, ReflectMut, ReflectOwned, + ReflectRef, TypeInfo, +}; +use bevy_reflect_derive::impl_type_path; use bevy_utils::{Entry, HashMap}; use std::fmt::{Debug, Formatter}; use std::{ @@ -401,6 +405,11 @@ impl Reflect for DynamicStruct { self.represented_type } + #[inline] + fn get_type_path(&self) -> &dyn DynamicTypePath { + self + } + #[inline] fn into_any(self: Box) -> Box { self @@ -485,6 +494,8 @@ impl Reflect for DynamicStruct { } } +impl_type_path!((in bevy_reflect) DynamicStruct); + impl Debug for DynamicStruct { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { self.debug(f) diff --git a/crates/bevy_reflect/src/tuple.rs b/crates/bevy_reflect/src/tuple.rs index 8f0340bcbb55e..2c7c6afaaa685 100644 --- a/crates/bevy_reflect/src/tuple.rs +++ b/crates/bevy_reflect/src/tuple.rs @@ -1,5 +1,8 @@ +use bevy_reflect_derive::impl_type_path; + use crate::{ - FromReflect, GetTypeRegistration, Reflect, ReflectMut, ReflectOwned, ReflectRef, TypeInfo, + self as bevy_reflect, utility::GenericTypePathCell, DynamicTypePath, FromReflect, + GetTypeRegistration, Reflect, ReflectMut, ReflectOwned, ReflectRef, TypeInfo, TypePath, TypeRegistration, Typed, UnnamedField, }; use std::any::{Any, TypeId}; @@ -318,6 +321,11 @@ impl Reflect for DynamicTuple { self.represented_type } + #[inline] + fn get_type_path(&self) -> &dyn DynamicTypePath { + self + } + #[inline] fn into_any(self: Box) -> Box { self @@ -393,6 +401,8 @@ impl Reflect for DynamicTuple { } } +impl_type_path!((in bevy_reflect) DynamicTuple); + /// Applies the elements of `b` to the corresponding elements of `a`. /// /// # Panics @@ -467,7 +477,7 @@ pub fn tuple_debug(dyn_tuple: &dyn Tuple, f: &mut std::fmt::Formatter<'_>) -> st macro_rules! impl_reflect_tuple { {$($index:tt : $name:tt),*} => { - impl<$($name: Reflect),*> Tuple for ($($name,)*) { + impl<$($name: Reflect + TypePath),*> Tuple for ($($name,)*) { #[inline] fn field(&self, index: usize) -> Option<&dyn Reflect> { match index { @@ -519,7 +529,7 @@ macro_rules! impl_reflect_tuple { } } - impl<$($name: Reflect),*> Reflect for ($($name,)*) { + impl<$($name: Reflect + TypePath),*> Reflect for ($($name,)*) { fn type_name(&self) -> &str { std::any::type_name::() } @@ -528,6 +538,11 @@ macro_rules! impl_reflect_tuple { Some(::type_info()) } + #[inline] + fn get_type_path(&self) -> &dyn DynamicTypePath { + self + } + fn into_any(self: Box) -> Box { self } @@ -582,7 +597,7 @@ macro_rules! impl_reflect_tuple { } } - impl <$($name: Reflect),*> Typed for ($($name,)*) { + impl <$($name: Reflect + TypePath),*> Typed for ($($name,)*) { fn type_info() -> &'static TypeInfo { static CELL: $crate::utility::GenericTypeInfoCell = $crate::utility::GenericTypeInfoCell::new(); CELL.get_or_insert::(|| { @@ -595,13 +610,30 @@ macro_rules! impl_reflect_tuple { } } - impl<$($name: Reflect + Typed),*> GetTypeRegistration for ($($name,)*) { + impl <$($name: Reflect + TypePath),*> TypePath for ($($name,)*) { + fn type_path() -> &'static str { + static CELL: GenericTypePathCell = GenericTypePathCell::new(); + CELL.get_or_insert::(|| { + "(".to_owned() $(+ <$name as TypePath>::type_path())* + ")" + }) + } + + fn short_type_path() -> &'static str { + static CELL: GenericTypePathCell = GenericTypePathCell::new(); + CELL.get_or_insert::(|| { + "(".to_owned() $(+ <$name as TypePath>::short_type_path())* + ")" + }) + } + } + + + impl<$($name: Reflect + TypePath),*> GetTypeRegistration for ($($name,)*) { fn get_type_registration() -> TypeRegistration { TypeRegistration::of::<($($name,)*)>() } } - impl<$($name: FromReflect),*> FromReflect for ($($name,)*) + impl<$($name: FromReflect + TypePath),*> FromReflect for ($($name,)*) { fn from_reflect(reflect: &dyn Reflect) -> Option { if let ReflectRef::Tuple(_ref_tuple) = reflect.reflect_ref() { diff --git a/crates/bevy_reflect/src/tuple_struct.rs b/crates/bevy_reflect/src/tuple_struct.rs index 9b7466d5757ad..b2703b65aa7b5 100644 --- a/crates/bevy_reflect/src/tuple_struct.rs +++ b/crates/bevy_reflect/src/tuple_struct.rs @@ -1,4 +1,9 @@ -use crate::{Reflect, ReflectMut, ReflectOwned, ReflectRef, TypeInfo, UnnamedField}; +use bevy_reflect_derive::impl_type_path; + +use crate::{ + self as bevy_reflect, DynamicTypePath, Reflect, ReflectMut, ReflectOwned, ReflectRef, TypeInfo, + UnnamedField, +}; use std::any::{Any, TypeId}; use std::fmt::{Debug, Formatter}; use std::slice::Iter; @@ -303,6 +308,11 @@ impl Reflect for DynamicTupleStruct { self.represented_type } + #[inline] + fn get_type_path(&self) -> &dyn DynamicTypePath { + self + } + #[inline] fn into_any(self: Box) -> Box { self @@ -386,6 +396,8 @@ impl Reflect for DynamicTupleStruct { } } +impl_type_path!((in bevy_reflect) DynamicTupleStruct); + impl Debug for DynamicTupleStruct { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { self.debug(f) diff --git a/crates/bevy_reflect/src/type_info.rs b/crates/bevy_reflect/src/type_info.rs index c99beece3a9a4..7a4099815ab5f 100644 --- a/crates/bevy_reflect/src/type_info.rs +++ b/crates/bevy_reflect/src/type_info.rs @@ -24,7 +24,7 @@ use std::fmt::Debug; /// /// ``` /// # use std::any::Any; -/// # use bevy_reflect::{NamedField, Reflect, ReflectMut, ReflectOwned, ReflectRef, StructInfo, TypeInfo, ValueInfo}; +/// # use bevy_reflect::{DynamicTypePath, NamedField, Reflect, ReflectMut, ReflectOwned, ReflectRef, StructInfo, TypeInfo, ValueInfo}; /// # use bevy_reflect::utility::NonGenericTypeInfoCell; /// use bevy_reflect::Typed; /// @@ -51,6 +51,7 @@ use std::fmt::Debug; /// # impl Reflect for MyStruct { /// # fn type_name(&self) -> &str { todo!() } /// # fn get_represented_type_info(&self) -> Option<&'static TypeInfo> { todo!() } +/// # fn get_type_path(&self) -> &dyn DynamicTypePath { todo!() } /// # fn into_any(self: Box) -> Box { todo!() } /// # fn as_any(&self) -> &dyn Any { todo!() } /// # fn as_any_mut(&mut self) -> &mut dyn Any { todo!() } diff --git a/crates/bevy_reflect/src/type_path.rs b/crates/bevy_reflect/src/type_path.rs new file mode 100644 index 0000000000000..387fb11930d9c --- /dev/null +++ b/crates/bevy_reflect/src/type_path.rs @@ -0,0 +1,167 @@ +/// A static accessor to type paths and names. +/// +/// The engine uses this trait over [`std::any::type_name`] for stability and flexibility. +/// +/// This trait is automatically implemented by the `#[derive(Reflect)]` macro +/// and allows type path information to be processed without an instance of that type. +/// +/// Implementors may have difficulty in generating references with static +/// lifetimes. Luckily, this crate comes with some [utility] structs, to make generating these +/// statics much simpler. +/// +/// # Stability +/// +/// Certain parts of the engine, e.g. [(de)serialization], rely on type paths as identifiers +/// for matching dynamic values to concrete types. +/// +/// Using [`std::any::type_name`], a scene containing `my_crate::foo::MyComponent` would break, +/// failing to deserialize if the component was moved from the `foo` module to the `bar` module, +/// becoming `my_crate::bar::MyComponent`. +/// This trait, through attributes when deriving itself or [`Reflect`], can ensure breaking changes are avoidable. +/// +/// The only external factor we rely on for stability when deriving is the [`module_path!`] macro, +/// only if the derive does not provide a `#[type_path = "..."]` attribute. +/// +/// # Anonymity +/// +/// Some methods on this trait return `Option<&'static str>` over `&'static str` +/// because not all types define all parts of a type path, for example the array type `[T; N]`. +/// +/// Such types are 'anonymous' in that they have only a defined [`type_path`] and [`short_type_path`] +/// and the methods [`crate_name`], [`module_path`] and [`type_ident`] all return `None`. +/// +/// Primitives are treated like anonymous types, except they also have a defined [`type_ident`]. +/// +/// # Example +/// +/// ```rust +/// use bevy_reflect::TypePath; +/// +/// // This type path will not change with compiler versions or recompiles, +/// // although it will not be the same if the definition is moved. +/// #[derive(TypePath)] +/// struct NonStableTypePath; +/// +/// // This type path will never change, even if the definition is moved. +/// #[derive(TypePath)] +/// #[type_path = "my_crate::foo"] +/// struct StableTypePath; +/// +/// // Type paths can have any number of path segments. +/// #[derive(TypePath)] +/// #[type_path = "my_crate::foo::bar::baz"] +/// struct DeeplyNestedStableTypePath; +/// +/// // Including just a crate name! +/// #[derive(TypePath)] +/// #[type_path = "my_crate"] +/// struct ShallowStableTypePath; +/// +/// // We can also rename the identifier/name of types. +/// #[derive(TypePath)] +/// #[type_path = "my_crate::foo"] +/// #[type_name = "RenamedStableTypePath"] +/// struct NamedStableTypePath; +/// +/// // Generics are also supported. +/// #[derive(TypePath)] +/// #[type_path = "my_crate::foo"] +/// struct StableGenericTypePath([T; N]); +/// ``` +/// +/// [utility]: crate::utility +/// [(de)serialization]: crate::serde::UntypedReflectDeserializer +/// [`Reflect`]: crate::Reflect +/// [`type_path`]: TypePath::type_path +/// [`short_type_path`]: TypePath::short_type_path +/// [`crate_name`]: TypePath::crate_name +/// [`module_path`]: TypePath::module_path +/// [`type_ident`]: TypePath::type_ident +pub trait TypePath: 'static { + /// Returns the fully qualified path of the underlying type. + /// + /// Generic parameter types are also fully expanded. + /// + /// For `Option`, this is `"core::option::Option"`. + fn type_path() -> &'static str; + + /// Returns a short, pretty-print enabled path to the type. + /// + /// Generic parameter types are also shortened. + /// + /// For `Option`, this is `"Option"`. + fn short_type_path() -> &'static str; + + /// Returns the name of the type, or [`None`] if it is [anonymous]. + /// + /// Primitive types will return [`Some`]. + /// + /// For `Option`, this is `"Option"`. + /// + /// [anonymous]: TypePath#anonymity + fn type_ident() -> Option<&'static str> { + None + } + + /// Returns the name of the crate the type is in, or [`None`] if it is [anonymous]. + /// + /// For `Option`, this is `"core"`. + /// + /// [anonymous]: TypePath#anonymity + fn crate_name() -> Option<&'static str> { + None + } + + /// Returns the path to the moudle the type is in, or [`None`] if it is [anonymous]. + /// + /// For `Option`, this is `"core::option"`. + /// + /// [anonymous]: TypePath#anonymity + fn module_path() -> Option<&'static str> { + None + } +} + +/// Dynamic dispatch for [`TypePath`]. +/// +/// Retrieved using [`Reflect::get_type_path`]. +/// +/// [`Reflect::get_type_path`]: crate::Reflect::get_type_path +pub trait DynamicTypePath { + /// See [`TypePath::type_path`]. + fn reflect_type_path(&self) -> &str; + + /// See [`TypePath::short_type_path`]. + fn reflect_short_type_path(&self) -> &str; + + /// See [`TypePath::type_ident`]. + fn reflect_type_ident(&self) -> Option<&str>; + + /// See [`TypePath::crate_name`]. + fn reflect_crate_name(&self) -> Option<&str>; + + /// See [`TypePath::module_path`]. + fn reflect_module_path(&self) -> Option<&str>; +} + +impl DynamicTypePath for T { + fn reflect_type_path(&self) -> &str { + Self::type_path() + } + + fn reflect_short_type_path(&self) -> &str { + Self::short_type_path() + } + + fn reflect_type_ident(&self) -> Option<&str> { + Self::type_ident() + } + + fn reflect_crate_name(&self) -> Option<&str> { + Self::crate_name() + } + + fn reflect_module_path(&self) -> Option<&str> { + Self::module_path() + } +} diff --git a/crates/bevy_reflect/src/utility.rs b/crates/bevy_reflect/src/utility.rs index 7c203631aa785..6344072b08215 100644 --- a/crates/bevy_reflect/src/utility.rs +++ b/crates/bevy_reflect/src/utility.rs @@ -9,132 +9,220 @@ use std::{ hash::BuildHasher, }; +/// A type that can be stored in a ([`Non`])[`GenericTypeCell`]. +/// +/// [`Non`]: NonGenericTypeCell +pub trait TypedProperty: sealed::Sealed { + type Stored: 'static; +} + +/// Used to store a [`String`] in a [`GenericTypePathCell`] as part of a [`TypePath`] implementation. +/// +/// [`TypePath`]: crate::TypePath +pub struct TypePathComponent; + +mod sealed { + use super::{TypeInfo, TypePathComponent, TypedProperty}; + + pub trait Sealed {} + + impl Sealed for TypeInfo {} + impl Sealed for TypePathComponent {} + + impl TypedProperty for TypeInfo { + type Stored = Self; + } + + impl TypedProperty for TypePathComponent { + type Stored = String; + } +} + /// A container for [`TypeInfo`] over non-generic types, allowing instances to be stored statically. /// /// This is specifically meant for use with _non_-generic types. If your type _is_ generic, -/// then use [`GenericTypeInfoCell`] instead. Otherwise, it will not take into account all +/// then use [`GenericTypeCell`] instead. Otherwise, it will not take into account all /// monomorphizations of your type. /// +/// Non-generic [`TypePath`]s should be trivially generated with string literals and [`concat!`]. +/// /// ## Example /// /// ``` /// # use std::any::Any; -/// # use bevy_reflect::{NamedField, Reflect, ReflectMut, ReflectOwned, ReflectRef, StructInfo, Typed, TypeInfo}; +/// # use bevy_reflect::{DynamicTypePath, NamedField, Reflect, ReflectMut, ReflectOwned, ReflectRef, StructInfo, Typed, TypeInfo}; /// use bevy_reflect::utility::NonGenericTypeInfoCell; /// /// struct Foo { -/// bar: i32 +/// bar: i32 /// } /// /// impl Typed for Foo { -/// fn type_info() -> &'static TypeInfo { -/// static CELL: NonGenericTypeInfoCell = NonGenericTypeInfoCell::new(); -/// CELL.get_or_set(|| { -/// let fields = [NamedField::new::("bar")]; -/// let info = StructInfo::new::("Foo", &fields); -/// TypeInfo::Struct(info) -/// }) -/// } +/// fn type_info() -> &'static TypeInfo { +/// static CELL: NonGenericTypeInfoCell = NonGenericTypeInfoCell::new(); +/// CELL.get_or_set(|| { +/// let fields = [NamedField::new::("bar")]; +/// let info = StructInfo::new::("Foo", &fields); +/// TypeInfo::Struct(info) +/// }) +/// } /// } /// # /// # impl Reflect for Foo { -/// # fn type_name(&self) -> &str { todo!() } -/// # fn get_represented_type_info(&self) -> Option<&'static TypeInfo> { todo!() } -/// # fn into_any(self: Box) -> Box { todo!() } -/// # fn as_any(&self) -> &dyn Any { todo!() } -/// # fn as_any_mut(&mut self) -> &mut dyn Any { todo!() } -/// # fn into_reflect(self: Box) -> Box { todo!() } -/// # fn as_reflect(&self) -> &dyn Reflect { todo!() } -/// # fn as_reflect_mut(&mut self) -> &mut dyn Reflect { todo!() } -/// # fn apply(&mut self, value: &dyn Reflect) { todo!() } -/// # fn set(&mut self, value: Box) -> Result<(), Box> { todo!() } -/// # fn reflect_ref(&self) -> ReflectRef { todo!() } -/// # fn reflect_mut(&mut self) -> ReflectMut { todo!() } -/// # fn reflect_owned(self: Box) -> ReflectOwned { todo!() } -/// # fn clone_value(&self) -> Box { todo!() } +/// # fn type_name(&self) -> &str { todo!() } +/// # fn get_type_path(&self) -> &dyn DynamicTypePath { todo!() } +/// # fn get_represented_type_info(&self) -> Option<&'static TypeInfo> { todo!() } +/// # fn into_any(self: Box) -> Box { todo!() } +/// # fn as_any(&self) -> &dyn Any { todo!() } +/// # fn as_any_mut(&mut self) -> &mut dyn Any { todo!() } +/// # fn into_reflect(self: Box) -> Box { todo!() } +/// # fn as_reflect(&self) -> &dyn Reflect { todo!() } +/// # fn as_reflect_mut(&mut self) -> &mut dyn Reflect { todo!() } +/// # fn apply(&mut self, value: &dyn Reflect) { todo!() } +/// # fn set(&mut self, value: Box) -> Result<(), Box> { todo!() } +/// # fn reflect_ref(&self) -> ReflectRef { todo!() } +/// # fn reflect_mut(&mut self) -> ReflectMut { todo!() } +/// # fn reflect_owned(self: Box) -> ReflectOwned { todo!() } +/// # fn clone_value(&self) -> Box { todo!() } /// # } /// ``` -pub struct NonGenericTypeInfoCell(OnceBox); +/// +/// [`TypePath`]: crate::TypePath +pub struct NonGenericTypeCell(OnceBox); + +/// See [`NonGenericTypeCell`]. +pub type NonGenericTypeInfoCell = NonGenericTypeCell; -impl NonGenericTypeInfoCell { - /// Initialize a [`NonGenericTypeInfoCell`] for non-generic types. +impl NonGenericTypeCell { + /// Initialize a [`NonGenericTypeCell`] for non-generic types. pub const fn new() -> Self { Self(OnceBox::new()) } - /// Returns a reference to the [`TypeInfo`] stored in the cell. + /// Returns a reference to the [`TypedProperty`] stored in the cell. /// - /// If there is no [`TypeInfo`] found, a new one will be generated from the given function. - /// - /// [`TypeInfos`]: TypeInfo - pub fn get_or_set(&self, f: F) -> &TypeInfo + /// If there is no entry found, a new one will be generated from the given function. + pub fn get_or_set(&self, f: F) -> &T::Stored where - F: FnOnce() -> TypeInfo, + F: FnOnce() -> T::Stored, { self.0.get_or_init(|| Box::new(f())) } } -/// A container for [`TypeInfo`] over generic types, allowing instances to be stored statically. +/// A container for [`TypedProperty`] over generic types, allowing instances to be stored statically. /// /// This is specifically meant for use with generic types. If your type isn't generic, -/// then use [`NonGenericTypeInfoCell`] instead as it should be much more performant. +/// then use [`NonGenericTypeCell`] instead as it should be much more performant. /// -/// ## Example +/// `#[derive(TypePath)]` and [`impl_type_path`] should always be used over [`GenericTypePathCell`] +/// where possible. +/// +/// ## Examples +/// +/// Implementing [`TypeInfo`] with generics. /// /// ``` /// # use std::any::Any; -/// # use bevy_reflect::{Reflect, ReflectMut, ReflectOwned, ReflectRef, TupleStructInfo, Typed, TypeInfo, UnnamedField}; +/// # use bevy_reflect::{DynamicTypePath, Reflect, ReflectMut, ReflectOwned, ReflectRef, TupleStructInfo, Typed, TypeInfo, UnnamedField}; /// use bevy_reflect::utility::GenericTypeInfoCell; /// -/// struct Foo(T); +/// struct Foo(T); /// /// impl Typed for Foo { -/// fn type_info() -> &'static TypeInfo { -/// static CELL: GenericTypeInfoCell = GenericTypeInfoCell::new(); -/// CELL.get_or_insert::(|| { -/// let fields = [UnnamedField::new::(0)]; -/// let info = TupleStructInfo::new::("Foo", &fields); -/// TypeInfo::TupleStruct(info) -/// }) -/// } +/// fn type_info() -> &'static TypeInfo { +/// static CELL: GenericTypeInfoCell = GenericTypeInfoCell::new(); +/// CELL.get_or_insert::(|| { +/// let fields = [UnnamedField::new::(0)]; +/// let info = TupleStructInfo::new::("Foo", &fields); +/// TypeInfo::TupleStruct(info) +/// }) +/// } +/// } +/// # +/// # impl Reflect for Foo { +/// # fn type_name(&self) -> &str { todo!() } +/// # fn get_type_path(&self) -> &dyn DynamicTypePath { todo!() } +/// # fn get_represented_type_info(&self) -> Option<&'static TypeInfo> { todo!() } +/// # fn into_any(self: Box) -> Box { todo!() } +/// # fn as_any(&self) -> &dyn Any { todo!() } +/// # fn as_any_mut(&mut self) -> &mut dyn Any { todo!() } +/// # fn into_reflect(self: Box) -> Box { todo!() } +/// # fn as_reflect(&self) -> &dyn Reflect { todo!() } +/// # fn as_reflect_mut(&mut self) -> &mut dyn Reflect { todo!() } +/// # fn apply(&mut self, value: &dyn Reflect) { todo!() } +/// # fn set(&mut self, value: Box) -> Result<(), Box> { todo!() } +/// # fn reflect_ref(&self) -> ReflectRef { todo!() } +/// # fn reflect_mut(&mut self) -> ReflectMut { todo!() } +/// # fn reflect_owned(self: Box) -> ReflectOwned { todo!() } +/// # fn clone_value(&self) -> Box { todo!() } +/// # } +/// ``` +/// +/// Implementing [`TypePath`] with generics. +/// +/// ``` +/// # use std::any::Any; +/// # use bevy_reflect::{DynamicTypePath, Reflect, ReflectMut, ReflectOwned, ReflectRef, TypeInfo, TypePath}; +/// use bevy_reflect::utility::GenericTypePathCell; +/// +/// struct Foo(T); +/// +/// impl TypePath for Foo { +/// fn type_path() -> &'static str { +/// static CELL: GenericTypePathCell = GenericTypePathCell::new(); +/// CELL.get_or_insert::(|| format!("my_crate::foo::Foo<{}>", T::type_path())) +/// } +/// +/// fn short_type_path() -> &'static str { +/// static CELL: GenericTypePathCell = GenericTypePathCell::new(); +/// CELL.get_or_insert::(|| format!("Foo<{}>", T::short_type_path())) +/// } /// } /// # /// # impl Reflect for Foo { -/// # fn type_name(&self) -> &str { todo!() } -/// # fn get_represented_type_info(&self) -> Option<&'static TypeInfo> { todo!() } -/// # fn into_any(self: Box) -> Box { todo!() } -/// # fn as_any(&self) -> &dyn Any { todo!() } -/// # fn as_any_mut(&mut self) -> &mut dyn Any { todo!() } -/// # fn into_reflect(self: Box) -> Box { todo!() } -/// # fn as_reflect(&self) -> &dyn Reflect { todo!() } -/// # fn as_reflect_mut(&mut self) -> &mut dyn Reflect { todo!() } -/// # fn apply(&mut self, value: &dyn Reflect) { todo!() } -/// # fn set(&mut self, value: Box) -> Result<(), Box> { todo!() } -/// # fn reflect_ref(&self) -> ReflectRef { todo!() } -/// # fn reflect_mut(&mut self) -> ReflectMut { todo!() } -/// # fn reflect_owned(self: Box) -> ReflectOwned { todo!() } -/// # fn clone_value(&self) -> Box { todo!() } +/// # fn type_name(&self) -> &str { todo!() } +/// # fn get_type_path(&self) -> &dyn DynamicTypePath { todo!() } +/// # fn get_represented_type_info(&self) -> Option<&'static TypeInfo> { todo!() } +/// # fn into_any(self: Box) -> Box { todo!() } +/// # fn as_any(&self) -> &dyn Any { todo!() } +/// # fn as_any_mut(&mut self) -> &mut dyn Any { todo!() } +/// # fn into_reflect(self: Box) -> Box { todo!() } +/// # fn as_reflect(&self) -> &dyn Reflect { todo!() } +/// # fn as_reflect_mut(&mut self) -> &mut dyn Reflect { todo!() } +/// # fn apply(&mut self, value: &dyn Reflect) { todo!() } +/// # fn set(&mut self, value: Box) -> Result<(), Box> { todo!() } +/// # fn reflect_ref(&self) -> ReflectRef { todo!() } +/// # fn reflect_mut(&mut self) -> ReflectMut { todo!() } +/// # fn reflect_owned(self: Box) -> ReflectOwned { todo!() } +/// # fn clone_value(&self) -> Box { todo!() } /// # } /// ``` -pub struct GenericTypeInfoCell(OnceBox>>); +/// [`impl_type_path`]: crate::impl_type_path +/// [`TypePath`]: crate::TypePath +pub struct GenericTypeCell(OnceBox>>); + +/// See [`GenericTypeCell`]. +pub type GenericTypeInfoCell = GenericTypeCell; +/// See [`GenericTypeCell`]. +pub type GenericTypePathCell = GenericTypeCell; -impl GenericTypeInfoCell { - /// Initialize a [`GenericTypeInfoCell`] for generic types. +impl GenericTypeCell { + /// Initialize a [`GenericTypeCell`] for generic types. pub const fn new() -> Self { Self(OnceBox::new()) } - /// Returns a reference to the [`TypeInfo`] stored in the cell. + /// Returns a reference to the [`TypedProperty`] stored in the cell. /// - /// This method will then return the correct [`TypeInfo`] reference for the given type `T`. - /// If there is no [`TypeInfo`] found, a new one will be generated from the given function. - pub fn get_or_insert(&self, f: F) -> &TypeInfo + /// This method will then return the correct [`TypedProperty`] reference for the given type `T`. + /// If there is no entry found, a new one will be generated from the given function. + pub fn get_or_insert(&self, f: F) -> &T::Stored where - T: Any + ?Sized, - F: FnOnce() -> TypeInfo, + G: Any + ?Sized, + F: FnOnce() -> T::Stored, { - let type_id = TypeId::of::(); + let type_id = TypeId::of::(); // let mapping = self.0.get_or_init(|| Box::new(RwLock::default())); let mapping = self.0.get_or_init(Box::default); if let Some(info) = mapping.read().get(&type_id) { diff --git a/crates/bevy_reflect_compile_fail_tests/tests/reflect_derive/generics.fail.rs b/crates/bevy_reflect_compile_fail_tests/tests/reflect_derive/generics.fail.rs index 964d784d354d0..1ed92883b1033 100644 --- a/crates/bevy_reflect_compile_fail_tests/tests/reflect_derive/generics.fail.rs +++ b/crates/bevy_reflect_compile_fail_tests/tests/reflect_derive/generics.fail.rs @@ -1,4 +1,4 @@ -use bevy_reflect::Reflect; +use bevy_reflect::{Reflect, TypePath}; #[derive(Reflect)] struct Foo { @@ -6,10 +6,11 @@ struct Foo { } // Type that doesn't implement Reflect +#[derive(TypePath)] struct NoReflect(f32); fn main() { let mut foo: Box = Box::new(Foo:: { a: NoReflect(42.0) }); // foo doesn't implement Reflect because NoReflect doesn't implement Reflect foo.get_field::("a").unwrap(); -} \ No newline at end of file +} diff --git a/crates/bevy_reflect_compile_fail_tests/tests/reflect_derive/generics.fail.stderr b/crates/bevy_reflect_compile_fail_tests/tests/reflect_derive/generics.fail.stderr index 568f5f8b6d997..043dcc16665da 100644 --- a/crates/bevy_reflect_compile_fail_tests/tests/reflect_derive/generics.fail.stderr +++ b/crates/bevy_reflect_compile_fail_tests/tests/reflect_derive/generics.fail.stderr @@ -1,13 +1,13 @@ error[E0599]: no method named `get_field` found for struct `Box<(dyn Reflect + 'static)>` in the current scope - --> tests/reflect_derive/generics.fail.rs:14:9 + --> tests/reflect_derive/generics.fail.rs:15:9 | -14 | foo.get_field::("a").unwrap(); +15 | foo.get_field::("a").unwrap(); | ^^^^^^^^^ method not found in `Box` error[E0277]: the trait bound `NoReflect: Reflect` is not satisfied - --> tests/reflect_derive/generics.fail.rs:12:37 + --> tests/reflect_derive/generics.fail.rs:13:37 | -12 | let mut foo: Box = Box::new(Foo:: { a: NoReflect(42.0) }); +13 | let mut foo: Box = Box::new(Foo:: { a: NoReflect(42.0) }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Reflect` is not implemented for `NoReflect` | = help: the following other types implement trait `Reflect`: diff --git a/crates/bevy_render/src/mesh/mesh/mod.rs b/crates/bevy_render/src/mesh/mesh/mod.rs index f327d9077a5e4..7d91788e7d27a 100644 --- a/crates/bevy_render/src/mesh/mesh/mod.rs +++ b/crates/bevy_render/src/mesh/mesh/mod.rs @@ -12,7 +12,7 @@ use bevy_core::cast_slice; use bevy_derive::EnumVariantMeta; use bevy_ecs::system::{lifetimeless::SRes, SystemParamItem}; use bevy_math::*; -use bevy_reflect::TypeUuid; +use bevy_reflect::{TypePath, TypeUuid}; use bevy_utils::{tracing::error, Hashed}; use std::{collections::BTreeMap, hash::Hash, iter::FusedIterator}; use thiserror::Error; @@ -25,7 +25,7 @@ pub const INDEX_BUFFER_ASSET_INDEX: u64 = 0; pub const VERTEX_ATTRIBUTE_BUFFER_ID: u64 = 10; // TODO: allow values to be unloaded after been submitting to the GPU to conserve memory -#[derive(Debug, TypeUuid, Clone)] +#[derive(Debug, TypeUuid, TypePath, Clone)] #[uuid = "8ecbac0f-f545-4473-ad43-e1f4243af51e"] pub struct Mesh { primitive_topology: PrimitiveTopology, diff --git a/crates/bevy_render/src/mesh/mesh/skinning.rs b/crates/bevy_render/src/mesh/mesh/skinning.rs index a124655df15c7..20df623c5a899 100644 --- a/crates/bevy_render/src/mesh/mesh/skinning.rs +++ b/crates/bevy_render/src/mesh/mesh/skinning.rs @@ -6,7 +6,7 @@ use bevy_ecs::{ reflect::ReflectMapEntities, }; use bevy_math::Mat4; -use bevy_reflect::{Reflect, TypeUuid}; +use bevy_reflect::{Reflect, TypePath, TypeUuid}; use std::ops::Deref; #[derive(Component, Debug, Default, Clone, Reflect)] @@ -24,7 +24,7 @@ impl MapEntities for SkinnedMesh { } } -#[derive(Debug, TypeUuid)] +#[derive(Debug, TypeUuid, TypePath)] #[uuid = "b9f155a9-54ec-4026-988f-e0a03e99a76f"] pub struct SkinnedMeshInverseBindposes(Box<[Mat4]>); diff --git a/crates/bevy_render/src/render_resource/shader.rs b/crates/bevy_render/src/render_resource/shader.rs index 572e5fea4abf4..7829224b2bb97 100644 --- a/crates/bevy_render/src/render_resource/shader.rs +++ b/crates/bevy_render/src/render_resource/shader.rs @@ -1,7 +1,7 @@ use super::ShaderDefVal; use crate::define_atomic_id; use bevy_asset::{AssetLoader, AssetPath, Handle, LoadContext, LoadedAsset}; -use bevy_reflect::TypeUuid; +use bevy_reflect::{TypePath, TypeUuid}; use bevy_utils::{tracing::error, BoxedFuture, HashMap}; #[cfg(feature = "shader_format_glsl")] use naga::back::wgsl::WriterFlags; @@ -31,7 +31,7 @@ pub enum ShaderReflectError { } /// A shader, as defined by its [`ShaderSource`] and [`ShaderStage`](naga::ShaderStage) /// This is an "unprocessed" shader. It can contain preprocessor directives. -#[derive(Debug, Clone, TypeUuid)] +#[derive(Debug, Clone, TypeUuid, TypePath)] #[uuid = "d95bc916-6c55-4de3-9622-37e7b6969fda"] pub struct Shader { source: Source, diff --git a/crates/bevy_render/src/view/visibility/mod.rs b/crates/bevy_render/src/view/visibility/mod.rs index 1b0c275004154..30e8819a641d9 100644 --- a/crates/bevy_render/src/view/visibility/mod.rs +++ b/crates/bevy_render/src/view/visibility/mod.rs @@ -69,7 +69,7 @@ bitflags::bitflags! { const VISIBLE_IN_HIERARCHY = 1 << 1; } } -bevy_reflect::impl_reflect_value!(ComputedVisibilityFlags); +bevy_reflect::impl_reflect_value!((in bevy_render::view) ComputedVisibilityFlags); bevy_reflect::impl_from_reflect_value!(ComputedVisibilityFlags); /// Algorithmically-computed indication of whether an entity is visible and should be extracted for rendering diff --git a/crates/bevy_scene/src/dynamic_scene.rs b/crates/bevy_scene/src/dynamic_scene.rs index 4acf375d1288f..3b4b681716a34 100644 --- a/crates/bevy_scene/src/dynamic_scene.rs +++ b/crates/bevy_scene/src/dynamic_scene.rs @@ -8,7 +8,7 @@ use bevy_ecs::{ reflect::{ReflectComponent, ReflectMapEntities}, world::World, }; -use bevy_reflect::{Reflect, TypeRegistryArc, TypeUuid}; +use bevy_reflect::{Reflect, TypePath, TypeRegistryArc, TypeUuid}; use bevy_utils::HashMap; #[cfg(feature = "serialize")] @@ -26,7 +26,7 @@ use serde::Serialize; /// * adding the [`Handle`](bevy_asset::Handle) to an entity (the scene will only be /// visible if the entity already has [`Transform`](bevy_transform::components::Transform) and /// [`GlobalTransform`](bevy_transform::components::GlobalTransform) components) -#[derive(Default, TypeUuid)] +#[derive(Default, TypeUuid, TypePath)] #[uuid = "749479b1-fb8c-4ff8-a775-623aa76014f5"] pub struct DynamicScene { pub resources: Vec>, diff --git a/crates/bevy_scene/src/scene.rs b/crates/bevy_scene/src/scene.rs index 741f570314004..ca8f1fae7bcab 100644 --- a/crates/bevy_scene/src/scene.rs +++ b/crates/bevy_scene/src/scene.rs @@ -4,7 +4,7 @@ use bevy_ecs::{ reflect::{ReflectComponent, ReflectMapEntities, ReflectResource}, world::World, }; -use bevy_reflect::TypeUuid; +use bevy_reflect::{TypePath, TypeUuid}; use crate::{DynamicScene, InstanceInfo, SceneSpawnError}; @@ -14,7 +14,7 @@ use crate::{DynamicScene, InstanceInfo, SceneSpawnError}; /// * adding the [`Handle`](bevy_asset::Handle) to an entity (the scene will only be /// visible if the entity already has [`Transform`](bevy_transform::components::Transform) and /// [`GlobalTransform`](bevy_transform::components::GlobalTransform) components) -#[derive(Debug, TypeUuid)] +#[derive(Debug, TypeUuid, TypePath)] #[uuid = "c156503c-edd9-4ec7-8d33-dab392df03cd"] pub struct Scene { pub world: World, diff --git a/crates/bevy_sprite/src/mesh2d/material.rs b/crates/bevy_sprite/src/mesh2d/material.rs index c99168486dda5..b33c7a5702578 100644 --- a/crates/bevy_sprite/src/mesh2d/material.rs +++ b/crates/bevy_sprite/src/mesh2d/material.rs @@ -14,7 +14,7 @@ use bevy_ecs::{ }, }; use bevy_log::error; -use bevy_reflect::TypeUuid; +use bevy_reflect::{TypePath, TypeUuid}; use bevy_render::{ extract_component::ExtractComponentPlugin, mesh::{Mesh, MeshVertexBufferLayout}, @@ -60,11 +60,11 @@ use crate::{ /// ``` /// # use bevy_sprite::{Material2d, MaterialMesh2dBundle}; /// # use bevy_ecs::prelude::*; -/// # use bevy_reflect::TypeUuid; +/// # use bevy_reflect::{TypeUuid, TypePath}; /// # use bevy_render::{render_resource::{AsBindGroup, ShaderRef}, texture::Image, color::Color}; /// # use bevy_asset::{Handle, AssetServer, Assets}; /// -/// #[derive(AsBindGroup, TypeUuid, Debug, Clone)] +/// #[derive(AsBindGroup, TypeUuid, TypePath, Debug, Clone)] /// #[uuid = "f690fdae-d598-45ab-8225-97e2a3f056e0"] /// pub struct CustomMaterial { /// // Uniform bindings must implement `ShaderType`, which will be used to convert the value to @@ -111,7 +111,7 @@ use crate::{ /// @group(1) @binding(2) /// var color_sampler: sampler; /// ``` -pub trait Material2d: AsBindGroup + Send + Sync + Clone + TypeUuid + Sized + 'static { +pub trait Material2d: AsBindGroup + Send + Sync + Clone + TypeUuid + TypePath + Sized { /// Returns this material's vertex shader. If [`ShaderRef::Default`] is returned, the default mesh vertex shader /// will be used. fn vertex_shader() -> ShaderRef { diff --git a/crates/bevy_text/src/font.rs b/crates/bevy_text/src/font.rs index 4b80d74ffa4d5..1d8a465a76ea2 100644 --- a/crates/bevy_text/src/font.rs +++ b/crates/bevy_text/src/font.rs @@ -1,11 +1,11 @@ use ab_glyph::{FontArc, FontVec, InvalidFont, OutlinedGlyph}; -use bevy_reflect::TypeUuid; +use bevy_reflect::{TypePath, TypeUuid}; use bevy_render::{ render_resource::{Extent3d, TextureDimension, TextureFormat}, texture::Image, }; -#[derive(Debug, TypeUuid, Clone)] +#[derive(Debug, TypeUuid, TypePath, Clone)] #[uuid = "97059ac6-c9ba-4da9-95b6-bed82c3ce198"] pub struct Font { pub font: FontArc, diff --git a/crates/bevy_text/src/font_atlas_set.rs b/crates/bevy_text/src/font_atlas_set.rs index 677bb6fcdd308..f28b4138ad8d0 100644 --- a/crates/bevy_text/src/font_atlas_set.rs +++ b/crates/bevy_text/src/font_atlas_set.rs @@ -2,6 +2,7 @@ use crate::{error::TextError, Font, FontAtlas}; use ab_glyph::{GlyphId, OutlinedGlyph, Point}; use bevy_asset::{Assets, Handle}; use bevy_math::Vec2; +use bevy_reflect::TypePath; use bevy_reflect::TypeUuid; use bevy_render::texture::Image; use bevy_sprite::TextureAtlas; @@ -10,7 +11,7 @@ use bevy_utils::HashMap; type FontSizeKey = FloatOrd; -#[derive(TypeUuid)] +#[derive(TypeUuid, TypePath)] #[uuid = "73ba778b-b6b5-4f45-982d-d21b6b86ace2"] pub struct FontAtlasSet { font_atlases: HashMap>, diff --git a/examples/2d/custom_gltf_vertex_attribute.rs b/examples/2d/custom_gltf_vertex_attribute.rs index a0d3c21c848f2..43356f04e4988 100644 --- a/examples/2d/custom_gltf_vertex_attribute.rs +++ b/examples/2d/custom_gltf_vertex_attribute.rs @@ -2,7 +2,7 @@ use bevy::gltf::GltfPlugin; use bevy::prelude::*; -use bevy::reflect::TypeUuid; +use bevy::reflect::{TypePath, TypeUuid}; use bevy::render::mesh::{MeshVertexAttribute, MeshVertexBufferLayout}; use bevy::render::render_resource::*; use bevy::sprite::{ @@ -55,7 +55,7 @@ fn setup( /// This custom material uses barycentric coordinates from /// `ATTRIBUTE_BARYCENTRIC` to shade a white border around each triangle. The /// thickness of the border is animated using the global time shader uniform. -#[derive(AsBindGroup, TypeUuid, Debug, Clone)] +#[derive(AsBindGroup, TypeUuid, TypePath, Debug, Clone)] #[uuid = "50ffce9e-1582-42e9-87cb-2233724426c0"] struct CustomMaterial {} diff --git a/examples/3d/lines.rs b/examples/3d/lines.rs index 805f1cce79895..0da72b3bd1c5e 100644 --- a/examples/3d/lines.rs +++ b/examples/3d/lines.rs @@ -3,7 +3,7 @@ use bevy::{ pbr::{MaterialPipeline, MaterialPipelineKey}, prelude::*, - reflect::TypeUuid, + reflect::{TypePath, TypeUuid}, render::{ mesh::{MeshVertexBufferLayout, PrimitiveTopology}, render_resource::{ @@ -62,7 +62,7 @@ fn setup( }); } -#[derive(Default, AsBindGroup, TypeUuid, Debug, Clone)] +#[derive(Default, AsBindGroup, TypeUuid, TypePath, Debug, Clone)] #[uuid = "050ce6ac-080a-4d8c-b6b5-b5bab7560d8f"] struct LineMaterial { #[uniform(0)] diff --git a/examples/3d/tonemapping.rs b/examples/3d/tonemapping.rs index 6b9bb09eb036f..9ae3c84dc8fc6 100644 --- a/examples/3d/tonemapping.rs +++ b/examples/3d/tonemapping.rs @@ -5,7 +5,7 @@ use bevy::{ math::vec2, pbr::CascadeShadowConfigBuilder, prelude::*, - reflect::TypeUuid, + reflect::{TypePath, TypeUuid}, render::{ render_resource::{ AsBindGroup, Extent3d, SamplerDescriptor, ShaderRef, TextureDimension, TextureFormat, @@ -689,7 +689,7 @@ impl Material for ColorGradientMaterial { } } -#[derive(AsBindGroup, Debug, Clone, TypeUuid)] +#[derive(AsBindGroup, Debug, Clone, TypeUuid, TypePath)] #[uuid = "117f64fe-6844-1822-8926-e3ed372291c8"] pub struct ColorGradientMaterial {} diff --git a/examples/asset/custom_asset.rs b/examples/asset/custom_asset.rs index faf6f48d125fc..3fe033a3f25b8 100644 --- a/examples/asset/custom_asset.rs +++ b/examples/asset/custom_asset.rs @@ -3,12 +3,12 @@ use bevy::{ asset::{AssetLoader, LoadContext, LoadedAsset}, prelude::*, - reflect::TypeUuid, + reflect::{TypePath, TypeUuid}, utils::BoxedFuture, }; use serde::Deserialize; -#[derive(Debug, Deserialize, TypeUuid)] +#[derive(Debug, Deserialize, TypeUuid, TypePath)] #[uuid = "39cadc56-aa9c-4543-8640-a018b74b5052"] pub struct CustomAsset { pub value: i32, diff --git a/examples/audio/decodable.rs b/examples/audio/decodable.rs index b9ecdd019a45c..13a986c5af82d 100644 --- a/examples/audio/decodable.rs +++ b/examples/audio/decodable.rs @@ -3,14 +3,14 @@ use bevy::audio::AddAudioSource; use bevy::audio::AudioPlugin; use bevy::audio::Source; use bevy::prelude::*; -use bevy::reflect::TypeUuid; +use bevy::reflect::{TypePath, TypeUuid}; use bevy::utils::Duration; // This struct usually contains the data for the audio being played. // This is where data read from an audio file would be stored, for example. // Implementing `TypeUuid` will automatically implement `Asset`. // This allows the type to be registered as an asset. -#[derive(TypeUuid)] +#[derive(TypePath, TypeUuid)] #[uuid = "c2090c23-78fd-44f1-8508-c89b1f3cec29"] struct SineAudio { frequency: f32, diff --git a/examples/shader/animate_shader.rs b/examples/shader/animate_shader.rs index c4add68af9611..682169032397b 100644 --- a/examples/shader/animate_shader.rs +++ b/examples/shader/animate_shader.rs @@ -1,7 +1,11 @@ //! A shader that uses dynamic data like the time since startup. //! The time data is in the globals binding which is part of the `mesh_view_bindings` shader import. -use bevy::{prelude::*, reflect::TypeUuid, render::render_resource::*}; +use bevy::{ + prelude::*, + reflect::{TypePath, TypeUuid}, + render::render_resource::*, +}; fn main() { App::new() @@ -31,7 +35,7 @@ fn setup( }); } -#[derive(AsBindGroup, TypeUuid, Debug, Clone)] +#[derive(AsBindGroup, TypeUuid, TypePath, Debug, Clone)] #[uuid = "a3d71c04-d054-4946-80f8-ba6cfbc90cad"] struct CustomMaterial {} diff --git a/examples/shader/array_texture.rs b/examples/shader/array_texture.rs index ee2ebe3070f5a..be32661f11649 100644 --- a/examples/shader/array_texture.rs +++ b/examples/shader/array_texture.rs @@ -1,7 +1,7 @@ use bevy::{ asset::LoadState, prelude::*, - reflect::TypeUuid, + reflect::{TypePath, TypeUuid}, render::render_resource::{AsBindGroup, ShaderRef}, }; @@ -89,7 +89,7 @@ fn create_array_texture( } } -#[derive(AsBindGroup, Debug, Clone, TypeUuid)] +#[derive(AsBindGroup, Debug, Clone, TypeUuid, TypePath)] #[uuid = "9c5a0ddf-1eaf-41b4-9832-ed736fd26af3"] struct ArrayTextureMaterial { #[texture(0, dimension = "2d_array")] diff --git a/examples/shader/custom_vertex_attribute.rs b/examples/shader/custom_vertex_attribute.rs index 628e145f64ea9..010701d28d75d 100644 --- a/examples/shader/custom_vertex_attribute.rs +++ b/examples/shader/custom_vertex_attribute.rs @@ -3,7 +3,7 @@ use bevy::{ pbr::{MaterialPipeline, MaterialPipelineKey}, prelude::*, - reflect::TypeUuid, + reflect::{TypePath, TypeUuid}, render::{ mesh::{MeshVertexAttribute, MeshVertexBufferLayout}, render_resource::{ @@ -57,7 +57,7 @@ fn setup( } // This is the struct that will be passed to your shader -#[derive(AsBindGroup, Debug, Clone, TypeUuid)] +#[derive(AsBindGroup, Debug, Clone, TypeUuid, TypePath)] #[uuid = "f690fdae-d598-45ab-8225-97e2a3f056e0"] pub struct CustomMaterial { #[uniform(0)] diff --git a/examples/shader/shader_defs.rs b/examples/shader/shader_defs.rs index fe38783215d32..a3ad6e30b7339 100644 --- a/examples/shader/shader_defs.rs +++ b/examples/shader/shader_defs.rs @@ -3,7 +3,7 @@ use bevy::{ pbr::{MaterialPipeline, MaterialPipelineKey}, prelude::*, - reflect::TypeUuid, + reflect::{TypePath, TypeUuid}, render::{ mesh::MeshVertexBufferLayout, render_resource::{ @@ -75,7 +75,7 @@ impl Material for CustomMaterial { } // This is the struct that will be passed to your shader -#[derive(AsBindGroup, TypeUuid, Debug, Clone)] +#[derive(AsBindGroup, TypeUuid, TypePath, Debug, Clone)] #[uuid = "f690fdae-d598-45ab-8225-97e2a3f056e0"] #[bind_group_data(CustomMaterialKey)] pub struct CustomMaterial { diff --git a/examples/shader/shader_material.rs b/examples/shader/shader_material.rs index ae8ea78e54d44..ac321d8ebc324 100644 --- a/examples/shader/shader_material.rs +++ b/examples/shader/shader_material.rs @@ -2,7 +2,7 @@ use bevy::{ prelude::*, - reflect::TypeUuid, + reflect::{TypePath, TypeUuid}, render::render_resource::{AsBindGroup, ShaderRef}, }; @@ -53,7 +53,7 @@ impl Material for CustomMaterial { } // This is the struct that will be passed to your shader -#[derive(AsBindGroup, TypeUuid, Debug, Clone)] +#[derive(AsBindGroup, TypeUuid, TypePath, Debug, Clone)] #[uuid = "f690fdae-d598-45ab-8225-97e2a3f056e0"] pub struct CustomMaterial { #[uniform(0)] diff --git a/examples/shader/shader_material_glsl.rs b/examples/shader/shader_material_glsl.rs index 3afae668f2359..7182cf439a3e2 100644 --- a/examples/shader/shader_material_glsl.rs +++ b/examples/shader/shader_material_glsl.rs @@ -3,7 +3,7 @@ use bevy::{ pbr::{MaterialPipeline, MaterialPipelineKey}, prelude::*, - reflect::TypeUuid, + reflect::{TypePath, TypeUuid}, render::{ mesh::MeshVertexBufferLayout, render_resource::{ @@ -47,7 +47,7 @@ fn setup( } // This is the struct that will be passed to your shader -#[derive(AsBindGroup, Clone, TypeUuid)] +#[derive(AsBindGroup, Clone, TypeUuid, TypePath)] #[uuid = "4ee9c363-1124-4113-890e-199d81b00281"] pub struct CustomMaterial { #[uniform(0)] diff --git a/examples/shader/shader_material_screenspace_texture.rs b/examples/shader/shader_material_screenspace_texture.rs index 7ead718c147d2..bace5855fba8c 100644 --- a/examples/shader/shader_material_screenspace_texture.rs +++ b/examples/shader/shader_material_screenspace_texture.rs @@ -2,7 +2,7 @@ use bevy::{ prelude::*, - reflect::TypeUuid, + reflect::{TypePath, TypeUuid}, render::render_resource::{AsBindGroup, ShaderRef}, }; @@ -66,7 +66,7 @@ fn rotate_camera(mut camera: Query<&mut Transform, With>, time: Res< cam_transform.look_at(Vec3::ZERO, Vec3::Y); } -#[derive(AsBindGroup, Debug, Clone, TypeUuid)] +#[derive(AsBindGroup, Debug, Clone, TypeUuid, TypePath)] #[uuid = "b62bb455-a72c-4b56-87bb-81e0554e234f"] pub struct CustomMaterial { #[texture(0)] diff --git a/examples/shader/shader_prepass.rs b/examples/shader/shader_prepass.rs index e7f848a0421bc..6d771628390c1 100644 --- a/examples/shader/shader_prepass.rs +++ b/examples/shader/shader_prepass.rs @@ -6,7 +6,7 @@ use bevy::{ core_pipeline::prepass::{DepthPrepass, MotionVectorPrepass, NormalPrepass}, pbr::{NotShadowCaster, PbrPlugin}, prelude::*, - reflect::TypeUuid, + reflect::{TypePath, TypeUuid}, render::render_resource::{AsBindGroup, ShaderRef, ShaderType}, }; @@ -155,7 +155,7 @@ fn setup( } // This is the struct that will be passed to your shader -#[derive(AsBindGroup, TypeUuid, Debug, Clone)] +#[derive(AsBindGroup, TypePath, TypeUuid, Debug, Clone)] #[uuid = "f690fdae-d598-45ab-8225-97e2a3f056e0"] pub struct CustomMaterial { #[uniform(0)] @@ -204,7 +204,7 @@ struct ShowPrepassSettings { } // This shader simply loads the prepass texture and outputs it directly -#[derive(AsBindGroup, TypeUuid, Debug, Clone)] +#[derive(AsBindGroup, TypePath, TypeUuid, Debug, Clone)] #[uuid = "0af99895-b96e-4451-bc12-c6b1c1c52750"] pub struct PrepassOutputMaterial { #[uniform(0)] diff --git a/examples/shader/texture_binding_array.rs b/examples/shader/texture_binding_array.rs index 22d4075a5e1d7..9bc7d6efb0d1d 100644 --- a/examples/shader/texture_binding_array.rs +++ b/examples/shader/texture_binding_array.rs @@ -3,7 +3,7 @@ use bevy::{ prelude::*, - reflect::TypeUuid, + reflect::{TypePath, TypeUuid}, render::{ render_asset::RenderAssets, render_resource::{AsBindGroupError, PreparedBindGroup, *}, @@ -85,7 +85,7 @@ fn setup( }); } -#[derive(Debug, Clone, TypeUuid)] +#[derive(Debug, Clone, TypePath, TypeUuid)] #[uuid = "8dd2b424-45a2-4a53-ac29-7ce356b2d5fe"] struct BindlessMaterial { textures: Vec>,