Skip to content

Commit 030657a

Browse files
authored
Doc trivial ecs unsafe (#22014)
# Objective - Trying to doc most of the unsafe in the ecs crate so we can turn on `unsafe_op_in_unsafe_fn`. ## Solution - Unfortunately reviewing the unsafe docs will probably not be trivial if we try to do it all in one pr. There are 400+ warnings when you turn on the lint. So we need to break it up as much as possible as reviewing the safety contracts in some cases isn't the easiest. - This pr includes two types of unsafe blocks. 1. Blocks that already had safety comments, but were missing the `unsafe {}` block. 2. Unsafe functions that have the same safety contract as their unsafe parent function and are very short. (Usually just the call to the function).
1 parent 6ee5067 commit 030657a

File tree

19 files changed

+186
-95
lines changed

19 files changed

+186
-95
lines changed

crates/bevy_ecs/src/bundle/impls.rs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -138,8 +138,14 @@ macro_rules! tuple_impl {
138138
bevy_ptr::deconstruct_moving_ptr!({
139139
let tuple { $($index: $alias,)* } = ptr;
140140
});
141+
#[allow(
142+
unused_unsafe,
143+
reason = "Zero-length tuples will generate a function body equivalatent to (); however, this macro is meant for all applicable tuples, and as such it makes no sense to rewrite it just for that case."
144+
)]
141145
// SAFETY: Caller ensures requirements for calling `get_components` are met.
142-
$( $name::get_components($alias, func); )*
146+
unsafe {
147+
$( $name::get_components($alias, func); )*
148+
}
143149
}
144150

145151
#[allow(
@@ -151,8 +157,14 @@ macro_rules! tuple_impl {
151157
bevy_ptr::deconstruct_moving_ptr!({
152158
let MaybeUninit::<tuple> { $($index: $alias,)* } = ptr;
153159
});
160+
#[allow(
161+
unused_unsafe,
162+
reason = "Zero-length tuples will generate a function body equivalent to `()`; however, this macro is meant for all applicable tuples, and as such it makes no sense to rewrite it just for that case."
163+
)]
154164
// SAFETY: Caller ensures requirements for calling `apply_effect` are met.
155-
$( $name::apply_effect($alias, entity); )*
165+
unsafe {
166+
$( $name::apply_effect($alias, entity); )*
167+
}
156168
}
157169
}
158170

crates/bevy_ecs/src/bundle/insert.rs

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -535,13 +535,15 @@ impl BundleInfo {
535535
};
536536
};
537537
// SAFETY: ids in self must be valid
538-
let (new_archetype_id, is_new_created) = archetypes.get_id_or_insert(
539-
components,
540-
observers,
541-
table_id,
542-
table_components,
543-
sparse_set_components,
544-
);
538+
let (new_archetype_id, is_new_created) = unsafe {
539+
archetypes.get_id_or_insert(
540+
components,
541+
observers,
542+
table_id,
543+
table_components,
544+
sparse_set_components,
545+
)
546+
};
545547

546548
// Add an edge from the old archetype to the new archetype.
547549
archetypes[archetype_id]

