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
					
				| @ -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…
	
		Reference in New Issue
	
	Block a user
	 JoJoJet
						JoJoJet