mirror of
https://github.com/bevyengine/bevy
synced 2025-02-18 06:58:34 +00:00
Make the SystemParam
derive macro more flexible (#6694)
# Objective Currently, the `SystemParam` derive forces you to declare the lifetime parameters `<'w, 's>`, even if you don't use them. If you don't follow this structure, the error message is quite nasty. ### Example (before): ```rust #[derive(SystemParam)] pub struct EventWriter<'w, 's, E: Event> { events: ResMut<'w, Events<E>>, // The derive forces us to declare the `'s` lifetime even though we don't use it, // so we have to add this `PhantomData` to please rustc. #[system_param(ignore)] _marker: PhantomData<&'s ()>, } ``` ## Solution * Allow the user to omit either lifetime. * Emit a descriptive error if any lifetimes used are invalid. ### Example (after): ```rust #[derive(SystemParam)] pub struct EventWriter<'w, E: Event> { events: ResMut<'w, Events<E>>, } ``` --- ## Changelog * The `SystemParam` derive is now more flexible, allowing you to omit unused lifetime parameters.
This commit is contained in:
parent
c55d553606
commit
05b498a224
3 changed files with 52 additions and 21 deletions
|
@ -373,7 +373,25 @@ pub fn derive_system_param(input: TokenStream) -> TokenStream {
|
||||||
}
|
}
|
||||||
|
|
||||||
let generics = ast.generics;
|
let generics = ast.generics;
|
||||||
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
|
|
||||||
|
// Emit an error if there's any unrecognized lifetime names.
|
||||||
|
for lt in generics.lifetimes() {
|
||||||
|
let ident = <.lifetime.ident;
|
||||||
|
let w = format_ident!("w");
|
||||||
|
let s = format_ident!("s");
|
||||||
|
if ident != &w && ident != &s {
|
||||||
|
return syn::Error::new_spanned(
|
||||||
|
lt,
|
||||||
|
r#"invalid lifetime name: expected `'w` or `'s`
|
||||||
|
'w -- refers to data stored in the World.
|
||||||
|
's -- refers to data stored in the SystemParam's state.'"#,
|
||||||
|
)
|
||||||
|
.into_compile_error()
|
||||||
|
.into();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let (_impl_generics, ty_generics, where_clause) = generics.split_for_impl();
|
||||||
|
|
||||||
let lifetimeless_generics: Vec<_> = generics
|
let lifetimeless_generics: Vec<_> = generics
|
||||||
.params
|
.params
|
||||||
|
@ -404,10 +422,16 @@ pub fn derive_system_param(input: TokenStream) -> TokenStream {
|
||||||
// The struct can still be accessed via SystemParam::Fetch, e.g. EventReaderState can be accessed via
|
// The struct can still be accessed via SystemParam::Fetch, e.g. EventReaderState can be accessed via
|
||||||
// <EventReader<'static, 'static, T> as SystemParam>::Fetch
|
// <EventReader<'static, 'static, T> as SystemParam>::Fetch
|
||||||
const _: () = {
|
const _: () = {
|
||||||
impl #impl_generics #path::system::SystemParam for #struct_name #ty_generics #where_clause {
|
impl<'w, 's, #punctuated_generics> #path::system::SystemParam for #struct_name #ty_generics #where_clause {
|
||||||
type Fetch = FetchState <(#(<#field_types as #path::system::SystemParam>::Fetch,)*), #punctuated_generic_idents>;
|
type Fetch = State<'w, 's, #punctuated_generic_idents>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
type State<'w, 's, #punctuated_generic_idents> = FetchState<
|
||||||
|
(#(<#field_types as #path::system::SystemParam>::Fetch,)*),
|
||||||
|
#punctuated_generic_idents
|
||||||
|
>;
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
#fetch_struct_visibility struct FetchState <TSystemParamState, #punctuated_generic_idents> {
|
#fetch_struct_visibility struct FetchState <TSystemParamState, #punctuated_generic_idents> {
|
||||||
state: TSystemParamState,
|
state: TSystemParamState,
|
||||||
|
@ -431,7 +455,7 @@ pub fn derive_system_param(input: TokenStream) -> TokenStream {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl #impl_generics #path::system::SystemParamFetch<'w, 's> for FetchState <(#(<#field_types as #path::system::SystemParam>::Fetch,)*), #punctuated_generic_idents> #where_clause {
|
impl<'w, 's, #punctuated_generics> #path::system::SystemParamFetch<'w, 's> for State<'w, 's, #punctuated_generic_idents> #where_clause {
|
||||||
type Item = #struct_name #ty_generics;
|
type Item = #struct_name #ty_generics;
|
||||||
unsafe fn get_param(
|
unsafe fn get_param(
|
||||||
state: &'s mut Self,
|
state: &'s mut Self,
|
||||||
|
|
|
@ -295,13 +295,11 @@ impl<'w, 's, E: Event> EventReader<'w, 's, E> {
|
||||||
/// ```
|
/// ```
|
||||||
/// Note that this is considered *non-idiomatic*, and should only be used when `EventWriter` will not work.
|
/// Note that this is considered *non-idiomatic*, and should only be used when `EventWriter` will not work.
|
||||||
#[derive(SystemParam)]
|
#[derive(SystemParam)]
|
||||||
pub struct EventWriter<'w, 's, E: Event> {
|
pub struct EventWriter<'w, E: Event> {
|
||||||
events: ResMut<'w, Events<E>>,
|
events: ResMut<'w, Events<E>>,
|
||||||
#[system_param(ignore)]
|
|
||||||
marker: PhantomData<&'s usize>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'w, 's, E: Event> EventWriter<'w, 's, E> {
|
impl<'w, E: Event> EventWriter<'w, E> {
|
||||||
/// Sends an `event`. [`EventReader`]s can then read the event.
|
/// Sends an `event`. [`EventReader`]s can then read the event.
|
||||||
/// See [`Events`] for details.
|
/// See [`Events`] for details.
|
||||||
pub fn send(&mut self, event: E) {
|
pub fn send(&mut self, event: E) {
|
||||||
|
|
|
@ -33,19 +33,15 @@ use std::{
|
||||||
/// See the *Generic `SystemParam`s* section for details and workarounds of the probable
|
/// See the *Generic `SystemParam`s* section for details and workarounds of the probable
|
||||||
/// cause if this derive causes an error to be emitted.
|
/// cause if this derive causes an error to be emitted.
|
||||||
///
|
///
|
||||||
///
|
/// Derived `SystemParam` structs may have two lifetimes: `'w` for data stored in the [`World`],
|
||||||
/// The struct for which `SystemParam` is derived must (currently) have exactly
|
/// and `'s` for data stored in the parameter's state.
|
||||||
/// two lifetime parameters.
|
|
||||||
/// The first is the lifetime of the world, and the second the lifetime
|
|
||||||
/// of the parameter's state.
|
|
||||||
///
|
///
|
||||||
/// ## Attributes
|
/// ## Attributes
|
||||||
///
|
///
|
||||||
/// `#[system_param(ignore)]`:
|
/// `#[system_param(ignore)]`:
|
||||||
/// Can be added to any field in the struct. Fields decorated with this attribute
|
/// Can be added to any field in the struct. Fields decorated with this attribute
|
||||||
/// will be created with the default value upon realisation.
|
/// will be created with the default value upon realisation.
|
||||||
/// This is most useful for `PhantomData` fields, to ensure that the required lifetimes are
|
/// This is most useful for `PhantomData` fields, such as markers for generic types.
|
||||||
/// used, as shown in the example.
|
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
|
@ -57,17 +53,17 @@ use std::{
|
||||||
/// use bevy_ecs::system::SystemParam;
|
/// use bevy_ecs::system::SystemParam;
|
||||||
///
|
///
|
||||||
/// #[derive(SystemParam)]
|
/// #[derive(SystemParam)]
|
||||||
/// struct MyParam<'w, 's> {
|
/// struct MyParam<'w, Marker: 'static> {
|
||||||
/// foo: Res<'w, SomeResource>,
|
/// foo: Res<'w, SomeResource>,
|
||||||
/// #[system_param(ignore)]
|
/// #[system_param(ignore)]
|
||||||
/// marker: PhantomData<&'s ()>,
|
/// marker: PhantomData<Marker>,
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// fn my_system(param: MyParam) {
|
/// fn my_system<T: 'static>(param: MyParam<T>) {
|
||||||
/// // Access the resource through `param.foo`
|
/// // Access the resource through `param.foo`
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// # bevy_ecs::system::assert_is_system(my_system);
|
/// # bevy_ecs::system::assert_is_system(my_system::<()>);
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// # Generic `SystemParam`s
|
/// # Generic `SystemParam`s
|
||||||
|
@ -1567,7 +1563,7 @@ pub mod lifetimeless {
|
||||||
/// struct GenericParam<'w,'s, T: SystemParam> {
|
/// struct GenericParam<'w,'s, T: SystemParam> {
|
||||||
/// field: T,
|
/// field: T,
|
||||||
/// #[system_param(ignore)]
|
/// #[system_param(ignore)]
|
||||||
/// // Use the lifetimes, as the `SystemParam` derive requires them
|
/// // Use the lifetimes in this type, or they will be unbound.
|
||||||
/// phantom: core::marker::PhantomData<&'w &'s ()>
|
/// phantom: core::marker::PhantomData<&'w &'s ()>
|
||||||
/// }
|
/// }
|
||||||
/// # fn check_always_is_system<T: SystemParam + 'static>(){
|
/// # fn check_always_is_system<T: SystemParam + 'static>(){
|
||||||
|
@ -1651,7 +1647,7 @@ unsafe impl<S: SystemParamState, P: SystemParam + 'static> SystemParamState
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::SystemParam;
|
use super::*;
|
||||||
use crate::{
|
use crate::{
|
||||||
self as bevy_ecs, // Necessary for the `SystemParam` Derive when used inside `bevy_ecs`.
|
self as bevy_ecs, // Necessary for the `SystemParam` Derive when used inside `bevy_ecs`.
|
||||||
query::{ReadOnlyWorldQuery, WorldQuery},
|
query::{ReadOnlyWorldQuery, WorldQuery},
|
||||||
|
@ -1668,4 +1664,17 @@ mod tests {
|
||||||
> {
|
> {
|
||||||
_query: Query<'w, 's, Q, F>,
|
_query: Query<'w, 's, Q, F>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(SystemParam)]
|
||||||
|
pub struct SpecialRes<'w, T: Resource> {
|
||||||
|
_res: Res<'w, T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(SystemParam)]
|
||||||
|
pub struct SpecialLocal<'s, T: FromWorld + Send + 'static> {
|
||||||
|
_local: Local<'s, T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(SystemParam)]
|
||||||
|
pub struct UnitParam {}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue