Skip to content

Commit 025996b

Browse files
committed
Lift the 16-field limit from the SystemParam derive (#6867)
# Objective * The `SystemParam` derive internally uses tuples, which means it is constrained by the 16-field limit on `all_tuples`. * The error message if you exceed this limit is abysmal. * Supercedes #5965 -- this does the same thing, but is simpler. ## Solution If any tuples have more than 16 fields, they are folded into tuples of tuples until they are under the 16-field limit.
1 parent 0363e0b commit 025996b

File tree

2 files changed

+50
-5
lines changed

2 files changed

+50
-5
lines changed

crates/bevy_ecs/macros/src/lib.rs

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use proc_macro2::Span;
1010
use quote::{format_ident, quote};
1111
use syn::{
1212
parse::{Parse, ParseStream},
13-
parse_macro_input,
13+
parse_macro_input, parse_quote,
1414
punctuated::Punctuated,
1515
spanned::Spanned,
1616
token::Comma,
@@ -359,8 +359,8 @@ pub fn derive_system_param(input: TokenStream) -> TokenStream {
359359
)
360360
})
361361
.collect::<Vec<(&Field, SystemParamFieldAttributes)>>();
362+
let mut field_locals = Vec::new();
362363
let mut fields = Vec::new();
363-
let mut field_indices = Vec::new();
364364
let mut field_types = Vec::new();
365365
let mut ignored_fields = Vec::new();
366366
let mut ignored_field_types = Vec::new();
@@ -369,6 +369,7 @@ pub fn derive_system_param(input: TokenStream) -> TokenStream {
369369
ignored_fields.push(field.ident.as_ref().unwrap());
370370
ignored_field_types.push(&field.ty);
371371
} else {
372+
field_locals.push(format_ident!("f{i}"));
372373
let i = Index::from(i);
373374
fields.push(
374375
field
@@ -378,7 +379,6 @@ pub fn derive_system_param(input: TokenStream) -> TokenStream {
378379
.unwrap_or_else(|| quote! { #i }),
379380
);
380381
field_types.push(&field.ty);
381-
field_indices.push(i);
382382
}
383383
}
384384

@@ -424,6 +424,19 @@ pub fn derive_system_param(input: TokenStream) -> TokenStream {
424424
_ => unreachable!(),
425425
}));
426426

427+
let mut tuple_types: Vec<_> = field_types.iter().map(|x| quote! { #x }).collect();
428+
let mut tuple_patterns: Vec<_> = field_locals.iter().map(|x| quote! { #x }).collect();
429+
430+
// If the number of fields exceeds the 16-parameter limit,
431+
// fold the fields into tuples of tuples until we are below the limit.
432+
const LIMIT: usize = 16;
433+
while tuple_types.len() > LIMIT {
434+
let end = Vec::from_iter(tuple_types.drain(..LIMIT));
435+
tuple_types.push(parse_quote!( (#(#end,)*) ));
436+
437+
let end = Vec::from_iter(tuple_patterns.drain(..LIMIT));
438+
tuple_patterns.push(parse_quote!( (#(#end,)*) ));
439+
}
427440
// Create a where clause for the `ReadOnlySystemParam` impl.
428441
// Ensure that each field implements `ReadOnlySystemParam`.
429442
let mut read_only_generics = generics.clone();
@@ -448,7 +461,7 @@ pub fn derive_system_param(input: TokenStream) -> TokenStream {
448461

449462
#[doc(hidden)]
450463
type State<'w, 's, #punctuated_generic_idents> = FetchState<
451-
(#(<#field_types as #path::system::SystemParam>::State,)*),
464+
(#(<#tuple_types as #path::system::SystemParam>::State,)*),
452465
#punctuated_generic_idents
453466
>;
454467

@@ -484,8 +497,11 @@ pub fn derive_system_param(input: TokenStream) -> TokenStream {
484497
world: &'w #path::world::World,
485498
change_tick: u32,
486499
) -> Self::Item<'w, 's> {
500+
let (#(#tuple_patterns,)*) = <
501+
<(#(#tuple_types,)*) as #path::system::SystemParam>::State as #path::system::SystemParamState
502+
>::get_param(&mut state.state, system_meta, world, change_tick);
487503
#struct_name {
488-
#(#fields: <<#field_types as #path::system::SystemParam>::State as #path::system::SystemParamState>::get_param(&mut state.state.#field_indices, system_meta, world, change_tick),)*
504+
#(#fields: #field_locals,)*
489505
#(#ignored_fields: <#ignored_field_types>::default(),)*
490506
}
491507
}

crates/bevy_ecs/src/system/system_param.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1661,6 +1661,35 @@ mod tests {
16611661
_local: Local<'s, T>,
16621662
}
16631663

1664+
#[derive(Resource)]
1665+
pub struct R<const I: usize>;
1666+
1667+
#[derive(SystemParam)]
1668+
pub struct LongParam<'w> {
1669+
_r0: Res<'w, R<0>>,
1670+
_r1: Res<'w, R<1>>,
1671+
_r2: Res<'w, R<2>>,
1672+
_r3: Res<'w, R<3>>,
1673+
_r4: Res<'w, R<4>>,
1674+
_r5: Res<'w, R<5>>,
1675+
_r6: Res<'w, R<6>>,
1676+
_r7: Res<'w, R<7>>,
1677+
_r8: Res<'w, R<8>>,
1678+
_r9: Res<'w, R<9>>,
1679+
_r10: Res<'w, R<10>>,
1680+
_r11: Res<'w, R<11>>,
1681+
_r12: Res<'w, R<12>>,
1682+
_r13: Res<'w, R<13>>,
1683+
_r14: Res<'w, R<14>>,
1684+
_r15: Res<'w, R<15>>,
1685+
_r16: Res<'w, R<16>>,
1686+
}
1687+
1688+
#[allow(dead_code)]
1689+
fn long_system(_param: LongParam) {
1690+
crate::system::assert_is_system(long_system);
1691+
}
1692+
16641693
#[derive(SystemParam)]
16651694
pub struct UnitParam;
16661695

0 commit comments

Comments
 (0)