Replace all labels with interned labels (#7762)
# Objective First of all, this PR took heavy inspiration from #7760 and #5715. It intends to also fix #5569, but with a slightly different approach. This also fixes #9335 by reexporting `DynEq`. ## Solution The advantage of this API is that we can intern a value without allocating for zero-sized-types and for enum variants that have no fields. This PR does this automatically in the `SystemSet` and `ScheduleLabel` derive macros for unit structs and fieldless enum variants. So this should cover many internal and external use cases of `SystemSet` and `ScheduleLabel`. In these optimal use cases, no memory will be allocated. - The interning returns a `Interned<dyn SystemSet>`, which is just a wrapper around a `&'static dyn SystemSet`. - `Hash` and `Eq` are implemented in terms of the pointer value of the reference, similar to my first approach of anonymous system sets in #7676. - Therefore, `Interned<T>` does not implement `Borrow<T>`, only `Deref`. - The debug output of `Interned<T>` is the same as the interned value. Edit: - `AppLabel` is now also interned and the old `derive_label`/`define_label` macros were replaced with the new interning implementation. - Anonymous set ids are reused for different `Schedule`s, reducing the amount of leaked memory. ### Pros - `InternedSystemSet` and `InternedScheduleLabel` behave very similar to the current `BoxedSystemSet` and `BoxedScheduleLabel`, but can be copied without an allocation. - Many use cases don't allocate at all. - Very fast lookups and comparisons when using `InternedSystemSet` and `InternedScheduleLabel`. - The `intern` module might be usable in other areas. - `Interned{ScheduleLabel, SystemSet, AppLabel}` does implement `{ScheduleLabel, SystemSet, AppLabel}`, increasing ergonomics. ### Cons - Implementors of `SystemSet` and `ScheduleLabel` still need to implement `Hash` and `Eq` (and `Clone`) for it to work. ## Changelog ### Added - Added `intern` module to `bevy_utils`. - Added reexports of `DynEq` to `bevy_ecs` and `bevy_app`. ### Changed - Replaced `BoxedSystemSet` and `BoxedScheduleLabel` with `InternedSystemSet` and `InternedScheduleLabel`. - Replaced `impl AsRef<dyn ScheduleLabel>` with `impl ScheduleLabel`. - Replaced `AppLabelId` with `InternedAppLabel`. - Changed `AppLabel` to use `Debug` for error messages. - Changed `AppLabel` to use interning. - Changed `define_label`/`derive_label` to use interning. - Replaced `define_boxed_label`/`derive_boxed_label` with `define_label`/`derive_label`. - Changed anonymous set ids to be only unique inside a schedule, not globally. - Made interned label types implement their label trait. ### Removed - Removed `define_boxed_label` and `derive_boxed_label`. ## Migration guide - Replace `BoxedScheduleLabel` and `Box<dyn ScheduleLabel>` with `InternedScheduleLabel` or `Interned<dyn ScheduleLabel>`. - Replace `BoxedSystemSet` and `Box<dyn SystemSet>` with `InternedSystemSet` or `Interned<dyn SystemSet>`. - Replace `AppLabelId` with `InternedAppLabel` or `Interned<dyn AppLabel>`. - Types manually implementing `ScheduleLabel`, `AppLabel` or `SystemSet` need to implement: - `dyn_hash` directly instead of implementing `DynHash` - `as_dyn_eq` - Pass labels to `World::try_schedule_scope`, `World::schedule_scope`, `World::try_run_schedule`. `World::run_schedule`, `Schedules::remove`, `Schedules::remove_entry`, `Schedules::contains`, `Schedules::get` and `Schedules::get_mut` by value instead of by reference. --------- Co-authored-by: Joseph <21144246+JoJoJet@users.noreply.github.com> Co-authored-by: Carter Anderson <mcanders1@gmail.com>
This commit is contained in:
		
							parent
							
								
									317903f42a
								
							
						
					
					
						commit
						a830530be4
					
				@ -4,11 +4,11 @@ use bevy_ecs::{
 | 
			
		||||
    prelude::*,
 | 
			
		||||
    schedule::{
 | 
			
		||||
        apply_state_transition, common_conditions::run_once as run_once_condition,
 | 
			
		||||
        run_enter_schedule, BoxedScheduleLabel, IntoSystemConfigs, IntoSystemSetConfigs,
 | 
			
		||||
        run_enter_schedule, InternedScheduleLabel, IntoSystemConfigs, IntoSystemSetConfigs,
 | 
			
		||||
        ScheduleBuildSettings, ScheduleLabel,
 | 
			
		||||
    },
 | 
			
		||||
};
 | 
			
		||||
use bevy_utils::{tracing::debug, HashMap, HashSet};
 | 
			
		||||
use bevy_utils::{intern::Interned, tracing::debug, HashMap, HashSet};
 | 
			
		||||
use std::{
 | 
			
		||||
    fmt::Debug,
 | 
			
		||||
    panic::{catch_unwind, resume_unwind, AssertUnwindSafe},
 | 
			
		||||
@ -20,10 +20,14 @@ use bevy_utils::tracing::info_span;
 | 
			
		||||
bevy_utils::define_label!(
 | 
			
		||||
    /// A strongly-typed class of labels used to identify an [`App`].
 | 
			
		||||
    AppLabel,
 | 
			
		||||
    /// A strongly-typed identifier for an [`AppLabel`].
 | 
			
		||||
    AppLabelId,
 | 
			
		||||
    APP_LABEL_INTERNER
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
pub use bevy_utils::label::DynEq;
 | 
			
		||||
 | 
			
		||||
/// A shorthand for `Interned<dyn AppLabel>`.
 | 
			
		||||
pub type InternedAppLabel = Interned<dyn AppLabel>;
 | 
			
		||||
 | 
			
		||||
pub(crate) enum AppError {
 | 
			
		||||
    DuplicatePlugin { plugin_name: String },
 | 
			
		||||
}
 | 
			
		||||
@ -70,8 +74,8 @@ pub struct App {
 | 
			
		||||
    /// The schedule that runs the main loop of schedule execution.
 | 
			
		||||
    ///
 | 
			
		||||
    /// This is initially set to [`Main`].
 | 
			
		||||
    pub main_schedule_label: BoxedScheduleLabel,
 | 
			
		||||
    sub_apps: HashMap<AppLabelId, SubApp>,
 | 
			
		||||
    pub main_schedule_label: InternedScheduleLabel,
 | 
			
		||||
    sub_apps: HashMap<InternedAppLabel, SubApp>,
 | 
			
		||||
    plugin_registry: Vec<Box<dyn Plugin>>,
 | 
			
		||||
    plugin_name_added: HashSet<String>,
 | 
			
		||||
    /// A private counter to prevent incorrect calls to `App::run()` from `Plugin::build()`
 | 
			
		||||
@ -157,7 +161,7 @@ impl SubApp {
 | 
			
		||||
 | 
			
		||||
    /// Runs the [`SubApp`]'s default schedule.
 | 
			
		||||
    pub fn run(&mut self) {
 | 
			
		||||
        self.app.world.run_schedule(&*self.app.main_schedule_label);
 | 
			
		||||
        self.app.world.run_schedule(self.app.main_schedule_label);
 | 
			
		||||
        self.app.world.clear_trackers();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -233,7 +237,7 @@ impl App {
 | 
			
		||||
            sub_apps: HashMap::default(),
 | 
			
		||||
            plugin_registry: Vec::default(),
 | 
			
		||||
            plugin_name_added: Default::default(),
 | 
			
		||||
            main_schedule_label: Box::new(Main),
 | 
			
		||||
            main_schedule_label: Main.intern(),
 | 
			
		||||
            building_plugin_depth: 0,
 | 
			
		||||
            plugins_state: PluginsState::Adding,
 | 
			
		||||
        }
 | 
			
		||||
@ -256,7 +260,7 @@ impl App {
 | 
			
		||||
        {
 | 
			
		||||
            #[cfg(feature = "trace")]
 | 
			
		||||
            let _bevy_main_update_span = info_span!("main app").entered();
 | 
			
		||||
            self.world.run_schedule(&*self.main_schedule_label);
 | 
			
		||||
            self.world.run_schedule(self.main_schedule_label);
 | 
			
		||||
        }
 | 
			
		||||
        for (_label, sub_app) in &mut self.sub_apps {
 | 
			
		||||
            #[cfg(feature = "trace")]
 | 
			
		||||
@ -409,9 +413,10 @@ impl App {
 | 
			
		||||
        schedule: impl ScheduleLabel,
 | 
			
		||||
        systems: impl IntoSystemConfigs<M>,
 | 
			
		||||
    ) -> &mut Self {
 | 
			
		||||
        let schedule = schedule.intern();
 | 
			
		||||
        let mut schedules = self.world.resource_mut::<Schedules>();
 | 
			
		||||
 | 
			
		||||
        if let Some(schedule) = schedules.get_mut(&schedule) {
 | 
			
		||||
        if let Some(schedule) = schedules.get_mut(schedule) {
 | 
			
		||||
            schedule.add_systems(systems);
 | 
			
		||||
        } else {
 | 
			
		||||
            let mut new_schedule = Schedule::new(schedule);
 | 
			
		||||
@ -440,8 +445,9 @@ impl App {
 | 
			
		||||
        schedule: impl ScheduleLabel,
 | 
			
		||||
        sets: impl IntoSystemSetConfigs,
 | 
			
		||||
    ) -> &mut Self {
 | 
			
		||||
        let schedule = schedule.intern();
 | 
			
		||||
        let mut schedules = self.world.resource_mut::<Schedules>();
 | 
			
		||||
        if let Some(schedule) = schedules.get_mut(&schedule) {
 | 
			
		||||
        if let Some(schedule) = schedules.get_mut(schedule) {
 | 
			
		||||
            schedule.configure_sets(sets);
 | 
			
		||||
        } else {
 | 
			
		||||
            let mut new_schedule = Schedule::new(schedule);
 | 
			
		||||
@ -784,16 +790,15 @@ impl App {
 | 
			
		||||
    pub fn sub_app_mut(&mut self, label: impl AppLabel) -> &mut App {
 | 
			
		||||
        match self.get_sub_app_mut(label) {
 | 
			
		||||
            Ok(app) => app,
 | 
			
		||||
            Err(label) => panic!("Sub-App with label '{:?}' does not exist", label.as_str()),
 | 
			
		||||
            Err(label) => panic!("Sub-App with label '{:?}' does not exist", label),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Retrieves a `SubApp` inside this [`App`] with the given label, if it exists. Otherwise returns
 | 
			
		||||
    /// an [`Err`] containing the given label.
 | 
			
		||||
    pub fn get_sub_app_mut(&mut self, label: impl AppLabel) -> Result<&mut App, AppLabelId> {
 | 
			
		||||
        let label = label.as_label();
 | 
			
		||||
    pub fn get_sub_app_mut(&mut self, label: impl AppLabel) -> Result<&mut App, impl AppLabel> {
 | 
			
		||||
        self.sub_apps
 | 
			
		||||
            .get_mut(&label)
 | 
			
		||||
            .get_mut(&label.intern())
 | 
			
		||||
            .map(|sub_app| &mut sub_app.app)
 | 
			
		||||
            .ok_or(label)
 | 
			
		||||
    }
 | 
			
		||||
@ -806,25 +811,25 @@ impl App {
 | 
			
		||||
    pub fn sub_app(&self, label: impl AppLabel) -> &App {
 | 
			
		||||
        match self.get_sub_app(label) {
 | 
			
		||||
            Ok(app) => app,
 | 
			
		||||
            Err(label) => panic!("Sub-App with label '{:?}' does not exist", label.as_str()),
 | 
			
		||||
            Err(label) => panic!("Sub-App with label '{:?}' does not exist", label),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Inserts an existing sub app into the app
 | 
			
		||||
    pub fn insert_sub_app(&mut self, label: impl AppLabel, sub_app: SubApp) {
 | 
			
		||||
        self.sub_apps.insert(label.as_label(), sub_app);
 | 
			
		||||
        self.sub_apps.insert(label.intern(), sub_app);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Removes a sub app from the app. Returns [`None`] if the label doesn't exist.
 | 
			
		||||
    pub fn remove_sub_app(&mut self, label: impl AppLabel) -> Option<SubApp> {
 | 
			
		||||
        self.sub_apps.remove(&label.as_label())
 | 
			
		||||
        self.sub_apps.remove(&label.intern())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Retrieves a `SubApp` inside this [`App`] with the given label, if it exists. Otherwise returns
 | 
			
		||||
    /// an [`Err`] containing the given label.
 | 
			
		||||
    pub fn get_sub_app(&self, label: impl AppLabel) -> Result<&App, impl AppLabel> {
 | 
			
		||||
        self.sub_apps
 | 
			
		||||
            .get(&label.as_label())
 | 
			
		||||
            .get(&label.intern())
 | 
			
		||||
            .map(|sub_app| &sub_app.app)
 | 
			
		||||
            .ok_or(label)
 | 
			
		||||
    }
 | 
			
		||||
@ -845,8 +850,9 @@ impl App {
 | 
			
		||||
    ///
 | 
			
		||||
    /// See [`App::add_schedule`] to pass in a pre-constructed schedule.
 | 
			
		||||
    pub fn init_schedule(&mut self, label: impl ScheduleLabel) -> &mut Self {
 | 
			
		||||
        let label = label.intern();
 | 
			
		||||
        let mut schedules = self.world.resource_mut::<Schedules>();
 | 
			
		||||
        if !schedules.contains(&label) {
 | 
			
		||||
        if !schedules.contains(label) {
 | 
			
		||||
            schedules.insert(Schedule::new(label));
 | 
			
		||||
        }
 | 
			
		||||
        self
 | 
			
		||||
@ -855,7 +861,7 @@ impl App {
 | 
			
		||||
    /// Gets read-only access to the [`Schedule`] with the provided `label` if it exists.
 | 
			
		||||
    pub fn get_schedule(&self, label: impl ScheduleLabel) -> Option<&Schedule> {
 | 
			
		||||
        let schedules = self.world.get_resource::<Schedules>()?;
 | 
			
		||||
        schedules.get(&label)
 | 
			
		||||
        schedules.get(label)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Gets read-write access to a [`Schedule`] with the provided `label` if it exists.
 | 
			
		||||
@ -863,7 +869,7 @@ impl App {
 | 
			
		||||
        let schedules = self.world.get_resource_mut::<Schedules>()?;
 | 
			
		||||
        // We need to call .into_inner here to satisfy the borrow checker:
 | 
			
		||||
        // it can reason about reborrows using ordinary references but not the `Mut` smart pointer.
 | 
			
		||||
        schedules.into_inner().get_mut(&label)
 | 
			
		||||
        schedules.into_inner().get_mut(label)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Applies the function to the [`Schedule`] associated with `label`.
 | 
			
		||||
@ -874,13 +880,14 @@ impl App {
 | 
			
		||||
        label: impl ScheduleLabel,
 | 
			
		||||
        f: impl FnOnce(&mut Schedule),
 | 
			
		||||
    ) -> &mut Self {
 | 
			
		||||
        let label = label.intern();
 | 
			
		||||
        let mut schedules = self.world.resource_mut::<Schedules>();
 | 
			
		||||
 | 
			
		||||
        if schedules.get(&label).is_none() {
 | 
			
		||||
            schedules.insert(Schedule::new(label.dyn_clone()));
 | 
			
		||||
        if schedules.get(label).is_none() {
 | 
			
		||||
            schedules.insert(Schedule::new(label));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let schedule = schedules.get_mut(&label).unwrap();
 | 
			
		||||
        let schedule = schedules.get_mut(label).unwrap();
 | 
			
		||||
        // Call the function f, passing in the schedule retrieved
 | 
			
		||||
        f(schedule);
 | 
			
		||||
 | 
			
		||||
@ -1008,6 +1015,8 @@ pub struct AppExit;
 | 
			
		||||
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod tests {
 | 
			
		||||
    use std::marker::PhantomData;
 | 
			
		||||
 | 
			
		||||
    use bevy_ecs::{
 | 
			
		||||
        schedule::{OnEnter, States},
 | 
			
		||||
        system::Commands,
 | 
			
		||||
@ -1104,4 +1113,107 @@ mod tests {
 | 
			
		||||
        app.world.run_schedule(OnEnter(AppState::MainMenu));
 | 
			
		||||
        assert_eq!(app.world.entities().len(), 2);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_derive_app_label() {
 | 
			
		||||
        use super::AppLabel;
 | 
			
		||||
        use crate::{self as bevy_app};
 | 
			
		||||
 | 
			
		||||
        #[derive(AppLabel, Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
 | 
			
		||||
        struct UnitLabel;
 | 
			
		||||
 | 
			
		||||
        #[derive(AppLabel, Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
 | 
			
		||||
        struct TupleLabel(u32, u32);
 | 
			
		||||
 | 
			
		||||
        #[derive(AppLabel, Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
 | 
			
		||||
        struct StructLabel {
 | 
			
		||||
            a: u32,
 | 
			
		||||
            b: u32,
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #[derive(AppLabel, Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
 | 
			
		||||
        struct EmptyTupleLabel();
 | 
			
		||||
 | 
			
		||||
        #[derive(AppLabel, Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
 | 
			
		||||
        struct EmptyStructLabel {}
 | 
			
		||||
 | 
			
		||||
        #[derive(AppLabel, Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
 | 
			
		||||
        enum EnumLabel {
 | 
			
		||||
            #[default]
 | 
			
		||||
            Unit,
 | 
			
		||||
            Tuple(u32, u32),
 | 
			
		||||
            Struct {
 | 
			
		||||
                a: u32,
 | 
			
		||||
                b: u32,
 | 
			
		||||
            },
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #[derive(AppLabel, Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
 | 
			
		||||
        struct GenericLabel<T>(PhantomData<T>);
 | 
			
		||||
 | 
			
		||||
        assert_eq!(UnitLabel.intern(), UnitLabel.intern());
 | 
			
		||||
        assert_eq!(EnumLabel::Unit.intern(), EnumLabel::Unit.intern());
 | 
			
		||||
        assert_ne!(UnitLabel.intern(), EnumLabel::Unit.intern());
 | 
			
		||||
        assert_ne!(UnitLabel.intern(), TupleLabel(0, 0).intern());
 | 
			
		||||
        assert_ne!(EnumLabel::Unit.intern(), EnumLabel::Tuple(0, 0).intern());
 | 
			
		||||
 | 
			
		||||
        assert_eq!(TupleLabel(0, 0).intern(), TupleLabel(0, 0).intern());
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            EnumLabel::Tuple(0, 0).intern(),
 | 
			
		||||
            EnumLabel::Tuple(0, 0).intern()
 | 
			
		||||
        );
 | 
			
		||||
        assert_ne!(TupleLabel(0, 0).intern(), TupleLabel(0, 1).intern());
 | 
			
		||||
        assert_ne!(
 | 
			
		||||
            EnumLabel::Tuple(0, 0).intern(),
 | 
			
		||||
            EnumLabel::Tuple(0, 1).intern()
 | 
			
		||||
        );
 | 
			
		||||
        assert_ne!(TupleLabel(0, 0).intern(), EnumLabel::Tuple(0, 0).intern());
 | 
			
		||||
        assert_ne!(
 | 
			
		||||
            TupleLabel(0, 0).intern(),
 | 
			
		||||
            StructLabel { a: 0, b: 0 }.intern()
 | 
			
		||||
        );
 | 
			
		||||
        assert_ne!(
 | 
			
		||||
            EnumLabel::Tuple(0, 0).intern(),
 | 
			
		||||
            EnumLabel::Struct { a: 0, b: 0 }.intern()
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            StructLabel { a: 0, b: 0 }.intern(),
 | 
			
		||||
            StructLabel { a: 0, b: 0 }.intern()
 | 
			
		||||
        );
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            EnumLabel::Struct { a: 0, b: 0 }.intern(),
 | 
			
		||||
            EnumLabel::Struct { a: 0, b: 0 }.intern()
 | 
			
		||||
        );
 | 
			
		||||
        assert_ne!(
 | 
			
		||||
            StructLabel { a: 0, b: 0 }.intern(),
 | 
			
		||||
            StructLabel { a: 0, b: 1 }.intern()
 | 
			
		||||
        );
 | 
			
		||||
        assert_ne!(
 | 
			
		||||
            EnumLabel::Struct { a: 0, b: 0 }.intern(),
 | 
			
		||||
            EnumLabel::Struct { a: 0, b: 1 }.intern()
 | 
			
		||||
        );
 | 
			
		||||
        assert_ne!(
 | 
			
		||||
            StructLabel { a: 0, b: 0 }.intern(),
 | 
			
		||||
            EnumLabel::Struct { a: 0, b: 0 }.intern()
 | 
			
		||||
        );
 | 
			
		||||
        assert_ne!(
 | 
			
		||||
            StructLabel { a: 0, b: 0 }.intern(),
 | 
			
		||||
            EnumLabel::Struct { a: 0, b: 0 }.intern()
 | 
			
		||||
        );
 | 
			
		||||
        assert_ne!(StructLabel { a: 0, b: 0 }.intern(), UnitLabel.intern(),);
 | 
			
		||||
        assert_ne!(
 | 
			
		||||
            EnumLabel::Struct { a: 0, b: 0 }.intern(),
 | 
			
		||||
            EnumLabel::Unit.intern()
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            GenericLabel::<u32>(PhantomData).intern(),
 | 
			
		||||
            GenericLabel::<u32>(PhantomData).intern()
 | 
			
		||||
        );
 | 
			
		||||
        assert_ne!(
 | 
			
		||||
            GenericLabel::<u32>(PhantomData).intern(),
 | 
			
		||||
            GenericLabel::<u64>(PhantomData).intern()
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,6 @@
 | 
			
		||||
use crate::{App, Plugin};
 | 
			
		||||
use bevy_ecs::{
 | 
			
		||||
    schedule::{ExecutorKind, Schedule, ScheduleLabel},
 | 
			
		||||
    schedule::{ExecutorKind, InternedScheduleLabel, Schedule, ScheduleLabel},
 | 
			
		||||
    system::{Local, Resource},
 | 
			
		||||
    world::{Mut, World},
 | 
			
		||||
};
 | 
			
		||||
@ -105,21 +105,21 @@ pub struct Last;
 | 
			
		||||
#[derive(Resource, Debug)]
 | 
			
		||||
pub struct MainScheduleOrder {
 | 
			
		||||
    /// The labels to run for the [`Main`] schedule (in the order they will be run).
 | 
			
		||||
    pub labels: Vec<Box<dyn ScheduleLabel>>,
 | 
			
		||||
    pub labels: Vec<InternedScheduleLabel>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Default for MainScheduleOrder {
 | 
			
		||||
    fn default() -> Self {
 | 
			
		||||
        Self {
 | 
			
		||||
            labels: vec![
 | 
			
		||||
                Box::new(First),
 | 
			
		||||
                Box::new(PreUpdate),
 | 
			
		||||
                Box::new(StateTransition),
 | 
			
		||||
                Box::new(RunFixedUpdateLoop),
 | 
			
		||||
                Box::new(Update),
 | 
			
		||||
                Box::new(SpawnScene),
 | 
			
		||||
                Box::new(PostUpdate),
 | 
			
		||||
                Box::new(Last),
 | 
			
		||||
                First.intern(),
 | 
			
		||||
                PreUpdate.intern(),
 | 
			
		||||
                StateTransition.intern(),
 | 
			
		||||
                RunFixedUpdateLoop.intern(),
 | 
			
		||||
                Update.intern(),
 | 
			
		||||
                SpawnScene.intern(),
 | 
			
		||||
                PostUpdate.intern(),
 | 
			
		||||
                Last.intern(),
 | 
			
		||||
            ],
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@ -133,7 +133,7 @@ impl MainScheduleOrder {
 | 
			
		||||
            .iter()
 | 
			
		||||
            .position(|current| (**current).eq(&after))
 | 
			
		||||
            .unwrap_or_else(|| panic!("Expected {after:?} to exist"));
 | 
			
		||||
        self.labels.insert(index + 1, Box::new(schedule));
 | 
			
		||||
        self.labels.insert(index + 1, schedule.intern());
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -148,8 +148,8 @@ impl Main {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        world.resource_scope(|world, order: Mut<MainScheduleOrder>| {
 | 
			
		||||
            for label in &order.labels {
 | 
			
		||||
                let _ = world.try_run_schedule(&**label);
 | 
			
		||||
            for &label in &order.labels {
 | 
			
		||||
                let _ = world.try_run_schedule(label);
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -201,12 +201,13 @@ pub fn derive_enum_variant_meta(input: TokenStream) -> TokenStream {
 | 
			
		||||
 | 
			
		||||
/// Generates an impl of the `AppLabel` trait.
 | 
			
		||||
///
 | 
			
		||||
/// This works only for unit structs, or enums with only unit variants.
 | 
			
		||||
/// You may force a struct or variant to behave as if it were fieldless with `#[app_label(ignore_fields)]`.
 | 
			
		||||
#[proc_macro_derive(AppLabel, attributes(app_label))]
 | 
			
		||||
/// This does not work for unions.
 | 
			
		||||
#[proc_macro_derive(AppLabel)]
 | 
			
		||||
pub fn derive_app_label(input: TokenStream) -> TokenStream {
 | 
			
		||||
    let input = syn::parse_macro_input!(input as syn::DeriveInput);
 | 
			
		||||
    let mut trait_path = BevyManifest::default().get_path("bevy_app");
 | 
			
		||||
    let mut dyn_eq_path = trait_path.clone();
 | 
			
		||||
    trait_path.segments.push(format_ident!("AppLabel").into());
 | 
			
		||||
    derive_label(input, &trait_path, "app_label")
 | 
			
		||||
    dyn_eq_path.segments.push(format_ident!("DynEq").into());
 | 
			
		||||
    derive_label(input, "AppLabel", &trait_path, &dyn_eq_path)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -2,13 +2,10 @@ extern crate proc_macro;
 | 
			
		||||
 | 
			
		||||
mod component;
 | 
			
		||||
mod fetch;
 | 
			
		||||
mod set;
 | 
			
		||||
mod states;
 | 
			
		||||
 | 
			
		||||
use crate::{fetch::derive_world_query_impl, set::derive_set};
 | 
			
		||||
use bevy_macro_utils::{
 | 
			
		||||
    derive_boxed_label, ensure_no_collision, get_named_struct_fields, BevyManifest,
 | 
			
		||||
};
 | 
			
		||||
use crate::fetch::derive_world_query_impl;
 | 
			
		||||
use bevy_macro_utils::{derive_label, ensure_no_collision, get_named_struct_fields, BevyManifest};
 | 
			
		||||
use proc_macro::TokenStream;
 | 
			
		||||
use proc_macro2::Span;
 | 
			
		||||
use quote::{format_ident, quote};
 | 
			
		||||
@ -438,25 +435,33 @@ pub fn derive_world_query(input: TokenStream) -> TokenStream {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Derive macro generating an impl of the trait `ScheduleLabel`.
 | 
			
		||||
///
 | 
			
		||||
/// This does not work for unions.
 | 
			
		||||
#[proc_macro_derive(ScheduleLabel)]
 | 
			
		||||
pub fn derive_schedule_label(input: TokenStream) -> TokenStream {
 | 
			
		||||
    let input = parse_macro_input!(input as DeriveInput);
 | 
			
		||||
    let mut trait_path = bevy_ecs_path();
 | 
			
		||||
    trait_path.segments.push(format_ident!("schedule").into());
 | 
			
		||||
    let mut dyn_eq_path = trait_path.clone();
 | 
			
		||||
    trait_path
 | 
			
		||||
        .segments
 | 
			
		||||
        .push(format_ident!("ScheduleLabel").into());
 | 
			
		||||
    derive_boxed_label(input, &trait_path)
 | 
			
		||||
    dyn_eq_path.segments.push(format_ident!("DynEq").into());
 | 
			
		||||
    derive_label(input, "ScheduleName", &trait_path, &dyn_eq_path)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Derive macro generating an impl of the trait `SystemSet`.
 | 
			
		||||
///
 | 
			
		||||
/// This does not work for unions.
 | 
			
		||||
#[proc_macro_derive(SystemSet)]
 | 
			
		||||
pub fn derive_system_set(input: TokenStream) -> TokenStream {
 | 
			
		||||
    let input = parse_macro_input!(input as DeriveInput);
 | 
			
		||||
    let mut trait_path = bevy_ecs_path();
 | 
			
		||||
    trait_path.segments.push(format_ident!("schedule").into());
 | 
			
		||||
    let mut dyn_eq_path = trait_path.clone();
 | 
			
		||||
    trait_path.segments.push(format_ident!("SystemSet").into());
 | 
			
		||||
    derive_set(input, &trait_path)
 | 
			
		||||
    dyn_eq_path.segments.push(format_ident!("DynEq").into());
 | 
			
		||||
    derive_label(input, "SystemSet", &trait_path, &dyn_eq_path)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub(crate) fn bevy_ecs_path() -> syn::Path {
 | 
			
		||||
 | 
			
		||||
@ -1,33 +0,0 @@
 | 
			
		||||
use proc_macro::TokenStream;
 | 
			
		||||
use quote::quote;
 | 
			
		||||
 | 
			
		||||
/// Derive a set trait
 | 
			
		||||
///
 | 
			
		||||
/// # Args
 | 
			
		||||
///
 | 
			
		||||
/// - `input`: The [`syn::DeriveInput`] for the struct that we want to derive the set trait for
 | 
			
		||||
/// - `trait_path`: The [`syn::Path`] to the set trait
 | 
			
		||||
pub fn derive_set(input: syn::DeriveInput, trait_path: &syn::Path) -> TokenStream {
 | 
			
		||||
    let ident = input.ident;
 | 
			
		||||
 | 
			
		||||
    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
 | 
			
		||||
    let mut where_clause = where_clause.cloned().unwrap_or_else(|| syn::WhereClause {
 | 
			
		||||
        where_token: Default::default(),
 | 
			
		||||
        predicates: Default::default(),
 | 
			
		||||
    });
 | 
			
		||||
    where_clause.predicates.push(
 | 
			
		||||
        syn::parse2(quote! {
 | 
			
		||||
            Self: 'static + Send + Sync + Clone + Eq + ::std::fmt::Debug + ::std::hash::Hash
 | 
			
		||||
        })
 | 
			
		||||
        .unwrap(),
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    (quote! {
 | 
			
		||||
        impl #impl_generics #trait_path for #ident #ty_generics #where_clause {
 | 
			
		||||
            fn dyn_clone(&self) -> std::boxed::Box<dyn #trait_path> {
 | 
			
		||||
                std::boxed::Box::new(std::clone::Clone::clone(self))
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    })
 | 
			
		||||
    .into()
 | 
			
		||||
}
 | 
			
		||||
@ -4,7 +4,7 @@ use crate::{
 | 
			
		||||
    schedule::{
 | 
			
		||||
        condition::{BoxedCondition, Condition},
 | 
			
		||||
        graph_utils::{Ambiguity, Dependency, DependencyKind, GraphInfo},
 | 
			
		||||
        set::{BoxedSystemSet, IntoSystemSet, SystemSet},
 | 
			
		||||
        set::{InternedSystemSet, IntoSystemSet, SystemSet},
 | 
			
		||||
    },
 | 
			
		||||
    system::{BoxedSystem, IntoSystem, System},
 | 
			
		||||
};
 | 
			
		||||
@ -20,7 +20,7 @@ fn new_condition<M>(condition: impl Condition<M>) -> BoxedCondition {
 | 
			
		||||
    Box::new(condition_system)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn ambiguous_with(graph_info: &mut GraphInfo, set: BoxedSystemSet) {
 | 
			
		||||
fn ambiguous_with(graph_info: &mut GraphInfo, set: InternedSystemSet) {
 | 
			
		||||
    match &mut graph_info.ambiguous_with {
 | 
			
		||||
        detection @ Ambiguity::Check => {
 | 
			
		||||
            *detection = Ambiguity::IgnoreWithSet(vec![set]);
 | 
			
		||||
@ -92,20 +92,20 @@ impl SystemConfigs {
 | 
			
		||||
 | 
			
		||||
impl<T> NodeConfigs<T> {
 | 
			
		||||
    /// Adds a new boxed system set to the systems.
 | 
			
		||||
    pub fn in_set_dyn(&mut self, set: BoxedSystemSet) {
 | 
			
		||||
    pub fn in_set_inner(&mut self, set: InternedSystemSet) {
 | 
			
		||||
        match self {
 | 
			
		||||
            Self::NodeConfig(config) => {
 | 
			
		||||
                config.graph_info.sets.push(set);
 | 
			
		||||
            }
 | 
			
		||||
            Self::Configs { configs, .. } => {
 | 
			
		||||
                for config in configs {
 | 
			
		||||
                    config.in_set_dyn(set.dyn_clone());
 | 
			
		||||
                    config.in_set_inner(set);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn before_inner(&mut self, set: BoxedSystemSet) {
 | 
			
		||||
    fn before_inner(&mut self, set: InternedSystemSet) {
 | 
			
		||||
        match self {
 | 
			
		||||
            Self::NodeConfig(config) => {
 | 
			
		||||
                config
 | 
			
		||||
@ -115,13 +115,13 @@ impl<T> NodeConfigs<T> {
 | 
			
		||||
            }
 | 
			
		||||
            Self::Configs { configs, .. } => {
 | 
			
		||||
                for config in configs {
 | 
			
		||||
                    config.before_inner(set.dyn_clone());
 | 
			
		||||
                    config.before_inner(set);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn after_inner(&mut self, set: BoxedSystemSet) {
 | 
			
		||||
    fn after_inner(&mut self, set: InternedSystemSet) {
 | 
			
		||||
        match self {
 | 
			
		||||
            Self::NodeConfig(config) => {
 | 
			
		||||
                config
 | 
			
		||||
@ -131,7 +131,7 @@ impl<T> NodeConfigs<T> {
 | 
			
		||||
            }
 | 
			
		||||
            Self::Configs { configs, .. } => {
 | 
			
		||||
                for config in configs {
 | 
			
		||||
                    config.after_inner(set.dyn_clone());
 | 
			
		||||
                    config.after_inner(set);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
@ -150,14 +150,14 @@ impl<T> NodeConfigs<T> {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn ambiguous_with_inner(&mut self, set: BoxedSystemSet) {
 | 
			
		||||
    fn ambiguous_with_inner(&mut self, set: InternedSystemSet) {
 | 
			
		||||
        match self {
 | 
			
		||||
            Self::NodeConfig(config) => {
 | 
			
		||||
                ambiguous_with(&mut config.graph_info, set);
 | 
			
		||||
            }
 | 
			
		||||
            Self::Configs { configs, .. } => {
 | 
			
		||||
                for config in configs {
 | 
			
		||||
                    config.ambiguous_with_inner(set.dyn_clone());
 | 
			
		||||
                    config.ambiguous_with_inner(set);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
@ -330,20 +330,20 @@ impl IntoSystemConfigs<()> for SystemConfigs {
 | 
			
		||||
            "adding arbitrary systems to a system type set is not allowed"
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        self.in_set_dyn(set.dyn_clone());
 | 
			
		||||
        self.in_set_inner(set.intern());
 | 
			
		||||
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn before<M>(mut self, set: impl IntoSystemSet<M>) -> Self {
 | 
			
		||||
        let set = set.into_system_set();
 | 
			
		||||
        self.before_inner(set.dyn_clone());
 | 
			
		||||
        self.before_inner(set.intern());
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn after<M>(mut self, set: impl IntoSystemSet<M>) -> Self {
 | 
			
		||||
        let set = set.into_system_set();
 | 
			
		||||
        self.after_inner(set.dyn_clone());
 | 
			
		||||
        self.after_inner(set.intern());
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -354,7 +354,7 @@ impl IntoSystemConfigs<()> for SystemConfigs {
 | 
			
		||||
 | 
			
		||||
    fn ambiguous_with<M>(mut self, set: impl IntoSystemSet<M>) -> Self {
 | 
			
		||||
        let set = set.into_system_set();
 | 
			
		||||
        self.ambiguous_with_inner(set.dyn_clone());
 | 
			
		||||
        self.ambiguous_with_inner(set.intern());
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -398,11 +398,11 @@ macro_rules! impl_system_collection {
 | 
			
		||||
all_tuples!(impl_system_collection, 1, 20, P, S);
 | 
			
		||||
 | 
			
		||||
/// A [`SystemSet`] with scheduling metadata.
 | 
			
		||||
pub type SystemSetConfig = NodeConfig<BoxedSystemSet>;
 | 
			
		||||
pub type SystemSetConfig = NodeConfig<InternedSystemSet>;
 | 
			
		||||
 | 
			
		||||
impl SystemSetConfig {
 | 
			
		||||
    #[track_caller]
 | 
			
		||||
    pub(super) fn new(set: BoxedSystemSet) -> Self {
 | 
			
		||||
    pub(super) fn new(set: InternedSystemSet) -> Self {
 | 
			
		||||
        // system type sets are automatically populated
 | 
			
		||||
        // to avoid unintentionally broad changes, they cannot be configured
 | 
			
		||||
        assert!(
 | 
			
		||||
@ -419,7 +419,7 @@ impl SystemSetConfig {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// A collection of [`SystemSetConfig`].
 | 
			
		||||
pub type SystemSetConfigs = NodeConfigs<BoxedSystemSet>;
 | 
			
		||||
pub type SystemSetConfigs = NodeConfigs<InternedSystemSet>;
 | 
			
		||||
 | 
			
		||||
/// Types that can convert into a [`SystemSetConfigs`].
 | 
			
		||||
pub trait IntoSystemSetConfigs
 | 
			
		||||
@ -485,21 +485,21 @@ impl IntoSystemSetConfigs for SystemSetConfigs {
 | 
			
		||||
            set.system_type().is_none(),
 | 
			
		||||
            "adding arbitrary systems to a system type set is not allowed"
 | 
			
		||||
        );
 | 
			
		||||
        self.in_set_dyn(set.dyn_clone());
 | 
			
		||||
        self.in_set_inner(set.intern());
 | 
			
		||||
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn before<M>(mut self, set: impl IntoSystemSet<M>) -> Self {
 | 
			
		||||
        let set = set.into_system_set();
 | 
			
		||||
        self.before_inner(set.dyn_clone());
 | 
			
		||||
        self.before_inner(set.intern());
 | 
			
		||||
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn after<M>(mut self, set: impl IntoSystemSet<M>) -> Self {
 | 
			
		||||
        let set = set.into_system_set();
 | 
			
		||||
        self.after_inner(set.dyn_clone());
 | 
			
		||||
        self.after_inner(set.intern());
 | 
			
		||||
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
@ -512,7 +512,7 @@ impl IntoSystemSetConfigs for SystemSetConfigs {
 | 
			
		||||
 | 
			
		||||
    fn ambiguous_with<M>(mut self, set: impl IntoSystemSet<M>) -> Self {
 | 
			
		||||
        let set = set.into_system_set();
 | 
			
		||||
        self.ambiguous_with_inner(set.dyn_clone());
 | 
			
		||||
        self.ambiguous_with_inner(set.intern());
 | 
			
		||||
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
@ -530,13 +530,7 @@ impl IntoSystemSetConfigs for SystemSetConfigs {
 | 
			
		||||
 | 
			
		||||
impl<S: SystemSet> IntoSystemSetConfigs for S {
 | 
			
		||||
    fn into_configs(self) -> SystemSetConfigs {
 | 
			
		||||
        SystemSetConfigs::NodeConfig(SystemSetConfig::new(Box::new(self)))
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl IntoSystemSetConfigs for BoxedSystemSet {
 | 
			
		||||
    fn into_configs(self) -> SystemSetConfigs {
 | 
			
		||||
        SystemSetConfigs::NodeConfig(SystemSetConfig::new(self))
 | 
			
		||||
        SystemSetConfigs::NodeConfig(SystemSetConfig::new(self.intern()))
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -51,11 +51,11 @@ pub(crate) enum DependencyKind {
 | 
			
		||||
#[derive(Clone)]
 | 
			
		||||
pub(crate) struct Dependency {
 | 
			
		||||
    pub(crate) kind: DependencyKind,
 | 
			
		||||
    pub(crate) set: BoxedSystemSet,
 | 
			
		||||
    pub(crate) set: InternedSystemSet,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Dependency {
 | 
			
		||||
    pub fn new(kind: DependencyKind, set: BoxedSystemSet) -> Self {
 | 
			
		||||
    pub fn new(kind: DependencyKind, set: InternedSystemSet) -> Self {
 | 
			
		||||
        Self { kind, set }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -66,14 +66,14 @@ pub(crate) enum Ambiguity {
 | 
			
		||||
    #[default]
 | 
			
		||||
    Check,
 | 
			
		||||
    /// Ignore warnings with systems in any of these system sets. May contain duplicates.
 | 
			
		||||
    IgnoreWithSet(Vec<BoxedSystemSet>),
 | 
			
		||||
    IgnoreWithSet(Vec<InternedSystemSet>),
 | 
			
		||||
    /// Ignore all warnings.
 | 
			
		||||
    IgnoreAll,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Default)]
 | 
			
		||||
pub(crate) struct GraphInfo {
 | 
			
		||||
    pub(crate) sets: Vec<BoxedSystemSet>,
 | 
			
		||||
    pub(crate) sets: Vec<InternedSystemSet>,
 | 
			
		||||
    pub(crate) dependencies: Vec<Dependency>,
 | 
			
		||||
    pub(crate) ambiguous_with: Ambiguity,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1011,7 +1011,7 @@ mod tests {
 | 
			
		||||
            schedule.graph_mut().initialize(&mut world);
 | 
			
		||||
            let _ = schedule.graph_mut().build_schedule(
 | 
			
		||||
                world.components(),
 | 
			
		||||
                &TestSchedule.dyn_clone(),
 | 
			
		||||
                TestSchedule.intern(),
 | 
			
		||||
                &BTreeSet::new(),
 | 
			
		||||
            );
 | 
			
		||||
 | 
			
		||||
@ -1060,7 +1060,7 @@ mod tests {
 | 
			
		||||
            schedule.graph_mut().initialize(&mut world);
 | 
			
		||||
            let _ = schedule.graph_mut().build_schedule(
 | 
			
		||||
                world.components(),
 | 
			
		||||
                &TestSchedule.dyn_clone(),
 | 
			
		||||
                TestSchedule.intern(),
 | 
			
		||||
                &BTreeSet::new(),
 | 
			
		||||
            );
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -28,7 +28,7 @@ use crate::{
 | 
			
		||||
/// Resource that stores [`Schedule`]s mapped to [`ScheduleLabel`]s.
 | 
			
		||||
#[derive(Default, Resource)]
 | 
			
		||||
pub struct Schedules {
 | 
			
		||||
    inner: HashMap<BoxedScheduleLabel, Schedule>,
 | 
			
		||||
    inner: HashMap<InternedScheduleLabel, Schedule>,
 | 
			
		||||
    /// List of [`ComponentId`]s to ignore when reporting system order ambiguity conflicts
 | 
			
		||||
    pub ignored_scheduling_ambiguities: BTreeSet<ComponentId>,
 | 
			
		||||
}
 | 
			
		||||
@ -47,36 +47,35 @@ impl Schedules {
 | 
			
		||||
    /// If the map already had an entry for `label`, `schedule` is inserted,
 | 
			
		||||
    /// and the old schedule is returned. Otherwise, `None` is returned.
 | 
			
		||||
    pub fn insert(&mut self, schedule: Schedule) -> Option<Schedule> {
 | 
			
		||||
        let label = schedule.name.dyn_clone();
 | 
			
		||||
        self.inner.insert(label, schedule)
 | 
			
		||||
        self.inner.insert(schedule.name, schedule)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Removes the schedule corresponding to the `label` from the map, returning it if it existed.
 | 
			
		||||
    pub fn remove(&mut self, label: &dyn ScheduleLabel) -> Option<Schedule> {
 | 
			
		||||
        self.inner.remove(label)
 | 
			
		||||
    pub fn remove(&mut self, label: impl ScheduleLabel) -> Option<Schedule> {
 | 
			
		||||
        self.inner.remove(&label.intern())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Removes the (schedule, label) pair corresponding to the `label` from the map, returning it if it existed.
 | 
			
		||||
    pub fn remove_entry(
 | 
			
		||||
        &mut self,
 | 
			
		||||
        label: &dyn ScheduleLabel,
 | 
			
		||||
    ) -> Option<(Box<dyn ScheduleLabel>, Schedule)> {
 | 
			
		||||
        self.inner.remove_entry(label)
 | 
			
		||||
        label: impl ScheduleLabel,
 | 
			
		||||
    ) -> Option<(InternedScheduleLabel, Schedule)> {
 | 
			
		||||
        self.inner.remove_entry(&label.intern())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Does a schedule with the provided label already exist?
 | 
			
		||||
    pub fn contains(&self, label: &dyn ScheduleLabel) -> bool {
 | 
			
		||||
        self.inner.contains_key(label)
 | 
			
		||||
    pub fn contains(&self, label: impl ScheduleLabel) -> bool {
 | 
			
		||||
        self.inner.contains_key(&label.intern())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Returns a reference to the schedule associated with `label`, if it exists.
 | 
			
		||||
    pub fn get(&self, label: &dyn ScheduleLabel) -> Option<&Schedule> {
 | 
			
		||||
        self.inner.get(label)
 | 
			
		||||
    pub fn get(&self, label: impl ScheduleLabel) -> Option<&Schedule> {
 | 
			
		||||
        self.inner.get(&label.intern())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Returns a mutable reference to the schedule associated with `label`, if it exists.
 | 
			
		||||
    pub fn get_mut(&mut self, label: &dyn ScheduleLabel) -> Option<&mut Schedule> {
 | 
			
		||||
        self.inner.get_mut(label)
 | 
			
		||||
    pub fn get_mut(&mut self, label: impl ScheduleLabel) -> Option<&mut Schedule> {
 | 
			
		||||
        self.inner.get_mut(&label.intern())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Returns an iterator over all schedules. Iteration order is undefined.
 | 
			
		||||
@ -195,7 +194,7 @@ fn make_executor(kind: ExecutorKind) -> Box<dyn SystemExecutor> {
 | 
			
		||||
/// }
 | 
			
		||||
/// ```
 | 
			
		||||
pub struct Schedule {
 | 
			
		||||
    name: BoxedScheduleLabel,
 | 
			
		||||
    name: InternedScheduleLabel,
 | 
			
		||||
    graph: ScheduleGraph,
 | 
			
		||||
    executable: SystemSchedule,
 | 
			
		||||
    executor: Box<dyn SystemExecutor>,
 | 
			
		||||
@ -219,7 +218,7 @@ impl Schedule {
 | 
			
		||||
    /// Constructs an empty `Schedule`.
 | 
			
		||||
    pub fn new(label: impl ScheduleLabel) -> Self {
 | 
			
		||||
        Self {
 | 
			
		||||
            name: label.dyn_clone(),
 | 
			
		||||
            name: label.intern(),
 | 
			
		||||
            graph: ScheduleGraph::new(),
 | 
			
		||||
            executable: SystemSchedule::new(),
 | 
			
		||||
            executor: make_executor(ExecutorKind::default()),
 | 
			
		||||
@ -307,7 +306,7 @@ impl Schedule {
 | 
			
		||||
                &mut self.executable,
 | 
			
		||||
                world.components(),
 | 
			
		||||
                &ignored_ambiguities,
 | 
			
		||||
                &self.name,
 | 
			
		||||
                self.name,
 | 
			
		||||
            )?;
 | 
			
		||||
            self.graph.changed = false;
 | 
			
		||||
            self.executor_initialized = false;
 | 
			
		||||
@ -401,11 +400,11 @@ impl Dag {
 | 
			
		||||
 | 
			
		||||
/// A [`SystemSet`] with metadata, stored in a [`ScheduleGraph`].
 | 
			
		||||
struct SystemSetNode {
 | 
			
		||||
    inner: BoxedSystemSet,
 | 
			
		||||
    inner: InternedSystemSet,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl SystemSetNode {
 | 
			
		||||
    pub fn new(set: BoxedSystemSet) -> Self {
 | 
			
		||||
    pub fn new(set: InternedSystemSet) -> Self {
 | 
			
		||||
        Self { inner: set }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -450,13 +449,14 @@ pub struct ScheduleGraph {
 | 
			
		||||
    system_conditions: Vec<Vec<BoxedCondition>>,
 | 
			
		||||
    system_sets: Vec<SystemSetNode>,
 | 
			
		||||
    system_set_conditions: Vec<Vec<BoxedCondition>>,
 | 
			
		||||
    system_set_ids: HashMap<BoxedSystemSet, NodeId>,
 | 
			
		||||
    system_set_ids: HashMap<InternedSystemSet, NodeId>,
 | 
			
		||||
    uninit: Vec<(NodeId, usize)>,
 | 
			
		||||
    hierarchy: Dag,
 | 
			
		||||
    dependency: Dag,
 | 
			
		||||
    ambiguous_with: UnGraphMap<NodeId, ()>,
 | 
			
		||||
    ambiguous_with_all: HashSet<NodeId>,
 | 
			
		||||
    conflicting_systems: Vec<(NodeId, NodeId, Vec<ComponentId>)>,
 | 
			
		||||
    anonymous_sets: usize,
 | 
			
		||||
    changed: bool,
 | 
			
		||||
    settings: ScheduleBuildSettings,
 | 
			
		||||
}
 | 
			
		||||
@ -476,6 +476,7 @@ impl ScheduleGraph {
 | 
			
		||||
            ambiguous_with: UnGraphMap::new(),
 | 
			
		||||
            ambiguous_with_all: HashSet::new(),
 | 
			
		||||
            conflicting_systems: Vec::new(),
 | 
			
		||||
            anonymous_sets: 0,
 | 
			
		||||
            changed: false,
 | 
			
		||||
            settings: default(),
 | 
			
		||||
        }
 | 
			
		||||
@ -604,11 +605,11 @@ impl ScheduleGraph {
 | 
			
		||||
                let more_than_one_entry = configs.len() > 1;
 | 
			
		||||
                if !collective_conditions.is_empty() {
 | 
			
		||||
                    if more_than_one_entry {
 | 
			
		||||
                        let set = AnonymousSet::new();
 | 
			
		||||
                        let set = self.create_anonymous_set();
 | 
			
		||||
                        for config in &mut configs {
 | 
			
		||||
                            config.in_set_dyn(set.dyn_clone());
 | 
			
		||||
                            config.in_set_inner(set.intern());
 | 
			
		||||
                        }
 | 
			
		||||
                        let mut set_config = SystemSetConfig::new(set.dyn_clone());
 | 
			
		||||
                        let mut set_config = SystemSetConfig::new(set.intern());
 | 
			
		||||
                        set_config.conditions.extend(collective_conditions);
 | 
			
		||||
                        self.configure_set_inner(set_config).unwrap();
 | 
			
		||||
                    } else {
 | 
			
		||||
@ -748,7 +749,7 @@ impl ScheduleGraph {
 | 
			
		||||
 | 
			
		||||
        let id = match self.system_set_ids.get(&set) {
 | 
			
		||||
            Some(&id) => id,
 | 
			
		||||
            None => self.add_set(set.dyn_clone()),
 | 
			
		||||
            None => self.add_set(set),
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        // graph updates are immediate
 | 
			
		||||
@ -762,36 +763,42 @@ impl ScheduleGraph {
 | 
			
		||||
        Ok(id)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn add_set(&mut self, set: BoxedSystemSet) -> NodeId {
 | 
			
		||||
    fn add_set(&mut self, set: InternedSystemSet) -> NodeId {
 | 
			
		||||
        let id = NodeId::Set(self.system_sets.len());
 | 
			
		||||
        self.system_sets.push(SystemSetNode::new(set.dyn_clone()));
 | 
			
		||||
        self.system_sets.push(SystemSetNode::new(set));
 | 
			
		||||
        self.system_set_conditions.push(Vec::new());
 | 
			
		||||
        self.system_set_ids.insert(set, id);
 | 
			
		||||
        id
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn check_set(&mut self, id: &NodeId, set: &dyn SystemSet) -> Result<(), ScheduleBuildError> {
 | 
			
		||||
        match self.system_set_ids.get(set) {
 | 
			
		||||
    fn check_set(&mut self, id: &NodeId, set: InternedSystemSet) -> Result<(), ScheduleBuildError> {
 | 
			
		||||
        match self.system_set_ids.get(&set) {
 | 
			
		||||
            Some(set_id) => {
 | 
			
		||||
                if id == set_id {
 | 
			
		||||
                    return Err(ScheduleBuildError::HierarchyLoop(self.get_node_name(id)));
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            None => {
 | 
			
		||||
                self.add_set(set.dyn_clone());
 | 
			
		||||
                self.add_set(set);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn create_anonymous_set(&mut self) -> AnonymousSet {
 | 
			
		||||
        let id = self.anonymous_sets;
 | 
			
		||||
        self.anonymous_sets += 1;
 | 
			
		||||
        AnonymousSet::new(id)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn check_sets(
 | 
			
		||||
        &mut self,
 | 
			
		||||
        id: &NodeId,
 | 
			
		||||
        graph_info: &GraphInfo,
 | 
			
		||||
    ) -> Result<(), ScheduleBuildError> {
 | 
			
		||||
        for set in &graph_info.sets {
 | 
			
		||||
            self.check_set(id, &**set)?;
 | 
			
		||||
        for &set in &graph_info.sets {
 | 
			
		||||
            self.check_set(id, set)?;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Ok(())
 | 
			
		||||
@ -810,7 +817,7 @@ impl ScheduleGraph {
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                None => {
 | 
			
		||||
                    self.add_set(set.dyn_clone());
 | 
			
		||||
                    self.add_set(*set);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
@ -818,7 +825,7 @@ impl ScheduleGraph {
 | 
			
		||||
        if let Ambiguity::IgnoreWithSet(ambiguous_with) = &graph_info.ambiguous_with {
 | 
			
		||||
            for set in ambiguous_with {
 | 
			
		||||
                if !self.system_set_ids.contains_key(set) {
 | 
			
		||||
                    self.add_set(set.dyn_clone());
 | 
			
		||||
                    self.add_set(*set);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
@ -915,7 +922,7 @@ impl ScheduleGraph {
 | 
			
		||||
    pub fn build_schedule(
 | 
			
		||||
        &mut self,
 | 
			
		||||
        components: &Components,
 | 
			
		||||
        schedule_label: &BoxedScheduleLabel,
 | 
			
		||||
        schedule_label: InternedScheduleLabel,
 | 
			
		||||
        ignored_ambiguities: &BTreeSet<ComponentId>,
 | 
			
		||||
    ) -> Result<SystemSchedule, ScheduleBuildError> {
 | 
			
		||||
        // check hierarchy for cycles
 | 
			
		||||
@ -1229,7 +1236,7 @@ impl ScheduleGraph {
 | 
			
		||||
        schedule: &mut SystemSchedule,
 | 
			
		||||
        components: &Components,
 | 
			
		||||
        ignored_ambiguities: &BTreeSet<ComponentId>,
 | 
			
		||||
        schedule_label: &BoxedScheduleLabel,
 | 
			
		||||
        schedule_label: InternedScheduleLabel,
 | 
			
		||||
    ) -> Result<(), ScheduleBuildError> {
 | 
			
		||||
        if !self.uninit.is_empty() {
 | 
			
		||||
            return Err(ScheduleBuildError::Uninitialized);
 | 
			
		||||
@ -1296,7 +1303,7 @@ impl ProcessNodeConfig for BoxedSystem {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl ProcessNodeConfig for BoxedSystemSet {
 | 
			
		||||
impl ProcessNodeConfig for InternedSystemSet {
 | 
			
		||||
    fn process_config(schedule_graph: &mut ScheduleGraph, config: NodeConfig<Self>) -> NodeId {
 | 
			
		||||
        schedule_graph.configure_set_inner(config).unwrap()
 | 
			
		||||
    }
 | 
			
		||||
@ -1372,7 +1379,7 @@ impl ScheduleGraph {
 | 
			
		||||
    fn optionally_check_hierarchy_conflicts(
 | 
			
		||||
        &self,
 | 
			
		||||
        transitive_edges: &[(NodeId, NodeId)],
 | 
			
		||||
        schedule_label: &BoxedScheduleLabel,
 | 
			
		||||
        schedule_label: InternedScheduleLabel,
 | 
			
		||||
    ) -> Result<(), ScheduleBuildError> {
 | 
			
		||||
        if self.settings.hierarchy_detection == LogLevel::Ignore || transitive_edges.is_empty() {
 | 
			
		||||
            return Ok(());
 | 
			
		||||
@ -1584,7 +1591,7 @@ impl ScheduleGraph {
 | 
			
		||||
        &self,
 | 
			
		||||
        conflicts: &[(NodeId, NodeId, Vec<ComponentId>)],
 | 
			
		||||
        components: &Components,
 | 
			
		||||
        schedule_label: &BoxedScheduleLabel,
 | 
			
		||||
        schedule_label: InternedScheduleLabel,
 | 
			
		||||
    ) -> Result<(), ScheduleBuildError> {
 | 
			
		||||
        if self.settings.ambiguity_detection == LogLevel::Ignore || conflicts.is_empty() {
 | 
			
		||||
            return Ok(());
 | 
			
		||||
 | 
			
		||||
@ -2,25 +2,27 @@ use std::any::TypeId;
 | 
			
		||||
use std::fmt::Debug;
 | 
			
		||||
use std::hash::{Hash, Hasher};
 | 
			
		||||
use std::marker::PhantomData;
 | 
			
		||||
use std::sync::atomic::{AtomicUsize, Ordering};
 | 
			
		||||
 | 
			
		||||
pub use bevy_ecs_macros::{ScheduleLabel, SystemSet};
 | 
			
		||||
use bevy_utils::define_boxed_label;
 | 
			
		||||
use bevy_utils::label::DynHash;
 | 
			
		||||
use bevy_utils::define_label;
 | 
			
		||||
use bevy_utils::intern::Interned;
 | 
			
		||||
pub use bevy_utils::label::DynEq;
 | 
			
		||||
 | 
			
		||||
use crate::system::{
 | 
			
		||||
    ExclusiveSystemParamFunction, IsExclusiveFunctionSystem, IsFunctionSystem, SystemParamFunction,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
define_boxed_label!(ScheduleLabel);
 | 
			
		||||
 | 
			
		||||
/// A shorthand for `Box<dyn SystemSet>`.
 | 
			
		||||
pub type BoxedSystemSet = Box<dyn SystemSet>;
 | 
			
		||||
/// A shorthand for `Box<dyn ScheduleLabel>`.
 | 
			
		||||
pub type BoxedScheduleLabel = Box<dyn ScheduleLabel>;
 | 
			
		||||
define_label!(
 | 
			
		||||
    /// A strongly-typed class of labels used to identify an [`Schedule`].
 | 
			
		||||
    ScheduleLabel,
 | 
			
		||||
    SCHEDULE_LABEL_INTERNER
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
define_label!(
 | 
			
		||||
    /// Types that identify logical groups of systems.
 | 
			
		||||
pub trait SystemSet: DynHash + Debug + Send + Sync + 'static {
 | 
			
		||||
    SystemSet,
 | 
			
		||||
    SYSTEM_SET_INTERNER,
 | 
			
		||||
    extra_methods: {
 | 
			
		||||
        /// Returns `Some` if this system set is a [`SystemTypeSet`].
 | 
			
		||||
        fn system_type(&self) -> Option<TypeId> {
 | 
			
		||||
            None
 | 
			
		||||
@ -30,29 +32,22 @@ pub trait SystemSet: DynHash + Debug + Send + Sync + 'static {
 | 
			
		||||
        fn is_anonymous(&self) -> bool {
 | 
			
		||||
            false
 | 
			
		||||
        }
 | 
			
		||||
    /// Creates a boxed clone of the label corresponding to this system set.
 | 
			
		||||
    fn dyn_clone(&self) -> Box<dyn SystemSet>;
 | 
			
		||||
    },
 | 
			
		||||
    extra_methods_impl: {
 | 
			
		||||
        fn system_type(&self) -> Option<TypeId> {
 | 
			
		||||
            (**self).system_type()
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
impl PartialEq for dyn SystemSet {
 | 
			
		||||
    fn eq(&self, other: &Self) -> bool {
 | 
			
		||||
        self.dyn_eq(other.as_dyn_eq())
 | 
			
		||||
        fn is_anonymous(&self) -> bool {
 | 
			
		||||
            (**self).is_anonymous()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
impl Eq for dyn SystemSet {}
 | 
			
		||||
 | 
			
		||||
impl Hash for dyn SystemSet {
 | 
			
		||||
    fn hash<H: Hasher>(&self, state: &mut H) {
 | 
			
		||||
        self.dyn_hash(state);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Clone for Box<dyn SystemSet> {
 | 
			
		||||
    fn clone(&self) -> Self {
 | 
			
		||||
        self.dyn_clone()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
/// A shorthand for `Interned<dyn SystemSet>`.
 | 
			
		||||
pub type InternedSystemSet = Interned<dyn SystemSet>;
 | 
			
		||||
/// A shorthand for `Interned<dyn ScheduleLabel>`.
 | 
			
		||||
pub type InternedScheduleLabel = Interned<dyn ScheduleLabel>;
 | 
			
		||||
 | 
			
		||||
/// A [`SystemSet`] grouping instances of the same function.
 | 
			
		||||
///
 | 
			
		||||
@ -108,6 +103,15 @@ impl<T> SystemSet for SystemTypeSet<T> {
 | 
			
		||||
    fn dyn_clone(&self) -> Box<dyn SystemSet> {
 | 
			
		||||
        Box::new(*self)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn as_dyn_eq(&self) -> &dyn DynEq {
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn dyn_hash(&self, mut state: &mut dyn Hasher) {
 | 
			
		||||
        std::any::TypeId::of::<Self>().hash(&mut state);
 | 
			
		||||
        self.hash(&mut state);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// A [`SystemSet`] implicitly created when using
 | 
			
		||||
@ -116,11 +120,9 @@ impl<T> SystemSet for SystemTypeSet<T> {
 | 
			
		||||
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
 | 
			
		||||
pub struct AnonymousSet(usize);
 | 
			
		||||
 | 
			
		||||
static NEXT_ANONYMOUS_SET_ID: AtomicUsize = AtomicUsize::new(0);
 | 
			
		||||
 | 
			
		||||
impl AnonymousSet {
 | 
			
		||||
    pub(crate) fn new() -> Self {
 | 
			
		||||
        Self(NEXT_ANONYMOUS_SET_ID.fetch_add(1, Ordering::Relaxed))
 | 
			
		||||
    pub(crate) fn new(id: usize) -> Self {
 | 
			
		||||
        Self(id)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -129,6 +131,15 @@ impl SystemSet for AnonymousSet {
 | 
			
		||||
        true
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn as_dyn_eq(&self) -> &dyn DynEq {
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn dyn_hash(&self, mut state: &mut dyn Hasher) {
 | 
			
		||||
        std::any::TypeId::of::<Self>().hash(&mut state);
 | 
			
		||||
        self.hash(&mut state);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn dyn_clone(&self) -> Box<dyn SystemSet> {
 | 
			
		||||
        Box::new(*self)
 | 
			
		||||
    }
 | 
			
		||||
@ -189,7 +200,7 @@ mod tests {
 | 
			
		||||
    use super::*;
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_boxed_label() {
 | 
			
		||||
    fn test_schedule_label() {
 | 
			
		||||
        use crate::{self as bevy_ecs, world::World};
 | 
			
		||||
 | 
			
		||||
        #[derive(Resource)]
 | 
			
		||||
@ -198,20 +209,220 @@ mod tests {
 | 
			
		||||
        #[derive(ScheduleLabel, Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
 | 
			
		||||
        struct A;
 | 
			
		||||
 | 
			
		||||
        #[derive(ScheduleLabel, Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
 | 
			
		||||
        struct B;
 | 
			
		||||
 | 
			
		||||
        let mut world = World::new();
 | 
			
		||||
 | 
			
		||||
        let mut schedule = Schedule::new(A);
 | 
			
		||||
        schedule.add_systems(|mut flag: ResMut<Flag>| flag.0 = true);
 | 
			
		||||
        world.add_schedule(schedule);
 | 
			
		||||
 | 
			
		||||
        let boxed: Box<dyn ScheduleLabel> = Box::new(A);
 | 
			
		||||
        let interned = A.intern();
 | 
			
		||||
 | 
			
		||||
        world.insert_resource(Flag(false));
 | 
			
		||||
        world.run_schedule(&boxed);
 | 
			
		||||
        world.run_schedule(interned);
 | 
			
		||||
        assert!(world.resource::<Flag>().0);
 | 
			
		||||
 | 
			
		||||
        world.insert_resource(Flag(false));
 | 
			
		||||
        world.run_schedule(boxed);
 | 
			
		||||
        world.run_schedule(interned);
 | 
			
		||||
        assert!(world.resource::<Flag>().0);
 | 
			
		||||
 | 
			
		||||
        assert_ne!(A.intern(), B.intern());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_derive_schedule_label() {
 | 
			
		||||
        use crate::{self as bevy_ecs};
 | 
			
		||||
 | 
			
		||||
        #[derive(ScheduleLabel, Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
 | 
			
		||||
        struct UnitLabel;
 | 
			
		||||
 | 
			
		||||
        #[derive(ScheduleLabel, Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
 | 
			
		||||
        struct TupleLabel(u32, u32);
 | 
			
		||||
 | 
			
		||||
        #[derive(ScheduleLabel, Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
 | 
			
		||||
        struct StructLabel {
 | 
			
		||||
            a: u32,
 | 
			
		||||
            b: u32,
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #[derive(ScheduleLabel, Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
 | 
			
		||||
        struct EmptyTupleLabel();
 | 
			
		||||
 | 
			
		||||
        #[derive(ScheduleLabel, Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
 | 
			
		||||
        struct EmptyStructLabel {}
 | 
			
		||||
 | 
			
		||||
        #[derive(ScheduleLabel, Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
 | 
			
		||||
        enum EnumLabel {
 | 
			
		||||
            #[default]
 | 
			
		||||
            Unit,
 | 
			
		||||
            Tuple(u32, u32),
 | 
			
		||||
            Struct {
 | 
			
		||||
                a: u32,
 | 
			
		||||
                b: u32,
 | 
			
		||||
            },
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #[derive(ScheduleLabel, Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
 | 
			
		||||
        struct GenericLabel<T>(PhantomData<T>);
 | 
			
		||||
 | 
			
		||||
        assert_eq!(UnitLabel.intern(), UnitLabel.intern());
 | 
			
		||||
        assert_eq!(EnumLabel::Unit.intern(), EnumLabel::Unit.intern());
 | 
			
		||||
        assert_ne!(UnitLabel.intern(), EnumLabel::Unit.intern());
 | 
			
		||||
        assert_ne!(UnitLabel.intern(), TupleLabel(0, 0).intern());
 | 
			
		||||
        assert_ne!(EnumLabel::Unit.intern(), EnumLabel::Tuple(0, 0).intern());
 | 
			
		||||
 | 
			
		||||
        assert_eq!(TupleLabel(0, 0).intern(), TupleLabel(0, 0).intern());
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            EnumLabel::Tuple(0, 0).intern(),
 | 
			
		||||
            EnumLabel::Tuple(0, 0).intern()
 | 
			
		||||
        );
 | 
			
		||||
        assert_ne!(TupleLabel(0, 0).intern(), TupleLabel(0, 1).intern());
 | 
			
		||||
        assert_ne!(
 | 
			
		||||
            EnumLabel::Tuple(0, 0).intern(),
 | 
			
		||||
            EnumLabel::Tuple(0, 1).intern()
 | 
			
		||||
        );
 | 
			
		||||
        assert_ne!(TupleLabel(0, 0).intern(), EnumLabel::Tuple(0, 0).intern());
 | 
			
		||||
        assert_ne!(
 | 
			
		||||
            TupleLabel(0, 0).intern(),
 | 
			
		||||
            StructLabel { a: 0, b: 0 }.intern()
 | 
			
		||||
        );
 | 
			
		||||
        assert_ne!(
 | 
			
		||||
            EnumLabel::Tuple(0, 0).intern(),
 | 
			
		||||
            EnumLabel::Struct { a: 0, b: 0 }.intern()
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            StructLabel { a: 0, b: 0 }.intern(),
 | 
			
		||||
            StructLabel { a: 0, b: 0 }.intern()
 | 
			
		||||
        );
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            EnumLabel::Struct { a: 0, b: 0 }.intern(),
 | 
			
		||||
            EnumLabel::Struct { a: 0, b: 0 }.intern()
 | 
			
		||||
        );
 | 
			
		||||
        assert_ne!(
 | 
			
		||||
            StructLabel { a: 0, b: 0 }.intern(),
 | 
			
		||||
            StructLabel { a: 0, b: 1 }.intern()
 | 
			
		||||
        );
 | 
			
		||||
        assert_ne!(
 | 
			
		||||
            EnumLabel::Struct { a: 0, b: 0 }.intern(),
 | 
			
		||||
            EnumLabel::Struct { a: 0, b: 1 }.intern()
 | 
			
		||||
        );
 | 
			
		||||
        assert_ne!(
 | 
			
		||||
            StructLabel { a: 0, b: 0 }.intern(),
 | 
			
		||||
            EnumLabel::Struct { a: 0, b: 0 }.intern()
 | 
			
		||||
        );
 | 
			
		||||
        assert_ne!(
 | 
			
		||||
            StructLabel { a: 0, b: 0 }.intern(),
 | 
			
		||||
            EnumLabel::Struct { a: 0, b: 0 }.intern()
 | 
			
		||||
        );
 | 
			
		||||
        assert_ne!(StructLabel { a: 0, b: 0 }.intern(), UnitLabel.intern(),);
 | 
			
		||||
        assert_ne!(
 | 
			
		||||
            EnumLabel::Struct { a: 0, b: 0 }.intern(),
 | 
			
		||||
            EnumLabel::Unit.intern()
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            GenericLabel::<u32>(PhantomData).intern(),
 | 
			
		||||
            GenericLabel::<u32>(PhantomData).intern()
 | 
			
		||||
        );
 | 
			
		||||
        assert_ne!(
 | 
			
		||||
            GenericLabel::<u32>(PhantomData).intern(),
 | 
			
		||||
            GenericLabel::<u64>(PhantomData).intern()
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_derive_system_set() {
 | 
			
		||||
        use crate::{self as bevy_ecs};
 | 
			
		||||
 | 
			
		||||
        #[derive(SystemSet, Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
 | 
			
		||||
        struct UnitSet;
 | 
			
		||||
 | 
			
		||||
        #[derive(SystemSet, Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
 | 
			
		||||
        struct TupleSet(u32, u32);
 | 
			
		||||
 | 
			
		||||
        #[derive(SystemSet, Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
 | 
			
		||||
        struct StructSet {
 | 
			
		||||
            a: u32,
 | 
			
		||||
            b: u32,
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #[derive(SystemSet, Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
 | 
			
		||||
        struct EmptyTupleSet();
 | 
			
		||||
 | 
			
		||||
        #[derive(SystemSet, Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
 | 
			
		||||
        struct EmptyStructSet {}
 | 
			
		||||
 | 
			
		||||
        #[derive(SystemSet, Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
 | 
			
		||||
        enum EnumSet {
 | 
			
		||||
            #[default]
 | 
			
		||||
            Unit,
 | 
			
		||||
            Tuple(u32, u32),
 | 
			
		||||
            Struct {
 | 
			
		||||
                a: u32,
 | 
			
		||||
                b: u32,
 | 
			
		||||
            },
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #[derive(SystemSet, Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
 | 
			
		||||
        struct GenericSet<T>(PhantomData<T>);
 | 
			
		||||
 | 
			
		||||
        assert_eq!(UnitSet.intern(), UnitSet.intern());
 | 
			
		||||
        assert_eq!(EnumSet::Unit.intern(), EnumSet::Unit.intern());
 | 
			
		||||
        assert_ne!(UnitSet.intern(), EnumSet::Unit.intern());
 | 
			
		||||
        assert_ne!(UnitSet.intern(), TupleSet(0, 0).intern());
 | 
			
		||||
        assert_ne!(EnumSet::Unit.intern(), EnumSet::Tuple(0, 0).intern());
 | 
			
		||||
 | 
			
		||||
        assert_eq!(TupleSet(0, 0).intern(), TupleSet(0, 0).intern());
 | 
			
		||||
        assert_eq!(EnumSet::Tuple(0, 0).intern(), EnumSet::Tuple(0, 0).intern());
 | 
			
		||||
        assert_ne!(TupleSet(0, 0).intern(), TupleSet(0, 1).intern());
 | 
			
		||||
        assert_ne!(EnumSet::Tuple(0, 0).intern(), EnumSet::Tuple(0, 1).intern());
 | 
			
		||||
        assert_ne!(TupleSet(0, 0).intern(), EnumSet::Tuple(0, 0).intern());
 | 
			
		||||
        assert_ne!(TupleSet(0, 0).intern(), StructSet { a: 0, b: 0 }.intern());
 | 
			
		||||
        assert_ne!(
 | 
			
		||||
            EnumSet::Tuple(0, 0).intern(),
 | 
			
		||||
            EnumSet::Struct { a: 0, b: 0 }.intern()
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            StructSet { a: 0, b: 0 }.intern(),
 | 
			
		||||
            StructSet { a: 0, b: 0 }.intern()
 | 
			
		||||
        );
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            EnumSet::Struct { a: 0, b: 0 }.intern(),
 | 
			
		||||
            EnumSet::Struct { a: 0, b: 0 }.intern()
 | 
			
		||||
        );
 | 
			
		||||
        assert_ne!(
 | 
			
		||||
            StructSet { a: 0, b: 0 }.intern(),
 | 
			
		||||
            StructSet { a: 0, b: 1 }.intern()
 | 
			
		||||
        );
 | 
			
		||||
        assert_ne!(
 | 
			
		||||
            EnumSet::Struct { a: 0, b: 0 }.intern(),
 | 
			
		||||
            EnumSet::Struct { a: 0, b: 1 }.intern()
 | 
			
		||||
        );
 | 
			
		||||
        assert_ne!(
 | 
			
		||||
            StructSet { a: 0, b: 0 }.intern(),
 | 
			
		||||
            EnumSet::Struct { a: 0, b: 0 }.intern()
 | 
			
		||||
        );
 | 
			
		||||
        assert_ne!(
 | 
			
		||||
            StructSet { a: 0, b: 0 }.intern(),
 | 
			
		||||
            EnumSet::Struct { a: 0, b: 0 }.intern()
 | 
			
		||||
        );
 | 
			
		||||
        assert_ne!(StructSet { a: 0, b: 0 }.intern(), UnitSet.intern(),);
 | 
			
		||||
        assert_ne!(
 | 
			
		||||
            EnumSet::Struct { a: 0, b: 0 }.intern(),
 | 
			
		||||
            EnumSet::Unit.intern()
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            GenericSet::<u32>(PhantomData).intern(),
 | 
			
		||||
            GenericSet::<u32>(PhantomData).intern()
 | 
			
		||||
        );
 | 
			
		||||
        assert_ne!(
 | 
			
		||||
            GenericSet::<u32>(PhantomData).intern(),
 | 
			
		||||
            GenericSet::<u64>(PhantomData).intern()
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1,7 +1,7 @@
 | 
			
		||||
use std::borrow::Cow;
 | 
			
		||||
 | 
			
		||||
use super::{ReadOnlySystem, System};
 | 
			
		||||
use crate::world::unsafe_world_cell::UnsafeWorldCell;
 | 
			
		||||
use crate::{schedule::InternedSystemSet, world::unsafe_world_cell::UnsafeWorldCell};
 | 
			
		||||
 | 
			
		||||
/// Customizes the behavior of an [`AdapterSystem`]
 | 
			
		||||
///
 | 
			
		||||
@ -143,7 +143,7 @@ where
 | 
			
		||||
        self.system.set_last_run(last_run);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn default_system_sets(&self) -> Vec<Box<dyn crate::schedule::SystemSet>> {
 | 
			
		||||
    fn default_system_sets(&self) -> Vec<InternedSystemSet> {
 | 
			
		||||
        self.system.default_system_sets()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -7,6 +7,7 @@ use crate::{
 | 
			
		||||
    component::{ComponentId, Tick},
 | 
			
		||||
    prelude::World,
 | 
			
		||||
    query::Access,
 | 
			
		||||
    schedule::InternedSystemSet,
 | 
			
		||||
    world::unsafe_world_cell::UnsafeWorldCell,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -226,7 +227,7 @@ where
 | 
			
		||||
        self.b.set_last_run(last_run);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn default_system_sets(&self) -> Vec<Box<dyn crate::schedule::SystemSet>> {
 | 
			
		||||
    fn default_system_sets(&self) -> Vec<InternedSystemSet> {
 | 
			
		||||
        let mut default_sets = self.a.default_system_sets();
 | 
			
		||||
        default_sets.append(&mut self.b.default_system_sets());
 | 
			
		||||
        default_sets
 | 
			
		||||
 | 
			
		||||
@ -2,6 +2,7 @@ use crate::{
 | 
			
		||||
    archetype::ArchetypeComponentId,
 | 
			
		||||
    component::{ComponentId, Tick},
 | 
			
		||||
    query::Access,
 | 
			
		||||
    schedule::{InternedSystemSet, SystemSet},
 | 
			
		||||
    system::{
 | 
			
		||||
        check_system_change_tick, ExclusiveSystemParam, ExclusiveSystemParamItem, In, IntoSystem,
 | 
			
		||||
        System, SystemMeta,
 | 
			
		||||
@ -150,9 +151,9 @@ where
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn default_system_sets(&self) -> Vec<Box<dyn crate::schedule::SystemSet>> {
 | 
			
		||||
    fn default_system_sets(&self) -> Vec<InternedSystemSet> {
 | 
			
		||||
        let set = crate::schedule::SystemTypeSet::<F>::new();
 | 
			
		||||
        vec![Box::new(set)]
 | 
			
		||||
        vec![set.intern()]
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -3,6 +3,7 @@ use crate::{
 | 
			
		||||
    component::{ComponentId, Tick},
 | 
			
		||||
    prelude::FromWorld,
 | 
			
		||||
    query::{Access, FilteredAccessSet},
 | 
			
		||||
    schedule::{InternedSystemSet, SystemSet},
 | 
			
		||||
    system::{check_system_change_tick, ReadOnlySystemParam, System, SystemParam, SystemParamItem},
 | 
			
		||||
    world::{unsafe_world_cell::UnsafeWorldCell, World, WorldId},
 | 
			
		||||
};
 | 
			
		||||
@ -528,9 +529,9 @@ where
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn default_system_sets(&self) -> Vec<Box<dyn crate::schedule::SystemSet>> {
 | 
			
		||||
    fn default_system_sets(&self) -> Vec<InternedSystemSet> {
 | 
			
		||||
        let set = crate::schedule::SystemTypeSet::<F>::new();
 | 
			
		||||
        vec![Box::new(set)]
 | 
			
		||||
        vec![set.intern()]
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -2,6 +2,7 @@ use bevy_utils::tracing::warn;
 | 
			
		||||
use core::fmt::Debug;
 | 
			
		||||
 | 
			
		||||
use crate::component::Tick;
 | 
			
		||||
use crate::schedule::InternedSystemSet;
 | 
			
		||||
use crate::world::unsafe_world_cell::UnsafeWorldCell;
 | 
			
		||||
use crate::{archetype::ArchetypeComponentId, component::ComponentId, query::Access, world::World};
 | 
			
		||||
 | 
			
		||||
@ -91,7 +92,7 @@ pub trait System: Send + Sync + 'static {
 | 
			
		||||
    fn check_change_tick(&mut self, change_tick: Tick);
 | 
			
		||||
 | 
			
		||||
    /// Returns the system's default [system sets](crate::schedule::SystemSet).
 | 
			
		||||
    fn default_system_sets(&self) -> Vec<Box<dyn crate::schedule::SystemSet>> {
 | 
			
		||||
    fn default_system_sets(&self) -> Vec<InternedSystemSet> {
 | 
			
		||||
        Vec::new()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -2,11 +2,11 @@
 | 
			
		||||
 | 
			
		||||
use thiserror::Error;
 | 
			
		||||
 | 
			
		||||
use crate::schedule::BoxedScheduleLabel;
 | 
			
		||||
use crate::schedule::InternedScheduleLabel;
 | 
			
		||||
 | 
			
		||||
/// The error type returned by [`World::try_run_schedule`] if the provided schedule does not exist.
 | 
			
		||||
///
 | 
			
		||||
/// [`World::try_run_schedule`]: crate::world::World::try_run_schedule
 | 
			
		||||
#[derive(Error, Debug)]
 | 
			
		||||
#[error("The schedule with the label {0:?} was not found.")]
 | 
			
		||||
pub struct TryRunScheduleError(pub BoxedScheduleLabel);
 | 
			
		||||
pub struct TryRunScheduleError(pub InternedScheduleLabel);
 | 
			
		||||
 | 
			
		||||
@ -1993,15 +1993,15 @@ impl World {
 | 
			
		||||
    /// For other use cases, see the example on [`World::schedule_scope`].
 | 
			
		||||
    pub fn try_schedule_scope<R>(
 | 
			
		||||
        &mut self,
 | 
			
		||||
        label: impl AsRef<dyn ScheduleLabel>,
 | 
			
		||||
        label: impl ScheduleLabel,
 | 
			
		||||
        f: impl FnOnce(&mut World, &mut Schedule) -> R,
 | 
			
		||||
    ) -> Result<R, TryRunScheduleError> {
 | 
			
		||||
        let label = label.as_ref();
 | 
			
		||||
        let label = label.intern();
 | 
			
		||||
        let Some(mut schedule) = self
 | 
			
		||||
            .get_resource_mut::<Schedules>()
 | 
			
		||||
            .and_then(|mut s| s.remove(label))
 | 
			
		||||
        else {
 | 
			
		||||
            return Err(TryRunScheduleError(label.dyn_clone()));
 | 
			
		||||
            return Err(TryRunScheduleError(label));
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        let value = f(self, &mut schedule);
 | 
			
		||||
@ -2053,7 +2053,7 @@ impl World {
 | 
			
		||||
    /// If the requested schedule does not exist.
 | 
			
		||||
    pub fn schedule_scope<R>(
 | 
			
		||||
        &mut self,
 | 
			
		||||
        label: impl AsRef<dyn ScheduleLabel>,
 | 
			
		||||
        label: impl ScheduleLabel,
 | 
			
		||||
        f: impl FnOnce(&mut World, &mut Schedule) -> R,
 | 
			
		||||
    ) -> R {
 | 
			
		||||
        self.try_schedule_scope(label, f)
 | 
			
		||||
@ -2069,7 +2069,7 @@ impl World {
 | 
			
		||||
    /// For simple testing use cases, call [`Schedule::run(&mut world)`](Schedule::run) instead.
 | 
			
		||||
    pub fn try_run_schedule(
 | 
			
		||||
        &mut self,
 | 
			
		||||
        label: impl AsRef<dyn ScheduleLabel>,
 | 
			
		||||
        label: impl ScheduleLabel,
 | 
			
		||||
    ) -> Result<(), TryRunScheduleError> {
 | 
			
		||||
        self.try_schedule_scope(label, |world, sched| sched.run(world))
 | 
			
		||||
    }
 | 
			
		||||
@ -2084,7 +2084,7 @@ impl World {
 | 
			
		||||
    /// # Panics
 | 
			
		||||
    ///
 | 
			
		||||
    /// If the requested schedule does not exist.
 | 
			
		||||
    pub fn run_schedule(&mut self, label: impl AsRef<dyn ScheduleLabel>) {
 | 
			
		||||
    pub fn run_schedule(&mut self, label: impl ScheduleLabel) {
 | 
			
		||||
        self.schedule_scope(label, |world, sched| sched.run(world));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -3,8 +3,6 @@ use quote::{quote, quote_spanned};
 | 
			
		||||
use rustc_hash::FxHashSet;
 | 
			
		||||
use syn::{spanned::Spanned, Ident};
 | 
			
		||||
 | 
			
		||||
use crate::BevyManifest;
 | 
			
		||||
 | 
			
		||||
/// Finds an identifier that will not conflict with the specified set of tokens.
 | 
			
		||||
/// If the identifier is present in `haystack`, extra characters will be added
 | 
			
		||||
/// to it until it no longer conflicts with anything.
 | 
			
		||||
@ -52,11 +50,24 @@ pub fn ensure_no_collision(value: Ident, haystack: TokenStream) -> Ident {
 | 
			
		||||
/// # Args
 | 
			
		||||
///
 | 
			
		||||
/// - `input`: The [`syn::DeriveInput`] for struct that is deriving the label trait
 | 
			
		||||
/// - `trait_path`: The path [`syn::Path`] to the label trait
 | 
			
		||||
pub fn derive_boxed_label(input: syn::DeriveInput, trait_path: &syn::Path) -> TokenStream {
 | 
			
		||||
    let bevy_utils_path = BevyManifest::default().get_path("bevy_utils");
 | 
			
		||||
/// - `trait_name`: Name of the label trait
 | 
			
		||||
/// - `trait_path`: The [path](`syn::Path`) to the label trait
 | 
			
		||||
/// - `dyn_eq_path`: The [path](`syn::Path`) to the `DynEq` trait
 | 
			
		||||
pub fn derive_label(
 | 
			
		||||
    input: syn::DeriveInput,
 | 
			
		||||
    trait_name: &str,
 | 
			
		||||
    trait_path: &syn::Path,
 | 
			
		||||
    dyn_eq_path: &syn::Path,
 | 
			
		||||
) -> TokenStream {
 | 
			
		||||
    if let syn::Data::Union(_) = &input.data {
 | 
			
		||||
        let message = format!("Cannot derive {trait_name} for unions.");
 | 
			
		||||
        return quote_spanned! {
 | 
			
		||||
            input.span() => compile_error!(#message);
 | 
			
		||||
        }
 | 
			
		||||
        .into();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let ident = input.ident;
 | 
			
		||||
    let ident = input.ident.clone();
 | 
			
		||||
    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
 | 
			
		||||
    let mut where_clause = where_clause.cloned().unwrap_or_else(|| syn::WhereClause {
 | 
			
		||||
        where_token: Default::default(),
 | 
			
		||||
@ -68,141 +79,22 @@ pub fn derive_boxed_label(input: syn::DeriveInput, trait_path: &syn::Path) -> To
 | 
			
		||||
        })
 | 
			
		||||
        .unwrap(),
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    (quote! {
 | 
			
		||||
        impl #impl_generics #trait_path for #ident #ty_generics #where_clause {
 | 
			
		||||
            fn dyn_clone(&self) -> std::boxed::Box<dyn #trait_path> {
 | 
			
		||||
                std::boxed::Box::new(std::clone::Clone::clone(self))
 | 
			
		||||
            fn dyn_clone(&self) -> ::std::boxed::Box<dyn #trait_path> {
 | 
			
		||||
                ::std::boxed::Box::new(::std::clone::Clone::clone(self))
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            fn as_dyn_eq(&self) -> &dyn #bevy_utils_path::label::DynEq {
 | 
			
		||||
            fn as_dyn_eq(&self) -> &dyn #dyn_eq_path {
 | 
			
		||||
                self
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            fn dyn_hash(&self, mut state: &mut dyn ::std::hash::Hasher) {
 | 
			
		||||
                let ty_id = #trait_path::inner_type_id(self);
 | 
			
		||||
                let ty_id = ::std::any::TypeId::of::<Self>();
 | 
			
		||||
                ::std::hash::Hash::hash(&ty_id, &mut state);
 | 
			
		||||
                ::std::hash::Hash::hash(self, &mut state);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        impl #impl_generics ::std::convert::AsRef<dyn #trait_path> for #ident #ty_generics #where_clause {
 | 
			
		||||
            fn as_ref(&self) -> &dyn #trait_path {
 | 
			
		||||
                self
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    })
 | 
			
		||||
    .into()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Derive a label trait
 | 
			
		||||
///
 | 
			
		||||
/// # Args
 | 
			
		||||
///
 | 
			
		||||
/// - `input`: The [`syn::DeriveInput`] for struct that is deriving the label trait
 | 
			
		||||
/// - `trait_path`: The path [`syn::Path`] to the label trait
 | 
			
		||||
pub fn derive_label(
 | 
			
		||||
    input: syn::DeriveInput,
 | 
			
		||||
    trait_path: &syn::Path,
 | 
			
		||||
    attr_name: &str,
 | 
			
		||||
) -> TokenStream {
 | 
			
		||||
    // return true if the variant specified is an `ignore_fields` attribute
 | 
			
		||||
    fn is_ignore(attr: &syn::Attribute, attr_name: &str) -> bool {
 | 
			
		||||
        if attr.path().get_ident().as_ref().unwrap() != &attr_name {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        syn::custom_keyword!(ignore_fields);
 | 
			
		||||
        attr.parse_args_with(|input: syn::parse::ParseStream| {
 | 
			
		||||
            let ignore = input.parse::<Option<ignore_fields>>()?.is_some();
 | 
			
		||||
            Ok(ignore)
 | 
			
		||||
        })
 | 
			
		||||
        .unwrap()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let ident = input.ident.clone();
 | 
			
		||||
 | 
			
		||||
    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
 | 
			
		||||
    let mut where_clause = where_clause.cloned().unwrap_or_else(|| syn::WhereClause {
 | 
			
		||||
        where_token: Default::default(),
 | 
			
		||||
        predicates: Default::default(),
 | 
			
		||||
    });
 | 
			
		||||
    where_clause
 | 
			
		||||
        .predicates
 | 
			
		||||
        .push(syn::parse2(quote! { Self: 'static }).unwrap());
 | 
			
		||||
 | 
			
		||||
    let as_str = match input.data {
 | 
			
		||||
        syn::Data::Struct(d) => {
 | 
			
		||||
            // see if the user tried to ignore fields incorrectly
 | 
			
		||||
            if let Some(attr) = d
 | 
			
		||||
                .fields
 | 
			
		||||
                .iter()
 | 
			
		||||
                .flat_map(|f| &f.attrs)
 | 
			
		||||
                .find(|a| is_ignore(a, attr_name))
 | 
			
		||||
            {
 | 
			
		||||
                let err_msg = format!("`#[{attr_name}(ignore_fields)]` cannot be applied to fields individually: add it to the struct declaration");
 | 
			
		||||
                return quote_spanned! {
 | 
			
		||||
                    attr.span() => compile_error!(#err_msg);
 | 
			
		||||
                }
 | 
			
		||||
                .into();
 | 
			
		||||
            }
 | 
			
		||||
            // Structs must either be fieldless, or explicitly ignore the fields.
 | 
			
		||||
            let ignore_fields = input.attrs.iter().any(|a| is_ignore(a, attr_name));
 | 
			
		||||
            if matches!(d.fields, syn::Fields::Unit) || ignore_fields {
 | 
			
		||||
                let lit = ident.to_string();
 | 
			
		||||
                quote! { #lit }
 | 
			
		||||
            } else {
 | 
			
		||||
                let err_msg = format!("Labels cannot contain data, unless explicitly ignored with `#[{attr_name}(ignore_fields)]`");
 | 
			
		||||
                return quote_spanned! {
 | 
			
		||||
                    d.fields.span() => compile_error!(#err_msg);
 | 
			
		||||
                }
 | 
			
		||||
                .into();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        syn::Data::Enum(d) => {
 | 
			
		||||
            // check if the user put #[label(ignore_fields)] in the wrong place
 | 
			
		||||
            if let Some(attr) = input.attrs.iter().find(|a| is_ignore(a, attr_name)) {
 | 
			
		||||
                let err_msg = format!("`#[{attr_name}(ignore_fields)]` can only be applied to enum variants or struct declarations");
 | 
			
		||||
                return quote_spanned! {
 | 
			
		||||
                    attr.span() => compile_error!(#err_msg);
 | 
			
		||||
                }
 | 
			
		||||
                .into();
 | 
			
		||||
            }
 | 
			
		||||
            let arms = d.variants.iter().map(|v| {
 | 
			
		||||
                // Variants must either be fieldless, or explicitly ignore the fields.
 | 
			
		||||
                let ignore_fields = v.attrs.iter().any(|a| is_ignore(a, attr_name));
 | 
			
		||||
                if matches!(v.fields, syn::Fields::Unit) | ignore_fields {
 | 
			
		||||
                    let mut path = syn::Path::from(ident.clone());
 | 
			
		||||
                    path.segments.push(v.ident.clone().into());
 | 
			
		||||
                    let lit = format!("{ident}::{}", v.ident.clone());
 | 
			
		||||
                    quote! { #path { .. } => #lit }
 | 
			
		||||
                } else {
 | 
			
		||||
                    let err_msg = format!("Label variants cannot contain data, unless explicitly ignored with `#[{attr_name}(ignore_fields)]`");
 | 
			
		||||
                    quote_spanned! {
 | 
			
		||||
                        v.fields.span() => _ => { compile_error!(#err_msg); }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
            quote! {
 | 
			
		||||
                match self {
 | 
			
		||||
                    #(#arms),*
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        syn::Data::Union(_) => {
 | 
			
		||||
            return quote_spanned! {
 | 
			
		||||
                input.span() => compile_error!("Unions cannot be used as labels.");
 | 
			
		||||
            }
 | 
			
		||||
            .into();
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    (quote! {
 | 
			
		||||
        impl #impl_generics #trait_path for #ident #ty_generics #where_clause {
 | 
			
		||||
            fn as_str(&self) -> &'static str {
 | 
			
		||||
                #as_str
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    })
 | 
			
		||||
    .into()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -406,7 +406,7 @@ unsafe fn initialize_render_app(app: &mut App) {
 | 
			
		||||
    app.init_resource::<ScratchMainWorld>();
 | 
			
		||||
 | 
			
		||||
    let mut render_app = App::empty();
 | 
			
		||||
    render_app.main_schedule_label = Box::new(Render);
 | 
			
		||||
    render_app.main_schedule_label = Render.intern();
 | 
			
		||||
 | 
			
		||||
    let mut extract_schedule = Schedule::new(ExtractSchedule);
 | 
			
		||||
    extract_schedule.set_apply_final_deferred(false);
 | 
			
		||||
@ -475,7 +475,7 @@ unsafe fn initialize_render_app(app: &mut App) {
 | 
			
		||||
fn apply_extract_commands(render_world: &mut World) {
 | 
			
		||||
    render_world.resource_scope(|render_world, mut schedules: Mut<Schedules>| {
 | 
			
		||||
        schedules
 | 
			
		||||
            .get_mut(&ExtractSchedule)
 | 
			
		||||
            .get_mut(ExtractSchedule)
 | 
			
		||||
            .unwrap()
 | 
			
		||||
            .apply_deferred(render_world);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										286
									
								
								crates/bevy_utils/src/intern.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										286
									
								
								crates/bevy_utils/src/intern.rs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,286 @@
 | 
			
		||||
//! Provides types used to statically intern immutable values.
 | 
			
		||||
//!
 | 
			
		||||
//! Interning is a pattern used to save memory by deduplicating identical values,
 | 
			
		||||
//! speed up code by shrinking the stack size of large types,
 | 
			
		||||
//! and make comparisons for any type as fast as integers.
 | 
			
		||||
 | 
			
		||||
use std::{
 | 
			
		||||
    fmt::Debug,
 | 
			
		||||
    hash::Hash,
 | 
			
		||||
    ops::Deref,
 | 
			
		||||
    sync::{OnceLock, PoisonError, RwLock},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
use crate::HashSet;
 | 
			
		||||
 | 
			
		||||
/// An interned value. Will stay valid until the end of the program and will not drop.
 | 
			
		||||
///
 | 
			
		||||
/// For details on interning, see [the module level docs](self).
 | 
			
		||||
///
 | 
			
		||||
/// # Comparisons
 | 
			
		||||
///
 | 
			
		||||
/// Interned values use reference equality, meaning they implement [`Eq`]
 | 
			
		||||
/// and [`Hash`] regardless of whether `T` implements these traits.
 | 
			
		||||
/// Two interned values are only guaranteed to compare equal if they were interned using
 | 
			
		||||
/// the same [`Interner`] instance.
 | 
			
		||||
// NOTE: This type must NEVER implement Borrow since it does not obey that trait's invariants.
 | 
			
		||||
///
 | 
			
		||||
/// ```
 | 
			
		||||
/// # use bevy_utils::intern::*;
 | 
			
		||||
/// #[derive(PartialEq, Eq, Hash, Debug)]
 | 
			
		||||
/// struct Value(i32);
 | 
			
		||||
/// impl Internable for Value {
 | 
			
		||||
///     // ...
 | 
			
		||||
/// # fn leak(&self) -> &'static Self { Box::leak(Box::new(Value(self.0))) }
 | 
			
		||||
/// # fn ref_eq(&self, other: &Self) -> bool { std::ptr::eq(self, other ) }
 | 
			
		||||
/// # fn ref_hash<H: std::hash::Hasher>(&self, state: &mut H) { std::ptr::hash(self, state); }
 | 
			
		||||
/// }
 | 
			
		||||
/// let interner_1 = Interner::new();
 | 
			
		||||
/// let interner_2 = Interner::new();
 | 
			
		||||
/// // Even though both values are identical, their interned forms do not
 | 
			
		||||
/// // compare equal as they use different interner instances.
 | 
			
		||||
/// assert_ne!(interner_1.intern(&Value(42)), interner_2.intern(&Value(42)));
 | 
			
		||||
/// ```
 | 
			
		||||
pub struct Interned<T: ?Sized + 'static>(pub &'static T);
 | 
			
		||||
 | 
			
		||||
impl<T: ?Sized> Deref for Interned<T> {
 | 
			
		||||
    type Target = T;
 | 
			
		||||
 | 
			
		||||
    fn deref(&self) -> &Self::Target {
 | 
			
		||||
        self.0
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<T: ?Sized> Clone for Interned<T> {
 | 
			
		||||
    fn clone(&self) -> Self {
 | 
			
		||||
        *self
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<T: ?Sized> Copy for Interned<T> {}
 | 
			
		||||
 | 
			
		||||
// Two Interned<T> should only be equal if they are clones from the same instance.
 | 
			
		||||
// Therefore, we only use the pointer to determine equality.
 | 
			
		||||
impl<T: ?Sized + Internable> PartialEq for Interned<T> {
 | 
			
		||||
    fn eq(&self, other: &Self) -> bool {
 | 
			
		||||
        self.0.ref_eq(other.0)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<T: ?Sized + Internable> Eq for Interned<T> {}
 | 
			
		||||
 | 
			
		||||
// Important: This must be kept in sync with the PartialEq/Eq implementation
 | 
			
		||||
impl<T: ?Sized + Internable> Hash for Interned<T> {
 | 
			
		||||
    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
 | 
			
		||||
        self.0.ref_hash(state);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<T: ?Sized + Debug> Debug for Interned<T> {
 | 
			
		||||
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 | 
			
		||||
        self.0.fmt(f)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<T> From<&Interned<T>> for Interned<T> {
 | 
			
		||||
    fn from(value: &Interned<T>) -> Self {
 | 
			
		||||
        *value
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// A trait for internable values.
 | 
			
		||||
///
 | 
			
		||||
/// This is used by [`Interner<T>`] to create static references for values that are interned.
 | 
			
		||||
pub trait Internable: Hash + Eq {
 | 
			
		||||
    /// Creates a static reference to `self`, possibly leaking memory.
 | 
			
		||||
    fn leak(&self) -> &'static Self;
 | 
			
		||||
 | 
			
		||||
    /// Returns `true` if the two references point to the same value.
 | 
			
		||||
    fn ref_eq(&self, other: &Self) -> bool;
 | 
			
		||||
 | 
			
		||||
    /// Feeds the reference to the hasher.
 | 
			
		||||
    fn ref_hash<H: std::hash::Hasher>(&self, state: &mut H);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Internable for str {
 | 
			
		||||
    fn leak(&self) -> &'static Self {
 | 
			
		||||
        let str = self.to_owned().into_boxed_str();
 | 
			
		||||
        Box::leak(str)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn ref_eq(&self, other: &Self) -> bool {
 | 
			
		||||
        self.as_ptr() == other.as_ptr() && self.len() == other.len()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn ref_hash<H: std::hash::Hasher>(&self, state: &mut H) {
 | 
			
		||||
        self.len().hash(state);
 | 
			
		||||
        self.as_ptr().hash(state);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// A thread-safe interner which can be used to create [`Interned<T>`] from `&T`
 | 
			
		||||
///
 | 
			
		||||
/// For details on interning, see [the module level docs](self).
 | 
			
		||||
///
 | 
			
		||||
/// The implementation ensures that two equal values return two equal [`Interned<T>`] values.
 | 
			
		||||
///
 | 
			
		||||
/// To use an [`Interner<T>`], `T` must implement [`Internable`].
 | 
			
		||||
pub struct Interner<T: ?Sized + 'static>(OnceLock<RwLock<HashSet<&'static T>>>);
 | 
			
		||||
 | 
			
		||||
impl<T: ?Sized> Interner<T> {
 | 
			
		||||
    /// Creates a new empty interner
 | 
			
		||||
    pub const fn new() -> Self {
 | 
			
		||||
        Self(OnceLock::new())
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<T: Internable + ?Sized> Interner<T> {
 | 
			
		||||
    /// Return the [`Interned<T>`] corresponding to `value`.
 | 
			
		||||
    ///
 | 
			
		||||
    /// If it is called the first time for `value`, it will possibly leak the value and return an
 | 
			
		||||
    /// [`Interned<T>`] using the obtained static reference. Subsequent calls for the same `value`
 | 
			
		||||
    /// will return [`Interned<T>`] using the same static reference.
 | 
			
		||||
    pub fn intern(&self, value: &T) -> Interned<T> {
 | 
			
		||||
        let lock = self.0.get_or_init(Default::default);
 | 
			
		||||
        {
 | 
			
		||||
            let set = lock.read().unwrap_or_else(PoisonError::into_inner);
 | 
			
		||||
            if let Some(value) = set.get(value) {
 | 
			
		||||
                return Interned(*value);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        {
 | 
			
		||||
            let mut set = lock.write().unwrap_or_else(PoisonError::into_inner);
 | 
			
		||||
            if let Some(value) = set.get(value) {
 | 
			
		||||
                Interned(*value)
 | 
			
		||||
            } else {
 | 
			
		||||
                let leaked = value.leak();
 | 
			
		||||
                set.insert(leaked);
 | 
			
		||||
                Interned(leaked)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<T: ?Sized> Default for Interner<T> {
 | 
			
		||||
    fn default() -> Self {
 | 
			
		||||
        Self::new()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod tests {
 | 
			
		||||
    use std::{
 | 
			
		||||
        collections::hash_map::DefaultHasher,
 | 
			
		||||
        hash::{Hash, Hasher},
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    use crate::intern::{Internable, Interned, Interner};
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn zero_sized_type() {
 | 
			
		||||
        #[derive(PartialEq, Eq, Hash, Debug)]
 | 
			
		||||
        pub struct A;
 | 
			
		||||
 | 
			
		||||
        impl Internable for A {
 | 
			
		||||
            fn leak(&self) -> &'static Self {
 | 
			
		||||
                &A
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            fn ref_eq(&self, other: &Self) -> bool {
 | 
			
		||||
                std::ptr::eq(self, other)
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            fn ref_hash<H: std::hash::Hasher>(&self, state: &mut H) {
 | 
			
		||||
                std::ptr::hash(self, state);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let interner = Interner::default();
 | 
			
		||||
        let x = interner.intern(&A);
 | 
			
		||||
        let y = interner.intern(&A);
 | 
			
		||||
        assert_eq!(x, y);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn fieldless_enum() {
 | 
			
		||||
        #[derive(PartialEq, Eq, Hash, Debug, Clone)]
 | 
			
		||||
        pub enum A {
 | 
			
		||||
            X,
 | 
			
		||||
            Y,
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        impl Internable for A {
 | 
			
		||||
            fn leak(&self) -> &'static Self {
 | 
			
		||||
                match self {
 | 
			
		||||
                    A::X => &A::X,
 | 
			
		||||
                    A::Y => &A::Y,
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            fn ref_eq(&self, other: &Self) -> bool {
 | 
			
		||||
                std::ptr::eq(self, other)
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            fn ref_hash<H: std::hash::Hasher>(&self, state: &mut H) {
 | 
			
		||||
                std::ptr::hash(self, state);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let interner = Interner::default();
 | 
			
		||||
        let x = interner.intern(&A::X);
 | 
			
		||||
        let y = interner.intern(&A::Y);
 | 
			
		||||
        assert_ne!(x, y);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn static_sub_strings() {
 | 
			
		||||
        let str = "ABC ABC";
 | 
			
		||||
        let a = &str[0..3];
 | 
			
		||||
        let b = &str[4..7];
 | 
			
		||||
        // Same contents
 | 
			
		||||
        assert_eq!(a, b);
 | 
			
		||||
        let x = Interned(a);
 | 
			
		||||
        let y = Interned(b);
 | 
			
		||||
        // Different pointers
 | 
			
		||||
        assert_ne!(x, y);
 | 
			
		||||
        let interner = Interner::default();
 | 
			
		||||
        let x = interner.intern(a);
 | 
			
		||||
        let y = interner.intern(b);
 | 
			
		||||
        // Same pointers returned by interner
 | 
			
		||||
        assert_eq!(x, y);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn same_interned_instance() {
 | 
			
		||||
        let a = Interned("A");
 | 
			
		||||
        let b = a;
 | 
			
		||||
 | 
			
		||||
        assert_eq!(a, b);
 | 
			
		||||
 | 
			
		||||
        let mut hasher = DefaultHasher::default();
 | 
			
		||||
        a.hash(&mut hasher);
 | 
			
		||||
        let hash_a = hasher.finish();
 | 
			
		||||
 | 
			
		||||
        let mut hasher = DefaultHasher::default();
 | 
			
		||||
        b.hash(&mut hasher);
 | 
			
		||||
        let hash_b = hasher.finish();
 | 
			
		||||
 | 
			
		||||
        assert_eq!(hash_a, hash_b);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn same_interned_content() {
 | 
			
		||||
        let a = Interned::<str>(Box::leak(Box::new("A".to_string())));
 | 
			
		||||
        let b = Interned::<str>(Box::leak(Box::new("A".to_string())));
 | 
			
		||||
 | 
			
		||||
        assert_ne!(a, b);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn different_interned_content() {
 | 
			
		||||
        let a = Interned::<str>("A");
 | 
			
		||||
        let b = Interned::<str>("B");
 | 
			
		||||
 | 
			
		||||
        assert_ne!(a, b);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -65,36 +65,97 @@ where
 | 
			
		||||
/// # Example
 | 
			
		||||
///
 | 
			
		||||
/// ```
 | 
			
		||||
/// # use bevy_utils::define_boxed_label;
 | 
			
		||||
/// define_boxed_label!(MyNewLabelTrait);
 | 
			
		||||
/// # use bevy_utils::define_label;
 | 
			
		||||
/// define_label!(
 | 
			
		||||
///     /// Documentation of label trait
 | 
			
		||||
///     MyNewLabelTrait,
 | 
			
		||||
///     MY_NEW_LABEL_TRAIT_INTERNER
 | 
			
		||||
/// );
 | 
			
		||||
///
 | 
			
		||||
/// define_label!(
 | 
			
		||||
///     /// Documentation of another label trait
 | 
			
		||||
///     MyNewExtendedLabelTrait,
 | 
			
		||||
///     MY_NEW_EXTENDED_LABEL_TRAIT_INTERNER,
 | 
			
		||||
///     extra_methods: {
 | 
			
		||||
///         // Extra methods for the trait can be defined here
 | 
			
		||||
///         fn additional_method(&self) -> i32;
 | 
			
		||||
///     },
 | 
			
		||||
///     extra_methods_impl: {
 | 
			
		||||
///         // Implementation of the extra methods for Interned<dyn MyNewExtendedLabelTrait>
 | 
			
		||||
///         fn additional_method(&self) -> i32 {
 | 
			
		||||
///             0
 | 
			
		||||
///         }
 | 
			
		||||
///     }
 | 
			
		||||
/// );
 | 
			
		||||
/// ```
 | 
			
		||||
#[macro_export]
 | 
			
		||||
macro_rules! define_boxed_label {
 | 
			
		||||
    ($label_trait_name:ident) => {
 | 
			
		||||
        /// A strongly-typed label.
 | 
			
		||||
macro_rules! define_label {
 | 
			
		||||
    (
 | 
			
		||||
        $(#[$label_attr:meta])*
 | 
			
		||||
        $label_trait_name:ident,
 | 
			
		||||
        $interner_name:ident
 | 
			
		||||
    ) => {
 | 
			
		||||
        $crate::define_label!(
 | 
			
		||||
            $(#[$label_attr])*
 | 
			
		||||
            $label_trait_name,
 | 
			
		||||
            $interner_name,
 | 
			
		||||
            extra_methods: {},
 | 
			
		||||
            extra_methods_impl: {}
 | 
			
		||||
        );
 | 
			
		||||
    };
 | 
			
		||||
    (
 | 
			
		||||
        $(#[$label_attr:meta])*
 | 
			
		||||
        $label_trait_name:ident,
 | 
			
		||||
        $interner_name:ident,
 | 
			
		||||
        extra_methods: { $($trait_extra_methods:tt)* },
 | 
			
		||||
        extra_methods_impl: { $($interned_extra_methods_impl:tt)* }
 | 
			
		||||
    ) => {
 | 
			
		||||
 | 
			
		||||
        $(#[$label_attr])*
 | 
			
		||||
        pub trait $label_trait_name: 'static + Send + Sync + ::std::fmt::Debug {
 | 
			
		||||
            /// Return's the [`TypeId`] of this label, or the ID of the
 | 
			
		||||
            /// wrapped label type for `Box<dyn
 | 
			
		||||
            #[doc = stringify!($label_trait_name)]
 | 
			
		||||
            /// >`
 | 
			
		||||
            ///
 | 
			
		||||
            /// [TypeId]: std::any::TypeId
 | 
			
		||||
            fn inner_type_id(&self) -> ::std::any::TypeId {
 | 
			
		||||
                std::any::TypeId::of::<Self>()
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            $($trait_extra_methods)*
 | 
			
		||||
 | 
			
		||||
            /// Clones this `
 | 
			
		||||
            #[doc = stringify!($label_trait_name)]
 | 
			
		||||
            /// `
 | 
			
		||||
            fn dyn_clone(&self) -> Box<dyn $label_trait_name>;
 | 
			
		||||
            ///`.
 | 
			
		||||
            fn dyn_clone(&self) -> ::std::boxed::Box<dyn $label_trait_name>;
 | 
			
		||||
 | 
			
		||||
            /// Casts this value to a form where it can be compared with other type-erased values.
 | 
			
		||||
            fn as_dyn_eq(&self) -> &dyn ::bevy_utils::label::DynEq;
 | 
			
		||||
            fn as_dyn_eq(&self) -> &dyn $crate::label::DynEq;
 | 
			
		||||
 | 
			
		||||
            /// Feeds this value into the given [`Hasher`].
 | 
			
		||||
            ///
 | 
			
		||||
            /// [`Hasher`]: std::hash::Hasher
 | 
			
		||||
            fn dyn_hash(&self, state: &mut dyn ::std::hash::Hasher);
 | 
			
		||||
 | 
			
		||||
            /// Returns an [`Interned`](bevy_utils::intern::Interned) value corresponding to `self`.
 | 
			
		||||
            fn intern(&self) -> $crate::intern::Interned<dyn $label_trait_name>
 | 
			
		||||
            where Self: Sized {
 | 
			
		||||
                $interner_name.intern(self)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        impl $label_trait_name for $crate::intern::Interned<dyn $label_trait_name> {
 | 
			
		||||
 | 
			
		||||
            $($interned_extra_methods_impl)*
 | 
			
		||||
 | 
			
		||||
            fn dyn_clone(&self) -> ::std::boxed::Box<dyn $label_trait_name> {
 | 
			
		||||
                (**self).dyn_clone()
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            /// Casts this value to a form where it can be compared with other type-erased values.
 | 
			
		||||
            fn as_dyn_eq(&self) -> &dyn $crate::label::DynEq {
 | 
			
		||||
                (**self).as_dyn_eq()
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            fn dyn_hash(&self, state: &mut dyn ::std::hash::Hasher) {
 | 
			
		||||
                (**self).dyn_hash(state);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            fn intern(&self) -> Self {
 | 
			
		||||
                *self
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        impl PartialEq for dyn $label_trait_name {
 | 
			
		||||
@ -111,108 +172,27 @@ macro_rules! define_boxed_label {
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        impl ::std::convert::AsRef<dyn $label_trait_name> for dyn $label_trait_name {
 | 
			
		||||
            #[inline]
 | 
			
		||||
            fn as_ref(&self) -> &Self {
 | 
			
		||||
                self
 | 
			
		||||
        impl $crate::intern::Internable for dyn $label_trait_name {
 | 
			
		||||
            fn leak(&self) -> &'static Self {
 | 
			
		||||
                Box::leak(self.dyn_clone())
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            fn ref_eq(&self, other: &Self) -> bool {
 | 
			
		||||
                if self.as_dyn_eq().type_id() == other.as_dyn_eq().type_id() {
 | 
			
		||||
                    (self as *const Self as *const ()) == (other as *const Self as *const ())
 | 
			
		||||
                } else {
 | 
			
		||||
                    false
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        impl Clone for Box<dyn $label_trait_name> {
 | 
			
		||||
            fn clone(&self) -> Self {
 | 
			
		||||
                self.dyn_clone()
 | 
			
		||||
            fn ref_hash<H: ::std::hash::Hasher>(&self, state: &mut H) {
 | 
			
		||||
                use ::std::hash::Hash;
 | 
			
		||||
                self.as_dyn_eq().type_id().hash(state);
 | 
			
		||||
                (self as *const Self as *const ()).hash(state);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        impl $label_trait_name for Box<dyn $label_trait_name> {
 | 
			
		||||
            fn inner_type_id(&self) -> ::std::any::TypeId {
 | 
			
		||||
                (**self).inner_type_id()
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            fn dyn_clone(&self) -> Box<dyn $label_trait_name> {
 | 
			
		||||
                // Be explicit that we want to use the inner value
 | 
			
		||||
                // to avoid infinite recursion.
 | 
			
		||||
                (**self).dyn_clone()
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            fn as_dyn_eq(&self) -> &dyn ::bevy_utils::label::DynEq {
 | 
			
		||||
                (**self).as_dyn_eq()
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            fn dyn_hash(&self, state: &mut dyn ::std::hash::Hasher) {
 | 
			
		||||
                (**self).dyn_hash(state);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Macro to define a new label trait
 | 
			
		||||
///
 | 
			
		||||
/// # Example
 | 
			
		||||
///
 | 
			
		||||
/// ```
 | 
			
		||||
/// # use bevy_utils::define_label;
 | 
			
		||||
/// define_label!(
 | 
			
		||||
///     /// A class of labels.
 | 
			
		||||
///     MyNewLabelTrait,
 | 
			
		||||
///     /// Identifies a value that implements `MyNewLabelTrait`.
 | 
			
		||||
///     MyNewLabelId,
 | 
			
		||||
/// );
 | 
			
		||||
/// ```
 | 
			
		||||
#[macro_export]
 | 
			
		||||
macro_rules! define_label {
 | 
			
		||||
    (
 | 
			
		||||
        $(#[$label_attr:meta])*
 | 
			
		||||
        $label_name:ident,
 | 
			
		||||
 | 
			
		||||
        $(#[$id_attr:meta])*
 | 
			
		||||
        $id_name:ident $(,)?
 | 
			
		||||
    ) => {
 | 
			
		||||
        $(#[$id_attr])*
 | 
			
		||||
        #[derive(Clone, Copy, PartialEq, Eq, Hash)]
 | 
			
		||||
        pub struct $id_name(::core::any::TypeId, &'static str);
 | 
			
		||||
 | 
			
		||||
        impl ::core::fmt::Debug for $id_name {
 | 
			
		||||
            fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
 | 
			
		||||
                write!(f, "{}", self.1)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $(#[$label_attr])*
 | 
			
		||||
        pub trait $label_name: Send + Sync + 'static {
 | 
			
		||||
            /// Converts this type into an opaque, strongly-typed label.
 | 
			
		||||
            fn as_label(&self) -> $id_name {
 | 
			
		||||
                let id = self.type_id();
 | 
			
		||||
                let label = self.as_str();
 | 
			
		||||
                $id_name(id, label)
 | 
			
		||||
            }
 | 
			
		||||
            /// Returns the [`TypeId`] used to differentiate labels.
 | 
			
		||||
            fn type_id(&self) -> ::core::any::TypeId {
 | 
			
		||||
                ::core::any::TypeId::of::<Self>()
 | 
			
		||||
            }
 | 
			
		||||
            /// Returns the representation of this label as a string literal.
 | 
			
		||||
            ///
 | 
			
		||||
            /// In cases where you absolutely need a label to be determined at runtime,
 | 
			
		||||
            /// you can use [`Box::leak`] to get a `'static` reference.
 | 
			
		||||
            fn as_str(&self) -> &'static str;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        impl $label_name for $id_name {
 | 
			
		||||
            fn as_label(&self) -> Self {
 | 
			
		||||
                *self
 | 
			
		||||
            }
 | 
			
		||||
            fn type_id(&self) -> ::core::any::TypeId {
 | 
			
		||||
                self.0
 | 
			
		||||
            }
 | 
			
		||||
            fn as_str(&self) -> &'static str {
 | 
			
		||||
                self.1
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        impl $label_name for &'static str {
 | 
			
		||||
            fn as_str(&self) -> Self {
 | 
			
		||||
                self
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        static $interner_name: $crate::intern::Interner<dyn $label_trait_name> =
 | 
			
		||||
            $crate::intern::Interner::new();
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -21,6 +21,7 @@ pub mod syncunsafecell;
 | 
			
		||||
mod cow_arc;
 | 
			
		||||
mod default;
 | 
			
		||||
mod float_ord;
 | 
			
		||||
pub mod intern;
 | 
			
		||||
 | 
			
		||||
pub use ahash::{AHasher, RandomState};
 | 
			
		||||
pub use bevy_utils_proc_macros::*;
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user