From b0d76b9f3436c3a5ea985886de580aa4957d3b5f Mon Sep 17 00:00:00 2001 From: Emerson Coskey Date: Mon, 3 Nov 2025 22:03:37 -0800 Subject: [PATCH 01/12] ReborrowQueryData --- crates/bevy_ecs/macros/src/query_data.rs | 34 +++ .../bevy_ecs/src/change_detection/params.rs | 15 ++ crates/bevy_ecs/src/query/fetch.rs | 216 ++++++++++++++++++ .../src/world/entity_access/entity_ref.rs | 6 + .../src/world/entity_access/except.rs | 10 + .../src/world/entity_access/filtered.rs | 9 + crates/bevy_render/src/sync_world.rs | 12 + .../migration-guides/reborrow_traits.md | 8 + 8 files changed, 310 insertions(+) create mode 100644 release-content/migration-guides/reborrow_traits.md diff --git a/crates/bevy_ecs/macros/src/query_data.rs b/crates/bevy_ecs/macros/src/query_data.rs index a91dd193e51c1..635c800d0f804 100644 --- a/crates/bevy_ecs/macros/src/query_data.rs +++ b/crates/bevy_ecs/macros/src/query_data.rs @@ -247,6 +247,8 @@ pub fn derive_query_data_impl(input: TokenStream) -> TokenStream { } } + + fn provide_extra_access( state: &mut Self::State, access: &mut #path::query::Access, @@ -275,6 +277,22 @@ pub fn derive_query_data_impl(input: TokenStream) -> TokenStream { } } + impl #user_impl_generics #path::query::ReborrowQueryData + for #read_only_struct_name #user_ty_generics #user_where_clauses + // Make these HRTBs with an unused lifetime parameter to allow trivial constraints + // See https://github.com/rust-lang/rust/issues/48214 + where #(for<'__a> #field_types: #path::query::ReborrowQueryData,)* { + fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( + item: &'short mut Self::Item<'wlong, 'slong>, + ) -> Self::Item<'short, 'short> { + #read_only_item_struct_name { + #( + #field_members: <#read_only_field_types>::reborrow(&mut item.#field_members), + )* + } + } + } + impl #user_impl_generics #path::query::ReleaseStateQueryData for #read_only_struct_name #user_ty_generics #user_where_clauses // Make these HRTBs with an unused lifetime parameter to allow trivial constraints @@ -346,6 +364,22 @@ pub fn derive_query_data_impl(input: TokenStream) -> TokenStream { } } + impl #user_impl_generics #path::query::ReborrowQueryData + for #struct_name #user_ty_generics #user_where_clauses + // Make these HRTBs with an unused lifetime parameter to allow trivial constraints + // See https://github.com/rust-lang/rust/issues/48214 + where #(for<'__a> #field_types: #path::query::ReborrowQueryData,)* { + fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( + item: &'short mut Self::Item<'wlong, 'slong>, + ) -> Self::Item<'short, 'short> { + #item_struct_name { + #( + #field_members: <#field_types>::reborrow(&mut item.#field_members), + )* + } + } + } + impl #user_impl_generics #path::query::ReleaseStateQueryData for #struct_name #user_ty_generics #user_where_clauses // Make these HRTBs with an unused lifetime parameter to allow trivial constraints diff --git a/crates/bevy_ecs/src/change_detection/params.rs b/crates/bevy_ecs/src/change_detection/params.rs index e66d62864a604..77e7537f001f9 100644 --- a/crates/bevy_ecs/src/change_detection/params.rs +++ b/crates/bevy_ecs/src/change_detection/params.rs @@ -360,6 +360,21 @@ impl<'w, T: ?Sized> Ref<'w, T> { self.ticks.last_run = last_run; self.ticks.this_run = this_run; } + + /// Returns a `Mut<>` with a smaller lifetime. + /// This is useful if you have `&Ref`, but you need a `Ref`. + pub fn reborrow(&self) -> Ref<'_, T> { + Ref { + value: self.value, + ticks: ComponentTicksRef { + added: self.ticks.added, + changed: self.ticks.changed, + changed_by: self.ticks.changed_by, + last_run: self.ticks.last_run, + this_run: self.ticks.this_run, + }, + } + } } impl<'w, 'a, T> IntoIterator for &'a Ref<'w, T> diff --git a/crates/bevy_ecs/src/query/fetch.rs b/crates/bevy_ecs/src/query/fetch.rs index d0b191513e8ed..156511ed34ed2 100644 --- a/crates/bevy_ecs/src/query/fetch.rs +++ b/crates/bevy_ecs/src/query/fetch.rs @@ -352,6 +352,15 @@ pub unsafe trait QueryData: WorldQuery { fn iter_access(state: &Self::State) -> impl Iterator>; } +/// A [`QueryData`] that's able to be reborrowed, converting a reference into +/// an owned struct with a shorter lifetime. +pub trait ReborrowQueryData: QueryData { + /// Returns a `QueryData` item with a smaller lifetime. + fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( + item: &'short mut Self::Item<'wlong, 'slong>, + ) -> Self::Item<'short, 'short>; +} + /// A [`QueryData`] that is read only. /// /// # Safety @@ -464,6 +473,14 @@ unsafe impl QueryData for Entity { } } +impl ReborrowQueryData for Entity { + fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( + item: &'short mut Self::Item<'wlong, 'slong>, + ) -> Self::Item<'short, 'short> { + *item + } +} + /// SAFETY: access is read only unsafe impl ReadOnlyQueryData for Entity {} @@ -561,6 +578,14 @@ unsafe impl QueryData for EntityLocation { } } +impl ReborrowQueryData for EntityLocation { + fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( + item: &'short mut Self::Item<'wlong, 'slong>, + ) -> Self::Item<'short, 'short> { + *item + } +} + /// SAFETY: access is read only unsafe impl ReadOnlyQueryData for EntityLocation {} @@ -741,6 +766,14 @@ unsafe impl QueryData for SpawnDetails { } } +impl ReborrowQueryData for SpawnDetails { + fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( + item: &'short mut Self::Item<'wlong, 'slong>, + ) -> Self::Item<'short, 'short> { + *item + } +} + /// SAFETY: access is read only unsafe impl ReadOnlyQueryData for SpawnDetails {} @@ -864,6 +897,14 @@ unsafe impl<'a> QueryData for EntityRef<'a> { } } +impl<'a> ReborrowQueryData for EntityRef<'a> { + fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( + item: &'short mut Self::Item<'wlong, 'slong>, + ) -> Self::Item<'short, 'short> { + item.reborrow() + } +} + /// SAFETY: access is read only unsafe impl ReadOnlyQueryData for EntityRef<'_> {} @@ -974,6 +1015,14 @@ unsafe impl<'a> QueryData for EntityMut<'a> { } } +impl<'a> ReborrowQueryData for EntityMut<'a> { + fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( + item: &'short mut Self::Item<'wlong, 'slong>, + ) -> Self::Item<'short, 'short> { + item.reborrow() + } +} + impl ReleaseStateQueryData for EntityMut<'_> { fn release_state<'w>(item: Self::Item<'w, '_>) -> Self::Item<'w, 'static> { item @@ -1102,6 +1151,14 @@ unsafe impl<'a, 'b> QueryData for FilteredEntityRef<'a, 'b> { } } +impl<'w, 's> ReborrowQueryData for FilteredEntityRef<'w, 's> { + fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( + item: &'short mut Self::Item<'wlong, 'slong>, + ) -> Self::Item<'short, 'short> { + item.reborrow() + } +} + /// SAFETY: Access is read-only. unsafe impl ReadOnlyQueryData for FilteredEntityRef<'_, '_> {} @@ -1225,6 +1282,14 @@ unsafe impl<'a, 'b> QueryData for FilteredEntityMut<'a, 'b> { } } +impl<'a, 'b> ReborrowQueryData for FilteredEntityMut<'a, 'b> { + fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( + item: &'short mut Self::Item<'wlong, 'slong>, + ) -> Self::Item<'short, 'short> { + item.reborrow() + } +} + impl ArchetypeQueryData for FilteredEntityMut<'_, '_> {} /// SAFETY: `EntityRefExcept` guards access to all components in the bundle `B` @@ -1339,6 +1404,14 @@ where } } +impl<'a, 'b, B: Bundle> ReborrowQueryData for EntityRefExcept<'a, 'b, B> { + fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( + item: &'short mut Self::Item<'wlong, 'slong>, + ) -> Self::Item<'short, 'short> { + item.reborrow() + } +} + /// SAFETY: `EntityRefExcept` enforces read-only access to its contained /// components. unsafe impl ReadOnlyQueryData for EntityRefExcept<'_, '_, B> where B: Bundle {} @@ -1458,6 +1531,14 @@ where } } +impl<'a, 'b, B: Bundle> ReborrowQueryData for EntityMutExcept<'a, 'b, B> { + fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( + item: &'short mut Self::Item<'wlong, 'slong>, + ) -> Self::Item<'short, 'short> { + item.reborrow() + } +} + impl ArchetypeQueryData for EntityMutExcept<'_, '_, B> {} /// SAFETY: @@ -1549,6 +1630,14 @@ unsafe impl QueryData for &Archetype { } } +impl ReborrowQueryData for &Archetype { + fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( + item: &'short mut Self::Item<'wlong, 'slong>, + ) -> Self::Item<'short, 'short> { + *item + } +} + /// SAFETY: access is read only unsafe impl ReadOnlyQueryData for &Archetype {} @@ -1722,6 +1811,14 @@ unsafe impl QueryData for &T { } } +impl ReborrowQueryData for &T { + fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( + item: &'short mut Self::Item<'wlong, 'slong>, + ) -> Self::Item<'short, 'short> { + *item + } +} + /// SAFETY: access is read only unsafe impl ReadOnlyQueryData for &T {} @@ -1939,6 +2036,14 @@ unsafe impl<'__w, T: Component> QueryData for Ref<'__w, T> { } } +impl<'__w, T: Component> ReborrowQueryData for Ref<'__w, T> { + fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( + item: &'short mut Self::Item<'wlong, 'slong>, + ) -> Self::Item<'short, 'short> { + item.reborrow() + } +} + /// SAFETY: access is read only unsafe impl<'__w, T: Component> ReadOnlyQueryData for Ref<'__w, T> {} @@ -2156,6 +2261,14 @@ unsafe impl<'__w, T: Component> QueryData for &'__w mut T } } +impl<'__w, T: Component> ReborrowQueryData for &'__w mut T { + fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( + item: &'short mut Self::Item<'wlong, 'slong>, + ) -> Self::Item<'short, 'short> { + item.reborrow() + } +} + impl> ReleaseStateQueryData for &mut T { fn release_state<'w>(item: Self::Item<'w, '_>) -> Self::Item<'w, 'static> { item @@ -2275,6 +2388,14 @@ unsafe impl<'__w, T: Component> QueryData for Mut<'__w, T> } } +impl<'__w, T: Component> ReborrowQueryData for Mut<'__w, T> { + fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( + item: &'short mut Self::Item<'wlong, 'slong>, + ) -> Self::Item<'short, 'short> { + item.reborrow() + } +} + impl> ReleaseStateQueryData for Mut<'_, T> { fn release_state<'w>(item: Self::Item<'w, '_>) -> Self::Item<'w, 'static> { item @@ -2427,6 +2548,14 @@ unsafe impl QueryData for Option { } } +impl ReborrowQueryData for Option { + fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( + item: &'short mut Self::Item<'wlong, 'slong>, + ) -> Self::Item<'short, 'short> { + item.as_mut().map(::reborrow) + } +} + /// SAFETY: [`OptionFetch`] is read only because `T` is read only unsafe impl ReadOnlyQueryData for Option {} @@ -2607,6 +2736,14 @@ unsafe impl QueryData for Has { } } +impl ReborrowQueryData for Has { + fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( + item: &'short mut Self::Item<'wlong, 'slong>, + ) -> Self::Item<'short, 'short> { + *item + } +} + /// SAFETY: [`Has`] is read only unsafe impl ReadOnlyQueryData for Has {} @@ -2687,6 +2824,31 @@ macro_rules! impl_tuple_query_data { } } + #[expect( + clippy::allow_attributes, + reason = "This is a tuple-related macro; as such the lints below may not always apply." + )] + #[allow( + non_snake_case, + reason = "The names of some variables are provided by the macro's caller, not by us." + )] + #[allow( + unused_variables, + reason = "Zero-length tuples won't use any of the parameters." + )] + #[allow( + clippy::unused_unit, + reason = "Zero-length tuples will generate some function bodies equivalent to `()`; however, this macro is meant for all applicable tuples, and as such it makes no sense to rewrite it just for that case." + )] + $(#[$meta])* + impl<$($name: ReborrowQueryData),*> ReborrowQueryData for ($($name,)*) { + fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( + ($($item,)*): &'short mut Self::Item<'wlong, 'slong>, + ) -> Self::Item<'short, 'short> { + ($(<$name as ReborrowQueryData>::reborrow($item),)*) + } + } + $(#[$meta])* /// SAFETY: each item in the tuple is read only unsafe impl<$($name: ReadOnlyQueryData),*> ReadOnlyQueryData for ($($name,)*) {} @@ -2856,6 +3018,7 @@ macro_rules! impl_anytuple_fetch { )*) } + #[inline(always)] unsafe fn fetch<'w, 's>( _state: &'s Self::State, @@ -2888,6 +3051,30 @@ macro_rules! impl_anytuple_fetch { iter::empty()$(.chain($name::iter_access($name)))* } } + #[expect( + clippy::allow_attributes, + reason = "This is a tuple-related macro; as such the lints below may not always apply." + )] + #[allow( + non_snake_case, + reason = "The names of some variables are provided by the macro's caller, not by us." + )] + #[allow( + unused_variables, + reason = "Zero-length tuples won't use any of the parameters." + )] + #[allow( + clippy::unused_unit, + reason = "Zero-length tuples will generate some function bodies equivalent to `()`; however, this macro is meant for all applicable tuples, and as such it makes no sense to rewrite it just for that case." + )] + $(#[$meta])* + impl<$($name: ReborrowQueryData),*> ReborrowQueryData for AnyOf<($($name,)*)> { + fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( + ($($item,)*): &'short mut Self::Item<'wlong, 'slong>, + ) -> Self::Item<'short, 'short> { + ($( as ReborrowQueryData>::reborrow($item),)*) + } + } $(#[$meta])* /// SAFETY: each item in the tuple is read only @@ -3014,6 +3201,12 @@ unsafe impl QueryData for NopWorldQuery { } } +impl ReborrowQueryData for NopWorldQuery { + fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( + _item: &'short mut Self::Item<'wlong, 'slong>, + ) -> Self::Item<'short, 'short> { + } +} /// SAFETY: `NopFetch` never accesses any data unsafe impl ReadOnlyQueryData for NopWorldQuery {} @@ -3103,6 +3296,12 @@ unsafe impl QueryData for PhantomData { } } +impl ReborrowQueryData for PhantomData { + fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( + _item: &'short mut Self::Item<'wlong, 'slong>, + ) -> Self::Item<'short, 'short> { + } +} /// SAFETY: `PhantomData` never accesses any world data. unsafe impl ReadOnlyQueryData for PhantomData {} @@ -3432,4 +3631,21 @@ mod tests { // we want EntityRef to use the change ticks of the system schedule.run(&mut world); } + + #[test] + fn test_derive_reborrow_query_data() { + struct A; + #[derive(Component)] + struct B; + + #[derive(QueryData)] + #[query_data(mutable)] + pub struct ReborrowData { + a: PhantomData, + b: &'static mut B, + } + + fn assert_is_reborrow_query_data() {} + assert_is_reborrow_query_data::(); + } } diff --git a/crates/bevy_ecs/src/world/entity_access/entity_ref.rs b/crates/bevy_ecs/src/world/entity_access/entity_ref.rs index abe884a1ccf0e..0e2839a58322e 100644 --- a/crates/bevy_ecs/src/world/entity_access/entity_ref.rs +++ b/crates/bevy_ecs/src/world/entity_access/entity_ref.rs @@ -286,6 +286,12 @@ impl<'w> EntityRef<'w> { pub fn spawn_tick(&self) -> Tick { self.cell.spawn_tick() } + + /// Returns an `EntityRef<>` with a smaller lifetime. + /// This is useful if you have `&EntityRef`, but you need an `EntityRef`. + pub fn reborrow(&self) -> EntityRef<'_> { + EntityRef { cell: self.cell } + } } impl<'a> From> for FilteredEntityRef<'a, 'static> { diff --git a/crates/bevy_ecs/src/world/entity_access/except.rs b/crates/bevy_ecs/src/world/entity_access/except.rs index 6a2261dbf2186..7f553ea03ac08 100644 --- a/crates/bevy_ecs/src/world/entity_access/except.rs +++ b/crates/bevy_ecs/src/world/entity_access/except.rs @@ -189,6 +189,16 @@ where }) .flatten() } + + /// Returns an `EntityRefExcept<>` with a smaller lifetime. + /// This is useful if you have `&EntityRefExcept`, but you need an `EntityRefExcept`. + pub fn reborrow(&self) -> EntityRefExcept<'_, '_, B> { + EntityRefExcept { + entity: self.entity, + access: self.access, + phantom: PhantomData, + } + } } impl<'w, 's, B: Bundle> From<&'w EntityRefExcept<'_, 's, B>> for FilteredEntityRef<'w, 's> { diff --git a/crates/bevy_ecs/src/world/entity_access/filtered.rs b/crates/bevy_ecs/src/world/entity_access/filtered.rs index 4459fc262e517..9225da42e33c4 100644 --- a/crates/bevy_ecs/src/world/entity_access/filtered.rs +++ b/crates/bevy_ecs/src/world/entity_access/filtered.rs @@ -208,6 +208,15 @@ impl<'w, 's> FilteredEntityRef<'w, 's> { pub fn spawn_tick(&self) -> Tick { self.entity.spawn_tick() } + + /// Returns an `FilteredEntityRef<>` with a smaller lifetime. + /// This is useful if you have `&FilteredEntityRef`, but you need an `FilteredEntityRef`. + pub fn reborrow(&self) -> FilteredEntityRef<'_, '_> { + FilteredEntityRef { + entity: self.entity, + access: self.access, + } + } } impl<'a> TryFrom> for EntityRef<'a> { diff --git a/crates/bevy_render/src/sync_world.rs b/crates/bevy_render/src/sync_world.rs index 6a1a1fa813a21..0a5f34799e962 100644 --- a/crates/bevy_render/src/sync_world.rs +++ b/crates/bevy_render/src/sync_world.rs @@ -373,6 +373,12 @@ mod render_entities_world_query_impls { item } + fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( + item: &'short mut Self::Item<'wlong, 'slong>, + ) -> Self::Item<'short, 'short> { + *item + } + #[inline(always)] unsafe fn fetch<'w, 's>( state: &'s Self::State, @@ -488,6 +494,12 @@ mod render_entities_world_query_impls { item } + fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( + item: &'short mut Self::Item<'wlong, 'slong>, + ) -> Self::Item<'short, 'short> { + *item + } + #[inline(always)] unsafe fn fetch<'w, 's>( state: &'s Self::State, diff --git a/release-content/migration-guides/reborrow_traits.md b/release-content/migration-guides/reborrow_traits.md new file mode 100644 index 0000000000000..be8a9dae5e0cb --- /dev/null +++ b/release-content/migration-guides/reborrow_traits.md @@ -0,0 +1,8 @@ +--- +title: ECS reborrowing traits +pull_requests: [todo] +--- + +Bevy 0.18 adds a new `ReborrowQueryData` trait to the `QueryData` family, which allows for +shortening the lifetime of a borrowed query item. While not a breaking change, it's recommended +to implement for any custom `QueryData` types From 5c96ec9f742606c25aa19d0c891e583e9fe23f3e Mon Sep 17 00:00:00 2001 From: Emerson Coskey Date: Wed, 3 Dec 2025 19:01:01 -0800 Subject: [PATCH 02/12] fix pr number --- release-content/migration-guides/reborrow_traits.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/release-content/migration-guides/reborrow_traits.md b/release-content/migration-guides/reborrow_traits.md index be8a9dae5e0cb..d9d219f306dfe 100644 --- a/release-content/migration-guides/reborrow_traits.md +++ b/release-content/migration-guides/reborrow_traits.md @@ -1,6 +1,6 @@ --- title: ECS reborrowing traits -pull_requests: [todo] +pull_requests: [22025] --- Bevy 0.18 adds a new `ReborrowQueryData` trait to the `QueryData` family, which allows for From 9a1b266509d03b069dedf861f185f8b7c0936d1c Mon Sep 17 00:00:00 2001 From: Emerson Coskey Date: Wed, 3 Dec 2025 19:18:58 -0800 Subject: [PATCH 03/12] fix impls --- crates/bevy_render/src/sync_world.rs | 30 ++++++++++++++++------------ 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/crates/bevy_render/src/sync_world.rs b/crates/bevy_render/src/sync_world.rs index 0a5f34799e962..88d54bdb2c2e5 100644 --- a/crates/bevy_render/src/sync_world.rs +++ b/crates/bevy_render/src/sync_world.rs @@ -282,7 +282,7 @@ mod render_entities_world_query_impls { component::{ComponentId, Components}, entity::Entity, query::{ - ArchetypeQueryData, FilteredAccess, QueryData, ReadOnlyQueryData, + ArchetypeQueryData, FilteredAccess, QueryData, ReadOnlyQueryData, ReborrowQueryData, ReleaseStateQueryData, WorldQuery, }, storage::{Table, TableRow}, @@ -373,12 +373,6 @@ mod render_entities_world_query_impls { item } - fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( - item: &'short mut Self::Item<'wlong, 'slong>, - ) -> Self::Item<'short, 'short> { - *item - } - #[inline(always)] unsafe fn fetch<'w, 's>( state: &'s Self::State, @@ -402,6 +396,14 @@ mod render_entities_world_query_impls { // SAFETY: the underlying `Entity` is copied, and no mutable access is provided. unsafe impl ReadOnlyQueryData for RenderEntity {} + impl ReborrowQueryData for RenderEntity { + fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( + item: &'short mut Self::Item<'wlong, 'slong>, + ) -> Self::Item<'short, 'short> { + *item + } + } + impl ArchetypeQueryData for RenderEntity {} impl ReleaseStateQueryData for RenderEntity { @@ -494,12 +496,6 @@ mod render_entities_world_query_impls { item } - fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( - item: &'short mut Self::Item<'wlong, 'slong>, - ) -> Self::Item<'short, 'short> { - *item - } - #[inline(always)] unsafe fn fetch<'w, 's>( state: &'s Self::State, @@ -523,6 +519,14 @@ mod render_entities_world_query_impls { // SAFETY: the underlying `Entity` is copied, and no mutable access is provided. unsafe impl ReadOnlyQueryData for MainEntity {} + impl ReborrowQueryData for MainEntity { + fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( + item: &'short mut Self::Item<'wlong, 'slong>, + ) -> Self::Item<'short, 'short> { + *item + } + } + impl ArchetypeQueryData for MainEntity {} impl ReleaseStateQueryData for MainEntity { From 80779a9363c03f5d3272aa3ed5d031f2477cebc3 Mon Sep 17 00:00:00 2001 From: Emerson Coskey Date: Wed, 3 Dec 2025 19:45:59 -0800 Subject: [PATCH 04/12] fix macro --- crates/bevy_ecs/macros/src/query_data.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/crates/bevy_ecs/macros/src/query_data.rs b/crates/bevy_ecs/macros/src/query_data.rs index 635c800d0f804..0e5d6b78e95a5 100644 --- a/crates/bevy_ecs/macros/src/query_data.rs +++ b/crates/bevy_ecs/macros/src/query_data.rs @@ -247,8 +247,6 @@ pub fn derive_query_data_impl(input: TokenStream) -> TokenStream { } } - - fn provide_extra_access( state: &mut Self::State, access: &mut #path::query::Access, @@ -281,7 +279,7 @@ pub fn derive_query_data_impl(input: TokenStream) -> TokenStream { for #read_only_struct_name #user_ty_generics #user_where_clauses // Make these HRTBs with an unused lifetime parameter to allow trivial constraints // See https://github.com/rust-lang/rust/issues/48214 - where #(for<'__a> #field_types: #path::query::ReborrowQueryData,)* { + where #(for<'__a> #field_types: #path::query::QueryData,)* { fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( item: &'short mut Self::Item<'wlong, 'slong>, ) -> Self::Item<'short, 'short> { From 0cf8f204ab8709d3344be12894d365afcff7d5b0 Mon Sep 17 00:00:00 2001 From: Emerson Coskey Date: Mon, 15 Dec 2025 11:50:34 -0800 Subject: [PATCH 05/12] merge traits --- crates/bevy_ecs/macros/src/query_data.rs | 52 ++- .../bevy_ecs/src/change_detection/params.rs | 10 +- crates/bevy_ecs/src/query/fetch.rs | 323 ++++++------------ .../src/world/entity_access/except.rs | 10 - .../src/world/entity_access/filtered.rs | 9 - crates/bevy_render/src/sync_world.rs | 26 +- 6 files changed, 133 insertions(+), 297 deletions(-) diff --git a/crates/bevy_ecs/macros/src/query_data.rs b/crates/bevy_ecs/macros/src/query_data.rs index 0e5d6b78e95a5..ea9ed75a196d7 100644 --- a/crates/bevy_ecs/macros/src/query_data.rs +++ b/crates/bevy_ecs/macros/src/query_data.rs @@ -247,6 +247,16 @@ pub fn derive_query_data_impl(input: TokenStream) -> TokenStream { } } + fn reborrow<'a>( + item: &'a mut Self::Item<'_, '_>, + ) -> Self::Item<'a, 'a> { + #read_only_item_struct_name { + #( + #field_members: <#read_only_field_types as #path::query::QueryData>::reborrow(&mut item.#field_members), + )* + } + } + fn provide_extra_access( state: &mut Self::State, access: &mut #path::query::Access, @@ -275,22 +285,6 @@ pub fn derive_query_data_impl(input: TokenStream) -> TokenStream { } } - impl #user_impl_generics #path::query::ReborrowQueryData - for #read_only_struct_name #user_ty_generics #user_where_clauses - // Make these HRTBs with an unused lifetime parameter to allow trivial constraints - // See https://github.com/rust-lang/rust/issues/48214 - where #(for<'__a> #field_types: #path::query::QueryData,)* { - fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( - item: &'short mut Self::Item<'wlong, 'slong>, - ) -> Self::Item<'short, 'short> { - #read_only_item_struct_name { - #( - #field_members: <#read_only_field_types>::reborrow(&mut item.#field_members), - )* - } - } - } - impl #user_impl_generics #path::query::ReleaseStateQueryData for #read_only_struct_name #user_ty_generics #user_where_clauses // Make these HRTBs with an unused lifetime parameter to allow trivial constraints @@ -334,6 +328,16 @@ pub fn derive_query_data_impl(input: TokenStream) -> TokenStream { } } + fn reborrow<'a>( + item: &'a mut Self::Item<'_, '_>, + ) -> Self::Item<'a, 'a> { + #item_struct_name { + #( + #field_members: <#field_types as #path::query::QueryData>::reborrow(&mut item.#field_members), + )* + } + } + fn provide_extra_access( state: &mut Self::State, access: &mut #path::query::Access, @@ -362,22 +366,6 @@ pub fn derive_query_data_impl(input: TokenStream) -> TokenStream { } } - impl #user_impl_generics #path::query::ReborrowQueryData - for #struct_name #user_ty_generics #user_where_clauses - // Make these HRTBs with an unused lifetime parameter to allow trivial constraints - // See https://github.com/rust-lang/rust/issues/48214 - where #(for<'__a> #field_types: #path::query::ReborrowQueryData,)* { - fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( - item: &'short mut Self::Item<'wlong, 'slong>, - ) -> Self::Item<'short, 'short> { - #item_struct_name { - #( - #field_members: <#field_types>::reborrow(&mut item.#field_members), - )* - } - } - } - impl #user_impl_generics #path::query::ReleaseStateQueryData for #struct_name #user_ty_generics #user_where_clauses // Make these HRTBs with an unused lifetime parameter to allow trivial constraints diff --git a/crates/bevy_ecs/src/change_detection/params.rs b/crates/bevy_ecs/src/change_detection/params.rs index 77e7537f001f9..e2e3cf96809d6 100644 --- a/crates/bevy_ecs/src/change_detection/params.rs +++ b/crates/bevy_ecs/src/change_detection/params.rs @@ -11,7 +11,7 @@ use core::{ /// Used by immutable query parameters (such as [`Ref`] and [`Res`]) /// to store immutable access to the [`Tick`]s of a single component or resource. -#[derive(Clone)] +#[derive(Clone, Copy)] pub(crate) struct ComponentTicksRef<'w> { pub(crate) added: &'w Tick, pub(crate) changed: &'w Tick, @@ -377,6 +377,14 @@ impl<'w, T: ?Sized> Ref<'w, T> { } } +impl<'w, T: ?Sized> Clone for Ref<'w, T> { + fn clone(&self) -> Self { + *self + } +} + +impl<'w, T: ?Sized> Copy for Ref<'w, T> {} + impl<'w, 'a, T> IntoIterator for &'a Ref<'w, T> where &'a T: IntoIterator, diff --git a/crates/bevy_ecs/src/query/fetch.rs b/crates/bevy_ecs/src/query/fetch.rs index 156511ed34ed2..fefbc4a79be31 100644 --- a/crates/bevy_ecs/src/query/fetch.rs +++ b/crates/bevy_ecs/src/query/fetch.rs @@ -309,6 +309,9 @@ pub unsafe trait QueryData: WorldQuery { item: Self::Item<'wlong, 's>, ) -> Self::Item<'wshort, 's>; + /// Returns a `QueryData` item with a smaller lifetime. + fn reborrow<'a>(item: &'a mut Self::Item<'_, '_>) -> Self::Item<'a, 'a>; + /// Offers additional access above what we requested in `update_component_access`. /// Implementations may add additional access that is a subset of `available_access` /// and does not conflict with anything in `access`, @@ -352,15 +355,6 @@ pub unsafe trait QueryData: WorldQuery { fn iter_access(state: &Self::State) -> impl Iterator>; } -/// A [`QueryData`] that's able to be reborrowed, converting a reference into -/// an owned struct with a shorter lifetime. -pub trait ReborrowQueryData: QueryData { - /// Returns a `QueryData` item with a smaller lifetime. - fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( - item: &'short mut Self::Item<'wlong, 'slong>, - ) -> Self::Item<'short, 'short>; -} - /// A [`QueryData`] that is read only. /// /// # Safety @@ -458,6 +452,10 @@ unsafe impl QueryData for Entity { item } + fn reborrow<'a>(item: &'a mut Self::Item<'_, '_>) -> Self::Item<'a, 'a> { + *item + } + #[inline(always)] unsafe fn fetch<'w, 's>( _state: &'s Self::State, @@ -473,14 +471,6 @@ unsafe impl QueryData for Entity { } } -impl ReborrowQueryData for Entity { - fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( - item: &'short mut Self::Item<'wlong, 'slong>, - ) -> Self::Item<'short, 'short> { - *item - } -} - /// SAFETY: access is read only unsafe impl ReadOnlyQueryData for Entity {} @@ -562,6 +552,10 @@ unsafe impl QueryData for EntityLocation { item } + fn reborrow<'a>(item: &'a mut Self::Item<'_, '_>) -> Self::Item<'a, 'a> { + *item + } + #[inline(always)] unsafe fn fetch<'w, 's>( _state: &'s Self::State, @@ -578,14 +572,6 @@ unsafe impl QueryData for EntityLocation { } } -impl ReborrowQueryData for EntityLocation { - fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( - item: &'short mut Self::Item<'wlong, 'slong>, - ) -> Self::Item<'short, 'short> { - *item - } -} - /// SAFETY: access is read only unsafe impl ReadOnlyQueryData for EntityLocation {} @@ -740,6 +726,10 @@ unsafe impl QueryData for SpawnDetails { item } + fn reborrow<'a>(item: &'a mut Self::Item<'_, '_>) -> Self::Item<'a, 'a> { + *item + } + #[inline(always)] unsafe fn fetch<'w, 's>( _state: &'s Self::State, @@ -766,14 +756,6 @@ unsafe impl QueryData for SpawnDetails { } } -impl ReborrowQueryData for SpawnDetails { - fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( - item: &'short mut Self::Item<'wlong, 'slong>, - ) -> Self::Item<'short, 'short> { - *item - } -} - /// SAFETY: access is read only unsafe impl ReadOnlyQueryData for SpawnDetails {} @@ -799,7 +781,7 @@ pub struct EntityFetch<'w> { /// `fetch` accesses all components in a readonly way. /// This is sound because `update_component_access` sets read access for all components and panic when appropriate. /// Filters are unchanged. -unsafe impl<'a> WorldQuery for EntityRef<'a> { +unsafe impl WorldQuery for EntityRef<'_> { type Fetch<'w> = EntityFetch<'w>; type State = (); @@ -862,7 +844,7 @@ unsafe impl<'a> WorldQuery for EntityRef<'a> { } /// SAFETY: `Self` is the same as `Self::ReadOnly` -unsafe impl<'a> QueryData for EntityRef<'a> { +unsafe impl QueryData for EntityRef<'_> { const IS_READ_ONLY: bool = true; const IS_ARCHETYPAL: bool = true; type ReadOnly = Self; @@ -874,6 +856,10 @@ unsafe impl<'a> QueryData for EntityRef<'a> { item } + fn reborrow<'a>(item: &'a mut Self::Item<'_, '_>) -> Self::Item<'a, 'a> { + item.reborrow() + } + #[inline(always)] unsafe fn fetch<'w, 's>( _state: &'s Self::State, @@ -897,14 +883,6 @@ unsafe impl<'a> QueryData for EntityRef<'a> { } } -impl<'a> ReborrowQueryData for EntityRef<'a> { - fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( - item: &'short mut Self::Item<'wlong, 'slong>, - ) -> Self::Item<'short, 'short> { - item.reborrow() - } -} - /// SAFETY: access is read only unsafe impl ReadOnlyQueryData for EntityRef<'_> {} @@ -917,7 +895,7 @@ impl ReleaseStateQueryData for EntityRef<'_> { impl ArchetypeQueryData for EntityRef<'_> {} /// SAFETY: The accesses of `Self::ReadOnly` are a subset of the accesses of `Self` -unsafe impl<'a> WorldQuery for EntityMut<'a> { +unsafe impl WorldQuery for EntityMut<'_> { type Fetch<'w> = EntityFetch<'w>; type State = (); @@ -980,10 +958,10 @@ unsafe impl<'a> WorldQuery for EntityMut<'a> { } /// SAFETY: access of `EntityRef` is a subset of `EntityMut` -unsafe impl<'a> QueryData for EntityMut<'a> { +unsafe impl<'__w> QueryData for EntityMut<'__w> { const IS_READ_ONLY: bool = false; const IS_ARCHETYPAL: bool = true; - type ReadOnly = EntityRef<'a>; + type ReadOnly = EntityRef<'__w>; type Item<'w, 's> = EntityMut<'w>; fn shrink<'wlong: 'wshort, 'wshort, 's>( @@ -992,6 +970,10 @@ unsafe impl<'a> QueryData for EntityMut<'a> { item } + fn reborrow<'a>(item: &'a mut Self::Item<'_, '_>) -> Self::Item<'a, 'a> { + item.reborrow() + } + #[inline(always)] unsafe fn fetch<'w, 's>( _state: &'s Self::State, @@ -1015,14 +997,6 @@ unsafe impl<'a> QueryData for EntityMut<'a> { } } -impl<'a> ReborrowQueryData for EntityMut<'a> { - fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( - item: &'short mut Self::Item<'wlong, 'slong>, - ) -> Self::Item<'short, 'short> { - item.reborrow() - } -} - impl ReleaseStateQueryData for EntityMut<'_> { fn release_state<'w>(item: Self::Item<'w, '_>) -> Self::Item<'w, 'static> { item @@ -1097,7 +1071,7 @@ unsafe impl WorldQuery for FilteredEntityRef<'_, '_> { } /// SAFETY: `Self` is the same as `Self::ReadOnly` -unsafe impl<'a, 'b> QueryData for FilteredEntityRef<'a, 'b> { +unsafe impl QueryData for FilteredEntityRef<'_, '_> { const IS_READ_ONLY: bool = true; const IS_ARCHETYPAL: bool = true; type ReadOnly = Self; @@ -1109,6 +1083,10 @@ unsafe impl<'a, 'b> QueryData for FilteredEntityRef<'a, 'b> { item } + fn reborrow<'a>(item: &'a mut Self::Item<'_, '_>) -> Self::Item<'a, 'a> { + *item + } + #[inline] fn provide_extra_access( state: &mut Self::State, @@ -1151,14 +1129,6 @@ unsafe impl<'a, 'b> QueryData for FilteredEntityRef<'a, 'b> { } } -impl<'w, 's> ReborrowQueryData for FilteredEntityRef<'w, 's> { - fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( - item: &'short mut Self::Item<'wlong, 'slong>, - ) -> Self::Item<'short, 'short> { - item.reborrow() - } -} - /// SAFETY: Access is read-only. unsafe impl ReadOnlyQueryData for FilteredEntityRef<'_, '_> {} @@ -1230,10 +1200,10 @@ unsafe impl WorldQuery for FilteredEntityMut<'_, '_> { } /// SAFETY: access of `FilteredEntityRef` is a subset of `FilteredEntityMut` -unsafe impl<'a, 'b> QueryData for FilteredEntityMut<'a, 'b> { +unsafe impl<'__w, '__s> QueryData for FilteredEntityMut<'__w, '__s> { const IS_READ_ONLY: bool = false; const IS_ARCHETYPAL: bool = true; - type ReadOnly = FilteredEntityRef<'a, 'b>; + type ReadOnly = FilteredEntityRef<'__w, '__s>; type Item<'w, 's> = FilteredEntityMut<'w, 's>; fn shrink<'wlong: 'wshort, 'wshort, 's>( @@ -1242,6 +1212,10 @@ unsafe impl<'a, 'b> QueryData for FilteredEntityMut<'a, 'b> { item } + fn reborrow<'a>(item: &'a mut Self::Item<'_, '_>) -> Self::Item<'a, 'a> { + item.reborrow() + } + #[inline] fn provide_extra_access( state: &mut Self::State, @@ -1282,20 +1256,12 @@ unsafe impl<'a, 'b> QueryData for FilteredEntityMut<'a, 'b> { } } -impl<'a, 'b> ReborrowQueryData for FilteredEntityMut<'a, 'b> { - fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( - item: &'short mut Self::Item<'wlong, 'slong>, - ) -> Self::Item<'short, 'short> { - item.reborrow() - } -} - impl ArchetypeQueryData for FilteredEntityMut<'_, '_> {} /// SAFETY: `EntityRefExcept` guards access to all components in the bundle `B` /// and populates `Access` values so that queries that conflict with this access /// are rejected. -unsafe impl<'a, 'b, B> WorldQuery for EntityRefExcept<'a, 'b, B> +unsafe impl WorldQuery for EntityRefExcept<'_, '_, B> where B: Bundle, { @@ -1371,7 +1337,7 @@ where } /// SAFETY: `Self` is the same as `Self::ReadOnly`. -unsafe impl<'a, 'b, B> QueryData for EntityRefExcept<'a, 'b, B> +unsafe impl QueryData for EntityRefExcept<'_, '_, B> where B: Bundle, { @@ -1386,6 +1352,10 @@ where item } + fn reborrow<'a>(item: &'a mut Self::Item<'_, '_>) -> Self::Item<'a, 'a> { + *item + } + unsafe fn fetch<'w, 's>( access: &'s Self::State, fetch: &mut Self::Fetch<'w>, @@ -1404,14 +1374,6 @@ where } } -impl<'a, 'b, B: Bundle> ReborrowQueryData for EntityRefExcept<'a, 'b, B> { - fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( - item: &'short mut Self::Item<'wlong, 'slong>, - ) -> Self::Item<'short, 'short> { - item.reborrow() - } -} - /// SAFETY: `EntityRefExcept` enforces read-only access to its contained /// components. unsafe impl ReadOnlyQueryData for EntityRefExcept<'_, '_, B> where B: Bundle {} @@ -1421,7 +1383,7 @@ impl ArchetypeQueryData for EntityRefExcept<'_, '_, B> {} /// SAFETY: `EntityMutExcept` guards access to all components in the bundle `B` /// and populates `Access` values so that queries that conflict with this access /// are rejected. -unsafe impl<'a, 'b, B> WorldQuery for EntityMutExcept<'a, 'b, B> +unsafe impl WorldQuery for EntityMutExcept<'_, '_, B> where B: Bundle, { @@ -1498,13 +1460,13 @@ where /// SAFETY: All accesses that `EntityRefExcept` provides are also accesses that /// `EntityMutExcept` provides. -unsafe impl<'a, 'b, B> QueryData for EntityMutExcept<'a, 'b, B> +unsafe impl<'__w, '__s, B> QueryData for EntityMutExcept<'__w, '__s, B> where B: Bundle, { const IS_READ_ONLY: bool = false; const IS_ARCHETYPAL: bool = true; - type ReadOnly = EntityRefExcept<'a, 'b, B>; + type ReadOnly = EntityRefExcept<'__w, '__s, B>; type Item<'w, 's> = EntityMutExcept<'w, 's, B>; fn shrink<'wlong: 'wshort, 'wshort, 's>( @@ -1513,6 +1475,10 @@ where item } + fn reborrow<'a>(item: &'a mut Self::Item<'_, '_>) -> Self::Item<'a, 'a> { + item.reborrow() + } + unsafe fn fetch<'w, 's>( access: &'s Self::State, fetch: &mut Self::Fetch<'w>, @@ -1531,14 +1497,6 @@ where } } -impl<'a, 'b, B: Bundle> ReborrowQueryData for EntityMutExcept<'a, 'b, B> { - fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( - item: &'short mut Self::Item<'wlong, 'slong>, - ) -> Self::Item<'short, 'short> { - item.reborrow() - } -} - impl ArchetypeQueryData for EntityMutExcept<'_, '_, B> {} /// SAFETY: @@ -1611,6 +1569,10 @@ unsafe impl QueryData for &Archetype { item } + fn reborrow<'a>(item: &'a mut Self::Item<'_, '_>) -> Self::Item<'a, 'a> { + *item + } + #[inline(always)] unsafe fn fetch<'w, 's>( _state: &'s Self::State, @@ -1630,14 +1592,6 @@ unsafe impl QueryData for &Archetype { } } -impl ReborrowQueryData for &Archetype { - fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( - item: &'short mut Self::Item<'wlong, 'slong>, - ) -> Self::Item<'short, 'short> { - *item - } -} - /// SAFETY: access is read only unsafe impl ReadOnlyQueryData for &Archetype {} @@ -1778,6 +1732,10 @@ unsafe impl QueryData for &T { item } + fn reborrow<'a>(item: &'a mut Self::Item<'_, '_>) -> Self::Item<'a, 'a> { + *item + } + #[inline(always)] unsafe fn fetch<'w, 's>( _state: &'s Self::State, @@ -1811,14 +1769,6 @@ unsafe impl QueryData for &T { } } -impl ReborrowQueryData for &T { - fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( - item: &'short mut Self::Item<'wlong, 'slong>, - ) -> Self::Item<'short, 'short> { - *item - } -} - /// SAFETY: access is read only unsafe impl ReadOnlyQueryData for &T {} @@ -1976,6 +1926,10 @@ unsafe impl<'__w, T: Component> QueryData for Ref<'__w, T> { item } + fn reborrow<'a>(item: &'a mut Self::Item<'_, '_>) -> Self::Item<'a, 'a> { + *item + } + #[inline(always)] unsafe fn fetch<'w, 's>( _state: &'s Self::State, @@ -2036,14 +1990,6 @@ unsafe impl<'__w, T: Component> QueryData for Ref<'__w, T> { } } -impl<'__w, T: Component> ReborrowQueryData for Ref<'__w, T> { - fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( - item: &'short mut Self::Item<'wlong, 'slong>, - ) -> Self::Item<'short, 'short> { - item.reborrow() - } -} - /// SAFETY: access is read only unsafe impl<'__w, T: Component> ReadOnlyQueryData for Ref<'__w, T> {} @@ -2201,6 +2147,10 @@ unsafe impl<'__w, T: Component> QueryData for &'__w mut T item } + fn reborrow<'a>(item: &'a mut Self::Item<'_, '_>) -> Self::Item<'a, 'a> { + item.reborrow() + } + #[inline(always)] unsafe fn fetch<'w, 's>( _state: &'s Self::State, @@ -2261,14 +2211,6 @@ unsafe impl<'__w, T: Component> QueryData for &'__w mut T } } -impl<'__w, T: Component> ReborrowQueryData for &'__w mut T { - fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( - item: &'short mut Self::Item<'wlong, 'slong>, - ) -> Self::Item<'short, 'short> { - item.reborrow() - } -} - impl> ReleaseStateQueryData for &mut T { fn release_state<'w>(item: Self::Item<'w, '_>) -> Self::Item<'w, 'static> { item @@ -2370,6 +2312,10 @@ unsafe impl<'__w, T: Component> QueryData for Mut<'__w, T> <&mut T as QueryData>::shrink(item) } + fn reborrow<'a>(item: &'a mut Self::Item<'_, '_>) -> Self::Item<'a, 'a> { + item.reborrow() + } + #[inline(always)] // Forwarded to `&mut T` unsafe fn fetch<'w, 's>( @@ -2388,14 +2334,6 @@ unsafe impl<'__w, T: Component> QueryData for Mut<'__w, T> } } -impl<'__w, T: Component> ReborrowQueryData for Mut<'__w, T> { - fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( - item: &'short mut Self::Item<'wlong, 'slong>, - ) -> Self::Item<'short, 'short> { - item.reborrow() - } -} - impl> ReleaseStateQueryData for Mut<'_, T> { fn release_state<'w>(item: Self::Item<'w, '_>) -> Self::Item<'w, 'static> { item @@ -2527,6 +2465,10 @@ unsafe impl QueryData for Option { item.map(T::shrink) } + fn reborrow<'a>(item: &'a mut Self::Item<'_, '_>) -> Self::Item<'a, 'a> { + item.as_mut().map(T::reborrow) + } + #[inline(always)] unsafe fn fetch<'w, 's>( state: &'s Self::State, @@ -2548,14 +2490,6 @@ unsafe impl QueryData for Option { } } -impl ReborrowQueryData for Option { - fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( - item: &'short mut Self::Item<'wlong, 'slong>, - ) -> Self::Item<'short, 'short> { - item.as_mut().map(::reborrow) - } -} - /// SAFETY: [`OptionFetch`] is read only because `T` is read only unsafe impl ReadOnlyQueryData for Option {} @@ -2721,6 +2655,10 @@ unsafe impl QueryData for Has { item } + fn reborrow<'a>(item: &'a mut Self::Item<'_, '_>) -> Self::Item<'a, 'a> { + *item + } + #[inline(always)] unsafe fn fetch<'w, 's>( _state: &'s Self::State, @@ -2736,14 +2674,6 @@ unsafe impl QueryData for Has { } } -impl ReborrowQueryData for Has { - fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( - item: &'short mut Self::Item<'wlong, 'slong>, - ) -> Self::Item<'short, 'short> { - *item - } -} - /// SAFETY: [`Has`] is read only unsafe impl ReadOnlyQueryData for Has {} @@ -2795,6 +2725,12 @@ macro_rules! impl_tuple_query_data { )*) } + fn reborrow<'a>( + ($($item,)*): &'a mut Self::Item<'_, '_>, + ) -> Self::Item<'a, 'a> { + ($($name::reborrow($item),)*) + } + #[inline] fn provide_extra_access( state: &mut Self::State, @@ -2824,31 +2760,6 @@ macro_rules! impl_tuple_query_data { } } - #[expect( - clippy::allow_attributes, - reason = "This is a tuple-related macro; as such the lints below may not always apply." - )] - #[allow( - non_snake_case, - reason = "The names of some variables are provided by the macro's caller, not by us." - )] - #[allow( - unused_variables, - reason = "Zero-length tuples won't use any of the parameters." - )] - #[allow( - clippy::unused_unit, - reason = "Zero-length tuples will generate some function bodies equivalent to `()`; however, this macro is meant for all applicable tuples, and as such it makes no sense to rewrite it just for that case." - )] - $(#[$meta])* - impl<$($name: ReborrowQueryData),*> ReborrowQueryData for ($($name,)*) { - fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( - ($($item,)*): &'short mut Self::Item<'wlong, 'slong>, - ) -> Self::Item<'short, 'short> { - ($(<$name as ReborrowQueryData>::reborrow($item),)*) - } - } - $(#[$meta])* /// SAFETY: each item in the tuple is read only unsafe impl<$($name: ReadOnlyQueryData),*> ReadOnlyQueryData for ($($name,)*) {} @@ -3018,6 +2929,11 @@ macro_rules! impl_anytuple_fetch { )*) } + fn reborrow<'a>( + ($($item,)*): &'a mut Self::Item<'_, '_>, + ) -> Self::Item<'a, 'a> { + ($( as QueryData>::reborrow($item),)*) + } #[inline(always)] unsafe fn fetch<'w, 's>( @@ -3051,30 +2967,6 @@ macro_rules! impl_anytuple_fetch { iter::empty()$(.chain($name::iter_access($name)))* } } - #[expect( - clippy::allow_attributes, - reason = "This is a tuple-related macro; as such the lints below may not always apply." - )] - #[allow( - non_snake_case, - reason = "The names of some variables are provided by the macro's caller, not by us." - )] - #[allow( - unused_variables, - reason = "Zero-length tuples won't use any of the parameters." - )] - #[allow( - clippy::unused_unit, - reason = "Zero-length tuples will generate some function bodies equivalent to `()`; however, this macro is meant for all applicable tuples, and as such it makes no sense to rewrite it just for that case." - )] - $(#[$meta])* - impl<$($name: ReborrowQueryData),*> ReborrowQueryData for AnyOf<($($name,)*)> { - fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( - ($($item,)*): &'short mut Self::Item<'wlong, 'slong>, - ) -> Self::Item<'short, 'short> { - ($( as ReborrowQueryData>::reborrow($item),)*) - } - } $(#[$meta])* /// SAFETY: each item in the tuple is read only @@ -3186,6 +3078,8 @@ unsafe impl QueryData for NopWorldQuery { ) -> Self::Item<'wshort, 's> { } + fn reborrow<'a>(_item: &'a mut Self::Item<'_, '_>) -> Self::Item<'a, 'a> {} + #[inline(always)] unsafe fn fetch<'w, 's>( _state: &'s Self::State, @@ -3201,12 +3095,6 @@ unsafe impl QueryData for NopWorldQuery { } } -impl ReborrowQueryData for NopWorldQuery { - fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( - _item: &'short mut Self::Item<'wlong, 'slong>, - ) -> Self::Item<'short, 'short> { - } -} /// SAFETY: `NopFetch` never accesses any data unsafe impl ReadOnlyQueryData for NopWorldQuery {} @@ -3282,6 +3170,8 @@ unsafe impl QueryData for PhantomData { ) -> Self::Item<'wshort, 's> { } + fn reborrow<'a>(_item: &'a mut Self::Item<'_, '_>) -> Self::Item<'a, 'a> {} + unsafe fn fetch<'w, 's>( _state: &'s Self::State, _fetch: &mut Self::Fetch<'w>, @@ -3296,12 +3186,6 @@ unsafe impl QueryData for PhantomData { } } -impl ReborrowQueryData for PhantomData { - fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( - _item: &'short mut Self::Item<'wlong, 'slong>, - ) -> Self::Item<'short, 'short> { - } -} /// SAFETY: `PhantomData` never accesses any world data. unsafe impl ReadOnlyQueryData for PhantomData {} @@ -3631,21 +3515,4 @@ mod tests { // we want EntityRef to use the change ticks of the system schedule.run(&mut world); } - - #[test] - fn test_derive_reborrow_query_data() { - struct A; - #[derive(Component)] - struct B; - - #[derive(QueryData)] - #[query_data(mutable)] - pub struct ReborrowData { - a: PhantomData, - b: &'static mut B, - } - - fn assert_is_reborrow_query_data() {} - assert_is_reborrow_query_data::(); - } } diff --git a/crates/bevy_ecs/src/world/entity_access/except.rs b/crates/bevy_ecs/src/world/entity_access/except.rs index 7f553ea03ac08..6a2261dbf2186 100644 --- a/crates/bevy_ecs/src/world/entity_access/except.rs +++ b/crates/bevy_ecs/src/world/entity_access/except.rs @@ -189,16 +189,6 @@ where }) .flatten() } - - /// Returns an `EntityRefExcept<>` with a smaller lifetime. - /// This is useful if you have `&EntityRefExcept`, but you need an `EntityRefExcept`. - pub fn reborrow(&self) -> EntityRefExcept<'_, '_, B> { - EntityRefExcept { - entity: self.entity, - access: self.access, - phantom: PhantomData, - } - } } impl<'w, 's, B: Bundle> From<&'w EntityRefExcept<'_, 's, B>> for FilteredEntityRef<'w, 's> { diff --git a/crates/bevy_ecs/src/world/entity_access/filtered.rs b/crates/bevy_ecs/src/world/entity_access/filtered.rs index 9225da42e33c4..4459fc262e517 100644 --- a/crates/bevy_ecs/src/world/entity_access/filtered.rs +++ b/crates/bevy_ecs/src/world/entity_access/filtered.rs @@ -208,15 +208,6 @@ impl<'w, 's> FilteredEntityRef<'w, 's> { pub fn spawn_tick(&self) -> Tick { self.entity.spawn_tick() } - - /// Returns an `FilteredEntityRef<>` with a smaller lifetime. - /// This is useful if you have `&FilteredEntityRef`, but you need an `FilteredEntityRef`. - pub fn reborrow(&self) -> FilteredEntityRef<'_, '_> { - FilteredEntityRef { - entity: self.entity, - access: self.access, - } - } } impl<'a> TryFrom> for EntityRef<'a> { diff --git a/crates/bevy_render/src/sync_world.rs b/crates/bevy_render/src/sync_world.rs index 88d54bdb2c2e5..1de21d71944ec 100644 --- a/crates/bevy_render/src/sync_world.rs +++ b/crates/bevy_render/src/sync_world.rs @@ -282,7 +282,7 @@ mod render_entities_world_query_impls { component::{ComponentId, Components}, entity::Entity, query::{ - ArchetypeQueryData, FilteredAccess, QueryData, ReadOnlyQueryData, ReborrowQueryData, + ArchetypeQueryData, FilteredAccess, QueryData, ReadOnlyQueryData, ReleaseStateQueryData, WorldQuery, }, storage::{Table, TableRow}, @@ -373,6 +373,10 @@ mod render_entities_world_query_impls { item } + fn reborrow<'a>(item: &'a mut Self::Item<'_, '_>) -> Self::Item<'a, 'a> { + *item + } + #[inline(always)] unsafe fn fetch<'w, 's>( state: &'s Self::State, @@ -396,14 +400,6 @@ mod render_entities_world_query_impls { // SAFETY: the underlying `Entity` is copied, and no mutable access is provided. unsafe impl ReadOnlyQueryData for RenderEntity {} - impl ReborrowQueryData for RenderEntity { - fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( - item: &'short mut Self::Item<'wlong, 'slong>, - ) -> Self::Item<'short, 'short> { - *item - } - } - impl ArchetypeQueryData for RenderEntity {} impl ReleaseStateQueryData for RenderEntity { @@ -496,6 +492,10 @@ mod render_entities_world_query_impls { item } + fn reborrow<'a>(item: &'a mut Self::Item<'_, '_>) -> Self::Item<'a, 'a> { + *item + } + #[inline(always)] unsafe fn fetch<'w, 's>( state: &'s Self::State, @@ -519,14 +519,6 @@ mod render_entities_world_query_impls { // SAFETY: the underlying `Entity` is copied, and no mutable access is provided. unsafe impl ReadOnlyQueryData for MainEntity {} - impl ReborrowQueryData for MainEntity { - fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( - item: &'short mut Self::Item<'wlong, 'slong>, - ) -> Self::Item<'short, 'short> { - *item - } - } - impl ArchetypeQueryData for MainEntity {} impl ReleaseStateQueryData for MainEntity { From d7163b9501be76c8b218f782b9917508b86de21c Mon Sep 17 00:00:00 2001 From: Emerson Coskey Date: Mon, 15 Dec 2025 12:35:05 -0800 Subject: [PATCH 06/12] cleanup --- crates/bevy_ecs/src/change_detection/params.rs | 15 --------------- crates/bevy_ecs/src/query/fetch.rs | 2 ++ .../src/world/entity_access/entity_ref.rs | 6 ------ .../migration-guides/reborrow_traits.md | 12 +++++++++--- 4 files changed, 11 insertions(+), 24 deletions(-) diff --git a/crates/bevy_ecs/src/change_detection/params.rs b/crates/bevy_ecs/src/change_detection/params.rs index e2e3cf96809d6..6aa5a3fde02ce 100644 --- a/crates/bevy_ecs/src/change_detection/params.rs +++ b/crates/bevy_ecs/src/change_detection/params.rs @@ -360,21 +360,6 @@ impl<'w, T: ?Sized> Ref<'w, T> { self.ticks.last_run = last_run; self.ticks.this_run = this_run; } - - /// Returns a `Mut<>` with a smaller lifetime. - /// This is useful if you have `&Ref`, but you need a `Ref`. - pub fn reborrow(&self) -> Ref<'_, T> { - Ref { - value: self.value, - ticks: ComponentTicksRef { - added: self.ticks.added, - changed: self.ticks.changed, - changed_by: self.ticks.changed_by, - last_run: self.ticks.last_run, - this_run: self.ticks.this_run, - }, - } - } } impl<'w, T: ?Sized> Clone for Ref<'w, T> { diff --git a/crates/bevy_ecs/src/query/fetch.rs b/crates/bevy_ecs/src/query/fetch.rs index fefbc4a79be31..0fd8832741b76 100644 --- a/crates/bevy_ecs/src/query/fetch.rs +++ b/crates/bevy_ecs/src/query/fetch.rs @@ -3383,6 +3383,8 @@ mod tests { ) -> Self::Item<'wshort, 's> { } + fn reborrow<'a>(_item: &'a mut Self::Item<'_, '_>) -> Self::Item<'a, 'a> {} + #[inline(always)] unsafe fn fetch<'w, 's>( _state: &'s Self::State, diff --git a/crates/bevy_ecs/src/world/entity_access/entity_ref.rs b/crates/bevy_ecs/src/world/entity_access/entity_ref.rs index 0e2839a58322e..abe884a1ccf0e 100644 --- a/crates/bevy_ecs/src/world/entity_access/entity_ref.rs +++ b/crates/bevy_ecs/src/world/entity_access/entity_ref.rs @@ -286,12 +286,6 @@ impl<'w> EntityRef<'w> { pub fn spawn_tick(&self) -> Tick { self.cell.spawn_tick() } - - /// Returns an `EntityRef<>` with a smaller lifetime. - /// This is useful if you have `&EntityRef`, but you need an `EntityRef`. - pub fn reborrow(&self) -> EntityRef<'_> { - EntityRef { cell: self.cell } - } } impl<'a> From> for FilteredEntityRef<'a, 'static> { diff --git a/release-content/migration-guides/reborrow_traits.md b/release-content/migration-guides/reborrow_traits.md index d9d219f306dfe..ccca63c283d24 100644 --- a/release-content/migration-guides/reborrow_traits.md +++ b/release-content/migration-guides/reborrow_traits.md @@ -3,6 +3,12 @@ title: ECS reborrowing traits pull_requests: [22025] --- -Bevy 0.18 adds a new `ReborrowQueryData` trait to the `QueryData` family, which allows for -shortening the lifetime of a borrowed query item. While not a breaking change, it's recommended -to implement for any custom `QueryData` types +Bevy 0.18 adds a new `reborrow` method to `QueryData`, which enables shortening the lifetime of a query item. + +```rust +fn reborrow<'a>(item: &'a mut Self::Item<'_, '_>) -> Self::Item<'a, 'a>; +``` + +Since `QueryData` implementers already have to be covariant over their lifetimes, +this shouldn't make the trait any harder to implement. For most read-only query +data, the method can be implemented with a simple deref: `*item`. From 4515b8a5c1ce30d4a8b8aaf97c3b8594ac4dfe18 Mon Sep 17 00:00:00 2001 From: Emerson Coskey Date: Mon, 15 Dec 2025 12:44:08 -0800 Subject: [PATCH 07/12] fix ci --- crates/bevy_ecs/src/query/fetch.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_ecs/src/query/fetch.rs b/crates/bevy_ecs/src/query/fetch.rs index 0fd8832741b76..40b25fc630ac5 100644 --- a/crates/bevy_ecs/src/query/fetch.rs +++ b/crates/bevy_ecs/src/query/fetch.rs @@ -857,7 +857,7 @@ unsafe impl QueryData for EntityRef<'_> { } fn reborrow<'a>(item: &'a mut Self::Item<'_, '_>) -> Self::Item<'a, 'a> { - item.reborrow() + *item } #[inline(always)] From dd572b3705178daecf478761f98fa4b792849ed7 Mon Sep 17 00:00:00 2001 From: Emerson Coskey Date: Mon, 15 Dec 2025 17:46:46 -0800 Subject: [PATCH 08/12] fix ci --- crates/bevy_ecs/src/change_detection/params.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_ecs/src/change_detection/params.rs b/crates/bevy_ecs/src/change_detection/params.rs index 6aa5a3fde02ce..12af8ac26c318 100644 --- a/crates/bevy_ecs/src/change_detection/params.rs +++ b/crates/bevy_ecs/src/change_detection/params.rs @@ -113,7 +113,7 @@ impl<'w, T: Resource> Res<'w, T> { pub fn clone(this: &Self) -> Self { Self { value: this.value, - ticks: this.ticks.clone(), + ticks: this.ticks, } } From 0b7dc03da31f28da48a3ab57e3b16e6beee23567 Mon Sep 17 00:00:00 2001 From: Emerson Coskey Date: Tue, 4 Nov 2025 16:24:38 -0800 Subject: [PATCH 09/12] ReborrowSystemParam fix all impls --- crates/bevy_ecs/macros/Cargo.toml | 2 +- crates/bevy_ecs/macros/src/lib.rs | 51 ++- .../bevy_ecs/src/change_detection/params.rs | 32 ++ .../bevy_ecs/src/change_detection/traits.rs | 12 +- crates/bevy_ecs/src/lifecycle.rs | 10 +- crates/bevy_ecs/src/system/commands/mod.rs | 12 +- crates/bevy_ecs/src/system/query.rs | 19 + crates/bevy_ecs/src/system/system_name.rs | 14 +- crates/bevy_ecs/src/system/system_param.rs | 354 ++++++++++++++++-- crates/bevy_ecs/src/world/identifier.rs | 12 +- crates/bevy_gizmos/src/gizmos.rs | 10 + crates/bevy_render/src/extract_param.rs | 16 + .../migration-guides/reborrow_traits.md | 16 + 13 files changed, 511 insertions(+), 49 deletions(-) diff --git a/crates/bevy_ecs/macros/Cargo.toml b/crates/bevy_ecs/macros/Cargo.toml index c70c258e6d74f..1103128a8b88a 100644 --- a/crates/bevy_ecs/macros/Cargo.toml +++ b/crates/bevy_ecs/macros/Cargo.toml @@ -11,7 +11,7 @@ proc-macro = true [dependencies] bevy_macro_utils = { path = "../../bevy_macro_utils", version = "0.18.0-dev" } -syn = { version = "2.0.108", features = ["full", "extra-traits"] } +syn = { version = "2.0.108", features = ["full", "extra-traits", "visit-mut"] } quote = "1.0" proc-macro2 = "1.0" [lints] diff --git a/crates/bevy_ecs/macros/src/lib.rs b/crates/bevy_ecs/macros/src/lib.rs index 6936ca4aff3a6..8ab5fb9bb4e00 100644 --- a/crates/bevy_ecs/macros/src/lib.rs +++ b/crates/bevy_ecs/macros/src/lib.rs @@ -20,8 +20,8 @@ use proc_macro::TokenStream; use proc_macro2::{Ident, Span}; use quote::{format_ident, quote, ToTokens}; use syn::{ - parse_macro_input, parse_quote, punctuated::Punctuated, token::Comma, ConstParam, Data, - DeriveInput, GenericParam, TypeParam, + parse_macro_input, parse_quote, punctuated::Punctuated, token::Comma, visit_mut::VisitMut, + ConstParam, Data, DeriveInput, GenericParam, TypeParam, }; enum BundleFieldKind { @@ -229,6 +229,16 @@ pub fn derive_map_entities(input: TokenStream) -> TokenStream { }) } +struct LifetimeEraser { + erased_ident: Ident, +} + +impl VisitMut for LifetimeEraser { + fn visit_lifetime_mut(&mut self, i: &mut syn::Lifetime) { + i.ident = self.erased_ident.clone(); + } +} + /// Implement `SystemParam` to use a struct as a parameter in a system #[proc_macro_derive(SystemParam, attributes(system_param))] pub fn derive_system_param(input: TokenStream) -> TokenStream { @@ -240,6 +250,7 @@ pub fn derive_system_param(input: TokenStream) -> TokenStream { Err(e) => e.into_compile_error().into(), } } + fn derive_system_param_impl( token_stream: TokenStream, ast: DeriveInput, @@ -253,6 +264,17 @@ fn derive_system_param_impl( .collect::>(); let field_members = fields.members().collect::>(); let field_types = fields.iter().map(|f| &f.ty).collect::>(); + let shadowed_lifetime_field_types = fields + .iter() + .map(|f| { + let mut ty = f.ty.clone(); + let mut eraser = LifetimeEraser { + erased_ident: format_ident!("static"), + }; + syn::visit_mut::visit_type_mut(&mut eraser, &mut ty); + ty + }) + .collect::>(); let field_validation_names = fields.members().map(|m| format!("::{}", quote! { #m })); let mut field_validation_messages = Vec::with_capacity(fields.len()); @@ -356,6 +378,18 @@ fn derive_system_param_impl( .push(syn::parse_quote!(#field_type: #path::system::ReadOnlySystemParam)); } + // Create a where clause for the `ReborrowSystemParam` impl. + // Ensure that each field implements `ReborrowSystemParam`. + let mut reborrow_generics = generics.clone(); + let reborrow_where_clause = reborrow_generics.make_where_clause(); + for (shadowed_lifetime_field_type, field_type) in + shadowed_lifetime_field_types.iter().zip(&field_types) + { + reborrow_where_clause + .predicates + .push(syn::parse_quote!(for<'w, 's> #shadowed_lifetime_field_type: #path::system::ReborrowSystemParam = #field_type>)); + } + let fields_alias = ensure_no_collision(format_ident!("__StructFieldsAlias"), token_stream.clone()); @@ -482,6 +516,19 @@ fn derive_system_param_impl( // Safety: Each field is `ReadOnlySystemParam`, so this can only read from the `World` unsafe impl<'w, 's, #punctuated_generics> #path::system::ReadOnlySystemParam for #struct_name #ty_generics #read_only_where_clause {} + impl<#punctuated_generics> #path::system::ReborrowSystemParam for #struct_name <#(#shadowed_lifetimes,)* #punctuated_generic_idents> #reborrow_where_clause { + fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( + item: &'short mut Self::Item<'wlong, 'slong>, + ) -> Self::Item<'short, 'short> { + #( + let #field_locals = <#shadowed_lifetime_field_types as #path::system::ReborrowSystemParam>::reborrow(&mut item.#field_members); + )* + #struct_name { + #(#field_members: #field_locals,)* + } + } + } + #builder_impl }; diff --git a/crates/bevy_ecs/src/change_detection/params.rs b/crates/bevy_ecs/src/change_detection/params.rs index 12af8ac26c318..8ef0070c873e6 100644 --- a/crates/bevy_ecs/src/change_detection/params.rs +++ b/crates/bevy_ecs/src/change_detection/params.rs @@ -123,6 +123,21 @@ impl<'w, T: Resource> Res<'w, T> { pub fn into_inner(self) -> &'w T { self.value } + + /// Returns a `Res<>` with a smaller lifetime. + /// This is useful if you have `&Res`, but you need a `Res`. + pub fn reborrow(&self) -> Res<'_, T> { + Res { + value: self.value, + ticks: ComponentTicksRef { + added: self.ticks.added, + changed: self.ticks.changed, + changed_by: self.ticks.changed_by, + last_run: self.ticks.last_run, + this_run: self.ticks.this_run, + }, + } + } } impl<'w, T: Resource> From> for Res<'w, T> { @@ -243,6 +258,23 @@ impl<'w, T> From> for NonSend<'w, T> { } } +impl<'w, T> NonSend<'w, T> { + /// Returns a `NonSend<>` with a smaller lifetime. + /// This is useful if you have `&NonSend`, but you need a `NonSend`. + pub fn reborrow(&mut self) -> NonSend<'_, T> { + NonSend { + value: self.value, + ticks: ComponentTicksRef { + added: self.ticks.added, + changed: self.ticks.changed, + changed_by: self.ticks.changed_by, + last_run: self.ticks.last_run, + this_run: self.ticks.this_run, + }, + } + } +} + /// Unique borrow of a non-[`Send`] resource. /// /// Only [`Send`] resources may be accessed with the [`ResMut`] [`SystemParam`](crate::system::SystemParam). In case that the diff --git a/crates/bevy_ecs/src/change_detection/traits.rs b/crates/bevy_ecs/src/change_detection/traits.rs index 30025551ada58..58b4dc342366e 100644 --- a/crates/bevy_ecs/src/change_detection/traits.rs +++ b/crates/bevy_ecs/src/change_detection/traits.rs @@ -449,12 +449,16 @@ macro_rules! impl_methods { self.value } - /// Returns a `Mut<>` with a smaller lifetime. + /// Returns a ` + #[doc = stringify!($name)] + /// <>` with a smaller lifetime. /// This is useful if you have `&mut #[doc = stringify!($name)] - /// `, but you need a `Mut`. - pub fn reborrow(&mut self) -> Mut<'_, $target> { - Mut { + /// `, but you need a ` + #[doc = stringify!($name)] + /// `. + pub fn reborrow(&mut self) -> $name<'_, $target> { + $name { value: self.value, ticks: ComponentTicksMut { added: self.ticks.added, diff --git a/crates/bevy_ecs/src/lifecycle.rs b/crates/bevy_ecs/src/lifecycle.rs index adfb660e8b38a..73b2a8bf4410e 100644 --- a/crates/bevy_ecs/src/lifecycle.rs +++ b/crates/bevy_ecs/src/lifecycle.rs @@ -60,7 +60,7 @@ use crate::{ query::FilteredAccessSet, relationship::RelationshipHookMode, storage::SparseSet, - system::{Local, ReadOnlySystemParam, SystemMeta, SystemParam}, + system::{Local, ReadOnlySystemParam, ReborrowSystemParam, SystemMeta, SystemParam}, world::{unsafe_world_cell::UnsafeWorldCell, DeferredWorld, World}, }; @@ -641,3 +641,11 @@ unsafe impl<'a> SystemParam for &'a RemovedComponentMessages { world.removed_components() } } + +impl<'a> ReborrowSystemParam for &'a RemovedComponentMessages { + fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( + item: &'short mut Self::Item<'wlong, 'slong>, + ) -> Self::Item<'short, 'short> { + *item + } +} diff --git a/crates/bevy_ecs/src/system/commands/mod.rs b/crates/bevy_ecs/src/system/commands/mod.rs index 8422ceacaa643..36bf26b36b07e 100644 --- a/crates/bevy_ecs/src/system/commands/mod.rs +++ b/crates/bevy_ecs/src/system/commands/mod.rs @@ -30,8 +30,8 @@ use crate::{ resource::Resource, schedule::ScheduleLabel, system::{ - Deferred, IntoObserverSystem, IntoSystem, RegisteredSystem, SystemId, SystemInput, - SystemParamValidationError, + Deferred, IntoObserverSystem, IntoSystem, ReborrowSystemParam, RegisteredSystem, SystemId, + SystemInput, SystemParamValidationError, }, world::{ command_queue::RawCommandQueue, unsafe_world_cell::UnsafeWorldCell, CommandQueue, @@ -216,6 +216,14 @@ const _: () = { &'w Entities: bevy_ecs::system::ReadOnlySystemParam, { } + + impl<'w, 's> ReborrowSystemParam for Commands<'w, 's> { + fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( + item: &'short mut Self::Item<'wlong, 'slong>, + ) -> Self::Item<'short, 'short> { + item.reborrow() + } + } }; enum InternalQueue<'s> { diff --git a/crates/bevy_ecs/src/system/query.rs b/crates/bevy_ecs/src/system/query.rs index b8c29d448adbf..4f57885e473ac 100644 --- a/crates/bevy_ecs/src/system/query.rs +++ b/crates/bevy_ecs/src/system/query.rs @@ -8,6 +8,7 @@ use crate::{ DebugCheckedUnwrap, NopWorldQuery, QueryCombinationIter, QueryData, QueryEntityError, QueryFilter, QueryIter, QueryManyIter, QueryManyUniqueIter, QueryParIter, QueryParManyIter, QueryParManyUniqueIter, QuerySingleError, QueryState, ROQueryItem, ReadOnlyQueryData, + ReborrowQueryData, }, world::unsafe_world_cell::UnsafeWorldCell, }; @@ -2674,6 +2675,18 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Single<'w, 's, D, F> { pub fn into_inner(self) -> D::Item<'w, 's> { self.item } + + /// Returns a `Single<>` with a smaller lifetime. + /// This is useful if you have `&Single`, but you need an `Single`. + pub fn reborrow(&mut self) -> Single<'_, '_, D, F> + where + D: ReborrowQueryData, + { + Single { + item: D::reborrow(&mut self.item), + _filter: PhantomData, + } + } } /// [System parameter] that works very much like [`Query`] except it always contains at least one matching entity. @@ -2710,6 +2723,12 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Populated<'w, 's, D, F> { pub fn into_inner(self) -> Query<'w, 's, D, F> { self.0 } + + /// Returns a `Populated<>` with a smaller lifetime. + /// This is useful if you have `&Populated`, but you need a `Populated`. + pub fn reborrow(&mut self) -> Populated<'_, '_, D, F> { + Populated(self.0.reborrow()) + } } impl<'w, 's, D: QueryData, F: QueryFilter> IntoIterator for Populated<'w, 's, D, F> { diff --git a/crates/bevy_ecs/src/system/system_name.rs b/crates/bevy_ecs/src/system/system_name.rs index af4fc91f3c9a2..e88e9138575f2 100644 --- a/crates/bevy_ecs/src/system/system_name.rs +++ b/crates/bevy_ecs/src/system/system_name.rs @@ -2,7 +2,9 @@ use crate::{ change_detection::Tick, prelude::World, query::FilteredAccessSet, - system::{ExclusiveSystemParam, ReadOnlySystemParam, SystemMeta, SystemParam}, + system::{ + ExclusiveSystemParam, ReadOnlySystemParam, ReborrowSystemParam, SystemMeta, SystemParam, + }, world::unsafe_world_cell::UnsafeWorldCell, }; use bevy_utils::prelude::DebugName; @@ -34,7 +36,7 @@ use derive_more::derive::{Display, Into}; /// logger.log("Hello"); /// } /// ``` -#[derive(Debug, Into, Display)] +#[derive(Debug, Into, Display, Clone)] pub struct SystemName(DebugName); impl SystemName { @@ -70,6 +72,14 @@ unsafe impl SystemParam for SystemName { } } +impl ReborrowSystemParam for SystemName { + fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( + item: &'short mut Self::Item<'wlong, 'slong>, + ) -> Self::Item<'short, 'short> { + item.clone() + } +} + // SAFETY: Only reads internal system state unsafe impl ReadOnlySystemParam for SystemName {} diff --git a/crates/bevy_ecs/src/system/system_param.rs b/crates/bevy_ecs/src/system/system_param.rs index de15c850ccdb9..804fd09c3bf5d 100644 --- a/crates/bevy_ecs/src/system/system_param.rs +++ b/crates/bevy_ecs/src/system/system_param.rs @@ -7,7 +7,7 @@ use crate::{ entity::{Entities, EntityAllocator}, query::{ Access, FilteredAccess, FilteredAccessSet, QueryData, QueryFilter, QuerySingleError, - QueryState, ReadOnlyQueryData, + QueryState, ReadOnlyQueryData, ReborrowQueryData, }, resource::Resource, storage::ResourceData, @@ -316,6 +316,16 @@ pub unsafe trait SystemParam: Sized { /// This must only be implemented for [`SystemParam`] impls that exclusively read the World passed in to [`SystemParam::get_param`] pub unsafe trait ReadOnlySystemParam: SystemParam {} +/// A [`SystemParam`] whose lifetime can be shortened via +/// [`reborrow`](ReborrowSystemParam::reborrow)-ing. This should be implemented +/// for most system params, except in the case of non-covariant lifetimes. +pub trait ReborrowSystemParam: SystemParam { + /// Returns a `SystemParam` item with a smaller lifetime. + fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( + item: &'short mut Self::Item<'wlong, 'slong>, + ) -> Self::Item<'short, 'short>; +} + /// Shorthand way of accessing the associated type [`SystemParam::Item`] for a given [`SystemParam`]. pub type SystemParamItem<'w, 's, P> =

::Item<'w, 's>; @@ -367,6 +377,14 @@ unsafe impl SystemParam for Qu } } +impl ReborrowSystemParam for Query<'_, '_, D, F> { + fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( + item: &'short mut Self::Item<'wlong, 'slong>, + ) -> Self::Item<'short, 'short> { + item.reborrow() + } +} + fn assert_component_access_compatibility( system_name: &DebugName, query_type: DebugName, @@ -458,6 +476,16 @@ unsafe impl<'a, 'b, D: ReadOnlyQueryData + 'static, F: QueryFilter + 'static> Re { } +impl<'a, 'b, D: ReborrowQueryData + 'static, F: QueryFilter + 'static> ReborrowSystemParam + for Single<'a, 'b, D, F> +{ + fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( + item: &'short mut Self::Item<'wlong, 'slong>, + ) -> Self::Item<'short, 'short> { + item.reborrow() + } +} + // SAFETY: Relevant query ComponentId access is applied to SystemMeta. If // this Query conflicts with any prior access, a panic will occur. unsafe impl SystemParam @@ -519,6 +547,16 @@ unsafe impl<'w, 's, D: ReadOnlyQueryData + 'static, F: QueryFilter + 'static> Re { } +impl ReborrowSystemParam + for Populated<'_, '_, D, F> +{ + fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( + item: &'short mut Self::Item<'wlong, 'slong>, + ) -> Self::Item<'short, 'short> { + item.reborrow() + } +} + /// A collection of potentially conflicting [`SystemParam`]s allowed by disjoint access. /// /// Allows systems to safely access and interact with up to 8 mutually exclusive [`SystemParam`]s, such as @@ -646,6 +684,15 @@ macro_rules! impl_param_set { where $($param: ReadOnlySystemParam,)* { } + + #[expect( + clippy::allow_attributes, + reason = "This is inside a macro meant for tuples; as such, `non_snake_case` won't always lint." + )] + #[allow( + non_snake_case, + reason = "Certain variable names are provided by the caller, not by us." + )] // SAFETY: Relevant parameter ComponentId access is applied to SystemMeta. If any ParamState conflicts // with any prior access, a panic will occur. unsafe impl<'_w, '_s, $($param: SystemParam,)*> SystemParam for ParamSet<'_w, '_s, ($($param,)*)> @@ -653,26 +700,10 @@ macro_rules! impl_param_set { type State = ($($param::State,)*); type Item<'w, 's> = ParamSet<'w, 's, ($($param,)*)>; - #[expect( - clippy::allow_attributes, - reason = "This is inside a macro meant for tuples; as such, `non_snake_case` won't always lint." - )] - #[allow( - non_snake_case, - reason = "Certain variable names are provided by the caller, not by us." - )] fn init_state(world: &mut World) -> Self::State { ($($param::init_state(world),)*) } - #[expect( - clippy::allow_attributes, - reason = "This is inside a macro meant for tuples; as such, `non_snake_case` won't always lint." - )] - #[allow( - non_snake_case, - reason = "Certain variable names are provided by the caller, not by us." - )] fn init_access(state: &Self::State, system_meta: &mut SystemMeta, component_access_set: &mut FilteredAccessSet, world: &mut World) { let ($($param,)*) = state; $( @@ -739,6 +770,31 @@ macro_rules! impl_param_set { } )* } + + impl<'w, 's, $($param: SystemParam + 'static,)*> ParamSet<'w, 's, ($($param,)*)> + { + /// Returns a `ParamSet` with a smaller lifetime. + /// This can be useful if you have an `&ParamSet` and need a `ParamSet`. + fn reborrow(&mut self) -> ParamSet<'_, '_, ($($param,)*)> { + ParamSet { + param_states: self.param_states, + world: self.world, + system_meta: self.system_meta.clone(), + change_tick: self.change_tick, + } + } + } + + // NOTE: the 'static bound isn't technically required here, but without it + // users can break `SystemParam` derives by introducing invariant lifetimes. + impl<'_w, '_s, $($param: SystemParam + 'static,)*> ReborrowSystemParam for ParamSet<'_w, '_s, ($($param,)*)> + { + fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( + item: &'short mut Self::Item<'wlong, 'slong>, + ) -> Self::Item<'short, 'short> { + item.reborrow() + } + } } } @@ -823,6 +879,14 @@ unsafe impl<'a, T: Resource> SystemParam for Res<'a, T> { } } +impl<'a, T: Resource> ReborrowSystemParam for Res<'a, T> { + fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( + item: &'short mut Self::Item<'wlong, 'slong>, + ) -> Self::Item<'short, 'short> { + item.reborrow() + } +} + // SAFETY: Res ComponentId access is applied to SystemMeta. If this Res // conflicts with any prior access, a panic will occur. unsafe impl<'a, T: Resource> SystemParam for ResMut<'a, T> { @@ -901,6 +965,14 @@ unsafe impl<'a, T: Resource> SystemParam for ResMut<'a, T> { } } +impl<'a, T: Resource> ReborrowSystemParam for ResMut<'a, T> { + fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( + item: &'short mut Self::Item<'wlong, 'slong>, + ) -> Self::Item<'short, 'short> { + item.reborrow() + } +} + /// SAFETY: only reads world unsafe impl<'w> ReadOnlySystemParam for &'w World {} @@ -941,6 +1013,14 @@ unsafe impl SystemParam for &'_ World { } } +impl<'w> ReborrowSystemParam for &'w World { + fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( + item: &'short mut Self::Item<'wlong, 'slong>, + ) -> Self::Item<'short, 'short> { + *item + } +} + /// SAFETY: `DeferredWorld` can read all components and resources but cannot be used to gain any other mutable references. unsafe impl<'w> SystemParam for DeferredWorld<'w> { type State = (); @@ -972,6 +1052,14 @@ unsafe impl<'w> SystemParam for DeferredWorld<'w> { } } +impl<'w> ReborrowSystemParam for DeferredWorld<'w> { + fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( + item: &'short mut Self::Item<'wlong, 'slong>, + ) -> Self::Item<'short, 'short> { + item.reborrow() + } +} + /// A [`SystemParam`] that provides a system-private value of `T` that persists across system calls. /// /// The initial value is created by calling `T`'s [`FromWorld::from_world`] (or [`Default::default`] if `T: Default`). @@ -1091,6 +1179,14 @@ unsafe impl<'w> SystemParam for DeferredWorld<'w> { #[derive(Debug)] pub struct Local<'s, T: FromWorld + Send + 'static>(pub(crate) &'s mut T); +impl<'s, T: FromWorld + Send + 'static> Local<'s, T> { + /// Returns a [`Local`] with a shorter lifetime. + /// This is useful if you have an `&mut Local` but want a `Local` + pub fn reborrow(&mut self) -> Local<'_, T> { + Local(self.0) + } +} + // SAFETY: Local only accesses internal state unsafe impl<'s, T: FromWorld + Send + 'static> ReadOnlySystemParam for Local<'s, T> {} @@ -1162,6 +1258,14 @@ unsafe impl<'a, T: FromWorld + Send + 'static> SystemParam for Local<'a, T> { } } +impl<'a, T: FromWorld + Send + 'static> ReborrowSystemParam for Local<'a, T> { + fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( + item: &'short mut Self::Item<'wlong, 'slong>, + ) -> Self::Item<'short, 'short> { + item.reborrow() + } +} + /// Types that can be used with [`Deferred`] in systems. /// This allows storing system-local data which is used to defer [`World`] mutations. /// @@ -1322,6 +1426,14 @@ impl Deferred<'_, T> { // SAFETY: Only local state is accessed. unsafe impl ReadOnlySystemParam for Deferred<'_, T> {} +impl ReborrowSystemParam for Deferred<'_, T> { + fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( + item: &'short mut Self::Item<'wlong, 'slong>, + ) -> Self::Item<'short, 'short> { + item.reborrow() + } +} + // SAFETY: Only local state is accessed. unsafe impl SystemParam for Deferred<'_, T> { type State = SyncCell; @@ -1427,6 +1539,14 @@ unsafe impl SystemParam for NonSendMarker { // SAFETY: Does not read any world state unsafe impl ReadOnlySystemParam for NonSendMarker {} +impl ReborrowSystemParam for NonSendMarker { + fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( + _item: &'short mut Self::Item<'wlong, 'slong>, + ) -> Self::Item<'short, 'short> { + Self(PhantomData) + } +} + // SAFETY: Only reads a single World non-send resource unsafe impl<'w, T> ReadOnlySystemParam for NonSend<'w, T> {} @@ -1501,6 +1621,14 @@ unsafe impl<'a, T: 'static> SystemParam for NonSend<'a, T> { } } +impl<'a, T: 'static> ReborrowSystemParam for NonSend<'a, T> { + fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( + item: &'short mut Self::Item<'wlong, 'slong>, + ) -> Self::Item<'short, 'short> { + item.reborrow() + } +} + // SAFETY: NonSendMut ComponentId access is applied to SystemMeta. If this // NonSendMut conflicts with any prior access, a panic will occur. unsafe impl<'a, T: 'static> SystemParam for NonSendMut<'a, T> { @@ -1575,6 +1703,14 @@ unsafe impl<'a, T: 'static> SystemParam for NonSendMut<'a, T> { } } +impl<'a, T: 'static> ReborrowSystemParam for NonSendMut<'a, T> { + fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( + item: &'short mut Self::Item<'wlong, 'slong>, + ) -> Self::Item<'short, 'short> { + item.reborrow() + } +} + // SAFETY: Only reads World archetypes unsafe impl<'a> ReadOnlySystemParam for &'a Archetypes {} @@ -1604,6 +1740,14 @@ unsafe impl<'a> SystemParam for &'a Archetypes { } } +impl<'a> ReborrowSystemParam for &'a Archetypes { + fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( + item: &'short mut Self::Item<'wlong, 'slong>, + ) -> Self::Item<'short, 'short> { + *item + } +} + // SAFETY: Only reads World components unsafe impl<'a> ReadOnlySystemParam for &'a Components {} @@ -1633,6 +1777,14 @@ unsafe impl<'a> SystemParam for &'a Components { } } +impl<'a> ReborrowSystemParam for &'a Components { + fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( + item: &'short mut Self::Item<'wlong, 'slong>, + ) -> Self::Item<'short, 'short> { + *item + } +} + // SAFETY: Only reads World entities unsafe impl<'a> ReadOnlySystemParam for &'a Entities {} @@ -1662,6 +1814,14 @@ unsafe impl<'a> SystemParam for &'a Entities { } } +impl<'a> ReborrowSystemParam for &'a Entities { + fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( + item: &'short mut Self::Item<'wlong, 'slong>, + ) -> Self::Item<'short, 'short> { + *item + } +} + // SAFETY: Only reads World entities unsafe impl<'a> ReadOnlySystemParam for &'a EntityAllocator {} @@ -1720,6 +1880,14 @@ unsafe impl<'a> SystemParam for &'a Bundles { } } +impl<'a> ReborrowSystemParam for &'a Bundles { + fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( + item: &'short mut Self::Item<'wlong, 'slong>, + ) -> Self::Item<'short, 'short> { + *item + } +} + /// A [`SystemParam`] that reads the previous and current change ticks of the system. /// /// A system's change ticks are updated each time it runs: @@ -1781,6 +1949,14 @@ unsafe impl SystemParam for SystemChangeTick { } } +impl ReborrowSystemParam for SystemChangeTick { + fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( + item: &'short mut Self::Item<'wlong, 'slong>, + ) -> Self::Item<'short, 'short> { + *item + } +} + // SAFETY: Delegates to `T`, which ensures the safety requirements are met unsafe impl SystemParam for Option { type State = T::State; @@ -1824,6 +2000,14 @@ unsafe impl SystemParam for Option { // SAFETY: Delegates to `T`, which ensures the safety requirements are met unsafe impl ReadOnlySystemParam for Option {} +impl ReborrowSystemParam for Option { + fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( + item: &'short mut Self::Item<'wlong, 'slong>, + ) -> Self::Item<'short, 'short> { + item.as_mut().map(T::reborrow) + } +} + // SAFETY: Delegates to `T`, which ensures the safety requirements are met unsafe impl SystemParam for Result { type State = T::State; @@ -1866,6 +2050,14 @@ unsafe impl SystemParam for Result ReadOnlySystemParam for Result {} +impl ReborrowSystemParam for Result { + fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( + item: &'short mut Self::Item<'wlong, 'slong>, + ) -> Self::Item<'short, 'short> { + item.as_mut().map(T::reborrow).map_err(|err| err.clone()) + } +} + /// A [`SystemParam`] that wraps another parameter and causes its system to skip instead of failing when the parameter is invalid. /// /// # Example @@ -1969,6 +2161,14 @@ unsafe impl SystemParam for If { } } +impl ReborrowSystemParam for If { + fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( + item: &'short mut Self::Item<'wlong, 'slong>, + ) -> Self::Item<'short, 'short> { + If(T::reborrow(&mut item.0)) + } +} + // SAFETY: Delegates to `T`, which ensures the safety requirements are met unsafe impl ReadOnlySystemParam for If {} @@ -2035,6 +2235,16 @@ unsafe impl SystemParam for Vec { } } +//TODO: should we implement reborrow for stuff that requires cloning? It'd still be faster than +//getting a new param each time I guess. +impl ReborrowSystemParam for Vec { + fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( + item: &'short mut Self::Item<'wlong, 'slong>, + ) -> Self::Item<'short, 'short> { + item.iter_mut().map(T::reborrow).collect() + } +} + // SAFETY: Registers access for each element of `state`. // If any one conflicts with a previous parameter, // the call passing a copy of the current access will panic. @@ -2095,7 +2305,26 @@ unsafe impl SystemParam for ParamSet<'_, '_, Vec> { } } +impl ReborrowSystemParam for ParamSet<'_, '_, Vec

> { + fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( + item: &'short mut Self::Item<'wlong, 'slong>, + ) -> Self::Item<'short, 'short> { + item.reborrow() + } +} + impl ParamSet<'_, '_, Vec> { + /// Returns a `ParamSet` with a smaller lifetime. + /// This can be useful if you have an `&ParamSet` and need a `ParamSet`. + fn reborrow(&mut self) -> ParamSet<'_, '_, Vec> { + ParamSet { + param_states: self.param_states, + world: self.world, + system_meta: self.system_meta.clone(), + change_tick: self.change_tick, + } + } + /// Accesses the parameter at the given index. /// No other parameters may be accessed while this one is active. pub fn get_mut(&mut self, index: usize) -> T::Item<'_, '_> { @@ -2128,7 +2357,7 @@ impl ParamSet<'_, '_, Vec> { } macro_rules! impl_system_param_tuple { - ($(#[$meta:meta])* $($param: ident),*) => { + ($(#[$meta:meta])* $(($param: ident, $item: ident)),*) => { $(#[$meta])* // SAFETY: tuple consists only of ReadOnlySystemParams unsafe impl<$($param: ReadOnlySystemParam),*> ReadOnlySystemParam for ($($param,)*) {} @@ -2145,13 +2374,17 @@ macro_rules! impl_system_param_tuple { unused_variables, reason = "Zero-length tuples won't use some of the parameters." )] - #[allow(clippy::unused_unit, reason = "Zero length tuple is unit.")] + #[allow( + clippy::unused_unit, + reason = "Zero-length tuples cause redundant unit expressions." + )] $(#[$meta])* // SAFETY: implementers of each `SystemParam` in the tuple have validated their impls unsafe impl<$($param: SystemParam),*> SystemParam for ($($param,)*) { type State = ($($param::State,)*); type Item<'w, 's> = ($($param::Item::<'w, 's>,)*); + #[inline] fn init_state(world: &mut World) -> Self::State { ($($param::init_state(world),)*) @@ -2197,13 +2430,23 @@ macro_rules! impl_system_param_tuple { change_tick: Tick, ) -> Self::Item<'w, 's> { let ($($param,)*) = state; - #[allow( - clippy::unused_unit, - reason = "Zero-length tuples won't have any params to get." - )] ($($param::get_param($param, system_meta, world, change_tick),)*) } } + + #[expect( + clippy::allow_attributes, + reason = "This is in a macro, and as such, the below lints may not always apply." + )] + + $(#[$meta])* + impl<$($param: ReborrowSystemParam),*> ReborrowSystemParam for ($($param,)*) { + fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( + ($($item,)*): &'short mut Self::Item<'wlong, 'slong>, + ) -> Self::Item<'short, 'short> { + ($($param::reborrow($item),)*) + } + } }; } @@ -2212,7 +2455,8 @@ all_tuples!( impl_system_param_tuple, 0, 16, - P + P, + i ); /// Contains type aliases for built-in [`SystemParam`]s with `'static` lifetimes. @@ -2369,6 +2613,14 @@ unsafe impl SystemParam for StaticSystemParam<'_, '_, } } +impl ReborrowSystemParam for StaticSystemParam<'_, '_, P> { + fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( + item: &'short mut Self::Item<'wlong, 'slong>, + ) -> Self::Item<'short, 'short> { + StaticSystemParam(P::reborrow(&mut item.0)) + } +} + // SAFETY: No world access. unsafe impl SystemParam for PhantomData { type State = (); @@ -2398,6 +2650,13 @@ unsafe impl SystemParam for PhantomData { // SAFETY: No world access. unsafe impl ReadOnlySystemParam for PhantomData {} +impl ReborrowSystemParam for PhantomData { + fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( + _item: &'short mut Self::Item<'wlong, 'slong>, + ) -> Self::Item<'short, 'short> { + PhantomData + } +} /// A [`SystemParam`] with a type that can be configured at runtime. /// /// To be useful, this must be configured using a [`DynParamBuilder`](crate::system::DynParamBuilder) to build the system using a [`SystemParamBuilder`](crate::prelude::SystemParamBuilder). @@ -2538,6 +2797,17 @@ impl<'w, 's> DynSystemParam<'w, 's> { // - The inner system param only performs read access, so it's safe to copy that access for the full `'w` lifetime. unsafe { downcast::(self.state, &self.system_meta, self.world, self.change_tick) } } + + /// Returns a `Res<>` with a smaller lifetime. + /// This is useful if you have `&Res`, but you need a `Res`. + pub fn reborrow(&mut self) -> DynSystemParam<'_, '_> { + DynSystemParam { + state: self.state, + world: self.world, + system_meta: self.system_meta.clone(), + change_tick: self.change_tick, + } + } } /// # Safety @@ -2697,6 +2967,14 @@ unsafe impl SystemParam for DynSystemParam<'_, '_> { } } +impl ReborrowSystemParam for DynSystemParam<'_, '_> { + fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( + item: &'short mut Self::Item<'wlong, 'slong>, + ) -> Self::Item<'short, 'short> { + item.reborrow() + } +} + // SAFETY: Resource ComponentId access is applied to the access. If this FilteredResources // conflicts with any prior access, a panic will occur. unsafe impl SystemParam for FilteredResources<'_, '_> { @@ -2743,6 +3021,14 @@ unsafe impl SystemParam for FilteredResources<'_, '_> { } } +impl ReborrowSystemParam for FilteredResources<'_, '_> { + fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( + item: &'short mut Self::Item<'wlong, 'slong>, + ) -> Self::Item<'short, 'short> { + *item + } +} + // SAFETY: FilteredResources only reads resources. unsafe impl ReadOnlySystemParam for FilteredResources<'_, '_> {} @@ -2800,6 +3086,14 @@ unsafe impl SystemParam for FilteredResourcesMut<'_, '_> { } } +impl ReborrowSystemParam for FilteredResourcesMut<'_, '_> { + fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( + item: &'short mut Self::Item<'wlong, 'slong>, + ) -> Self::Item<'short, 'short> { + item.reborrow() + } +} + /// An error that occurs when a system parameter is not valid, /// used by system executors to determine what to do with a system. /// @@ -3074,18 +3368,6 @@ mod tests { assert_is_system(my_system); } - // Regression test for https://github.com/bevyengine/bevy/issues/8192. - #[test] - fn system_param_invariant_lifetime() { - #[derive(SystemParam)] - pub struct InvariantParam<'w, 's> { - _set: ParamSet<'w, 's, (Query<'w, 's, ()>,)>, - } - - fn my_system(_: InvariantParam) {} - assert_is_system(my_system); - } - // Compile test for https://github.com/bevyengine/bevy/pull/9589. #[test] fn non_sync_local() { diff --git a/crates/bevy_ecs/src/world/identifier.rs b/crates/bevy_ecs/src/world/identifier.rs index ea4a2b20bc74b..98912f8080fa2 100644 --- a/crates/bevy_ecs/src/world/identifier.rs +++ b/crates/bevy_ecs/src/world/identifier.rs @@ -2,7 +2,9 @@ use crate::{ change_detection::Tick, query::FilteredAccessSet, storage::SparseSetIndex, - system::{ExclusiveSystemParam, ReadOnlySystemParam, SystemMeta, SystemParam}, + system::{ + ExclusiveSystemParam, ReadOnlySystemParam, ReborrowSystemParam, SystemMeta, SystemParam, + }, world::{FromWorld, World}, }; use bevy_platform::sync::atomic::{AtomicUsize, Ordering}; @@ -75,6 +77,14 @@ unsafe impl SystemParam for WorldId { } } +impl ReborrowSystemParam for WorldId { + fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( + item: &'short mut Self::Item<'wlong, 'slong>, + ) -> Self::Item<'short, 'short> { + *item + } +} + impl ExclusiveSystemParam for WorldId { type State = WorldId; type Item<'s> = WorldId; diff --git a/crates/bevy_gizmos/src/gizmos.rs b/crates/bevy_gizmos/src/gizmos.rs index 3d666cbbf51fe..2375cd208c5b1 100644 --- a/crates/bevy_gizmos/src/gizmos.rs +++ b/crates/bevy_gizmos/src/gizmos.rs @@ -200,6 +200,16 @@ where type State = GizmosFetchState; type Item<'w, 's> = Gizmos<'w, 's, Config, Clear>; + fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( + item: &'short mut Self::Item<'wlong, 'slong>, + ) -> Self::Item<'short, 'short> { + Gizmos { + buffer: item.buffer.reborrow(), + config: item.config, + config_ext: item.config_ext, + } + } + fn init_state(world: &mut World) -> Self::State { GizmosFetchState { state: GizmosState::::init_state(world), diff --git a/crates/bevy_render/src/extract_param.rs b/crates/bevy_render/src/extract_param.rs index 4d81fb9eb1903..848a2f1fbdc86 100644 --- a/crates/bevy_render/src/extract_param.rs +++ b/crates/bevy_render/src/extract_param.rs @@ -72,6 +72,14 @@ where type State = ExtractState

