diff --git a/crates/bevy_ecs/macros/src/lib.rs b/crates/bevy_ecs/macros/src/lib.rs index 2d1bab83b2882..46d0199de5f35 100644 --- a/crates/bevy_ecs/macros/src/lib.rs +++ b/crates/bevy_ecs/macros/src/lib.rs @@ -343,7 +343,13 @@ pub fn derive_system_param(input: TokenStream) -> TokenStream { } else { fields.push(field.ident.as_ref().unwrap()); field_types.push(&field.ty); - field_indices.push(Index::from(i)); + let mut punct = Punctuated::<_, Token![.]>::new(); + if field_attributes.len() > 16 { + punct.push_value(Index::from(0)); + punct.push_punct(Default::default()); + } + punct.push_value(Index::from(i)); + field_indices.push(punct); } } @@ -374,13 +380,62 @@ pub fn derive_system_param(input: TokenStream) -> TokenStream { let struct_name = &ast.ident; let fetch_struct_visibility = &ast.vis; + // This exists so the macro doesn't depend on the built-in invocation of the system param tuple macro, which is capped at 16. + let (independent_tuple_impl, fetchstate_type_param) = if field_types.len() > 16 { + let list = (0..field_types.len()) + .map(|i| format!("T{i}")) + .map(|s| syn::Ident::new(&s, Span::call_site())) + .collect::>(); + + let independent_tuple_impl = quote! { + #[doc(hidden)] + #fetch_struct_visibility struct MyTuple(T); + + const _: () = { + use #path::{ + archetype::Archetype, + world::World, + system::{ + SystemParam, SystemParamFetch, SystemParamState, SystemMeta, ReadOnlySystemParamFetch, AsTuple, + }, + }; + + impl AsTuple for MyTuple { + type Tuple = T; + fn as_tuple_mut(&mut self) -> &mut T { + &mut self.0 + } + } + + impl From for MyTuple { + fn from(t: T) -> Self { + Self(t) + } + } + + #path::system::impl_system_param_tuple_base!(MyTuple, #list); + }; + }; + let fetchstate_type_param = quote! { + MyTuple<(#(<#field_types as #path::system::SystemParam>::Fetch,)*)> + }; + (independent_tuple_impl, fetchstate_type_param) + } else { + let fetchstate_type_param = quote! { + (#(<#field_types as #path::system::SystemParam>::Fetch,)*) + }; + (quote! {}, fetchstate_type_param) + }; + TokenStream::from(quote! { // We define the FetchState struct in an anonymous scope to avoid polluting the user namespace. // The struct can still be accessed via SystemParam::Fetch, e.g. EventReaderState can be accessed via // as SystemParam>::Fetch const _: () = { + #independent_tuple_impl + impl #impl_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 = FetchState <#fetchstate_type_param, #punctuated_generic_idents>; } #[doc(hidden)] @@ -406,7 +461,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 #impl_generics #path::system::SystemParamFetch<'w, 's> for FetchState <#fetchstate_type_param, #punctuated_generic_idents> #where_clause { type Item = #struct_name #ty_generics; unsafe fn get_param( state: &'s mut Self, diff --git a/crates/bevy_ecs/src/system/system_param.rs b/crates/bevy_ecs/src/system/system_param.rs index ef956c25d553e..3780074ec1581 100644 --- a/crates/bevy_ecs/src/system/system_param.rs +++ b/crates/bevy_ecs/src/system/system_param.rs @@ -1400,19 +1400,20 @@ impl<'w, 's> SystemParamFetch<'w, 's> for SystemNameState { } } -macro_rules! impl_system_param_tuple { - ($($param: ident),*) => { - impl<$($param: SystemParam),*> SystemParam for ($($param,)*) { - type Fetch = ($($param::Fetch,)*); +#[macro_export] +macro_rules! impl_system_param_tuple_base { + ($tuple:ident, $($param: ident),*) => { + impl<$($param: SystemParam),*> SystemParam for $tuple<($($param,)*)> { + type Fetch = $tuple<($($param::Fetch,)*)>; } // SAFETY: tuple consists only of ReadOnlySystemParamFetches - unsafe impl<$($param: ReadOnlySystemParamFetch),*> ReadOnlySystemParamFetch for ($($param,)*) {} + unsafe impl<$($param: ReadOnlySystemParamFetch),*> ReadOnlySystemParamFetch for $tuple<($($param,)*)> {} #[allow(unused_variables)] #[allow(non_snake_case)] - impl<'w, 's, $($param: SystemParamFetch<'w, 's>),*> SystemParamFetch<'w, 's> for ($($param,)*) { - type Item = ($($param::Item,)*); + impl<'w, 's, $($param: SystemParamFetch<'w, 's>),*> SystemParamFetch<'w, 's> for $tuple<($($param,)*)> { + type Item = $tuple<($($param::Item,)*)>; #[inline] #[allow(clippy::unused_unit)] @@ -1422,36 +1423,57 @@ macro_rules! impl_system_param_tuple { world: &'w World, change_tick: u32, ) -> Self::Item { - - let ($($param,)*) = state; - ($($param::get_param($param, system_meta, world, change_tick),)*) + let ($($param,)*) = state.as_tuple_mut(); + ($($param::get_param($param, system_meta, world, change_tick),)*).into() } } // SAFETY: implementors of each `SystemParamState` in the tuple have validated their impls #[allow(clippy::undocumented_unsafe_blocks)] // false positive by clippy #[allow(non_snake_case)] - unsafe impl<$($param: SystemParamState),*> SystemParamState for ($($param,)*) { + unsafe impl<$($param: SystemParamState),*> SystemParamState for $tuple<($($param,)*)> { #[inline] fn init(_world: &mut World, _system_meta: &mut SystemMeta) -> Self { - (($($param::init(_world, _system_meta),)*)) + (($($param::init(_world, _system_meta),)*)).into() } #[inline] fn new_archetype(&mut self, _archetype: &Archetype, _system_meta: &mut SystemMeta) { - let ($($param,)*) = self; + let ($($param,)*) = self.as_tuple_mut(); $($param.new_archetype(_archetype, _system_meta);)* } #[inline] fn apply(&mut self, _world: &mut World) { - let ($($param,)*) = self; + let ($($param,)*) = self.as_tuple_mut(); $($param.apply(_world);)* } } }; } +pub use impl_system_param_tuple_base; + +#[doc(hidden)] +pub trait AsTuple { + type Tuple; + fn as_tuple_mut(&mut self) -> &mut Self::Tuple; +} + +type Identity = T; +macro_rules! impl_system_param_tuple { + ($($param: ident),*) => { + impl_system_param_tuple_base!(Identity, $($param),*); + + impl<$($param,)*> AsTuple for ($($param,)*) { + type Tuple = Self; + fn as_tuple_mut(&mut self) -> &mut Self { + self + } + } + }; +} + all_tuples!(impl_system_param_tuple, 0, 16, P); pub mod lifetimeless {