-
-
Notifications
You must be signed in to change notification settings - Fork 3.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
stable reflect type name #5805
stable reflect type name #5805
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just some preliminary comments. I'll try to look at this more over the weekend 🙂
Co-authored-by: Gino Valente <[email protected]>
Co-authored-by: Gino Valente <[email protected]>
…into reflect/type_name
Current state of replacing
|
I think one solution for the issues you're coming across is to do what For example, Others might disagree, but I think we can do the same to |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking really good! I think this will be a good starting point for improving the type system even more :D
Co-authored-by: Gino Valente <[email protected]>
I tried to mark this PR as closing #3327, but it didn't seem to take. Could you added a |
we could allow associated constant type names with a fixed length array as storage which would panic on overflow instead of using trait TypeName {
const NAME: &'static str;
}
impl TypeName for u8 {
const NAME: &'static str = "u8";
}
impl<T: TypeName> TypeName for Vec<T> {
const NAME: &'static str = ConstString::<1024>::from_str("Vec<")
.with_str(T::NAME)
.with_str(">")
.as_str();
}
assert_eq!(Vec::<u8>::NAME, "Vec<u8>"); but are const typenames helpful at all? and would limiting their maximum length be particularly hindering? |
Since there is a lot of conflict with the main branch I think the best to do is not solve them, keep this PR open to solve the pending questions and then open a fresh PR to implement the feature. |
to solve the tuple issue, what about seperate impl<T: TypeName, U: TypeName> TypeName for (T, U) {
fn type_name() -> &'static str {
// e.g. "(my_crate::Foo, my_crate::Bar)"
let name = format!("({}, {})", T::type_name(), U::type_name());
Box::leak(Box::new(name)) // but better
}
fn short_type_name() -> &'static str {
// e.g. "(Foo, Bar)"
let name = format!("({}, {})", T::short_type_name(), U::short_type_name());
Box::leak(Box::new(name))
}
}
struct StableName<T, U> {
a: T,
b: (T, U),
}
// generated by derive macro
const PATH: &'static str = module_path!();
const IDENT: &'static str = "StableName";
impl<T: TypeName, U: TypeName> TypeName for StableName<T, U> {
fn type_name() -> &'static str {
<Self as TypePath>::type_path()
}
fn short_type_name() -> &'static str {
<Self as TypePath>::short_type_path()
}
}
// NB: not `T: TypePath`.
impl<T: TypeName, U: TypeName> TypePath for StableName<T, U> {
fn type_path() -> &'static str {
// e.g. "my_crate::StableName::<my_crate::Foo, my_crate::Bar>"
let path = format!("{}::{}::<{}, {}>", PATH, IDENT, T::type_name(), U::type_name());
Box::leak(Box::new(path))
}
fn short_type_path() -> &'static str {
// e.g. "StableName::<Foo, Bar>"
let path = format!("{}::<{}, {}>", IDENT, T::short_type_name(), U::short_type_name());
Box::leak(Box::new(path))
}
// etc.
} or something |
Sorry for taking so long to respond to this. I think this effort is important and worth driving forward.
This will also be a problem for reflected array types, which also don't have a crate or module in their type name. In addition to @soqb's idea to separate the traits (ill call that option 1) I think we have a few more options: Option 2: Allow empty crates, but only for built in types like arrays and tuples. Specify this rule in the trait docs and enforce it in the derive macro (as any derived impl is not-built-in by definition). Option 3: Arbitrarily define a crate for these types (ex: I personally like Option 2 the most. Keeps our traits simple, seems workable generally, and aligns pretty well with std type names. Slightly more complicated rules / constraints, but we do get to make the rules and I can't think of any negative implications.
I think yes. Better to ensure data integrity at the definition level. |
should the methods on the |
Imo probably not. We want to be able to access these methods without needing an actual instance of that field (i.e. when registering the type itself). If we want access to this, we could probably do one of the following:
Any thoughts on these options? Maybe there are other solutions I'm missing here, but one of these would make sense to me. |
Agreed. I think option (1) is pretty good. |
i think there is a fourth option we haven't considered as much as we should. moving the functionality into a returning type A = T;
type B = (T, U);
impl TypePath {
// A: "my_crate::foo::T"
// B: "(my_crate::foo::T, my_crate::foo::bar::U)"
fn type_path(&self) -> &str;
// A: "T"
// B: "(T, U)"
fn short_type_path(&self) -> &str;
// A: Some("T")
// B: None
fn type_name(&self) -> Option<&str>;
// A: Some("my_crate")
// B: None
fn crate_name(&self) -> Option<&str>;
// A: Some("my_crate::foo")
// B: None
fn module_path(&self) -> Option<&str>;
}
// i suppose `TypePath` would have a `Cow<'static, str>` (potentially `Option`) field for each method.
trait Typed {
fn type_info() -> &'static TypeInfo;
fn type_path() -> &'static TypePath;
}
trait Reflect {
fn get_type_info(&self) -> &'static TypeInfo;
fn get_type_path(&self) -> &'static TypePath;
// ...
} |
side note @tguichaoua: i would love to adopt/adapt this PR if you're not willing since its relevant to some of my reflection mad science experiments. |
@soqb np, feel free to adopt this PR 👍 |
I noticed the current implementation can cause potential name conflicts: use bevy::reflect::TypePath;
#[derive(TypePath)]
pub struct Foo;
pub fn bar() {
#[derive(TypePath)]
pub struct Foo;
println!("{}", Foo::type_path());
}
fn main() {
println!("{}", Foo::type_path());
bar();
} Not sure how to resolve this, |
i wonder if we can do something like this if it becomes an issue. it's very hacks and not guaranteed to work across compiler versions but it might be enough. |
Reading the first sentence of the top comment on this PR, I feel like that might be a non-starter?
Making this stable across compiler versions is one of the core goals of this PR, right? |
effectively, yes - this is the absolute last thing i'd want to do. but if and only if we really, really need this conflict-resolution, due to the crawling pace of the |
Closing this out as superseded by #7184 |
# Objective - Introduce a stable alternative to [`std::any::type_name`](https://doc.rust-lang.org/std/any/fn.type_name.html). - Rewrite of #5805 with heavy inspiration in design. - On the path to #5830. - Part of solving #3327. ## Solution - Add a `TypePath` trait for static stable type path/name information. - Add a `TypePath` derive macro. - Add a `impl_type_path` macro for implementing internal and foreign types in `bevy_reflect`. --- ## Changelog - Added `TypePath` trait. - Added `DynamicTypePath` trait and `get_type_path` method to `Reflect`. - Added a `TypePath` derive macro. - Added a `bevy_reflect::impl_type_path` for implementing `TypePath` on internal and foreign types in `bevy_reflect`. - Changed `bevy_reflect::utility::(Non)GenericTypeInfoCell` to `(Non)GenericTypedCell<T>` which allows us to be generic over both `TypeInfo` and `TypePath`. - `TypePath` is now a supertrait of `Asset`, `Material` and `Material2d`. - `impl_reflect_struct` needs a `#[type_path = "..."]` attribute to be specified. - `impl_reflect_value` needs to either specify path starting with a double colon (`::core::option::Option`) or an `in my_crate::foo` declaration. - Added `bevy_reflect_derive::ReflectTypePath`. - Most uses of `Ident` in `bevy_reflect_derive` changed to use `ReflectTypePath`. ## Migration Guide - Implementors of `Asset`, `Material` and `Material2d` now also need to derive `TypePath`. - Manual implementors of `Reflect` will need to implement the new `get_type_path` method. ## Open Questions - [x] ~This PR currently does not migrate any usages of `std::any::type_name` to use `bevy_reflect::TypePath` to ease the review process. Should it?~ Migration will be left to a follow-up PR. - [ ] This PR adds a lot of `#[derive(TypePath)]` and `T: TypePath` to satisfy new bounds, mostly when deriving `TypeUuid`. Should we make `TypePath` a supertrait of `TypeUuid`? [Should we remove `TypeUuid` in favour of `TypePath`?](https://github.com/bevyengine/bevy/pull/5805/files/2afbd855327c4b68e0a6b6f03118f289988441a4#r961067892)
Objective
Drop the usage of
std::any::type_name
to introduce a stable type name for reflected types.Based on #5745 (comment)
closes #3327
Solution
TypePath
trait that can be derived with associated methods to get the type's path.Reflect::type_path
(formelytype_name
) relied onTypePath::type_path
When
TypePath
is automatically derived, the base name is by defaultpath::to::mod::MyType
wherepath::to::mod
is the result ofmodule_path!
andMyType
the ident of the type.If the type is generic then the base name is followed by the type name of its generic types.
e.g.
path::to::mod::MyType<'a, u32, glam::Vec3, 4>
TypeName::name()
ABC
a::b::c::ABC
AB<ABC>
a::b::Foo<a::b::c::ABC>
A<ABC>
a::A<a::b::c::ABC>
A<AB<ABC>>
a::A<a::b::Foo<a::b::c::ABC>>
Open questions
std::any::type_name
s ? (see stable reflect type name #5805 (comment))const
before const generic parameters in type name ? e.g.MyType<42>
vsMyType<const 42>
. (see stable reflect type name #5805 (comment))String
)alloc::string::String
)std::String
)Changelog
TypePath
trait automatically implemented by#[derive(Reflect)]
TypePath
as super trait ofAsset
(because ofHandle<T>
)Migration Guide
TypePath
traitTypePath
traitReflect
on generic type require the generic parameters to implementTypePath
Input
require theTypePath
trait.Additional context
Why not an associated
const
?It's not possible to concat string literal and const value in const context.
There is the
const_format
crate but it's not working with generic (see limitations).