crates/bevy_ecs/src/component/required.rs

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -390,11 +390,12 @@ impl Components {
390390
let old_required_count = required_components.all.len();
391391

392392
// SAFETY: the caller guarantees that `requiree` is valid in `self`.
393-
self.required_components_scope(requiree, |this, required_components| {
394-
// SAFETY: the caller guarantees that `required` is valid for type `R` in `self`
395-
unsafe { required_components.register_by_id(required, this, constructor) };
396-
});
397-
393+
unsafe {
394+
self.required_components_scope(requiree, |this, required_components| {
395+
// SAFETY: the caller guarantees that `required` is valid for type `R` in `self`
396+
required_components.register_by_id(required, this, constructor);
397+
});
398+
}
398399
// Third step: update the required components and required_by of all the indirect requirements/requirees.
399400

400401
// Borrow again otherwise it conflicts with the `self.required_components_scope` call.
@@ -435,11 +436,13 @@ impl Components {
435436
// Skip the first one (requiree) because we already updates it.
436437
for &indirect_requiree in &new_requiree_components[1..] {
437438
// SAFETY: `indirect_requiree` comes from `self` so it must be valid.
438-
self.required_components_scope(indirect_requiree, |this, required_components| {
439-
// Rebuild the inherited required components.
440-
// SAFETY: `required_components` comes from `self`, so all its components must have be valid in `self`.
441-
unsafe { required_components.rebuild_inherited_required_components(this) };
442-
});
439+
unsafe {
440+
self.required_components_scope(indirect_requiree, |this, required_components| {
441+
// Rebuild the inherited required components.
442+
// SAFETY: `required_components` comes from `self`, so all its components must have be valid in `self`.
443+
required_components.rebuild_inherited_required_components(this);
444+
});
445+
}
443446
}
444447

445448
// Update the `required_by` of all the components that were newly required (directly or indirectly).

crates/bevy_ecs/src/entity/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -993,7 +993,7 @@ impl Entities {
993993
) -> Option<EntityLocation> {
994994
self.ensure_index_index_is_valid(index);
995995
// SAFETY: We just did `ensure_index`
996-
self.update_existing_location(index, location)
996+
unsafe { self.update_existing_location(index, location) }
997997
}
998998

999999
/// Ensures the index is within the bounds of [`Self::meta`], expanding it if necessary.

crates/bevy_ecs/src/query/state.rs

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1099,7 +1099,8 @@ impl<D: QueryData, F: QueryFilter> QueryState<D, F> {
10991099
world: UnsafeWorldCell<'w>,
11001100
entity: Entity,
11011101
) -> Result<D::Item<'w, '_>, QueryEntityError> {
1102-
self.query_unchecked(world).get_inner(entity)
1102+
// SAFETY: Upheld by caller
1103+
unsafe { self.query_unchecked(world) }.get_inner(entity)
11031104
}
11041105

11051106
/// Returns an [`Iterator`] over the query results for the given [`World`].
@@ -1314,7 +1315,8 @@ impl<D: QueryData, F: QueryFilter> QueryState<D, F> {
13141315
&'s mut self,
13151316
world: UnsafeWorldCell<'w>,
13161317
) -> QueryIter<'w, 's, D, F> {
1317-
self.query_unchecked(world).into_iter()
1318+
// SAFETY: Upheld by caller
1319+
unsafe { self.query_unchecked(world) }.into_iter()
13181320
}
13191321

13201322
/// Returns an [`Iterator`] over all possible combinations of `K` query results for the
@@ -1333,7 +1335,8 @@ impl<D: QueryData, F: QueryFilter> QueryState<D, F> {
13331335
&'s mut self,
13341336
world: UnsafeWorldCell<'w>,
13351337
) -> QueryCombinationIter<'w, 's, D, F, K> {
1336-
self.query_unchecked(world).iter_combinations_inner()
1338+
// SAFETY: Upheld by caller
1339+
unsafe { self.query_unchecked(world) }.iter_combinations_inner()
13371340
}
13381341

13391342
/// Returns a parallel iterator over the query results for the given [`World`].
@@ -1755,7 +1758,8 @@ impl<D: QueryData, F: QueryFilter> QueryState<D, F> {
17551758
&mut self,
17561759
world: UnsafeWorldCell<'w>,
17571760
) -> Result<D::Item<'w, '_>, QuerySingleError> {
1758-
self.query_unchecked(world).single_inner()
1761+
// SAFETY: Upheld by caller
1762+
unsafe { self.query_unchecked(world) }.single_inner()
17591763
}
17601764

17611765
/// Returns a query result when there is exactly one entity matching the query,
@@ -1780,8 +1784,7 @@ impl<D: QueryData, F: QueryFilter> QueryState<D, F> {
17801784
// SAFETY:
17811785
// - The caller ensured we have the correct access to the world.
17821786
// - The caller ensured that the world matches.
1783-
self.query_unchecked_manual_with_ticks(world, last_run, this_run)
1784-
.single_inner()
1787+
unsafe { self.query_unchecked_manual_with_ticks(world, last_run, this_run) }.single_inner()
17851788
}
17861789
}
17871790

crates/bevy_ecs/src/schedule/executor/mod.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -260,7 +260,8 @@ mod __rust_begin_short_backtrace {
260260
system: &mut ScheduleSystem,
261261
world: UnsafeWorldCell,
262262
) -> Result<(), RunSystemError> {
263-
let result = system.run_unsafe((), world);
263+
// SAFETY: Upheld by caller
264+
let result = unsafe { system.run_unsafe((), world) };
264265
// Call `black_box` to prevent this frame from being tail-call optimized away
265266
black_box(());
266267
result
@@ -276,7 +277,8 @@ mod __rust_begin_short_backtrace {
276277
world: UnsafeWorldCell,
277278
) -> Result<O, RunSystemError> {
278279
// Call `black_box` to prevent this frame from being tail-call optimized away
279-
black_box(system.run_unsafe((), world))
280+
// SAFETY: Upheld by caller
281+
black_box(unsafe { system.run_unsafe((), world) })
280282
}
281283

282284
#[cfg(feature = "std")]

crates/bevy_ecs/src/spawn.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -327,7 +327,7 @@ impl<R: Relationship, L: SpawnableList<R>> DynamicBundle for SpawnRelatedBundle<
327327
// called exactly once for each component being fetched with the correct `StorageType`
328328
// - `Effect: !NoBundleEffect`, which means the caller is responsible for calling this type's `apply_effect`
329329
// at least once before returning to safe code.
330-
<R::RelationshipTarget as DynamicBundle>::get_components(target, func);
330+
unsafe { <R::RelationshipTarget as DynamicBundle>::get_components(target, func) };
331331
// Forget the pointer so that the value is available in `apply_effect`.
332332
mem::forget(ptr);
333333
}
@@ -372,7 +372,7 @@ impl<R: Relationship, B: Bundle> DynamicBundle for SpawnOneRelated<R, B> {
372372
// called exactly once for each component being fetched with the correct `StorageType`
373373
// - `Effect: !NoBundleEffect`, which means the caller is responsible for calling this type's `apply_effect`
374374
// at least once before returning to safe code.
375-
<R::RelationshipTarget as DynamicBundle>::get_components(target, func);
375+
unsafe { <R::RelationshipTarget as DynamicBundle>::get_components(target, func) };
376376
// Forget the pointer so that the value is available in `apply_effect`.
377377
mem::forget(ptr);
378378
}

crates/bevy_ecs/src/storage/blob_array.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,8 @@ impl BlobArray {
5454
capacity,
5555
}
5656
} else {
57-
let mut arr = Self::with_capacity(item_layout, drop_fn, 0);
57+
// SAFETY: Upheld by caller
58+
let mut arr = unsafe { Self::with_capacity(item_layout, drop_fn, 0) };
5859
// SAFETY: `capacity` > 0
5960
unsafe { arr.alloc(NonZeroUsize::new_unchecked(capacity)) }
6061
arr

crates/bevy_ecs/src/storage/mod.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,8 @@ impl<T> VecExtensions<T> for Vec<T> {
102102
// SAFETY: We replace self[index] with the last element. The caller must ensure that
103103
// both the last element and `index` must be valid and cannot point to the same place.
104104
unsafe { core::ptr::copy_nonoverlapping(base_ptr.add(len - 1), base_ptr.add(index), 1) };
105-
self.set_len(len - 1);
105+
// SAFETY: Upheld by caller
106+
unsafe { self.set_len(len - 1) };
106107
value
107108
}
108109
}