; type Item<'w, 's> = Extract<'w, 's, P>; + fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( + item: &'short mut Self::Item<'wlong, 'slong>, + ) -> Self::Item<'short, 'short> { + Extract { + item:

::reborrow(&mut item.item), + } + } + fn init_state(world: &mut World) -> Self::State { let mut main_world = world.resource_mut::(); ExtractState { @@ -141,6 +149,14 @@ where } } +impl<'w, 's, P: ReadOnlySystemParam> Extract<'w, 's, P> { + pub fn reborrow(&mut self) -> Extract<'_, '_, P> { + Extract { + item:

::reborrow(&mut self.item), + } + } +} + impl<'w, 's, P> Deref for Extract<'w, 's, P> where P: ReadOnlySystemParam, diff --git a/release-content/migration-guides/reborrow_traits.md b/release-content/migration-guides/reborrow_traits.md index ccca63c283d24..533697face64d 100644 --- a/release-content/migration-guides/reborrow_traits.md +++ b/release-content/migration-guides/reborrow_traits.md @@ -12,3 +12,19 @@ fn reborrow<'a>(item: &'a mut Self::Item<'_, '_>) -> Self::Item<'a, 'a>; Since `QueryData` implementers already have to be covariant over their lifetimes, this shouldn't make the trait any harder to implement. For most read-only query data, the method can be implemented with a simple deref: `*item`. + +Bevy 0.18 adds a few new traits to the ECS family: `ReborrowQueryData` and `ReborrowSystemParam`, +which allow for shortening the lifetime of a borrowed query item or system param respectively. +While not a breaking change, they're recommended to implement for most custom types where possible. + +```rust +/// A [`SystemParam`] whose lifetime can be shortened via +/// [`reborrow`](ReborrowSystemParam::reborrow)-ing. This should be implemented +/// for most system params, except in the case of non-covariant lifetimes. +pub trait ReborrowSystemParam: SystemParam { + /// Returns a `SystemParam` item with a smaller lifetime. + fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( + item: &'short mut Self::Item<'wlong, 'slong>, + ) -> Self::Item<'short, 'short>; +} +``` From d33fcbab8135ebfe1d5f2bb00d98d10cdfc8dc4d Mon Sep 17 00:00:00 2001 From: Emerson Coskey Date: Wed, 3 Dec 2025 19:30:28 -0800 Subject: [PATCH 10/12] fix impls --- crates/bevy_render/src/extract_param.rs | 26 +++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/crates/bevy_render/src/extract_param.rs b/crates/bevy_render/src/extract_param.rs index 848a2f1fbdc86..65a9518564434 100644 --- a/crates/bevy_render/src/extract_param.rs +++ b/crates/bevy_render/src/extract_param.rs @@ -4,8 +4,8 @@ use bevy_ecs::{ prelude::*, query::FilteredAccessSet, system::{ - ReadOnlySystemParam, SystemMeta, SystemParam, SystemParamItem, SystemParamValidationError, - SystemState, + ReadOnlySystemParam, ReborrowSystemParam, SystemMeta, SystemParam, SystemParamItem, + SystemParamValidationError, SystemState, }, world::unsafe_world_cell::UnsafeWorldCell, }; @@ -72,14 +72,6 @@ where type State = ExtractState

