Skip to content

Commit 3442a13

Browse files
authored
Use UnsafeWorldCell to increase code quality for SystemParam (#8174)
# Objective The type `&World` is currently in an awkward place, since it has two meanings: 1. Read-only access to the entire world. 2. Interior mutable access to the world; immutable and/or mutable access to certain portions of world data. This makes `&World` difficult to reason about, and surprising to see in function signatures if one does not know about the interior mutable property. The type `UnsafeWorldCell` was added in #6404, which is meant to alleviate this confusion by adding a dedicated type for interior mutable world access. However, much of the engine still treats `&World` as an interior mutable-ish type. One of those places is `SystemParam`. ## Solution Modify `SystemParam::get_param` to accept `UnsafeWorldCell` instead of `&World`. Simplify the safety invariants, since the `UnsafeWorldCell` type encapsulates the concept of constrained world access. --- ## Changelog `SystemParam::get_param` now accepts an `UnsafeWorldCell` instead of `&World`. This type provides a high-level API for unsafe interior mutable world access. ## Migration Guide For manual implementers of `SystemParam`: the function `get_item` now takes `UnsafeWorldCell` instead of `&World`. To access world data, use: * `.get_entity()`, which returns an `UnsafeEntityCell` which can be used to access component data. * `get_resource()` and its variants, to access resource data.
1 parent 253db50 commit 3442a13

File tree

7 files changed

+57
-48
lines changed

7 files changed

+57
-48
lines changed

crates/bevy_ecs/macros/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,7 @@ pub fn impl_param_set(_input: TokenStream) -> TokenStream {
229229
unsafe fn get_param<'w, 's>(
230230
state: &'s mut Self::State,
231231
system_meta: &SystemMeta,
232-
world: &'w World,
232+
world: UnsafeWorldCell<'w>,
233233
change_tick: Tick,
234234
) -> Self::Item<'w, 's> {
235235
ParamSet {
@@ -407,7 +407,7 @@ pub fn derive_system_param(input: TokenStream) -> TokenStream {
407407
unsafe fn get_param<'w, 's>(
408408
state: &'s mut Self::State,
409409
system_meta: &#path::system::SystemMeta,
410-
world: &'w #path::world::World,
410+
world: #path::world::unsafe_world_cell::UnsafeWorldCell<'w>,
411411
change_tick: #path::component::Tick,
412412
) -> Self::Item<'w, 's> {
413413
let (#(#tuple_patterns,)*) = <

crates/bevy_ecs/src/removal_detection.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use crate::{
88
prelude::Local,
99
storage::SparseSet,
1010
system::{ReadOnlySystemParam, SystemMeta, SystemParam},
11-
world::World,
11+
world::{unsafe_world_cell::UnsafeWorldCell, World},
1212
};
1313

1414
use std::{
@@ -264,9 +264,9 @@ unsafe impl<'a> SystemParam for &'a RemovedComponentEvents {
264264
unsafe fn get_param<'w, 's>(
265265
_state: &'s mut Self::State,
266266
_system_meta: &SystemMeta,
267-
world: &'w World,
267+
world: UnsafeWorldCell<'w>,
268268
_change_tick: Tick,
269269
) -> Self::Item<'w, 's> {
270-
world.removed_components()
270+
world.world_metadata().removed_components()
271271
}
272272
}

crates/bevy_ecs/src/system/function_system.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -291,7 +291,12 @@ impl<Param: SystemParam> SystemState<Param> {
291291
world: &'w World,
292292
change_tick: Tick,
293293
) -> SystemParamItem<'w, 's, Param> {
294-
let param = Param::get_param(&mut self.param_state, &self.meta, world, change_tick);
294+
let param = Param::get_param(
295+
&mut self.param_state,
296+
&self.meta,
297+
world.as_unsafe_world_cell_migration_internal(),
298+
change_tick,
299+
);
295300
self.meta.last_run = change_tick;
296301
param
297302
}
@@ -481,7 +486,7 @@ where
481486
let params = F::Param::get_param(
482487
self.param_state.as_mut().expect(Self::PARAM_MESSAGE),
483488
&self.system_meta,
484-
world,
489+
world.as_unsafe_world_cell_migration_internal(),
485490
change_tick,
486491
);
487492
let out = self.func.run(input, params);

crates/bevy_ecs/src/system/system_param.rs

Lines changed: 38 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use crate::{
99
Access, FilteredAccess, FilteredAccessSet, QueryState, ReadOnlyWorldQuery, WorldQuery,
1010
},
1111
system::{Query, SystemMeta},
12-
world::{FromWorld, World},
12+
world::{unsafe_world_cell::UnsafeWorldCell, FromWorld, World},
1313
};
1414
use bevy_ecs_macros::impl_param_set;
1515
pub use bevy_ecs_macros::Resource;
@@ -127,14 +127,13 @@ pub unsafe trait SystemParam: Sized {
127127

128128
/// # Safety
129129
///
130-
/// This call might use any of the [`World`] accesses that were registered in [`Self::init_state`].
131-
/// - None of those accesses may conflict with any other [`SystemParam`]s
132-
/// that exist at the same time, including those on other threads.
130+
/// - The passed [`UnsafeWorldCell`] must have access to any world data
131+
/// registered in [`init_state`](SystemParam::init_state).
133132
/// - `world` must be the same `World` that was used to initialize [`state`](SystemParam::init_state).
134133
unsafe fn get_param<'world, 'state>(
135134
state: &'state mut Self::State,
136135
system_meta: &SystemMeta,
137-
world: &'world World,
136+
world: UnsafeWorldCell<'world>,
138137
change_tick: Tick,
139138
) -> Self::Item<'world, 'state>;
140139
}
@@ -192,10 +191,19 @@ unsafe impl<Q: WorldQuery + 'static, F: ReadOnlyWorldQuery + 'static> SystemPara
192191
unsafe fn get_param<'w, 's>(
193192
state: &'s mut Self::State,
194193
system_meta: &SystemMeta,
195-
world: &'w World,
194+
world: UnsafeWorldCell<'w>,
196195
change_tick: Tick,
197196
) -> Self::Item<'w, 's> {
198-
Query::new(world, state, system_meta.last_run, change_tick, false)
197+
Query::new(
198+
// SAFETY: We have registered all of the query's world accesses,
199+
// so the caller ensures that `world` has permission to access any
200+
// world data that the query needs.
201+
world.unsafe_world(),
202+
state,
203+
system_meta.last_run,
204+
change_tick,
205+
false,
206+
)
199207
}
200208
}
201209

@@ -329,7 +337,7 @@ fn assert_component_access_compatibility(
329337
/// ```
330338
pub struct ParamSet<'w, 's, T: SystemParam> {
331339
param_states: &'s mut T::State,
332-
world: &'w World,
340+
world: UnsafeWorldCell<'w>,
333341
system_meta: SystemMeta,
334342
change_tick: Tick,
335343
}
@@ -435,11 +443,10 @@ unsafe impl<'a, T: Resource> SystemParam for Res<'a, T> {
435443
unsafe fn get_param<'w, 's>(
436444
&mut component_id: &'s mut Self::State,
437445
system_meta: &SystemMeta,
438-
world: &'w World,
446+
world: UnsafeWorldCell<'w>,
439447
change_tick: Tick,
440448
) -> Self::Item<'w, 's> {
441449
let (ptr, ticks) = world
442-
.as_unsafe_world_cell_migration_internal()
443450
.get_resource_with_ticks(component_id)
444451
.unwrap_or_else(|| {
445452
panic!(
@@ -476,11 +483,10 @@ unsafe impl<'a, T: Resource> SystemParam for Option<Res<'a, T>> {
476483
unsafe fn get_param<'w, 's>(
477484
&mut component_id: &'s mut Self::State,
478485
system_meta: &SystemMeta,
479-
world: &'w World,
486+
world: UnsafeWorldCell<'w>,
480487
change_tick: Tick,
481488
) -> Self::Item<'w, 's> {
482489
world
483-
.as_unsafe_world_cell_migration_internal()
484490
.get_resource_with_ticks(component_id)
485491
.map(|(ptr, ticks)| Res {
486492
value: ptr.deref(),
@@ -530,11 +536,10 @@ unsafe impl<'a, T: Resource> SystemParam for ResMut<'a, T> {
530536
unsafe fn get_param<'w, 's>(
531537
&mut component_id: &'s mut Self::State,
532538
system_meta: &SystemMeta,
533-
world: &'w World,
539+
world: UnsafeWorldCell<'w>,
534540
change_tick: Tick,
535541
) -> Self::Item<'w, 's> {
536542
let value = world
537-
.as_unsafe_world_cell_migration_internal()
538543
.get_resource_mut_by_id(component_id)
539544
.unwrap_or_else(|| {
540545
panic!(
@@ -568,11 +573,10 @@ unsafe impl<'a, T: Resource> SystemParam for Option<ResMut<'a, T>> {
568573
unsafe fn get_param<'w, 's>(
569574
&mut component_id: &'s mut Self::State,
570575
system_meta: &SystemMeta,
571-
world: &'w World,
576+
world: UnsafeWorldCell<'w>,
572577
change_tick: Tick,
573578
) -> Self::Item<'w, 's> {
574579
world
575-
.as_unsafe_world_cell_migration_internal()
576580
.get_resource_mut_by_id(component_id)
577581
.map(|value| ResMut {
578582
value: value.value.deref_mut::<T>(),
@@ -621,10 +625,11 @@ unsafe impl SystemParam for &'_ World {
621625
unsafe fn get_param<'w, 's>(
622626
_state: &'s mut Self::State,
623627
_system_meta: &SystemMeta,
624-
world: &'w World,
628+
world: UnsafeWorldCell<'w>,
625629
_change_tick: Tick,
626630
) -> Self::Item<'w, 's> {
627-
world
631+
// SAFETY: Read-only access to the entire world was registerd in `init_state`.
632+
world.world()
628633
}
629634
}
630635

@@ -742,7 +747,7 @@ unsafe impl<'a, T: FromWorld + Send + 'static> SystemParam for Local<'a, T> {
742747
unsafe fn get_param<'w, 's>(
743748
state: &'s mut Self::State,
744749
_system_meta: &SystemMeta,
745-
_world: &'w World,
750+
_world: UnsafeWorldCell<'w>,
746751
_change_tick: Tick,
747752
) -> Self::Item<'w, 's> {
748753
Local(state.get())
@@ -916,7 +921,7 @@ unsafe impl<T: SystemBuffer> SystemParam for Deferred<'_, T> {
916921
unsafe fn get_param<'w, 's>(
917922
state: &'s mut Self::State,
918923
_system_meta: &SystemMeta,
919-
_world: &'w World,
924+
_world: UnsafeWorldCell<'w>,
920925
_change_tick: Tick,
921926
) -> Self::Item<'w, 's> {
922927
Deferred(state.get())
@@ -1022,11 +1027,10 @@ unsafe impl<'a, T: 'static> SystemParam for NonSend<'a, T> {
10221027
unsafe fn get_param<'w, 's>(
10231028
&mut component_id: &'s mut Self::State,
10241029
system_meta: &SystemMeta,
1025-
world: &'w World,
1030+
world: UnsafeWorldCell<'w>,
10261031
change_tick: Tick,
10271032
) -> Self::Item<'w, 's> {
10281033
let (ptr, ticks) = world
1029-
.as_unsafe_world_cell_migration_internal()
10301034
.get_non_send_with_ticks(component_id)
10311035
.unwrap_or_else(|| {
10321036
panic!(
@@ -1061,11 +1065,10 @@ unsafe impl<T: 'static> SystemParam for Option<NonSend<'_, T>> {
10611065
unsafe fn get_param<'w, 's>(
10621066
&mut component_id: &'s mut Self::State,
10631067
system_meta: &SystemMeta,
1064-
world: &'w World,
1068+
world: UnsafeWorldCell<'w>,
10651069
change_tick: Tick,
10661070
) -> Self::Item<'w, 's> {
10671071
world
1068-
.as_unsafe_world_cell_migration_internal()
10691072
.get_non_send_with_ticks(component_id)
10701073
.map(|(ptr, ticks)| NonSend {
10711074
value: ptr.deref(),
@@ -1114,11 +1117,10 @@ unsafe impl<'a, T: 'static> SystemParam for NonSendMut<'a, T> {
11141117
unsafe fn get_param<'w, 's>(
11151118
&mut component_id: &'s mut Self::State,
11161119
system_meta: &SystemMeta,
1117-
world: &'w World,
1120+
world: UnsafeWorldCell<'w>,
11181121
change_tick: Tick,
11191122
) -> Self::Item<'w, 's> {
11201123
let (ptr, ticks) = world
1121-
.as_unsafe_world_cell_migration_internal()
11221124
.get_non_send_with_ticks(component_id)
11231125
.unwrap_or_else(|| {
11241126
panic!(
@@ -1147,11 +1149,10 @@ unsafe impl<'a, T: 'static> SystemParam for Option<NonSendMut<'a, T>> {
11471149
unsafe fn get_param<'w, 's>(
11481150
&mut component_id: &'s mut Self::State,
11491151
system_meta: &SystemMeta,
1150-
world: &'w World,
1152+
world: UnsafeWorldCell<'w>,
11511153
change_tick: Tick,
11521154
) -> Self::Item<'w, 's> {
11531155
world
1154-
.as_unsafe_world_cell_migration_internal()
11551156
.get_non_send_with_ticks(component_id)
11561157
.map(|(ptr, ticks)| NonSendMut {
11571158
value: ptr.assert_unique().deref_mut(),
@@ -1174,7 +1175,7 @@ unsafe impl<'a> SystemParam for &'a Archetypes {
11741175
unsafe fn get_param<'w, 's>(
11751176
_state: &'s mut Self::State,
11761177
_system_meta: &SystemMeta,
1177-
world: &'w World,
1178+
world: UnsafeWorldCell<'w>,
11781179
_change_tick: Tick,
11791180
) -> Self::Item<'w, 's> {
11801181
world.archetypes()
@@ -1195,7 +1196,7 @@ unsafe impl<'a> SystemParam for &'a Components {
11951196
unsafe fn get_param<'w, 's>(
11961197
_state: &'s mut Self::State,
11971198
_system_meta: &SystemMeta,
1198-
world: &'w World,
1199+
world: UnsafeWorldCell<'w>,
11991200
_change_tick: Tick,
12001201
) -> Self::Item<'w, 's> {
12011202
world.components()
@@ -1216,7 +1217,7 @@ unsafe impl<'a> SystemParam for &'a Entities {
12161217
unsafe fn get_param<'w, 's>(
12171218
_state: &'s mut Self::State,
12181219
_system_meta: &SystemMeta,
1219-
world: &'w World,
1220+
world: UnsafeWorldCell<'w>,
12201221
_change_tick: Tick,
12211222
) -> Self::Item<'w, 's> {
12221223
world.entities()
@@ -1237,7 +1238,7 @@ unsafe impl<'a> SystemParam for &'a Bundles {
12371238
unsafe fn get_param<'w, 's>(
12381239
_state: &'s mut Self::State,
12391240
_system_meta: &SystemMeta,
1240-
world: &'w World,
1241+
world: UnsafeWorldCell<'w>,
12411242
_change_tick: Tick,
12421243
) -> Self::Item<'w, 's> {
12431244
world.bundles()
@@ -1286,7 +1287,7 @@ unsafe impl SystemParam for SystemChangeTick {
12861287
unsafe fn get_param<'w, 's>(
12871288
_state: &'s mut Self::State,
12881289
system_meta: &SystemMeta,
1289-
_world: &'w World,
1290+
_world: UnsafeWorldCell<'w>,
12901291
change_tick: Tick,
12911292
) -> Self::Item<'w, 's> {
12921293
SystemChangeTick {
@@ -1356,7 +1357,7 @@ unsafe impl SystemParam for SystemName<'_> {
13561357
unsafe fn get_param<'w, 's>(
13571358
name: &'s mut Self::State,
13581359
_system_meta: &SystemMeta,
1359-
_world: &'w World,
1360+
_world: UnsafeWorldCell<'w>,
13601361
_change_tick: Tick,
13611362
) -> Self::Item<'w, 's> {
13621363
SystemName { name }
@@ -1398,7 +1399,7 @@ macro_rules! impl_system_param_tuple {
13981399
unsafe fn get_param<'w, 's>(
13991400
state: &'s mut Self::State,
14001401
_system_meta: &SystemMeta,
1401-
_world: &'w World,
1402+
_world: UnsafeWorldCell<'w>,
14021403
_change_tick: Tick,
14031404
) -> Self::Item<'w, 's> {
14041405

@@ -1521,7 +1522,7 @@ unsafe impl<P: SystemParam + 'static> SystemParam for StaticSystemParam<'_, '_,
15211522
unsafe fn get_param<'world, 'state>(
15221523
state: &'state mut Self::State,
15231524
system_meta: &SystemMeta,
1524-
world: &'world World,
1525+
world: UnsafeWorldCell<'world>,
15251526
change_tick: Tick,
15261527
) -> Self::Item<'world, 'state> {
15271528
// SAFETY: Defer to the safety of P::SystemParam
@@ -1539,7 +1540,7 @@ unsafe impl<T: ?Sized> SystemParam for PhantomData<T> {
15391540
unsafe fn get_param<'world, 'state>(
15401541
_state: &'state mut Self::State,
15411542
_system_meta: &SystemMeta,
1542-
_world: &'world World,
1543+
_world: UnsafeWorldCell<'world>,
15431544
_change_tick: Tick,
15441545
) -> Self::Item<'world, 'state> {
15451546
PhantomData

crates/bevy_ecs/src/world/identifier.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ use crate::{
66
};
77
use std::sync::atomic::{AtomicUsize, Ordering};
88

9+
use super::unsafe_world_cell::UnsafeWorldCell;
10+
911
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
1012
// We use usize here because that is the largest `Atomic` we want to require
1113
/// A unique identifier for a [`World`].
@@ -56,10 +58,10 @@ unsafe impl SystemParam for WorldId {
5658
unsafe fn get_param<'world, 'state>(
5759
_: &'state mut Self::State,
5860
_: &crate::system::SystemMeta,
59-
world: &'world super::World,
61+
world: UnsafeWorldCell<'world>,
6062
_: Tick,
6163
) -> Self::Item<'world, 'state> {
62-
world.id
64+
world.world_metadata().id()
6365
}
6466
}
6567

crates/bevy_ecs/src/world/unsafe_world_cell.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ impl<'w> UnsafeWorldCell<'w> {
144144
/// # Safety
145145
/// - must not be used in a way that would conflict with any
146146
/// live exclusive borrows on world data
147-
unsafe fn unsafe_world(self) -> &'w World {
147+
pub(crate) unsafe fn unsafe_world(self) -> &'w World {
148148
// SAFETY:
149149
// - caller ensures that the returned `&World` is not used in a way that would conflict
150150
// with any existing mutable borrows of world data

crates/bevy_render/src/extract_param.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use bevy_ecs::{
33
component::Tick,
44
prelude::*,
55
system::{ReadOnlySystemParam, SystemMeta, SystemParam, SystemParamItem, SystemState},
6+
world::unsafe_world_cell::UnsafeWorldCell,
67
};
78
use std::ops::{Deref, DerefMut};
89

@@ -76,7 +77,7 @@ where
7677
unsafe fn get_param<'w, 's>(
7778
state: &'s mut Self::State,
7879
system_meta: &SystemMeta,
79-
world: &'w World,
80+
world: UnsafeWorldCell<'w>,
8081
change_tick: Tick,
8182
) -> Self::Item<'w, 's> {
8283
// SAFETY:

0 commit comments

Comments
 (0)