Skip to content

Commit 20f9c8f

Browse files
committed
Lift the 16-field limit from the SystemParam derive (bevyengine#6867)
* 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 bevyengine#5965 -- this does the same thing, but is simpler. 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 ea9bee0 commit 20f9c8f

File tree

2 files changed

+49
-4
lines changed

2 files changed

+49
-4
lines changed

crates/bevy_ecs/macros/src/lib.rs

Lines changed: 20 additions & 4 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,
@@ -343,8 +343,8 @@ pub fn derive_system_param(input: TokenStream) -> TokenStream {
343343
)
344344
})
345345
.collect::<Vec<(&Field, SystemParamFieldAttributes)>>();
346+
let mut field_locals = Vec::new();
346347
let mut fields = Vec::new();
347-
let mut field_indices = Vec::new();
348348
let mut field_types = Vec::new();
349349
let mut ignored_fields = Vec::new();
350350
let mut ignored_field_types = Vec::new();
@@ -353,6 +353,7 @@ pub fn derive_system_param(input: TokenStream) -> TokenStream {
353353
ignored_fields.push(field.ident.as_ref().unwrap());
354354
ignored_field_types.push(&field.ty);
355355
} else {
356+
field_locals.push(format_ident!("f{i}"));
356357
let i = Index::from(i);
357358
fields.push(
358359
field
@@ -362,7 +363,6 @@ pub fn derive_system_param(input: TokenStream) -> TokenStream {
362363
.unwrap_or_else(|| quote! { #i }),
363364
);
364365
field_types.push(&field.ty);
365-
field_indices.push(i);
366366
}
367367
}
368368

@@ -414,6 +414,19 @@ pub fn derive_system_param(input: TokenStream) -> TokenStream {
414414
_ => unreachable!(),
415415
}));
416416

417+
let mut tuple_types: Vec<_> = field_types.iter().map(|x| quote! { #x }).collect();
418+
let mut tuple_patterns: Vec<_> = field_locals.iter().map(|x| quote! { #x }).collect();
419+
420+
// If the number of fields exceeds the 16-parameter limit,
421+
// fold the fields into tuples of tuples until we are below the limit.
422+
const LIMIT: usize = 16;
423+
while tuple_types.len() > LIMIT {
424+
let end = Vec::from_iter(tuple_types.drain(..LIMIT));
425+
tuple_types.push(parse_quote!( (#(#end,)*) ));
426+
427+
let end = Vec::from_iter(tuple_patterns.drain(..LIMIT));
428+
tuple_patterns.push(parse_quote!( (#(#end,)*) ));
429+
}
417430
// Create a where clause for the `ReadOnlySystemParam` impl.
418431
// Ensure that each field implements `ReadOnlySystemParam`.
419432
let mut read_only_generics = generics.clone();
@@ -453,8 +466,11 @@ pub fn derive_system_param(input: TokenStream) -> TokenStream {
453466
world: &'w2 #path::world::World,
454467
change_tick: u32,
455468
) -> Self::Item<'w2, 's2> {
469+
let (#(#tuple_patterns,)*) = <
470+
(#(#tuple_types,)*) as #path::system::SystemParam
471+
>::get_param(&mut state.state, system_meta, world, change_tick);
456472
#struct_name {
457-
#(#fields: <#field_types as #path::system::SystemParam>::get_param(&mut state.#field_indices, system_meta, world, change_tick),)*
473+
#(#fields: #field_locals,)*
458474
#(#ignored_fields: <#ignored_field_types>::default(),)*
459475
}
460476
}

crates/bevy_ecs/src/system/system_param.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1459,6 +1459,35 @@ mod tests {
14591459
_local: Local<'s, T>,
14601460
}
14611461

1462+
#[derive(Resource)]
1463+
pub struct R<const I: usize>;
1464+
1465+
#[derive(SystemParam)]
1466+
pub struct LongParam<'w> {
1467+
_r0: Res<'w, R<0>>,
1468+
_r1: Res<'w, R<1>>,
1469+
_r2: Res<'w, R<2>>,
1470+
_r3: Res<'w, R<3>>,
1471+
_r4: Res<'w, R<4>>,
1472+
_r5: Res<'w, R<5>>,
1473+
_r6: Res<'w, R<6>>,
1474+
_r7: Res<'w, R<7>>,
1475+
_r8: Res<'w, R<8>>,
1476+
_r9: Res<'w, R<9>>,
1477+
_r10: Res<'w, R<10>>,
1478+
_r11: Res<'w, R<11>>,
1479+
_r12: Res<'w, R<12>>,
1480+
_r13: Res<'w, R<13>>,
1481+
_r14: Res<'w, R<14>>,
1482+
_r15: Res<'w, R<15>>,
1483+
_r16: Res<'w, R<16>>,
1484+
}
1485+
1486+
#[allow(dead_code)]
1487+
fn long_system(_param: LongParam) {
1488+
crate::system::assert_is_system(long_system);
1489+
}
1490+
14621491
#[derive(SystemParam)]
14631492
pub struct UnitParam;
14641493

0 commit comments

Comments
 (0)