crates/bevy_ecs/src/storage/table/column.rs

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -336,23 +336,26 @@ impl Column {
336336
/// - `T` must match the type of data that's stored in this [`Column`]
337337
/// - `len` must match the actual length of this column (number of elements stored)
338338
pub unsafe fn get_data_slice<T>(&self, len: usize) -> &[UnsafeCell<T>] {
339-
self.data.get_sub_slice(len)
339+
// SAFETY: Upheld by caller
340+
unsafe { self.data.get_sub_slice(len) }
340341
}
341342

342343
/// Get a slice to the added [`ticks`](Tick) in this [`Column`].
343344
///
344345
/// # Safety
345346
/// - `len` must match the actual length of this column (number of elements stored)
346347
pub unsafe fn get_added_ticks_slice(&self, len: usize) -> &[UnsafeCell<Tick>] {
347-
self.added_ticks.as_slice(len)
348+
// SAFETY: Upheld by caller
349+
unsafe { self.added_ticks.as_slice(len) }
348350
}
349351

350352
/// Get a slice to the changed [`ticks`](Tick) in this [`Column`].
351353
///
352354
/// # Safety
353355
/// - `len` must match the actual length of this column (number of elements stored)
354356
pub unsafe fn get_changed_ticks_slice(&self, len: usize) -> &[UnsafeCell<Tick>] {
355-
self.changed_ticks.as_slice(len)
357+
// SAFETY: Upheld by caller
358+
unsafe { self.changed_ticks.as_slice(len) }
356359
}
357360

358361
/// Get a slice to the calling locations that last changed each value in this [`Column`]
@@ -402,7 +405,8 @@ impl Column {
402405
/// `row` must be within the range `[0, self.len())`.
403406
#[inline]
404407
pub unsafe fn get_added_tick_unchecked(&self, row: TableRow) -> &UnsafeCell<Tick> {
405-
self.added_ticks.get_unchecked(row.index())
408+
// SAFETY: Upheld by caller
409+
unsafe { self.added_ticks.get_unchecked(row.index()) }
406410
}
407411

408412
/// Fetches the "changed" change detection tick for the value at `row`
@@ -412,7 +416,8 @@ impl Column {
412416
/// `row` must be within the range `[0, self.len())`.
413417
#[inline]
414418
pub unsafe fn get_changed_tick_unchecked(&self, row: TableRow) -> &UnsafeCell<Tick> {
415-
self.changed_ticks.get_unchecked(row.index())
419+
// SAFETY: Upheld by caller
420+
unsafe { self.changed_ticks.get_unchecked(row.index()) }
416421
}
417422

418423
/// Fetches the change detection ticks for the value at `row`.

0 commit comments

Comments
 (0)