Skip to content

Commit 45112a2

Browse files
joseph-gioProfLander
authored andcommitted
Add marker traits to distinguish base sets from regular system sets (bevyengine#7863)
# Objective Base sets, added in bevyengine#7466 are a special type of system set. Systems can only be added to base sets via `in_base_set`, while non-base sets can only be added via `in_set`. Unfortunately this is currently guarded by a runtime panic, which presents an unfortunate toe-stub when the wrong method is used. The delayed response between writing code and encountering the error (possibly hours) makes the distinction between base sets and other sets much more difficult to learn. ## Solution Add the marker traits `BaseSystemSet` and `FreeSystemSet`. `in_base_set` and `in_set` now respectively accept these traits, which moves the runtime panic to a compile time error. --- ## Changelog + Added the marker trait `BaseSystemSet`, which is distinguished from a `FreeSystemSet`. These are both subtraits of `SystemSet`. ## Migration Guide None if merged with 0.10
1 parent 43f92d3 commit 45112a2

File tree

6 files changed

+56
-96
lines changed

6 files changed

+56
-96
lines changed

benches/benches/bevy_ecs/scheduling/schedule.rs

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -63,16 +63,11 @@ pub fn build_schedule(criterion: &mut Criterion) {
6363

6464
// Use multiple different kinds of label to ensure that dynamic dispatch
6565
// doesn't somehow get optimized away.
66-
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
66+
#[derive(SystemSet, Debug, Clone, Copy, PartialEq, Eq, Hash)]
6767
struct NumSet(usize);
68-
#[derive(Debug, Clone, Copy, SystemSet, PartialEq, Eq, Hash)]
69-
struct DummySet;
7068

71-
impl SystemSet for NumSet {
72-
fn dyn_clone(&self) -> Box<dyn SystemSet> {
73-
Box::new(self.clone())
74-
}
75-
}
69+
#[derive(SystemSet, Debug, Clone, Copy, PartialEq, Eq, Hash)]
70+
struct DummySet;
7671

7772
let mut group = criterion.benchmark_group("build_schedule");
7873
group.warm_up_time(std::time::Duration::from_millis(500));

crates/bevy_app/src/config.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
use bevy_ecs::{
22
all_tuples,
33
schedule::{
4-
BoxedScheduleLabel, Condition, IntoSystemConfig, IntoSystemSet, ScheduleLabel,
5-
SystemConfig, SystemConfigs, SystemSet,
4+
BaseSystemSet, BoxedScheduleLabel, Condition, FreeSystemSet, IntoSystemConfig,
5+
IntoSystemSet, ScheduleLabel, SystemConfig, SystemConfigs,
66
},
77
};
88

@@ -84,7 +84,7 @@ impl IntoSystemConfig<(), Self> for SystemAppConfig {
8484
}
8585

8686
#[track_caller]
87-
fn in_set(self, set: impl SystemSet) -> Self {
87+
fn in_set(self, set: impl FreeSystemSet) -> Self {
8888
let Self { system, schedule } = self;
8989
Self {
9090
system: system.in_set(set),
@@ -93,7 +93,7 @@ impl IntoSystemConfig<(), Self> for SystemAppConfig {
9393
}
9494

9595
#[track_caller]
96-
fn in_base_set(self, set: impl SystemSet) -> Self {
96+
fn in_base_set(self, set: impl BaseSystemSet) -> Self {
9797
let Self { system, schedule } = self;
9898
Self {
9999
system: system.in_base_set(set),

crates/bevy_ecs/macros/src/set.rs

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use proc_macro::TokenStream;
2-
use quote::{quote, ToTokens};
2+
use quote::{format_ident, quote, ToTokens};
33
use syn::parse::{Parse, ParseStream};
44

55
pub static SYSTEM_SET_ATTRIBUTE_NAME: &str = "system_set";
@@ -12,6 +12,14 @@ pub static BASE_ATTRIBUTE_NAME: &str = "base";
1212
/// - `input`: The [`syn::DeriveInput`] for the struct that we want to derive the set trait for
1313
/// - `trait_path`: The [`syn::Path`] to the set trait
1414
pub fn derive_set(input: syn::DeriveInput, trait_path: &syn::Path) -> TokenStream {
15+
let mut base_trait_path = trait_path.clone();
16+
let ident = &mut base_trait_path.segments.last_mut().unwrap().ident;
17+
*ident = format_ident!("Base{ident}");
18+
19+
let mut free_trait_path = trait_path.clone();
20+
let ident = &mut free_trait_path.segments.last_mut().unwrap().ident;
21+
*ident = format_ident!("Free{ident}");
22+
1523
let ident = input.ident;
1624

1725
let mut is_base = false;
@@ -65,6 +73,16 @@ pub fn derive_set(input: syn::DeriveInput, trait_path: &syn::Path) -> TokenStrea
6573
.unwrap(),
6674
);
6775

76+
let marker_impl = if is_base {
77+
quote! {
78+
impl #impl_generics #base_trait_path for #ident #ty_generics #where_clause {}
79+
}
80+
} else {
81+
quote! {
82+
impl #impl_generics #free_trait_path for #ident #ty_generics #where_clause {}
83+
}
84+
};
85+
6886
(quote! {
6987
impl #impl_generics #trait_path for #ident #ty_generics #where_clause {
7088
fn is_base(&self) -> bool {
@@ -75,6 +93,8 @@ pub fn derive_set(input: syn::DeriveInput, trait_path: &syn::Path) -> TokenStrea
7593
std::boxed::Box::new(std::clone::Clone::clone(self))
7694
}
7795
}
96+
97+
#marker_impl
7898
})
7999
.into()
80100
}

crates/bevy_ecs/src/schedule/config.rs

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ use crate::{
99
system::{BoxedSystem, IntoSystem, System},
1010
};
1111

12+
use super::{BaseSystemSet, FreeSystemSet};
13+
1214
/// A [`SystemSet`] with scheduling metadata.
1315
pub struct SystemSetConfig {
1416
pub(super) set: BoxedSystemSet,
@@ -86,10 +88,10 @@ pub trait IntoSystemSetConfig {
8688
fn into_config(self) -> SystemSetConfig;
8789
/// Add to the provided `set`.
8890
#[track_caller]
89-
fn in_set(self, set: impl SystemSet) -> SystemSetConfig;
91+
fn in_set(self, set: impl FreeSystemSet) -> SystemSetConfig;
9092
/// Add to the provided "base" `set`. For more information on base sets, see [`SystemSet::is_base`].
9193
#[track_caller]
92-
fn in_base_set(self, set: impl SystemSet) -> SystemSetConfig;
94+
fn in_base_set(self, set: impl BaseSystemSet) -> SystemSetConfig;
9395
/// Add this set to the schedules's default base set.
9496
fn in_default_base_set(self) -> SystemSetConfig;
9597
/// Run before all systems in `set`.
@@ -115,12 +117,12 @@ impl<S: SystemSet> IntoSystemSetConfig for S {
115117
}
116118

117119
#[track_caller]
118-
fn in_set(self, set: impl SystemSet) -> SystemSetConfig {
120+
fn in_set(self, set: impl FreeSystemSet) -> SystemSetConfig {
119121
self.into_config().in_set(set)
120122
}
121123

122124
#[track_caller]
123-
fn in_base_set(self, set: impl SystemSet) -> SystemSetConfig {
125+
fn in_base_set(self, set: impl BaseSystemSet) -> SystemSetConfig {
124126
self.into_config().in_base_set(set)
125127
}
126128

@@ -155,12 +157,12 @@ impl IntoSystemSetConfig for BoxedSystemSet {
155157
}
156158

157159
#[track_caller]
158-
fn in_set(self, set: impl SystemSet) -> SystemSetConfig {
160+
fn in_set(self, set: impl FreeSystemSet) -> SystemSetConfig {
159161
self.into_config().in_set(set)
160162
}
161163

162164
#[track_caller]
163-
fn in_base_set(self, set: impl SystemSet) -> SystemSetConfig {
165+
fn in_base_set(self, set: impl BaseSystemSet) -> SystemSetConfig {
164166
self.into_config().in_base_set(set)
165167
}
166168

@@ -277,10 +279,10 @@ pub trait IntoSystemConfig<Marker, Config = SystemConfig> {
277279
fn into_config(self) -> Config;
278280
/// Add to `set` membership.
279281
#[track_caller]
280-
fn in_set(self, set: impl SystemSet) -> Config;
282+
fn in_set(self, set: impl FreeSystemSet) -> Config;
281283
/// Add to the provided "base" `set`. For more information on base sets, see [`SystemSet::is_base`].
282284
#[track_caller]
283-
fn in_base_set(self, set: impl SystemSet) -> Config;
285+
fn in_base_set(self, set: impl BaseSystemSet) -> Config;
284286
/// Don't add this system to the schedules's default set.
285287
fn no_default_base_set(self) -> Config;
286288
/// Run before all systems in `set`.
@@ -309,12 +311,12 @@ where
309311
}
310312

311313
#[track_caller]
312-
fn in_set(self, set: impl SystemSet) -> SystemConfig {
314+
fn in_set(self, set: impl FreeSystemSet) -> SystemConfig {
313315
self.into_config().in_set(set)
314316
}
315317

316318
#[track_caller]
317-
fn in_base_set(self, set: impl SystemSet) -> SystemConfig {
319+
fn in_base_set(self, set: impl BaseSystemSet) -> SystemConfig {
318320
self.into_config().in_base_set(set)
319321
}
320322

@@ -349,12 +351,12 @@ impl IntoSystemConfig<()> for BoxedSystem<(), ()> {
349351
}
350352

351353
#[track_caller]
352-
fn in_set(self, set: impl SystemSet) -> SystemConfig {
354+
fn in_set(self, set: impl FreeSystemSet) -> SystemConfig {
353355
self.into_config().in_set(set)
354356
}
355357

356358
#[track_caller]
357-
fn in_base_set(self, set: impl SystemSet) -> SystemConfig {
359+
fn in_base_set(self, set: impl BaseSystemSet) -> SystemConfig {
358360
self.into_config().in_base_set(set)
359361
}
360362

@@ -471,13 +473,13 @@ where
471473

472474
/// Add these systems to the provided `set`.
473475
#[track_caller]
474-
fn in_set(self, set: impl SystemSet) -> SystemConfigs {
476+
fn in_set(self, set: impl FreeSystemSet) -> SystemConfigs {
475477
self.into_configs().in_set(set)
476478
}
477479

478480
/// Add these systems to the provided "base" `set`. For more information on base sets, see [`SystemSet::is_base`].
479481
#[track_caller]
480-
fn in_base_set(self, set: impl SystemSet) -> SystemConfigs {
482+
fn in_base_set(self, set: impl BaseSystemSet) -> SystemConfigs {
481483
self.into_configs().in_base_set(set)
482484
}
483485

@@ -657,13 +659,13 @@ where
657659

658660
/// Add these system sets to the provided `set`.
659661
#[track_caller]
660-
fn in_set(self, set: impl SystemSet) -> SystemSetConfigs {
662+
fn in_set(self, set: impl FreeSystemSet) -> SystemSetConfigs {
661663
self.into_configs().in_set(set)
662664
}
663665

664666
/// Add these system sets to the provided "base" `set`. For more information on base sets, see [`SystemSet::is_base`].
665667
#[track_caller]
666-
fn in_base_set(self, set: impl SystemSet) -> SystemSetConfigs {
668+
fn in_base_set(self, set: impl BaseSystemSet) -> SystemSetConfigs {
667669
self.into_configs().in_base_set(set)
668670
}
669671

crates/bevy_ecs/src/schedule/mod.rs

Lines changed: 0 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -567,16 +567,6 @@ mod tests {
567567
));
568568
}
569569

570-
#[test]
571-
#[should_panic]
572-
fn in_system_type_set() {
573-
fn foo() {}
574-
fn bar() {}
575-
576-
let mut schedule = Schedule::new();
577-
schedule.add_system(foo.in_set(bar.into_system_set()));
578-
}
579-
580570
#[test]
581571
#[should_panic]
582572
fn configure_system_type_set() {
@@ -688,63 +678,6 @@ mod tests {
688678
enum Normal {
689679
X,
690680
Y,
691-
Z,
692-
}
693-
694-
#[test]
695-
#[should_panic]
696-
fn disallow_adding_base_sets_to_system_with_in_set() {
697-
let mut schedule = Schedule::new();
698-
schedule.add_system(named_system.in_set(Base::A));
699-
}
700-
701-
#[test]
702-
#[should_panic]
703-
fn disallow_adding_sets_to_system_with_in_base_set() {
704-
let mut schedule = Schedule::new();
705-
schedule.add_system(named_system.in_base_set(Normal::X));
706-
}
707-
708-
#[test]
709-
#[should_panic]
710-
fn disallow_adding_base_sets_to_systems_with_in_set() {
711-
let mut schedule = Schedule::new();
712-
schedule.add_systems((named_system, named_system).in_set(Base::A));
713-
}
714-
715-
#[test]
716-
#[should_panic]
717-
fn disallow_adding_sets_to_systems_with_in_base_set() {
718-
let mut schedule = Schedule::new();
719-
schedule.add_systems((named_system, named_system).in_base_set(Normal::X));
720-
}
721-
722-
#[test]
723-
#[should_panic]
724-
fn disallow_adding_base_sets_to_set_with_in_set() {
725-
let mut schedule = Schedule::new();
726-
schedule.configure_set(Normal::Y.in_set(Base::A));
727-
}
728-
729-
#[test]
730-
#[should_panic]
731-
fn disallow_adding_sets_to_set_with_in_base_set() {
732-
let mut schedule = Schedule::new();
733-
schedule.configure_set(Normal::Y.in_base_set(Normal::X));
734-
}
735-
736-
#[test]
737-
#[should_panic]
738-
fn disallow_adding_base_sets_to_sets_with_in_set() {
739-
let mut schedule = Schedule::new();
740-
schedule.configure_sets((Normal::X, Normal::Y).in_set(Base::A));
741-
}
742-
743-
#[test]
744-
#[should_panic]
745-
fn disallow_adding_sets_to_sets_with_in_base_set() {
746-
let mut schedule = Schedule::new();
747-
schedule.configure_sets((Normal::X, Normal::Y).in_base_set(Normal::Z));
748681
}
749682

750683
#[test]

crates/bevy_ecs/src/schedule/set.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,16 @@ pub trait SystemSet: DynHash + Debug + Send + Sync + 'static {
3737
fn dyn_clone(&self) -> Box<dyn SystemSet>;
3838
}
3939

40+
/// A system set that is never contained in another set.
41+
/// Systems and other system sets may only belong to one base set.
42+
///
43+
/// This should only be implemented for types that return `true` from [`SystemSet::is_base`].
44+
pub trait BaseSystemSet: SystemSet {}
45+
46+
/// System sets that are *not* base sets. This should not be implemented for
47+
/// any types that implement [`BaseSystemSet`].
48+
pub trait FreeSystemSet: SystemSet {}
49+
4050
impl PartialEq for dyn SystemSet {
4151
fn eq(&self, other: &Self) -> bool {
4252
self.dyn_eq(other.as_dyn_eq())

0 commit comments

Comments
 (0)