Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion crates/bevy_ecs/macros/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
51 changes: 49 additions & 2 deletions crates/bevy_ecs/macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand All @@ -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,
Expand All @@ -253,6 +264,17 @@ fn derive_system_param_impl(
.collect::<Vec<_>>();
let field_members = fields.members().collect::<Vec<_>>();
let field_types = fields.iter().map(|f| &f.ty).collect::<Vec<_>>();
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::<Vec<_>>();

let field_validation_names = fields.members().map(|m| format!("::{}", quote! { #m }));
let mut field_validation_messages = Vec::with_capacity(fields.len());
Expand Down Expand Up @@ -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<Item<'w, 's> = #field_type>));
}

let fields_alias =
ensure_no_collision(format_ident!("__StructFieldsAlias"), token_stream.clone());

Expand Down Expand Up @@ -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
};

Expand Down
32 changes: 32 additions & 0 deletions crates/bevy_ecs/macros/src/query_data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,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::QueryData<ReadOnly: #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
Expand Down Expand Up @@ -346,6 +362,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
Expand Down
47 changes: 47 additions & 0 deletions crates/bevy_ecs/src/change_detection/params.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<ResMut<'w, T>> for Res<'w, T> {
Expand Down Expand Up @@ -243,6 +258,23 @@ impl<'w, T> From<NonSendMut<'w, T>> for NonSend<'w, T> {
}
}

impl<'w, T> NonSend<'w, T> {
/// Returns a `NonSend<>` with a smaller lifetime.
/// This is useful if you have `&NonSend<T>`, but you need a `NonSend<T>`.
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
Expand Down Expand Up @@ -360,6 +392,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<T>`, but you need a `Ref<T>`.
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>
Expand Down
12 changes: 8 additions & 4 deletions crates/bevy_ecs/src/change_detection/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
/// <T>`, but you need a `Mut<T>`.
pub fn reborrow(&mut self) -> Mut<'_, $target> {
Mut {
/// <T>`, but you need a `
#[doc = stringify!($name)]
/// <T>`.
pub fn reborrow(&mut self) -> $name<'_, $target> {
$name {
value: self.value,
ticks: ComponentTicksMut {
added: self.ticks.added,
Expand Down
10 changes: 9 additions & 1 deletion crates/bevy_ecs/src/lifecycle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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},
};

Expand Down Expand Up @@ -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
}
}
Loading
Loading