; type Item<'w, 's> = Extract<'w, 's, P>; - fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( - item: &'short mut Self::Item<'wlong, 'slong>, - ) -> Self::Item<'short, 'short> { - Extract { - item:

::reborrow(&mut item.item), - } - } - fn init_state(world: &mut World) -> Self::State { let mut main_world = world.resource_mut::(); ExtractState { @@ -149,10 +141,20 @@ where } } -impl<'w, 's, P: ReadOnlySystemParam> Extract<'w, 's, P> { +impl ReborrowSystemParam for Extract<'_, '_, P> { + fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( + item: &'short mut Self::Item<'wlong, 'slong>, + ) -> Self::Item<'short, 'short> { + Extract { + item: P::reborrow(&mut item.item), + } + } +} + +impl<'w, 's, P: ReborrowSystemParam + ReadOnlySystemParam> Extract<'w, 's, P> { pub fn reborrow(&mut self) -> Extract<'_, '_, P> { Extract { - item:

::reborrow(&mut self.item), + item: P::reborrow(&mut self.item), } } } From 67dd83a7b59d61f487bbdf1f453e033d44ad95e7 Mon Sep 17 00:00:00 2001 From: Emerson Coskey Date: Thu, 4 Dec 2025 14:03:24 -0800 Subject: [PATCH 11/12] fix gizmos --- crates/bevy_gizmos/src/gizmos.rs | 42 +++++++++++++++++++++++--------- 1 file changed, 30 insertions(+), 12 deletions(-) diff --git a/crates/bevy_gizmos/src/gizmos.rs b/crates/bevy_gizmos/src/gizmos.rs index 2375cd208c5b1..6de36392c02f8 100644 --- a/crates/bevy_gizmos/src/gizmos.rs +++ b/crates/bevy_gizmos/src/gizmos.rs @@ -13,8 +13,8 @@ use bevy_ecs::{ query::FilteredAccessSet, resource::Resource, system::{ - Deferred, ReadOnlySystemParam, Res, SystemBuffer, SystemMeta, SystemParam, - SystemParamValidationError, + Deferred, ReadOnlySystemParam, ReborrowSystemParam, Res, SystemBuffer, SystemMeta, + SystemParam, SystemParamValidationError, }, world::{unsafe_world_cell::UnsafeWorldCell, World}, }; @@ -152,6 +152,22 @@ where pub config_ext: &'w Config, } +impl<'w, 's, Config, Clear> Gizmos<'w, 's, Config, Clear> +where + Config: GizmoConfigGroup, + Clear: 'static + Send + Sync, +{ + /// Returns a [`Gizmos`] with a shorter lifetime. + /// Useful if you have an `&mut Gizmos` but want a `Gizmos` + pub fn reborrow(&mut self) -> Gizmos<'_, '_, Config, Clear> { + Gizmos { + buffer: self.buffer.reborrow(), + config: self.config, + config_ext: self.config_ext, + } + } +} + impl<'w, 's, Config, Clear> Deref for Gizmos<'w, 's, Config, Clear> where Config: GizmoConfigGroup, @@ -200,16 +216,6 @@ where type State = GizmosFetchState; type Item<'w, 's> = Gizmos<'w, 's, Config, Clear>; - fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( - item: &'short mut Self::Item<'wlong, 'slong>, - ) -> Self::Item<'short, 'short> { - Gizmos { - buffer: item.buffer.reborrow(), - config: item.config, - config_ext: item.config_ext, - } - } - fn init_state(world: &mut World) -> Self::State { GizmosFetchState { state: GizmosState::::init_state(world), @@ -277,6 +283,18 @@ where } } +impl ReborrowSystemParam for Gizmos<'_, '_, Config, Clear> +where + Config: GizmoConfigGroup, + Clear: 'static + Send + Sync, +{ + fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( + item: &'short mut Self::Item<'wlong, 'slong>, + ) -> Self::Item<'short, 'short> { + item.reborrow() + } +} + #[expect( unsafe_code, reason = "We cannot implement ReadOnlySystemParam without using unsafe code." From 34af9048937bbb80d1b4584a972690d66a1957a0 Mon Sep 17 00:00:00 2001 From: Emerson Coskey Date: Mon, 15 Dec 2025 13:30:44 -0800 Subject: [PATCH 12/12] merge traits --- crates/bevy_ecs/macros/Cargo.toml | 2 +- crates/bevy_ecs/macros/src/lib.rs | 36 +- .../bevy_ecs/src/change_detection/params.rs | 40 +- crates/bevy_ecs/src/lifecycle.rs | 18 +- crates/bevy_ecs/src/system/commands/mod.rs | 16 +- crates/bevy_ecs/src/system/query.rs | 6 +- crates/bevy_ecs/src/system/system_name.rs | 16 +- crates/bevy_ecs/src/system/system_param.rs | 476 +++++++----------- crates/bevy_ecs/src/world/identifier.rs | 16 +- crates/bevy_gizmos/src/gizmos.rs | 20 +- crates/bevy_render/src/extract_param.rs | 20 +- .../migration-guides/reborrow_methods.md | 20 + .../migration-guides/reborrow_traits.md | 30 -- 13 files changed, 265 insertions(+), 451 deletions(-) create mode 100644 release-content/migration-guides/reborrow_methods.md delete mode 100644 release-content/migration-guides/reborrow_traits.md diff --git a/crates/bevy_ecs/macros/Cargo.toml b/crates/bevy_ecs/macros/Cargo.toml index 1103128a8b88a..c70c258e6d74f 100644 --- a/crates/bevy_ecs/macros/Cargo.toml +++ b/crates/bevy_ecs/macros/Cargo.toml @@ -11,7 +11,7 @@ proc-macro = true [dependencies] bevy_macro_utils = { path = "../../bevy_macro_utils", version = "0.18.0-dev" } -syn = { version = "2.0.108", features = ["full", "extra-traits", "visit-mut"] } +syn = { version = "2.0.108", features = ["full", "extra-traits"] } quote = "1.0" proc-macro2 = "1.0" [lints] diff --git a/crates/bevy_ecs/macros/src/lib.rs b/crates/bevy_ecs/macros/src/lib.rs index 8ab5fb9bb4e00..bc843cd32e4ab 100644 --- a/crates/bevy_ecs/macros/src/lib.rs +++ b/crates/bevy_ecs/macros/src/lib.rs @@ -378,18 +378,6 @@ fn derive_system_param_impl( .push(syn::parse_quote!(#field_type: #path::system::ReadOnlySystemParam)); } - // Create a where clause for the `ReborrowSystemParam` impl. - // Ensure that each field implements `ReborrowSystemParam`. - let mut reborrow_generics = generics.clone(); - let reborrow_where_clause = reborrow_generics.make_where_clause(); - for (shadowed_lifetime_field_type, field_type) in - shadowed_lifetime_field_types.iter().zip(&field_types) - { - reborrow_where_clause - .predicates - .push(syn::parse_quote!(for<'w, 's> #shadowed_lifetime_field_type: #path::system::ReborrowSystemParam = #field_type>)); - } - let fields_alias = ensure_no_collision(format_ident!("__StructFieldsAlias"), token_stream.clone()); @@ -465,6 +453,17 @@ fn derive_system_param_impl( type State = #state_struct_name<#punctuated_generic_idents>; type Item<'w, 's> = #struct_name #ty_generics; + fn reborrow<'a>( + item: &'a mut Self::Item<'_, '_>, + ) -> Self::Item<'a, 'a> { + #( + let #field_locals = <#shadowed_lifetime_field_types as #path::system::SystemParam>::reborrow(&mut item.#field_members); + )* + #struct_name { + #(#field_members: #field_locals,)* + } + } + fn init_state(world: &mut #path::world::World) -> Self::State { #state_struct_name { state: <#fields_alias::<'_, '_, #punctuated_generic_idents> as #path::system::SystemParam>::init_state(world), @@ -516,19 +515,6 @@ fn derive_system_param_impl( // Safety: Each field is `ReadOnlySystemParam`, so this can only read from the `World` unsafe impl<'w, 's, #punctuated_generics> #path::system::ReadOnlySystemParam for #struct_name #ty_generics #read_only_where_clause {} - impl<#punctuated_generics> #path::system::ReborrowSystemParam for #struct_name <#(#shadowed_lifetimes,)* #punctuated_generic_idents> #reborrow_where_clause { - fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( - item: &'short mut Self::Item<'wlong, 'slong>, - ) -> Self::Item<'short, 'short> { - #( - let #field_locals = <#shadowed_lifetime_field_types as #path::system::ReborrowSystemParam>::reborrow(&mut item.#field_members); - )* - #struct_name { - #(#field_members: #field_locals,)* - } - } - } - #builder_impl }; diff --git a/crates/bevy_ecs/src/change_detection/params.rs b/crates/bevy_ecs/src/change_detection/params.rs index 8ef0070c873e6..2fa833227cca5 100644 --- a/crates/bevy_ecs/src/change_detection/params.rs +++ b/crates/bevy_ecs/src/change_detection/params.rs @@ -123,21 +123,6 @@ impl<'w, T: Resource> Res<'w, T> { pub fn into_inner(self) -> &'w T { self.value } - - /// Returns a `Res<>` with a smaller lifetime. - /// This is useful if you have `&Res`, but you need a `Res`. - pub fn reborrow(&self) -> Res<'_, T> { - Res { - value: self.value, - ticks: ComponentTicksRef { - added: self.ticks.added, - changed: self.ticks.changed, - changed_by: self.ticks.changed_by, - last_run: self.ticks.last_run, - this_run: self.ticks.this_run, - }, - } - } } impl<'w, T: Resource> From> for Res<'w, T> { @@ -246,6 +231,14 @@ pub struct NonSend<'w, T: ?Sized + 'static> { pub(crate) ticks: ComponentTicksRef<'w>, } +impl<'w, T: ?Sized + 'static> Clone for NonSend<'w, T> { + fn clone(&self) -> Self { + *self + } +} + +impl<'w, T: ?Sized + 'static> Copy for NonSend<'w, T> {} + change_detection_impl!(NonSend<'w, T>, T,); impl_debug!(NonSend<'w, T>,); @@ -258,23 +251,6 @@ impl<'w, T> From> for NonSend<'w, T> { } } -impl<'w, T> NonSend<'w, T> { - /// Returns a `NonSend<>` with a smaller lifetime. - /// This is useful if you have `&NonSend`, but you need a `NonSend`. - pub fn reborrow(&mut self) -> NonSend<'_, T> { - NonSend { - value: self.value, - ticks: ComponentTicksRef { - added: self.ticks.added, - changed: self.ticks.changed, - changed_by: self.ticks.changed_by, - last_run: self.ticks.last_run, - this_run: self.ticks.this_run, - }, - } - } -} - /// Unique borrow of a non-[`Send`] resource. /// /// Only [`Send`] resources may be accessed with the [`ResMut`] [`SystemParam`](crate::system::SystemParam). In case that the diff --git a/crates/bevy_ecs/src/lifecycle.rs b/crates/bevy_ecs/src/lifecycle.rs index 73b2a8bf4410e..26535d3769403 100644 --- a/crates/bevy_ecs/src/lifecycle.rs +++ b/crates/bevy_ecs/src/lifecycle.rs @@ -60,7 +60,7 @@ use crate::{ query::FilteredAccessSet, relationship::RelationshipHookMode, storage::SparseSet, - system::{Local, ReadOnlySystemParam, ReborrowSystemParam, SystemMeta, SystemParam}, + system::{Local, ReadOnlySystemParam, SystemMeta, SystemParam}, world::{unsafe_world_cell::UnsafeWorldCell, DeferredWorld, World}, }; @@ -614,13 +614,17 @@ impl<'w, 's, T: Component> RemovedComponents<'w, 's, T> { } // SAFETY: Only reads World removed component messages -unsafe impl<'a> ReadOnlySystemParam for &'a RemovedComponentMessages {} +unsafe impl ReadOnlySystemParam for &'_ RemovedComponentMessages {} // SAFETY: no component value access. -unsafe impl<'a> SystemParam for &'a RemovedComponentMessages { +unsafe impl SystemParam for &'_ RemovedComponentMessages { type State = (); type Item<'w, 's> = &'w RemovedComponentMessages; + fn reborrow<'a>(item: &'a mut Self::Item<'_, '_>) -> Self::Item<'a, 'a> { + *item + } + fn init_state(_world: &mut World) -> Self::State {} fn init_access( @@ -641,11 +645,3 @@ unsafe impl<'a> SystemParam for &'a RemovedComponentMessages { world.removed_components() } } - -impl<'a> ReborrowSystemParam for &'a RemovedComponentMessages { - fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( - item: &'short mut Self::Item<'wlong, 'slong>, - ) -> Self::Item<'short, 'short> { - *item - } -} diff --git a/crates/bevy_ecs/src/system/commands/mod.rs b/crates/bevy_ecs/src/system/commands/mod.rs index 36bf26b36b07e..7da3ba8fa2bfd 100644 --- a/crates/bevy_ecs/src/system/commands/mod.rs +++ b/crates/bevy_ecs/src/system/commands/mod.rs @@ -30,8 +30,8 @@ use crate::{ resource::Resource, schedule::ScheduleLabel, system::{ - Deferred, IntoObserverSystem, IntoSystem, ReborrowSystemParam, RegisteredSystem, SystemId, - SystemInput, SystemParamValidationError, + Deferred, IntoObserverSystem, IntoSystem, RegisteredSystem, SystemId, SystemInput, + SystemParamValidationError, }, world::{ command_queue::RawCommandQueue, unsafe_world_cell::UnsafeWorldCell, CommandQueue, @@ -130,6 +130,10 @@ const _: () = { type Item<'w, 's> = Commands<'w, 's>; + fn reborrow<'a>(item: &'a mut Self::Item<'_, '_>) -> Self::Item<'a, 'a> { + item.reborrow() + } + fn init_state(world: &mut World) -> Self::State { FetchState { state: <__StructFieldsAlias<'_, '_> as bevy_ecs::system::SystemParam>::init_state( @@ -216,14 +220,6 @@ const _: () = { &'w Entities: bevy_ecs::system::ReadOnlySystemParam, { } - - impl<'w, 's> ReborrowSystemParam for Commands<'w, 's> { - fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( - item: &'short mut Self::Item<'wlong, 'slong>, - ) -> Self::Item<'short, 'short> { - item.reborrow() - } - } }; enum InternalQueue<'s> { diff --git a/crates/bevy_ecs/src/system/query.rs b/crates/bevy_ecs/src/system/query.rs index 4f57885e473ac..20ba6c121ffc2 100644 --- a/crates/bevy_ecs/src/system/query.rs +++ b/crates/bevy_ecs/src/system/query.rs @@ -8,7 +8,6 @@ use crate::{ DebugCheckedUnwrap, NopWorldQuery, QueryCombinationIter, QueryData, QueryEntityError, QueryFilter, QueryIter, QueryManyIter, QueryManyUniqueIter, QueryParIter, QueryParManyIter, QueryParManyUniqueIter, QuerySingleError, QueryState, ROQueryItem, ReadOnlyQueryData, - ReborrowQueryData, }, world::unsafe_world_cell::UnsafeWorldCell, }; @@ -2678,10 +2677,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Single<'w, 's, D, F> { /// Returns a `Single<>` with a smaller lifetime. /// This is useful if you have `&Single`, but you need an `Single`. - pub fn reborrow(&mut self) -> Single<'_, '_, D, F> - where - D: ReborrowQueryData, - { + pub fn reborrow(&mut self) -> Single<'_, '_, D, F> { Single { item: D::reborrow(&mut self.item), _filter: PhantomData, diff --git a/crates/bevy_ecs/src/system/system_name.rs b/crates/bevy_ecs/src/system/system_name.rs index e88e9138575f2..55dcab66b8730 100644 --- a/crates/bevy_ecs/src/system/system_name.rs +++ b/crates/bevy_ecs/src/system/system_name.rs @@ -2,9 +2,7 @@ use crate::{ change_detection::Tick, prelude::World, query::FilteredAccessSet, - system::{ - ExclusiveSystemParam, ReadOnlySystemParam, ReborrowSystemParam, SystemMeta, SystemParam, - }, + system::{ExclusiveSystemParam, ReadOnlySystemParam, SystemMeta, SystemParam}, world::unsafe_world_cell::UnsafeWorldCell, }; use bevy_utils::prelude::DebugName; @@ -51,6 +49,10 @@ unsafe impl SystemParam for SystemName { type State = (); type Item<'w, 's> = SystemName; + fn reborrow<'a>(item: &'a mut Self::Item<'_, '_>) -> Self::Item<'a, 'a> { + item.clone() + } + fn init_state(_world: &mut World) -> Self::State {} fn init_access( @@ -72,14 +74,6 @@ unsafe impl SystemParam for SystemName { } } -impl ReborrowSystemParam for SystemName { - fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( - item: &'short mut Self::Item<'wlong, 'slong>, - ) -> Self::Item<'short, 'short> { - item.clone() - } -} - // SAFETY: Only reads internal system state unsafe impl ReadOnlySystemParam for SystemName {} diff --git a/crates/bevy_ecs/src/system/system_param.rs b/crates/bevy_ecs/src/system/system_param.rs index 804fd09c3bf5d..3601d011659e4 100644 --- a/crates/bevy_ecs/src/system/system_param.rs +++ b/crates/bevy_ecs/src/system/system_param.rs @@ -7,7 +7,7 @@ use crate::{ entity::{Entities, EntityAllocator}, query::{ Access, FilteredAccess, FilteredAccessSet, QueryData, QueryFilter, QuerySingleError, - QueryState, ReadOnlyQueryData, ReborrowQueryData, + QueryState, ReadOnlyQueryData, }, resource::Resource, storage::ResourceData, @@ -220,6 +220,9 @@ pub unsafe trait SystemParam: Sized { /// You could think of [`SystemParam::Item<'w, 's>`] as being an *operation* that changes the lifetimes bound to `Self`. type Item<'world, 'state>: SystemParam; + /// Returns a `SystemParam` item with a smaller lifetime. + fn reborrow<'a>(item: &'a mut Self::Item<'_, '_>) -> Self::Item<'a, 'a>; + /// Creates a new instance of this param's [`State`](SystemParam::State). fn init_state(world: &mut World) -> Self::State; @@ -316,16 +319,6 @@ pub unsafe trait SystemParam: Sized { /// This must only be implemented for [`SystemParam`] impls that exclusively read the World passed in to [`SystemParam::get_param`] pub unsafe trait ReadOnlySystemParam: SystemParam {} -/// A [`SystemParam`] whose lifetime can be shortened via -/// [`reborrow`](ReborrowSystemParam::reborrow)-ing. This should be implemented -/// for most system params, except in the case of non-covariant lifetimes. -pub trait ReborrowSystemParam: SystemParam { - /// Returns a `SystemParam` item with a smaller lifetime. - fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( - item: &'short mut Self::Item<'wlong, 'slong>, - ) -> Self::Item<'short, 'short>; -} - /// Shorthand way of accessing the associated type [`SystemParam::Item`] for a given [`SystemParam`]. pub type SystemParamItem<'w, 's, P> =

::Item<'w, 's>; @@ -341,6 +334,11 @@ unsafe impl SystemParam for Qu type State = QueryState; type Item<'w, 's> = Query<'w, 's, D, F>; + #[inline] + fn reborrow<'a>(item: &'a mut Self::Item<'_, '_>) -> Self::Item<'a, 'a> { + item.reborrow() + } + fn init_state(world: &mut World) -> Self::State { QueryState::new(world) } @@ -377,14 +375,6 @@ unsafe impl SystemParam for Qu } } -impl ReborrowSystemParam for Query<'_, '_, D, F> { - fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( - item: &'short mut Self::Item<'wlong, 'slong>, - ) -> Self::Item<'short, 'short> { - item.reborrow() - } -} - fn assert_component_access_compatibility( system_name: &DebugName, query_type: DebugName, @@ -407,12 +397,15 @@ fn assert_component_access_compatibility( // SAFETY: Relevant query ComponentId access is applied to SystemMeta. If // this Query conflicts with any prior access, a panic will occur. -unsafe impl<'a, 'b, D: QueryData + 'static, F: QueryFilter + 'static> SystemParam - for Single<'a, 'b, D, F> -{ +unsafe impl SystemParam for Single<'_, '_, D, F> { type State = QueryState; type Item<'w, 's> = Single<'w, 's, D, F>; + #[inline] + fn reborrow<'a>(item: &'a mut Self::Item<'_, '_>) -> Self::Item<'a, 'a> { + item.reborrow() + } + fn init_state(world: &mut World) -> Self::State { Query::init_state(world) } @@ -471,21 +464,11 @@ unsafe impl<'a, 'b, D: QueryData + 'static, F: QueryFilter + 'static> SystemPara } // SAFETY: QueryState is constrained to read-only fetches, so it only reads World. -unsafe impl<'a, 'b, D: ReadOnlyQueryData + 'static, F: QueryFilter + 'static> ReadOnlySystemParam - for Single<'a, 'b, D, F> +unsafe impl ReadOnlySystemParam + for Single<'_, '_, D, F> { } -impl<'a, 'b, D: ReborrowQueryData + 'static, F: QueryFilter + 'static> ReborrowSystemParam - for Single<'a, 'b, D, F> -{ - fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( - item: &'short mut Self::Item<'wlong, 'slong>, - ) -> Self::Item<'short, 'short> { - item.reborrow() - } -} - // SAFETY: Relevant query ComponentId access is applied to SystemMeta. If // this Query conflicts with any prior access, a panic will occur. unsafe impl SystemParam @@ -494,6 +477,11 @@ unsafe impl SystemParam type State = QueryState; type Item<'w, 's> = Populated<'w, 's, D, F>; + #[inline] + fn reborrow<'a>(item: &'a mut Self::Item<'_, '_>) -> Self::Item<'a, 'a> { + item.reborrow() + } + fn init_state(world: &mut World) -> Self::State { Query::init_state(world) } @@ -547,16 +535,6 @@ unsafe impl<'w, 's, D: ReadOnlyQueryData + 'static, F: QueryFilter + 'static> Re { } -impl ReborrowSystemParam - for Populated<'_, '_, D, F> -{ - fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( - item: &'short mut Self::Item<'wlong, 'slong>, - ) -> Self::Item<'short, 'short> { - item.reborrow() - } -} - /// A collection of potentially conflicting [`SystemParam`]s allowed by disjoint access. /// /// Allows systems to safely access and interact with up to 8 mutually exclusive [`SystemParam`]s, such as @@ -661,7 +639,7 @@ impl ReborrowSystemParam /// for message in set.p0().read() { /// // ... /// # let _message = message; -/// } +/// }https://doc.rust-lang.org/nomicon/subtyping.html /// set.p1().write(MyMessage::new()); /// /// let entities = set.p2().entities(); @@ -681,7 +659,7 @@ macro_rules! impl_param_set { ($(($index: tt, $param: ident, $fn_name: ident)),*) => { // SAFETY: All parameters are constrained to ReadOnlySystemParam, so World is only read unsafe impl<'w, 's, $($param,)*> ReadOnlySystemParam for ParamSet<'w, 's, ($($param,)*)> - where $($param: ReadOnlySystemParam,)* + where $($param: ReadOnlySystemParam + 'static,)* { } @@ -700,6 +678,13 @@ macro_rules! impl_param_set { type State = ($($param::State,)*); type Item<'w, 's> = ParamSet<'w, 's, ($($param,)*)>; + #[inline] + fn reborrow<'a>( + item: &'a mut Self::Item<'_, '_>, + ) -> Self::Item<'a, 'a> { + item.reborrow() + } + fn init_state(world: &mut World) -> Self::State { ($($param::init_state(world),)*) } @@ -771,10 +756,11 @@ macro_rules! impl_param_set { )* } - impl<'w, 's, $($param: SystemParam + 'static,)*> ParamSet<'w, 's, ($($param,)*)> + impl<'w, 's, $($param: SystemParam,)*> ParamSet<'w, 's, ($($param,)*)> { /// Returns a `ParamSet` with a smaller lifetime. /// This can be useful if you have an `&ParamSet` and need a `ParamSet`. + #[inline] fn reborrow(&mut self) -> ParamSet<'_, '_, ($($param,)*)> { ParamSet { param_states: self.param_states, @@ -784,31 +770,25 @@ macro_rules! impl_param_set { } } } - - // NOTE: the 'static bound isn't technically required here, but without it - // users can break `SystemParam` derives by introducing invariant lifetimes. - impl<'_w, '_s, $($param: SystemParam + 'static,)*> ReborrowSystemParam for ParamSet<'_w, '_s, ($($param,)*)> - { - fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( - item: &'short mut Self::Item<'wlong, 'slong>, - ) -> Self::Item<'short, 'short> { - item.reborrow() - } - } } } all_tuples_enumerated!(impl_param_set, 1, 8, P, p); // SAFETY: Res only reads a single World resource -unsafe impl<'a, T: Resource> ReadOnlySystemParam for Res<'a, T> {} +unsafe impl ReadOnlySystemParam for Res<'_, T> {} // SAFETY: Res ComponentId access is applied to SystemMeta. If this Res // conflicts with any prior access, a panic will occur. -unsafe impl<'a, T: Resource> SystemParam for Res<'a, T> { +unsafe impl SystemParam for Res<'_, T> { type State = ComponentId; type Item<'w, 's> = Res<'w, T>; + #[inline] + fn reborrow<'a>(item: &'a mut Self::Item<'_, '_>) -> Self::Item<'a, 'a> { + Res::clone(item) + } + fn init_state(world: &mut World) -> Self::State { world.components_registrator().register_resource::() } @@ -879,20 +859,17 @@ unsafe impl<'a, T: Resource> SystemParam for Res<'a, T> { } } -impl<'a, T: Resource> ReborrowSystemParam for Res<'a, T> { - fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( - item: &'short mut Self::Item<'wlong, 'slong>, - ) -> Self::Item<'short, 'short> { - item.reborrow() - } -} - // SAFETY: Res ComponentId access is applied to SystemMeta. If this Res // conflicts with any prior access, a panic will occur. -unsafe impl<'a, T: Resource> SystemParam for ResMut<'a, T> { +unsafe impl SystemParam for ResMut<'_, T> { type State = ComponentId; type Item<'w, 's> = ResMut<'w, T>; + #[inline] + fn reborrow<'a>(item: &'a mut Self::Item<'_, '_>) -> Self::Item<'a, 'a> { + item.reborrow() + } + fn init_state(world: &mut World) -> Self::State { world.components_registrator().register_resource::() } @@ -965,14 +942,6 @@ unsafe impl<'a, T: Resource> SystemParam for ResMut<'a, T> { } } -impl<'a, T: Resource> ReborrowSystemParam for ResMut<'a, T> { - fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( - item: &'short mut Self::Item<'wlong, 'slong>, - ) -> Self::Item<'short, 'short> { - item.reborrow() - } -} - /// SAFETY: only reads world unsafe impl<'w> ReadOnlySystemParam for &'w World {} @@ -981,6 +950,11 @@ unsafe impl SystemParam for &'_ World { type State = (); type Item<'w, 's> = &'w World; + #[inline] + fn reborrow<'a>(item: &'a mut Self::Item<'_, '_>) -> Self::Item<'a, 'a> { + *item + } + fn init_state(_world: &mut World) -> Self::State {} fn init_access( @@ -1013,19 +987,16 @@ unsafe impl SystemParam for &'_ World { } } -impl<'w> ReborrowSystemParam for &'w World { - fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( - item: &'short mut Self::Item<'wlong, 'slong>, - ) -> Self::Item<'short, 'short> { - *item - } -} - /// SAFETY: `DeferredWorld` can read all components and resources but cannot be used to gain any other mutable references. unsafe impl<'w> SystemParam for DeferredWorld<'w> { type State = (); type Item<'world, 'state> = DeferredWorld<'world>; + #[inline] + fn reborrow<'a>(item: &'a mut Self::Item<'_, '_>) -> Self::Item<'a, 'a> { + item.reborrow() + } + fn init_state(_world: &mut World) -> Self::State {} fn init_access( @@ -1052,14 +1023,6 @@ unsafe impl<'w> SystemParam for DeferredWorld<'w> { } } -impl<'w> ReborrowSystemParam for DeferredWorld<'w> { - fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( - item: &'short mut Self::Item<'wlong, 'slong>, - ) -> Self::Item<'short, 'short> { - item.reborrow() - } -} - /// A [`SystemParam`] that provides a system-private value of `T` that persists across system calls. /// /// The initial value is created by calling `T`'s [`FromWorld::from_world`] (or [`Default::default`] if `T: Default`). @@ -1182,6 +1145,7 @@ pub struct Local<'s, T: FromWorld + Send + 'static>(pub(crate) &'s mut T); impl<'s, T: FromWorld + Send + 'static> Local<'s, T> { /// Returns a [`Local`] with a shorter lifetime. /// This is useful if you have an `&mut Local` but want a `Local` + #[inline] pub fn reborrow(&mut self) -> Local<'_, T> { Local(self.0) } @@ -1231,10 +1195,15 @@ where } // SAFETY: only local state is accessed -unsafe impl<'a, T: FromWorld + Send + 'static> SystemParam for Local<'a, T> { +unsafe impl SystemParam for Local<'_, T> { type State = SyncCell; type Item<'w, 's> = Local<'s, T>; + #[inline] + fn reborrow<'a>(item: &'a mut Self::Item<'_, '_>) -> Self::Item<'a, 'a> { + item.reborrow() + } + fn init_state(world: &mut World) -> Self::State { SyncCell::new(T::from_world(world)) } @@ -1258,14 +1227,6 @@ unsafe impl<'a, T: FromWorld + Send + 'static> SystemParam for Local<'a, T> { } } -impl<'a, T: FromWorld + Send + 'static> ReborrowSystemParam for Local<'a, T> { - fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( - item: &'short mut Self::Item<'wlong, 'slong>, - ) -> Self::Item<'short, 'short> { - item.reborrow() - } -} - /// Types that can be used with [`Deferred`] in systems. /// This allows storing system-local data which is used to defer [`World`] mutations. /// @@ -1418,6 +1379,7 @@ impl<'a, T: SystemBuffer> DerefMut for Deferred<'a, T> { impl Deferred<'_, T> { /// Returns a [`Deferred`] with a smaller lifetime. /// This is useful if you have `&mut Deferred` but need `Deferred`. + #[inline] pub fn reborrow(&mut self) -> Deferred<'_, T> { Deferred(self.0) } @@ -1426,19 +1388,16 @@ impl Deferred<'_, T> { // SAFETY: Only local state is accessed. unsafe impl ReadOnlySystemParam for Deferred<'_, T> {} -impl ReborrowSystemParam for Deferred<'_, T> { - fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( - item: &'short mut Self::Item<'wlong, 'slong>, - ) -> Self::Item<'short, 'short> { - item.reborrow() - } -} - // SAFETY: Only local state is accessed. unsafe impl SystemParam for Deferred<'_, T> { type State = SyncCell; type Item<'w, 's> = Deferred<'s, T>; + #[inline] + fn reborrow<'a>(item: &'a mut Self::Item<'_, '_>) -> Self::Item<'a, 'a> { + item.reborrow() + } + fn init_state(world: &mut World) -> Self::State { SyncCell::new(T::from_world(world)) } @@ -1479,6 +1438,11 @@ unsafe impl SystemParam for ExclusiveMarker { type State = (); type Item<'w, 's> = Self; + #[inline] + fn reborrow<'a>(_item: &'a mut Self::Item<'_, '_>) -> Self::Item<'a, 'a> { + Self(PhantomData) + } + #[inline] fn init_state(_world: &mut World) -> Self::State {} @@ -1513,6 +1477,11 @@ unsafe impl SystemParam for NonSendMarker { type State = (); type Item<'w, 's> = Self; + #[inline] + fn reborrow<'a>(_item: &'a mut Self::Item<'_, '_>) -> Self::Item<'a, 'a> { + NonSendMarker(PhantomData) + } + #[inline] fn init_state(_world: &mut World) -> Self::State {} @@ -1539,23 +1508,20 @@ unsafe impl SystemParam for NonSendMarker { // SAFETY: Does not read any world state unsafe impl ReadOnlySystemParam for NonSendMarker {} -impl ReborrowSystemParam for NonSendMarker { - fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( - _item: &'short mut Self::Item<'wlong, 'slong>, - ) -> Self::Item<'short, 'short> { - Self(PhantomData) - } -} - // SAFETY: Only reads a single World non-send resource -unsafe impl<'w, T> ReadOnlySystemParam for NonSend<'w, T> {} +unsafe impl ReadOnlySystemParam for NonSend<'_, T> {} // SAFETY: NonSendComponentId access is applied to SystemMeta. If this // NonSend conflicts with any prior access, a panic will occur. -unsafe impl<'a, T: 'static> SystemParam for NonSend<'a, T> { +unsafe impl SystemParam for NonSend<'_, T> { type State = ComponentId; type Item<'w, 's> = NonSend<'w, T>; + #[inline] + fn reborrow<'a>(item: &'a mut Self::Item<'_, '_>) -> Self::Item<'a, 'a> { + *item + } + fn init_state(world: &mut World) -> Self::State { world.components_registrator().register_non_send::() } @@ -1621,20 +1587,17 @@ unsafe impl<'a, T: 'static> SystemParam for NonSend<'a, T> { } } -impl<'a, T: 'static> ReborrowSystemParam for NonSend<'a, T> { - fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( - item: &'short mut Self::Item<'wlong, 'slong>, - ) -> Self::Item<'short, 'short> { - item.reborrow() - } -} - // SAFETY: NonSendMut ComponentId access is applied to SystemMeta. If this // NonSendMut conflicts with any prior access, a panic will occur. -unsafe impl<'a, T: 'static> SystemParam for NonSendMut<'a, T> { +unsafe impl SystemParam for NonSendMut<'_, T> { type State = ComponentId; type Item<'w, 's> = NonSendMut<'w, T>; + #[inline] + fn reborrow<'a>(item: &'a mut Self::Item<'_, '_>) -> Self::Item<'a, 'a> { + item.reborrow() + } + fn init_state(world: &mut World) -> Self::State { world.components_registrator().register_non_send::() } @@ -1703,22 +1666,19 @@ unsafe impl<'a, T: 'static> SystemParam for NonSendMut<'a, T> { } } -impl<'a, T: 'static> ReborrowSystemParam for NonSendMut<'a, T> { - fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( - item: &'short mut Self::Item<'wlong, 'slong>, - ) -> Self::Item<'short, 'short> { - item.reborrow() - } -} - // SAFETY: Only reads World archetypes -unsafe impl<'a> ReadOnlySystemParam for &'a Archetypes {} +unsafe impl ReadOnlySystemParam for &'_ Archetypes {} // SAFETY: no component value access -unsafe impl<'a> SystemParam for &'a Archetypes { +unsafe impl SystemParam for &'_ Archetypes { type State = (); type Item<'w, 's> = &'w Archetypes; + #[inline] + fn reborrow<'a>(item: &'a mut Self::Item<'_, '_>) -> Self::Item<'a, 'a> { + *item + } + fn init_state(_world: &mut World) -> Self::State {} fn init_access( @@ -1740,22 +1700,19 @@ unsafe impl<'a> SystemParam for &'a Archetypes { } } -impl<'a> ReborrowSystemParam for &'a Archetypes { - fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( - item: &'short mut Self::Item<'wlong, 'slong>, - ) -> Self::Item<'short, 'short> { - *item - } -} - // SAFETY: Only reads World components -unsafe impl<'a> ReadOnlySystemParam for &'a Components {} +unsafe impl ReadOnlySystemParam for &'_ Components {} // SAFETY: no component value access -unsafe impl<'a> SystemParam for &'a Components { +unsafe impl SystemParam for &'_ Components { type State = (); type Item<'w, 's> = &'w Components; + #[inline] + fn reborrow<'a>(item: &'a mut Self::Item<'_, '_>) -> Self::Item<'a, 'a> { + *item + } + fn init_state(_world: &mut World) -> Self::State {} fn init_access( @@ -1777,22 +1734,19 @@ unsafe impl<'a> SystemParam for &'a Components { } } -impl<'a> ReborrowSystemParam for &'a Components { - fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( - item: &'short mut Self::Item<'wlong, 'slong>, - ) -> Self::Item<'short, 'short> { - *item - } -} - // SAFETY: Only reads World entities -unsafe impl<'a> ReadOnlySystemParam for &'a Entities {} +unsafe impl ReadOnlySystemParam for &'_ Entities {} // SAFETY: no component value access -unsafe impl<'a> SystemParam for &'a Entities { +unsafe impl SystemParam for &'_ Entities { type State = (); type Item<'w, 's> = &'w Entities; + #[inline] + fn reborrow<'a>(item: &'a mut Self::Item<'_, '_>) -> Self::Item<'a, 'a> { + *item + } + fn init_state(_world: &mut World) -> Self::State {} fn init_access( @@ -1814,22 +1768,19 @@ unsafe impl<'a> SystemParam for &'a Entities { } } -impl<'a> ReborrowSystemParam for &'a Entities { - fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( - item: &'short mut Self::Item<'wlong, 'slong>, - ) -> Self::Item<'short, 'short> { - *item - } -} - // SAFETY: Only reads World entities -unsafe impl<'a> ReadOnlySystemParam for &'a EntityAllocator {} +unsafe impl ReadOnlySystemParam for &'_ EntityAllocator {} // SAFETY: no component value access -unsafe impl<'a> SystemParam for &'a EntityAllocator { +unsafe impl SystemParam for &'_ EntityAllocator { type State = (); type Item<'w, 's> = &'w EntityAllocator; + #[inline] + fn reborrow<'a>(item: &'a mut Self::Item<'_, '_>) -> Self::Item<'a, 'a> { + *item + } + fn init_state(_world: &mut World) -> Self::State {} fn init_access( @@ -1852,13 +1803,18 @@ unsafe impl<'a> SystemParam for &'a EntityAllocator { } // SAFETY: Only reads World bundles -unsafe impl<'a> ReadOnlySystemParam for &'a Bundles {} +unsafe impl ReadOnlySystemParam for &'_ Bundles {} // SAFETY: no component value access -unsafe impl<'a> SystemParam for &'a Bundles { +unsafe impl SystemParam for &'_ Bundles { type State = (); type Item<'w, 's> = &'w Bundles; + #[inline] + fn reborrow<'a>(item: &'a mut Self::Item<'_, '_>) -> Self::Item<'a, 'a> { + *item + } + fn init_state(_world: &mut World) -> Self::State {} fn init_access( @@ -1880,14 +1836,6 @@ unsafe impl<'a> SystemParam for &'a Bundles { } } -impl<'a> ReborrowSystemParam for &'a Bundles { - fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( - item: &'short mut Self::Item<'wlong, 'slong>, - ) -> Self::Item<'short, 'short> { - *item - } -} - /// A [`SystemParam`] that reads the previous and current change ticks of the system. /// /// A system's change ticks are updated each time it runs: @@ -1925,6 +1873,11 @@ unsafe impl SystemParam for SystemChangeTick { type State = (); type Item<'w, 's> = SystemChangeTick; + #[inline] + fn reborrow<'a>(item: &'a mut Self::Item<'_, '_>) -> Self::Item<'a, 'a> { + *item + } + fn init_state(_world: &mut World) -> Self::State {} fn init_access( @@ -1949,20 +1902,16 @@ unsafe impl SystemParam for SystemChangeTick { } } -impl ReborrowSystemParam for SystemChangeTick { - fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( - item: &'short mut Self::Item<'wlong, 'slong>, - ) -> Self::Item<'short, 'short> { - *item - } -} - // SAFETY: Delegates to `T`, which ensures the safety requirements are met unsafe impl SystemParam for Option { type State = T::State; - type Item<'world, 'state> = Option>; + #[inline] + fn reborrow<'a>(item: &'a mut Self::Item<'_, '_>) -> Self::Item<'a, 'a> { + item.as_mut().map(T::reborrow) + } + fn init_state(world: &mut World) -> Self::State { T::init_state(world) } @@ -2000,20 +1949,16 @@ unsafe impl SystemParam for Option { // SAFETY: Delegates to `T`, which ensures the safety requirements are met unsafe impl ReadOnlySystemParam for Option {} -impl ReborrowSystemParam for Option { - fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( - item: &'short mut Self::Item<'wlong, 'slong>, - ) -> Self::Item<'short, 'short> { - item.as_mut().map(T::reborrow) - } -} - // SAFETY: Delegates to `T`, which ensures the safety requirements are met unsafe impl SystemParam for Result { type State = T::State; - type Item<'world, 'state> = Result, SystemParamValidationError>; + #[inline] + fn reborrow<'a>(item: &'a mut Self::Item<'_, '_>) -> Self::Item<'a, 'a> { + item.as_mut().map(T::reborrow).map_err(|err| err.clone()) + } + fn init_state(world: &mut World) -> Self::State { T::init_state(world) } @@ -2050,14 +1995,6 @@ unsafe impl SystemParam for Result ReadOnlySystemParam for Result {} -impl ReborrowSystemParam for Result { - fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( - item: &'short mut Self::Item<'wlong, 'slong>, - ) -> Self::Item<'short, 'short> { - item.as_mut().map(T::reborrow).map_err(|err| err.clone()) - } -} - /// A [`SystemParam`] that wraps another parameter and causes its system to skip instead of failing when the parameter is invalid. /// /// # Example @@ -2117,6 +2054,11 @@ unsafe impl SystemParam for If { type Item<'world, 'state> = If>; + #[inline] + fn reborrow<'a>(item: &'a mut Self::Item<'_, '_>) -> Self::Item<'a, 'a> { + If(T::reborrow(&mut item.0)) + } + fn init_state(world: &mut World) -> Self::State { T::init_state(world) } @@ -2161,14 +2103,6 @@ unsafe impl SystemParam for If { } } -impl ReborrowSystemParam for If { - fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( - item: &'short mut Self::Item<'wlong, 'slong>, - ) -> Self::Item<'short, 'short> { - If(T::reborrow(&mut item.0)) - } -} - // SAFETY: Delegates to `T`, which ensures the safety requirements are met unsafe impl ReadOnlySystemParam for If {} @@ -2176,9 +2110,13 @@ unsafe impl ReadOnlySystemParam for If {} // If any one conflicts, it will panic. unsafe impl SystemParam for Vec { type State = Vec; - type Item<'world, 'state> = Vec>; + #[inline] + fn reborrow<'a>(item: &'a mut Self::Item<'_, '_>) -> Self::Item<'a, 'a> { + item.iter_mut().map(T::reborrow).collect() + } + fn init_state(_world: &mut World) -> Self::State { Vec::new() } @@ -2235,24 +2173,18 @@ unsafe impl SystemParam for Vec { } } -//TODO: should we implement reborrow for stuff that requires cloning? It'd still be faster than -//getting a new param each time I guess. -impl ReborrowSystemParam for Vec { - fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( - item: &'short mut Self::Item<'wlong, 'slong>, - ) -> Self::Item<'short, 'short> { - item.iter_mut().map(T::reborrow).collect() - } -} - // SAFETY: Registers access for each element of `state`. // If any one conflicts with a previous parameter, // the call passing a copy of the current access will panic. unsafe impl SystemParam for ParamSet<'_, '_, Vec> { type State = Vec; - type Item<'world, 'state> = ParamSet<'world, 'state, Vec>; + #[inline] + fn reborrow<'a>(item: &'a mut Self::Item<'_, '_>) -> Self::Item<'a, 'a> { + item.reborrow() + } + fn init_state(_world: &mut World) -> Self::State { Vec::new() } @@ -2305,17 +2237,10 @@ unsafe impl SystemParam for ParamSet<'_, '_, Vec> { } } -impl ReborrowSystemParam for ParamSet<'_, '_, Vec

> { - fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( - item: &'short mut Self::Item<'wlong, 'slong>, - ) -> Self::Item<'short, 'short> { - item.reborrow() - } -} - impl ParamSet<'_, '_, Vec> { /// Returns a `ParamSet` with a smaller lifetime. /// This can be useful if you have an `&ParamSet` and need a `ParamSet`. + #[inline] fn reborrow(&mut self) -> ParamSet<'_, '_, Vec> { ParamSet { param_states: self.param_states, @@ -2384,6 +2309,10 @@ macro_rules! impl_system_param_tuple { type State = ($($param::State,)*); type Item<'w, 's> = ($($param::Item::<'w, 's>,)*); + #[inline] + fn reborrow<'a>(($($item,)*): &'a mut Self::Item<'_, '_>) -> Self::Item<'a, 'a> { + ($($param::reborrow($item),)*) + } #[inline] fn init_state(world: &mut World) -> Self::State { @@ -2433,20 +2362,6 @@ macro_rules! impl_system_param_tuple { ($($param::get_param($param, system_meta, world, change_tick),)*) } } - - #[expect( - clippy::allow_attributes, - reason = "This is in a macro, and as such, the below lints may not always apply." - )] - - $(#[$meta])* - impl<$($param: ReborrowSystemParam),*> ReborrowSystemParam for ($($param,)*) { - fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( - ($($item,)*): &'short mut Self::Item<'wlong, 'slong>, - ) -> Self::Item<'short, 'short> { - ($($param::reborrow($item),)*) - } - } }; } @@ -2571,6 +2486,11 @@ unsafe impl SystemParam for StaticSystemParam<'_, '_, type State = P::State; type Item<'world, 'state> = StaticSystemParam<'world, 'state, P>; + #[inline] + fn reborrow<'a>(item: &'a mut Self::Item<'_, '_>) -> Self::Item<'a, 'a> { + StaticSystemParam(P::reborrow(&mut item.0)) + } + fn init_state(world: &mut World) -> Self::State { P::init_state(world) } @@ -2613,19 +2533,16 @@ unsafe impl SystemParam for StaticSystemParam<'_, '_, } } -impl ReborrowSystemParam for StaticSystemParam<'_, '_, P> { - fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( - item: &'short mut Self::Item<'wlong, 'slong>, - ) -> Self::Item<'short, 'short> { - StaticSystemParam(P::reborrow(&mut item.0)) - } -} - // SAFETY: No world access. unsafe impl SystemParam for PhantomData { type State = (); type Item<'world, 'state> = Self; + #[inline] + fn reborrow<'a>(_item: &'a mut Self::Item<'_, '_>) -> Self::Item<'a, 'a> { + PhantomData + } + fn init_state(_world: &mut World) -> Self::State {} fn init_access( @@ -2650,13 +2567,6 @@ unsafe impl SystemParam for PhantomData { // SAFETY: No world access. unsafe impl ReadOnlySystemParam for PhantomData {} -impl ReborrowSystemParam for PhantomData { - fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( - _item: &'short mut Self::Item<'wlong, 'slong>, - ) -> Self::Item<'short, 'short> { - PhantomData - } -} /// A [`SystemParam`] with a type that can be configured at runtime. /// /// To be useful, this must be configured using a [`DynParamBuilder`](crate::system::DynParamBuilder) to build the system using a [`SystemParamBuilder`](crate::prelude::SystemParamBuilder). @@ -2800,6 +2710,7 @@ impl<'w, 's> DynSystemParam<'w, 's> { /// Returns a `Res<>` with a smaller lifetime. /// This is useful if you have `&Res`, but you need a `Res`. + #[inline] pub fn reborrow(&mut self) -> DynSystemParam<'_, '_> { DynSystemParam { state: self.state, @@ -2916,9 +2827,13 @@ impl DynParamState for ParamState { // SAFETY: Delegates to the wrapped parameter, which ensures the safety requirements are met unsafe impl SystemParam for DynSystemParam<'_, '_> { type State = DynSystemParamState; - type Item<'world, 'state> = DynSystemParam<'world, 'state>; + #[inline] + fn reborrow<'a>(item: &'a mut Self::Item<'_, '_>) -> Self::Item<'a, 'a> { + item.reborrow() + } + fn init_state(_world: &mut World) -> Self::State { DynSystemParamState::new::<()>(()) } @@ -2967,21 +2882,17 @@ unsafe impl SystemParam for DynSystemParam<'_, '_> { } } -impl ReborrowSystemParam for DynSystemParam<'_, '_> { - fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( - item: &'short mut Self::Item<'wlong, 'slong>, - ) -> Self::Item<'short, 'short> { - item.reborrow() - } -} - // SAFETY: Resource ComponentId access is applied to the access. If this FilteredResources // conflicts with any prior access, a panic will occur. unsafe impl SystemParam for FilteredResources<'_, '_> { type State = Access; - type Item<'world, 'state> = FilteredResources<'world, 'state>; + #[inline] + fn reborrow<'a>(item: &'a mut Self::Item<'_, '_>) -> Self::Item<'a, 'a> { + *item + } + fn init_state(_world: &mut World) -> Self::State { Access::new() } @@ -3021,14 +2932,6 @@ unsafe impl SystemParam for FilteredResources<'_, '_> { } } -impl ReborrowSystemParam for FilteredResources<'_, '_> { - fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( - item: &'short mut Self::Item<'wlong, 'slong>, - ) -> Self::Item<'short, 'short> { - *item - } -} - // SAFETY: FilteredResources only reads resources. unsafe impl ReadOnlySystemParam for FilteredResources<'_, '_> {} @@ -3039,6 +2942,11 @@ unsafe impl SystemParam for FilteredResourcesMut<'_, '_> { type Item<'world, 'state> = FilteredResourcesMut<'world, 'state>; + #[inline] + fn reborrow<'a>(item: &'a mut Self::Item<'_, '_>) -> Self::Item<'a, 'a> { + item.reborrow() + } + fn init_state(_world: &mut World) -> Self::State { Access::new() } @@ -3086,14 +2994,6 @@ unsafe impl SystemParam for FilteredResourcesMut<'_, '_> { } } -impl ReborrowSystemParam for FilteredResourcesMut<'_, '_> { - fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( - item: &'short mut Self::Item<'wlong, 'slong>, - ) -> Self::Item<'short, 'short> { - item.reborrow() - } -} - /// An error that occurs when a system parameter is not valid, /// used by system executors to determine what to do with a system. /// diff --git a/crates/bevy_ecs/src/world/identifier.rs b/crates/bevy_ecs/src/world/identifier.rs index 98912f8080fa2..bcaa02e51a507 100644 --- a/crates/bevy_ecs/src/world/identifier.rs +++ b/crates/bevy_ecs/src/world/identifier.rs @@ -2,9 +2,7 @@ use crate::{ change_detection::Tick, query::FilteredAccessSet, storage::SparseSetIndex, - system::{ - ExclusiveSystemParam, ReadOnlySystemParam, ReborrowSystemParam, SystemMeta, SystemParam, - }, + system::{ExclusiveSystemParam, ReadOnlySystemParam, SystemMeta, SystemParam}, world::{FromWorld, World}, }; use bevy_platform::sync::atomic::{AtomicUsize, Ordering}; @@ -56,6 +54,10 @@ unsafe impl SystemParam for WorldId { type Item<'world, 'state> = WorldId; + fn reborrow<'a>(item: &'a mut Self::Item<'_, '_>) -> Self::Item<'a, 'a> { + *item + } + fn init_state(_: &mut World) -> Self::State {} fn init_access( @@ -77,14 +79,6 @@ unsafe impl SystemParam for WorldId { } } -impl ReborrowSystemParam for WorldId { - fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( - item: &'short mut Self::Item<'wlong, 'slong>, - ) -> Self::Item<'short, 'short> { - *item - } -} - impl ExclusiveSystemParam for WorldId { type State = WorldId; type Item<'s> = WorldId; diff --git a/crates/bevy_gizmos/src/gizmos.rs b/crates/bevy_gizmos/src/gizmos.rs index 6de36392c02f8..9840bee4aeeb4 100644 --- a/crates/bevy_gizmos/src/gizmos.rs +++ b/crates/bevy_gizmos/src/gizmos.rs @@ -13,8 +13,8 @@ use bevy_ecs::{ query::FilteredAccessSet, resource::Resource, system::{ - Deferred, ReadOnlySystemParam, ReborrowSystemParam, Res, SystemBuffer, SystemMeta, - SystemParam, SystemParamValidationError, + Deferred, ReadOnlySystemParam, Res, SystemBuffer, SystemMeta, SystemParam, + SystemParamValidationError, }, world::{unsafe_world_cell::UnsafeWorldCell, World}, }; @@ -216,6 +216,10 @@ where type State = GizmosFetchState; type Item<'w, 's> = Gizmos<'w, 's, Config, Clear>; + fn reborrow<'a>(item: &'a mut Self::Item<'_, '_>) -> Self::Item<'a, 'a> { + item.reborrow() + } + fn init_state(world: &mut World) -> Self::State { GizmosFetchState { state: GizmosState::::init_state(world), @@ -283,18 +287,6 @@ where } } -impl ReborrowSystemParam for Gizmos<'_, '_, Config, Clear> -where - Config: GizmoConfigGroup, - Clear: 'static + Send + Sync, -{ - fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( - item: &'short mut Self::Item<'wlong, 'slong>, - ) -> Self::Item<'short, 'short> { - item.reborrow() - } -} - #[expect( unsafe_code, reason = "We cannot implement ReadOnlySystemParam without using unsafe code." diff --git a/crates/bevy_render/src/extract_param.rs b/crates/bevy_render/src/extract_param.rs index 65a9518564434..a9aedc77ce776 100644 --- a/crates/bevy_render/src/extract_param.rs +++ b/crates/bevy_render/src/extract_param.rs @@ -4,8 +4,8 @@ use bevy_ecs::{ prelude::*, query::FilteredAccessSet, system::{ - ReadOnlySystemParam, ReborrowSystemParam, SystemMeta, SystemParam, SystemParamItem, - SystemParamValidationError, SystemState, + ReadOnlySystemParam, SystemMeta, SystemParam, SystemParamItem, SystemParamValidationError, + SystemState, }, world::unsafe_world_cell::UnsafeWorldCell, }; @@ -72,6 +72,10 @@ where type State = ExtractState

