Skip to content

derive(SystemParam) better lifetime param (#10331) #13794

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
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
33 changes: 31 additions & 2 deletions crates/bevy_ecs/macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use proc_macro2::Span;
use quote::{format_ident, quote};
use syn::{
parse_macro_input, parse_quote, punctuated::Punctuated, spanned::Spanned, token::Comma,
ConstParam, DeriveInput, GenericParam, Ident, Index, TypeParam,
visit_mut::VisitMut, ConstParam, DeriveInput, GenericParam, Ident, Index, TypeParam,
};

enum BundleFieldKind {
Expand Down Expand Up @@ -274,9 +274,37 @@ pub fn impl_param_set(_input: TokenStream) -> TokenStream {
tokens
}

// replace 'world with 'w and 'state with 's
fn replace_lifetimes(stream: TokenStream, f: Box<dyn Fn(&str) -> &str>) -> TokenStream {
struct LifetimeReplacer {
f: Box<dyn Fn(&str) -> &str>,
}
impl VisitMut for LifetimeReplacer {
fn visit_lifetime_mut(&mut self, lifetime: &mut syn::Lifetime) {
lifetime.ident = syn::Ident::new(
(self.f)(lifetime.ident.to_string().as_str()),
lifetime.ident.span(),
);
}
}

let mut ast = parse_macro_input!(stream as DeriveInput);
LifetimeReplacer { f }.visit_derive_input_mut(&mut ast);

quote! {#ast}.into()
}

/// 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 {
let input = replace_lifetimes(
input,
Box::new(|s| match s {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like we should just not special-case lifetime names at all 🤔 Are we able to do so?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I respect your intuition, but i dont understand it. the whole point of that function is to abbreviate lifetimes, since 'world and 'state should be a special exception; replacing other things like variable names or punctuation is really not what were after.
Honestly id go back to the previous iteration where the values were hardcoded, and declare the function inside the only place where it is used (if rust allows that)
maybe someone with macro experience can actually find a more elegant solution, but idk if there is one

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's do that and solve the issue raised directly.

"world" => "w",
"state" => "s",
other => other,
}),
);
let token_stream = input.clone();
let ast = parse_macro_input!(input as DeriveInput);
let syn::Data::Struct(syn::DataStruct {
Expand Down Expand Up @@ -321,7 +349,8 @@ pub fn derive_system_param(input: TokenStream) -> TokenStream {
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.'"#,
's -- refers to data stored in the SystemParam's state.
using `'world` or `'state` is permitted aswell"#,
)
.into_compile_error()
.into();
Expand Down
23 changes: 23 additions & 0 deletions crates/bevy_ecs/src/system/system_param.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ use std::{
///
/// Derived `SystemParam` structs may have two lifetimes: `'w` for data stored in the [`World`],
/// and `'s` for data stored in the parameter's state.
/// instead of `'w` and `'s`, you are permitted to use `'world` and `'state`
///
/// The following list shows the most common [`SystemParam`]s and which lifetime they require
///
Expand Down Expand Up @@ -124,6 +125,28 @@ use std::{
/// by [`SystemParam::get_param`] with the provided [`system_meta`](SystemMeta).
/// - None of the world accesses may conflict with any prior accesses registered
/// on `system_meta`.
///
/// Doc test to ensure that 'world and 'state are permitted aswell
/// ```
/// # use bevy_ecs::prelude::*;
/// # #[derive(Resource)]
/// # struct SomeResource;
/// # #[derive(Event)]
/// # struct SomeEvent;
/// # #[derive(Resource)]
/// # struct SomeOtherResource;
/// # use bevy_ecs::system::SystemParam;
/// # #[derive(SystemParam)]
/// # struct ParamsExample<'world, 'state> {
/// # query: Query<'world, 'state, Entity>,
/// # res: Res<'world, SomeResource>,
/// # res_mut: ResMut<'world, SomeOtherResource>,
/// # world: Local<'state, u8>,
/// # commands: Commands<'world, 'state>,
/// # eventreader: EventReader<'world, 'state, SomeEvent>,
/// # eventwriter: EventWriter<'world, SomeEvent>
/// # }
/// ```
pub unsafe trait SystemParam: Sized {
/// Used to store data which persists across invocations of a system.
type State: Send + Sync + 'static;
Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_macro_utils/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ keywords = ["bevy"]
toml_edit = { version = "0.22.7", default-features = false, features = [
"parse",
] }
syn = "2.0"
syn = { version = "2.0", features = ["visit-mut"] }
quote = "1.0"
proc-macro2 = "1.0"

Expand Down