bevy_reflect: Generic parameter info (#15475)
# Objective Currently, reflecting a generic type provides no information about the generic parameters. This means that you can't get access to the type of `T` in `Foo<T>` without creating custom type data (we do this for [`ReflectHandle`](https://docs.rs/bevy/0.14.2/bevy/asset/struct.ReflectHandle.html#method.asset_type_id)). ## Solution This PR makes it so that generic type parameters and generic const parameters are tracked in a `Generics` struct stored on the `TypeInfo` for a type. For example, `struct Foo<T, const N: usize>` will store `T` and `N` as a `TypeParamInfo` and `ConstParamInfo`, respectively. The stored information includes: - The name of the generic parameter (i.e. `T`, `N`, etc.) - The type of the generic parameter (remember that we're dealing with monomorphized types, so this will actually be a concrete type) - The default type/value, if any (e.g. `f32` in `T = f32` or `10` in `const N: usize = 10`) ### Caveats The only requirement for this to work is that the user does not opt-out of the automatic `TypePath` derive with `#[reflect(type_path = false)]`. Doing so prevents the macro code from 100% knowing that the generic type implements `TypePath`. This in turn means the generated `Typed` impl can't add generics to the type. There are two solutions for this—both of which I think we should explore in a future PR: 1. We could just not use `TypePath`. This would mean that we can't store the `Type` of the generic, but we can at least store the `TypeId`. 2. We could provide a way to opt out of the automatic `Typed` derive with a `#[reflect(typed = false)]` attribute. This would allow users to manually implement `Typed` to add whatever generic information they need (e.g. skipping a parameter that can't implement `TypePath` while the rest can). I originally thought about making `Generics` an enum with `Generic`, `NonGeneric`, and `Unavailable` variants to signify whether there are generics, no generics, or generics that cannot be added due to opting out of `TypePath`. I ultimately decided against this as I think it adds a bit too much complexity for such an uncommon problem. Additionally, user's don't necessarily _have_ to know the generics of a type, so just skipping them should generally be fine for now. ## Testing You can test locally by running: ``` cargo test --package bevy_reflect ``` --- ## Showcase You can now access generic parameters via `TypeInfo`! ```rust #[derive(Reflect)] struct MyStruct<T, const N: usize>([T; N]); let generics = MyStruct::<f32, 10>::type_info().generics(); // Get by index: let t = generics.get(0).unwrap(); assert_eq!(t.name(), "T"); assert!(t.ty().is::<f32>()); assert!(!t.is_const()); // Or by name: let n = generics.get_named("N").unwrap(); assert_eq!(n.name(), "N"); assert!(n.ty().is::<usize>()); assert!(n.is_const()); ``` You can even access parameter defaults: ```rust #[derive(Reflect)] struct MyStruct<T = String, const N: usize = 10>([T; N]); let generics = MyStruct::<f32, 5>::type_info().generics(); let GenericInfo::Type(info) = generics.get_named("T").unwrap() else { panic!("expected a type parameter"); }; let default = info.default().unwrap(); assert!(default.is::<String>()); let GenericInfo::Const(info) = generics.get_named("N").unwrap() else { panic!("expected a const parameter"); }; let default = info.default().unwrap(); assert_eq!(default.downcast_ref::<usize>().unwrap(), &10); ```
This commit is contained in:
parent
8bcda3d2e8
commit
397f20e835
@ -15,6 +15,7 @@ use crate::{
|
||||
use quote::{quote, ToTokens};
|
||||
use syn::token::Comma;
|
||||
|
||||
use crate::generics::generate_generics;
|
||||
use syn::{
|
||||
parse_str, punctuated::Punctuated, spanned::Spanned, Data, DeriveInput, Field, Fields,
|
||||
GenericParam, Generics, Ident, LitStr, Meta, Path, PathSegment, Type, TypeParam, Variant,
|
||||
@ -627,13 +628,6 @@ impl<'a> ReflectStruct<'a> {
|
||||
.custom_attributes()
|
||||
.to_tokens(bevy_reflect_path);
|
||||
|
||||
#[cfg_attr(
|
||||
not(feature = "documentation"),
|
||||
expect(
|
||||
unused_mut,
|
||||
reason = "Needs to be mutable if `documentation` feature is enabled.",
|
||||
)
|
||||
)]
|
||||
let mut info = quote! {
|
||||
#bevy_reflect_path::#info_struct::new::<Self>(&[
|
||||
#(#field_infos),*
|
||||
@ -641,6 +635,12 @@ impl<'a> ReflectStruct<'a> {
|
||||
.with_custom_attributes(#custom_attributes)
|
||||
};
|
||||
|
||||
if let Some(generics) = generate_generics(self.meta()) {
|
||||
info.extend(quote! {
|
||||
.with_generics(#generics)
|
||||
});
|
||||
}
|
||||
|
||||
#[cfg(feature = "documentation")]
|
||||
{
|
||||
let docs = self.meta().doc();
|
||||
@ -730,13 +730,6 @@ impl<'a> ReflectEnum<'a> {
|
||||
.custom_attributes()
|
||||
.to_tokens(bevy_reflect_path);
|
||||
|
||||
#[cfg_attr(
|
||||
not(feature = "documentation"),
|
||||
expect(
|
||||
unused_mut,
|
||||
reason = "Needs to be mutable if `documentation` feature is enabled.",
|
||||
)
|
||||
)]
|
||||
let mut info = quote! {
|
||||
#bevy_reflect_path::EnumInfo::new::<Self>(&[
|
||||
#(#variants),*
|
||||
@ -744,6 +737,12 @@ impl<'a> ReflectEnum<'a> {
|
||||
.with_custom_attributes(#custom_attributes)
|
||||
};
|
||||
|
||||
if let Some(generics) = generate_generics(self.meta()) {
|
||||
info.extend(quote! {
|
||||
.with_generics(#generics)
|
||||
});
|
||||
}
|
||||
|
||||
#[cfg(feature = "documentation")]
|
||||
{
|
||||
let docs = self.meta().doc();
|
||||
|
||||
72
crates/bevy_reflect/derive/src/generics.rs
Normal file
72
crates/bevy_reflect/derive/src/generics.rs
Normal file
@ -0,0 +1,72 @@
|
||||
use crate::derive_data::ReflectMeta;
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::quote;
|
||||
use syn::punctuated::Punctuated;
|
||||
use syn::{GenericParam, Token};
|
||||
|
||||
/// Creates a `TokenStream` for generating an expression that creates a `Generics` instance.
|
||||
///
|
||||
/// Returns `None` if `Generics` cannot or should not be generated.
|
||||
pub(crate) fn generate_generics(meta: &ReflectMeta) -> Option<TokenStream> {
|
||||
if !meta.attrs().type_path_attrs().should_auto_derive() {
|
||||
// Cannot verify that all generic parameters implement `TypePath`
|
||||
return None;
|
||||
}
|
||||
|
||||
let bevy_reflect_path = meta.bevy_reflect_path();
|
||||
|
||||
let generics = meta
|
||||
.type_path()
|
||||
.generics()
|
||||
.params
|
||||
.iter()
|
||||
.filter_map(|param| match param {
|
||||
GenericParam::Type(ty_param) => {
|
||||
let ident = &ty_param.ident;
|
||||
let name = ident.to_string();
|
||||
let with_default = ty_param
|
||||
.default
|
||||
.as_ref()
|
||||
.map(|default_ty| quote!(.with_default::<#default_ty>()));
|
||||
|
||||
Some(quote! {
|
||||
#bevy_reflect_path::GenericInfo::Type(
|
||||
#bevy_reflect_path::TypeParamInfo::new::<#ident>(
|
||||
::std::borrow::Cow::Borrowed(#name),
|
||||
)
|
||||
#with_default
|
||||
)
|
||||
})
|
||||
}
|
||||
GenericParam::Const(const_param) => {
|
||||
let ty = &const_param.ty;
|
||||
let name = const_param.ident.to_string();
|
||||
let with_default = const_param.default.as_ref().map(|default| {
|
||||
// We add the `as #ty` to ensure that the correct type is inferred.
|
||||
quote!(.with_default(#default as #ty))
|
||||
});
|
||||
|
||||
Some(quote! {
|
||||
#[allow(
|
||||
clippy::unnecessary_cast,
|
||||
reason = "reflection requires an explicit type hint for const generics"
|
||||
)]
|
||||
#bevy_reflect_path::GenericInfo::Const(
|
||||
#bevy_reflect_path::ConstParamInfo::new::<#ty>(
|
||||
::std::borrow::Cow::Borrowed(#name),
|
||||
)
|
||||
#with_default
|
||||
)
|
||||
})
|
||||
}
|
||||
GenericParam::Lifetime(_) => None,
|
||||
})
|
||||
.collect::<Punctuated<_, Token![,]>>();
|
||||
|
||||
if generics.is_empty() {
|
||||
// No generics to generate
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(quote!(#bevy_reflect_path::Generics::from_iter([ #generics ])))
|
||||
}
|
||||
@ -25,6 +25,7 @@ mod documentation;
|
||||
mod enum_utility;
|
||||
mod field_attributes;
|
||||
mod from_reflect;
|
||||
mod generics;
|
||||
mod ident;
|
||||
mod impls;
|
||||
mod meta;
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
use crate::generics::impl_generic_info_methods;
|
||||
use crate::{
|
||||
self as bevy_reflect, type_info::impl_type_methods, utility::reflect_hasher, ApplyError,
|
||||
MaybeTyped, PartialReflect, Reflect, ReflectKind, ReflectMut, ReflectOwned, ReflectRef, Type,
|
||||
TypeInfo, TypePath,
|
||||
Generics, MaybeTyped, PartialReflect, Reflect, ReflectKind, ReflectMut, ReflectOwned,
|
||||
ReflectRef, Type, TypeInfo, TypePath,
|
||||
};
|
||||
use bevy_reflect_derive::impl_type_path;
|
||||
use core::{
|
||||
@ -79,6 +80,7 @@ pub trait Array: PartialReflect {
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ArrayInfo {
|
||||
ty: Type,
|
||||
generics: Generics,
|
||||
item_info: fn() -> Option<&'static TypeInfo>,
|
||||
item_ty: Type,
|
||||
capacity: usize,
|
||||
@ -97,6 +99,7 @@ impl ArrayInfo {
|
||||
) -> Self {
|
||||
Self {
|
||||
ty: Type::of::<TArray>(),
|
||||
generics: Generics::new(),
|
||||
item_info: TItem::maybe_type_info,
|
||||
item_ty: Type::of::<TItem>(),
|
||||
capacity,
|
||||
@ -138,6 +141,8 @@ impl ArrayInfo {
|
||||
pub fn docs(&self) -> Option<&'static str> {
|
||||
self.docs
|
||||
}
|
||||
|
||||
impl_generic_info_methods!(generics);
|
||||
}
|
||||
|
||||
/// A fixed-size list of reflected values.
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
use crate::generics::impl_generic_info_methods;
|
||||
use crate::{
|
||||
attributes::{impl_custom_attribute_methods, CustomAttributes},
|
||||
type_info::impl_type_methods,
|
||||
DynamicEnum, PartialReflect, Type, TypePath, VariantInfo, VariantType,
|
||||
DynamicEnum, Generics, PartialReflect, Type, TypePath, VariantInfo, VariantType,
|
||||
};
|
||||
use alloc::sync::Arc;
|
||||
use bevy_utils::HashMap;
|
||||
@ -138,6 +139,7 @@ pub trait Enum: PartialReflect {
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct EnumInfo {
|
||||
ty: Type,
|
||||
generics: Generics,
|
||||
variants: Box<[VariantInfo]>,
|
||||
variant_names: Box<[&'static str]>,
|
||||
variant_indices: HashMap<&'static str, usize>,
|
||||
@ -163,6 +165,7 @@ impl EnumInfo {
|
||||
|
||||
Self {
|
||||
ty: Type::of::<TEnum>(),
|
||||
generics: Generics::new(),
|
||||
variants: variants.to_vec().into_boxed_slice(),
|
||||
variant_names,
|
||||
variant_indices,
|
||||
@ -239,6 +242,8 @@ impl EnumInfo {
|
||||
}
|
||||
|
||||
impl_custom_attribute_methods!(self.custom_attributes, "enum");
|
||||
|
||||
impl_generic_info_methods!(generics);
|
||||
}
|
||||
|
||||
/// An iterator over the fields in the current enum variant.
|
||||
|
||||
338
crates/bevy_reflect/src/generics.rs
Normal file
338
crates/bevy_reflect/src/generics.rs
Normal file
@ -0,0 +1,338 @@
|
||||
use crate::type_info::impl_type_methods;
|
||||
use crate::{Reflect, Type, TypePath};
|
||||
use alloc::borrow::Cow;
|
||||
use alloc::sync::Arc;
|
||||
use core::ops::Deref;
|
||||
|
||||
/// The generic parameters of a type.
|
||||
///
|
||||
/// This is automatically generated via the [`Reflect` derive macro]
|
||||
/// and stored on the [`TypeInfo`] returned by [`Typed::type_info`]
|
||||
/// for types that have generics.
|
||||
///
|
||||
/// It supports both type parameters and const parameters
|
||||
/// so long as they implement [`TypePath`].
|
||||
///
|
||||
/// If the type has no generics, this will be empty.
|
||||
///
|
||||
/// If the type is marked with `#[reflect(type_path = false)]`,
|
||||
/// the generics will be empty even if the type has generics.
|
||||
///
|
||||
/// [`Reflect` derive macro]: bevy_reflect_derive::Reflect
|
||||
/// [`TypeInfo`]: crate::type_info::TypeInfo
|
||||
/// [`Typed::type_info`]: crate::Typed::type_info
|
||||
#[derive(Clone, Default, Debug)]
|
||||
pub struct Generics(Box<[GenericInfo]>);
|
||||
|
||||
impl Generics {
|
||||
/// Creates an empty set of generics.
|
||||
pub fn new() -> Self {
|
||||
Self(Box::new([]))
|
||||
}
|
||||
|
||||
/// Finds the generic parameter with the given name.
|
||||
///
|
||||
/// Returns `None` if no such parameter exists.
|
||||
pub fn get_named(&self, name: &str) -> Option<&GenericInfo> {
|
||||
// For small sets of generics (the most common case),
|
||||
// a linear search is often faster using a `HashMap`.
|
||||
self.0.iter().find(|info| info.name() == name)
|
||||
}
|
||||
|
||||
/// Adds the given generic parameter to the set.
|
||||
pub fn with(mut self, info: impl Into<GenericInfo>) -> Self {
|
||||
self.0 = IntoIterator::into_iter(self.0)
|
||||
.chain(core::iter::once(info.into()))
|
||||
.collect();
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Into<GenericInfo>> FromIterator<T> for Generics {
|
||||
fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
|
||||
Self(iter.into_iter().map(Into::into).collect())
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for Generics {
|
||||
type Target = [GenericInfo];
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
/// An enum representing a generic parameter.
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum GenericInfo {
|
||||
/// A type parameter.
|
||||
///
|
||||
/// An example would be `T` in `struct Foo<T, U>`.
|
||||
Type(TypeParamInfo),
|
||||
/// A const parameter.
|
||||
///
|
||||
/// An example would be `N` in `struct Foo<const N: usize>`.
|
||||
Const(ConstParamInfo),
|
||||
}
|
||||
|
||||
impl GenericInfo {
|
||||
/// The name of the generic parameter.
|
||||
pub fn name(&self) -> &Cow<'static, str> {
|
||||
match self {
|
||||
Self::Type(info) => info.name(),
|
||||
Self::Const(info) => info.name(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether the generic parameter is a const parameter.
|
||||
pub fn is_const(&self) -> bool {
|
||||
match self {
|
||||
Self::Type(_) => false,
|
||||
Self::Const(_) => true,
|
||||
}
|
||||
}
|
||||
|
||||
impl_type_methods!(self => {
|
||||
match self {
|
||||
Self::Type(info) => info.ty(),
|
||||
Self::Const(info) => info.ty(),
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
impl From<TypeParamInfo> for GenericInfo {
|
||||
fn from(info: TypeParamInfo) -> Self {
|
||||
Self::Type(info)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ConstParamInfo> for GenericInfo {
|
||||
fn from(info: ConstParamInfo) -> Self {
|
||||
Self::Const(info)
|
||||
}
|
||||
}
|
||||
|
||||
/// Type information for a generic type parameter.
|
||||
///
|
||||
/// An example of a type parameter would be `T` in `struct Foo<T>`.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct TypeParamInfo {
|
||||
name: Cow<'static, str>,
|
||||
ty: Type,
|
||||
default: Option<Type>,
|
||||
}
|
||||
|
||||
impl TypeParamInfo {
|
||||
/// Creates a new type parameter with the given name.
|
||||
pub fn new<T: TypePath + ?Sized>(name: impl Into<Cow<'static, str>>) -> Self {
|
||||
Self {
|
||||
name: name.into(),
|
||||
ty: Type::of::<T>(),
|
||||
default: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the default type for the parameter.
|
||||
pub fn with_default<T: TypePath + ?Sized>(mut self) -> Self {
|
||||
self.default = Some(Type::of::<T>());
|
||||
self
|
||||
}
|
||||
|
||||
/// The name of the type parameter.
|
||||
pub fn name(&self) -> &Cow<'static, str> {
|
||||
&self.name
|
||||
}
|
||||
|
||||
/// The default type for the parameter, if any.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # use bevy_reflect::{GenericInfo, Reflect, Typed};
|
||||
/// #[derive(Reflect)]
|
||||
/// struct Foo<T = f32>(T);
|
||||
///
|
||||
/// let generics = Foo::<String>::type_info().generics();
|
||||
/// let GenericInfo::Type(info) = generics.get_named("T").unwrap() else {
|
||||
/// panic!("expected a type parameter");
|
||||
/// };
|
||||
///
|
||||
/// let default = info.default().unwrap();
|
||||
///
|
||||
/// assert!(default.is::<f32>());
|
||||
/// ```
|
||||
pub fn default(&self) -> Option<&Type> {
|
||||
self.default.as_ref()
|
||||
}
|
||||
|
||||
impl_type_methods!(ty);
|
||||
}
|
||||
|
||||
/// Type information for a const generic parameter.
|
||||
///
|
||||
/// An example of a const parameter would be `N` in `struct Foo<const N: usize>`.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ConstParamInfo {
|
||||
name: Cow<'static, str>,
|
||||
ty: Type,
|
||||
// Rust currently only allows certain primitive types in const generic position,
|
||||
// meaning that `Reflect` is guaranteed to be implemented for the default value.
|
||||
default: Option<Arc<dyn Reflect>>,
|
||||
}
|
||||
|
||||
impl ConstParamInfo {
|
||||
/// Creates a new const parameter with the given name.
|
||||
pub fn new<T: TypePath + ?Sized>(name: impl Into<Cow<'static, str>>) -> Self {
|
||||
Self {
|
||||
name: name.into(),
|
||||
ty: Type::of::<T>(),
|
||||
default: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the default value for the parameter.
|
||||
pub fn with_default<T: Reflect + 'static>(mut self, default: T) -> Self {
|
||||
self.default = Some(Arc::new(default));
|
||||
self
|
||||
}
|
||||
|
||||
/// The name of the const parameter.
|
||||
pub fn name(&self) -> &Cow<'static, str> {
|
||||
&self.name
|
||||
}
|
||||
|
||||
/// The default value for the parameter, if any.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # use bevy_reflect::{GenericInfo, Reflect, Typed};
|
||||
/// #[derive(Reflect)]
|
||||
/// struct Foo<const N: usize = 10>([u8; N]);
|
||||
///
|
||||
/// let generics = Foo::<5>::type_info().generics();
|
||||
/// let GenericInfo::Const(info) = generics.get_named("N").unwrap() else {
|
||||
/// panic!("expected a const parameter");
|
||||
/// };
|
||||
///
|
||||
/// let default = info.default().unwrap();
|
||||
///
|
||||
/// assert_eq!(default.downcast_ref::<usize>().unwrap(), &10);
|
||||
/// ```
|
||||
pub fn default(&self) -> Option<&dyn Reflect> {
|
||||
self.default.as_deref()
|
||||
}
|
||||
|
||||
impl_type_methods!(ty);
|
||||
}
|
||||
|
||||
macro_rules! impl_generic_info_methods {
|
||||
// Implements both getter and setter methods for the given field.
|
||||
($field:ident) => {
|
||||
$crate::generics::impl_generic_info_methods!(self => &self.$field);
|
||||
|
||||
/// Sets the generic parameters for this type.
|
||||
pub fn with_generics(mut self, generics: crate::generics::Generics) -> Self {
|
||||
self.$field = generics;
|
||||
self
|
||||
}
|
||||
};
|
||||
// Implements only a getter method for the given expression.
|
||||
($self:ident => $expr:expr) => {
|
||||
/// Gets the generic parameters for this type.
|
||||
pub fn generics(&$self) -> &crate::generics::Generics {
|
||||
$expr
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub(crate) use impl_generic_info_methods;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate as bevy_reflect;
|
||||
use crate::{Reflect, Typed};
|
||||
use core::fmt::Debug;
|
||||
|
||||
#[test]
|
||||
fn should_maintain_order() {
|
||||
#[derive(Reflect)]
|
||||
struct Test<T, U: Debug, const N: usize>([(T, U); N]);
|
||||
|
||||
let generics = <Test<f32, String, 10> as Typed>::type_info()
|
||||
.as_tuple_struct()
|
||||
.unwrap()
|
||||
.generics();
|
||||
|
||||
assert_eq!(generics.len(), 3);
|
||||
|
||||
let mut iter = generics.iter();
|
||||
|
||||
let t = iter.next().unwrap();
|
||||
assert_eq!(t.name(), "T");
|
||||
assert!(t.ty().is::<f32>());
|
||||
assert!(!t.is_const());
|
||||
|
||||
let u = iter.next().unwrap();
|
||||
assert_eq!(u.name(), "U");
|
||||
assert!(u.ty().is::<String>());
|
||||
assert!(!u.is_const());
|
||||
|
||||
let n = iter.next().unwrap();
|
||||
assert_eq!(n.name(), "N");
|
||||
assert!(n.ty().is::<usize>());
|
||||
assert!(n.is_const());
|
||||
|
||||
assert!(iter.next().is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_get_by_name() {
|
||||
#[derive(Reflect)]
|
||||
enum Test<T, U: Debug, const N: usize> {
|
||||
Array([(T, U); N]),
|
||||
}
|
||||
|
||||
let generics = <Test<f32, String, 10> as Typed>::type_info()
|
||||
.as_enum()
|
||||
.unwrap()
|
||||
.generics();
|
||||
|
||||
let t = generics.get_named("T").unwrap();
|
||||
assert_eq!(t.name(), "T");
|
||||
assert!(t.ty().is::<f32>());
|
||||
assert!(!t.is_const());
|
||||
|
||||
let u = generics.get_named("U").unwrap();
|
||||
assert_eq!(u.name(), "U");
|
||||
assert!(u.ty().is::<String>());
|
||||
assert!(!u.is_const());
|
||||
|
||||
let n = generics.get_named("N").unwrap();
|
||||
assert_eq!(n.name(), "N");
|
||||
assert!(n.ty().is::<usize>());
|
||||
assert!(n.is_const());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_store_defaults() {
|
||||
#[derive(Reflect)]
|
||||
struct Test<T, U: Debug = String, const N: usize = 10>([(T, U); N]);
|
||||
|
||||
let generics = <Test<f32> as Typed>::type_info()
|
||||
.as_tuple_struct()
|
||||
.unwrap()
|
||||
.generics();
|
||||
|
||||
let GenericInfo::Type(u) = generics.get_named("U").unwrap() else {
|
||||
panic!("expected a type parameter");
|
||||
};
|
||||
assert_eq!(u.default().unwrap(), &Type::of::<String>());
|
||||
|
||||
let GenericInfo::Const(n) = generics.get_named("N").unwrap() else {
|
||||
panic!("expected a const parameter");
|
||||
};
|
||||
assert_eq!(n.default().unwrap().downcast_ref::<usize>().unwrap(), &10);
|
||||
}
|
||||
}
|
||||
@ -5,9 +5,9 @@ use core::any::Any;
|
||||
|
||||
use crate::{
|
||||
self as bevy_reflect, utility::GenericTypeInfoCell, ApplyError, FromReflect, FromType,
|
||||
GetTypeRegistration, List, ListInfo, ListIter, MaybeTyped, PartialReflect, Reflect,
|
||||
ReflectFromPtr, ReflectKind, ReflectMut, ReflectOwned, ReflectRef, TypeInfo, TypePath,
|
||||
TypeRegistration, Typed,
|
||||
Generics, GetTypeRegistration, List, ListInfo, ListIter, MaybeTyped, PartialReflect, Reflect,
|
||||
ReflectFromPtr, ReflectKind, ReflectMut, ReflectOwned, ReflectRef, TypeInfo, TypeParamInfo,
|
||||
TypePath, TypeRegistration, Typed,
|
||||
};
|
||||
|
||||
impl<T: SmallArray + TypePath + Send + Sync> List for SmallVec<T>
|
||||
@ -183,7 +183,12 @@ where
|
||||
{
|
||||
fn type_info() -> &'static TypeInfo {
|
||||
static CELL: GenericTypeInfoCell = GenericTypeInfoCell::new();
|
||||
CELL.get_or_insert::<Self, _>(|| TypeInfo::List(ListInfo::new::<Self, T::Item>()))
|
||||
CELL.get_or_insert::<Self, _>(|| {
|
||||
TypeInfo::List(
|
||||
ListInfo::new::<Self, T::Item>()
|
||||
.with_generics(Generics::from_iter([TypeParamInfo::new::<T>("T")])),
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -8,10 +8,10 @@ use crate::{
|
||||
set_apply, set_partial_eq, set_try_apply,
|
||||
utility::{reflect_hasher, GenericTypeInfoCell, GenericTypePathCell, NonGenericTypeInfoCell},
|
||||
ApplyError, Array, ArrayInfo, ArrayIter, DynamicMap, DynamicSet, DynamicTypePath, FromReflect,
|
||||
FromType, GetTypeRegistration, List, ListInfo, ListIter, Map, MapInfo, MapIter, MaybeTyped,
|
||||
OpaqueInfo, PartialReflect, Reflect, ReflectDeserialize, ReflectFromPtr, ReflectFromReflect,
|
||||
ReflectKind, ReflectMut, ReflectOwned, ReflectRef, ReflectSerialize, Set, SetInfo, TypeInfo,
|
||||
TypePath, TypeRegistration, TypeRegistry, Typed,
|
||||
FromType, Generics, GetTypeRegistration, List, ListInfo, ListIter, Map, MapInfo, MapIter,
|
||||
MaybeTyped, OpaqueInfo, PartialReflect, Reflect, ReflectDeserialize, ReflectFromPtr,
|
||||
ReflectFromReflect, ReflectKind, ReflectMut, ReflectOwned, ReflectRef, ReflectSerialize, Set,
|
||||
SetInfo, TypeInfo, TypeParamInfo, TypePath, TypeRegistration, TypeRegistry, Typed,
|
||||
};
|
||||
use alloc::{borrow::Cow, collections::VecDeque};
|
||||
use bevy_reflect_derive::{impl_reflect, impl_reflect_opaque};
|
||||
@ -525,7 +525,13 @@ macro_rules! impl_reflect_for_veclike {
|
||||
impl<T: FromReflect + MaybeTyped + TypePath + GetTypeRegistration> Typed for $ty {
|
||||
fn type_info() -> &'static TypeInfo {
|
||||
static CELL: GenericTypeInfoCell = GenericTypeInfoCell::new();
|
||||
CELL.get_or_insert::<Self, _>(|| TypeInfo::List(ListInfo::new::<Self, T>()))
|
||||
CELL.get_or_insert::<Self, _>(|| {
|
||||
TypeInfo::List(
|
||||
ListInfo::new::<Self, T>().with_generics(Generics::from_iter([
|
||||
TypeParamInfo::new::<T>("T")
|
||||
]))
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -764,7 +770,14 @@ macro_rules! impl_reflect_for_hashmap {
|
||||
{
|
||||
fn type_info() -> &'static TypeInfo {
|
||||
static CELL: GenericTypeInfoCell = GenericTypeInfoCell::new();
|
||||
CELL.get_or_insert::<Self, _>(|| TypeInfo::Map(MapInfo::new::<Self, K, V>()))
|
||||
CELL.get_or_insert::<Self, _>(|| {
|
||||
TypeInfo::Map(
|
||||
MapInfo::new::<Self, K, V>().with_generics(Generics::from_iter([
|
||||
TypeParamInfo::new::<K>("K"),
|
||||
TypeParamInfo::new::<V>("V"),
|
||||
])),
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -981,7 +994,13 @@ macro_rules! impl_reflect_for_hashset {
|
||||
{
|
||||
fn type_info() -> &'static TypeInfo {
|
||||
static CELL: GenericTypeInfoCell = GenericTypeInfoCell::new();
|
||||
CELL.get_or_insert::<Self, _>(|| TypeInfo::Set(SetInfo::new::<Self, V>()))
|
||||
CELL.get_or_insert::<Self, _>(|| {
|
||||
TypeInfo::Set(
|
||||
SetInfo::new::<Self, V>().with_generics(Generics::from_iter([
|
||||
TypeParamInfo::new::<V>("V")
|
||||
]))
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -1233,7 +1252,14 @@ where
|
||||
{
|
||||
fn type_info() -> &'static TypeInfo {
|
||||
static CELL: GenericTypeInfoCell = GenericTypeInfoCell::new();
|
||||
CELL.get_or_insert::<Self, _>(|| TypeInfo::Map(MapInfo::new::<Self, K, V>()))
|
||||
CELL.get_or_insert::<Self, _>(|| {
|
||||
TypeInfo::Map(
|
||||
MapInfo::new::<Self, K, V>().with_generics(Generics::from_iter([
|
||||
TypeParamInfo::new::<K>("K"),
|
||||
TypeParamInfo::new::<V>("V"),
|
||||
])),
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -583,6 +583,7 @@ mod impls {
|
||||
|
||||
pub mod attributes;
|
||||
mod enums;
|
||||
mod generics;
|
||||
pub mod serde;
|
||||
pub mod std_traits;
|
||||
#[cfg(feature = "debug_stack")]
|
||||
@ -610,6 +611,7 @@ pub use array::*;
|
||||
pub use enums::*;
|
||||
pub use fields::*;
|
||||
pub use from_reflect::*;
|
||||
pub use generics::*;
|
||||
pub use kind::*;
|
||||
pub use list::*;
|
||||
pub use map::*;
|
||||
|
||||
@ -6,10 +6,11 @@ use core::{
|
||||
|
||||
use bevy_reflect_derive::impl_type_path;
|
||||
|
||||
use crate::generics::impl_generic_info_methods;
|
||||
use crate::{
|
||||
self as bevy_reflect, type_info::impl_type_methods, utility::reflect_hasher, ApplyError,
|
||||
FromReflect, MaybeTyped, PartialReflect, Reflect, ReflectKind, ReflectMut, ReflectOwned,
|
||||
ReflectRef, Type, TypeInfo, TypePath,
|
||||
FromReflect, Generics, MaybeTyped, PartialReflect, Reflect, ReflectKind, ReflectMut,
|
||||
ReflectOwned, ReflectRef, Type, TypeInfo, TypePath,
|
||||
};
|
||||
|
||||
/// A trait used to power [list-like] operations via [reflection].
|
||||
@ -114,6 +115,7 @@ pub trait List: PartialReflect {
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ListInfo {
|
||||
ty: Type,
|
||||
generics: Generics,
|
||||
item_info: fn() -> Option<&'static TypeInfo>,
|
||||
item_ty: Type,
|
||||
#[cfg(feature = "documentation")]
|
||||
@ -125,6 +127,7 @@ impl ListInfo {
|
||||
pub fn new<TList: List + TypePath, TItem: FromReflect + MaybeTyped + TypePath>() -> Self {
|
||||
Self {
|
||||
ty: Type::of::<TList>(),
|
||||
generics: Generics::new(),
|
||||
item_info: TItem::maybe_type_info,
|
||||
item_ty: Type::of::<TItem>(),
|
||||
#[cfg(feature = "documentation")]
|
||||
@ -160,6 +163,8 @@ impl ListInfo {
|
||||
pub fn docs(&self) -> Option<&'static str> {
|
||||
self.docs
|
||||
}
|
||||
|
||||
impl_generic_info_methods!(generics);
|
||||
}
|
||||
|
||||
/// A list of reflected values.
|
||||
|
||||
@ -3,9 +3,11 @@ use core::fmt::{Debug, Formatter};
|
||||
use bevy_reflect_derive::impl_type_path;
|
||||
use bevy_utils::hashbrown::HashTable;
|
||||
|
||||
use crate::generics::impl_generic_info_methods;
|
||||
use crate::{
|
||||
self as bevy_reflect, type_info::impl_type_methods, ApplyError, MaybeTyped, PartialReflect,
|
||||
Reflect, ReflectKind, ReflectMut, ReflectOwned, ReflectRef, Type, TypeInfo, TypePath,
|
||||
self as bevy_reflect, type_info::impl_type_methods, ApplyError, Generics, MaybeTyped,
|
||||
PartialReflect, Reflect, ReflectKind, ReflectMut, ReflectOwned, ReflectRef, Type, TypeInfo,
|
||||
TypePath,
|
||||
};
|
||||
|
||||
/// A trait used to power [map-like] operations via [reflection].
|
||||
@ -103,6 +105,7 @@ pub trait Map: PartialReflect {
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct MapInfo {
|
||||
ty: Type,
|
||||
generics: Generics,
|
||||
key_info: fn() -> Option<&'static TypeInfo>,
|
||||
key_ty: Type,
|
||||
value_info: fn() -> Option<&'static TypeInfo>,
|
||||
@ -120,6 +123,7 @@ impl MapInfo {
|
||||
>() -> Self {
|
||||
Self {
|
||||
ty: Type::of::<TMap>(),
|
||||
generics: Generics::new(),
|
||||
key_info: TKey::maybe_type_info,
|
||||
key_ty: Type::of::<TKey>(),
|
||||
value_info: TValue::maybe_type_info,
|
||||
@ -172,6 +176,8 @@ impl MapInfo {
|
||||
pub fn docs(&self) -> Option<&'static str> {
|
||||
self.docs
|
||||
}
|
||||
|
||||
impl_generic_info_methods!(generics);
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
|
||||
@ -3,9 +3,11 @@ use core::fmt::{Debug, Formatter};
|
||||
use bevy_reflect_derive::impl_type_path;
|
||||
use bevy_utils::hashbrown::{hash_table::OccupiedEntry as HashTableOccupiedEntry, HashTable};
|
||||
|
||||
use crate::generics::impl_generic_info_methods;
|
||||
use crate::{
|
||||
self as bevy_reflect, hash_error, type_info::impl_type_methods, ApplyError, PartialReflect,
|
||||
Reflect, ReflectKind, ReflectMut, ReflectOwned, ReflectRef, Type, TypeInfo, TypePath,
|
||||
self as bevy_reflect, hash_error, type_info::impl_type_methods, ApplyError, Generics,
|
||||
PartialReflect, Reflect, ReflectKind, ReflectMut, ReflectOwned, ReflectRef, Type, TypeInfo,
|
||||
TypePath,
|
||||
};
|
||||
|
||||
/// A trait used to power [set-like] operations via [reflection].
|
||||
@ -88,6 +90,7 @@ pub trait Set: PartialReflect {
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct SetInfo {
|
||||
ty: Type,
|
||||
generics: Generics,
|
||||
value_ty: Type,
|
||||
#[cfg(feature = "documentation")]
|
||||
docs: Option<&'static str>,
|
||||
@ -98,6 +101,7 @@ impl SetInfo {
|
||||
pub fn new<TSet: Set + TypePath, TValue: Reflect + TypePath>() -> Self {
|
||||
Self {
|
||||
ty: Type::of::<TSet>(),
|
||||
generics: Generics::new(),
|
||||
value_ty: Type::of::<TValue>(),
|
||||
#[cfg(feature = "documentation")]
|
||||
docs: None,
|
||||
@ -124,6 +128,8 @@ impl SetInfo {
|
||||
pub fn docs(&self) -> Option<&'static str> {
|
||||
self.docs
|
||||
}
|
||||
|
||||
impl_generic_info_methods!(generics);
|
||||
}
|
||||
|
||||
/// An ordered set of reflected values.
|
||||
|
||||
@ -1,9 +1,10 @@
|
||||
use crate::generics::impl_generic_info_methods;
|
||||
use crate::{
|
||||
self as bevy_reflect,
|
||||
attributes::{impl_custom_attribute_methods, CustomAttributes},
|
||||
type_info::impl_type_methods,
|
||||
ApplyError, NamedField, PartialReflect, Reflect, ReflectKind, ReflectMut, ReflectOwned,
|
||||
ReflectRef, Type, TypeInfo, TypePath,
|
||||
ApplyError, Generics, NamedField, PartialReflect, Reflect, ReflectKind, ReflectMut,
|
||||
ReflectOwned, ReflectRef, Type, TypeInfo, TypePath,
|
||||
};
|
||||
use alloc::{borrow::Cow, sync::Arc};
|
||||
use bevy_reflect_derive::impl_type_path;
|
||||
@ -79,6 +80,7 @@ pub trait Struct: PartialReflect {
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct StructInfo {
|
||||
ty: Type,
|
||||
generics: Generics,
|
||||
fields: Box<[NamedField]>,
|
||||
field_names: Box<[&'static str]>,
|
||||
field_indices: HashMap<&'static str, usize>,
|
||||
@ -104,6 +106,7 @@ impl StructInfo {
|
||||
|
||||
Self {
|
||||
ty: Type::of::<T>(),
|
||||
generics: Generics::new(),
|
||||
fields: fields.to_vec().into_boxed_slice(),
|
||||
field_names,
|
||||
field_indices,
|
||||
@ -168,6 +171,8 @@ impl StructInfo {
|
||||
}
|
||||
|
||||
impl_custom_attribute_methods!(self.custom_attributes, "struct");
|
||||
|
||||
impl_generic_info_methods!(generics);
|
||||
}
|
||||
|
||||
/// An iterator over the field values of a struct.
|
||||
|
||||
@ -1,11 +1,12 @@
|
||||
use bevy_reflect_derive::impl_type_path;
|
||||
use bevy_utils::all_tuples;
|
||||
|
||||
use crate::generics::impl_generic_info_methods;
|
||||
use crate::{
|
||||
self as bevy_reflect, type_info::impl_type_methods, utility::GenericTypePathCell, ApplyError,
|
||||
FromReflect, GetTypeRegistration, MaybeTyped, PartialReflect, Reflect, ReflectKind, ReflectMut,
|
||||
ReflectOwned, ReflectRef, Type, TypeInfo, TypePath, TypeRegistration, TypeRegistry, Typed,
|
||||
UnnamedField,
|
||||
FromReflect, Generics, GetTypeRegistration, MaybeTyped, PartialReflect, Reflect, ReflectKind,
|
||||
ReflectMut, ReflectOwned, ReflectRef, Type, TypeInfo, TypePath, TypeRegistration, TypeRegistry,
|
||||
Typed, UnnamedField,
|
||||
};
|
||||
use core::{
|
||||
any::Any,
|
||||
@ -142,6 +143,7 @@ impl GetTupleField for dyn Tuple {
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct TupleInfo {
|
||||
ty: Type,
|
||||
generics: Generics,
|
||||
fields: Box<[UnnamedField]>,
|
||||
#[cfg(feature = "documentation")]
|
||||
docs: Option<&'static str>,
|
||||
@ -156,6 +158,7 @@ impl TupleInfo {
|
||||
pub fn new<T: Reflect + TypePath>(fields: &[UnnamedField]) -> Self {
|
||||
Self {
|
||||
ty: Type::of::<T>(),
|
||||
generics: Generics::new(),
|
||||
fields: fields.to_vec().into_boxed_slice(),
|
||||
#[cfg(feature = "documentation")]
|
||||
docs: None,
|
||||
@ -190,6 +193,8 @@ impl TupleInfo {
|
||||
pub fn docs(&self) -> Option<&'static str> {
|
||||
self.docs
|
||||
}
|
||||
|
||||
impl_generic_info_methods!(generics);
|
||||
}
|
||||
|
||||
/// A tuple which allows fields to be added at runtime.
|
||||
|
||||
@ -1,11 +1,12 @@
|
||||
use bevy_reflect_derive::impl_type_path;
|
||||
|
||||
use crate::generics::impl_generic_info_methods;
|
||||
use crate::{
|
||||
self as bevy_reflect,
|
||||
attributes::{impl_custom_attribute_methods, CustomAttributes},
|
||||
type_info::impl_type_methods,
|
||||
ApplyError, DynamicTuple, PartialReflect, Reflect, ReflectKind, ReflectMut, ReflectOwned,
|
||||
ReflectRef, Tuple, Type, TypeInfo, TypePath, UnnamedField,
|
||||
ApplyError, DynamicTuple, Generics, PartialReflect, Reflect, ReflectKind, ReflectMut,
|
||||
ReflectOwned, ReflectRef, Tuple, Type, TypeInfo, TypePath, UnnamedField,
|
||||
};
|
||||
use alloc::sync::Arc;
|
||||
use core::{
|
||||
@ -62,6 +63,7 @@ pub trait TupleStruct: PartialReflect {
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct TupleStructInfo {
|
||||
ty: Type,
|
||||
generics: Generics,
|
||||
fields: Box<[UnnamedField]>,
|
||||
custom_attributes: Arc<CustomAttributes>,
|
||||
#[cfg(feature = "documentation")]
|
||||
@ -77,6 +79,7 @@ impl TupleStructInfo {
|
||||
pub fn new<T: Reflect + TypePath>(fields: &[UnnamedField]) -> Self {
|
||||
Self {
|
||||
ty: Type::of::<T>(),
|
||||
generics: Generics::new(),
|
||||
fields: fields.to_vec().into_boxed_slice(),
|
||||
custom_attributes: Arc::new(CustomAttributes::default()),
|
||||
#[cfg(feature = "documentation")]
|
||||
@ -122,6 +125,8 @@ impl TupleStructInfo {
|
||||
}
|
||||
|
||||
impl_custom_attribute_methods!(self.custom_attributes, "struct");
|
||||
|
||||
impl_generic_info_methods!(generics);
|
||||
}
|
||||
|
||||
/// An iterator over the field values of a tuple struct.
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
use crate::{
|
||||
ArrayInfo, DynamicArray, DynamicEnum, DynamicList, DynamicMap, DynamicStruct, DynamicTuple,
|
||||
DynamicTupleStruct, EnumInfo, ListInfo, MapInfo, PartialReflect, Reflect, ReflectKind, SetInfo,
|
||||
StructInfo, TupleInfo, TupleStructInfo, TypePath, TypePathTable,
|
||||
DynamicTupleStruct, EnumInfo, Generics, ListInfo, MapInfo, PartialReflect, Reflect,
|
||||
ReflectKind, SetInfo, StructInfo, TupleInfo, TupleStructInfo, TypePath, TypePathTable,
|
||||
};
|
||||
use core::{
|
||||
any::{Any, TypeId},
|
||||
@ -293,6 +293,20 @@ impl TypeInfo {
|
||||
Self::Opaque(_) => ReflectKind::Opaque,
|
||||
}
|
||||
}
|
||||
|
||||
impl_generic_info_methods!(self => {
|
||||
match self {
|
||||
Self::Struct(info) => info.generics(),
|
||||
Self::TupleStruct(info) => info.generics(),
|
||||
Self::Tuple(info) => info.generics(),
|
||||
Self::List(info) => info.generics(),
|
||||
Self::Array(info) => info.generics(),
|
||||
Self::Map(info) => info.generics(),
|
||||
Self::Set(info) => info.generics(),
|
||||
Self::Enum(info) => info.generics(),
|
||||
Self::Opaque(info) => info.generics(),
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
macro_rules! impl_cast_method {
|
||||
@ -467,19 +481,26 @@ impl Hash for Type {
|
||||
}
|
||||
|
||||
macro_rules! impl_type_methods {
|
||||
// Generates the type methods based off a single field.
|
||||
($field:ident) => {
|
||||
$crate::type_info::impl_type_methods!(self => {
|
||||
&self.$field
|
||||
});
|
||||
};
|
||||
// Generates the type methods based off a custom expression.
|
||||
($self:ident => $expr:expr) => {
|
||||
/// The underlying Rust [type].
|
||||
///
|
||||
/// [type]: crate::type_info::Type
|
||||
pub fn ty(&self) -> &$crate::type_info::Type {
|
||||
&self.$field
|
||||
pub fn ty(&$self) -> &$crate::type_info::Type {
|
||||
$expr
|
||||
}
|
||||
|
||||
/// The [`TypeId`] of this type.
|
||||
///
|
||||
/// [`TypeId`]: std::any::TypeId
|
||||
pub fn type_id(&self) -> ::core::any::TypeId {
|
||||
self.$field.id()
|
||||
self.ty().id()
|
||||
}
|
||||
|
||||
/// The [stable, full type path] of this type.
|
||||
@ -489,7 +510,7 @@ macro_rules! impl_type_methods {
|
||||
/// [stable, full type path]: TypePath
|
||||
/// [`type_path_table`]: Self::type_path_table
|
||||
pub fn type_path(&self) -> &'static str {
|
||||
self.$field.path()
|
||||
self.ty().path()
|
||||
}
|
||||
|
||||
/// A representation of the type path of this type.
|
||||
@ -498,7 +519,7 @@ macro_rules! impl_type_methods {
|
||||
///
|
||||
/// [`TypePath`]: crate::type_path::TypePath
|
||||
pub fn type_path_table(&self) -> &$crate::type_path::TypePathTable {
|
||||
&self.$field.type_path_table()
|
||||
&self.ty().type_path_table()
|
||||
}
|
||||
|
||||
/// Check if the given type matches this one.
|
||||
@ -510,11 +531,12 @@ macro_rules! impl_type_methods {
|
||||
/// [`TypeId`]: std::any::TypeId
|
||||
/// [`TypePath`]: crate::type_path::TypePath
|
||||
pub fn is<T: ::core::any::Any>(&self) -> bool {
|
||||
self.$field.is::<T>()
|
||||
self.ty().is::<T>()
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
use crate::generics::impl_generic_info_methods;
|
||||
pub(crate) use impl_type_methods;
|
||||
|
||||
/// A container for compile-time info related to reflection-opaque types, including primitives.
|
||||
@ -528,6 +550,7 @@ pub(crate) use impl_type_methods;
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct OpaqueInfo {
|
||||
ty: Type,
|
||||
generics: Generics,
|
||||
#[cfg(feature = "documentation")]
|
||||
docs: Option<&'static str>,
|
||||
}
|
||||
@ -536,6 +559,7 @@ impl OpaqueInfo {
|
||||
pub fn new<T: Reflect + TypePath + ?Sized>() -> Self {
|
||||
Self {
|
||||
ty: Type::of::<T>(),
|
||||
generics: Generics::new(),
|
||||
#[cfg(feature = "documentation")]
|
||||
docs: None,
|
||||
}
|
||||
@ -554,6 +578,8 @@ impl OpaqueInfo {
|
||||
pub fn docs(&self) -> Option<&'static str> {
|
||||
self.docs
|
||||
}
|
||||
|
||||
impl_generic_info_methods!(generics);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@ -138,7 +138,7 @@ impl<T: TypedProperty> Default for NonGenericTypeCell<T> {
|
||||
///
|
||||
/// ```
|
||||
/// # use std::any::Any;
|
||||
/// # use bevy_reflect::{DynamicTypePath, PartialReflect, Reflect, ReflectMut, ReflectOwned, ReflectRef, TupleStructInfo, Typed, TypeInfo, TypePath, UnnamedField, ApplyError};
|
||||
/// # use bevy_reflect::{DynamicTypePath, PartialReflect, Reflect, ReflectMut, ReflectOwned, ReflectRef, TupleStructInfo, Typed, TypeInfo, TypePath, UnnamedField, ApplyError, Generics, TypeParamInfo};
|
||||
/// use bevy_reflect::utility::GenericTypeInfoCell;
|
||||
///
|
||||
/// struct Foo<T>(T);
|
||||
@ -148,7 +148,8 @@ impl<T: TypedProperty> Default for NonGenericTypeCell<T> {
|
||||
/// static CELL: GenericTypeInfoCell = GenericTypeInfoCell::new();
|
||||
/// CELL.get_or_insert::<Self, _>(|| {
|
||||
/// let fields = [UnnamedField::new::<T>(0)];
|
||||
/// let info = TupleStructInfo::new::<Self>(&fields);
|
||||
/// let info = TupleStructInfo::new::<Self>(&fields)
|
||||
/// .with_generics(Generics::from_iter([TypeParamInfo::new::<T>("T")]));
|
||||
/// TypeInfo::TupleStruct(info)
|
||||
/// })
|
||||
/// }
|
||||
|
||||
Loading…
Reference in New Issue
Block a user