Skip to content

Commit

Permalink
Improve codegen for world validation (#9464)
Browse files Browse the repository at this point in the history
# Objective

Improve code-gen for `QueryState::validate_world` and
`SystemState::validate_world`.

## Solution

* Move panics into separate, non-inlined functions, to reduce the code
size of the outer methods.
* Mark the panicking functions with `#[cold]` to help the compiler
optimize for the happy path.
* Mark the functions with `#[track_caller]` to make debugging easier.

---------

Co-authored-by: James Liu <contact@jamessliu.com>
  • Loading branch information
JoJoJet and james7132 committed Sep 21, 2023
1 parent bdb0634 commit e60249e
Show file tree
Hide file tree
Showing 3 changed files with 23 additions and 7 deletions.
16 changes: 11 additions & 5 deletions crates/bevy_ecs/src/query/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -224,12 +224,18 @@ impl<Q: WorldQuery, F: ReadOnlyWorldQuery> QueryState<Q, F> {
/// Many unsafe query methods require the world to match for soundness. This function is the easiest
/// way of ensuring that it matches.
#[inline]
#[track_caller]
pub fn validate_world(&self, world_id: WorldId) {
assert!(
world_id == self.world_id,
"Attempted to use {} with a mismatched World. QueryStates can only be used with the World they were created from.",
std::any::type_name::<Self>(),
);
#[inline(never)]
#[track_caller]
#[cold]
fn panic_mismatched(this: WorldId, other: WorldId) -> ! {
panic!("Encountered a mismatched World. This QueryState was created from {this:?}, but a method was called using {other:?}.");
}

if self.world_id != world_id {
panic_mismatched(self.world_id, world_id);
}
}

/// Update the current [`QueryState`] with information from the provided [`Archetype`]
Expand Down
12 changes: 11 additions & 1 deletion crates/bevy_ecs/src/system/function_system.rs
Original file line number Diff line number Diff line change
Expand Up @@ -232,8 +232,18 @@ impl<Param: SystemParam> SystemState<Param> {

/// Asserts that the [`SystemState`] matches the provided world.
#[inline]
#[track_caller]
fn validate_world(&self, world_id: WorldId) {
assert!(self.matches_world(world_id), "Encountered a mismatched World. A SystemState cannot be used with Worlds other than the one it was created with.");
#[inline(never)]
#[track_caller]
#[cold]
fn panic_mismatched(this: WorldId, other: WorldId) -> ! {
panic!("Encountered a mismatched World. This SystemState was created from {this:?}, but a method was called using {other:?}.");
}

if !self.matches_world(world_id) {
panic_mismatched(self.world_id, world_id);
}
}

/// Updates the state's internal view of the [`World`]'s archetypes. If this is not called before fetching the parameters,
Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_ecs/src/system/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1775,7 +1775,7 @@ mod tests {
}

#[test]
#[should_panic = "Attempted to use bevy_ecs::query::state::QueryState<()> with a mismatched World."]
#[should_panic = "Encountered a mismatched World."]
fn query_validates_world_id() {
let mut world1 = World::new();
let world2 = World::new();
Expand Down

0 comments on commit e60249e

Please sign in to comment.