Remove upcasting methods + Cleanup interned label code (#18984)

Hiya!

# Objective

- Remove upcasting methods that are no longer necessary since Rust 1.86.
- Cleanup the interned label code.
 
## Notes
- I didn't try to remove the upcasting methods from `bevy_reflect`, as
there appears to be some complexity related to remote type reflection.
- There are likely some other upcasting methods floating around.

## Testing
I ran the `breakout` example to check that the hashing/eq
implementations of the labels are still correct.

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
This commit is contained in:
Tim 2025-05-26 17:38:12 +02:00 committed by GitHub
parent 93b8f9a303
commit 4924cf5828
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 23 additions and 97 deletions

View File

@ -10,7 +10,7 @@ keywords = ["game", "engine", "gamedev", "graphics", "bevy"]
license = "MIT OR Apache-2.0" license = "MIT OR Apache-2.0"
repository = "https://github.com/bevyengine/bevy" repository = "https://github.com/bevyengine/bevy"
documentation = "https://docs.rs/bevy" documentation = "https://docs.rs/bevy"
rust-version = "1.85.0" rust-version = "1.86.0"
[workspace] [workspace]
resolver = "2" resolver = "2"

View File

@ -205,8 +205,6 @@ pub fn derive_enum_variant_meta(input: TokenStream) -> TokenStream {
pub fn derive_app_label(input: TokenStream) -> TokenStream { pub fn derive_app_label(input: TokenStream) -> TokenStream {
let input = syn::parse_macro_input!(input as syn::DeriveInput); let input = syn::parse_macro_input!(input as syn::DeriveInput);
let mut trait_path = BevyManifest::shared().get_path("bevy_app"); let mut trait_path = BevyManifest::shared().get_path("bevy_app");
let mut dyn_eq_path = trait_path.clone();
trait_path.segments.push(format_ident!("AppLabel").into()); trait_path.segments.push(format_ident!("AppLabel").into());
dyn_eq_path.segments.push(format_ident!("DynEq").into()); derive_label(input, "AppLabel", &trait_path)
derive_label(input, "AppLabel", &trait_path, &dyn_eq_path)
} }

View File

@ -8,7 +8,7 @@ repository = "https://github.com/bevyengine/bevy"
license = "MIT OR Apache-2.0" license = "MIT OR Apache-2.0"
keywords = ["ecs", "game", "bevy"] keywords = ["ecs", "game", "bevy"]
categories = ["game-engines", "data-structures"] categories = ["game-engines", "data-structures"]
rust-version = "1.85.0" rust-version = "1.86.0"
[features] [features]
default = ["std", "bevy_reflect", "async_executor", "backtrace"] default = ["std", "bevy_reflect", "async_executor", "backtrace"]

View File

@ -503,12 +503,10 @@ pub fn derive_schedule_label(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput); let input = parse_macro_input!(input as DeriveInput);
let mut trait_path = bevy_ecs_path(); let mut trait_path = bevy_ecs_path();
trait_path.segments.push(format_ident!("schedule").into()); trait_path.segments.push(format_ident!("schedule").into());
let mut dyn_eq_path = trait_path.clone();
trait_path trait_path
.segments .segments
.push(format_ident!("ScheduleLabel").into()); .push(format_ident!("ScheduleLabel").into());
dyn_eq_path.segments.push(format_ident!("DynEq").into()); derive_label(input, "ScheduleLabel", &trait_path)
derive_label(input, "ScheduleLabel", &trait_path, &dyn_eq_path)
} }
/// Derive macro generating an impl of the trait `SystemSet`. /// Derive macro generating an impl of the trait `SystemSet`.
@ -519,10 +517,8 @@ pub fn derive_system_set(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput); let input = parse_macro_input!(input as DeriveInput);
let mut trait_path = bevy_ecs_path(); let mut trait_path = bevy_ecs_path();
trait_path.segments.push(format_ident!("schedule").into()); trait_path.segments.push(format_ident!("schedule").into());
let mut dyn_eq_path = trait_path.clone();
trait_path.segments.push(format_ident!("SystemSet").into()); trait_path.segments.push(format_ident!("SystemSet").into());
dyn_eq_path.segments.push(format_ident!("DynEq").into()); derive_label(input, "SystemSet", &trait_path)
derive_label(input, "SystemSet", &trait_path, &dyn_eq_path)
} }
pub(crate) fn bevy_ecs_path() -> syn::Path { pub(crate) fn bevy_ecs_path() -> syn::Path {

View File

@ -12,9 +12,6 @@ pub use alloc::boxed::Box;
/// An object safe version of [`Eq`]. This trait is automatically implemented /// An object safe version of [`Eq`]. This trait is automatically implemented
/// for any `'static` type that implements `Eq`. /// for any `'static` type that implements `Eq`.
pub trait DynEq: Any { pub trait DynEq: Any {
/// Casts the type to `dyn Any`.
fn as_any(&self) -> &dyn Any;
/// This method tests for `self` and `other` values to be equal. /// This method tests for `self` and `other` values to be equal.
/// ///
/// Implementers should avoid returning `true` when the underlying types are /// Implementers should avoid returning `true` when the underlying types are
@ -29,12 +26,8 @@ impl<T> DynEq for T
where where
T: Any + Eq, T: Any + Eq,
{ {
fn as_any(&self) -> &dyn Any {
self
}
fn dyn_eq(&self, other: &dyn DynEq) -> bool { fn dyn_eq(&self, other: &dyn DynEq) -> bool {
if let Some(other) = other.as_any().downcast_ref::<T>() { if let Some(other) = (other as &dyn Any).downcast_ref::<T>() {
return self == other; return self == other;
} }
false false
@ -44,9 +37,6 @@ where
/// An object safe version of [`Hash`]. This trait is automatically implemented /// An object safe version of [`Hash`]. This trait is automatically implemented
/// for any `'static` type that implements `Hash`. /// for any `'static` type that implements `Hash`.
pub trait DynHash: DynEq { pub trait DynHash: DynEq {
/// Casts the type to `dyn Any`.
fn as_dyn_eq(&self) -> &dyn DynEq;
/// Feeds this value into the given [`Hasher`]. /// Feeds this value into the given [`Hasher`].
fn dyn_hash(&self, state: &mut dyn Hasher); fn dyn_hash(&self, state: &mut dyn Hasher);
} }
@ -58,10 +48,6 @@ impl<T> DynHash for T
where where
T: DynEq + Hash, T: DynEq + Hash,
{ {
fn as_dyn_eq(&self) -> &dyn DynEq {
self
}
fn dyn_hash(&self, mut state: &mut dyn Hasher) { fn dyn_hash(&self, mut state: &mut dyn Hasher) {
T::hash(self, &mut state); T::hash(self, &mut state);
self.type_id().hash(&mut state); self.type_id().hash(&mut state);
@ -120,7 +106,7 @@ macro_rules! define_label {
) => { ) => {
$(#[$label_attr])* $(#[$label_attr])*
pub trait $label_trait_name: 'static + Send + Sync + ::core::fmt::Debug { pub trait $label_trait_name: Send + Sync + ::core::fmt::Debug + $crate::label::DynEq + $crate::label::DynHash {
$($trait_extra_methods)* $($trait_extra_methods)*
@ -129,12 +115,6 @@ macro_rules! define_label {
///`. ///`.
fn dyn_clone(&self) -> $crate::label::Box<dyn $label_trait_name>; fn dyn_clone(&self) -> $crate::label::Box<dyn $label_trait_name>;
/// Casts this value to a form where it can be compared with other type-erased values.
fn as_dyn_eq(&self) -> &dyn $crate::label::DynEq;
/// Feeds this value into the given [`Hasher`].
fn dyn_hash(&self, state: &mut dyn ::core::hash::Hasher);
/// Returns an [`Interned`] value corresponding to `self`. /// Returns an [`Interned`] value corresponding to `self`.
fn intern(&self) -> $crate::intern::Interned<dyn $label_trait_name> fn intern(&self) -> $crate::intern::Interned<dyn $label_trait_name>
where Self: Sized { where Self: Sized {
@ -151,15 +131,6 @@ macro_rules! define_label {
(**self).dyn_clone() (**self).dyn_clone()
} }
/// Casts this value to a form where it can be compared with other type-erased values.
fn as_dyn_eq(&self) -> &dyn $crate::label::DynEq {
(**self).as_dyn_eq()
}
fn dyn_hash(&self, state: &mut dyn ::core::hash::Hasher) {
(**self).dyn_hash(state);
}
fn intern(&self) -> Self { fn intern(&self) -> Self {
*self *self
} }
@ -167,7 +138,7 @@ macro_rules! define_label {
impl PartialEq for dyn $label_trait_name { impl PartialEq for dyn $label_trait_name {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
self.as_dyn_eq().dyn_eq(other.as_dyn_eq()) self.dyn_eq(other)
} }
} }
@ -188,7 +159,7 @@ macro_rules! define_label {
use ::core::ptr; use ::core::ptr;
// Test that both the type id and pointer address are equivalent. // Test that both the type id and pointer address are equivalent.
self.as_dyn_eq().type_id() == other.as_dyn_eq().type_id() self.type_id() == other.type_id()
&& ptr::addr_eq(ptr::from_ref::<Self>(self), ptr::from_ref::<Self>(other)) && ptr::addr_eq(ptr::from_ref::<Self>(self), ptr::from_ref::<Self>(other))
} }
@ -196,7 +167,7 @@ macro_rules! define_label {
use ::core::{hash::Hash, ptr}; use ::core::{hash::Hash, ptr};
// Hash the type id... // Hash the type id...
self.as_dyn_eq().type_id().hash(state); self.type_id().hash(state);
// ...and the pointer address. // ...and the pointer address.
// Cast to a unit `()` first to discard any pointer metadata. // Cast to a unit `()` first to discard any pointer metadata.

View File

@ -147,15 +147,6 @@ impl<T> SystemSet for SystemTypeSet<T> {
fn dyn_clone(&self) -> Box<dyn SystemSet> { fn dyn_clone(&self) -> Box<dyn SystemSet> {
Box::new(*self) Box::new(*self)
} }
fn as_dyn_eq(&self) -> &dyn DynEq {
self
}
fn dyn_hash(&self, mut state: &mut dyn Hasher) {
TypeId::of::<Self>().hash(&mut state);
self.hash(&mut state);
}
} }
/// A [`SystemSet`] implicitly created when using /// A [`SystemSet`] implicitly created when using
@ -178,15 +169,6 @@ impl SystemSet for AnonymousSet {
fn dyn_clone(&self) -> Box<dyn SystemSet> { fn dyn_clone(&self) -> Box<dyn SystemSet> {
Box::new(*self) Box::new(*self)
} }
fn as_dyn_eq(&self) -> &dyn DynEq {
self
}
fn dyn_hash(&self, mut state: &mut dyn Hasher) {
TypeId::of::<Self>().hash(&mut state);
self.hash(&mut state);
}
} }
/// Types that can be converted into a [`SystemSet`]. /// Types that can be converted into a [`SystemSet`].

View File

@ -2510,10 +2510,7 @@ impl DynSystemParamState {
} }
/// Allows a [`SystemParam::State`] to be used as a trait object for implementing [`DynSystemParam`]. /// Allows a [`SystemParam::State`] to be used as a trait object for implementing [`DynSystemParam`].
trait DynParamState: Sync + Send { trait DynParamState: Sync + Send + Any {
/// Casts the underlying `ParamState<T>` to an `Any` so it can be downcast.
fn as_any_mut(&mut self) -> &mut dyn Any;
/// For the specified [`Archetype`], registers the components accessed by this [`SystemParam`] (if applicable).a /// For the specified [`Archetype`], registers the components accessed by this [`SystemParam`] (if applicable).a
/// ///
/// # Safety /// # Safety
@ -2544,10 +2541,6 @@ trait DynParamState: Sync + Send {
struct ParamState<T: SystemParam>(T::State); struct ParamState<T: SystemParam>(T::State);
impl<T: SystemParam + 'static> DynParamState for ParamState<T> { impl<T: SystemParam + 'static> DynParamState for ParamState<T> {
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
unsafe fn new_archetype(&mut self, archetype: &Archetype, system_meta: &mut SystemMeta) { unsafe fn new_archetype(&mut self, archetype: &Archetype, system_meta: &mut SystemMeta) {
// SAFETY: The caller ensures that `archetype` is from the World the state was initialized from in `init_state`. // SAFETY: The caller ensures that `archetype` is from the World the state was initialized from in `init_state`.
unsafe { T::new_archetype(&mut self.0, archetype, system_meta) }; unsafe { T::new_archetype(&mut self.0, archetype, system_meta) };
@ -2597,18 +2590,11 @@ unsafe impl SystemParam for DynSystemParam<'_, '_> {
change_tick: Tick, change_tick: Tick,
) -> Self::Item<'world, 'state> { ) -> Self::Item<'world, 'state> {
// SAFETY: // SAFETY:
// - `state.0` is a boxed `ParamState<T>`, and its implementation of `as_any_mut` returns `self`. // - `state.0` is a boxed `ParamState<T>`.
// - The state was obtained from `SystemParamBuilder::build()`, which registers all [`World`] accesses used // - The state was obtained from `SystemParamBuilder::build()`, which registers all [`World`] accesses used
// by [`SystemParam::get_param`] with the provided [`system_meta`](SystemMeta). // by [`SystemParam::get_param`] with the provided [`system_meta`](SystemMeta).
// - The caller ensures that the provided world is the same and has the required access. // - The caller ensures that the provided world is the same and has the required access.
unsafe { unsafe { DynSystemParam::new(state.0.as_mut(), world, system_meta.clone(), change_tick) }
DynSystemParam::new(
state.0.as_any_mut(),
world,
system_meta.clone(),
change_tick,
)
}
} }
unsafe fn new_archetype( unsafe fn new_archetype(

View File

@ -58,7 +58,6 @@ pub fn derive_label(
input: syn::DeriveInput, input: syn::DeriveInput,
trait_name: &str, trait_name: &str,
trait_path: &syn::Path, trait_path: &syn::Path,
dyn_eq_path: &syn::Path,
) -> TokenStream { ) -> TokenStream {
if let syn::Data::Union(_) = &input.data { if let syn::Data::Union(_) = &input.data {
let message = format!("Cannot derive {trait_name} for unions."); let message = format!("Cannot derive {trait_name} for unions.");
@ -89,16 +88,6 @@ pub fn derive_label(
fn dyn_clone(&self) -> alloc::boxed::Box<dyn #trait_path> { fn dyn_clone(&self) -> alloc::boxed::Box<dyn #trait_path> {
alloc::boxed::Box::new(::core::clone::Clone::clone(self)) alloc::boxed::Box::new(::core::clone::Clone::clone(self))
} }
fn as_dyn_eq(&self) -> &dyn #dyn_eq_path {
self
}
fn dyn_hash(&self, mut state: &mut dyn ::core::hash::Hasher) {
let ty_id = ::core::any::TypeId::of::<Self>();
::core::hash::Hash::hash(&ty_id, &mut state);
::core::hash::Hash::hash(self, &mut state);
}
} }
}; };
} }

View File

@ -80,12 +80,10 @@ pub fn derive_render_label(input: TokenStream) -> TokenStream {
trait_path trait_path
.segments .segments
.push(format_ident!("render_graph").into()); .push(format_ident!("render_graph").into());
let mut dyn_eq_path = trait_path.clone();
trait_path trait_path
.segments .segments
.push(format_ident!("RenderLabel").into()); .push(format_ident!("RenderLabel").into());
dyn_eq_path.segments.push(format_ident!("DynEq").into()); derive_label(input, "RenderLabel", &trait_path)
derive_label(input, "RenderLabel", &trait_path, &dyn_eq_path)
} }
/// Derive macro generating an impl of the trait `RenderSubGraph`. /// Derive macro generating an impl of the trait `RenderSubGraph`.
@ -98,10 +96,8 @@ pub fn derive_render_sub_graph(input: TokenStream) -> TokenStream {
trait_path trait_path
.segments .segments
.push(format_ident!("render_graph").into()); .push(format_ident!("render_graph").into());
let mut dyn_eq_path = trait_path.clone();
trait_path trait_path
.segments .segments
.push(format_ident!("RenderSubGraph").into()); .push(format_ident!("RenderSubGraph").into());
dyn_eq_path.segments.push(format_ident!("DynEq").into()); derive_label(input, "RenderSubGraph", &trait_path)
derive_label(input, "RenderSubGraph", &trait_path, &dyn_eq_path)
} }

View File

@ -0,0 +1,8 @@
---
title: Interned labels cleanup
pull_requests: [18984]
---
- `DynEq::as_any` has been removed. Use `&value as &dyn Any` instead.
- `DynHash::as_dyn_eq` has been removed. Use `&value as &dyn DynEq` instead.
- `as_dyn_eq` has been removed from 'label' types such as `ScheduleLabel` and `SystemSet`. Call `DynEq::dyn_eq` directly on the label instead.