add more SAFETY
comments and lint for missing ones in bevy_ecs
(#4835)
# Objective `SAFETY` comments are meant to be placed before `unsafe` blocks and should contain the reasoning of why in this case the usage of unsafe is okay. This is useful when reading the code because it makes it clear which assumptions are required for safety, and makes it easier to spot possible unsoundness holes. It also forces the code writer to think of something to write and maybe look at the safety contracts of any called unsafe methods again to double-check their correct usage. There's a clippy lint called `undocumented_unsafe_blocks` which warns when using a block without such a comment. ## Solution - since clippy expects `SAFETY` instead of `SAFE`, rename those - add `SAFETY` comments in more places - for the last remaining 3 places, add an `#[allow()]` and `// TODO` since I wasn't comfortable enough with the code to justify their safety - add ` #![warn(clippy::undocumented_unsafe_blocks)]` to `bevy_ecs` ### Note for reviewers The first commit only renames `SAFETY` to `SAFE` so it doesn't need a thorough review.cb042a416e..55cef2d6fa
is the diff for all other changes. ### Safety comments where I'm not too familiar with the code774012ece5/crates/bevy_ecs/src/entity/mod.rs (L540-L546)
774012ece5/crates/bevy_ecs/src/world/entity_ref.rs (L249-L252)
### Locations left undocumented with a `TODO` comment5dde944a30/crates/bevy_ecs/src/schedule/executor_parallel.rs (L196-L199)
5dde944a30/crates/bevy_ecs/src/world/entity_ref.rs (L287-L289)
5dde944a30/crates/bevy_ecs/src/world/entity_ref.rs (L413-L415)
Co-authored-by: Jakob Hellermann <hellermann@sipgate.de>
This commit is contained in:
parent
4d05eb19be
commit
d38a8dfdd7
@ -144,7 +144,7 @@ pub fn derive_bundle(input: TokenStream) -> TokenStream {
|
|||||||
let struct_name = &ast.ident;
|
let struct_name = &ast.ident;
|
||||||
|
|
||||||
TokenStream::from(quote! {
|
TokenStream::from(quote! {
|
||||||
/// SAFE: ComponentId is returned in field-definition-order. [from_components] and [get_components] use field-definition-order
|
/// SAFETY: ComponentId is returned in field-definition-order. [from_components] and [get_components] use field-definition-order
|
||||||
unsafe impl #impl_generics #ecs_path::bundle::Bundle for #struct_name #ty_generics #where_clause {
|
unsafe impl #impl_generics #ecs_path::bundle::Bundle for #struct_name #ty_generics #where_clause {
|
||||||
fn component_ids(
|
fn component_ids(
|
||||||
components: &mut #ecs_path::component::Components,
|
components: &mut #ecs_path::component::Components,
|
||||||
@ -192,7 +192,7 @@ pub fn impl_param_set(_input: TokenStream) -> TokenStream {
|
|||||||
let index = Index::from(i);
|
let index = Index::from(i);
|
||||||
param_fn_muts.push(quote! {
|
param_fn_muts.push(quote! {
|
||||||
pub fn #fn_name<'a>(&'a mut self) -> <#param::Fetch as SystemParamFetch<'a, 'a>>::Item {
|
pub fn #fn_name<'a>(&'a mut self) -> <#param::Fetch as SystemParamFetch<'a, 'a>>::Item {
|
||||||
// SAFE: systems run without conflicts with other systems.
|
// SAFETY: systems run without conflicts with other systems.
|
||||||
// Conflicting params in ParamSet are not accessible at the same time
|
// Conflicting params in ParamSet are not accessible at the same time
|
||||||
// ParamSets are guaranteed to not conflict with other SystemParams
|
// ParamSets are guaranteed to not conflict with other SystemParams
|
||||||
unsafe {
|
unsafe {
|
||||||
@ -213,13 +213,13 @@ pub fn impl_param_set(_input: TokenStream) -> TokenStream {
|
|||||||
type Fetch = ParamSetState<(#(#param::Fetch,)*)>;
|
type Fetch = ParamSetState<(#(#param::Fetch,)*)>;
|
||||||
}
|
}
|
||||||
|
|
||||||
// SAFE: All parameters are constrained to ReadOnlyFetch, so World is only read
|
// SAFETY: All parameters are constrained to ReadOnlyFetch, so World is only read
|
||||||
|
|
||||||
unsafe impl<#(#param_fetch: for<'w1, 's1> SystemParamFetch<'w1, 's1>,)*> ReadOnlySystemParamFetch for ParamSetState<(#(#param_fetch,)*)>
|
unsafe impl<#(#param_fetch: for<'w1, 's1> SystemParamFetch<'w1, 's1>,)*> ReadOnlySystemParamFetch for ParamSetState<(#(#param_fetch,)*)>
|
||||||
where #(#param_fetch: ReadOnlySystemParamFetch,)*
|
where #(#param_fetch: ReadOnlySystemParamFetch,)*
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
// SAFE: Relevant parameter ComponentId and ArchetypeComponentId access is applied to SystemMeta. If any ParamState conflicts
|
// SAFETY: Relevant parameter ComponentId and ArchetypeComponentId access is applied to SystemMeta. If any ParamState conflicts
|
||||||
// with any prior access, a panic will occur.
|
// with any prior access, a panic will occur.
|
||||||
|
|
||||||
unsafe impl<#(#param_fetch: for<'w1, 's1> SystemParamFetch<'w1, 's1>,)*> SystemParamState for ParamSetState<(#(#param_fetch,)*)>
|
unsafe impl<#(#param_fetch: for<'w1, 's1> SystemParamFetch<'w1, 's1>,)*> SystemParamState for ParamSetState<(#(#param_fetch,)*)>
|
||||||
|
@ -420,13 +420,13 @@ impl Archetypes {
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn empty(&self) -> &Archetype {
|
pub fn empty(&self) -> &Archetype {
|
||||||
// SAFE: empty archetype always exists
|
// SAFETY: empty archetype always exists
|
||||||
unsafe { self.archetypes.get_unchecked(ArchetypeId::EMPTY.index()) }
|
unsafe { self.archetypes.get_unchecked(ArchetypeId::EMPTY.index()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn empty_mut(&mut self) -> &mut Archetype {
|
pub(crate) fn empty_mut(&mut self) -> &mut Archetype {
|
||||||
// SAFE: empty archetype always exists
|
// SAFETY: empty archetype always exists
|
||||||
unsafe {
|
unsafe {
|
||||||
self.archetypes
|
self.archetypes
|
||||||
.get_unchecked_mut(ArchetypeId::EMPTY.index())
|
.get_unchecked_mut(ArchetypeId::EMPTY.index())
|
||||||
@ -435,13 +435,13 @@ impl Archetypes {
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn resource(&self) -> &Archetype {
|
pub fn resource(&self) -> &Archetype {
|
||||||
// SAFE: resource archetype always exists
|
// SAFETY: resource archetype always exists
|
||||||
unsafe { self.archetypes.get_unchecked(ArchetypeId::RESOURCE.index()) }
|
unsafe { self.archetypes.get_unchecked(ArchetypeId::RESOURCE.index()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn resource_mut(&mut self) -> &mut Archetype {
|
pub(crate) fn resource_mut(&mut self) -> &mut Archetype {
|
||||||
// SAFE: resource archetype always exists
|
// SAFETY: resource archetype always exists
|
||||||
unsafe {
|
unsafe {
|
||||||
self.archetypes
|
self.archetypes
|
||||||
.get_unchecked_mut(ArchetypeId::RESOURCE.index())
|
.get_unchecked_mut(ArchetypeId::RESOURCE.index())
|
||||||
|
@ -99,6 +99,10 @@ pub unsafe trait Bundle: Send + Sync + 'static {
|
|||||||
|
|
||||||
macro_rules! tuple_impl {
|
macro_rules! tuple_impl {
|
||||||
($($name: ident),*) => {
|
($($name: ident),*) => {
|
||||||
|
// SAFETY:
|
||||||
|
// - `Bundle::component_ids` returns the `ComponentId`s for each component type in the
|
||||||
|
// bundle, in the exact order that `Bundle::get_components` is called.
|
||||||
|
// - `Bundle::from_components` calls `func` exactly once for each `ComponentId` returned by `Bundle::component_ids`.
|
||||||
unsafe impl<$($name: Component),*> Bundle for ($($name,)*) {
|
unsafe impl<$($name: Component),*> Bundle for ($($name,)*) {
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
fn component_ids(components: &mut Components, storages: &mut Storages) -> Vec<ComponentId> {
|
fn component_ids(components: &mut Components, storages: &mut Storages) -> Vec<ComponentId> {
|
||||||
@ -325,7 +329,7 @@ impl BundleInfo {
|
|||||||
bundle_status.push(ComponentStatus::Mutated);
|
bundle_status.push(ComponentStatus::Mutated);
|
||||||
} else {
|
} else {
|
||||||
bundle_status.push(ComponentStatus::Added);
|
bundle_status.push(ComponentStatus::Added);
|
||||||
// SAFE: component_id exists
|
// SAFETY: component_id exists
|
||||||
let component_info = unsafe { components.get_info_unchecked(component_id) };
|
let component_info = unsafe { components.get_info_unchecked(component_id) };
|
||||||
match component_info.storage_type() {
|
match component_info.storage_type() {
|
||||||
StorageType::Table => new_table_components.push(component_id),
|
StorageType::Table => new_table_components.push(component_id),
|
||||||
@ -354,7 +358,7 @@ impl BundleInfo {
|
|||||||
new_table_components.extend(current_archetype.table_components());
|
new_table_components.extend(current_archetype.table_components());
|
||||||
// sort to ignore order while hashing
|
// sort to ignore order while hashing
|
||||||
new_table_components.sort();
|
new_table_components.sort();
|
||||||
// SAFE: all component ids in `new_table_components` exist
|
// SAFETY: all component ids in `new_table_components` exist
|
||||||
table_id = unsafe {
|
table_id = unsafe {
|
||||||
storages
|
storages
|
||||||
.tables
|
.tables
|
||||||
@ -492,7 +496,7 @@ impl<'a, 'b> BundleInserter<'a, 'b> {
|
|||||||
} else if new_archetype.id() == swapped_location.archetype_id {
|
} else if new_archetype.id() == swapped_location.archetype_id {
|
||||||
new_archetype
|
new_archetype
|
||||||
} else {
|
} else {
|
||||||
// SAFE: the only two borrowed archetypes are above and we just did collision checks
|
// SAFETY: the only two borrowed archetypes are above and we just did collision checks
|
||||||
&mut *self
|
&mut *self
|
||||||
.archetypes_ptr
|
.archetypes_ptr
|
||||||
.add(swapped_location.archetype_id.index())
|
.add(swapped_location.archetype_id.index())
|
||||||
@ -567,7 +571,7 @@ impl<'a, 'b> BundleSpawner<'a, 'b> {
|
|||||||
#[inline]
|
#[inline]
|
||||||
pub unsafe fn spawn<T: Bundle>(&mut self, bundle: T) -> Entity {
|
pub unsafe fn spawn<T: Bundle>(&mut self, bundle: T) -> Entity {
|
||||||
let entity = self.entities.alloc();
|
let entity = self.entities.alloc();
|
||||||
// SAFE: entity is allocated (but non-existent), `T` matches this BundleInfo's type
|
// SAFETY: entity is allocated (but non-existent), `T` matches this BundleInfo's type
|
||||||
self.spawn_non_existent(entity, bundle);
|
self.spawn_non_existent(entity, bundle);
|
||||||
entity
|
entity
|
||||||
}
|
}
|
||||||
@ -599,14 +603,14 @@ impl Bundles {
|
|||||||
let id = self.bundle_ids.entry(TypeId::of::<T>()).or_insert_with(|| {
|
let id = self.bundle_ids.entry(TypeId::of::<T>()).or_insert_with(|| {
|
||||||
let component_ids = T::component_ids(components, storages);
|
let component_ids = T::component_ids(components, storages);
|
||||||
let id = BundleId(bundle_infos.len());
|
let id = BundleId(bundle_infos.len());
|
||||||
// SAFE: T::component_id ensures info was created
|
// SAFETY: T::component_id ensures info was created
|
||||||
let bundle_info = unsafe {
|
let bundle_info = unsafe {
|
||||||
initialize_bundle(std::any::type_name::<T>(), component_ids, id, components)
|
initialize_bundle(std::any::type_name::<T>(), component_ids, id, components)
|
||||||
};
|
};
|
||||||
bundle_infos.push(bundle_info);
|
bundle_infos.push(bundle_info);
|
||||||
id
|
id
|
||||||
});
|
});
|
||||||
// SAFE: index either exists, or was initialized
|
// SAFETY: index either exists, or was initialized
|
||||||
unsafe { self.bundle_infos.get_unchecked(id.0) }
|
unsafe { self.bundle_infos.get_unchecked(id.0) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -623,7 +627,7 @@ unsafe fn initialize_bundle(
|
|||||||
let mut storage_types = Vec::new();
|
let mut storage_types = Vec::new();
|
||||||
|
|
||||||
for &component_id in &component_ids {
|
for &component_id in &component_ids {
|
||||||
// SAFE: component_id exists and is therefore valid
|
// SAFETY: component_id exists and is therefore valid
|
||||||
let component_info = components.get_info_unchecked(component_id);
|
let component_info = components.get_info_unchecked(component_id);
|
||||||
storage_types.push(component_info.storage_type());
|
storage_types.push(component_info.storage_type());
|
||||||
}
|
}
|
||||||
|
@ -474,7 +474,7 @@ impl Components {
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn init_resource<T: Resource>(&mut self) -> ComponentId {
|
pub fn init_resource<T: Resource>(&mut self) -> ComponentId {
|
||||||
// SAFE: The [`ComponentDescriptor`] matches the [`TypeId`]
|
// SAFETY: The [`ComponentDescriptor`] matches the [`TypeId`]
|
||||||
unsafe {
|
unsafe {
|
||||||
self.get_or_insert_resource_with(TypeId::of::<T>(), || {
|
self.get_or_insert_resource_with(TypeId::of::<T>(), || {
|
||||||
ComponentDescriptor::new_resource::<T>()
|
ComponentDescriptor::new_resource::<T>()
|
||||||
@ -484,7 +484,7 @@ impl Components {
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn init_non_send<T: Any>(&mut self) -> ComponentId {
|
pub fn init_non_send<T: Any>(&mut self) -> ComponentId {
|
||||||
// SAFE: The [`ComponentDescriptor`] matches the [`TypeId`]
|
// SAFETY: The [`ComponentDescriptor`] matches the [`TypeId`]
|
||||||
unsafe {
|
unsafe {
|
||||||
self.get_or_insert_resource_with(TypeId::of::<T>(), || {
|
self.get_or_insert_resource_with(TypeId::of::<T>(), || {
|
||||||
ComponentDescriptor::new_non_send::<T>(StorageType::default())
|
ComponentDescriptor::new_non_send::<T>(StorageType::default())
|
||||||
|
@ -591,6 +591,8 @@ impl Entities {
|
|||||||
// Flushes all reserved entities to an "invalid" state. Attempting to retrieve them will return None
|
// Flushes all reserved entities to an "invalid" state. Attempting to retrieve them will return None
|
||||||
// unless they are later populated with a valid archetype.
|
// unless they are later populated with a valid archetype.
|
||||||
pub fn flush_as_invalid(&mut self) {
|
pub fn flush_as_invalid(&mut self) {
|
||||||
|
// SAFETY: as per `flush` safety docs, the archetype id can be set to [`ArchetypeId::INVALID`] if
|
||||||
|
// the [`Entity`] has not been assigned to an [`Archetype`][crate::archetype::Archetype], which is the case here
|
||||||
unsafe {
|
unsafe {
|
||||||
self.flush(|_entity, location| {
|
self.flush(|_entity, location| {
|
||||||
location.archetype_id = ArchetypeId::INVALID;
|
location.archetype_id = ArchetypeId::INVALID;
|
||||||
@ -658,6 +660,7 @@ mod tests {
|
|||||||
fn reserve_entity_len() {
|
fn reserve_entity_len() {
|
||||||
let mut e = Entities::default();
|
let mut e = Entities::default();
|
||||||
e.reserve_entity();
|
e.reserve_entity();
|
||||||
|
// SAFETY: entity_location is left invalid
|
||||||
unsafe { e.flush(|_, _| {}) };
|
unsafe { e.flush(|_, _| {}) };
|
||||||
assert_eq!(e.len(), 1);
|
assert_eq!(e.len(), 1);
|
||||||
}
|
}
|
||||||
@ -669,6 +672,7 @@ mod tests {
|
|||||||
assert!(entities.contains(e));
|
assert!(entities.contains(e));
|
||||||
assert!(entities.get(e).is_none());
|
assert!(entities.get(e).is_none());
|
||||||
|
|
||||||
|
// SAFETY: entity_location is left invalid
|
||||||
unsafe {
|
unsafe {
|
||||||
entities.flush(|_entity, _location| {
|
entities.flush(|_entity, _location| {
|
||||||
// do nothing ... leaving entity location invalid
|
// do nothing ... leaving entity location invalid
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
#![warn(clippy::undocumented_unsafe_blocks)]
|
||||||
#![doc = include_str!("../README.md")]
|
#![doc = include_str!("../README.md")]
|
||||||
|
|
||||||
#[cfg(target_pointer_width = "16")]
|
#[cfg(target_pointer_width = "16")]
|
||||||
|
@ -480,7 +480,7 @@ macro_rules! impl_query_filter_tuple {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// SAFE: filters are read only
|
// SAFETY: filters are read only
|
||||||
unsafe impl<$($filter: ReadOnlyWorldQuery),*> ReadOnlyWorldQuery for Or<($($filter,)*)> {}
|
unsafe impl<$($filter: ReadOnlyWorldQuery),*> ReadOnlyWorldQuery for Or<($($filter,)*)> {}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -52,6 +52,9 @@ where
|
|||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
// SAFETY:
|
||||||
|
// `tables` and `archetypes` belong to the same world that the cursor was initialized for.
|
||||||
|
// `query_state` is the state that was passed to `QueryIterationCursor::init`.
|
||||||
unsafe {
|
unsafe {
|
||||||
self.cursor
|
self.cursor
|
||||||
.next(self.tables, self.archetypes, self.query_state)
|
.next(self.tables, self.archetypes, self.query_state)
|
||||||
@ -150,33 +153,42 @@ where
|
|||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
unsafe {
|
for entity in self.entity_iter.by_ref() {
|
||||||
for entity in self.entity_iter.by_ref() {
|
let location = match self.entities.get(*entity.borrow()) {
|
||||||
let location = match self.entities.get(*entity.borrow()) {
|
Some(location) => location,
|
||||||
Some(location) => location,
|
None => continue,
|
||||||
None => continue,
|
};
|
||||||
};
|
|
||||||
|
|
||||||
if !self
|
if !self
|
||||||
.query_state
|
.query_state
|
||||||
.matched_archetypes
|
.matched_archetypes
|
||||||
.contains(location.archetype_id.index())
|
.contains(location.archetype_id.index())
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let archetype = &self.archetypes[location.archetype_id];
|
let archetype = &self.archetypes[location.archetype_id];
|
||||||
|
|
||||||
|
// 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
|
||||||
|
unsafe {
|
||||||
self.fetch
|
self.fetch
|
||||||
.set_archetype(&self.query_state.fetch_state, archetype, self.tables);
|
.set_archetype(&self.query_state.fetch_state, archetype, self.tables);
|
||||||
|
}
|
||||||
|
// 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
|
||||||
|
unsafe {
|
||||||
self.filter
|
self.filter
|
||||||
.set_archetype(&self.query_state.filter_state, archetype, self.tables);
|
.set_archetype(&self.query_state.filter_state, archetype, self.tables);
|
||||||
if self.filter.archetype_filter_fetch(location.index) {
|
|
||||||
return Some(self.fetch.archetype_fetch(location.index));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
None
|
// SAFETY: set_archetype was called prior.
|
||||||
|
// `location.index` is an archetype index row in range of the current archetype, because if it was not, the match above would have `continue`d
|
||||||
|
if unsafe { self.filter.archetype_filter_fetch(location.index) } {
|
||||||
|
// SAFETY: set_archetype was called prior, `location.index` is an archetype index in range of the current archetype
|
||||||
|
return Some(unsafe { self.fetch.archetype_fetch(location.index) });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||||
@ -289,7 +301,7 @@ impl<'w, 's, Q: WorldQuery, F: WorldQuery, const K: usize> QueryCombinationIter<
|
|||||||
for<'a> QueryFetch<'a, Q>: Clone,
|
for<'a> QueryFetch<'a, Q>: Clone,
|
||||||
for<'a> QueryFetch<'a, F>: Clone,
|
for<'a> QueryFetch<'a, F>: Clone,
|
||||||
{
|
{
|
||||||
// safety: we are limiting the returned reference to self,
|
// SAFETY: we are limiting the returned reference to self,
|
||||||
// making sure this method cannot be called multiple times without getting rid
|
// making sure this method cannot be called multiple times without getting rid
|
||||||
// of any previously returned unique references first, thus preventing aliasing.
|
// of any previously returned unique references first, thus preventing aliasing.
|
||||||
unsafe {
|
unsafe {
|
||||||
@ -374,7 +386,9 @@ struct QueryIterationCursor<'w, 's, Q: WorldQuery, QF: Fetch<'w, State = Q::Stat
|
|||||||
archetype_id_iter: std::slice::Iter<'s, ArchetypeId>,
|
archetype_id_iter: std::slice::Iter<'s, ArchetypeId>,
|
||||||
fetch: QF,
|
fetch: QF,
|
||||||
filter: QueryFetch<'w, F>,
|
filter: QueryFetch<'w, F>,
|
||||||
|
// length of the table table or length of the archetype, depending on whether both `Q`'s and `F`'s fetches are dense
|
||||||
current_len: usize,
|
current_len: usize,
|
||||||
|
// either table row or archetype index, depending on whether both `Q`'s and `F`'s fetches are dense
|
||||||
current_index: usize,
|
current_index: usize,
|
||||||
phantom: PhantomData<(&'w (), Q)>,
|
phantom: PhantomData<(&'w (), Q)>,
|
||||||
}
|
}
|
||||||
@ -461,6 +475,10 @@ where
|
|||||||
|
|
||||||
// NOTE: If you are changing query iteration code, remember to update the following places, where relevant:
|
// NOTE: If you are changing query iteration code, remember to update the following places, where relevant:
|
||||||
// QueryIterationCursor, QueryState::for_each_unchecked_manual, QueryState::par_for_each_unchecked_manual
|
// QueryIterationCursor, QueryState::for_each_unchecked_manual, QueryState::par_for_each_unchecked_manual
|
||||||
|
/// # Safety
|
||||||
|
/// `tables` and `archetypes` must belong to the same world that the [`QueryIterationCursor`]
|
||||||
|
/// was initialized for.
|
||||||
|
/// `query_state` must be the same [`QueryState`] that was passed to `init` or `init_empty`.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
unsafe fn next(
|
unsafe fn next(
|
||||||
&mut self,
|
&mut self,
|
||||||
@ -470,9 +488,12 @@ where
|
|||||||
) -> Option<QF::Item> {
|
) -> Option<QF::Item> {
|
||||||
if Self::IS_DENSE {
|
if Self::IS_DENSE {
|
||||||
loop {
|
loop {
|
||||||
|
// we are on the beginning of the query, or finished processing a table, so skip to the next
|
||||||
if self.current_index == self.current_len {
|
if self.current_index == self.current_len {
|
||||||
let table_id = self.table_id_iter.next()?;
|
let table_id = self.table_id_iter.next()?;
|
||||||
let table = &tables[*table_id];
|
let table = &tables[*table_id];
|
||||||
|
// 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
|
||||||
self.fetch.set_table(&query_state.fetch_state, table);
|
self.fetch.set_table(&query_state.fetch_state, table);
|
||||||
self.filter.set_table(&query_state.filter_state, table);
|
self.filter.set_table(&query_state.filter_state, table);
|
||||||
self.current_len = table.len();
|
self.current_len = table.len();
|
||||||
@ -480,11 +501,15 @@ where
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SAFETY: set_table was called prior.
|
||||||
|
// `current_index` is a table row in range of the current table, because if it was not, then the if above would have been executed.
|
||||||
if !self.filter.table_filter_fetch(self.current_index) {
|
if !self.filter.table_filter_fetch(self.current_index) {
|
||||||
self.current_index += 1;
|
self.current_index += 1;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SAFETY: set_table was called prior.
|
||||||
|
// `current_index` is a table row in range of the current table, because if it was not, then the if above would have been executed.
|
||||||
let item = self.fetch.table_fetch(self.current_index);
|
let item = self.fetch.table_fetch(self.current_index);
|
||||||
|
|
||||||
self.current_index += 1;
|
self.current_index += 1;
|
||||||
@ -495,6 +520,8 @@ where
|
|||||||
if self.current_index == self.current_len {
|
if self.current_index == self.current_len {
|
||||||
let archetype_id = self.archetype_id_iter.next()?;
|
let archetype_id = self.archetype_id_iter.next()?;
|
||||||
let archetype = &archetypes[*archetype_id];
|
let archetype = &archetypes[*archetype_id];
|
||||||
|
// 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
|
||||||
self.fetch
|
self.fetch
|
||||||
.set_archetype(&query_state.fetch_state, archetype, tables);
|
.set_archetype(&query_state.fetch_state, archetype, tables);
|
||||||
self.filter
|
self.filter
|
||||||
@ -504,11 +531,15 @@ where
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SAFETY: set_archetype was called prior.
|
||||||
|
// `current_index` is an archetype index row in range of the current archetype, because if it was not, then the if above would have been executed.
|
||||||
if !self.filter.archetype_filter_fetch(self.current_index) {
|
if !self.filter.archetype_filter_fetch(self.current_index) {
|
||||||
self.current_index += 1;
|
self.current_index += 1;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SAFETY: set_archetype was called prior, `current_index` is an archetype index in range of the current archetype
|
||||||
|
// `current_index` 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 item = self.fetch.archetype_fetch(self.current_index);
|
let item = self.fetch.archetype_fetch(self.current_index);
|
||||||
self.current_index += 1;
|
self.current_index += 1;
|
||||||
return Some(item);
|
return Some(item);
|
||||||
|
@ -81,7 +81,7 @@ impl<Q: WorldQuery, F: WorldQuery> QueryState<Q, F> {
|
|||||||
/// Checks if the query is empty for the given [`World`], where the last change and current tick are given.
|
/// Checks if the query is empty for the given [`World`], where the last change and current tick are given.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn is_empty(&self, world: &World, last_change_tick: u32, change_tick: u32) -> bool {
|
pub fn is_empty(&self, world: &World, last_change_tick: u32, change_tick: u32) -> bool {
|
||||||
// SAFE: NopFetch does not access any members while &self ensures no one has exclusive access
|
// SAFETY: NopFetch does not access any members while &self ensures no one has exclusive access
|
||||||
unsafe {
|
unsafe {
|
||||||
self.iter_unchecked_manual::<NopFetch<Q::State>>(world, last_change_tick, change_tick)
|
self.iter_unchecked_manual::<NopFetch<Q::State>>(world, last_change_tick, change_tick)
|
||||||
.next()
|
.next()
|
||||||
@ -211,7 +211,7 @@ impl<Q: WorldQuery, F: WorldQuery> QueryState<Q, F> {
|
|||||||
) -> Result<[ROQueryItem<'w, Q>; N], QueryEntityError> {
|
) -> Result<[ROQueryItem<'w, Q>; N], QueryEntityError> {
|
||||||
self.update_archetypes(world);
|
self.update_archetypes(world);
|
||||||
|
|
||||||
// SAFE: update_archetypes validates the `World` matches
|
// SAFETY: update_archetypes validates the `World` matches
|
||||||
unsafe {
|
unsafe {
|
||||||
self.get_many_read_only_manual(
|
self.get_many_read_only_manual(
|
||||||
world,
|
world,
|
||||||
@ -287,7 +287,7 @@ impl<Q: WorldQuery, F: WorldQuery> QueryState<Q, F> {
|
|||||||
) -> Result<[QueryItem<'w, Q>; N], QueryEntityError> {
|
) -> Result<[QueryItem<'w, Q>; N], QueryEntityError> {
|
||||||
self.update_archetypes(world);
|
self.update_archetypes(world);
|
||||||
|
|
||||||
// SAFE: method requires exclusive world access
|
// SAFETY: method requires exclusive world access
|
||||||
// and world has been validated via update_archetypes
|
// and world has been validated via update_archetypes
|
||||||
unsafe {
|
unsafe {
|
||||||
self.get_many_unchecked_manual(
|
self.get_many_unchecked_manual(
|
||||||
@ -397,7 +397,7 @@ impl<Q: WorldQuery, F: WorldQuery> QueryState<Q, F> {
|
|||||||
last_change_tick: u32,
|
last_change_tick: u32,
|
||||||
change_tick: u32,
|
change_tick: u32,
|
||||||
) -> Result<[ROQueryItem<'w, Q>; N], QueryEntityError> {
|
) -> Result<[ROQueryItem<'w, Q>; N], QueryEntityError> {
|
||||||
// SAFE: fetch is read-only
|
// SAFETY: fetch is read-only
|
||||||
// and world must be validated
|
// and world must be validated
|
||||||
let array_of_results = entities.map(|entity| {
|
let array_of_results = entities.map(|entity| {
|
||||||
self.get_unchecked_manual::<ROQueryFetch<'w, Q>>(
|
self.get_unchecked_manual::<ROQueryFetch<'w, Q>>(
|
||||||
@ -527,7 +527,7 @@ impl<Q: WorldQuery, F: WorldQuery> QueryState<Q, F> {
|
|||||||
&'s mut self,
|
&'s mut self,
|
||||||
world: &'w World,
|
world: &'w World,
|
||||||
) -> QueryCombinationIter<'w, 's, Q, F, K> {
|
) -> QueryCombinationIter<'w, 's, Q, F, K> {
|
||||||
// SAFE: query is read only
|
// SAFETY: query is read only
|
||||||
unsafe {
|
unsafe {
|
||||||
self.update_archetypes(world);
|
self.update_archetypes(world);
|
||||||
self.iter_combinations_unchecked_manual(
|
self.iter_combinations_unchecked_manual(
|
||||||
@ -550,7 +550,7 @@ impl<Q: WorldQuery, F: WorldQuery> QueryState<Q, F> {
|
|||||||
&'s mut self,
|
&'s mut self,
|
||||||
world: &'w mut World,
|
world: &'w mut World,
|
||||||
) -> QueryCombinationIter<'w, 's, Q, F, K> {
|
) -> QueryCombinationIter<'w, 's, Q, F, K> {
|
||||||
// SAFE: query has unique world access
|
// SAFETY: query has unique world access
|
||||||
unsafe {
|
unsafe {
|
||||||
self.update_archetypes(world);
|
self.update_archetypes(world);
|
||||||
self.iter_combinations_unchecked_manual(
|
self.iter_combinations_unchecked_manual(
|
||||||
@ -1272,6 +1272,8 @@ mod tests {
|
|||||||
// It's best to test get_many_unchecked_manual directly,
|
// It's best to test get_many_unchecked_manual directly,
|
||||||
// as it is shared and unsafe
|
// as it is shared and unsafe
|
||||||
// We don't care about aliased mutabilty for the read-only equivalent
|
// We don't care about aliased mutabilty for the read-only equivalent
|
||||||
|
|
||||||
|
// SAFETY: mutable access is not checked, but we own the world and don't use the query results
|
||||||
assert!(unsafe {
|
assert!(unsafe {
|
||||||
query_state
|
query_state
|
||||||
.get_many_unchecked_manual::<10>(
|
.get_many_unchecked_manual::<10>(
|
||||||
@ -1284,6 +1286,7 @@ mod tests {
|
|||||||
});
|
});
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
// SAFETY: mutable access is not checked, but we own the world and don't use the query results
|
||||||
unsafe {
|
unsafe {
|
||||||
query_state
|
query_state
|
||||||
.get_many_unchecked_manual(
|
.get_many_unchecked_manual(
|
||||||
@ -1298,6 +1301,7 @@ mod tests {
|
|||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
// SAFETY: mutable access is not checked, but we own the world and don't use the query results
|
||||||
unsafe {
|
unsafe {
|
||||||
query_state
|
query_state
|
||||||
.get_many_unchecked_manual(
|
.get_many_unchecked_manual(
|
||||||
@ -1312,6 +1316,7 @@ mod tests {
|
|||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
// SAFETY: mutable access is not checked, but we own the world and don't use the query results
|
||||||
unsafe {
|
unsafe {
|
||||||
query_state
|
query_state
|
||||||
.get_many_unchecked_manual(
|
.get_many_unchecked_manual(
|
||||||
|
@ -69,7 +69,7 @@ impl ReflectComponent {
|
|||||||
world: &'a mut World,
|
world: &'a mut World,
|
||||||
entity: Entity,
|
entity: Entity,
|
||||||
) -> Option<ReflectMut<'a>> {
|
) -> Option<ReflectMut<'a>> {
|
||||||
// SAFE: unique world access
|
// SAFETY: unique world access
|
||||||
unsafe { (self.reflect_component_mut)(world, entity) }
|
unsafe { (self.reflect_component_mut)(world, entity) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -137,14 +137,18 @@ impl<C: Component + Reflect + FromWorld> FromType<C> for ReflectComponent {
|
|||||||
.get::<C>()
|
.get::<C>()
|
||||||
.map(|c| c as &dyn Reflect)
|
.map(|c| c as &dyn Reflect)
|
||||||
},
|
},
|
||||||
reflect_component_mut: |world, entity| unsafe {
|
reflect_component_mut: |world, entity| {
|
||||||
world
|
// SAFETY: reflect_component_mut is an unsafe function pointer used by `reflect_component_unchecked_mut` which promises to never
|
||||||
.get_entity(entity)?
|
// produce aliasing mutable references, and reflect_component_mut, which has mutable world access
|
||||||
.get_unchecked_mut::<C>(world.last_change_tick(), world.read_change_tick())
|
unsafe {
|
||||||
.map(|c| ReflectMut {
|
world
|
||||||
value: c.value as &mut dyn Reflect,
|
.get_entity(entity)?
|
||||||
ticks: c.ticks,
|
.get_unchecked_mut::<C>(world.last_change_tick(), world.read_change_tick())
|
||||||
})
|
.map(|c| ReflectMut {
|
||||||
|
value: c.value as &mut dyn Reflect,
|
||||||
|
ticks: c.ticks,
|
||||||
|
})
|
||||||
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -191,7 +195,7 @@ impl ReflectResource {
|
|||||||
|
|
||||||
/// Gets the value of this [`Resource`] type from the world as a mutable reflected reference.
|
/// Gets the value of this [`Resource`] type from the world as a mutable reflected reference.
|
||||||
pub fn reflect_resource_mut<'a>(&self, world: &'a mut World) -> Option<ReflectMut<'a>> {
|
pub fn reflect_resource_mut<'a>(&self, world: &'a mut World) -> Option<ReflectMut<'a>> {
|
||||||
// SAFE: unique world access
|
// SAFETY: unique world access
|
||||||
unsafe { (self.reflect_resource_unchecked_mut)(world) }
|
unsafe { (self.reflect_resource_unchecked_mut)(world) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -205,6 +209,7 @@ impl ReflectResource {
|
|||||||
&self,
|
&self,
|
||||||
world: &'a World,
|
world: &'a World,
|
||||||
) -> Option<ReflectMut<'a>> {
|
) -> Option<ReflectMut<'a>> {
|
||||||
|
// SAFETY: caller promises to uphold uniqueness guarantees
|
||||||
(self.reflect_resource_unchecked_mut)(world)
|
(self.reflect_resource_unchecked_mut)(world)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -234,13 +239,17 @@ impl<C: Resource + Reflect + FromWorld> FromType<C> for ReflectResource {
|
|||||||
world.remove_resource::<C>();
|
world.remove_resource::<C>();
|
||||||
},
|
},
|
||||||
reflect_resource: |world| world.get_resource::<C>().map(|res| res as &dyn Reflect),
|
reflect_resource: |world| world.get_resource::<C>().map(|res| res as &dyn Reflect),
|
||||||
reflect_resource_unchecked_mut: |world| unsafe {
|
reflect_resource_unchecked_mut: |world| {
|
||||||
world
|
// SAFETY: all usages of `reflect_resource_unchecked_mut` guarantee that there is either a single mutable
|
||||||
.get_resource_unchecked_mut::<C>()
|
// reference or multiple immutable ones alive at any given point
|
||||||
.map(|res| ReflectMut {
|
unsafe {
|
||||||
value: res.value as &mut dyn Reflect,
|
world
|
||||||
ticks: res.ticks,
|
.get_resource_unchecked_mut::<C>()
|
||||||
})
|
.map(|res| ReflectMut {
|
||||||
|
value: res.value as &mut dyn Reflect,
|
||||||
|
ticks: res.ticks,
|
||||||
|
})
|
||||||
|
}
|
||||||
},
|
},
|
||||||
copy_resource: |source_world, destination_world| {
|
copy_resource: |source_world, destination_world| {
|
||||||
let source_resource = source_world.resource::<C>();
|
let source_resource = source_world.resource::<C>();
|
||||||
|
@ -190,6 +190,7 @@ impl ParallelExecutor {
|
|||||||
.unwrap_or_else(|error| unreachable!("{}", error));
|
.unwrap_or_else(|error| unreachable!("{}", error));
|
||||||
#[cfg(feature = "trace")]
|
#[cfg(feature = "trace")]
|
||||||
let system_guard = system_span.enter();
|
let system_guard = system_span.enter();
|
||||||
|
// SAFETY: the executor prevents two systems with conflicting access from running simultaneously.
|
||||||
unsafe { system.run_unsafe((), world) };
|
unsafe { system.run_unsafe((), world) };
|
||||||
#[cfg(feature = "trace")]
|
#[cfg(feature = "trace")]
|
||||||
drop(system_guard);
|
drop(system_guard);
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
use std::{
|
use std::{
|
||||||
alloc::{handle_alloc_error, Layout},
|
alloc::{handle_alloc_error, Layout},
|
||||||
cell::UnsafeCell,
|
cell::UnsafeCell,
|
||||||
|
num::NonZeroUsize,
|
||||||
ptr::NonNull,
|
ptr::NonNull,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -14,7 +15,9 @@ pub(super) struct BlobVec {
|
|||||||
capacity: usize,
|
capacity: usize,
|
||||||
/// Number of elements, not bytes
|
/// Number of elements, not bytes
|
||||||
len: usize,
|
len: usize,
|
||||||
|
// the `data` ptr's layout is always `array_layout(item_layout, capacity)`
|
||||||
data: NonNull<u8>,
|
data: NonNull<u8>,
|
||||||
|
// the `swap_scratch` ptr's layout is always `item_layout`
|
||||||
swap_scratch: NonNull<u8>,
|
swap_scratch: NonNull<u8>,
|
||||||
// None if the underlying type doesn't need to be dropped
|
// None if the underlying type doesn't need to be dropped
|
||||||
drop: Option<unsafe fn(OwningPtr<'_>)>,
|
drop: Option<unsafe fn(OwningPtr<'_>)>,
|
||||||
@ -93,33 +96,45 @@ impl BlobVec {
|
|||||||
|
|
||||||
pub fn reserve_exact(&mut self, additional: usize) {
|
pub fn reserve_exact(&mut self, additional: usize) {
|
||||||
let available_space = self.capacity - self.len;
|
let available_space = self.capacity - self.len;
|
||||||
if available_space < additional {
|
if available_space < additional && self.item_layout.size() > 0 {
|
||||||
self.grow_exact(additional - available_space);
|
// SAFETY: `available_space < additional`, so `additional - available_space > 0`
|
||||||
|
let increment = unsafe { NonZeroUsize::new_unchecked(additional - available_space) };
|
||||||
|
// SAFETY: not called for ZSTs
|
||||||
|
unsafe { self.grow_exact(increment) };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: this should probably be an unsafe fn as it shouldn't be called if the layout
|
// SAFETY: must not be called for a ZST item layout
|
||||||
// is for a ZST
|
#[warn(unsafe_op_in_unsafe_fn)] // to allow unsafe blocks in unsafe fn
|
||||||
fn grow_exact(&mut self, increment: usize) {
|
unsafe fn grow_exact(&mut self, increment: NonZeroUsize) {
|
||||||
debug_assert!(self.item_layout.size() != 0);
|
debug_assert!(self.item_layout.size() != 0);
|
||||||
|
|
||||||
let new_capacity = self.capacity + increment;
|
let new_capacity = self.capacity + increment.get();
|
||||||
let new_layout =
|
let new_layout =
|
||||||
array_layout(&self.item_layout, new_capacity).expect("array layout should be valid");
|
array_layout(&self.item_layout, new_capacity).expect("array layout should be valid");
|
||||||
unsafe {
|
let new_data = if self.capacity == 0 {
|
||||||
let new_data = if self.capacity == 0 {
|
// SAFETY:
|
||||||
std::alloc::alloc(new_layout)
|
// - layout has non-zero size as per safety requirement
|
||||||
} else {
|
unsafe { std::alloc::alloc(new_layout) }
|
||||||
|
} else {
|
||||||
|
// SAFETY:
|
||||||
|
// - ptr was be allocated via this allocator
|
||||||
|
// - the layout of the ptr was `array_layout(self.item_layout, self.capacity)`
|
||||||
|
// - `item_layout.size() > 0` and `new_capacity > 0`, so the layout size is non-zero
|
||||||
|
// - "new_size, when rounded up to the nearest multiple of layout.align(), must not overflow (i.e., the rounded value must be less than usize::MAX)",
|
||||||
|
// since the item size is always a multiple of its align, the rounding cannot happen
|
||||||
|
// here and the overflow is handled in `array_layout`
|
||||||
|
unsafe {
|
||||||
std::alloc::realloc(
|
std::alloc::realloc(
|
||||||
self.get_ptr_mut().as_ptr(),
|
self.get_ptr_mut().as_ptr(),
|
||||||
array_layout(&self.item_layout, self.capacity)
|
array_layout(&self.item_layout, self.capacity)
|
||||||
.expect("array layout should be valid"),
|
.expect("array layout should be valid"),
|
||||||
new_layout.size(),
|
new_layout.size(),
|
||||||
)
|
)
|
||||||
};
|
}
|
||||||
|
};
|
||||||
|
|
||||||
self.data = NonNull::new(new_data).unwrap_or_else(|| handle_alloc_error(new_layout));
|
self.data = NonNull::new(new_data).unwrap_or_else(|| handle_alloc_error(new_layout));
|
||||||
}
|
|
||||||
self.capacity = new_capacity;
|
self.capacity = new_capacity;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -274,14 +289,14 @@ impl BlobVec {
|
|||||||
/// Gets a [`Ptr`] to the start of the vec
|
/// Gets a [`Ptr`] to the start of the vec
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_ptr(&self) -> Ptr<'_> {
|
pub fn get_ptr(&self) -> Ptr<'_> {
|
||||||
// SAFE: the inner data will remain valid for as long as 'self.
|
// SAFETY: the inner data will remain valid for as long as 'self.
|
||||||
unsafe { Ptr::new(self.data) }
|
unsafe { Ptr::new(self.data) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets a [`PtrMut`] to the start of the vec
|
/// Gets a [`PtrMut`] to the start of the vec
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_ptr_mut(&mut self) -> PtrMut<'_> {
|
pub fn get_ptr_mut(&mut self) -> PtrMut<'_> {
|
||||||
// SAFE: the inner data will remain valid for as long as 'self.
|
// SAFETY: the inner data will remain valid for as long as 'self.
|
||||||
unsafe { PtrMut::new(self.data) }
|
unsafe { PtrMut::new(self.data) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -290,7 +305,7 @@ impl BlobVec {
|
|||||||
/// # Safety
|
/// # Safety
|
||||||
/// The type `T` must be the type of the items in this [`BlobVec`].
|
/// The type `T` must be the type of the items in this [`BlobVec`].
|
||||||
pub unsafe fn get_slice<T>(&self) -> &[UnsafeCell<T>] {
|
pub unsafe fn get_slice<T>(&self) -> &[UnsafeCell<T>] {
|
||||||
// SAFE: the inner data will remain valid for as long as 'self.
|
// 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)
|
std::slice::from_raw_parts(self.data.as_ptr() as *const UnsafeCell<T>, self.len)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -302,6 +317,7 @@ impl BlobVec {
|
|||||||
if let Some(drop) = self.drop {
|
if let Some(drop) = self.drop {
|
||||||
let layout_size = self.item_layout.size();
|
let layout_size = self.item_layout.size();
|
||||||
for i in 0..len {
|
for i in 0..len {
|
||||||
|
// SAFETY: `i * layout_size` is inbounds for the allocation, and the item is left unreachable so it can be safely promoted to an `OwningPtr`
|
||||||
unsafe {
|
unsafe {
|
||||||
// NOTE: this doesn't use self.get_unchecked(i) because the debug_assert on index
|
// NOTE: this doesn't use self.get_unchecked(i) because the debug_assert on index
|
||||||
// will panic here due to self.len being set to 0
|
// will panic here due to self.len being set to 0
|
||||||
@ -317,6 +333,7 @@ impl Drop for BlobVec {
|
|||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
self.clear();
|
self.clear();
|
||||||
if self.item_layout.size() > 0 {
|
if self.item_layout.size() > 0 {
|
||||||
|
// SAFETY: the `swap_scratch` pointer is always allocated using `self.item_layout`
|
||||||
unsafe {
|
unsafe {
|
||||||
std::alloc::dealloc(self.swap_scratch.as_ptr(), self.item_layout);
|
std::alloc::dealloc(self.swap_scratch.as_ptr(), self.item_layout);
|
||||||
}
|
}
|
||||||
@ -324,6 +341,7 @@ impl Drop for BlobVec {
|
|||||||
let array_layout =
|
let array_layout =
|
||||||
array_layout(&self.item_layout, self.capacity).expect("array layout should be valid");
|
array_layout(&self.item_layout, self.capacity).expect("array layout should be valid");
|
||||||
if array_layout.size() > 0 {
|
if array_layout.size() > 0 {
|
||||||
|
// SAFETY: data ptr layout is correct, swap_scratch ptr layout is correct
|
||||||
unsafe {
|
unsafe {
|
||||||
std::alloc::dealloc(self.get_ptr_mut().as_ptr(), array_layout);
|
std::alloc::dealloc(self.get_ptr_mut().as_ptr(), array_layout);
|
||||||
}
|
}
|
||||||
@ -427,8 +445,9 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn resize_test() {
|
fn resize_test() {
|
||||||
let item_layout = Layout::new::<usize>();
|
let item_layout = Layout::new::<usize>();
|
||||||
// usize doesn't need dropping
|
// SAFETY: `drop` fn is `None`, usize doesn't need dropping
|
||||||
let mut blob_vec = unsafe { BlobVec::new(item_layout, None, 64) };
|
let mut blob_vec = unsafe { BlobVec::new(item_layout, None, 64) };
|
||||||
|
// SAFETY: `i` is a usize, i.e. the type corresponding to `item_layout`
|
||||||
unsafe {
|
unsafe {
|
||||||
for i in 0..1_000 {
|
for i in 0..1_000 {
|
||||||
push(&mut blob_vec, i as usize);
|
push(&mut blob_vec, i as usize);
|
||||||
@ -458,8 +477,12 @@ mod tests {
|
|||||||
{
|
{
|
||||||
let item_layout = Layout::new::<Foo>();
|
let item_layout = Layout::new::<Foo>();
|
||||||
let drop = drop_ptr::<Foo>;
|
let drop = drop_ptr::<Foo>;
|
||||||
|
// SAFETY: drop is able to drop a value of its `item_layout`
|
||||||
let mut blob_vec = unsafe { BlobVec::new(item_layout, Some(drop), 2) };
|
let mut blob_vec = unsafe { BlobVec::new(item_layout, Some(drop), 2) };
|
||||||
assert_eq!(blob_vec.capacity(), 2);
|
assert_eq!(blob_vec.capacity(), 2);
|
||||||
|
// SAFETY: the following code only deals with values of type `Foo`, which satisfies the safety requirement of `push`, `get_mut` and `swap_remove` that the
|
||||||
|
// values have a layout compatible to the blob vec's `item_layout`.
|
||||||
|
// Every index is in range.
|
||||||
unsafe {
|
unsafe {
|
||||||
let foo1 = Foo {
|
let foo1 = Foo {
|
||||||
a: 42,
|
a: 42,
|
||||||
@ -518,6 +541,7 @@ mod tests {
|
|||||||
fn blob_vec_drop_empty_capacity() {
|
fn blob_vec_drop_empty_capacity() {
|
||||||
let item_layout = Layout::new::<Foo>();
|
let item_layout = Layout::new::<Foo>();
|
||||||
let drop = drop_ptr::<Foo>;
|
let drop = drop_ptr::<Foo>;
|
||||||
|
// SAFETY: drop is able to drop a value of its `item_layout`
|
||||||
let _ = unsafe { BlobVec::new(item_layout, Some(drop), 0) };
|
let _ = unsafe { BlobVec::new(item_layout, Some(drop), 0) };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -159,7 +159,7 @@ impl ComponentSparseSet {
|
|||||||
let dense_index = *dense_index as usize;
|
let dense_index = *dense_index as usize;
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
assert_eq!(entity, self.entities[dense_index]);
|
assert_eq!(entity, self.entities[dense_index]);
|
||||||
// SAFE: if the sparse index points to something in the dense vec, it exists
|
// SAFETY: if the sparse index points to something in the dense vec, it exists
|
||||||
unsafe { self.dense.get_data_unchecked(dense_index) }
|
unsafe { self.dense.get_data_unchecked(dense_index) }
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -169,7 +169,7 @@ impl ComponentSparseSet {
|
|||||||
let dense_index = *self.sparse.get(entity.id())? as usize;
|
let dense_index = *self.sparse.get(entity.id())? as usize;
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
assert_eq!(entity, self.entities[dense_index]);
|
assert_eq!(entity, self.entities[dense_index]);
|
||||||
// SAFE: if the sparse index points to something in the dense vec, it exists
|
// SAFETY: if the sparse index points to something in the dense vec, it exists
|
||||||
unsafe {
|
unsafe {
|
||||||
Some((
|
Some((
|
||||||
self.dense.get_data_unchecked(dense_index),
|
self.dense.get_data_unchecked(dense_index),
|
||||||
@ -183,7 +183,7 @@ impl ComponentSparseSet {
|
|||||||
let dense_index = *self.sparse.get(entity.id())? as usize;
|
let dense_index = *self.sparse.get(entity.id())? as usize;
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
assert_eq!(entity, self.entities[dense_index]);
|
assert_eq!(entity, self.entities[dense_index]);
|
||||||
// SAFE: if the sparse index points to something in the dense vec, it exists
|
// SAFETY: if the sparse index points to something in the dense vec, it exists
|
||||||
unsafe { Some(self.dense.get_ticks_unchecked(dense_index)) }
|
unsafe { Some(self.dense.get_ticks_unchecked(dense_index)) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -197,7 +197,7 @@ impl ComponentSparseSet {
|
|||||||
assert_eq!(entity, self.entities[dense_index]);
|
assert_eq!(entity, self.entities[dense_index]);
|
||||||
self.entities.swap_remove(dense_index);
|
self.entities.swap_remove(dense_index);
|
||||||
let is_last = dense_index == self.dense.len() - 1;
|
let is_last = dense_index == self.dense.len() - 1;
|
||||||
// SAFE: dense_index was just removed from `sparse`, which ensures that it is valid
|
// SAFETY: dense_index was just removed from `sparse`, which ensures that it is valid
|
||||||
let (value, _) = unsafe { self.dense.swap_remove_and_forget_unchecked(dense_index) };
|
let (value, _) = unsafe { self.dense.swap_remove_and_forget_unchecked(dense_index) };
|
||||||
if !is_last {
|
if !is_last {
|
||||||
let swapped_entity = self.entities[dense_index];
|
let swapped_entity = self.entities[dense_index];
|
||||||
@ -218,7 +218,7 @@ impl ComponentSparseSet {
|
|||||||
assert_eq!(entity, self.entities[dense_index]);
|
assert_eq!(entity, self.entities[dense_index]);
|
||||||
self.entities.swap_remove(dense_index);
|
self.entities.swap_remove(dense_index);
|
||||||
let is_last = dense_index == self.dense.len() - 1;
|
let is_last = dense_index == self.dense.len() - 1;
|
||||||
// SAFE: if the sparse index points to something in the dense vec, it exists
|
// SAFETY: if the sparse index points to something in the dense vec, it exists
|
||||||
unsafe { self.dense.swap_remove_unchecked(dense_index) }
|
unsafe { self.dense.swap_remove_unchecked(dense_index) }
|
||||||
if !is_last {
|
if !is_last {
|
||||||
let swapped_entity = self.entities[dense_index];
|
let swapped_entity = self.entities[dense_index];
|
||||||
@ -280,7 +280,7 @@ impl<I: SparseSetIndex, V> SparseSet<I, V> {
|
|||||||
|
|
||||||
pub fn insert(&mut self, index: I, value: V) {
|
pub fn insert(&mut self, index: I, value: V) {
|
||||||
if let Some(dense_index) = self.sparse.get(index.clone()).cloned() {
|
if let Some(dense_index) = self.sparse.get(index.clone()).cloned() {
|
||||||
// SAFE: dense indices stored in self.sparse always exist
|
// SAFETY: dense indices stored in self.sparse always exist
|
||||||
unsafe {
|
unsafe {
|
||||||
*self.dense.get_unchecked_mut(dense_index) = value;
|
*self.dense.get_unchecked_mut(dense_index) = value;
|
||||||
}
|
}
|
||||||
@ -293,7 +293,7 @@ impl<I: SparseSetIndex, V> SparseSet<I, V> {
|
|||||||
|
|
||||||
pub fn get_or_insert_with(&mut self, index: I, func: impl FnOnce() -> V) -> &mut V {
|
pub fn get_or_insert_with(&mut self, index: I, func: impl FnOnce() -> V) -> &mut V {
|
||||||
if let Some(dense_index) = self.sparse.get(index.clone()).cloned() {
|
if let Some(dense_index) = self.sparse.get(index.clone()).cloned() {
|
||||||
// SAFE: dense indices stored in self.sparse always exist
|
// SAFETY: dense indices stored in self.sparse always exist
|
||||||
unsafe { self.dense.get_unchecked_mut(dense_index) }
|
unsafe { self.dense.get_unchecked_mut(dense_index) }
|
||||||
} else {
|
} else {
|
||||||
let value = func();
|
let value = func();
|
||||||
@ -301,7 +301,7 @@ impl<I: SparseSetIndex, V> SparseSet<I, V> {
|
|||||||
self.sparse.insert(index.clone(), dense_index);
|
self.sparse.insert(index.clone(), dense_index);
|
||||||
self.indices.push(index);
|
self.indices.push(index);
|
||||||
self.dense.push(value);
|
self.dense.push(value);
|
||||||
// SAFE: dense index was just populated above
|
// SAFETY: dense index was just populated above
|
||||||
unsafe { self.dense.get_unchecked_mut(dense_index) }
|
unsafe { self.dense.get_unchecked_mut(dense_index) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -323,7 +323,7 @@ impl<I: SparseSetIndex, V> SparseSet<I, V> {
|
|||||||
|
|
||||||
pub fn get(&self, index: I) -> Option<&V> {
|
pub fn get(&self, index: I) -> Option<&V> {
|
||||||
self.sparse.get(index).map(|dense_index| {
|
self.sparse.get(index).map(|dense_index| {
|
||||||
// SAFE: if the sparse index points to something in the dense vec, it exists
|
// SAFETY: if the sparse index points to something in the dense vec, it exists
|
||||||
unsafe { self.dense.get_unchecked(*dense_index) }
|
unsafe { self.dense.get_unchecked(*dense_index) }
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -331,7 +331,7 @@ impl<I: SparseSetIndex, V> SparseSet<I, V> {
|
|||||||
pub fn get_mut(&mut self, index: I) -> Option<&mut V> {
|
pub fn get_mut(&mut self, index: I) -> Option<&mut V> {
|
||||||
let dense = &mut self.dense;
|
let dense = &mut self.dense;
|
||||||
self.sparse.get(index).map(move |dense_index| {
|
self.sparse.get(index).map(move |dense_index| {
|
||||||
// SAFE: if the sparse index points to something in the dense vec, it exists
|
// SAFETY: if the sparse index points to something in the dense vec, it exists
|
||||||
unsafe { dense.get_unchecked_mut(*dense_index) }
|
unsafe { dense.get_unchecked_mut(*dense_index) }
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -42,7 +42,7 @@ impl Column {
|
|||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn with_capacity(component_info: &ComponentInfo, capacity: usize) -> Self {
|
pub(crate) fn with_capacity(component_info: &ComponentInfo, capacity: usize) -> Self {
|
||||||
Column {
|
Column {
|
||||||
// SAFE: component_info.drop() is valid for the types that will be inserted.
|
// SAFETY: component_info.drop() is valid for the types that will be inserted.
|
||||||
data: unsafe { BlobVec::new(component_info.layout(), component_info.drop(), capacity) },
|
data: unsafe { BlobVec::new(component_info.layout(), component_info.drop(), capacity) },
|
||||||
ticks: Vec::with_capacity(capacity),
|
ticks: Vec::with_capacity(capacity),
|
||||||
}
|
}
|
||||||
@ -559,7 +559,7 @@ mod tests {
|
|||||||
table.add_column(components.get_info(component_id).unwrap());
|
table.add_column(components.get_info(component_id).unwrap());
|
||||||
let entities = (0..200).map(Entity::from_raw).collect::<Vec<_>>();
|
let entities = (0..200).map(Entity::from_raw).collect::<Vec<_>>();
|
||||||
for entity in &entities {
|
for entity in &entities {
|
||||||
// SAFE: we allocate and immediately set data afterwards
|
// SAFETY: we allocate and immediately set data afterwards
|
||||||
unsafe {
|
unsafe {
|
||||||
let row = table.allocate(*entity);
|
let row = table.allocate(*entity);
|
||||||
let value: W<usize> = W(row);
|
let value: W<usize> = W(row);
|
||||||
|
@ -21,10 +21,10 @@ pub struct CommandQueue {
|
|||||||
metas: Vec<CommandMeta>,
|
metas: Vec<CommandMeta>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// SAFE: All commands [`Command`] implement [`Send`]
|
// SAFETY: All commands [`Command`] implement [`Send`]
|
||||||
unsafe impl Send for CommandQueue {}
|
unsafe impl Send for CommandQueue {}
|
||||||
|
|
||||||
// SAFE: `&CommandQueue` never gives access to the inner commands.
|
// SAFETY: `&CommandQueue` never gives access to the inner commands.
|
||||||
unsafe impl Sync for CommandQueue {}
|
unsafe impl Sync for CommandQueue {}
|
||||||
|
|
||||||
impl CommandQueue {
|
impl CommandQueue {
|
||||||
@ -34,7 +34,7 @@ impl CommandQueue {
|
|||||||
where
|
where
|
||||||
C: Command,
|
C: Command,
|
||||||
{
|
{
|
||||||
/// SAFE: This function is only every called when the `command` bytes is the associated
|
/// SAFETY: This function is only every called when the `command` bytes is the associated
|
||||||
/// [`Commands`] `T` type. Also this only reads the data via `read_unaligned` so unaligned
|
/// [`Commands`] `T` type. Also this only reads the data via `read_unaligned` so unaligned
|
||||||
/// accesses are safe.
|
/// accesses are safe.
|
||||||
unsafe fn write_command<T: Command>(command: *mut MaybeUninit<u8>, world: &mut World) {
|
unsafe fn write_command<T: Command>(command: *mut MaybeUninit<u8>, world: &mut World) {
|
||||||
@ -57,7 +57,7 @@ impl CommandQueue {
|
|||||||
if size > 0 {
|
if size > 0 {
|
||||||
self.bytes.reserve(size);
|
self.bytes.reserve(size);
|
||||||
|
|
||||||
// SAFE: The internal `bytes` vector has enough storage for the
|
// SAFETY: The internal `bytes` vector has enough storage for the
|
||||||
// command (see the call the `reserve` above), the vector has
|
// command (see the call the `reserve` above), the vector has
|
||||||
// its length set appropriately and can contain any kind of bytes.
|
// its length set appropriately and can contain any kind of bytes.
|
||||||
// In case we're writing a ZST and the `Vec` hasn't allocated yet
|
// In case we're writing a ZST and the `Vec` hasn't allocated yet
|
||||||
@ -83,13 +83,13 @@ impl CommandQueue {
|
|||||||
// flush the previously queued entities
|
// flush the previously queued entities
|
||||||
world.flush();
|
world.flush();
|
||||||
|
|
||||||
// SAFE: In the iteration below, `meta.func` will safely consume and drop each pushed command.
|
// SAFETY: In the iteration below, `meta.func` will safely consume and drop each pushed command.
|
||||||
// This operation is so that we can reuse the bytes `Vec<u8>`'s internal storage and prevent
|
// This operation is so that we can reuse the bytes `Vec<u8>`'s internal storage and prevent
|
||||||
// unnecessary allocations.
|
// unnecessary allocations.
|
||||||
unsafe { self.bytes.set_len(0) };
|
unsafe { self.bytes.set_len(0) };
|
||||||
|
|
||||||
for meta in self.metas.drain(..) {
|
for meta in self.metas.drain(..) {
|
||||||
// SAFE: The implementation of `write_command` is safe for the according Command type.
|
// SAFETY: The implementation of `write_command` is safe for the according Command type.
|
||||||
// It's ok to read from `bytes.as_mut_ptr()` because we just wrote to it in `push`.
|
// It's ok to read from `bytes.as_mut_ptr()` because we just wrote to it in `push`.
|
||||||
// The bytes are safely cast to their original type, safely read, and then dropped.
|
// The bytes are safely cast to their original type, safely read, and then dropped.
|
||||||
unsafe {
|
unsafe {
|
||||||
|
@ -68,7 +68,7 @@ impl<'w, 's> SystemParamFetch<'w, 's> for ParallelCommandsState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// SAFE: no component or resource access to report
|
// SAFETY: no component or resource access to report
|
||||||
unsafe impl SystemParamState for ParallelCommandsState {
|
unsafe impl SystemParamState for ParallelCommandsState {
|
||||||
fn init(_: &mut World, _: &mut crate::system::SystemMeta) -> Self {
|
fn init(_: &mut World, _: &mut crate::system::SystemMeta) -> Self {
|
||||||
Self::default()
|
Self::default()
|
||||||
|
@ -166,7 +166,7 @@ impl<Param: SystemParam> SystemState<Param> {
|
|||||||
Param::Fetch: ReadOnlySystemParamFetch,
|
Param::Fetch: ReadOnlySystemParamFetch,
|
||||||
{
|
{
|
||||||
self.validate_world_and_update_archetypes(world);
|
self.validate_world_and_update_archetypes(world);
|
||||||
// SAFE: Param is read-only and doesn't allow mutable access to World. It also matches the World this SystemState was created with.
|
// SAFETY: Param is read-only and doesn't allow mutable access to World. It also matches the World this SystemState was created with.
|
||||||
unsafe { self.get_unchecked_manual(world) }
|
unsafe { self.get_unchecked_manual(world) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -177,7 +177,7 @@ impl<Param: SystemParam> SystemState<Param> {
|
|||||||
world: &'w mut World,
|
world: &'w mut World,
|
||||||
) -> <Param::Fetch as SystemParamFetch<'w, 's>>::Item {
|
) -> <Param::Fetch as SystemParamFetch<'w, 's>>::Item {
|
||||||
self.validate_world_and_update_archetypes(world);
|
self.validate_world_and_update_archetypes(world);
|
||||||
// SAFE: World is uniquely borrowed and matches the World this SystemState was created with.
|
// SAFETY: World is uniquely borrowed and matches the World this SystemState was created with.
|
||||||
unsafe { self.get_unchecked_manual(world) }
|
unsafe { self.get_unchecked_manual(world) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -293,7 +293,7 @@ impl<'w, 's, Q: WorldQuery, F: WorldQuery> Query<'w, 's, Q, F> {
|
|||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn iter(&self) -> QueryIter<'_, 's, Q, ROQueryFetch<'_, Q>, F> {
|
pub fn iter(&self) -> QueryIter<'_, 's, Q, ROQueryFetch<'_, Q>, F> {
|
||||||
// SAFE: system runs without conflicts with other systems.
|
// SAFETY: system runs without conflicts with other systems.
|
||||||
// same-system queries have runtime borrow checks when they conflict
|
// same-system queries have runtime borrow checks when they conflict
|
||||||
unsafe {
|
unsafe {
|
||||||
self.state
|
self.state
|
||||||
@ -323,7 +323,7 @@ impl<'w, 's, Q: WorldQuery, F: WorldQuery> Query<'w, 's, Q, F> {
|
|||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn iter_mut(&mut self) -> QueryIter<'_, '_, Q, QueryFetch<'_, Q>, F> {
|
pub fn iter_mut(&mut self) -> QueryIter<'_, '_, Q, QueryFetch<'_, Q>, F> {
|
||||||
// SAFE: system runs without conflicts with other systems.
|
// SAFETY: system runs without conflicts with other systems.
|
||||||
// same-system queries have runtime borrow checks when they conflict
|
// same-system queries have runtime borrow checks when they conflict
|
||||||
unsafe {
|
unsafe {
|
||||||
self.state
|
self.state
|
||||||
@ -340,7 +340,7 @@ impl<'w, 's, Q: WorldQuery, F: WorldQuery> Query<'w, 's, Q, F> {
|
|||||||
/// - if K > N: empty set (no K-sized combinations exist)
|
/// - if K > N: empty set (no K-sized combinations exist)
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn iter_combinations<const K: usize>(&self) -> QueryCombinationIter<'_, '_, Q, F, K> {
|
pub fn iter_combinations<const K: usize>(&self) -> QueryCombinationIter<'_, '_, Q, F, K> {
|
||||||
// SAFE: system runs without conflicts with other systems.
|
// SAFETY: system runs without conflicts with other systems.
|
||||||
// same-system queries have runtime borrow checks when they conflict
|
// same-system queries have runtime borrow checks when they conflict
|
||||||
unsafe {
|
unsafe {
|
||||||
self.state.iter_combinations_unchecked_manual(
|
self.state.iter_combinations_unchecked_manual(
|
||||||
@ -377,7 +377,7 @@ impl<'w, 's, Q: WorldQuery, F: WorldQuery> Query<'w, 's, Q, F> {
|
|||||||
pub fn iter_combinations_mut<const K: usize>(
|
pub fn iter_combinations_mut<const K: usize>(
|
||||||
&mut self,
|
&mut self,
|
||||||
) -> QueryCombinationIter<'_, '_, Q, F, K> {
|
) -> QueryCombinationIter<'_, '_, Q, F, K> {
|
||||||
// SAFE: system runs without conflicts with other systems.
|
// SAFETY: system runs without conflicts with other systems.
|
||||||
// same-system queries have runtime borrow checks when they conflict
|
// same-system queries have runtime borrow checks when they conflict
|
||||||
unsafe {
|
unsafe {
|
||||||
self.state.iter_combinations_unchecked_manual(
|
self.state.iter_combinations_unchecked_manual(
|
||||||
@ -446,7 +446,7 @@ impl<'w, 's, Q: WorldQuery, F: WorldQuery> Query<'w, 's, Q, F> {
|
|||||||
/// this call does not result in multiple mutable references to the same component
|
/// this call does not result in multiple mutable references to the same component
|
||||||
#[inline]
|
#[inline]
|
||||||
pub unsafe fn iter_unsafe(&'s self) -> QueryIter<'w, 's, Q, QueryFetch<'w, Q>, F> {
|
pub unsafe fn iter_unsafe(&'s self) -> QueryIter<'w, 's, Q, QueryFetch<'w, Q>, F> {
|
||||||
// SEMI-SAFE: system runs without conflicts with other systems.
|
// SEMI-SAFETY: system runs without conflicts with other systems.
|
||||||
// same-system queries have runtime borrow checks when they conflict
|
// same-system queries have runtime borrow checks when they conflict
|
||||||
self.state
|
self.state
|
||||||
.iter_unchecked_manual(self.world, self.last_change_tick, self.change_tick)
|
.iter_unchecked_manual(self.world, self.last_change_tick, self.change_tick)
|
||||||
@ -462,7 +462,7 @@ impl<'w, 's, Q: WorldQuery, F: WorldQuery> Query<'w, 's, Q, F> {
|
|||||||
pub unsafe fn iter_combinations_unsafe<const K: usize>(
|
pub unsafe fn iter_combinations_unsafe<const K: usize>(
|
||||||
&self,
|
&self,
|
||||||
) -> QueryCombinationIter<'_, '_, Q, F, K> {
|
) -> QueryCombinationIter<'_, '_, Q, F, K> {
|
||||||
// SEMI-SAFE: system runs without conflicts with other systems.
|
// SEMI-SAFETY: system runs without conflicts with other systems.
|
||||||
// same-system queries have runtime borrow checks when they conflict
|
// same-system queries have runtime borrow checks when they conflict
|
||||||
self.state.iter_combinations_unchecked_manual(
|
self.state.iter_combinations_unchecked_manual(
|
||||||
self.world,
|
self.world,
|
||||||
@ -519,7 +519,7 @@ impl<'w, 's, Q: WorldQuery, F: WorldQuery> Query<'w, 's, Q, F> {
|
|||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn for_each<'this>(&'this self, f: impl FnMut(ROQueryItem<'this, Q>)) {
|
pub fn for_each<'this>(&'this self, f: impl FnMut(ROQueryItem<'this, Q>)) {
|
||||||
// SAFE: system runs without conflicts with other systems.
|
// SAFETY: system runs without conflicts with other systems.
|
||||||
// same-system queries have runtime borrow checks when they conflict
|
// same-system queries have runtime borrow checks when they conflict
|
||||||
unsafe {
|
unsafe {
|
||||||
self.state.for_each_unchecked_manual::<ROQueryFetch<Q>, _>(
|
self.state.for_each_unchecked_manual::<ROQueryFetch<Q>, _>(
|
||||||
@ -554,7 +554,7 @@ impl<'w, 's, Q: WorldQuery, F: WorldQuery> Query<'w, 's, Q, F> {
|
|||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn for_each_mut<'a, FN: FnMut(QueryItem<'a, Q>)>(&'a mut self, f: FN) {
|
pub fn for_each_mut<'a, FN: FnMut(QueryItem<'a, Q>)>(&'a mut self, f: FN) {
|
||||||
// SAFE: system runs without conflicts with other systems. same-system queries have runtime
|
// SAFETY: system runs without conflicts with other systems. same-system queries have runtime
|
||||||
// borrow checks when they conflict
|
// borrow checks when they conflict
|
||||||
unsafe {
|
unsafe {
|
||||||
self.state.for_each_unchecked_manual::<QueryFetch<Q>, FN>(
|
self.state.for_each_unchecked_manual::<QueryFetch<Q>, FN>(
|
||||||
@ -597,7 +597,7 @@ impl<'w, 's, Q: WorldQuery, F: WorldQuery> Query<'w, 's, Q, F> {
|
|||||||
batch_size: usize,
|
batch_size: usize,
|
||||||
f: impl Fn(ROQueryItem<'this, Q>) + Send + Sync + Clone,
|
f: impl Fn(ROQueryItem<'this, Q>) + Send + Sync + Clone,
|
||||||
) {
|
) {
|
||||||
// SAFE: system runs without conflicts with other systems. same-system queries have runtime
|
// SAFETY: system runs without conflicts with other systems. same-system queries have runtime
|
||||||
// borrow checks when they conflict
|
// borrow checks when they conflict
|
||||||
unsafe {
|
unsafe {
|
||||||
self.state
|
self.state
|
||||||
@ -625,7 +625,7 @@ impl<'w, 's, Q: WorldQuery, F: WorldQuery> Query<'w, 's, Q, F> {
|
|||||||
batch_size: usize,
|
batch_size: usize,
|
||||||
f: FN,
|
f: FN,
|
||||||
) {
|
) {
|
||||||
// SAFE: system runs without conflicts with other systems. same-system queries have runtime
|
// SAFETY: system runs without conflicts with other systems. same-system queries have runtime
|
||||||
// borrow checks when they conflict
|
// borrow checks when they conflict
|
||||||
unsafe {
|
unsafe {
|
||||||
self.state
|
self.state
|
||||||
@ -675,7 +675,7 @@ impl<'w, 's, Q: WorldQuery, F: WorldQuery> Query<'w, 's, Q, F> {
|
|||||||
) where
|
) where
|
||||||
EntityList::Item: Borrow<Entity>,
|
EntityList::Item: Borrow<Entity>,
|
||||||
{
|
{
|
||||||
// SAFE: system runs without conflicts with other systems.
|
// SAFETY: system runs without conflicts with other systems.
|
||||||
// same-system queries have runtime borrow checks when they conflict
|
// same-system queries have runtime borrow checks when they conflict
|
||||||
unsafe {
|
unsafe {
|
||||||
self.state.many_for_each_unchecked_manual(
|
self.state.many_for_each_unchecked_manual(
|
||||||
@ -721,7 +721,7 @@ impl<'w, 's, Q: WorldQuery, F: WorldQuery> Query<'w, 's, Q, F> {
|
|||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get(&self, entity: Entity) -> Result<ROQueryItem<'_, Q>, QueryEntityError> {
|
pub fn get(&self, entity: Entity) -> Result<ROQueryItem<'_, Q>, QueryEntityError> {
|
||||||
// SAFE: system runs without conflicts with other systems.
|
// SAFETY: system runs without conflicts with other systems.
|
||||||
// same-system queries have runtime borrow checks when they conflict
|
// same-system queries have runtime borrow checks when they conflict
|
||||||
unsafe {
|
unsafe {
|
||||||
self.state.get_unchecked_manual::<ROQueryFetch<Q>>(
|
self.state.get_unchecked_manual::<ROQueryFetch<Q>>(
|
||||||
@ -746,7 +746,7 @@ impl<'w, 's, Q: WorldQuery, F: WorldQuery> Query<'w, 's, Q, F> {
|
|||||||
&self,
|
&self,
|
||||||
entities: [Entity; N],
|
entities: [Entity; N],
|
||||||
) -> Result<[ROQueryItem<'_, Q>; N], QueryEntityError> {
|
) -> Result<[ROQueryItem<'_, Q>; N], QueryEntityError> {
|
||||||
// SAFE: it is the scheduler's responsibility to ensure that `Query` is never handed out on the wrong `World`.
|
// SAFETY: it is the scheduler's responsibility to ensure that `Query` is never handed out on the wrong `World`.
|
||||||
unsafe {
|
unsafe {
|
||||||
self.state.get_many_read_only_manual(
|
self.state.get_many_read_only_manual(
|
||||||
self.world,
|
self.world,
|
||||||
@ -823,7 +823,7 @@ impl<'w, 's, Q: WorldQuery, F: WorldQuery> Query<'w, 's, Q, F> {
|
|||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_mut(&mut self, entity: Entity) -> Result<QueryItem<'_, Q>, QueryEntityError> {
|
pub fn get_mut(&mut self, entity: Entity) -> Result<QueryItem<'_, Q>, QueryEntityError> {
|
||||||
// SAFE: system runs without conflicts with other systems.
|
// SAFETY: system runs without conflicts with other systems.
|
||||||
// same-system queries have runtime borrow checks when they conflict
|
// same-system queries have runtime borrow checks when they conflict
|
||||||
unsafe {
|
unsafe {
|
||||||
self.state.get_unchecked_manual::<QueryFetch<Q>>(
|
self.state.get_unchecked_manual::<QueryFetch<Q>>(
|
||||||
@ -846,7 +846,7 @@ impl<'w, 's, Q: WorldQuery, F: WorldQuery> Query<'w, 's, Q, F> {
|
|||||||
&mut self,
|
&mut self,
|
||||||
entities: [Entity; N],
|
entities: [Entity; N],
|
||||||
) -> Result<[QueryItem<'_, Q>; N], QueryEntityError> {
|
) -> Result<[QueryItem<'_, Q>; N], QueryEntityError> {
|
||||||
// SAFE: scheduler ensures safe Query world access
|
// SAFETY: scheduler ensures safe Query world access
|
||||||
unsafe {
|
unsafe {
|
||||||
self.state.get_many_unchecked_manual(
|
self.state.get_many_unchecked_manual(
|
||||||
self.world,
|
self.world,
|
||||||
@ -917,7 +917,7 @@ impl<'w, 's, Q: WorldQuery, F: WorldQuery> Query<'w, 's, Q, F> {
|
|||||||
&'s self,
|
&'s self,
|
||||||
entity: Entity,
|
entity: Entity,
|
||||||
) -> Result<QueryItem<'w, Q>, QueryEntityError> {
|
) -> Result<QueryItem<'w, Q>, QueryEntityError> {
|
||||||
// SEMI-SAFE: system runs without conflicts with other systems.
|
// SEMI-SAFETY: system runs without conflicts with other systems.
|
||||||
// same-system queries have runtime borrow checks when they conflict
|
// same-system queries have runtime borrow checks when they conflict
|
||||||
self.state.get_unchecked_manual::<QueryFetch<Q>>(
|
self.state.get_unchecked_manual::<QueryFetch<Q>>(
|
||||||
self.world,
|
self.world,
|
||||||
@ -1011,7 +1011,7 @@ impl<'w, 's, Q: WorldQuery, F: WorldQuery> Query<'w, 's, Q, F> {
|
|||||||
&mut self,
|
&mut self,
|
||||||
entity: Entity,
|
entity: Entity,
|
||||||
) -> Result<Mut<'_, T>, QueryComponentError> {
|
) -> Result<Mut<'_, T>, QueryComponentError> {
|
||||||
// SAFE: unique access to query (preventing aliased access)
|
// SAFETY: unique access to query (preventing aliased access)
|
||||||
unsafe { self.get_component_unchecked_mut(entity) }
|
unsafe { self.get_component_unchecked_mut(entity) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1117,6 +1117,9 @@ impl<'w, 's, Q: WorldQuery, F: WorldQuery> Query<'w, 's, Q, F> {
|
|||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_single(&self) -> Result<ROQueryItem<'_, Q>, QuerySingleError> {
|
pub fn get_single(&self) -> Result<ROQueryItem<'_, Q>, QuerySingleError> {
|
||||||
|
// SAFETY:
|
||||||
|
// the query ensures that the components it accesses are not mutably accessible somewhere else
|
||||||
|
// and the query is read only.
|
||||||
unsafe {
|
unsafe {
|
||||||
self.state.get_single_unchecked_manual::<ROQueryFetch<Q>>(
|
self.state.get_single_unchecked_manual::<ROQueryFetch<Q>>(
|
||||||
self.world,
|
self.world,
|
||||||
@ -1179,6 +1182,9 @@ impl<'w, 's, Q: WorldQuery, F: WorldQuery> Query<'w, 's, Q, F> {
|
|||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_single_mut(&mut self) -> Result<QueryItem<'_, Q>, QuerySingleError> {
|
pub fn get_single_mut(&mut self) -> Result<QueryItem<'_, Q>, QuerySingleError> {
|
||||||
|
// SAFETY:
|
||||||
|
// the query ensures mutable access to the components it accesses, and the query
|
||||||
|
// is uniquely borrowed
|
||||||
unsafe {
|
unsafe {
|
||||||
self.state.get_single_unchecked_manual::<QueryFetch<Q>>(
|
self.state.get_single_unchecked_manual::<QueryFetch<Q>>(
|
||||||
self.world,
|
self.world,
|
||||||
@ -1238,7 +1244,7 @@ impl<'w, 's, Q: WorldQuery, F: WorldQuery> Query<'w, 's, Q, F> {
|
|||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn contains(&self, entity: Entity) -> bool {
|
pub fn contains(&self, entity: Entity) -> bool {
|
||||||
// SAFE: NopFetch does not access any members while &self ensures no one has exclusive access
|
// SAFETY: NopFetch does not access any members while &self ensures no one has exclusive access
|
||||||
unsafe {
|
unsafe {
|
||||||
self.state
|
self.state
|
||||||
.get_unchecked_manual::<NopFetch<Q::State>>(
|
.get_unchecked_manual::<NopFetch<Q::State>>(
|
||||||
@ -1340,7 +1346,7 @@ impl<'w, 's, Q: ReadOnlyWorldQuery, F: WorldQuery> Query<'w, 's, Q, F> {
|
|||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_inner(&'s self, entity: Entity) -> Result<ROQueryItem<'w, Q>, QueryEntityError> {
|
pub fn get_inner(&'s self, entity: Entity) -> Result<ROQueryItem<'w, Q>, QueryEntityError> {
|
||||||
// SAFE: system runs without conflicts with other systems.
|
// SAFETY: system runs without conflicts with other systems.
|
||||||
// same-system queries have runtime borrow checks when they conflict
|
// same-system queries have runtime borrow checks when they conflict
|
||||||
unsafe {
|
unsafe {
|
||||||
self.state.get_unchecked_manual::<ROQueryFetch<'w, Q>>(
|
self.state.get_unchecked_manual::<ROQueryFetch<'w, Q>>(
|
||||||
@ -1377,7 +1383,7 @@ impl<'w, 's, Q: ReadOnlyWorldQuery, F: WorldQuery> Query<'w, 's, Q, F> {
|
|||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn iter_inner(&'s self) -> QueryIter<'w, 's, Q, ROQueryFetch<'w, Q>, F> {
|
pub fn iter_inner(&'s self) -> QueryIter<'w, 's, Q, ROQueryFetch<'w, Q>, F> {
|
||||||
// SAFE: system runs without conflicts with other systems.
|
// SAFETY: system runs without conflicts with other systems.
|
||||||
// same-system queries have runtime borrow checks when they conflict
|
// same-system queries have runtime borrow checks when they conflict
|
||||||
unsafe {
|
unsafe {
|
||||||
self.state
|
self.state
|
||||||
|
@ -46,7 +46,7 @@ pub trait System: Send + Sync + 'static {
|
|||||||
/// Runs the system with the given input in the world.
|
/// Runs the system with the given input in the world.
|
||||||
fn run(&mut self, input: Self::In, world: &mut World) -> Self::Out {
|
fn run(&mut self, input: Self::In, world: &mut World) -> Self::Out {
|
||||||
self.update_archetype_component_access(world);
|
self.update_archetype_component_access(world);
|
||||||
// SAFE: world and resources are exclusively borrowed
|
// SAFETY: world and resources are exclusively borrowed
|
||||||
unsafe { self.run_unsafe(input, world) }
|
unsafe { self.run_unsafe(input, world) }
|
||||||
}
|
}
|
||||||
fn apply_buffers(&mut self, world: &mut World);
|
fn apply_buffers(&mut self, world: &mut World);
|
||||||
|
@ -134,10 +134,10 @@ impl<'w, 's, Q: WorldQuery + 'static, F: WorldQuery + 'static> SystemParam for Q
|
|||||||
type Fetch = QueryState<Q, F>;
|
type Fetch = QueryState<Q, F>;
|
||||||
}
|
}
|
||||||
|
|
||||||
// SAFE: QueryState is constrained to read-only fetches, so it only reads World.
|
// SAFETY: QueryState is constrained to read-only fetches, so it only reads World.
|
||||||
unsafe impl<Q: ReadOnlyWorldQuery, F: WorldQuery> ReadOnlySystemParamFetch for QueryState<Q, F> {}
|
unsafe impl<Q: ReadOnlyWorldQuery, F: WorldQuery> ReadOnlySystemParamFetch for QueryState<Q, F> {}
|
||||||
|
|
||||||
// SAFE: Relevant query ComponentId and ArchetypeComponentId access is applied to SystemMeta. If
|
// SAFETY: Relevant query ComponentId and ArchetypeComponentId access is applied to SystemMeta. If
|
||||||
// this QueryState conflicts with any prior access, a panic will occur.
|
// this QueryState conflicts with any prior access, a panic will occur.
|
||||||
unsafe impl<Q: WorldQuery + 'static, F: WorldQuery + 'static> SystemParamState
|
unsafe impl<Q: WorldQuery + 'static, F: WorldQuery + 'static> SystemParamState
|
||||||
for QueryState<Q, F>
|
for QueryState<Q, F>
|
||||||
@ -239,7 +239,7 @@ pub struct Res<'w, T: Resource> {
|
|||||||
change_tick: u32,
|
change_tick: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
// SAFE: Res only reads a single World resource
|
// SAFETY: Res only reads a single World resource
|
||||||
unsafe impl<T: Resource> ReadOnlySystemParamFetch for ResState<T> {}
|
unsafe impl<T: Resource> ReadOnlySystemParamFetch for ResState<T> {}
|
||||||
|
|
||||||
impl<'w, T: Resource> Debug for Res<'w, T>
|
impl<'w, T: Resource> Debug for Res<'w, T>
|
||||||
@ -305,7 +305,7 @@ impl<'a, T: Resource> SystemParam for Res<'a, T> {
|
|||||||
type Fetch = ResState<T>;
|
type Fetch = ResState<T>;
|
||||||
}
|
}
|
||||||
|
|
||||||
// SAFE: Res ComponentId and ArchetypeComponentId access is applied to SystemMeta. If this Res
|
// SAFETY: Res ComponentId and ArchetypeComponentId access is applied to SystemMeta. If this Res
|
||||||
// conflicts with any prior access, a panic will occur.
|
// conflicts with any prior access, a panic will occur.
|
||||||
unsafe impl<T: Resource> SystemParamState for ResState<T> {
|
unsafe impl<T: Resource> SystemParamState for ResState<T> {
|
||||||
fn init(world: &mut World, system_meta: &mut SystemMeta) -> Self {
|
fn init(world: &mut World, system_meta: &mut SystemMeta) -> Self {
|
||||||
@ -370,9 +370,11 @@ impl<'a, T: Resource> SystemParam for Option<Res<'a, T>> {
|
|||||||
type Fetch = OptionResState<T>;
|
type Fetch = OptionResState<T>;
|
||||||
}
|
}
|
||||||
|
|
||||||
// SAFE: Only reads a single World resource
|
// SAFETY: Only reads a single World resource
|
||||||
unsafe impl<T: Resource> ReadOnlySystemParamFetch for OptionResState<T> {}
|
unsafe impl<T: Resource> ReadOnlySystemParamFetch for OptionResState<T> {}
|
||||||
|
|
||||||
|
// SAFETY: this impl defers to `ResState`, which initializes
|
||||||
|
// and validates the correct world access
|
||||||
unsafe impl<T: Resource> SystemParamState for OptionResState<T> {
|
unsafe impl<T: Resource> SystemParamState for OptionResState<T> {
|
||||||
fn init(world: &mut World, system_meta: &mut SystemMeta) -> Self {
|
fn init(world: &mut World, system_meta: &mut SystemMeta) -> Self {
|
||||||
Self(ResState::init(world, system_meta))
|
Self(ResState::init(world, system_meta))
|
||||||
@ -411,7 +413,7 @@ impl<'a, T: Resource> SystemParam for ResMut<'a, T> {
|
|||||||
type Fetch = ResMutState<T>;
|
type Fetch = ResMutState<T>;
|
||||||
}
|
}
|
||||||
|
|
||||||
// SAFE: Res ComponentId and ArchetypeComponentId access is applied to SystemMeta. If this Res
|
// SAFETY: Res ComponentId and ArchetypeComponentId access is applied to SystemMeta. If this Res
|
||||||
// conflicts with any prior access, a panic will occur.
|
// conflicts with any prior access, a panic will occur.
|
||||||
unsafe impl<T: Resource> SystemParamState for ResMutState<T> {
|
unsafe impl<T: Resource> SystemParamState for ResMutState<T> {
|
||||||
fn init(world: &mut World, system_meta: &mut SystemMeta) -> Self {
|
fn init(world: &mut World, system_meta: &mut SystemMeta) -> Self {
|
||||||
@ -481,6 +483,8 @@ impl<'a, T: Resource> SystemParam for Option<ResMut<'a, T>> {
|
|||||||
type Fetch = OptionResMutState<T>;
|
type Fetch = OptionResMutState<T>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SAFETY: this impl defers to `ResMutState`, which initializes
|
||||||
|
// and validates the correct world access
|
||||||
unsafe impl<T: Resource> SystemParamState for OptionResMutState<T> {
|
unsafe impl<T: Resource> SystemParamState for OptionResMutState<T> {
|
||||||
fn init(world: &mut World, system_meta: &mut SystemMeta) -> Self {
|
fn init(world: &mut World, system_meta: &mut SystemMeta) -> Self {
|
||||||
Self(ResMutState::init(world, system_meta))
|
Self(ResMutState::init(world, system_meta))
|
||||||
@ -514,10 +518,10 @@ impl<'w, 's> SystemParam for Commands<'w, 's> {
|
|||||||
type Fetch = CommandQueue;
|
type Fetch = CommandQueue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// SAFE: Commands only accesses internal state
|
// SAFETY: Commands only accesses internal state
|
||||||
unsafe impl ReadOnlySystemParamFetch for CommandQueue {}
|
unsafe impl ReadOnlySystemParamFetch for CommandQueue {}
|
||||||
|
|
||||||
// SAFE: only local state is accessed
|
// SAFETY: only local state is accessed
|
||||||
unsafe impl SystemParamState for CommandQueue {
|
unsafe impl SystemParamState for CommandQueue {
|
||||||
fn init(_world: &mut World, _system_meta: &mut SystemMeta) -> Self {
|
fn init(_world: &mut World, _system_meta: &mut SystemMeta) -> Self {
|
||||||
Default::default()
|
Default::default()
|
||||||
@ -542,7 +546,7 @@ impl<'w, 's> SystemParamFetch<'w, 's> for CommandQueue {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// SAFE: only reads world
|
/// SAFETY: only reads world
|
||||||
unsafe impl ReadOnlySystemParamFetch for WorldState {}
|
unsafe impl ReadOnlySystemParamFetch for WorldState {}
|
||||||
|
|
||||||
/// The [`SystemParamState`] of [`&World`](crate::world::World).
|
/// The [`SystemParamState`] of [`&World`](crate::world::World).
|
||||||
@ -553,6 +557,7 @@ impl<'w> SystemParam for &'w World {
|
|||||||
type Fetch = WorldState;
|
type Fetch = WorldState;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SAFETY: `read_all` access is set and conflicts result in a panic
|
||||||
unsafe impl SystemParamState for WorldState {
|
unsafe impl SystemParamState for WorldState {
|
||||||
fn init(_world: &mut World, system_meta: &mut SystemMeta) -> Self {
|
fn init(_world: &mut World, system_meta: &mut SystemMeta) -> Self {
|
||||||
let mut access = Access::default();
|
let mut access = Access::default();
|
||||||
@ -637,7 +642,7 @@ impl<'w, 's> SystemParamFetch<'w, 's> for WorldState {
|
|||||||
/// ```
|
/// ```
|
||||||
pub struct Local<'a, T: Resource>(&'a mut T);
|
pub struct Local<'a, T: Resource>(&'a mut T);
|
||||||
|
|
||||||
// SAFE: Local only accesses internal state
|
// SAFETY: Local only accesses internal state
|
||||||
unsafe impl<T: Resource> ReadOnlySystemParamFetch for LocalState<T> {}
|
unsafe impl<T: Resource> ReadOnlySystemParamFetch for LocalState<T> {}
|
||||||
|
|
||||||
impl<'a, T: Resource> Debug for Local<'a, T>
|
impl<'a, T: Resource> Debug for Local<'a, T>
|
||||||
@ -673,7 +678,7 @@ impl<'a, T: Resource + FromWorld> SystemParam for Local<'a, T> {
|
|||||||
type Fetch = LocalState<T>;
|
type Fetch = LocalState<T>;
|
||||||
}
|
}
|
||||||
|
|
||||||
// SAFE: only local state is accessed
|
// SAFETY: only local state is accessed
|
||||||
unsafe impl<T: Resource + FromWorld> SystemParamState for LocalState<T> {
|
unsafe impl<T: Resource + FromWorld> SystemParamState for LocalState<T> {
|
||||||
fn init(world: &mut World, _system_meta: &mut SystemMeta) -> Self {
|
fn init(world: &mut World, _system_meta: &mut SystemMeta) -> Self {
|
||||||
Self(T::from_world(world))
|
Self(T::from_world(world))
|
||||||
@ -741,7 +746,7 @@ impl<'a, T: Component> RemovedComponents<'a, T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// SAFE: Only reads World components
|
// SAFETY: Only reads World components
|
||||||
unsafe impl<T: Component> ReadOnlySystemParamFetch for RemovedComponentsState<T> {}
|
unsafe impl<T: Component> ReadOnlySystemParamFetch for RemovedComponentsState<T> {}
|
||||||
|
|
||||||
/// The [`SystemParamState`] of [`RemovedComponents<T>`].
|
/// The [`SystemParamState`] of [`RemovedComponents<T>`].
|
||||||
@ -755,7 +760,7 @@ impl<'a, T: Component> SystemParam for RemovedComponents<'a, T> {
|
|||||||
type Fetch = RemovedComponentsState<T>;
|
type Fetch = RemovedComponentsState<T>;
|
||||||
}
|
}
|
||||||
|
|
||||||
// SAFE: no component access. removed component entity collections can be read in parallel and are
|
// SAFETY: no component access. removed component entity collections can be read in parallel and are
|
||||||
// never mutably borrowed during system execution
|
// never mutably borrowed during system execution
|
||||||
unsafe impl<T: Component> SystemParamState for RemovedComponentsState<T> {
|
unsafe impl<T: Component> SystemParamState for RemovedComponentsState<T> {
|
||||||
fn init(world: &mut World, _system_meta: &mut SystemMeta) -> Self {
|
fn init(world: &mut World, _system_meta: &mut SystemMeta) -> Self {
|
||||||
@ -803,7 +808,7 @@ pub struct NonSend<'w, T: 'static> {
|
|||||||
change_tick: u32,
|
change_tick: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
// SAFE: Only reads a single World non-send resource
|
// SAFETY: Only reads a single World non-send resource
|
||||||
unsafe impl<T> ReadOnlySystemParamFetch for NonSendState<T> {}
|
unsafe impl<T> ReadOnlySystemParamFetch for NonSendState<T> {}
|
||||||
|
|
||||||
impl<'w, T> Debug for NonSend<'w, T>
|
impl<'w, T> Debug for NonSend<'w, T>
|
||||||
@ -857,7 +862,7 @@ impl<'a, T: 'static> SystemParam for NonSend<'a, T> {
|
|||||||
type Fetch = NonSendState<T>;
|
type Fetch = NonSendState<T>;
|
||||||
}
|
}
|
||||||
|
|
||||||
// SAFE: NonSendComponentId and ArchetypeComponentId access is applied to SystemMeta. If this
|
// SAFETY: NonSendComponentId and ArchetypeComponentId access is applied to SystemMeta. If this
|
||||||
// NonSend conflicts with any prior access, a panic will occur.
|
// NonSend conflicts with any prior access, a panic will occur.
|
||||||
unsafe impl<T: 'static> SystemParamState for NonSendState<T> {
|
unsafe impl<T: 'static> SystemParamState for NonSendState<T> {
|
||||||
fn init(world: &mut World, system_meta: &mut SystemMeta) -> Self {
|
fn init(world: &mut World, system_meta: &mut SystemMeta) -> Self {
|
||||||
@ -926,9 +931,11 @@ impl<'w, T: 'static> SystemParam for Option<NonSend<'w, T>> {
|
|||||||
type Fetch = OptionNonSendState<T>;
|
type Fetch = OptionNonSendState<T>;
|
||||||
}
|
}
|
||||||
|
|
||||||
// SAFE: Only reads a single non-send resource
|
// SAFETY: Only reads a single non-send resource
|
||||||
unsafe impl<T: 'static> ReadOnlySystemParamFetch for OptionNonSendState<T> {}
|
unsafe impl<T: 'static> ReadOnlySystemParamFetch for OptionNonSendState<T> {}
|
||||||
|
|
||||||
|
// SAFETY: this impl defers to `NonSendState`, which initializes
|
||||||
|
// and validates the correct world access
|
||||||
unsafe impl<T: 'static> SystemParamState for OptionNonSendState<T> {
|
unsafe impl<T: 'static> SystemParamState for OptionNonSendState<T> {
|
||||||
fn init(world: &mut World, system_meta: &mut SystemMeta) -> Self {
|
fn init(world: &mut World, system_meta: &mut SystemMeta) -> Self {
|
||||||
Self(NonSendState::init(world, system_meta))
|
Self(NonSendState::init(world, system_meta))
|
||||||
@ -968,7 +975,7 @@ impl<'a, T: 'static> SystemParam for NonSendMut<'a, T> {
|
|||||||
type Fetch = NonSendMutState<T>;
|
type Fetch = NonSendMutState<T>;
|
||||||
}
|
}
|
||||||
|
|
||||||
// SAFE: NonSendMut ComponentId and ArchetypeComponentId access is applied to SystemMeta. If this
|
// SAFETY: NonSendMut ComponentId and ArchetypeComponentId access is applied to SystemMeta. If this
|
||||||
// NonSendMut conflicts with any prior access, a panic will occur.
|
// NonSendMut conflicts with any prior access, a panic will occur.
|
||||||
unsafe impl<T: 'static> SystemParamState for NonSendMutState<T> {
|
unsafe impl<T: 'static> SystemParamState for NonSendMutState<T> {
|
||||||
fn init(world: &mut World, system_meta: &mut SystemMeta) -> Self {
|
fn init(world: &mut World, system_meta: &mut SystemMeta) -> Self {
|
||||||
@ -1041,6 +1048,8 @@ impl<'a, T: 'static> SystemParam for Option<NonSendMut<'a, T>> {
|
|||||||
type Fetch = OptionNonSendMutState<T>;
|
type Fetch = OptionNonSendMutState<T>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SAFETY: this impl defers to `NonSendMutState`, which initializes
|
||||||
|
// and validates the correct world access
|
||||||
unsafe impl<T: 'static> SystemParamState for OptionNonSendMutState<T> {
|
unsafe impl<T: 'static> SystemParamState for OptionNonSendMutState<T> {
|
||||||
fn init(world: &mut World, system_meta: &mut SystemMeta) -> Self {
|
fn init(world: &mut World, system_meta: &mut SystemMeta) -> Self {
|
||||||
Self(NonSendMutState::init(world, system_meta))
|
Self(NonSendMutState::init(world, system_meta))
|
||||||
@ -1075,14 +1084,14 @@ impl<'a> SystemParam for &'a Archetypes {
|
|||||||
type Fetch = ArchetypesState;
|
type Fetch = ArchetypesState;
|
||||||
}
|
}
|
||||||
|
|
||||||
// SAFE: Only reads World archetypes
|
// SAFETY: Only reads World archetypes
|
||||||
unsafe impl ReadOnlySystemParamFetch for ArchetypesState {}
|
unsafe impl ReadOnlySystemParamFetch for ArchetypesState {}
|
||||||
|
|
||||||
/// The [`SystemParamState`] of [`Archetypes`].
|
/// The [`SystemParamState`] of [`Archetypes`].
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub struct ArchetypesState;
|
pub struct ArchetypesState;
|
||||||
|
|
||||||
// SAFE: no component value access
|
// SAFETY: no component value access
|
||||||
unsafe impl SystemParamState for ArchetypesState {
|
unsafe impl SystemParamState for ArchetypesState {
|
||||||
fn init(_world: &mut World, _system_meta: &mut SystemMeta) -> Self {
|
fn init(_world: &mut World, _system_meta: &mut SystemMeta) -> Self {
|
||||||
Self
|
Self
|
||||||
@ -1107,14 +1116,14 @@ impl<'a> SystemParam for &'a Components {
|
|||||||
type Fetch = ComponentsState;
|
type Fetch = ComponentsState;
|
||||||
}
|
}
|
||||||
|
|
||||||
// SAFE: Only reads World components
|
// SAFETY: Only reads World components
|
||||||
unsafe impl ReadOnlySystemParamFetch for ComponentsState {}
|
unsafe impl ReadOnlySystemParamFetch for ComponentsState {}
|
||||||
|
|
||||||
/// The [`SystemParamState`] of [`Components`].
|
/// The [`SystemParamState`] of [`Components`].
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub struct ComponentsState;
|
pub struct ComponentsState;
|
||||||
|
|
||||||
// SAFE: no component value access
|
// SAFETY: no component value access
|
||||||
unsafe impl SystemParamState for ComponentsState {
|
unsafe impl SystemParamState for ComponentsState {
|
||||||
fn init(_world: &mut World, _system_meta: &mut SystemMeta) -> Self {
|
fn init(_world: &mut World, _system_meta: &mut SystemMeta) -> Self {
|
||||||
Self
|
Self
|
||||||
@ -1139,14 +1148,14 @@ impl<'a> SystemParam for &'a Entities {
|
|||||||
type Fetch = EntitiesState;
|
type Fetch = EntitiesState;
|
||||||
}
|
}
|
||||||
|
|
||||||
// SAFE: Only reads World entities
|
// SAFETY: Only reads World entities
|
||||||
unsafe impl ReadOnlySystemParamFetch for EntitiesState {}
|
unsafe impl ReadOnlySystemParamFetch for EntitiesState {}
|
||||||
|
|
||||||
/// The [`SystemParamState`] of [`Entities`].
|
/// The [`SystemParamState`] of [`Entities`].
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub struct EntitiesState;
|
pub struct EntitiesState;
|
||||||
|
|
||||||
// SAFE: no component value access
|
// SAFETY: no component value access
|
||||||
unsafe impl SystemParamState for EntitiesState {
|
unsafe impl SystemParamState for EntitiesState {
|
||||||
fn init(_world: &mut World, _system_meta: &mut SystemMeta) -> Self {
|
fn init(_world: &mut World, _system_meta: &mut SystemMeta) -> Self {
|
||||||
Self
|
Self
|
||||||
@ -1171,14 +1180,14 @@ impl<'a> SystemParam for &'a Bundles {
|
|||||||
type Fetch = BundlesState;
|
type Fetch = BundlesState;
|
||||||
}
|
}
|
||||||
|
|
||||||
// SAFE: Only reads World bundles
|
// SAFETY: Only reads World bundles
|
||||||
unsafe impl ReadOnlySystemParamFetch for BundlesState {}
|
unsafe impl ReadOnlySystemParamFetch for BundlesState {}
|
||||||
|
|
||||||
/// The [`SystemParamState`] of [`Bundles`].
|
/// The [`SystemParamState`] of [`Bundles`].
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub struct BundlesState;
|
pub struct BundlesState;
|
||||||
|
|
||||||
// SAFE: no component value access
|
// SAFETY: no component value access
|
||||||
unsafe impl SystemParamState for BundlesState {
|
unsafe impl SystemParamState for BundlesState {
|
||||||
fn init(_world: &mut World, _system_meta: &mut SystemMeta) -> Self {
|
fn init(_world: &mut World, _system_meta: &mut SystemMeta) -> Self {
|
||||||
Self
|
Self
|
||||||
@ -1228,7 +1237,7 @@ impl SystemChangeTick {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// SAFE: Only reads internal system state
|
// SAFETY: Only reads internal system state
|
||||||
unsafe impl ReadOnlySystemParamFetch for SystemChangeTickState {}
|
unsafe impl ReadOnlySystemParamFetch for SystemChangeTickState {}
|
||||||
|
|
||||||
impl SystemParam for SystemChangeTick {
|
impl SystemParam for SystemChangeTick {
|
||||||
@ -1239,6 +1248,7 @@ impl SystemParam for SystemChangeTick {
|
|||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub struct SystemChangeTickState {}
|
pub struct SystemChangeTickState {}
|
||||||
|
|
||||||
|
// SAFETY: `SystemParamTickState` doesn't require any world access
|
||||||
unsafe impl SystemParamState for SystemChangeTickState {
|
unsafe impl SystemParamState for SystemChangeTickState {
|
||||||
fn init(_world: &mut World, _system_meta: &mut SystemMeta) -> Self {
|
fn init(_world: &mut World, _system_meta: &mut SystemMeta) -> Self {
|
||||||
Self {}
|
Self {}
|
||||||
@ -1267,7 +1277,7 @@ macro_rules! impl_system_param_tuple {
|
|||||||
type Fetch = ($($param::Fetch,)*);
|
type Fetch = ($($param::Fetch,)*);
|
||||||
}
|
}
|
||||||
|
|
||||||
// SAFE: tuple consists only of ReadOnlySystemParamFetches
|
// SAFETY: tuple consists only of ReadOnlySystemParamFetches
|
||||||
unsafe impl<$($param: ReadOnlySystemParamFetch),*> ReadOnlySystemParamFetch for ($($param,)*) {}
|
unsafe impl<$($param: ReadOnlySystemParamFetch),*> ReadOnlySystemParamFetch for ($($param,)*) {}
|
||||||
|
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
@ -1289,7 +1299,8 @@ macro_rules! impl_system_param_tuple {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// SAFE: implementors of each `SystemParamState` in the tuple have validated their impls
|
// SAFETY: implementors of each `SystemParamState` in the tuple have validated their impls
|
||||||
|
#[allow(clippy::undocumented_unsafe_blocks)] // false positive by clippy
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
unsafe impl<$($param: SystemParamState),*> SystemParamState for ($($param,)*) {
|
unsafe impl<$($param: SystemParamState),*> SystemParamState for ($($param,)*) {
|
||||||
#[inline]
|
#[inline]
|
||||||
@ -1403,7 +1414,7 @@ impl<'w, 's, P: SystemParam> StaticSystemParam<'w, 's, P> {
|
|||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub struct StaticSystemParamState<S, P>(S, PhantomData<fn() -> P>);
|
pub struct StaticSystemParamState<S, P>(S, PhantomData<fn() -> P>);
|
||||||
|
|
||||||
// Safe: This doesn't add any more reads, and the delegated fetch confirms it
|
// SAFETY: This doesn't add any more reads, and the delegated fetch confirms it
|
||||||
unsafe impl<S: ReadOnlySystemParamFetch, P> ReadOnlySystemParamFetch
|
unsafe impl<S: ReadOnlySystemParamFetch, P> ReadOnlySystemParamFetch
|
||||||
for StaticSystemParamState<S, P>
|
for StaticSystemParamState<S, P>
|
||||||
{
|
{
|
||||||
@ -1428,11 +1439,12 @@ where
|
|||||||
world: &'world World,
|
world: &'world World,
|
||||||
change_tick: u32,
|
change_tick: u32,
|
||||||
) -> Self::Item {
|
) -> Self::Item {
|
||||||
// Safe: We properly delegate SystemParamState
|
// SAFETY: We properly delegate SystemParamState
|
||||||
StaticSystemParam(S::get_param(&mut state.0, system_meta, world, change_tick))
|
StaticSystemParam(S::get_param(&mut state.0, system_meta, world, change_tick))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SAFETY: all methods are just delegated to `S`'s `SystemParamState` implementation
|
||||||
unsafe impl<S: SystemParamState, P: SystemParam + 'static> SystemParamState
|
unsafe impl<S: SystemParamState, P: SystemParam + 'static> SystemParamState
|
||||||
for StaticSystemParamState<S, P>
|
for StaticSystemParamState<S, P>
|
||||||
{
|
{
|
||||||
|
@ -65,7 +65,7 @@ impl<'w> EntityRef<'w> {
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get<T: Component>(&self) -> Option<&'w T> {
|
pub fn get<T: Component>(&self) -> Option<&'w T> {
|
||||||
// SAFE: entity location is valid and returned component is of type T
|
// SAFETY: entity location is valid and returned component is of type T
|
||||||
unsafe {
|
unsafe {
|
||||||
get_component_with_type(self.world, TypeId::of::<T>(), self.entity, self.location)
|
get_component_with_type(self.world, TypeId::of::<T>(), self.entity, self.location)
|
||||||
.map(|value| value.deref::<T>())
|
.map(|value| value.deref::<T>())
|
||||||
@ -76,7 +76,7 @@ impl<'w> EntityRef<'w> {
|
|||||||
/// detection in custom runtimes.
|
/// detection in custom runtimes.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_change_ticks<T: Component>(&self) -> Option<&'w ComponentTicks> {
|
pub fn get_change_ticks<T: Component>(&self) -> Option<&'w ComponentTicks> {
|
||||||
// SAFE: entity location is valid
|
// SAFETY: entity location is valid
|
||||||
unsafe {
|
unsafe {
|
||||||
get_ticks_with_type(self.world, TypeId::of::<T>(), self.entity, self.location)
|
get_ticks_with_type(self.world, TypeId::of::<T>(), self.entity, self.location)
|
||||||
.map(|ticks| ticks.deref())
|
.map(|ticks| ticks.deref())
|
||||||
@ -123,7 +123,7 @@ impl<'w> EntityRef<'w> {
|
|||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_by_id(&self, component_id: ComponentId) -> Option<Ptr<'w>> {
|
pub fn get_by_id(&self, component_id: ComponentId) -> Option<Ptr<'w>> {
|
||||||
self.world.components().get_info(component_id)?;
|
self.world.components().get_info(component_id)?;
|
||||||
// SAFE: entity_location is valid, component_id is valid as checked by the line above
|
// SAFETY: entity_location is valid, component_id is valid as checked by the line above
|
||||||
unsafe { get_component(self.world, component_id, self.entity, self.location) }
|
unsafe { get_component(self.world, component_id, self.entity, self.location) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -184,7 +184,7 @@ impl<'w> EntityMut<'w> {
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get<T: Component>(&self) -> Option<&'_ T> {
|
pub fn get<T: Component>(&self) -> Option<&'_ T> {
|
||||||
// SAFE: lifetimes enforce correct usage of returned borrow
|
// SAFETY: lifetimes enforce correct usage of returned borrow
|
||||||
unsafe {
|
unsafe {
|
||||||
get_component_with_type(self.world, TypeId::of::<T>(), self.entity, self.location)
|
get_component_with_type(self.world, TypeId::of::<T>(), self.entity, self.location)
|
||||||
.map(|value| value.deref::<T>())
|
.map(|value| value.deref::<T>())
|
||||||
@ -193,7 +193,7 @@ impl<'w> EntityMut<'w> {
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_mut<T: Component>(&mut self) -> Option<Mut<'_, T>> {
|
pub fn get_mut<T: Component>(&mut self) -> Option<Mut<'_, T>> {
|
||||||
// SAFE: world access is unique, and lifetimes enforce correct usage of returned borrow
|
// SAFETY: world access is unique, and lifetimes enforce correct usage of returned borrow
|
||||||
unsafe { self.get_unchecked_mut::<T>() }
|
unsafe { self.get_unchecked_mut::<T>() }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -201,7 +201,7 @@ impl<'w> EntityMut<'w> {
|
|||||||
/// detection in custom runtimes.
|
/// detection in custom runtimes.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_change_ticks<T: Component>(&self) -> Option<&ComponentTicks> {
|
pub fn get_change_ticks<T: Component>(&self) -> Option<&ComponentTicks> {
|
||||||
// SAFE: entity location is valid
|
// SAFETY: entity location is valid
|
||||||
unsafe {
|
unsafe {
|
||||||
get_ticks_with_type(self.world, TypeId::of::<T>(), self.entity, self.location)
|
get_ticks_with_type(self.world, TypeId::of::<T>(), self.entity, self.location)
|
||||||
.map(|ticks| ticks.deref())
|
.map(|ticks| ticks.deref())
|
||||||
@ -245,7 +245,7 @@ impl<'w> EntityMut<'w> {
|
|||||||
self.location.archetype_id,
|
self.location.archetype_id,
|
||||||
change_tick,
|
change_tick,
|
||||||
);
|
);
|
||||||
// SAFE: location matches current entity. `T` matches `bundle_info`
|
// SAFETY: location matches current entity. `T` matches `bundle_info`
|
||||||
unsafe {
|
unsafe {
|
||||||
self.location = bundle_inserter.insert(self.entity, self.location.index, bundle);
|
self.location = bundle_inserter.insert(self.entity, self.location.index, bundle);
|
||||||
}
|
}
|
||||||
@ -263,6 +263,8 @@ impl<'w> EntityMut<'w> {
|
|||||||
|
|
||||||
let bundle_info = self.world.bundles.init_info::<T>(components, storages);
|
let bundle_info = self.world.bundles.init_info::<T>(components, storages);
|
||||||
let old_location = self.location;
|
let old_location = self.location;
|
||||||
|
// SAFETY: `archetype_id` exists because it is referenced in the old `EntityLocation` which is valid,
|
||||||
|
// components exist in `bundle_info` because `Bundles::init_info` initializes a `BundleInfo` containing all components of the bundle type `T`
|
||||||
let new_archetype_id = unsafe {
|
let new_archetype_id = unsafe {
|
||||||
remove_bundle_from_archetype(
|
remove_bundle_from_archetype(
|
||||||
archetypes,
|
archetypes,
|
||||||
@ -281,12 +283,12 @@ impl<'w> EntityMut<'w> {
|
|||||||
let old_archetype = &mut archetypes[old_location.archetype_id];
|
let old_archetype = &mut archetypes[old_location.archetype_id];
|
||||||
let mut bundle_components = bundle_info.component_ids.iter().cloned();
|
let mut bundle_components = bundle_info.component_ids.iter().cloned();
|
||||||
let entity = self.entity;
|
let entity = self.entity;
|
||||||
// SAFE: bundle components are iterated in order, which guarantees that the component type
|
// SAFETY: bundle components are iterated in order, which guarantees that the component type
|
||||||
// matches
|
// matches
|
||||||
let result = unsafe {
|
let result = unsafe {
|
||||||
T::from_components(storages, |storages| {
|
T::from_components(storages, |storages| {
|
||||||
let component_id = bundle_components.next().unwrap();
|
let component_id = bundle_components.next().unwrap();
|
||||||
// SAFE: entity location is valid and table row is removed below
|
// SAFETY: entity location is valid and table row is removed below
|
||||||
take_component(
|
take_component(
|
||||||
components,
|
components,
|
||||||
storages,
|
storages,
|
||||||
@ -299,6 +301,7 @@ impl<'w> EntityMut<'w> {
|
|||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[allow(clippy::undocumented_unsafe_blocks)] // TODO: document why this is safe
|
||||||
unsafe {
|
unsafe {
|
||||||
Self::move_entity_from_remove::<false>(
|
Self::move_entity_from_remove::<false>(
|
||||||
entity,
|
entity,
|
||||||
@ -350,14 +353,14 @@ impl<'w> EntityMut<'w> {
|
|||||||
.tables
|
.tables
|
||||||
.get_2_mut(old_table_id, new_archetype.table_id());
|
.get_2_mut(old_table_id, new_archetype.table_id());
|
||||||
|
|
||||||
// SAFE: old_table_row exists
|
// SAFETY: old_table_row exists
|
||||||
let move_result = if DROP {
|
let move_result = if DROP {
|
||||||
old_table.move_to_and_drop_missing_unchecked(old_table_row, new_table)
|
old_table.move_to_and_drop_missing_unchecked(old_table_row, new_table)
|
||||||
} else {
|
} else {
|
||||||
old_table.move_to_and_forget_missing_unchecked(old_table_row, new_table)
|
old_table.move_to_and_forget_missing_unchecked(old_table_row, new_table)
|
||||||
};
|
};
|
||||||
|
|
||||||
// SAFE: move_result.new_row is a valid position in new_archetype's 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 = new_archetype.allocate(entity, move_result.new_row);
|
||||||
|
|
||||||
// if an entity was moved into this entity's table spot, update its table row
|
// if an entity was moved into this entity's table spot, update its table row
|
||||||
@ -385,6 +388,9 @@ impl<'w> EntityMut<'w> {
|
|||||||
|
|
||||||
let bundle_info = self.world.bundles.init_info::<T>(components, storages);
|
let bundle_info = self.world.bundles.init_info::<T>(components, storages);
|
||||||
let old_location = self.location;
|
let old_location = self.location;
|
||||||
|
|
||||||
|
// SAFETY: `archetype_id` exists because it is referenced in the old `EntityLocation` which is valid,
|
||||||
|
// components exist in `bundle_info` because `Bundles::init_info` initializes a `BundleInfo` containing all components of the bundle type `T`
|
||||||
let new_archetype_id = unsafe {
|
let new_archetype_id = unsafe {
|
||||||
remove_bundle_from_archetype(
|
remove_bundle_from_archetype(
|
||||||
archetypes,
|
archetypes,
|
||||||
@ -421,6 +427,7 @@ impl<'w> EntityMut<'w> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::undocumented_unsafe_blocks)] // TODO: document why this is safe
|
||||||
unsafe {
|
unsafe {
|
||||||
Self::move_entity_from_remove::<true>(
|
Self::move_entity_from_remove::<true>(
|
||||||
entity,
|
entity,
|
||||||
@ -470,7 +477,7 @@ impl<'w> EntityMut<'w> {
|
|||||||
let sparse_set = world.storages.sparse_sets.get_mut(*component_id).unwrap();
|
let sparse_set = world.storages.sparse_sets.get_mut(*component_id).unwrap();
|
||||||
sparse_set.remove(self.entity);
|
sparse_set.remove(self.entity);
|
||||||
}
|
}
|
||||||
// SAFE: table rows stored in archetypes always exist
|
// SAFETY: table rows stored in archetypes always exist
|
||||||
moved_entity = unsafe {
|
moved_entity = unsafe {
|
||||||
world.storages.tables[archetype.table_id()].swap_remove_unchecked(table_row)
|
world.storages.tables[archetype.table_id()].swap_remove_unchecked(table_row)
|
||||||
};
|
};
|
||||||
@ -517,7 +524,7 @@ impl<'w> EntityMut<'w> {
|
|||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_by_id(&self, component_id: ComponentId) -> Option<Ptr<'_>> {
|
pub fn get_by_id(&self, component_id: ComponentId) -> Option<Ptr<'_>> {
|
||||||
self.world.components().get_info(component_id)?;
|
self.world.components().get_info(component_id)?;
|
||||||
// SAFE: entity_location is valid, component_id is valid as checked by the line above
|
// SAFETY: entity_location is valid, component_id is valid as checked by the line above
|
||||||
unsafe { get_component(self.world, component_id, self.entity, self.location) }
|
unsafe { get_component(self.world, component_id, self.entity, self.location) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -532,7 +539,7 @@ impl<'w> EntityMut<'w> {
|
|||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_mut_by_id(&mut self, component_id: ComponentId) -> Option<MutUntyped<'_>> {
|
pub fn get_mut_by_id(&mut self, component_id: ComponentId) -> Option<MutUntyped<'_>> {
|
||||||
self.world.components().get_info(component_id)?;
|
self.world.components().get_info(component_id)?;
|
||||||
// SAFE: entity_location is valid, component_id is valid as checked by the line above
|
// SAFETY: entity_location is valid, component_id is valid as checked by the line above
|
||||||
unsafe { get_mut_by_id(self.world, self.entity, self.location, component_id) }
|
unsafe { get_mut_by_id(self.world, self.entity, self.location, component_id) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -552,14 +559,14 @@ pub(crate) unsafe fn get_component(
|
|||||||
location: EntityLocation,
|
location: EntityLocation,
|
||||||
) -> Option<Ptr<'_>> {
|
) -> Option<Ptr<'_>> {
|
||||||
let archetype = &world.archetypes[location.archetype_id];
|
let archetype = &world.archetypes[location.archetype_id];
|
||||||
// SAFE: component_id exists and is therefore valid
|
// SAFETY: component_id exists and is therefore valid
|
||||||
let component_info = world.components.get_info_unchecked(component_id);
|
let component_info = world.components.get_info_unchecked(component_id);
|
||||||
match component_info.storage_type() {
|
match component_info.storage_type() {
|
||||||
StorageType::Table => {
|
StorageType::Table => {
|
||||||
let table = &world.storages.tables[archetype.table_id()];
|
let table = &world.storages.tables[archetype.table_id()];
|
||||||
let components = table.get_column(component_id)?;
|
let components = table.get_column(component_id)?;
|
||||||
let table_row = archetype.entity_table_row(location.index);
|
let table_row = archetype.entity_table_row(location.index);
|
||||||
// SAFE: archetypes only store valid table_rows and the stored component type is T
|
// SAFETY: archetypes only store valid table_rows and the stored component type is T
|
||||||
Some(components.get_data_unchecked(table_row))
|
Some(components.get_data_unchecked(table_row))
|
||||||
}
|
}
|
||||||
StorageType::SparseSet => world
|
StorageType::SparseSet => world
|
||||||
@ -589,7 +596,7 @@ unsafe fn get_component_and_ticks(
|
|||||||
let table = &world.storages.tables[archetype.table_id()];
|
let table = &world.storages.tables[archetype.table_id()];
|
||||||
let components = table.get_column(component_id)?;
|
let components = table.get_column(component_id)?;
|
||||||
let table_row = archetype.entity_table_row(location.index);
|
let table_row = archetype.entity_table_row(location.index);
|
||||||
// SAFE: archetypes only store valid table_rows and the stored component type is T
|
// SAFETY: archetypes only store valid table_rows and the stored component type is T
|
||||||
Some((
|
Some((
|
||||||
components.get_data_unchecked(table_row),
|
components.get_data_unchecked(table_row),
|
||||||
components.get_ticks_unchecked(table_row),
|
components.get_ticks_unchecked(table_row),
|
||||||
@ -617,7 +624,7 @@ unsafe fn get_ticks(
|
|||||||
let table = &world.storages.tables[archetype.table_id()];
|
let table = &world.storages.tables[archetype.table_id()];
|
||||||
let components = table.get_column(component_id)?;
|
let components = table.get_column(component_id)?;
|
||||||
let table_row = archetype.entity_table_row(location.index);
|
let table_row = archetype.entity_table_row(location.index);
|
||||||
// SAFE: archetypes only store valid table_rows and the stored component type is T
|
// SAFETY: archetypes only store valid table_rows and the stored component type is T
|
||||||
Some(components.get_ticks_unchecked(table_row))
|
Some(components.get_ticks_unchecked(table_row))
|
||||||
}
|
}
|
||||||
StorageType::SparseSet => world
|
StorageType::SparseSet => world
|
||||||
@ -655,10 +662,10 @@ unsafe fn take_component<'a>(
|
|||||||
match component_info.storage_type() {
|
match component_info.storage_type() {
|
||||||
StorageType::Table => {
|
StorageType::Table => {
|
||||||
let table = &mut storages.tables[archetype.table_id()];
|
let table = &mut storages.tables[archetype.table_id()];
|
||||||
// SAFE: archetypes will always point to valid columns
|
// SAFETY: archetypes will always point to valid columns
|
||||||
let components = table.get_column_mut(component_id).unwrap();
|
let components = table.get_column_mut(component_id).unwrap();
|
||||||
let table_row = archetype.entity_table_row(location.index);
|
let table_row = archetype.entity_table_row(location.index);
|
||||||
// SAFE: archetypes only store valid table_rows and the stored component type is T
|
// SAFETY: archetypes only store valid table_rows and the stored component type is T
|
||||||
components.get_data_unchecked_mut(table_row).promote()
|
components.get_data_unchecked_mut(table_row).promote()
|
||||||
}
|
}
|
||||||
StorageType::SparseSet => storages
|
StorageType::SparseSet => storages
|
||||||
@ -768,7 +775,7 @@ unsafe fn remove_bundle_from_archetype(
|
|||||||
let mut removed_sparse_set_components = Vec::new();
|
let mut removed_sparse_set_components = Vec::new();
|
||||||
for component_id in bundle_info.component_ids.iter().cloned() {
|
for component_id in bundle_info.component_ids.iter().cloned() {
|
||||||
if current_archetype.contains(component_id) {
|
if current_archetype.contains(component_id) {
|
||||||
// SAFE: bundle components were already initialized by bundles.get_info
|
// SAFETY: bundle components were already initialized by bundles.get_info
|
||||||
let component_info = components.get_info_unchecked(component_id);
|
let component_info = components.get_info_unchecked(component_id);
|
||||||
match component_info.storage_type() {
|
match component_info.storage_type() {
|
||||||
StorageType::Table => removed_table_components.push(component_id),
|
StorageType::Table => removed_table_components.push(component_id),
|
||||||
@ -800,7 +807,7 @@ unsafe fn remove_bundle_from_archetype(
|
|||||||
next_table_id = if removed_table_components.is_empty() {
|
next_table_id = if removed_table_components.is_empty() {
|
||||||
current_archetype.table_id()
|
current_archetype.table_id()
|
||||||
} else {
|
} else {
|
||||||
// SAFE: all components in next_table_components exist
|
// SAFETY: all components in next_table_components exist
|
||||||
storages
|
storages
|
||||||
.tables
|
.tables
|
||||||
.get_id_or_insert(&next_table_components, components)
|
.get_id_or_insert(&next_table_components, components)
|
||||||
@ -850,7 +857,7 @@ pub(crate) unsafe fn get_mut<T: Component>(
|
|||||||
entity: Entity,
|
entity: Entity,
|
||||||
location: EntityLocation,
|
location: EntityLocation,
|
||||||
) -> Option<Mut<'_, T>> {
|
) -> Option<Mut<'_, T>> {
|
||||||
// SAFE: world access is unique, entity location is valid, and returned component is of type
|
// SAFETY: world access is unique, entity location is valid, and returned component is of type
|
||||||
// T
|
// T
|
||||||
let change_tick = world.change_tick();
|
let change_tick = world.change_tick();
|
||||||
let last_change_tick = world.last_change_tick();
|
let last_change_tick = world.last_change_tick();
|
||||||
@ -874,7 +881,7 @@ pub(crate) unsafe fn get_mut_by_id(
|
|||||||
location: EntityLocation,
|
location: EntityLocation,
|
||||||
component_id: ComponentId,
|
component_id: ComponentId,
|
||||||
) -> Option<MutUntyped> {
|
) -> Option<MutUntyped> {
|
||||||
// SAFE: world access is unique, entity location and component_id required to be valid
|
// SAFETY: world access is unique, entity location and component_id required to be valid
|
||||||
get_component_and_ticks(world, component_id, entity, location).map(|(value, ticks)| {
|
get_component_and_ticks(world, component_id, entity, location).map(|(value, ticks)| {
|
||||||
MutUntyped {
|
MutUntyped {
|
||||||
value: value.assert_unique(),
|
value: value.assert_unique(),
|
||||||
@ -928,7 +935,7 @@ mod tests {
|
|||||||
|
|
||||||
let entity = world.entity(entity);
|
let entity = world.entity(entity);
|
||||||
let test_component = entity.get_by_id(component_id).unwrap();
|
let test_component = entity.get_by_id(component_id).unwrap();
|
||||||
// SAFE: points to a valid `TestComponent`
|
// SAFETY: points to a valid `TestComponent`
|
||||||
let test_component = unsafe { test_component.deref::<TestComponent>() };
|
let test_component = unsafe { test_component.deref::<TestComponent>() };
|
||||||
|
|
||||||
assert_eq!(test_component.0, 42);
|
assert_eq!(test_component.0, 42);
|
||||||
@ -947,14 +954,15 @@ mod tests {
|
|||||||
let mut test_component = entity_mut.get_mut_by_id(component_id).unwrap();
|
let mut test_component = entity_mut.get_mut_by_id(component_id).unwrap();
|
||||||
{
|
{
|
||||||
test_component.set_changed();
|
test_component.set_changed();
|
||||||
// SAFE: `test_component` has unique access of the `EntityMut` and is not used afterwards
|
|
||||||
let test_component =
|
let test_component =
|
||||||
|
// SAFETY: `test_component` has unique access of the `EntityMut` and is not used afterwards
|
||||||
unsafe { test_component.into_inner().deref_mut::<TestComponent>() };
|
unsafe { test_component.into_inner().deref_mut::<TestComponent>() };
|
||||||
test_component.0 = 43;
|
test_component.0 = 43;
|
||||||
}
|
}
|
||||||
|
|
||||||
let entity = world.entity(entity);
|
let entity = world.entity(entity);
|
||||||
let test_component = entity.get_by_id(component_id).unwrap();
|
let test_component = entity.get_by_id(component_id).unwrap();
|
||||||
|
// SAFETY: `TestComponent` is the correct component type
|
||||||
let test_component = unsafe { test_component.deref::<TestComponent>() };
|
let test_component = unsafe { test_component.deref::<TestComponent>() };
|
||||||
|
|
||||||
assert_eq!(test_component.0, 43);
|
assert_eq!(test_component.0, 43);
|
||||||
|
@ -317,11 +317,11 @@ impl World {
|
|||||||
self.flush();
|
self.flush();
|
||||||
match self.entities.alloc_at_without_replacement(entity) {
|
match self.entities.alloc_at_without_replacement(entity) {
|
||||||
AllocAtWithoutReplacement::Exists(location) => {
|
AllocAtWithoutReplacement::Exists(location) => {
|
||||||
// SAFE: `entity` exists and `location` is that entity's location
|
// SAFETY: `entity` exists and `location` is that entity's location
|
||||||
Some(unsafe { EntityMut::new(self, entity, location) })
|
Some(unsafe { EntityMut::new(self, entity, location) })
|
||||||
}
|
}
|
||||||
AllocAtWithoutReplacement::DidNotExist => {
|
AllocAtWithoutReplacement::DidNotExist => {
|
||||||
// SAFE: entity was just allocated
|
// SAFETY: entity was just allocated
|
||||||
Some(unsafe { self.spawn_at_internal(entity) })
|
Some(unsafe { self.spawn_at_internal(entity) })
|
||||||
}
|
}
|
||||||
AllocAtWithoutReplacement::ExistsWithWrongGeneration => None,
|
AllocAtWithoutReplacement::ExistsWithWrongGeneration => None,
|
||||||
@ -381,7 +381,7 @@ impl World {
|
|||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_entity_mut(&mut self, entity: Entity) -> Option<EntityMut> {
|
pub fn get_entity_mut(&mut self, entity: Entity) -> Option<EntityMut> {
|
||||||
let location = self.entities.get(entity)?;
|
let location = self.entities.get(entity)?;
|
||||||
// SAFE: `entity` exists and `location` is that entity's location
|
// SAFETY: `entity` exists and `location` is that entity's location
|
||||||
Some(unsafe { EntityMut::new(self, entity, location) })
|
Some(unsafe { EntityMut::new(self, entity, location) })
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -413,7 +413,7 @@ impl World {
|
|||||||
pub fn spawn(&mut self) -> EntityMut {
|
pub fn spawn(&mut self) -> EntityMut {
|
||||||
self.flush();
|
self.flush();
|
||||||
let entity = self.entities.alloc();
|
let entity = self.entities.alloc();
|
||||||
// SAFE: entity was just allocated
|
// SAFETY: entity was just allocated
|
||||||
unsafe { self.spawn_at_internal(entity) }
|
unsafe { self.spawn_at_internal(entity) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -423,10 +423,10 @@ impl World {
|
|||||||
let archetype = self.archetypes.empty_mut();
|
let archetype = self.archetypes.empty_mut();
|
||||||
// PERF: consider avoiding allocating entities in the empty archetype unless needed
|
// PERF: consider avoiding allocating entities in the empty archetype unless needed
|
||||||
let table_row = self.storages.tables[archetype.table_id()].allocate(entity);
|
let table_row = self.storages.tables[archetype.table_id()].allocate(entity);
|
||||||
// SAFE: no components are allocated by archetype.allocate() because the archetype is
|
// SAFETY: no components are allocated by archetype.allocate() because the archetype is
|
||||||
// empty
|
// empty
|
||||||
let location = archetype.allocate(entity, table_row);
|
let location = archetype.allocate(entity, table_row);
|
||||||
// SAFE: entity index was just allocated
|
// SAFETY: entity index was just allocated
|
||||||
self.entities
|
self.entities
|
||||||
.meta
|
.meta
|
||||||
.get_unchecked_mut(entity.id() as usize)
|
.get_unchecked_mut(entity.id() as usize)
|
||||||
@ -507,7 +507,7 @@ impl World {
|
|||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_mut<T: Component>(&mut self, entity: Entity) -> Option<Mut<T>> {
|
pub fn get_mut<T: Component>(&mut self, entity: Entity) -> Option<Mut<T>> {
|
||||||
// SAFE: lifetimes enforce correct usage of returned borrow
|
// SAFETY: lifetimes enforce correct usage of returned borrow
|
||||||
unsafe { get_mut(self, entity, self.get_entity(entity)?.location()) }
|
unsafe { get_mut(self, entity, self.get_entity(entity)?.location()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -688,7 +688,7 @@ impl World {
|
|||||||
#[inline]
|
#[inline]
|
||||||
pub fn insert_resource<R: Resource>(&mut self, value: R) {
|
pub fn insert_resource<R: Resource>(&mut self, value: R) {
|
||||||
let component_id = self.components.init_resource::<R>();
|
let component_id = self.components.init_resource::<R>();
|
||||||
// SAFE: component_id just initialized and corresponds to resource of type T
|
// SAFETY: component_id just initialized and corresponds to resource of type T
|
||||||
unsafe { self.insert_resource_with_id(component_id, value) };
|
unsafe { self.insert_resource_with_id(component_id, value) };
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -716,21 +716,21 @@ impl World {
|
|||||||
pub fn insert_non_send_resource<R: 'static>(&mut self, value: R) {
|
pub fn insert_non_send_resource<R: 'static>(&mut self, value: R) {
|
||||||
self.validate_non_send_access::<R>();
|
self.validate_non_send_access::<R>();
|
||||||
let component_id = self.components.init_non_send::<R>();
|
let component_id = self.components.init_non_send::<R>();
|
||||||
// SAFE: component_id just initialized and corresponds to resource of type R
|
// SAFETY: component_id just initialized and corresponds to resource of type R
|
||||||
unsafe { self.insert_resource_with_id(component_id, value) };
|
unsafe { self.insert_resource_with_id(component_id, value) };
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Removes the resource of a given type and returns it, if it exists. Otherwise returns [None].
|
/// Removes the resource of a given type and returns it, if it exists. Otherwise returns [None].
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn remove_resource<R: Resource>(&mut self) -> Option<R> {
|
pub fn remove_resource<R: Resource>(&mut self) -> Option<R> {
|
||||||
// SAFE: R is Send + Sync
|
// SAFETY: R is Send + Sync
|
||||||
unsafe { self.remove_resource_unchecked() }
|
unsafe { self.remove_resource_unchecked() }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn remove_non_send_resource<R: 'static>(&mut self) -> Option<R> {
|
pub fn remove_non_send_resource<R: 'static>(&mut self) -> Option<R> {
|
||||||
self.validate_non_send_access::<R>();
|
self.validate_non_send_access::<R>();
|
||||||
// SAFE: we are on main thread
|
// SAFETY: we are on main thread
|
||||||
unsafe { self.remove_resource_unchecked() }
|
unsafe { self.remove_resource_unchecked() }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -747,10 +747,10 @@ impl World {
|
|||||||
if column.is_empty() {
|
if column.is_empty() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
// SAFE: if a resource column exists, row 0 exists as well. caller takes ownership of the
|
// SAFETY: if a resource column exists, row 0 exists as well. caller takes ownership of the
|
||||||
// ptr value / drop is called when R is dropped
|
// ptr value / drop is called when R is dropped
|
||||||
let (ptr, _) = unsafe { column.swap_remove_and_forget_unchecked(0) };
|
let (ptr, _) = unsafe { column.swap_remove_and_forget_unchecked(0) };
|
||||||
// SAFE: column is of type R
|
// SAFETY: column is of type R
|
||||||
Some(unsafe { ptr.read::<R>() })
|
Some(unsafe { ptr.read::<R>() })
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -778,7 +778,7 @@ impl World {
|
|||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
// SAFE: resources table always have row 0
|
// SAFETY: resources table always have row 0
|
||||||
let ticks = unsafe { column.get_ticks_unchecked(0).deref() };
|
let ticks = unsafe { column.get_ticks_unchecked(0).deref() };
|
||||||
ticks.is_added(self.last_change_tick(), self.read_change_tick())
|
ticks.is_added(self.last_change_tick(), self.read_change_tick())
|
||||||
}
|
}
|
||||||
@ -795,7 +795,7 @@ impl World {
|
|||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
// SAFE: resources table always have row 0
|
// SAFETY: resources table always have row 0
|
||||||
let ticks = unsafe { column.get_ticks_unchecked(0).deref() };
|
let ticks = unsafe { column.get_ticks_unchecked(0).deref() };
|
||||||
ticks.is_changed(self.last_change_tick(), self.read_change_tick())
|
ticks.is_changed(self.last_change_tick(), self.read_change_tick())
|
||||||
}
|
}
|
||||||
@ -852,14 +852,14 @@ impl World {
|
|||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_resource<R: Resource>(&self) -> Option<&R> {
|
pub fn get_resource<R: Resource>(&self) -> Option<&R> {
|
||||||
let component_id = self.components.get_resource_id(TypeId::of::<R>())?;
|
let component_id = self.components.get_resource_id(TypeId::of::<R>())?;
|
||||||
// SAFE: unique world access
|
// SAFETY: unique world access
|
||||||
unsafe { self.get_resource_with_id(component_id) }
|
unsafe { self.get_resource_with_id(component_id) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets a mutable reference to the resource of the given type if it exists
|
/// Gets a mutable reference to the resource of the given type if it exists
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_resource_mut<R: Resource>(&mut self) -> Option<Mut<'_, R>> {
|
pub fn get_resource_mut<R: Resource>(&mut self) -> Option<Mut<'_, R>> {
|
||||||
// SAFE: unique world access
|
// SAFETY: unique world access
|
||||||
unsafe { self.get_resource_unchecked_mut() }
|
unsafe { self.get_resource_unchecked_mut() }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -882,7 +882,7 @@ impl World {
|
|||||||
///
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
/// This will allow aliased mutable access to the given resource type. The caller must ensure
|
/// This will allow aliased mutable access to the given resource type. The caller must ensure
|
||||||
/// that only one mutable access exists at a time.
|
/// that there is either only one mutable access or multiple immutable accesses at a time.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub unsafe fn get_resource_unchecked_mut<R: Resource>(&self) -> Option<Mut<'_, R>> {
|
pub unsafe fn get_resource_unchecked_mut<R: Resource>(&self) -> Option<Mut<'_, R>> {
|
||||||
let component_id = self.components.get_resource_id(TypeId::of::<R>())?;
|
let component_id = self.components.get_resource_id(TypeId::of::<R>())?;
|
||||||
@ -934,7 +934,7 @@ impl World {
|
|||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_non_send_resource<R: 'static>(&self) -> Option<&R> {
|
pub fn get_non_send_resource<R: 'static>(&self) -> Option<&R> {
|
||||||
let component_id = self.components.get_resource_id(TypeId::of::<R>())?;
|
let component_id = self.components.get_resource_id(TypeId::of::<R>())?;
|
||||||
// SAFE: component id matches type T
|
// SAFETY: component id matches type T
|
||||||
unsafe { self.get_non_send_with_id(component_id) }
|
unsafe { self.get_non_send_with_id(component_id) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -942,7 +942,7 @@ impl World {
|
|||||||
/// Otherwise returns [None]
|
/// Otherwise returns [None]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_non_send_resource_mut<R: 'static>(&mut self) -> Option<Mut<'_, R>> {
|
pub fn get_non_send_resource_mut<R: 'static>(&mut self) -> Option<Mut<'_, R>> {
|
||||||
// SAFE: unique world access
|
// SAFETY: unique world access
|
||||||
unsafe { self.get_non_send_resource_unchecked_mut() }
|
unsafe { self.get_non_send_resource_unchecked_mut() }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -951,7 +951,7 @@ impl World {
|
|||||||
///
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
/// This will allow aliased mutable access to the given non-send resource type. The caller must
|
/// This will allow aliased mutable access to the given non-send resource type. The caller must
|
||||||
/// ensure that only one mutable access exists at a time.
|
/// ensure that there is either only one mutable access or multiple immutable accesses at a time.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub unsafe fn get_non_send_resource_unchecked_mut<R: 'static>(&self) -> Option<Mut<'_, R>> {
|
pub unsafe fn get_non_send_resource_unchecked_mut<R: 'static>(&self) -> Option<Mut<'_, R>> {
|
||||||
let component_id = self.components.get_resource_id(TypeId::of::<R>())?;
|
let component_id = self.components.get_resource_id(TypeId::of::<R>())?;
|
||||||
@ -1033,7 +1033,7 @@ impl World {
|
|||||||
SpawnOrInsert::Insert(ref mut inserter, archetype)
|
SpawnOrInsert::Insert(ref mut inserter, archetype)
|
||||||
if location.archetype_id == archetype =>
|
if location.archetype_id == archetype =>
|
||||||
{
|
{
|
||||||
// SAFE: `entity` is valid, `location` matches entity, bundle matches inserter
|
// SAFETY: `entity` is valid, `location` matches entity, bundle matches inserter
|
||||||
unsafe { inserter.insert(entity, location.index, bundle) };
|
unsafe { inserter.insert(entity, location.index, bundle) };
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
@ -1045,7 +1045,7 @@ impl World {
|
|||||||
location.archetype_id,
|
location.archetype_id,
|
||||||
change_tick,
|
change_tick,
|
||||||
);
|
);
|
||||||
// SAFE: `entity` is valid, `location` matches entity, bundle matches inserter
|
// SAFETY: `entity` is valid, `location` matches entity, bundle matches inserter
|
||||||
unsafe { inserter.insert(entity, location.index, bundle) };
|
unsafe { inserter.insert(entity, location.index, bundle) };
|
||||||
spawn_or_insert =
|
spawn_or_insert =
|
||||||
SpawnOrInsert::Insert(inserter, location.archetype_id);
|
SpawnOrInsert::Insert(inserter, location.archetype_id);
|
||||||
@ -1054,7 +1054,7 @@ impl World {
|
|||||||
}
|
}
|
||||||
AllocAtWithoutReplacement::DidNotExist => {
|
AllocAtWithoutReplacement::DidNotExist => {
|
||||||
if let SpawnOrInsert::Spawn(ref mut spawner) = spawn_or_insert {
|
if let SpawnOrInsert::Spawn(ref mut spawner) = spawn_or_insert {
|
||||||
// SAFE: `entity` is allocated (but non existent), bundle matches inserter
|
// SAFETY: `entity` is allocated (but non existent), bundle matches inserter
|
||||||
unsafe { spawner.spawn_non_existent(entity, bundle) };
|
unsafe { spawner.spawn_non_existent(entity, bundle) };
|
||||||
} else {
|
} else {
|
||||||
let mut spawner = bundle_info.get_bundle_spawner(
|
let mut spawner = bundle_info.get_bundle_spawner(
|
||||||
@ -1064,7 +1064,7 @@ impl World {
|
|||||||
&mut self.storages,
|
&mut self.storages,
|
||||||
change_tick,
|
change_tick,
|
||||||
);
|
);
|
||||||
// SAFE: `entity` is valid, `location` matches entity, bundle matches inserter
|
// SAFETY: `entity` is valid, `location` matches entity, bundle matches inserter
|
||||||
unsafe { spawner.spawn_non_existent(entity, bundle) };
|
unsafe { spawner.spawn_non_existent(entity, bundle) };
|
||||||
spawn_or_insert = SpawnOrInsert::Spawn(spawner);
|
spawn_or_insert = SpawnOrInsert::Spawn(spawner);
|
||||||
}
|
}
|
||||||
@ -1123,11 +1123,11 @@ impl World {
|
|||||||
"resource does not exist: {}",
|
"resource does not exist: {}",
|
||||||
std::any::type_name::<R>()
|
std::any::type_name::<R>()
|
||||||
);
|
);
|
||||||
// SAFE: if a resource column exists, row 0 exists as well. caller takes ownership of
|
// SAFETY: if a resource column exists, row 0 exists as well. caller takes ownership of
|
||||||
// the ptr value / drop is called when R is dropped
|
// the ptr value / drop is called when R is dropped
|
||||||
unsafe { column.swap_remove_and_forget_unchecked(0) }
|
unsafe { column.swap_remove_and_forget_unchecked(0) }
|
||||||
};
|
};
|
||||||
// SAFE: pointer is of type R
|
// SAFETY: pointer is of type R
|
||||||
// Read the value onto the stack to avoid potential mut aliasing.
|
// Read the value onto the stack to avoid potential mut aliasing.
|
||||||
let mut value = unsafe { ptr.read::<R>() };
|
let mut value = unsafe { ptr.read::<R>() };
|
||||||
let value_mut = Mut {
|
let value_mut = Mut {
|
||||||
@ -1148,8 +1148,8 @@ impl World {
|
|||||||
.unwrap_or_else(|| panic!("resource does not exist: {}", std::any::type_name::<R>()));
|
.unwrap_or_else(|| panic!("resource does not exist: {}", std::any::type_name::<R>()));
|
||||||
|
|
||||||
OwningPtr::make(value, |ptr| {
|
OwningPtr::make(value, |ptr| {
|
||||||
|
// SAFETY: pointer is of type R
|
||||||
unsafe {
|
unsafe {
|
||||||
// SAFE: pointer is of type R
|
|
||||||
column.push(ptr, ticks);
|
column.push(ptr, ticks);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -1216,12 +1216,12 @@ impl World {
|
|||||||
let change_tick = self.change_tick();
|
let change_tick = self.change_tick();
|
||||||
let column = self.initialize_resource_internal(component_id);
|
let column = self.initialize_resource_internal(component_id);
|
||||||
if column.is_empty() {
|
if column.is_empty() {
|
||||||
// SAFE: column is of type R and has been allocated above
|
// SAFETY: column is of type R and has been allocated above
|
||||||
OwningPtr::make(value, |ptr| {
|
OwningPtr::make(value, |ptr| {
|
||||||
column.push(ptr, ComponentTicks::new(change_tick));
|
column.push(ptr, ComponentTicks::new(change_tick));
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
// SAFE: column is of type R and has already been allocated
|
// SAFETY: column is of type R and has already been allocated
|
||||||
*column.get_data_unchecked_mut(0).deref_mut::<R>() = value;
|
*column.get_data_unchecked_mut(0).deref_mut::<R>() = value;
|
||||||
column.get_ticks_unchecked_mut(0).set_changed(change_tick);
|
column.get_ticks_unchecked_mut(0).set_changed(change_tick);
|
||||||
}
|
}
|
||||||
@ -1246,10 +1246,10 @@ impl World {
|
|||||||
"insert_resource_by_id called with component id which doesn't exist in this world"
|
"insert_resource_by_id called with component id which doesn't exist in this world"
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
// SAFE: component_id is valid, checked by the lines above
|
// SAFETY: component_id is valid, checked by the lines above
|
||||||
let column = self.initialize_resource_internal(component_id);
|
let column = self.initialize_resource_internal(component_id);
|
||||||
if column.is_empty() {
|
if column.is_empty() {
|
||||||
// SAFE: column is of type R and has been allocated above
|
// SAFETY: column is of type R and has been allocated above
|
||||||
column.push(value, ComponentTicks::new(change_tick));
|
column.push(value, ComponentTicks::new(change_tick));
|
||||||
} else {
|
} else {
|
||||||
let ptr = column.get_data_unchecked_mut(0);
|
let ptr = column.get_data_unchecked_mut(0);
|
||||||
@ -1266,7 +1266,7 @@ impl World {
|
|||||||
/// `component_id` must be valid for this world
|
/// `component_id` must be valid for this world
|
||||||
#[inline]
|
#[inline]
|
||||||
unsafe fn initialize_resource_internal(&mut self, component_id: ComponentId) -> &mut Column {
|
unsafe fn initialize_resource_internal(&mut self, component_id: ComponentId) -> &mut Column {
|
||||||
// SAFE: resource archetype always exists
|
// SAFETY: resource archetype always exists
|
||||||
let resource_archetype = self
|
let resource_archetype = self
|
||||||
.archetypes
|
.archetypes
|
||||||
.archetypes
|
.archetypes
|
||||||
@ -1294,14 +1294,14 @@ impl World {
|
|||||||
|
|
||||||
pub(crate) fn initialize_resource<R: Resource>(&mut self) -> ComponentId {
|
pub(crate) fn initialize_resource<R: Resource>(&mut self) -> ComponentId {
|
||||||
let component_id = self.components.init_resource::<R>();
|
let component_id = self.components.init_resource::<R>();
|
||||||
// SAFE: resource initialized above
|
// SAFETY: resource initialized above
|
||||||
unsafe { self.initialize_resource_internal(component_id) };
|
unsafe { self.initialize_resource_internal(component_id) };
|
||||||
component_id
|
component_id
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn initialize_non_send_resource<R: 'static>(&mut self) -> ComponentId {
|
pub(crate) fn initialize_non_send_resource<R: 'static>(&mut self) -> ComponentId {
|
||||||
let component_id = self.components.init_non_send::<R>();
|
let component_id = self.components.init_non_send::<R>();
|
||||||
// SAFE: resource initialized above
|
// SAFETY: resource initialized above
|
||||||
unsafe { self.initialize_resource_internal(component_id) };
|
unsafe { self.initialize_resource_internal(component_id) };
|
||||||
component_id
|
component_id
|
||||||
}
|
}
|
||||||
@ -1343,12 +1343,12 @@ impl World {
|
|||||||
/// such as inserting a [Component].
|
/// such as inserting a [Component].
|
||||||
pub(crate) fn flush(&mut self) {
|
pub(crate) fn flush(&mut self) {
|
||||||
let empty_archetype = self.archetypes.empty_mut();
|
let empty_archetype = self.archetypes.empty_mut();
|
||||||
|
let table = &mut self.storages.tables[empty_archetype.table_id()];
|
||||||
|
// PERF: consider pre-allocating space for flushed entities
|
||||||
|
// SAFETY: entity is set to a valid location
|
||||||
unsafe {
|
unsafe {
|
||||||
let table = &mut self.storages.tables[empty_archetype.table_id()];
|
|
||||||
// PERF: consider pre-allocating space for flushed entities
|
|
||||||
// SAFE: entity is set to a valid location
|
|
||||||
self.entities.flush(|entity, location| {
|
self.entities.flush(|entity, location| {
|
||||||
// SAFE: no components are allocated by archetype.allocate() because the archetype
|
// SAFETY: no components are allocated by archetype.allocate() because the archetype
|
||||||
// is empty
|
// is empty
|
||||||
*location = empty_archetype.allocate(entity, table.allocate(entity));
|
*location = empty_archetype.allocate(entity, table.allocate(entity));
|
||||||
});
|
});
|
||||||
@ -1428,9 +1428,10 @@ impl World {
|
|||||||
|
|
||||||
let column = self.get_populated_resource_column(component_id)?;
|
let column = self.get_populated_resource_column(component_id)?;
|
||||||
|
|
||||||
// SAFE: get_data_ptr requires that the mutability rules are not violated, and the caller promises
|
// SAFETY: get_data_ptr requires that the mutability rules are not violated, and the caller promises
|
||||||
// to only modify the resource while the mutable borrow of the world is valid
|
// to only modify the resource while the mutable borrow of the world is valid
|
||||||
let ticks = Ticks {
|
let ticks = Ticks {
|
||||||
|
// SAFETY:
|
||||||
// - index is in-bounds because the column is initialized and non-empty
|
// - index is in-bounds because the column is initialized and non-empty
|
||||||
// - no other reference to the ticks of the same row can exist at the same time
|
// - no other reference to the ticks of the same row can exist at the same time
|
||||||
component_ticks: unsafe { &mut *column.get_ticks_unchecked(0).get() },
|
component_ticks: unsafe { &mut *column.get_ticks_unchecked(0).get() },
|
||||||
@ -1439,6 +1440,7 @@ impl World {
|
|||||||
};
|
};
|
||||||
|
|
||||||
Some(MutUntyped {
|
Some(MutUntyped {
|
||||||
|
// SAFETY: world access is unique, so no other reference can exist at the same time
|
||||||
value: unsafe { column.get_data_ptr().assert_unique() },
|
value: unsafe { column.get_data_ptr().assert_unique() },
|
||||||
ticks,
|
ticks,
|
||||||
})
|
})
|
||||||
@ -1460,7 +1462,7 @@ impl World {
|
|||||||
if column.is_empty() {
|
if column.is_empty() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
// SAFE: if a resource column exists, row 0 exists as well
|
// SAFETY: if a resource column exists, row 0 exists as well
|
||||||
unsafe { column.swap_remove_unchecked(0) };
|
unsafe { column.swap_remove_unchecked(0) };
|
||||||
|
|
||||||
Some(())
|
Some(())
|
||||||
@ -1474,7 +1476,7 @@ impl World {
|
|||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_by_id(&self, entity: Entity, component_id: ComponentId) -> Option<Ptr<'_>> {
|
pub fn get_by_id(&self, entity: Entity, component_id: ComponentId) -> Option<Ptr<'_>> {
|
||||||
self.components().get_info(component_id)?;
|
self.components().get_info(component_id)?;
|
||||||
// SAFE: entity_location is valid, component_id is valid as checked by the line above
|
// SAFETY: entity_location is valid, component_id is valid as checked by the line above
|
||||||
unsafe {
|
unsafe {
|
||||||
get_component(
|
get_component(
|
||||||
self,
|
self,
|
||||||
@ -1497,7 +1499,7 @@ impl World {
|
|||||||
component_id: ComponentId,
|
component_id: ComponentId,
|
||||||
) -> Option<MutUntyped<'_>> {
|
) -> Option<MutUntyped<'_>> {
|
||||||
self.components().get_info(component_id)?;
|
self.components().get_info(component_id)?;
|
||||||
// SAFE: entity_location is valid, component_id is valid as checked by the line above
|
// SAFETY: entity_location is valid, component_id is valid as checked by the line above
|
||||||
unsafe {
|
unsafe {
|
||||||
get_mut_by_id(
|
get_mut_by_id(
|
||||||
self,
|
self,
|
||||||
@ -1526,7 +1528,9 @@ impl fmt::Debug for World {
|
|||||||
|
|
||||||
// TODO: remove allow on lint - https://github.com/bevyengine/bevy/issues/3666
|
// TODO: remove allow on lint - https://github.com/bevyengine/bevy/issues/3666
|
||||||
#[allow(clippy::non_send_fields_in_send_ty)]
|
#[allow(clippy::non_send_fields_in_send_ty)]
|
||||||
|
// SAFETY: all methods on the world ensure that non-send resources are only accessible on the main thread
|
||||||
unsafe impl Send for World {}
|
unsafe impl Send for World {}
|
||||||
|
// SAFETY: all methods on the world ensure that non-send resources are only accessible on the main thread
|
||||||
unsafe impl Sync for World {}
|
unsafe impl Sync for World {}
|
||||||
|
|
||||||
/// Creates an instance of the type this trait is implemented for
|
/// Creates an instance of the type this trait is implemented for
|
||||||
@ -1711,6 +1715,7 @@ mod tests {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let resource = world.get_resource_by_id(component_id).unwrap();
|
let resource = world.get_resource_by_id(component_id).unwrap();
|
||||||
|
// SAFETY: `TestResource` is the correct resource type
|
||||||
let resource = unsafe { resource.deref::<TestResource>() };
|
let resource = unsafe { resource.deref::<TestResource>() };
|
||||||
|
|
||||||
assert_eq!(resource.0, 42);
|
assert_eq!(resource.0, 42);
|
||||||
@ -1728,11 +1733,13 @@ mod tests {
|
|||||||
{
|
{
|
||||||
let mut resource = world.get_resource_mut_by_id(component_id).unwrap();
|
let mut resource = world.get_resource_mut_by_id(component_id).unwrap();
|
||||||
resource.set_changed();
|
resource.set_changed();
|
||||||
|
// SAFETY: `TestResource` is the correct resource type
|
||||||
let resource = unsafe { resource.into_inner().deref_mut::<TestResource>() };
|
let resource = unsafe { resource.into_inner().deref_mut::<TestResource>() };
|
||||||
resource.0 = 43;
|
resource.0 = 43;
|
||||||
}
|
}
|
||||||
|
|
||||||
let resource = world.get_resource_by_id(component_id).unwrap();
|
let resource = world.get_resource_by_id(component_id).unwrap();
|
||||||
|
// SAFETY: `TestResource` is the correct resource type
|
||||||
let resource = unsafe { resource.deref::<TestResource>() };
|
let resource = unsafe { resource.deref::<TestResource>() };
|
||||||
|
|
||||||
assert_eq!(resource.0, 43);
|
assert_eq!(resource.0, 43);
|
||||||
@ -1744,7 +1751,7 @@ mod tests {
|
|||||||
|
|
||||||
let mut world = World::new();
|
let mut world = World::new();
|
||||||
|
|
||||||
// SAFE: the drop function is valid for the layout and the data will be safe to access from any thread
|
// SAFETY: the drop function is valid for the layout and the data will be safe to access from any thread
|
||||||
let descriptor = unsafe {
|
let descriptor = unsafe {
|
||||||
ComponentDescriptor::new_with_layout(
|
ComponentDescriptor::new_with_layout(
|
||||||
"Custom Test Component".to_string(),
|
"Custom Test Component".to_string(),
|
||||||
@ -1761,11 +1768,14 @@ mod tests {
|
|||||||
let component_id = world.init_component_with_descriptor(descriptor);
|
let component_id = world.init_component_with_descriptor(descriptor);
|
||||||
|
|
||||||
let value: [u8; 8] = [0, 1, 2, 3, 4, 5, 6, 7];
|
let value: [u8; 8] = [0, 1, 2, 3, 4, 5, 6, 7];
|
||||||
OwningPtr::make(value, |ptr| unsafe {
|
OwningPtr::make(value, |ptr| {
|
||||||
// SAFE: value is valid for the component layout
|
// SAFETY: value is valid for the component layout
|
||||||
world.insert_resource_by_id(component_id, ptr);
|
unsafe {
|
||||||
|
world.insert_resource_by_id(component_id, ptr);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// SAFETY: [u8; 8] is the correct type for the resource
|
||||||
let data = unsafe {
|
let data = unsafe {
|
||||||
world
|
world
|
||||||
.get_resource_by_id(component_id)
|
.get_resource_by_id(component_id)
|
||||||
@ -1785,9 +1795,11 @@ mod tests {
|
|||||||
let invalid_component_id = ComponentId::new(usize::MAX);
|
let invalid_component_id = ComponentId::new(usize::MAX);
|
||||||
|
|
||||||
let mut world = World::new();
|
let mut world = World::new();
|
||||||
OwningPtr::make((), |ptr| unsafe {
|
OwningPtr::make((), |ptr| {
|
||||||
// SAFE: ptr must be valid for the component_id `invalid_component_id` which is invalid, but checked by `insert_resource_by_id`
|
// SAFETY: ptr must be valid for the component_id `invalid_component_id` which is invalid, but checked by `insert_resource_by_id`
|
||||||
world.insert_resource_by_id(invalid_component_id, ptr);
|
unsafe {
|
||||||
|
world.insert_resource_by_id(invalid_component_id, ptr);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,7 +67,7 @@ where
|
|||||||
|
|
||||||
fn next(&mut self) -> Option<Entity> {
|
fn next(&mut self) -> Option<Entity> {
|
||||||
let bundle = self.inner.next()?;
|
let bundle = self.inner.next()?;
|
||||||
// SAFE: bundle matches spawner type
|
// SAFETY: bundle matches spawner type
|
||||||
unsafe { Some(self.spawner.spawn(bundle)) }
|
unsafe { Some(self.spawner.spawn(bundle)) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -186,7 +186,7 @@ impl<'w> WorldCell<'w> {
|
|||||||
let resource_archetype = self.world.archetypes.resource();
|
let resource_archetype = self.world.archetypes.resource();
|
||||||
let archetype_component_id = resource_archetype.get_archetype_component_id(component_id)?;
|
let archetype_component_id = resource_archetype.get_archetype_component_id(component_id)?;
|
||||||
Some(WorldBorrow::new(
|
Some(WorldBorrow::new(
|
||||||
// SAFE: ComponentId matches TypeId
|
// SAFETY: ComponentId matches TypeId
|
||||||
unsafe { self.world.get_resource_with_id(component_id)? },
|
unsafe { self.world.get_resource_with_id(component_id)? },
|
||||||
archetype_component_id,
|
archetype_component_id,
|
||||||
self.access.clone(),
|
self.access.clone(),
|
||||||
@ -218,7 +218,7 @@ impl<'w> WorldCell<'w> {
|
|||||||
let resource_archetype = self.world.archetypes.resource();
|
let resource_archetype = self.world.archetypes.resource();
|
||||||
let archetype_component_id = resource_archetype.get_archetype_component_id(component_id)?;
|
let archetype_component_id = resource_archetype.get_archetype_component_id(component_id)?;
|
||||||
Some(WorldBorrowMut::new(
|
Some(WorldBorrowMut::new(
|
||||||
// SAFE: ComponentId matches TypeId and access is checked by WorldBorrowMut
|
// SAFETY: ComponentId matches TypeId and access is checked by WorldBorrowMut
|
||||||
unsafe {
|
unsafe {
|
||||||
self.world
|
self.world
|
||||||
.get_resource_unchecked_mut_with_id(component_id)?
|
.get_resource_unchecked_mut_with_id(component_id)?
|
||||||
@ -253,7 +253,7 @@ impl<'w> WorldCell<'w> {
|
|||||||
let resource_archetype = self.world.archetypes.resource();
|
let resource_archetype = self.world.archetypes.resource();
|
||||||
let archetype_component_id = resource_archetype.get_archetype_component_id(component_id)?;
|
let archetype_component_id = resource_archetype.get_archetype_component_id(component_id)?;
|
||||||
Some(WorldBorrow::new(
|
Some(WorldBorrow::new(
|
||||||
// SAFE: ComponentId matches TypeId
|
// SAFETY: ComponentId matches TypeId
|
||||||
unsafe { self.world.get_non_send_with_id(component_id)? },
|
unsafe { self.world.get_non_send_with_id(component_id)? },
|
||||||
archetype_component_id,
|
archetype_component_id,
|
||||||
self.access.clone(),
|
self.access.clone(),
|
||||||
@ -285,7 +285,7 @@ impl<'w> WorldCell<'w> {
|
|||||||
let resource_archetype = self.world.archetypes.resource();
|
let resource_archetype = self.world.archetypes.resource();
|
||||||
let archetype_component_id = resource_archetype.get_archetype_component_id(component_id)?;
|
let archetype_component_id = resource_archetype.get_archetype_component_id(component_id)?;
|
||||||
Some(WorldBorrowMut::new(
|
Some(WorldBorrowMut::new(
|
||||||
// SAFE: ComponentId matches TypeId and access is checked by WorldBorrowMut
|
// SAFETY: ComponentId matches TypeId and access is checked by WorldBorrowMut
|
||||||
unsafe {
|
unsafe {
|
||||||
self.world
|
self.world
|
||||||
.get_non_send_unchecked_mut_with_id(component_id)?
|
.get_non_send_unchecked_mut_with_id(component_id)?
|
||||||
|
@ -345,7 +345,7 @@ impl<'w> BuildWorldChildren for EntityMut<'w> {
|
|||||||
let mut builder = WorldChildBuilder {
|
let mut builder = WorldChildBuilder {
|
||||||
current_entity: None,
|
current_entity: None,
|
||||||
parent_entities: vec![entity],
|
parent_entities: vec![entity],
|
||||||
// SAFE: self.update_location() is called below. It is impossible to make EntityMut
|
// SAFETY: self.update_location() is called below. It is impossible to make EntityMut
|
||||||
// function calls on `self` within the scope defined here
|
// function calls on `self` within the scope defined here
|
||||||
world: unsafe { self.world_mut() },
|
world: unsafe { self.world_mut() },
|
||||||
};
|
};
|
||||||
@ -359,7 +359,7 @@ impl<'w> BuildWorldChildren for EntityMut<'w> {
|
|||||||
fn push_children(&mut self, children: &[Entity]) -> &mut Self {
|
fn push_children(&mut self, children: &[Entity]) -> &mut Self {
|
||||||
let parent = self.id();
|
let parent = self.id();
|
||||||
{
|
{
|
||||||
// SAFE: parent entity is not modified and its location is updated manually
|
// SAFETY: parent entity is not modified and its location is updated manually
|
||||||
let world = unsafe { self.world_mut() };
|
let world = unsafe { self.world_mut() };
|
||||||
for child in children.iter() {
|
for child in children.iter() {
|
||||||
world
|
world
|
||||||
@ -381,7 +381,7 @@ impl<'w> BuildWorldChildren for EntityMut<'w> {
|
|||||||
fn insert_children(&mut self, index: usize, children: &[Entity]) -> &mut Self {
|
fn insert_children(&mut self, index: usize, children: &[Entity]) -> &mut Self {
|
||||||
let parent = self.id();
|
let parent = self.id();
|
||||||
{
|
{
|
||||||
// SAFE: parent entity is not modified and its location is updated manually
|
// SAFETY: parent entity is not modified and its location is updated manually
|
||||||
let world = unsafe { self.world_mut() };
|
let world = unsafe { self.world_mut() };
|
||||||
for child in children.iter() {
|
for child in children.iter() {
|
||||||
world
|
world
|
||||||
@ -403,7 +403,7 @@ impl<'w> BuildWorldChildren for EntityMut<'w> {
|
|||||||
|
|
||||||
fn remove_children(&mut self, children: &[Entity]) -> &mut Self {
|
fn remove_children(&mut self, children: &[Entity]) -> &mut Self {
|
||||||
let parent = self.id();
|
let parent = self.id();
|
||||||
// SAFE: This doesn't change the parent's location
|
// SAFETY: This doesn't change the parent's location
|
||||||
let world = unsafe { self.world_mut() };
|
let world = unsafe { self.world_mut() };
|
||||||
for child in children.iter() {
|
for child in children.iter() {
|
||||||
let mut child = world.entity_mut(*child);
|
let mut child = world.entity_mut(*child);
|
||||||
|
@ -114,7 +114,7 @@ impl<'w> DespawnRecursiveExt for EntityMut<'w> {
|
|||||||
)
|
)
|
||||||
.entered();
|
.entered();
|
||||||
|
|
||||||
// SAFE: EntityMut is consumed so even though the location is no longer
|
// SAFETY: EntityMut is consumed so even though the location is no longer
|
||||||
// valid, it cannot be accessed again with the invalid location.
|
// valid, it cannot be accessed again with the invalid location.
|
||||||
unsafe {
|
unsafe {
|
||||||
despawn_with_children_recursive(self.world_mut(), entity);
|
despawn_with_children_recursive(self.world_mut(), entity);
|
||||||
@ -131,7 +131,7 @@ impl<'w> DespawnRecursiveExt for EntityMut<'w> {
|
|||||||
)
|
)
|
||||||
.entered();
|
.entered();
|
||||||
|
|
||||||
// SAFE: The location is updated.
|
// SAFETY: The location is updated.
|
||||||
unsafe {
|
unsafe {
|
||||||
despawn_children(self.world_mut(), entity);
|
despawn_children(self.world_mut(), entity);
|
||||||
self.update_location();
|
self.update_location();
|
||||||
|
@ -161,6 +161,7 @@ impl<'a> OwningPtr<'a> {
|
|||||||
#[inline]
|
#[inline]
|
||||||
pub fn make<T, F: FnOnce(OwningPtr<'_>) -> R, R>(val: T, f: F) -> R {
|
pub fn make<T, F: FnOnce(OwningPtr<'_>) -> R, R>(val: T, f: F) -> R {
|
||||||
let mut temp = MaybeUninit::new(val);
|
let mut temp = MaybeUninit::new(val);
|
||||||
|
// SAFETY: `temp.as_mut_ptr()` is a reference to a local value on the stack, so it cannot be null
|
||||||
let ptr = unsafe { NonNull::new_unchecked(temp.as_mut_ptr().cast::<u8>()) };
|
let ptr = unsafe { NonNull::new_unchecked(temp.as_mut_ptr().cast::<u8>()) };
|
||||||
f(Self(ptr, PhantomData))
|
f(Self(ptr, PhantomData))
|
||||||
}
|
}
|
||||||
@ -233,6 +234,7 @@ impl<'a, T> From<&'a [T]> for ThinSlicePtr<'a, T> {
|
|||||||
#[inline]
|
#[inline]
|
||||||
fn from(slice: &'a [T]) -> Self {
|
fn from(slice: &'a [T]) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
// SAFETY: a reference can never be null
|
||||||
ptr: unsafe { NonNull::new_unchecked(slice.as_ptr() as *mut T) },
|
ptr: unsafe { NonNull::new_unchecked(slice.as_ptr() as *mut T) },
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
len: slice.len(),
|
len: slice.len(),
|
||||||
|
@ -190,7 +190,7 @@ impl System for FixedTimestep {
|
|||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn run_unsafe(&mut self, _input: (), world: &World) -> ShouldRun {
|
unsafe fn run_unsafe(&mut self, _input: (), world: &World) -> ShouldRun {
|
||||||
// SAFE: this system inherits the internal system's component access and archetype component
|
// SAFETY: this system inherits the internal system's component access and archetype component
|
||||||
// access, which means the caller has ensured running the internal system is safe
|
// access, which means the caller has ensured running the internal system is safe
|
||||||
self.internal_system.run_unsafe((), world)
|
self.internal_system.run_unsafe((), world)
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,7 @@ pub struct FlexSurface {
|
|||||||
taffy: Taffy,
|
taffy: Taffy,
|
||||||
}
|
}
|
||||||
|
|
||||||
// SAFE: as long as MeasureFunc is Send + Sync. https://github.com/DioxusLabs/taffy/issues/146
|
// SAFETY: as long as MeasureFunc is Send + Sync. https://github.com/DioxusLabs/taffy/issues/146
|
||||||
// TODO: remove allow on lint - https://github.com/bevyengine/bevy/issues/3666
|
// TODO: remove allow on lint - https://github.com/bevyengine/bevy/issues/3666
|
||||||
#[allow(clippy::non_send_fields_in_send_ty)]
|
#[allow(clippy::non_send_fields_in_send_ty)]
|
||||||
unsafe impl Send for FlexSurface {}
|
unsafe impl Send for FlexSurface {}
|
||||||
|
@ -29,5 +29,7 @@ fn noop_raw_waker() -> RawWaker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn noop_waker() -> Waker {
|
fn noop_waker() -> Waker {
|
||||||
|
// SAFETY: the `RawWakerVTable` is just a big noop and doesn't violate any of the rules in `RawWakerVTable`s documentation
|
||||||
|
// (which talks about retaining and releasing any "resources", of which there are none in this case)
|
||||||
unsafe { Waker::from_raw(noop_raw_waker()) }
|
unsafe { Waker::from_raw(noop_raw_waker()) }
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,7 @@ impl RawWindowHandleWrapper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// SAFE: RawWindowHandle is just a normal "raw pointer", which doesn't impl Send/Sync. However the pointer is only
|
// SAFETY: RawWindowHandle is just a normal "raw pointer", which doesn't impl Send/Sync. However the pointer is only
|
||||||
// exposed via an unsafe method that forces the user to make a call for a given platform. (ex: some platforms don't
|
// exposed via an unsafe method that forces the user to make a call for a given platform. (ex: some platforms don't
|
||||||
// support doing window operations off of the main thread).
|
// support doing window operations off of the main thread).
|
||||||
// A recommendation for this pattern (and more context) is available here:
|
// A recommendation for this pattern (and more context) is available here:
|
||||||
@ -41,7 +41,7 @@ unsafe impl Sync for RawWindowHandleWrapper {}
|
|||||||
/// In many cases, this should only be constructed on the main thread.
|
/// In many cases, this should only be constructed on the main thread.
|
||||||
pub struct ThreadLockedRawWindowHandleWrapper(RawWindowHandle);
|
pub struct ThreadLockedRawWindowHandleWrapper(RawWindowHandle);
|
||||||
|
|
||||||
// SAFE: the caller has validated that this is a valid context to get RawWindowHandle
|
// SAFETY: the caller has validated that this is a valid context to get RawWindowHandle
|
||||||
// as otherwise an instance of this type could not have been constructed
|
// as otherwise an instance of this type could not have been constructed
|
||||||
// NOTE: we cannot simply impl HasRawWindowHandle for RawWindowHandleWrapper,
|
// NOTE: we cannot simply impl HasRawWindowHandle for RawWindowHandleWrapper,
|
||||||
// as the `raw_window_handle` method is safe. We cannot guarantee that all calls
|
// as the `raw_window_handle` method is safe. We cannot guarantee that all calls
|
||||||
|
Loading…
Reference in New Issue
Block a user