Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Merged by Bors] - Exclusive Systems Now Implement System. Flexible Exclusive System Params #6083

Closed
wants to merge 10 commits into from

Conversation

cart
Copy link
Member

@cart cart commented Sep 24, 2022

Objective

The Stageless RFC involves allowing exclusive systems to be referenced and ordered relative to parallel systems. We've agreed that unifying systems under System is the right move.

This is an alternative to #4166 (see rationale in the comments I left there). Note that this builds on the learnings established there (and borrows some patterns).

Solution

This unifies parallel and exclusive systems under the shared System trait, removing the old ExclusiveSystem trait / impls. This is accomplished by adding a new ExclusiveFunctionSystem impl similar to FunctionSystem. It is backed by ExclusiveSystemParam, which is similar to SystemParam. There is a new flattened out SystemContainer api (which cuts out a lot of trait and type complexity).

This means you can remove all cases of exclusive_system():

// before
commands.add_system(some_system.exclusive_system());
// after
commands.add_system(some_system);

I've also implemented ExclusiveSystemParam for &mut QueryState and &mut SystemState, which makes this possible in exclusive systems:

fn some_exclusive_system(
    world: &mut World,
    transforms: &mut QueryState<&Transform>,
    state: &mut SystemState<(Res<Time>, Query<&Player>)>,
) {
    for transform in transforms.iter(world) {
        println!("{transform:?}");
    }
    let (time, players) = state.get(world);
    for player in players.iter() {
        println!("{player:?}");
    }
}

Note that "exclusive function systems" assume &mut World is present (and the first param). I think this is a fair assumption, given that the presence of &mut World is what defines the need for an exclusive system.

I added some targeted SystemParam static constraints, which removed the need for this:

fn some_exclusive_system(state: &mut SystemState<(Res<'static, Time>, Query<&'static Player>)>) {}

Related

Changelog

  • ExclusiveSystem trait (and implementations) has been removed in favor of sharing the System trait.
  • ExclusiveFunctionSystem and ExclusiveSystemParam were added, enabling flexible exclusive function systems
  • &mut SystemState and &mut QueryState now implement ExclusiveSystemParam
  • Exclusive and parallel System configuration is now done via a unified SystemDescriptor, IntoSystemDescriptor, and SystemContainer api.

Migration Guide

Calling .exclusive_system() is no longer required (or supported) for converting exclusive system functions to exclusive systems:

// Old (0.8)
app.add_system(some_exclusive_system.exclusive_system());
// New (0.9)
app.add_system(some_exclusive_system);

Converting "normal" parallel systems to exclusive systems is done by calling the exclusive ordering apis:

// Old (0.8)
app.add_system(some_system.exclusive_system().at_end());
// New (0.9)
app.add_system(some_system.at_end());

Query state in exclusive systems can now be cached via ExclusiveSystemParams, which should be preferred for clarity and performance reasons:

// Old (0.8)
fn some_system(world: &mut World) {
  let mut transforms = world.query::<&Transform>();
  for transform in transforms.iter(world) {
  }
}
// New (0.9)
fn some_system(world: &mut World, transforms: &mut QueryState<&Transform>) {
  for transform in transforms.iter(world) {
  }
}

@cart cart added A-ECS Entities, components, systems, and events C-Code-Quality A section of code that is hard to understand or change X-Controversial There is active debate or serious implications around merging this PR labels Sep 24, 2022
@cart cart changed the title System unification Exclusive Systems Now Implement System. Flexible Exclusive System Params Sep 24, 2022
@cart cart marked this pull request as draft September 24, 2022 02:34
@cart cart added this to the Bevy 0.9 milestone Sep 24, 2022
@maniwani
Copy link
Contributor

maniwani commented Sep 24, 2022

To fix the ambiguity checker test, I think one solution would be to change the lines in find_ambiguities here to something like:

