bevy_ecs address trivial cases of unsafe_op_in_unsafe_fn (#11861)

# Objective

- Part of #11590
- Fix `unsafe_op_in_unsafe_fn` for trivial cases in bevy_ecs

## Solution

Fix `unsafe_op_in_unsafe_fn` in bevy_ecs for trivial cases, i.e., add an
`unsafe` block when the safety comment already exists or add a comment
like "The invariants are uphold by the caller".

---------

Co-authored-by: James Liu <contact@jamessliu.com>
This commit is contained in:
Tristan Guichaoua 2024-02-22 01:04:38 +01:00 committed by GitHub
parent a491bce680
commit 33c7a2251e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
22 changed files with 459 additions and 263 deletions

View file

@ -134,7 +134,7 @@ impl BundleComponentStatus for AddBundle {
#[inline]
unsafe fn get_status(&self, index: usize) -> ComponentStatus {
// SAFETY: caller has ensured index is a valid bundle index for this bundle
*self.bundle_status.get_unchecked(index)
unsafe { *self.bundle_status.get_unchecked(index) }
}
}

View file

@ -192,8 +192,9 @@ unsafe impl<C: Component> Bundle for C {
F: for<'a> FnMut(&'a mut T) -> OwningPtr<'a>,
Self: Sized,
{
let ptr = func(ctx);
// Safety: The id given in `component_ids` is for `Self`
func(ctx).read()
unsafe { ptr.read() }
}
}
@ -224,9 +225,10 @@ macro_rules! tuple_impl {
where
F: FnMut(&mut T) -> OwningPtr<'_>
{
// Rust guarantees that tuple calls are evaluated 'left to right'.
#[allow(unused_unsafe)]
// SAFETY: Rust guarantees that tuple calls are evaluated 'left to right'.
// https://doc.rust-lang.org/reference/expressions.html#evaluation-order-of-operands
($(<$name as Bundle>::from_components(ctx, func),)*)
unsafe { ($(<$name as Bundle>::from_components(ctx, func),)*) }
}
}
@ -466,7 +468,8 @@ impl BundleInfo {
// the target table contains the component.
unsafe { table.get_column_mut(component_id).debug_checked_unwrap() };
// SAFETY: bundle_component is a valid index for this bundle
match bundle_component_status.get_status(bundle_component) {
let status = unsafe { bundle_component_status.get_status(bundle_component) };
match status {
ComponentStatus::Added => {
column.initialize(table_row, component_ptr, change_tick);
}
@ -702,9 +705,11 @@ impl<'a, 'b> BundleInserter<'a, 'b> {
new_archetype
} else {
// SAFETY: the only two borrowed archetypes are above and we just did collision checks
&mut *self
.archetypes_ptr
.add(swapped_location.archetype_id.index())
unsafe {
&mut *self
.archetypes_ptr
.add(swapped_location.archetype_id.index())
}
};
self.entities.set(
@ -787,7 +792,9 @@ impl<'a, 'b> BundleSpawner<'a, 'b> {
pub unsafe fn spawn<T: Bundle>(&mut self, bundle: T) -> Entity {
let entity = self.entities.alloc();
// SAFETY: entity is allocated (but non-existent), `T` matches this BundleInfo's type
self.spawn_non_existent(entity, bundle);
unsafe {
self.spawn_non_existent(entity, bundle);
}
entity
}
}

View file

@ -427,8 +427,10 @@ impl<'w> Ticks<'w> {
this_run: Tick,
) -> Self {
Self {
added: cells.added.deref(),
changed: cells.changed.deref(),
// SAFETY: Caller ensures there is no mutable access to the cell.
added: unsafe { cells.added.deref() },
// SAFETY: Caller ensures there is no mutable access to the cell.
changed: unsafe { cells.changed.deref() },
last_run,
this_run,
}
@ -452,8 +454,10 @@ impl<'w> TicksMut<'w> {
this_run: Tick,
) -> Self {
Self {
added: cells.added.deref_mut(),
changed: cells.changed.deref_mut(),
// SAFETY: Caller ensures there is no alias to the cell.
added: unsafe { cells.added.deref_mut() },
// SAFETY: Caller ensures there is no alias to the cell.
changed: unsafe { cells.changed.deref_mut() },
last_run,
this_run,
}
@ -879,7 +883,8 @@ impl<'w> MutUntyped<'w> {
/// - `T` must be the erased pointee type for this [`MutUntyped`].
pub unsafe fn with_type<T>(self) -> Mut<'w, T> {
Mut {
value: self.value.deref_mut(),
// SAFETY: `value` is `Aligned` and caller ensures the pointee type is `T`.
value: unsafe { self.value.deref_mut() },
ticks: self.ticks,
}
}

View file

@ -357,9 +357,14 @@ impl std::fmt::Debug for ComponentDescriptor {
}
impl ComponentDescriptor {
// SAFETY: The pointer points to a valid value of type `T` and it is safe to drop this value.
/// # SAFETY
///
/// `x` must points to a valid value of type `T`.
unsafe fn drop_ptr<T>(x: OwningPtr<'_>) {
x.drop_as::<T>();
// SAFETY: Contract is required to be upheld by the caller.
unsafe {
x.drop_as::<T>();
}
}
/// Create a new `ComponentDescriptor` for the type `T`.
@ -542,7 +547,8 @@ impl Components {
#[inline]
pub unsafe fn get_info_unchecked(&self, id: ComponentId) -> &ComponentInfo {
debug_assert!(id.index() < self.components.len());
self.components.get_unchecked(id.0)
// SAFETY: The caller ensures `id` is valid.
unsafe { self.components.get_unchecked(id.0) }
}
/// Type-erased equivalent of [`Components::component_id()`].
@ -763,8 +769,10 @@ impl<'a> TickCells<'a> {
#[inline]
pub(crate) unsafe fn read(&self) -> ComponentTicks {
ComponentTicks {
added: self.added.read(),
changed: self.changed.read(),
// SAFETY: The callers uphold the invariants for `read`.
added: unsafe { self.added.read() },
// SAFETY: The callers uphold the invariants for `read`.
changed: unsafe { self.changed.read() },
}
}
}

View file

@ -741,7 +741,8 @@ impl Entities {
#[inline]
pub(crate) unsafe fn set(&mut self, index: u32, location: EntityLocation) {
// SAFETY: Caller guarantees that `index` a valid entity index
self.meta.get_unchecked_mut(index as usize).location = location;
let meta = unsafe { self.meta.get_unchecked_mut(index as usize) };
meta.location = location;
}
/// Increments the `generation` of a freed [`Entity`]. The next entity ID allocated with this
@ -848,9 +849,14 @@ impl Entities {
let free_cursor = self.free_cursor.get_mut();
*free_cursor = 0;
self.meta.reserve(count);
// the EntityMeta struct only contains integers, and it is valid to have all bytes set to u8::MAX
self.meta.as_mut_ptr().write_bytes(u8::MAX, count);
self.meta.set_len(count);
// SAFETY: The EntityMeta struct only contains integers, and it is valid to have all bytes set to u8::MAX
unsafe {
self.meta.as_mut_ptr().write_bytes(u8::MAX, count);
}
// SAFETY: We have reserved `count` elements above and we have initialized values from index 0 to `count`.
unsafe {
self.meta.set_len(count);
}
self.len = count as u32;
}

View file

@ -389,9 +389,9 @@ unsafe impl<'a> WorldQuery for EntityRef<'a> {
_table_row: TableRow,
) -> Self::Item<'w> {
// SAFETY: `fetch` must be called with an entity that exists in the world
let cell = world.get_entity(entity).debug_checked_unwrap();
let cell = unsafe { world.get_entity(entity).debug_checked_unwrap() };
// SAFETY: Read-only access to every component has been registered.
EntityRef::new(cell)
unsafe { EntityRef::new(cell) }
}
fn update_component_access(_state: &Self::State, access: &mut FilteredAccess<ComponentId>) {
@ -465,9 +465,9 @@ unsafe impl<'a> WorldQuery for EntityMut<'a> {
_table_row: TableRow,
) -> Self::Item<'w> {
// SAFETY: `fetch` must be called with an entity that exists in the world
let cell = world.get_entity(entity).debug_checked_unwrap();
let cell = unsafe { world.get_entity(entity).debug_checked_unwrap() };
// SAFETY: mutable access to every component has been registered.
EntityMut::new(cell)
unsafe { EntityMut::new(cell) }
}
fn update_component_access(_state: &Self::State, access: &mut FilteredAccess<ComponentId>) {
@ -560,9 +560,9 @@ unsafe impl<'a> WorldQuery for FilteredEntityRef<'a> {
_table_row: TableRow,
) -> Self::Item<'w> {
// SAFETY: `fetch` must be called with an entity that exists in the world
let cell = world.get_entity(entity).debug_checked_unwrap();
let cell = unsafe { world.get_entity(entity).debug_checked_unwrap() };
// SAFETY: mutable access to every component has been registered.
FilteredEntityRef::new(cell, access.clone())
unsafe { FilteredEntityRef::new(cell, access.clone()) }
}
fn update_component_access(
@ -672,9 +672,9 @@ unsafe impl<'a> WorldQuery for FilteredEntityMut<'a> {
_table_row: TableRow,
) -> Self::Item<'w> {
// SAFETY: `fetch` must be called with an entity that exists in the world
let cell = world.get_entity(entity).debug_checked_unwrap();
let cell = unsafe { world.get_entity(entity).debug_checked_unwrap() };
// SAFETY: mutable access to every component has been registered.
FilteredEntityMut::new(cell, access.clone())
unsafe { FilteredEntityMut::new(cell, access.clone()) }
}
fn update_component_access(
@ -748,15 +748,17 @@ unsafe impl<T: Component> WorldQuery for &T {
ReadFetch {
table_components: None,
sparse_set: (T::Storage::STORAGE_TYPE == StorageType::SparseSet).then(|| {
world
// SAFETY: The underlying type associated with `component_id` is `T`,
// which we are allowed to access since we registered it in `update_archetype_component_access`.
// Note that we do not actually access any components in this function, we just get a shared
// reference to the sparse set, which is used to access the components in `Self::fetch`.
.storages()
.sparse_sets
.get(component_id)
.debug_checked_unwrap()
// SAFETY: The underlying type associated with `component_id` is `T`,
// which we are allowed to access since we registered it in `update_archetype_component_access`.
// Note that we do not actually access any components in this function, we just get a shared
// reference to the sparse set, which is used to access the components in `Self::fetch`.
unsafe {
world
.storages()
.sparse_sets
.get(component_id)
.debug_checked_unwrap()
}
}),
}
}
@ -776,7 +778,10 @@ unsafe impl<T: Component> WorldQuery for &T {
table: &'w Table,
) {
if Self::IS_DENSE {
Self::set_table(fetch, component_id, table);
// SAFETY: `set_archetype`'s safety rules are a super set of the `set_table`'s ones.
unsafe {
Self::set_table(fetch, component_id, table);
}
}
}
@ -802,17 +807,20 @@ unsafe impl<T: Component> WorldQuery for &T {
table_row: TableRow,
) -> Self::Item<'w> {
match T::Storage::STORAGE_TYPE {
StorageType::Table => fetch
.table_components
.debug_checked_unwrap()
.get(table_row.as_usize())
.deref(),
StorageType::SparseSet => fetch
.sparse_set
.debug_checked_unwrap()
.get(entity)
.debug_checked_unwrap()
.deref(),
StorageType::Table => {
// SAFETY: STORAGE_TYPE = Table
let table = unsafe { fetch.table_components.debug_checked_unwrap() };
// SAFETY: Caller ensures `table_row` is in range.
let item = unsafe { table.get(table_row.as_usize()) };
item.deref()
}
StorageType::SparseSet => {
// SAFETY: STORAGE_TYPE = SparseSet
let sparse_set = unsafe { fetch.sparse_set.debug_checked_unwrap() };
// SAFETY: Caller ensures `entity` is in range.
let item = unsafe { sparse_set.get(entity).debug_checked_unwrap() };
item.deref()
}
}
}
@ -898,12 +906,17 @@ unsafe impl<'__w, T: Component> WorldQuery for Ref<'__w, T> {
RefFetch {
table_data: None,
sparse_set: (T::Storage::STORAGE_TYPE == StorageType::SparseSet).then(|| {
world
// SAFETY: See &T::init_fetch.
.storages()
.sparse_sets
.get(component_id)
.debug_checked_unwrap()
// SAFETY: The underlying type associated with `component_id` is `T`,
// which we are allowed to access since we registered it in `update_archetype_component_access`.
// Note that we do not actually access any components in this function, we just get a shared
// reference to the sparse set, which is used to access the components in `Self::fetch`.
unsafe {
world
.storages()
.sparse_sets
.get(component_id)
.debug_checked_unwrap()
}
}),
last_run,
this_run,
@ -925,7 +938,10 @@ unsafe impl<'__w, T: Component> WorldQuery for Ref<'__w, T> {
table: &'w Table,
) {
if Self::IS_DENSE {
Self::set_table(fetch, component_id, table);
// SAFETY: `set_archetype`'s safety rules are a super set of the `set_table`'s ones.
unsafe {
Self::set_table(fetch, component_id, table);
}
}
}
@ -951,24 +967,38 @@ unsafe impl<'__w, T: Component> WorldQuery for Ref<'__w, T> {
) -> Self::Item<'w> {
match T::Storage::STORAGE_TYPE {
StorageType::Table => {
// SAFETY: STORAGE_TYPE = Table
let (table_components, added_ticks, changed_ticks) =
fetch.table_data.debug_checked_unwrap();
unsafe { fetch.table_data.debug_checked_unwrap() };
// SAFETY: The caller ensures `table_row` is in range.
let component = unsafe { table_components.get(table_row.as_usize()) };
// SAFETY: The caller ensures `table_row` is in range.
let added = unsafe { added_ticks.get(table_row.as_usize()) };
// SAFETY: The caller ensures `table_row` is in range.
let changed = unsafe { changed_ticks.get(table_row.as_usize()) };
Ref {
value: table_components.get(table_row.as_usize()).deref(),
value: component.deref(),
ticks: Ticks {
added: added_ticks.get(table_row.as_usize()).deref(),
changed: changed_ticks.get(table_row.as_usize()).deref(),
added: added.deref(),
changed: changed.deref(),
this_run: fetch.this_run,
last_run: fetch.last_run,
},
}
}
StorageType::SparseSet => {
let (component, ticks) = fetch
.sparse_set
.debug_checked_unwrap()
.get_with_ticks(entity)
.debug_checked_unwrap();
// SAFETY: STORAGE_TYPE = SparseSet
let component_sparse_set = unsafe { fetch.sparse_set.debug_checked_unwrap() };
// SAFETY: The caller ensures `entity` is in range.
let (component, ticks) = unsafe {
component_sparse_set
.get_with_ticks(entity)
.debug_checked_unwrap()
};
Ref {
value: component.deref(),
ticks: Ticks::from_tick_cells(ticks, fetch.last_run, fetch.this_run),
@ -1059,12 +1089,17 @@ unsafe impl<'__w, T: Component> WorldQuery for &'__w mut T {
WriteFetch {
table_data: None,
sparse_set: (T::Storage::STORAGE_TYPE == StorageType::SparseSet).then(|| {
world
// SAFETY: See &T::init_fetch.
.storages()
.sparse_sets
.get(component_id)
.debug_checked_unwrap()
// SAFETY: The underlying type associated with `component_id` is `T`,
// which we are allowed to access since we registered it in `update_archetype_component_access`.
// Note that we do not actually access any components in this function, we just get a shared
// reference to the sparse set, which is used to access the components in `Self::fetch`.
unsafe {
world
.storages()
.sparse_sets
.get(component_id)
.debug_checked_unwrap()
}
}),
last_run,
this_run,
@ -1086,7 +1121,10 @@ unsafe impl<'__w, T: Component> WorldQuery for &'__w mut T {
table: &'w Table,
) {
if Self::IS_DENSE {
Self::set_table(fetch, component_id, table);
// SAFETY: `set_archetype`'s safety rules are a super set of the `set_table`'s ones.
unsafe {
Self::set_table(fetch, component_id, table);
}
}
}
@ -1112,24 +1150,38 @@ unsafe impl<'__w, T: Component> WorldQuery for &'__w mut T {
) -> Self::Item<'w> {
match T::Storage::STORAGE_TYPE {
StorageType::Table => {
// SAFETY: STORAGE_TYPE = Table
let (table_components, added_ticks, changed_ticks) =
fetch.table_data.debug_checked_unwrap();
unsafe { fetch.table_data.debug_checked_unwrap() };
// SAFETY: The caller ensures `table_row` is in range.
let component = unsafe { table_components.get(table_row.as_usize()) };
// SAFETY: The caller ensures `table_row` is in range.
let added = unsafe { added_ticks.get(table_row.as_usize()) };
// SAFETY: The caller ensures `table_row` is in range.
let changed = unsafe { changed_ticks.get(table_row.as_usize()) };
Mut {
value: table_components.get(table_row.as_usize()).deref_mut(),
value: component.deref_mut(),
ticks: TicksMut {
added: added_ticks.get(table_row.as_usize()).deref_mut(),
changed: changed_ticks.get(table_row.as_usize()).deref_mut(),
added: added.deref_mut(),
changed: changed.deref_mut(),
this_run: fetch.this_run,
last_run: fetch.last_run,
},
}
}
StorageType::SparseSet => {
let (component, ticks) = fetch
.sparse_set
.debug_checked_unwrap()
.get_with_ticks(entity)
.debug_checked_unwrap();
// SAFETY: STORAGE_TYPE = SparseSet
let component_sparse_set = unsafe { fetch.sparse_set.debug_checked_unwrap() };
// SAFETY: The caller ensures `entity` is in range.
let (component, ticks) = unsafe {
component_sparse_set
.get_with_ticks(entity)
.debug_checked_unwrap()
};
Mut {
value: component.assert_unique().deref_mut(),
ticks: TicksMut::from_tick_cells(ticks, fetch.last_run, fetch.this_run),
@ -1207,7 +1259,8 @@ unsafe impl<T: WorldQuery> WorldQuery for Option<T> {
this_run: Tick,
) -> OptionFetch<'w, T> {
OptionFetch {
fetch: T::init_fetch(world, state, last_run, this_run),
// SAFETY: The invariants are uphold by the caller.
fetch: unsafe { T::init_fetch(world, state, last_run, this_run) },
matches: false,
}
}
@ -1223,7 +1276,10 @@ unsafe impl<T: WorldQuery> WorldQuery for Option<T> {
) {
fetch.matches = T::matches_component_set(state, &|id| archetype.contains(id));
if fetch.matches {
T::set_archetype(&mut fetch.fetch, state, archetype, table);
// SAFETY: The invariants are uphold by the caller.
unsafe {
T::set_archetype(&mut fetch.fetch, state, archetype, table);
}
}
}
@ -1231,7 +1287,10 @@ unsafe impl<T: WorldQuery> WorldQuery for Option<T> {
unsafe fn set_table<'w>(fetch: &mut OptionFetch<'w, T>, state: &T::State, table: &'w Table) {
fetch.matches = T::matches_component_set(state, &|id| table.has_column(id));
if fetch.matches {
T::set_table(&mut fetch.fetch, state, table);
// SAFETY: The invariants are uphold by the caller.
unsafe {
T::set_table(&mut fetch.fetch, state, table);
}
}
}
@ -1243,7 +1302,8 @@ unsafe impl<T: WorldQuery> WorldQuery for Option<T> {
) -> Self::Item<'w> {
fetch
.matches
.then(|| T::fetch(&mut fetch.fetch, entity, table_row))
// SAFETY: The invariants are uphold by the caller.
.then(|| unsafe { T::fetch(&mut fetch.fetch, entity, table_row) })
}
fn update_component_access(state: &T::State, access: &mut FilteredAccess<ComponentId>) {
@ -1484,7 +1544,8 @@ macro_rules! impl_anytuple_fetch {
#[allow(clippy::unused_unit)]
unsafe fn init_fetch<'w>(_world: UnsafeWorldCell<'w>, state: &Self::State, _last_run: Tick, _this_run: Tick) -> Self::Fetch<'w> {
let ($($name,)*) = state;
($(($name::init_fetch(_world, $name, _last_run, _this_run), false),)*)
// SAFETY: The invariants are uphold by the caller.
($(( unsafe { $name::init_fetch(_world, $name, _last_run, _this_run) }, false),)*)
}
const IS_DENSE: bool = true $(&& $name::IS_DENSE)*;
@ -1501,7 +1562,8 @@ macro_rules! impl_anytuple_fetch {
$(
$name.1 = $name::matches_component_set($state, &|id| _archetype.contains(id));
if $name.1 {
$name::set_archetype(&mut $name.0, $state, _archetype, _table);
// SAFETY: The invariants are uphold by the caller.
unsafe { $name::set_archetype(&mut $name.0, $state, _archetype, _table); }
}
)*
}
@ -1513,7 +1575,8 @@ macro_rules! impl_anytuple_fetch {
$(
$name.1 = $name::matches_component_set($state, &|id| _table.has_column(id));
if $name.1 {
$name::set_table(&mut $name.0, $state, _table);
// SAFETY: The invariants are required to be upheld by the caller.
unsafe { $name::set_table(&mut $name.0, $state, _table); }
}
)*
}
@ -1527,7 +1590,8 @@ macro_rules! impl_anytuple_fetch {
) -> Self::Item<'w> {
let ($($name,)*) = _fetch;
($(
$name.1.then(|| $name::fetch(&mut $name.0, _entity, _table_row)),
// SAFETY: The invariants are required to be upheld by the caller.
$name.1.then(|| unsafe { $name::fetch(&mut $name.0, _entity, _table_row) }),
)*)
}

View file

@ -382,7 +382,8 @@ macro_rules! impl_query_filter_tuple {
unsafe fn init_fetch<'w>(world: UnsafeWorldCell<'w>, state: &Self::State, last_run: Tick, this_run: Tick) -> Self::Fetch<'w> {
let ($($filter,)*) = state;
($(OrFetch {
fetch: $filter::init_fetch(world, $filter, last_run, this_run),
// SAFETY: The invariants are uphold by the caller.
fetch: unsafe { $filter::init_fetch(world, $filter, last_run, this_run) },
matches: false,
},)*)
}
@ -394,7 +395,8 @@ macro_rules! impl_query_filter_tuple {
$(
$filter.matches = $filter::matches_component_set($state, &|id| table.has_column(id));
if $filter.matches {
$filter::set_table(&mut $filter.fetch, $state, table);
// SAFETY: The invariants are uphold by the caller.
unsafe { $filter::set_table(&mut $filter.fetch, $state, table); }
}
)*
}
@ -411,7 +413,8 @@ macro_rules! impl_query_filter_tuple {
$(
$filter.matches = $filter::matches_component_set($state, &|id| archetype.contains(id));
if $filter.matches {
$filter::set_archetype(&mut $filter.fetch, $state, archetype, table);
// SAFETY: The invariants are uphold by the caller.
unsafe { $filter::set_archetype(&mut $filter.fetch, $state, archetype, table); }
}
)*
}
@ -423,7 +426,8 @@ macro_rules! impl_query_filter_tuple {
_table_row: TableRow
) -> Self::Item<'w> {
let ($($filter,)*) = fetch;
false $(|| ($filter.matches && $filter::filter_fetch(&mut $filter.fetch, _entity, _table_row)))*
// SAFETY: The invariants are uphold by the caller.
false $(|| ($filter.matches && unsafe { $filter::filter_fetch(&mut $filter.fetch, _entity, _table_row) }))*
}
fn update_component_access(state: &Self::State, access: &mut FilteredAccess<ComponentId>) {
@ -470,7 +474,8 @@ macro_rules! impl_query_filter_tuple {
entity: Entity,
table_row: TableRow
) -> bool {
Self::fetch(fetch, entity, table_row)
// SAFETY: The invariants are uphold by the caller.
unsafe { Self::fetch(fetch, entity, table_row) }
}
}
};
@ -492,7 +497,8 @@ macro_rules! impl_tuple_query_filter {
_table_row: TableRow
) -> bool {
let ($($name,)*) = fetch;
true $(&& $name::filter_fetch($name, _entity, _table_row))*
// SAFETY: The invariants are uphold by the caller.
true $(&& unsafe { $name::filter_fetch($name, _entity, _table_row) })*
}
}
@ -620,7 +626,10 @@ unsafe impl<T: Component> WorldQuery for Added<T> {
table: &'w Table,
) {
if Self::IS_DENSE {
Self::set_table(fetch, component_id, table);
// SAFETY: `set_archetype`'s safety rules are a super set of the `set_table`'s ones.
unsafe {
Self::set_table(fetch, component_id, table);
}
}
}
@ -643,18 +652,23 @@ unsafe impl<T: Component> WorldQuery for Added<T> {
table_row: TableRow,
) -> Self::Item<'w> {
match T::Storage::STORAGE_TYPE {
StorageType::Table => fetch
.table_ticks
.debug_checked_unwrap()
.get(table_row.as_usize())
.deref()
.is_newer_than(fetch.last_run, fetch.this_run),
StorageType::Table => {
// SAFETY: STORAGE_TYPE = Table
let table = unsafe { fetch.table_ticks.debug_checked_unwrap() };
// SAFETY: The caller ensures `table_row` is in range.
let tick = unsafe { table.get(table_row.as_usize()) };
tick.deref().is_newer_than(fetch.last_run, fetch.this_run)
}
StorageType::SparseSet => {
let sparse_set = &fetch.sparse_set.debug_checked_unwrap();
ComponentSparseSet::get_added_tick(sparse_set, entity)
.debug_checked_unwrap()
.deref()
.is_newer_than(fetch.last_run, fetch.this_run)
// SAFETY: STORAGE_TYPE = SparseSet
let sparse_set = unsafe { &fetch.sparse_set.debug_checked_unwrap() };
// SAFETY: The caller ensures `entity` is in range.
let tick = unsafe {
ComponentSparseSet::get_added_tick(sparse_set, entity).debug_checked_unwrap()
};
tick.deref().is_newer_than(fetch.last_run, fetch.this_run)
}
}
}
@ -691,7 +705,8 @@ impl<T: Component> QueryFilter for Added<T> {
entity: Entity,
table_row: TableRow,
) -> bool {
Self::fetch(fetch, entity, table_row)
// SAFETY: The invariants are uphold by the caller.
unsafe { Self::fetch(fetch, entity, table_row) }
}
}
@ -820,7 +835,10 @@ unsafe impl<T: Component> WorldQuery for Changed<T> {
table: &'w Table,
) {
if Self::IS_DENSE {
Self::set_table(fetch, component_id, table);
// SAFETY: `set_archetype`'s safety rules are a super set of the `set_table`'s ones.
unsafe {
Self::set_table(fetch, component_id, table);
}
}
}
@ -843,18 +861,23 @@ unsafe impl<T: Component> WorldQuery for Changed<T> {
table_row: TableRow,
) -> Self::Item<'w> {
match T::Storage::STORAGE_TYPE {
StorageType::Table => fetch
.table_ticks
.debug_checked_unwrap()
.get(table_row.as_usize())
.deref()
.is_newer_than(fetch.last_run, fetch.this_run),
StorageType::Table => {
// SAFETY: STORAGE_TYPE = Table
let table = unsafe { fetch.table_ticks.debug_checked_unwrap() };
// SAFETY: The caller ensures `table_row` is in range.
let tick = unsafe { table.get(table_row.as_usize()) };
tick.deref().is_newer_than(fetch.last_run, fetch.this_run)
}
StorageType::SparseSet => {
let sparse_set = &fetch.sparse_set.debug_checked_unwrap();
ComponentSparseSet::get_changed_tick(sparse_set, entity)
.debug_checked_unwrap()
.deref()
.is_newer_than(fetch.last_run, fetch.this_run)
// SAFETY: STORAGE_TYPE = SparseSet
let sparse_set = unsafe { &fetch.sparse_set.debug_checked_unwrap() };
// SAFETY: The caller ensures `entity` is in range.
let tick = unsafe {
ComponentSparseSet::get_changed_tick(sparse_set, entity).debug_checked_unwrap()
};
tick.deref().is_newer_than(fetch.last_run, fetch.this_run)
}
}
}
@ -892,7 +915,8 @@ impl<T: Component> QueryFilter for Changed<T> {
entity: Entity,
table_row: TableRow,
) -> bool {
Self::fetch(fetch, entity, table_row)
// SAFETY: The invariants are uphold by the caller.
unsafe { Self::fetch(fetch, entity, table_row) }
}
}

View file

@ -34,9 +34,10 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIter<'w, 's, D, F> {
QueryIter {
query_state,
// SAFETY: We only access table data that has been registered in `query_state`.
tables: &world.storages().tables,
tables: unsafe { &world.storages().tables },
archetypes: world.archetypes(),
cursor: QueryIterationCursor::init(world, query_state, last_run, this_run),
// SAFETY: The invariants are uphold by the caller.
cursor: unsafe { QueryIterationCursor::init(world, query_state, last_run, this_run) },
}
}
@ -121,11 +122,13 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIter<'w, 's, D, F> {
let entities = table.entities();
for row in rows {
// SAFETY: Caller assures `row` in range of the current archetype.
let entity = entities.get_unchecked(row);
let entity = unsafe { entities.get_unchecked(row) };
let row = TableRow::from_usize(row);
// SAFETY: set_table was called prior.
// Caller assures `row` in range of the current archetype.
if !F::filter_fetch(&mut self.cursor.filter, *entity, row) {
let fetched = unsafe { !F::filter_fetch(&mut self.cursor.filter, *entity, row) };
if fetched {
continue;
}
@ -173,24 +176,30 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIter<'w, 's, D, F> {
let entities = archetype.entities();
for index in indices {
// SAFETY: Caller assures `index` in range of the current archetype.
let archetype_entity = entities.get_unchecked(index);
let archetype_entity = unsafe { entities.get_unchecked(index) };
// SAFETY: set_archetype was called prior.
// Caller assures `index` in range of the current archetype.
if !F::filter_fetch(
&mut self.cursor.filter,
archetype_entity.id(),
archetype_entity.table_row(),
) {
let fetched = unsafe {
!F::filter_fetch(
&mut self.cursor.filter,
archetype_entity.id(),
archetype_entity.table_row(),
)
};
if fetched {
continue;
}
// SAFETY: set_archetype was called prior, `index` is an archetype index in range of the current archetype
// Caller assures `index` in range of the current archetype.
let item = D::fetch(
&mut self.cursor.fetch,
archetype_entity.id(),
archetype_entity.table_row(),
);
let item = unsafe {
D::fetch(
&mut self.cursor.fetch,
archetype_entity.id(),
archetype_entity.table_row(),
)
};
accum = func(accum, item);
}
@ -339,28 +348,32 @@ where
// SAFETY: `archetype` is from the world that `fetch/filter` were created for,
// `fetch_state`/`filter_state` are the states that `fetch/filter` were initialized with
D::set_archetype(
&mut self.fetch,
&self.query_state.fetch_state,
archetype,
table,
);
unsafe {
D::set_archetype(
&mut self.fetch,
&self.query_state.fetch_state,
archetype,
table,
);
}
// SAFETY: `table` is from the world that `fetch/filter` were created for,
// `fetch_state`/`filter_state` are the states that `fetch/filter` were initialized with
F::set_archetype(
&mut self.filter,
&self.query_state.filter_state,
archetype,
table,
);
unsafe {
F::set_archetype(
&mut self.filter,
&self.query_state.filter_state,
archetype,
table,
);
}
// SAFETY: set_archetype was called prior.
// `location.archetype_row` is an archetype index row in range of the current archetype, because if it was not, the match above would have `continue`d
if F::filter_fetch(&mut self.filter, entity, location.table_row) {
if unsafe { F::filter_fetch(&mut self.filter, entity, location.table_row) } {
// SAFETY:
// - set_archetype was called prior, `location.archetype_row` is an archetype index in range of the current archetype
// - fetch is only called once for each entity.
return Some(D::fetch(&mut self.fetch, entity, location.table_row));
return Some(unsafe { D::fetch(&mut self.fetch, entity, location.table_row) });
}
}
None
@ -510,7 +523,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter, const K: usize> QueryCombinationIter<
QueryCombinationIter {
query_state,
// SAFETY: We only access table data that has been registered in `query_state`.
tables: &world.storages().tables,
tables: unsafe { &world.storages().tables },
archetypes: world.archetypes(),
cursors: array.assume_init(),
}
@ -764,8 +777,10 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIterationCursor<'w, 's, D, F> {
let table = tables.get(*table_id).debug_checked_unwrap();
// SAFETY: `table` is from the world that `fetch/filter` were created for,
// `fetch_state`/`filter_state` are the states that `fetch/filter` were initialized with
D::set_table(&mut self.fetch, &query_state.fetch_state, table);
F::set_table(&mut self.filter, &query_state.filter_state, table);
unsafe {
D::set_table(&mut self.fetch, &query_state.fetch_state, table);
F::set_table(&mut self.filter, &query_state.filter_state, table);
}
self.table_entities = table.entities();
self.current_len = table.entity_count();
self.current_row = 0;
@ -774,7 +789,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIterationCursor<'w, 's, D, F> {
// SAFETY: set_table was called prior.
// `current_row` is a table row in range of the current table, because if it was not, then the if above would have been executed.
let entity = self.table_entities.get_unchecked(self.current_row);
let entity = unsafe { self.table_entities.get_unchecked(self.current_row) };
let row = TableRow::from_usize(self.current_row);
if !F::filter_fetch(&mut self.filter, *entity, row) {
self.current_row += 1;
@ -786,7 +801,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIterationCursor<'w, 's, D, F> {
// - `current_row` must be a table row in range of the current table,
// because if it was not, then the if above would have been executed.
// - fetch is only called once for each `entity`.
let item = D::fetch(&mut self.fetch, *entity, row);
let item = unsafe { D::fetch(&mut self.fetch, *entity, row) };
self.current_row += 1;
return Some(item);
@ -799,13 +814,20 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIterationCursor<'w, 's, D, F> {
let table = tables.get(archetype.table_id()).debug_checked_unwrap();
// SAFETY: `archetype` and `tables` are from the world that `fetch/filter` were created for,
// `fetch_state`/`filter_state` are the states that `fetch/filter` were initialized with
D::set_archetype(&mut self.fetch, &query_state.fetch_state, archetype, table);
F::set_archetype(
&mut self.filter,
&query_state.filter_state,
archetype,
table,
);
unsafe {
D::set_archetype(
&mut self.fetch,
&query_state.fetch_state,
archetype,
table,
);
F::set_archetype(
&mut self.filter,
&query_state.filter_state,
archetype,
table,
);
}
self.archetype_entities = archetype.entities();
self.current_len = archetype.len();
self.current_row = 0;
@ -814,7 +836,8 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIterationCursor<'w, 's, D, F> {
// SAFETY: set_archetype was called prior.
// `current_row` is an archetype index row in range of the current archetype, because if it was not, then the if above would have been executed.
let archetype_entity = self.archetype_entities.get_unchecked(self.current_row);
let archetype_entity =
unsafe { self.archetype_entities.get_unchecked(self.current_row) };
if !F::filter_fetch(
&mut self.filter,
archetype_entity.id(),
@ -829,11 +852,13 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIterationCursor<'w, 's, D, F> {
// - `current_row` must be an archetype index row in range of the current archetype,
// because if it was not, then the if above would have been executed.
// - fetch is only called once for each `archetype_entity`.
let item = D::fetch(
&mut self.fetch,
archetype_entity.id(),
archetype_entity.table_row(),
);
let item = unsafe {
D::fetch(
&mut self.fetch,
archetype_entity.id(),
archetype_entity.table_row(),
)
};
self.current_row += 1;
return Some(item);
}

View file

@ -664,16 +664,16 @@ impl<D: QueryData, F: QueryFilter> QueryState<D, F> {
let mut values = [(); N].map(|_| MaybeUninit::uninit());
for (value, entity) in std::iter::zip(&mut values, entities) {
// SAFETY: fetch is read-only
// and world must be validated
let item = self
.as_readonly()
.get_unchecked_manual(world, entity, last_run, this_run)?;
// SAFETY: fetch is read-only and world must be validated
let item = unsafe {
self.as_readonly()
.get_unchecked_manual(world, entity, last_run, this_run)?
};
*value = MaybeUninit::new(item);
}
// SAFETY: Each value has been fully initialized.
Ok(values.map(|x| x.assume_init()))
Ok(values.map(|x| unsafe { x.assume_init() }))
}
/// Gets the query results for the given [`World`] and array of [`Entity`], where the last change and
@ -1134,7 +1134,7 @@ impl<D: QueryData, F: QueryFilter> QueryState<D, F> {
bevy_tasks::ComputeTaskPool::get().scope(|scope| {
if D::IS_DENSE && F::IS_DENSE {
// SAFETY: We only access table data that has been registered in `self.archetype_component_access`.
let tables = &world.storages().tables;
let tables = unsafe { &world.storages().tables };
for table_id in &self.matched_table_ids {
let table = &tables[*table_id];
if table.is_empty() {

View file

@ -164,7 +164,8 @@ macro_rules! impl_tuple_world_query {
#[allow(clippy::unused_unit)]
unsafe fn init_fetch<'w>(_world: UnsafeWorldCell<'w>, state: &Self::State, _last_run: Tick, _this_run: Tick) -> Self::Fetch<'w> {
let ($($name,)*) = state;
($($name::init_fetch(_world, $name, _last_run, _this_run),)*)
// SAFETY: The invariants are uphold by the caller.
($(unsafe { $name::init_fetch(_world, $name, _last_run, _this_run) },)*)
}
const IS_DENSE: bool = true $(&& $name::IS_DENSE)*;
@ -178,14 +179,16 @@ macro_rules! impl_tuple_world_query {
) {
let ($($name,)*) = _fetch;
let ($($state,)*) = _state;
$($name::set_archetype($name, $state, _archetype, _table);)*
// SAFETY: The invariants are uphold by the caller.
$(unsafe { $name::set_archetype($name, $state, _archetype, _table); })*
}
#[inline]
unsafe fn set_table<'w>(_fetch: &mut Self::Fetch<'w>, _state: &Self::State, _table: &'w Table) {
let ($($name,)*) = _fetch;
let ($($state,)*) = _state;
$($name::set_table($name, $state, _table);)*
// SAFETY: The invariants are uphold by the caller.
$(unsafe { $name::set_table($name, $state, _table); })*
}
#[inline(always)]
@ -196,7 +199,8 @@ macro_rules! impl_tuple_world_query {
_table_row: TableRow
) -> Self::Item<'w> {
let ($($name,)*) = _fetch;
($($name::fetch($name, _entity, _table_row),)*)
// SAFETY: The invariants are uphold by the caller.
($(unsafe { $name::fetch($name, _entity, _table_row) },)*)
}
fn update_component_access(state: &Self::State, _access: &mut FilteredAccess<ComponentId>) {

View file

@ -194,7 +194,7 @@ impl ReflectComponent {
entity: UnsafeEntityCell<'a>,
) -> Option<Mut<'a, dyn Reflect>> {
// SAFETY: safety requirements deferred to caller
(self.0.reflect_unchecked_mut)(entity)
unsafe { (self.0.reflect_unchecked_mut)(entity) }
}
/// Gets the value of this [`Component`] type from entity from `source_world` and [applies](Self::apply()) it to the value of this [`Component`] type in entity in `destination_world`.

View file

@ -116,7 +116,7 @@ impl ReflectResource {
world: UnsafeWorldCell<'w>,
) -> Option<Mut<'w, dyn Reflect>> {
// SAFETY: caller promises to uphold uniqueness guarantees
(self.0.reflect_unchecked_mut)(world)
unsafe { (self.0.reflect_unchecked_mut)(world) }
}
/// Gets the value of this [`Resource`] type from `source_world` and [applies](Self::apply()) it to the value of this [`Resource`] type in `destination_world`.

View file

@ -362,7 +362,7 @@ impl MultiThreadedExecutor {
// SAFETY: `can_run` returned true, which means that:
// - It must have called `update_archetype_component_access` for each run condition.
// - There can be no systems running whose accesses would conflict with any conditions.
if !self.should_run(system_index, system, conditions, world_cell) {
if unsafe { !self.should_run(system_index, system, conditions, world_cell) } {
self.skip_system_and_signal_dependents(system_index);
continue;
}
@ -479,8 +479,9 @@ impl MultiThreadedExecutor {
// - The caller ensures that `world` has permission to read any data
// required by the conditions.
// - `update_archetype_component_access` has been called for each run condition.
let set_conditions_met =
evaluate_and_fold_conditions(&mut conditions.set_conditions[set_idx], world);
let set_conditions_met = unsafe {
evaluate_and_fold_conditions(&mut conditions.set_conditions[set_idx], world)
};
if !set_conditions_met {
self.skipped_systems
@ -496,8 +497,9 @@ impl MultiThreadedExecutor {
// - The caller ensures that `world` has permission to read any data
// required by the conditions.
// - `update_archetype_component_access` has been called for each run condition.
let system_conditions_met =
evaluate_and_fold_conditions(&mut conditions.system_conditions[system_index], world);
let system_conditions_met = unsafe {
evaluate_and_fold_conditions(&mut conditions.system_conditions[system_index], world)
};
if !system_conditions_met {
self.skipped_systems.insert(system_index);

View file

@ -208,7 +208,7 @@ impl BlobVec {
// Pointer to the value in the vector that will get replaced.
// SAFETY: The caller ensures that `index` fits in this vector.
let destination = NonNull::from(self.get_unchecked_mut(index));
let destination = NonNull::from(unsafe { self.get_unchecked_mut(index) });
let source = value.as_ptr();
if let Some(drop) = self.drop {
@ -226,7 +226,7 @@ impl BlobVec {
// that the element will not get observed or double dropped later.
// - If a panic occurs, `self.len` will remain `0`, which ensures a double-drop
// does not occur. Instead, all elements will be forgotten.
let old_value = OwningPtr::new(destination);
let old_value = unsafe { OwningPtr::new(destination) };
// This closure will run in case `drop()` panics,
// which ensures that `value` does not get forgotten.
@ -249,7 +249,13 @@ impl BlobVec {
// so it must still be initialized and it is safe to transfer ownership into the vector.
// - `source` and `destination` were obtained from different memory locations,
// both of which we have exclusive access to, so they are guaranteed not to overlap.
std::ptr::copy_nonoverlapping::<u8>(source, destination.as_ptr(), self.item_layout.size());
unsafe {
std::ptr::copy_nonoverlapping::<u8>(
source,
destination.as_ptr(),
self.item_layout.size(),
);
}
}
/// Appends an element to the back of the vector.
@ -304,7 +310,8 @@ impl BlobVec {
// - `new_len` is less than the old len, so it must fit in this vector's allocation.
// - `size` is a multiple of the erased type's alignment,
// so adding a multiple of `size` will preserve alignment.
self.get_ptr_mut().byte_add(new_len * size).promote()
let p = unsafe { self.get_ptr_mut().byte_add(new_len * size) };
p.promote()
}
/// Removes the value at `index` and copies the value stored into `ptr`.
@ -358,7 +365,7 @@ impl BlobVec {
// so this operation will not overflow the original allocation.
// - `size` is a multiple of the erased type's alignment,
// so adding a multiple of `size` will preserve alignment.
self.get_ptr().byte_add(index * size)
unsafe { self.get_ptr().byte_add(index * size) }
}
/// Returns a mutable reference to the element at `index`, without doing bounds checking.
@ -374,7 +381,7 @@ impl BlobVec {
// so this operation will not overflow the original allocation.
// - `size` is a multiple of the erased type's alignment,
// so adding a multiple of `size` will preserve alignment.
self.get_ptr_mut().byte_add(index * size)
unsafe { self.get_ptr_mut().byte_add(index * size) }
}
/// Gets a [`Ptr`] to the start of the vec
@ -397,7 +404,7 @@ impl BlobVec {
/// The type `T` must be the type of the items in this [`BlobVec`].
pub unsafe fn get_slice<T>(&self) -> &[UnsafeCell<T>] {
// SAFETY: the inner data will remain valid for as long as 'self.
std::slice::from_raw_parts(self.data.as_ptr() as *const UnsafeCell<T>, self.len)
unsafe { std::slice::from_raw_parts(self.data.as_ptr() as *const UnsafeCell<T>, self.len) }
}
/// Clears the vector, removing (and dropping) all values.
@ -502,9 +509,11 @@ mod tests {
use super::BlobVec;
use std::{alloc::Layout, cell::RefCell, mem, rc::Rc};
// SAFETY: The pointer points to a valid value of type `T` and it is safe to drop this value.
unsafe fn drop_ptr<T>(x: OwningPtr<'_>) {
x.drop_as::<T>();
// SAFETY: The pointer points to a valid value of type `T` and it is safe to drop this value.
unsafe {
x.drop_as::<T>();
}
}
/// # Safety

View file

@ -154,7 +154,9 @@ impl<const SEND: bool> ResourceData<SEND> {
// SAFETY: The caller ensures that the provided value is valid for the underlying type and
// is properly initialized. We've ensured that a value is already present and previously
// initialized.
self.data.replace_unchecked(Self::ROW, value);
unsafe {
self.data.replace_unchecked(Self::ROW, value);
}
} else {
if !SEND {
self.origin_thread_id = Some(std::thread::current().id());
@ -185,7 +187,9 @@ impl<const SEND: bool> ResourceData<SEND> {
// SAFETY: The caller ensures that the provided value is valid for the underlying type and
// is properly initialized. We've ensured that a value is already present and previously
// initialized.
self.data.replace_unchecked(Self::ROW, value);
unsafe {
self.data.replace_unchecked(Self::ROW, value);
}
} else {
if !SEND {
self.origin_thread_id = Some(std::thread::current().id());

View file

@ -107,8 +107,9 @@ where
#[inline]
unsafe fn run_unsafe(&mut self, input: Self::In, world: UnsafeWorldCell) -> Self::Out {
// SAFETY: `system.run_unsafe` has the same invariants as `self.run_unsafe`.
self.func
.adapt(input, |input| self.system.run_unsafe(input, world))
self.func.adapt(input, |input| unsafe {
self.system.run_unsafe(input, world)
})
}
#[inline]

View file

@ -171,8 +171,9 @@ where
// in parallel, so their world accesses will not conflict with each other.
// Additionally, `update_archetype_component_access` has been called,
// which forwards to the implementations for `self.a` and `self.b`.
|input| self.a.run_unsafe(input, world),
|input| self.b.run_unsafe(input, world),
|input| unsafe { self.a.run_unsafe(input, world) },
// SAFETY: See the comment above.
|input| unsafe { self.b.run_unsafe(input, world) },
)
}

View file

@ -343,7 +343,8 @@ impl<Param: SystemParam> SystemState<Param> {
world: UnsafeWorldCell<'w>,
) -> SystemParamItem<'w, 's, Param> {
let change_tick = world.increment_change_tick();
self.fetch(world, change_tick)
// SAFETY: The invariants are uphold by the caller.
unsafe { self.fetch(world, change_tick) }
}
/// # Safety
@ -356,7 +357,9 @@ impl<Param: SystemParam> SystemState<Param> {
world: UnsafeWorldCell<'w>,
change_tick: Tick,
) -> SystemParamItem<'w, 's, Param> {
let param = Param::get_param(&mut self.param_state, &self.meta, world, change_tick);
// SAFETY: The invariants are uphold by the caller.
let param =
unsafe { Param::get_param(&mut self.param_state, &self.meta, world, change_tick) };
self.meta.last_run = change_tick;
param
}
@ -490,12 +493,14 @@ where
// if the world does not match.
// - All world accesses used by `F::Param` have been registered, so the caller
// will ensure that there are no data access conflicts.
let params = F::Param::get_param(
self.param_state.as_mut().expect(Self::PARAM_MESSAGE),
&self.system_meta,
world,
change_tick,
);
let params = unsafe {
F::Param::get_param(
self.param_state.as_mut().expect(Self::PARAM_MESSAGE),
&self.system_meta,
world,
change_tick,
)
};
let out = self.func.run(input, params);
self.system_meta.last_run = change_tick;
out

View file

@ -661,8 +661,10 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> {
// SAFETY:
// - `self.world` has permission to access the required components.
// - The caller ensures that this operation will not result in any aliased mutable accesses.
self.state
.iter_unchecked_manual(self.world, self.last_run, self.this_run)
unsafe {
self.state
.iter_unchecked_manual(self.world, self.last_run, self.this_run)
}
}
/// Iterates over all possible combinations of `K` query items without repetition.
@ -682,8 +684,10 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> {
// SAFETY:
// - `self.world` has permission to access the required components.
// - The caller ensures that this operation will not result in any aliased mutable accesses.
self.state
.iter_combinations_unchecked_manual(self.world, self.last_run, self.this_run)
unsafe {
self.state
.iter_combinations_unchecked_manual(self.world, self.last_run, self.this_run)
}
}
/// Returns an [`Iterator`] over the query items generated from an [`Entity`] list.
@ -707,8 +711,14 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> {
// SAFETY:
// - `self.world` has permission to access the required components.
// - The caller ensures that this operation will not result in any aliased mutable accesses.
self.state
.iter_many_unchecked_manual(entities, self.world, self.last_run, self.this_run)
unsafe {
self.state.iter_many_unchecked_manual(
entities,
self.world,
self.last_run,
self.this_run,
)
}
}
/// Returns a parallel iterator over the query results for the given [`World`].
@ -1014,8 +1024,10 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> {
pub unsafe fn get_unchecked(&self, entity: Entity) -> Result<D::Item<'_>, QueryEntityError> {
// SEMI-SAFETY: system runs without conflicts with other systems.
// same-system queries have runtime borrow checks when they conflict
self.state
.get_unchecked_manual(self.world, entity, self.last_run, self.this_run)
unsafe {
self.state
.get_unchecked_manual(self.world, entity, self.last_run, self.this_run)
}
}
/// Returns a single read-only query item when there is exactly one entity matching the query.

View file

@ -228,7 +228,7 @@ unsafe impl<D: QueryData + 'static, F: QueryFilter + 'static> SystemParam for Qu
// SAFETY: We have registered all of the query's world accesses,
// so the caller ensures that `world` has permission to access any
// world data that the query needs.
Query::new(world, state, system_meta.last_run, change_tick)
unsafe { Query::new(world, state, system_meta.last_run, change_tick) }
}
}
@ -654,7 +654,7 @@ unsafe impl SystemParam for &'_ World {
_change_tick: Tick,
) -> Self::Item<'w, 's> {
// SAFETY: Read-only access to the entire world was registered in `init_state`.
world.world()
unsafe { world.world() }
}
}
@ -1500,7 +1500,7 @@ unsafe impl<P: SystemParam + 'static> SystemParam for StaticSystemParam<'_, '_,
change_tick: Tick,
) -> Self::Item<'world, 'state> {
// SAFETY: Defer to the safety of P::SystemParam
StaticSystemParam(P::get_param(state, system_meta, world, change_tick))
StaticSystemParam(unsafe { P::get_param(state, system_meta, world, change_tick) })
}
}

View file

@ -828,7 +828,9 @@ impl<'w> EntityWorldMut<'w> {
Some(result)
}
/// Safety: `new_archetype_id` must have the same or a subset of the components
/// # Safety
///
/// `new_archetype_id` must have the same or a subset of the components
/// in `old_archetype_id`. Probably more safety stuff too, audit a call to
/// this fn as if the code here was written inline
///
@ -874,15 +876,16 @@ impl<'w> EntityWorldMut<'w> {
.tables
.get_2_mut(old_table_id, new_archetype.table_id());
// SAFETY: old_table_row exists
let move_result = if DROP {
old_table.move_to_and_drop_missing_unchecked(old_table_row, new_table)
// SAFETY: old_table_row exists
unsafe { old_table.move_to_and_drop_missing_unchecked(old_table_row, new_table) }
} else {
old_table.move_to_and_forget_missing_unchecked(old_table_row, new_table)
// SAFETY: old_table_row exists
unsafe { old_table.move_to_and_forget_missing_unchecked(old_table_row, new_table) }
};
// SAFETY: move_result.new_row is a valid position in new_archetype's table
let new_location = new_archetype.allocate(entity, move_result.new_row);
let new_location = unsafe { new_archetype.allocate(entity, move_result.new_row) };
// if an entity was moved into this entity's table row, update its table row
if let Some(swapped_entity) = move_result.swapped_entity {
@ -906,7 +909,9 @@ impl<'w> EntityWorldMut<'w> {
*self_location = new_location;
// SAFETY: The entity is valid and has been moved to the new location already.
entities.set(entity.index(), new_location);
unsafe {
entities.set(entity.index(), new_location);
}
}
/// Remove the components of `bundle_info` from `entity`, where `self_location` and `old_location`
@ -927,14 +932,16 @@ impl<'w> EntityWorldMut<'w> {
) {
// SAFETY: `archetype_id` exists because it is referenced in `old_location` which is valid
// and components in `bundle_info` must exist due to this functions safety invariants.
let new_archetype_id = remove_bundle_from_archetype(
archetypes,
storages,
components,
old_location.archetype_id,
bundle_info,
true,
)
let new_archetype_id = unsafe {
remove_bundle_from_archetype(
archetypes,
storages,
components,
old_location.archetype_id,
bundle_info,
true,
)
}
.expect("intersections should always return a result");
if new_archetype_id == old_location.archetype_id {
@ -960,16 +967,18 @@ impl<'w> EntityWorldMut<'w> {
// SAFETY: `new_archetype_id` is a subset of the components in `old_location.archetype_id`
// because it is created by removing a bundle from these components.
Self::move_entity_from_remove::<true>(
entity,
self_location,
old_location.archetype_id,
old_location,
entities,
archetypes,
storages,
new_archetype_id,
);
unsafe {
Self::move_entity_from_remove::<true>(
entity,
self_location,
old_location.archetype_id,
old_location,
entities,
archetypes,
storages,
new_archetype_id,
);
}
}
/// Removes any components in the [`Bundle`] from the entity.
@ -2135,7 +2144,7 @@ unsafe fn remove_bundle_from_archetype(
for component_id in bundle_info.components().iter().cloned() {
if current_archetype.contains(component_id) {
// SAFETY: bundle components were already initialized by bundles.get_info
let component_info = components.get_info_unchecked(component_id);
let component_info = unsafe { components.get_info_unchecked(component_id) };
match component_info.storage_type() {
StorageType::Table => removed_table_components.push(component_id),
StorageType::SparseSet => removed_sparse_set_components.push(component_id),
@ -2167,9 +2176,11 @@ unsafe fn remove_bundle_from_archetype(
current_archetype.table_id()
} else {
// SAFETY: all components in next_table_components exist
storages
.tables
.get_id_or_insert(&next_table_components, components)
unsafe {
storages
.tables
.get_id_or_insert(&next_table_components, components)
}
};
}
@ -2230,7 +2241,7 @@ pub(crate) unsafe fn take_component<'a>(
location: EntityLocation,
) -> OwningPtr<'a> {
// SAFETY: caller promises component_id to be valid
let component_info = components.get_info_unchecked(component_id);
let component_info = unsafe { components.get_info_unchecked(component_id) };
removed_components.send(component_id, entity);
match component_info.storage_type() {
StorageType::Table => {
@ -2240,9 +2251,11 @@ pub(crate) unsafe fn take_component<'a>(
// - archetypes only store valid table_rows
// - index is in bounds as promised by caller
// - promote is safe because the caller promises to remove the table row without dropping it immediately afterwards
components
.get_data_unchecked_mut(location.table_row)
.promote()
unsafe {
components
.get_data_unchecked_mut(location.table_row)
.promote()
}
}
StorageType::SparseSet => storages
.sparse_sets

View file

@ -773,9 +773,11 @@ impl World {
let table_row = self.storages.tables[archetype.table_id()].allocate(entity);
// SAFETY: no components are allocated by archetype.allocate() because the archetype is
// empty
let location = archetype.allocate(entity, table_row);
let location = unsafe { archetype.allocate(entity, table_row) };
// SAFETY: entity index was just allocated
self.entities.set(entity.index(), location);
unsafe {
self.entities.set(entity.index(), location);
}
EntityWorldMut::new(self, entity, location)
}
@ -1743,9 +1745,11 @@ impl World {
) {
let change_tick = self.change_tick();
// SAFETY: value is valid for component_id, ensured by caller
self.initialize_resource_internal(component_id)
.insert(value, change_tick);
let resource = self.initialize_resource_internal(component_id);
// SAFETY: `value` is valid for `component_id`, ensured by caller
unsafe {
resource.insert(value, change_tick);
}
}
/// Inserts a new `!Send` resource with the given `value`. Will replace the value if it already
@ -1768,9 +1772,11 @@ impl World {
) {
let change_tick = self.change_tick();
// SAFETY: value is valid for component_id, ensured by caller
self.initialize_non_send_internal(component_id)
.insert(value, change_tick);
let resource = self.initialize_non_send_internal(component_id);
// SAFETY: `value` is valid for `component_id`, ensured by caller
unsafe {
resource.insert(value, change_tick);
}
}
/// # Panics