; type Item<'w, 's> = Extract<'w, 's, P>; + fn reborrow<'a>(item: &'a mut Self::Item<'_, '_>) -> Self::Item<'a, 'a> { + item.reborrow() + } + fn init_state(world: &mut World) -> Self::State { let mut main_world = world.resource_mut::(); ExtractState { @@ -141,17 +145,7 @@ where } } -impl ReborrowSystemParam for Extract<'_, '_, P> { - fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( - item: &'short mut Self::Item<'wlong, 'slong>, - ) -> Self::Item<'short, 'short> { - Extract { - item: P::reborrow(&mut item.item), - } - } -} - -impl<'w, 's, P: ReborrowSystemParam + ReadOnlySystemParam> Extract<'w, 's, P> { +impl<'w, 's, P: ReadOnlySystemParam> Extract<'w, 's, P> { pub fn reborrow(&mut self) -> Extract<'_, '_, P> { Extract { item: P::reborrow(&mut self.item), diff --git a/release-content/migration-guides/reborrow_methods.md b/release-content/migration-guides/reborrow_methods.md new file mode 100644 index 0000000000000..182e4c2588a1f --- /dev/null +++ b/release-content/migration-guides/reborrow_methods.md @@ -0,0 +1,20 @@ +--- +title: ECS reborrowing methods +pull_requests: [22025] +--- + +Bevy 0.18 adds a new `reborrow` method to `QueryData` and `SystemParam`, which +enables shortening the lifetime of a system param/query item. + +```rust +fn reborrow<'a>(item: &'a mut Self::Item<'_, '_>) -> Self::Item<'a, 'a>; +``` + +Since most implementations will have covariant lifetimes, this should be +an easy method to add. However, there's a couple narrow exceptions. + +If you have a `ParamSet` in a custom system param that looks like +`ParamSet<'w, 's, InnerParam<'w, 's>>`, this is actually *invariant* over +the lifetimes `'w` and `'s`, so it's impossible to implement `reborrow` +for the custom param. Instead, you should write the inner param's lifetimes +as `'static`. For more info on lifetime variance, see the [nomicon](https://doc.rust-lang.org/nomicon/subtyping.html). diff --git a/release-content/migration-guides/reborrow_traits.md b/release-content/migration-guides/reborrow_traits.md deleted file mode 100644 index 533697face64d..0000000000000 --- a/release-content/migration-guides/reborrow_traits.md +++ /dev/null @@ -1,30 +0,0 @@ ---- -title: ECS reborrowing traits -pull_requests: [22025] ---- - -Bevy 0.18 adds a new `reborrow` method to `QueryData`, which enables shortening the lifetime of a query item. - -```rust -fn reborrow<'a>(item: &'a mut Self::Item<'_, '_>) -> Self::Item<'a, 'a>; -``` - -Since `QueryData` implementers already have to be covariant over their lifetimes, -this shouldn't make the trait any harder to implement. For most read-only query -data, the method can be implemented with a simple deref: `*item`. - -Bevy 0.18 adds a few new traits to the ECS family: `ReborrowQueryData` and `ReborrowSystemParam`, -which allow for shortening the lifetime of a borrowed query item or system param respectively. -While not a breaking change, they're recommended to implement for most custom types where possible. - -```rust -/// A [`SystemParam`] whose lifetime can be shortened via -/// [`reborrow`](ReborrowSystemParam::reborrow)-ing. This should be implemented -/// for most system params, except in the case of non-covariant lifetimes. -pub trait ReborrowSystemParam: SystemParam { - /// Returns a `SystemParam` item with a smaller lifetime. - fn reborrow<'wlong: 'short, 'slong: 'short, 'short>( - item: &'short mut Self::Item<'wlong, 'slong>, - ) -> Self::Item<'short, 'short>; -} -```