let system_a = systems[index_a].system();
let system_b = systems[index_b].system();
if system_a.is_exclusive() || system_b.is_exclusive() {
    ambiguities.push((index_a, index_b, Vec::new());
} else {
    let access_a = system_a.component_access();
    let access_b = system_b.component_access();
    let conflicts = access_a.get_conflicts(access_b);
    if !conflicts.is_empty() {
        ambiguities.push((index_a, index_b, conflicts));
    }
}

At the moment, find_ambiguities assumes SystemContainer::component_access returns None when the system is exclusive. But that's no longer the case here, so the existing else branch is never taken.

Copy link
Member

@alice-i-cecile alice-i-cecile left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a clear and straightforward improvement.

[#4166] does not have a hard line exclusive/parallel trait split, which is what causes arbitrary limits / inconsistencies. E.g. #6083 would prevent a user from implementing a custom param with a &mut World field and from using &mut World in a ParamSet.

This is the largest argument in favor of #4166 to me, and I think it's quite compelling. I think that the model there is fundamentally more correct, but it is also more complex to verify the changeset.

My overall opinion is that we should merge #4166 instead; that trait-level divergence will bite us and there's less ad-hoc special-casing. That said, those details are much less important to me than unblocking further work on stageless implementation, and I would not fight you on a decision to merge this (but would push for those changes later).

@cart
Copy link
Member Author

cart commented Sep 26, 2022

4166 does not have a hard line exclusive/parallel trait split, which is what causes arbitrary limits / inconsistencies. E.g. #6083 would prevent a user from implementing a custom param with a &mut World field and from using &mut World in a ParamSet.
My overall opinion is that we should merge #4166 instead; that trait-level divergence will bite us

From my perspective, these are two entirely separate problem spaces. This isn't some hack that will burn us later, its a fundamental difference between parameter types (which both PRs enforce) encoded statically in the type system.

  1. SystemParam: extract internal references from &World and ensure these references don't conflict with each other.
  2. ExclusiveSystemParam: cannot ever own any references to &World and therefore never needs to worry about conflicts. These are "just" references to data to facilitate interacting with &mut World.

In #4166, MaybeUnsafeCell exists to allow exclusively run systems to pass their &mut World pointer in, while letting parallel systems pass their &World pointer in. It will never be sound to convert an &World pointer to &mut World, so the only time &mut World can show up in a system is if it is run exclusively.

Likewise world_access_level exists solely to ensure that parallel systems already accessing &World never try to also access &mut World, which would obviously be unsound.

We're doing all of this work to ensure a hard split at runtime between the two types of parameters (which is necessary), in the interest of sharing a common trait. Why open that door? Why incur runtime overhead and increased risk of doing "the wrong thing" (which is very unsafe). What does it win us (now or in the future)?

If the answer is ParamSet<(&mut World, Query<&Transform>)> (which is the only way we could possibly make putting &mut World in a parallel system valid), I don't think thats worth it. That would only be safe to run as an exclusive system. And that could more clearly be written as:

fn exclusive_system(world: &mut World, transforms: &mut QueryState<&Transform>) {
}

// or alternatively
fn exclusive_system(world: &mut World, state: &mut SystemState<Query<&Transform>>) {
}

In addition to arguments about internals made above, this is better for users for a number of reasons:

  1. The query is named
  2. No need for set.p1().iter(). Just type transforms.iter(world)
  3. Unlike 4166, which will have runtime errors if you try doing something like fn system(set: ParamSet<(&mut World, Query<&Transform>)>, q: Query<&Player>). Instead, you get a compile time error because the only way to access &mut World is via an exclusive system. Attempting to mix and match incompatible parameters will be compiler errors, not runtime errors.

@maniwani
Copy link
Contributor

maniwani commented Sep 26, 2022

What does it win us?

The ability for users to write custom params with &mut World as a field. If that can still be done here or you've decided that the static guarantees are worth not being able to do that (or if you consider that a small loss or even a plus), then I have nothing else to add.

Attempting to mix and match incompatible parameters will be compiler errors, not runtime errors.

Just between SystemParam and ExclusiveSystemParam. Incompatible queries will still be runtime errors.


I think a trait split is less convenient for custom system param impls, but more obviously sound. That's my feedback. I'm a bit biased, but I'd be glad to see either of these merged.

Anecdotally, the edict library seems to have chosen (or is testing) the 4166 route. Not saying that's a reason for us to do it, I just thought it was interesting.
(FnArg -> SystemParam, FnArgCache -> SystemParamState, ActionEncoder -> Commands)
https://docs.rs/edict/0.2.0-rc.3/edict/system/trait.FnArg.html#implementors
https://docs.rs/edict/0.2.0-rc.3/edict/system/trait.System.html#tymethod.world_access

Copy link
Contributor

@maniwani maniwani left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One fix related to change detection and a sanity check that this PR won't affect adapting the executor to support exclusive systems.

crates/bevy_ecs/src/system/exclusive_function_system.rs Outdated Show resolved Hide resolved
crates/bevy_ecs/src/system/exclusive_function_system.rs Outdated Show resolved Hide resolved
@cart
Copy link
Member Author

cart commented Sep 26, 2022

The ability for users to write custom params with &mut World as a field. If that can still be done here or you've decided that the static guarantees are worth not being able to do that (or if you consider that a small loss or even a plus), then I have nothing else to add.

I can't think of any use case for custom system params containing &mut World that wouldn't be better handled as an exclusive system expressed via ExclusiveSystemParams. If anyone can think of one, I'm all ears!

@cart
Copy link
Member Author

cart commented Sep 26, 2022

Just between SystemParam and ExclusiveSystemParam. Incompatible queries will still be runtime errors.

Yup this was the only point I was trying to make. This specific type of incompatibility (the problem space solved by world_access_level in #4166) is static instead of dynamic.

@cart
Copy link
Member Author

cart commented Sep 26, 2022

At the moment, find_ambiguities assumes SystemContainer::component_access returns None when the system is exclusive. But that's no longer the case here, so the existing else branch is never taken.

Very nice catch! That does seem like the fix. Thanks!

@cart cart marked this pull request as ready for review September 26, 2022 22:28
@cart
Copy link
Member Author

cart commented Sep 26, 2022

It is worth calling out that #4166 could have better error messages for this class of invalid system, compared to this PR. Consider the following example:

fn setup(
    mut materials: ResMut<Assets<StandardMaterial>>,
    world: &mut World,
) {

#4166 currently fails with 'Invalid combination of system params.'
#6083 fails with the classic "invalid system error": the trait bound "for<'r, 's> fn(bevy::prelude::ResMut<'r, bevy::prelude::Assetsbevy::prelude::StandardMaterial>, &'s mut bevy::prelude::World) {setup}: IntoSystem<(), (), _>` is not satisfied"

Currently, neither tells you which param is bad, but in theory #4166 could be adapted to call out &mut World explicitly (because it has access to runtime state).

I personally think this isn't enough to tilt the scales in favor of #4166. Invalid system functions already hit the for<'r, 's> class of error and we already have good reason to think that Rust will give us tooling to make this better in the near future. This is just one more (relatively unlikely) expression of a common issue.

@cart
Copy link
Member Author

cart commented Sep 26, 2022

bors r+

bors bot pushed a commit that referenced this pull request Sep 26, 2022
…arams (#6083)

# Objective

The [Stageless RFC](bevyengine/rfcs#45) involves allowing exclusive systems to be referenced and ordered relative to parallel systems. We've agreed that unifying systems under `System` is the right move.

This is an alternative to #4166 (see rationale in the comments I left there). Note that this builds on the learnings established there (and borrows some patterns).

## Solution

This unifies parallel and exclusive systems under the shared `System` trait, removing the old `ExclusiveSystem` trait / impls. This is accomplished by adding a new `ExclusiveFunctionSystem` impl similar to `FunctionSystem`. It is backed by `ExclusiveSystemParam`, which is similar to `SystemParam`. There is a new flattened out SystemContainer api (which cuts out a lot of trait and type complexity). 

This means you can remove all cases of `exclusive_system()`:

```rust
// before
commands.add_system(some_system.exclusive_system());
// after
commands.add_system(some_system);
```

I've also implemented `ExclusiveSystemParam` for `&mut QueryState` and `&mut SystemState`, which makes this possible in exclusive systems:

```rust
fn some_exclusive_system(
    world: &mut World,
    transforms: &mut QueryState<&Transform>,
    state: &mut SystemState<(Res<Time>, Query<&Player>)>,
) {
    for transform in transforms.iter(world) {
        println!("{transform:?}");
    }
    let (time, players) = state.get(world);
    for player in players.iter() {
        println!("{player:?}");
    }
}
```

Note that "exclusive function systems" assume `&mut World` is present (and the first param). I think this is a fair assumption, given that the presence of `&mut World` is what defines the need for an exclusive system.

I added some targeted SystemParam `static` constraints, which removed the need for this:
``` rust
fn some_exclusive_system(state: &mut SystemState<(Res<'static, Time>, Query<&'static Player>)>) {}
```

## Related

- #2923
- #3001
- #3946

## Changelog

- `ExclusiveSystem` trait (and implementations) has been removed in favor of sharing the `System` trait.
- `ExclusiveFunctionSystem` and `ExclusiveSystemParam` were added, enabling flexible exclusive function systems
- `&mut SystemState` and `&mut QueryState` now implement `ExclusiveSystemParam`
- Exclusive and parallel System configuration is now done via a unified `SystemDescriptor`, `IntoSystemDescriptor`, and `SystemContainer` api.

## Migration Guide

Calling `.exclusive_system()` is no longer required (or supported) for converting exclusive system functions to exclusive systems:

```rust
// Old (0.8)
app.add_system(some_exclusive_system.exclusive_system());
// New (0.9)
app.add_system(some_exclusive_system);
```

Converting "normal" parallel systems to exclusive systems is done by calling the exclusive ordering apis:

```rust
// Old (0.8)
app.add_system(some_system.exclusive_system().at_end());
// New (0.9)
app.add_system(some_system.at_end());
```

Query state in exclusive systems can now be cached via ExclusiveSystemParams, which should be preferred for clarity and performance reasons:
```rust
// Old (0.8)
fn some_system(world: &mut World) {
  let mut transforms = world.query::<&Transform>();
  for transform in transforms.iter(world) {
  }
}
// New (0.9)
fn some_system(world: &mut World, transforms: &mut QueryState<&Transform>) {
  for transform in transforms.iter(world) {
  }
}
```
@bors bors bot changed the title Exclusive Systems Now Implement System. Flexible Exclusive System Params [Merged by Bors] - Exclusive Systems Now Implement System. Flexible Exclusive System Params Sep 27, 2022
@bors bors bot closed this Sep 27, 2022
bors bot pushed a commit that referenced this pull request Oct 3, 2022
# Objective

Now that #6083 has been merged, we can clean up some ugly ambiguity detection code.

# Solution

Deduplicate code.
james7132 pushed a commit to james7132/bevy that referenced this pull request Oct 19, 2022
…arams (bevyengine#6083)

# Objective

The [Stageless RFC](bevyengine/rfcs#45) involves allowing exclusive systems to be referenced and ordered relative to parallel systems. We've agreed that unifying systems under `System` is the right move.

This is an alternative to bevyengine#4166 (see rationale in the comments I left there). Note that this builds on the learnings established there (and borrows some patterns).

## Solution

This unifies parallel and exclusive systems under the shared `System` trait, removing the old `ExclusiveSystem` trait / impls. This is accomplished by adding a new `ExclusiveFunctionSystem` impl similar to `FunctionSystem`. It is backed by `ExclusiveSystemParam`, which is similar to `SystemParam`. There is a new flattened out SystemContainer api (which cuts out a lot of trait and type complexity). 

This means you can remove all cases of `exclusive_system()`:

```rust
// before
commands.add_system(some_system.exclusive_system());
// after
commands.add_system(some_system);
```

I've also implemented `ExclusiveSystemParam` for `&mut QueryState` and `&mut SystemState`, which makes this possible in exclusive systems:

```rust
fn some_exclusive_system(
    world: &mut World,
    transforms: &mut QueryState<&Transform>,
    state: &mut SystemState<(Res<Time>, Query<&Player>)>,
) {
    for transform in transforms.iter(world) {
        println!("{transform:?}");
    }
    let (time, players) = state.get(world);
    for player in players.iter() {
        println!("{player:?}");
    }
}
```

Note that "exclusive function systems" assume `&mut World` is present (and the first param). I think this is a fair assumption, given that the presence of `&mut World` is what defines the need for an exclusive system.

I added some targeted SystemParam `static` constraints, which removed the need for this:
``` rust
fn some_exclusive_system(state: &mut SystemState<(Res<'static, Time>, Query<&'static Player>)>) {}
```

## Related

- bevyengine#2923
- bevyengine#3001
- bevyengine#3946

## Changelog

- `ExclusiveSystem` trait (and implementations) has been removed in favor of sharing the `System` trait.
- `ExclusiveFunctionSystem` and `ExclusiveSystemParam` were added, enabling flexible exclusive function systems
- `&mut SystemState` and `&mut QueryState` now implement `ExclusiveSystemParam`
- Exclusive and parallel System configuration is now done via a unified `SystemDescriptor`, `IntoSystemDescriptor`, and `SystemContainer` api.

## Migration Guide

Calling `.exclusive_system()` is no longer required (or supported) for converting exclusive system functions to exclusive systems:

```rust
// Old (0.8)
app.add_system(some_exclusive_system.exclusive_system());
// New (0.9)
app.add_system(some_exclusive_system);
```

Converting "normal" parallel systems to exclusive systems is done by calling the exclusive ordering apis:

```rust
// Old (0.8)
app.add_system(some_system.exclusive_system().at_end());
// New (0.9)
app.add_system(some_system.at_end());
```

Query state in exclusive systems can now be cached via ExclusiveSystemParams, which should be preferred for clarity and performance reasons:
```rust
// Old (0.8)
fn some_system(world: &mut World) {
  let mut transforms = world.query::<&Transform>();
  for transform in transforms.iter(world) {
  }
}
// New (0.9)
fn some_system(world: &mut World, transforms: &mut QueryState<&Transform>) {
  for transform in transforms.iter(world) {
  }
}
```
james7132 pushed a commit to james7132/bevy that referenced this pull request Oct 19, 2022
# Objective

Now that bevyengine#6083 has been merged, we can clean up some ugly ambiguity detection code.

# Solution

Deduplicate code.
@ickk ickk added the M-Needs-Migration-Guide A breaking change to Bevy's public API that needs to be noted in a migration guide label Oct 26, 2022
james7132 pushed a commit to james7132/bevy that referenced this pull request Oct 28, 2022
…arams (bevyengine#6083)

# Objective

The [Stageless RFC](bevyengine/rfcs#45) involves allowing exclusive systems to be referenced and ordered relative to parallel systems. We've agreed that unifying systems under `System` is the right move.

This is an alternative to bevyengine#4166 (see rationale in the comments I left there). Note that this builds on the learnings established there (and borrows some patterns).

## Solution

This unifies parallel and exclusive systems under the shared `System` trait, removing the old `ExclusiveSystem` trait / impls. This is accomplished by adding a new `ExclusiveFunctionSystem` impl similar to `FunctionSystem`. It is backed by `ExclusiveSystemParam`, which is similar to `SystemParam`. There is a new flattened out SystemContainer api (which cuts out a lot of trait and type complexity). 

This means you can remove all cases of `exclusive_system()`:

```rust
// before
commands.add_system(some_system.exclusive_system());
// after
commands.add_system(some_system);
```

I've also implemented `ExclusiveSystemParam` for `&mut QueryState` and `&mut SystemState`, which makes this possible in exclusive systems:

```rust
fn some_exclusive_system(
    world: &mut World,
    transforms: &mut QueryState<&Transform>,
    state: &mut SystemState<(Res<Time>, Query<&Player>)>,
) {
    for transform in transforms.iter(world) {
        println!("{transform:?}");
    }
    let (time, players) = state.get(world);
    for player in players.iter() {
        println!("{player:?}");
    }
}
```

Note that "exclusive function systems" assume `&mut World` is present (and the first param). I think this is a fair assumption, given that the presence of `&mut World` is what defines the need for an exclusive system.

I added some targeted SystemParam `static` constraints, which removed the need for this:
``` rust
fn some_exclusive_system(state: &mut SystemState<(Res<'static, Time>, Query<&'static Player>)>) {}
```

## Related

- bevyengine#2923
- bevyengine#3001
- bevyengine#3946

## Changelog

- `ExclusiveSystem` trait (and implementations) has been removed in favor of sharing the `System` trait.
- `ExclusiveFunctionSystem` and `ExclusiveSystemParam` were added, enabling flexible exclusive function systems
- `&mut SystemState` and `&mut QueryState` now implement `ExclusiveSystemParam`
- Exclusive and parallel System configuration is now done via a unified `SystemDescriptor`, `IntoSystemDescriptor`, and `SystemContainer` api.

## Migration Guide

Calling `.exclusive_system()` is no longer required (or supported) for converting exclusive system functions to exclusive systems:

```rust
// Old (0.8)
app.add_system(some_exclusive_system.exclusive_system());
// New (0.9)
app.add_system(some_exclusive_system);
```

Converting "normal" parallel systems to exclusive systems is done by calling the exclusive ordering apis:

```rust
// Old (0.8)
app.add_system(some_system.exclusive_system().at_end());
// New (0.9)
app.add_system(some_system.at_end());
```

Query state in exclusive systems can now be cached via ExclusiveSystemParams, which should be preferred for clarity and performance reasons:
```rust
// Old (0.8)
fn some_system(world: &mut World) {
  let mut transforms = world.query::<&Transform>();
  for transform in transforms.iter(world) {
  }
}
// New (0.9)
fn some_system(world: &mut World, transforms: &mut QueryState<&Transform>) {
  for transform in transforms.iter(world) {
  }
}
```
james7132 pushed a commit to james7132/bevy that referenced this pull request Oct 28, 2022
# Objective

Now that bevyengine#6083 has been merged, we can clean up some ugly ambiguity detection code.

# Solution

Deduplicate code.
ItsDoot pushed a commit to ItsDoot/bevy that referenced this pull request Feb 1, 2023
…arams (bevyengine#6083)

# Objective

The [Stageless RFC](bevyengine/rfcs#45) involves allowing exclusive systems to be referenced and ordered relative to parallel systems. We've agreed that unifying systems under `System` is the right move.

This is an alternative to bevyengine#4166 (see rationale in the comments I left there). Note that this builds on the learnings established there (and borrows some patterns).

## Solution

This unifies parallel and exclusive systems under the shared `System` trait, removing the old `ExclusiveSystem` trait / impls. This is accomplished by adding a new `ExclusiveFunctionSystem` impl similar to `FunctionSystem`. It is backed by `ExclusiveSystemParam`, which is similar to `SystemParam`. There is a new flattened out SystemContainer api (which cuts out a lot of trait and type complexity). 

This means you can remove all cases of `exclusive_system()`:

```rust
// before
commands.add_system(some_system.exclusive_system());
// after
commands.add_system(some_system);
```

I've also implemented `ExclusiveSystemParam` for `&mut QueryState` and `&mut SystemState`, which makes this possible in exclusive systems:

```rust
fn some_exclusive_system(
    world: &mut World,
    transforms: &mut QueryState<&Transform>,
    state: &mut SystemState<(Res<Time>, Query<&Player>)>,
) {
    for transform in transforms.iter(world) {
        println!("{transform:?}");
    }
    let (time, players) = state.get(world);
    for player in players.iter() {
        println!("{player:?}");
    }
}
```

Note that "exclusive function systems" assume `&mut World` is present (and the first param). I think this is a fair assumption, given that the presence of `&mut World` is what defines the need for an exclusive system.

I added some targeted SystemParam `static` constraints, which removed the need for this:
``` rust
fn some_exclusive_system(state: &mut SystemState<(Res<'static, Time>, Query<&'static Player>)>) {}
```

## Related

- bevyengine#2923
- bevyengine#3001
- bevyengine#3946

## Changelog

- `ExclusiveSystem` trait (and implementations) has been removed in favor of sharing the `System` trait.
- `ExclusiveFunctionSystem` and `ExclusiveSystemParam` were added, enabling flexible exclusive function systems
- `&mut SystemState` and `&mut QueryState` now implement `ExclusiveSystemParam`
- Exclusive and parallel System configuration is now done via a unified `SystemDescriptor`, `IntoSystemDescriptor`, and `SystemContainer` api.

## Migration Guide

Calling `.exclusive_system()` is no longer required (or supported) for converting exclusive system functions to exclusive systems:

```rust
// Old (0.8)
app.add_system(some_exclusive_system.exclusive_system());
// New (0.9)
app.add_system(some_exclusive_system);
```

Converting "normal" parallel systems to exclusive systems is done by calling the exclusive ordering apis:

```rust
// Old (0.8)
app.add_system(some_system.exclusive_system().at_end());
// New (0.9)
app.add_system(some_system.at_end());
```

Query state in exclusive systems can now be cached via ExclusiveSystemParams, which should be preferred for clarity and performance reasons:
```rust
// Old (0.8)
fn some_system(world: &mut World) {
  let mut transforms = world.query::<&Transform>();
  for transform in transforms.iter(world) {
  }
}
// New (0.9)
fn some_system(world: &mut World, transforms: &mut QueryState<&Transform>) {
  for transform in transforms.iter(world) {
  }
}
```
ItsDoot pushed a commit to ItsDoot/bevy that referenced this pull request Feb 1, 2023
# Objective

Now that bevyengine#6083 has been merged, we can clean up some ugly ambiguity detection code.

# Solution

Deduplicate code.
bors bot pushed a commit that referenced this pull request Feb 4, 2023
# Objective

The trait method `SystemParam::apply` allows a `SystemParam` type to defer world mutations, which is internally used to apply `Commands` at the end of the stage. Any operations that require `&mut World` access must be deferred in this way, since parallel systems do not have exclusive access to the world.

The `ExclusiveSystemParam` trait (added in #6083) has an `apply` method which serves the same purpose. However, deferring mutations in this way does not make sense for exclusive systems since they already have `&mut World` access: there is no need to wait until a hard sync point, as the system *is* a hard sync point. World mutations can and should be performed within the body of the system.

## Solution

Remove the method. There were no implementations of this method in the engine.

---

## Changelog

*Note for maintainers: this changelog makes more sense if it's placed above the one for #6919.*

- Removed the method `ExclusiveSystemParamState::apply`.

## Migration Guide

*Note for maintainers: this migration guide makes more sense if it's placed above the one for #6919.*

The trait method `ExclusiveSystemParamState::apply` has been removed. If you have an exclusive system with buffers that must be applied, you should apply them within the body of the exclusive system.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-ECS Entities, components, systems, and events C-Code-Quality A section of code that is hard to understand or change M-Needs-Migration-Guide A breaking change to Bevy's public API that needs to be noted in a migration guide X-Controversial There is active debate or serious implications around merging this PR
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants