diff --git a/crates/bevy_ecs/src/bundle.rs b/crates/bevy_ecs/src/bundle.rs index 285e42d4e6..fcfd514f68 100644 --- a/crates/bevy_ecs/src/bundle.rs +++ b/crates/bevy_ecs/src/bundle.rs @@ -9,6 +9,7 @@ use crate::{ Archetype, ArchetypeAfterBundleInsert, ArchetypeId, Archetypes, BundleComponentStatus, ComponentStatus, SpawnBundleStatus, }, + change_detection::MaybeLocation, component::{ Component, ComponentId, Components, RequiredComponentConstructor, RequiredComponents, StorageType, Tick, @@ -24,8 +25,6 @@ use alloc::{boxed::Box, vec, vec::Vec}; use bevy_platform_support::collections::{HashMap, HashSet}; use bevy_ptr::{ConstNonNull, OwningPtr}; use bevy_utils::TypeIdMap; -#[cfg(feature = "track_location")] -use core::panic::Location; use core::{any::TypeId, ptr::NonNull}; use variadics_please::all_tuples; @@ -623,7 +622,7 @@ impl BundleInfo { change_tick: Tick, bundle: T, insert_mode: InsertMode, - #[cfg(feature = "track_location")] caller: &'static Location<'static>, + caller: MaybeLocation, ) -> T::Effect { // NOTE: get_components calls this closure on each component in "bundle order". // bundle_info.component_ids are also in "bundle order" @@ -638,20 +637,12 @@ impl BundleInfo { // the target table contains the component. let column = table.get_column_mut(component_id).debug_checked_unwrap(); match (status, insert_mode) { - (ComponentStatus::Added, _) => column.initialize( - table_row, - component_ptr, - change_tick, - #[cfg(feature = "track_location")] - caller, - ), - (ComponentStatus::Existing, InsertMode::Replace) => column.replace( - table_row, - component_ptr, - change_tick, - #[cfg(feature = "track_location")] - caller, - ), + (ComponentStatus::Added, _) => { + column.initialize(table_row, component_ptr, change_tick, caller); + } + (ComponentStatus::Existing, InsertMode::Replace) => { + column.replace(table_row, component_ptr, change_tick, caller); + } (ComponentStatus::Existing, InsertMode::Keep) => { if let Some(drop_fn) = table.get_drop_for(component_id) { drop_fn(component_ptr); @@ -664,13 +655,7 @@ impl BundleInfo { // SAFETY: If component_id is in self.component_ids, BundleInfo::new ensures that // a sparse set exists for the component. unsafe { sparse_sets.get_mut(component_id).debug_checked_unwrap() }; - sparse_set.insert( - entity, - component_ptr, - change_tick, - #[cfg(feature = "track_location")] - caller, - ); + sparse_set.insert(entity, component_ptr, change_tick, caller); } } bundle_component += 1; @@ -683,7 +668,6 @@ impl BundleInfo { change_tick, table_row, entity, - #[cfg(feature = "track_location")] caller, ); } @@ -712,7 +696,7 @@ impl BundleInfo { component_id: ComponentId, storage_type: StorageType, component_ptr: OwningPtr, - #[cfg(feature = "track_location")] caller: &'static Location<'static>, + caller: MaybeLocation, ) { { match storage_type { @@ -721,26 +705,14 @@ impl BundleInfo { // SAFETY: If component_id is in required_components, BundleInfo::new requires that // the target table contains the component. unsafe { table.get_column_mut(component_id).debug_checked_unwrap() }; - column.initialize( - table_row, - component_ptr, - change_tick, - #[cfg(feature = "track_location")] - caller, - ); + column.initialize(table_row, component_ptr, change_tick, caller); } StorageType::SparseSet => { let sparse_set = // SAFETY: If component_id is in required_components, BundleInfo::new requires that // a sparse set exists for the component. unsafe { sparse_sets.get_mut(component_id).debug_checked_unwrap() }; - sparse_set.insert( - entity, - component_ptr, - change_tick, - #[cfg(feature = "track_location")] - caller, - ); + sparse_set.insert(entity, component_ptr, change_tick, caller); } } } @@ -1127,7 +1099,7 @@ impl<'w> BundleInserter<'w> { location: EntityLocation, bundle: T, insert_mode: InsertMode, - #[cfg(feature = "track_location")] caller: &'static Location<'static>, + caller: MaybeLocation, ) -> (EntityLocation, T::Effect) { let bundle_info = self.bundle_info.as_ref(); let archetype_after_insert = self.archetype_after_insert.as_ref(); @@ -1145,7 +1117,6 @@ impl<'w> BundleInserter<'w> { ON_REPLACE, entity, archetype_after_insert.iter_existing(), - #[cfg(feature = "track_location")] caller, ); } @@ -1153,7 +1124,6 @@ impl<'w> BundleInserter<'w> { archetype, entity, archetype_after_insert.iter_existing(), - #[cfg(feature = "track_location")] caller, ); } @@ -1183,7 +1153,6 @@ impl<'w> BundleInserter<'w> { self.change_tick, bundle, insert_mode, - #[cfg(feature = "track_location")] caller, ); @@ -1225,7 +1194,6 @@ impl<'w> BundleInserter<'w> { self.change_tick, bundle, insert_mode, - #[cfg(feature = "track_location")] caller, ); @@ -1308,7 +1276,6 @@ impl<'w> BundleInserter<'w> { self.change_tick, bundle, insert_mode, - #[cfg(feature = "track_location")] caller, ); @@ -1327,7 +1294,6 @@ impl<'w> BundleInserter<'w> { new_archetype, entity, archetype_after_insert.iter_added(), - #[cfg(feature = "track_location")] caller, ); if new_archetype.has_add_observer() { @@ -1335,7 +1301,6 @@ impl<'w> BundleInserter<'w> { ON_ADD, entity, archetype_after_insert.iter_added(), - #[cfg(feature = "track_location")] caller, ); } @@ -1346,7 +1311,6 @@ impl<'w> BundleInserter<'w> { new_archetype, entity, archetype_after_insert.iter_inserted(), - #[cfg(feature = "track_location")] caller, ); if new_archetype.has_insert_observer() { @@ -1354,7 +1318,6 @@ impl<'w> BundleInserter<'w> { ON_INSERT, entity, archetype_after_insert.iter_inserted(), - #[cfg(feature = "track_location")] caller, ); } @@ -1366,7 +1329,6 @@ impl<'w> BundleInserter<'w> { new_archetype, entity, archetype_after_insert.iter_added(), - #[cfg(feature = "track_location")] caller, ); if new_archetype.has_insert_observer() { @@ -1374,7 +1336,6 @@ impl<'w> BundleInserter<'w> { ON_INSERT, entity, archetype_after_insert.iter_added(), - #[cfg(feature = "track_location")] caller, ); } @@ -1456,7 +1417,7 @@ impl<'w> BundleSpawner<'w> { &mut self, entity: Entity, bundle: T, - #[cfg(feature = "track_location")] caller: &'static Location<'static>, + caller: MaybeLocation, ) -> (EntityLocation, T::Effect) { // SAFETY: We do not make any structural changes to the archetype graph through self.world so these pointers always remain valid let bundle_info = self.bundle_info.as_ref(); @@ -1481,7 +1442,6 @@ impl<'w> BundleSpawner<'w> { self.change_tick, bundle, InsertMode::Replace, - #[cfg(feature = "track_location")] caller, ); entities.set(entity.index(), location); @@ -1499,7 +1459,6 @@ impl<'w> BundleSpawner<'w> { archetype, entity, bundle_info.iter_contributed_components(), - #[cfg(feature = "track_location")] caller, ); if archetype.has_add_observer() { @@ -1507,7 +1466,6 @@ impl<'w> BundleSpawner<'w> { ON_ADD, entity, bundle_info.iter_contributed_components(), - #[cfg(feature = "track_location")] caller, ); } @@ -1515,7 +1473,6 @@ impl<'w> BundleSpawner<'w> { archetype, entity, bundle_info.iter_contributed_components(), - #[cfg(feature = "track_location")] caller, ); if archetype.has_insert_observer() { @@ -1523,7 +1480,6 @@ impl<'w> BundleSpawner<'w> { ON_INSERT, entity, bundle_info.iter_contributed_components(), - #[cfg(feature = "track_location")] caller, ); } @@ -1538,18 +1494,11 @@ impl<'w> BundleSpawner<'w> { pub unsafe fn spawn( &mut self, bundle: T, - #[cfg(feature = "track_location")] caller: &'static Location<'static>, + caller: MaybeLocation, ) -> (Entity, T::Effect) { let entity = self.entities().alloc(); // SAFETY: entity is allocated (but non-existent), `T` matches this BundleInfo's type - let (_, after_effect) = unsafe { - self.spawn_non_existent( - entity, - bundle, - #[cfg(feature = "track_location")] - caller, - ) - }; + let (_, after_effect) = unsafe { self.spawn_non_existent(entity, bundle, caller) }; (entity, after_effect) } diff --git a/crates/bevy_ecs/src/change_detection.rs b/crates/bevy_ecs/src/change_detection.rs index 7af159e5bd..256ac89547 100644 --- a/crates/bevy_ecs/src/change_detection.rs +++ b/crates/bevy_ecs/src/change_detection.rs @@ -7,14 +7,13 @@ use crate::{ }; use alloc::borrow::ToOwned; use bevy_ptr::{Ptr, UnsafeCellDeref}; +#[cfg(feature = "bevy_reflect")] +use bevy_reflect::Reflect; use core::{ + marker::PhantomData, mem, ops::{Deref, DerefMut}, -}; -#[cfg(feature = "track_location")] -use { - bevy_ptr::ThinSlicePtr, - core::{cell::UnsafeCell, panic::Location}, + panic::Location, }; /// The (arbitrarily chosen) minimum number of world tick increments between `check_tick` scans. @@ -73,8 +72,7 @@ pub trait DetectChanges { fn last_changed(&self) -> Tick; /// The location that last caused this to change. - #[cfg(feature = "track_location")] - fn changed_by(&self) -> &'static Location<'static>; + fn changed_by(&self) -> MaybeLocation; } /// Types that implement reliable change detection. @@ -343,9 +341,8 @@ macro_rules! change_detection_impl { } #[inline] - #[cfg(feature = "track_location")] - fn changed_by(&self) -> &'static Location<'static> { - self.changed_by + fn changed_by(&self) -> MaybeLocation { + self.changed_by.copied() } } @@ -376,20 +373,14 @@ macro_rules! change_detection_mut_impl { #[track_caller] fn set_changed(&mut self) { *self.ticks.changed = self.ticks.this_run; - #[cfg(feature = "track_location")] - { - *self.changed_by = Location::caller(); - } + self.changed_by.assign(MaybeLocation::caller()); } #[inline] #[track_caller] fn set_last_changed(&mut self, last_changed: Tick) { *self.ticks.changed = last_changed; - #[cfg(feature = "track_location")] - { - *self.changed_by = Location::caller(); - } + self.changed_by.assign(MaybeLocation::caller()); } #[inline] @@ -403,10 +394,7 @@ macro_rules! change_detection_mut_impl { #[track_caller] fn deref_mut(&mut self) -> &mut Self::Target { self.set_changed(); - #[cfg(feature = "track_location")] - { - *self.changed_by = Location::caller(); - } + self.changed_by.assign(MaybeLocation::caller()); self.value } } @@ -444,8 +432,7 @@ macro_rules! impl_methods { last_run: self.ticks.last_run, this_run: self.ticks.this_run, }, - #[cfg(feature = "track_location")] - changed_by: self.changed_by, + changed_by: self.changed_by.as_deref_mut(), } } @@ -475,7 +462,6 @@ macro_rules! impl_methods { Mut { value: f(self.value), ticks: self.ticks, - #[cfg(feature = "track_location")] changed_by: self.changed_by, } } @@ -489,7 +475,6 @@ macro_rules! impl_methods { value.map(|value| Mut { value, ticks: self.ticks, - #[cfg(feature = "track_location")] changed_by: self.changed_by, }) } @@ -503,7 +488,6 @@ macro_rules! impl_methods { value.map(|value| Mut { value, ticks: self.ticks, - #[cfg(feature = "track_location")] changed_by: self.changed_by, }) } @@ -614,8 +598,7 @@ impl<'w> From> for Ticks<'w> { pub struct Res<'w, T: ?Sized + Resource> { pub(crate) value: &'w T, pub(crate) ticks: Ticks<'w>, - #[cfg(feature = "track_location")] - pub(crate) changed_by: &'static Location<'static>, + pub(crate) changed_by: MaybeLocation<&'w &'static Location<'static>>, } impl<'w, T: Resource> Res<'w, T> { @@ -631,7 +614,6 @@ impl<'w, T: Resource> Res<'w, T> { Self { value: this.value, ticks: this.ticks.clone(), - #[cfg(feature = "track_location")] changed_by: this.changed_by, } } @@ -649,8 +631,7 @@ impl<'w, T: Resource> From> for Res<'w, T> { Self { value: res.value, ticks: res.ticks.into(), - #[cfg(feature = "track_location")] - changed_by: res.changed_by, + changed_by: res.changed_by.map(|changed_by| &*changed_by), } } } @@ -662,7 +643,6 @@ impl<'w, T: Resource> From> for Ref<'w, T> { Self { value: res.value, ticks: res.ticks, - #[cfg(feature = "track_location")] changed_by: res.changed_by, } } @@ -695,8 +675,7 @@ impl_debug!(Res<'w, T>, Resource); pub struct ResMut<'w, T: ?Sized + Resource> { pub(crate) value: &'w mut T, pub(crate) ticks: TicksMut<'w>, - #[cfg(feature = "track_location")] - pub(crate) changed_by: &'w mut &'static Location<'static>, + pub(crate) changed_by: MaybeLocation<&'w mut &'static Location<'static>>, } impl<'w, 'a, T: Resource> IntoIterator for &'a ResMut<'w, T> @@ -736,7 +715,6 @@ impl<'w, T: Resource> From> for Mut<'w, T> { Mut { value: other.value, ticks: other.ticks, - #[cfg(feature = "track_location")] changed_by: other.changed_by, } } @@ -756,8 +734,7 @@ impl<'w, T: Resource> From> for Mut<'w, T> { pub struct NonSendMut<'w, T: ?Sized + 'static> { pub(crate) value: &'w mut T, pub(crate) ticks: TicksMut<'w>, - #[cfg(feature = "track_location")] - pub(crate) changed_by: &'w mut &'static Location<'static>, + pub(crate) changed_by: MaybeLocation<&'w mut &'static Location<'static>>, } change_detection_impl!(NonSendMut<'w, T>, T,); @@ -772,7 +749,6 @@ impl<'w, T: 'static> From> for Mut<'w, T> { Mut { value: other.value, ticks: other.ticks, - #[cfg(feature = "track_location")] changed_by: other.changed_by, } } @@ -805,8 +781,7 @@ impl<'w, T: 'static> From> for Mut<'w, T> { pub struct Ref<'w, T: ?Sized> { pub(crate) value: &'w T, pub(crate) ticks: Ticks<'w>, - #[cfg(feature = "track_location")] - pub(crate) changed_by: &'static Location<'static>, + pub(crate) changed_by: MaybeLocation<&'w &'static Location<'static>>, } impl<'w, T: ?Sized> Ref<'w, T> { @@ -823,7 +798,6 @@ impl<'w, T: ?Sized> Ref<'w, T> { Ref { value: f(self.value), ticks: self.ticks, - #[cfg(feature = "track_location")] changed_by: self.changed_by, } } @@ -845,7 +819,7 @@ impl<'w, T: ?Sized> Ref<'w, T> { changed: &'w Tick, last_run: Tick, this_run: Tick, - #[cfg(feature = "track_location")] caller: &'static Location<'static>, + caller: MaybeLocation<&'w &'static Location<'static>>, ) -> Ref<'w, T> { Ref { value, @@ -855,7 +829,6 @@ impl<'w, T: ?Sized> Ref<'w, T> { last_run, this_run, }, - #[cfg(feature = "track_location")] changed_by: caller, } } @@ -938,8 +911,7 @@ impl_debug!(Ref<'w, T>,); pub struct Mut<'w, T: ?Sized> { pub(crate) value: &'w mut T, pub(crate) ticks: TicksMut<'w>, - #[cfg(feature = "track_location")] - pub(crate) changed_by: &'w mut &'static Location<'static>, + pub(crate) changed_by: MaybeLocation<&'w mut &'static Location<'static>>, } impl<'w, T: ?Sized> Mut<'w, T> { @@ -964,7 +936,7 @@ impl<'w, T: ?Sized> Mut<'w, T> { last_changed: &'w mut Tick, last_run: Tick, this_run: Tick, - #[cfg(feature = "track_location")] caller: &'w mut &'static Location<'static>, + caller: MaybeLocation<&'w mut &'static Location<'static>>, ) -> Self { Self { value, @@ -974,7 +946,6 @@ impl<'w, T: ?Sized> Mut<'w, T> { last_run, this_run, }, - #[cfg(feature = "track_location")] changed_by: caller, } } @@ -985,8 +956,7 @@ impl<'w, T: ?Sized> From> for Ref<'w, T> { Self { value: mut_ref.value, ticks: mut_ref.ticks.into(), - #[cfg(feature = "track_location")] - changed_by: mut_ref.changed_by, + changed_by: mut_ref.changed_by.map(|changed_by| &*changed_by), } } } @@ -1032,8 +1002,7 @@ impl_debug!(Mut<'w, T>,); pub struct MutUntyped<'w> { pub(crate) value: PtrMut<'w>, pub(crate) ticks: TicksMut<'w>, - #[cfg(feature = "track_location")] - pub(crate) changed_by: &'w mut &'static Location<'static>, + pub(crate) changed_by: MaybeLocation<&'w mut &'static Location<'static>>, } impl<'w> MutUntyped<'w> { @@ -1058,8 +1027,7 @@ impl<'w> MutUntyped<'w> { last_run: self.ticks.last_run, this_run: self.ticks.this_run, }, - #[cfg(feature = "track_location")] - changed_by: self.changed_by, + changed_by: self.changed_by.as_deref_mut(), } } @@ -1110,7 +1078,6 @@ impl<'w> MutUntyped<'w> { Mut { value: f(self.value), ticks: self.ticks, - #[cfg(feature = "track_location")] changed_by: self.changed_by, } } @@ -1125,7 +1092,6 @@ impl<'w> MutUntyped<'w> { value: unsafe { self.value.deref_mut() }, ticks: self.ticks, // SAFETY: `caller` is `Aligned`. - #[cfg(feature = "track_location")] changed_by: self.changed_by, } } @@ -1152,9 +1118,8 @@ impl<'w> DetectChanges for MutUntyped<'w> { } #[inline] - #[cfg(feature = "track_location")] - fn changed_by(&self) -> &'static Location<'static> { - self.changed_by + fn changed_by(&self) -> MaybeLocation { + self.changed_by.copied() } } @@ -1165,20 +1130,14 @@ impl<'w> DetectChangesMut for MutUntyped<'w> { #[track_caller] fn set_changed(&mut self) { *self.ticks.changed = self.ticks.this_run; - #[cfg(feature = "track_location")] - { - *self.changed_by = Location::caller(); - } + self.changed_by.assign(MaybeLocation::caller()); } #[inline] #[track_caller] fn set_last_changed(&mut self, last_changed: Tick) { *self.ticks.changed = last_changed; - #[cfg(feature = "track_location")] - { - *self.changed_by = Location::caller(); - } + self.changed_by.assign(MaybeLocation::caller()); } #[inline] @@ -1201,62 +1160,294 @@ impl<'w, T> From> for MutUntyped<'w> { MutUntyped { value: value.value.into(), ticks: value.ticks, - #[cfg(feature = "track_location")] changed_by: value.changed_by, } } } -/// A type alias to [`&'static Location<'static>`](std::panic::Location) when the `track_location` feature is -/// enabled, and the unit type `()` when it is not. +/// A value that contains a `T` if the `track_location` feature is enabled, +/// and is a ZST if it is not. /// -/// This is primarily used in places where `#[cfg(...)]` attributes are not allowed, such as -/// function return types. Because unit is a zero-sized type, it is the equivalent of not using a -/// `Location` at all. +/// The overall API is similar to [`Option`], but whether the value is `Some` or `None` is set at compile +/// time and is the same for all values. /// -/// Please use this type sparingly: prefer normal `#[cfg(...)]` attributes when possible. -#[cfg(feature = "track_location")] -pub(crate) type MaybeLocation = &'static Location<'static>; +/// If the `track_location` feature is disabled, then all functions on this type that return +/// an `MaybeLocation` will have an empty body and should be removed by the optimizer. +/// +/// This allows code to be written that will be checked by the compiler even when the feature is disabled, +/// but that will be entirely removed during compilation. +#[cfg_attr(feature = "bevy_reflect", derive(Reflect))] +#[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct MaybeLocation> { + #[cfg_attr(feature = "bevy_reflect", reflect(ignore))] + marker: PhantomData, + #[cfg(feature = "track_location")] + value: T, +} -/// A type alias to [`&'static Location<'static>`](std::panic::Location) when the `track_location` feature is -/// enabled, and the unit type `()` when it is not. -/// -/// This is primarily used in places where `#[cfg(...)]` attributes are not allowed, such as -/// function return types. Because unit is a zero-sized type, it is the equivalent of not using a -/// `Location` at all. -/// -/// Please use this type sparingly: prefer normal `#[cfg(...)]` attributes when possible. -#[cfg(not(feature = "track_location"))] -pub(crate) type MaybeLocation = (); +impl core::fmt::Display for MaybeLocation { + fn fmt(&self, _f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + #[cfg(feature = "track_location")] + { + self.value.fmt(_f)?; + } + Ok(()) + } +} -/// A type alias to `&UnsafeCell<&'static Location<'static>>` when the `track_location` -/// feature is enabled, and the unit type `()` when it is not. -/// -/// See [`MaybeLocation`] for further information. -#[cfg(feature = "track_location")] -pub(crate) type MaybeUnsafeCellLocation<'a> = &'a UnsafeCell<&'static Location<'static>>; +impl MaybeLocation { + /// Constructs a new `MaybeLocation` that wraps the given value. + /// + /// This may only accept `Copy` types, + /// since it needs to drop the value if the `track_location` feature is disabled, + /// and non-`Copy` types cannot be dropped in `const` context. + /// Use [`new_with`][Self::new_with] if you need to construct a non-`Copy` value. + /// + /// # See also + /// - [`new_with`][Self::new_with] to initialize using a closure. + /// - [`new_with_flattened`][Self::new_with_flattened] to initialize using a closure that returns an `Option>`. + #[inline] + pub const fn new(_value: T) -> Self + where + T: Copy, + { + Self { + #[cfg(feature = "track_location")] + value: _value, + marker: PhantomData, + } + } -/// A type alias to `&UnsafeCell<&'static Location<'static>>` when the `track_location` -/// feature is enabled, and the unit type `()` when it is not. -/// -/// See [`MaybeLocation`] for further information. -#[cfg(not(feature = "track_location"))] -pub(crate) type MaybeUnsafeCellLocation<'a> = (); + /// Constructs a new `MaybeLocation` that wraps the result of the given closure. + /// + /// # See also + /// - [`new`][Self::new] to initialize using a value. + /// - [`new_with_flattened`][Self::new_with_flattened] to initialize using a closure that returns an `Option>`. + #[inline] + pub fn new_with(_f: impl FnOnce() -> T) -> Self { + Self { + #[cfg(feature = "track_location")] + value: _f(), + marker: PhantomData, + } + } -/// A type alias to `ThinSlicePtr<'w, UnsafeCell<&'static Location<'static>>>` when the -/// `track_location` feature is enabled, and the unit type `()` when it is not. -/// -/// See [`MaybeLocation`] for further information. -#[cfg(feature = "track_location")] -pub(crate) type MaybeThinSlicePtrLocation<'w> = - ThinSlicePtr<'w, UnsafeCell<&'static Location<'static>>>; + /// Maps an `MaybeLocation `to `MaybeLocation` by applying a function to a contained value. + #[inline] + pub fn map(self, _f: impl FnOnce(T) -> U) -> MaybeLocation { + MaybeLocation { + #[cfg(feature = "track_location")] + value: _f(self.value), + marker: PhantomData, + } + } -/// A type alias to `ThinSlicePtr<'w, UnsafeCell<&'static Location<'static>>>` when the -/// `track_location` feature is enabled, and the unit type `()` when it is not. -/// -/// See [`MaybeLocation`] for further information. -#[cfg(not(feature = "track_location"))] -pub(crate) type MaybeThinSlicePtrLocation<'w> = (); + /// Converts a pair of `MaybeLocation` values to an `MaybeLocation` of a tuple. + #[inline] + pub fn zip(self, _other: MaybeLocation) -> MaybeLocation<(T, U)> { + MaybeLocation { + #[cfg(feature = "track_location")] + value: (self.value, _other.value), + marker: PhantomData, + } + } + + /// Returns the contained value or a default. + /// If the `track_location` feature is enabled, this always returns the contained value. + /// If it is disabled, this always returns `T::Default()`. + #[inline] + pub fn unwrap_or_default(self) -> T + where + T: Default, + { + self.into_option().unwrap_or_default() + } + + /// Converts an `MaybeLocation` to an [`Option`] to allow run-time branching. + /// If the `track_location` feature is enabled, this always returns `Some`. + /// If it is disabled, this always returns `None`. + #[inline] + pub fn into_option(self) -> Option { + #[cfg(feature = "track_location")] + { + Some(self.value) + } + #[cfg(not(feature = "track_location"))] + { + None + } + } +} + +impl MaybeLocation> { + /// Constructs a new `MaybeLocation` that wraps the result of the given closure. + /// If the closure returns `Some`, it unwraps the inner value. + /// + /// # See also + /// - [`new`][Self::new] to initialize using a value. + /// - [`new_with`][Self::new_with] to initialize using a closure. + #[inline] + pub fn new_with_flattened(_f: impl FnOnce() -> Option>) -> Self { + Self { + #[cfg(feature = "track_location")] + value: _f().map(|value| value.value), + marker: PhantomData, + } + } + + /// Transposes a `MaybeLocation` of an [`Option`] into an [`Option`] of a `MaybeLocation`. + /// + /// This can be useful if you want to use the `?` operator to exit early + /// if the `track_location` feature is enabled but the value is not found. + /// + /// If the `track_location` feature is enabled, + /// this returns `Some` if the inner value is `Some` + /// and `None` if the inner value is `None`. + /// + /// If it is disabled, this always returns `Some`. + /// + /// # Example + /// + /// ``` + /// # use bevy_ecs::{change_detection::MaybeLocation, world::World}; + /// # use core::panic::Location; + /// # + /// # fn test() -> Option<()> { + /// let mut world = World::new(); + /// let entity = world.spawn(()).id(); + /// let location: MaybeLocation>> = + /// world.entities().entity_get_spawned_or_despawned_by(entity); + /// let location: MaybeLocation<&'static Location<'static>> = location.transpose()?; + /// # Some(()) + /// # } + /// # test(); + /// ``` + /// + /// # See also + /// + /// - [`into_option`][Self::into_option] to convert to an `Option>`. + /// When used with [`Option::flatten`], this will have a similar effect, + /// but will return `None` when the `track_location` feature is disabled. + #[inline] + pub fn transpose(self) -> Option> { + #[cfg(feature = "track_location")] + { + self.value.map(|value| MaybeLocation { + value, + marker: PhantomData, + }) + } + #[cfg(not(feature = "track_location"))] + { + Some(MaybeLocation { + marker: PhantomData, + }) + } + } +} + +impl MaybeLocation<&T> { + /// Maps an `MaybeLocation<&T>` to an `MaybeLocation` by copying the contents. + #[inline] + pub const fn copied(&self) -> MaybeLocation + where + T: Copy, + { + MaybeLocation { + #[cfg(feature = "track_location")] + value: *self.value, + marker: PhantomData, + } + } +} + +impl MaybeLocation<&mut T> { + /// Maps an `MaybeLocation<&mut T>` to an `MaybeLocation` by copying the contents. + #[inline] + pub const fn copied(&self) -> MaybeLocation + where + T: Copy, + { + MaybeLocation { + #[cfg(feature = "track_location")] + value: *self.value, + marker: PhantomData, + } + } + + /// Assigns the contents of an `MaybeLocation` to an `MaybeLocation<&mut T>`. + #[inline] + pub fn assign(&mut self, _value: MaybeLocation) { + #[cfg(feature = "track_location")] + { + *self.value = _value.value; + } + } +} + +impl MaybeLocation { + /// Converts from `&MaybeLocation` to `MaybeLocation<&T>`. + #[inline] + pub const fn as_ref(&self) -> MaybeLocation<&T> { + MaybeLocation { + #[cfg(feature = "track_location")] + value: &self.value, + marker: PhantomData, + } + } + + /// Converts from `&mut MaybeLocation` to `MaybeLocation<&mut T>`. + #[inline] + pub const fn as_mut(&mut self) -> MaybeLocation<&mut T> { + MaybeLocation { + #[cfg(feature = "track_location")] + value: &mut self.value, + marker: PhantomData, + } + } + + /// Converts from `&MaybeLocation` to `MaybeLocation<&T::Target>`. + #[inline] + pub fn as_deref(&self) -> MaybeLocation<&T::Target> + where + T: Deref, + { + MaybeLocation { + #[cfg(feature = "track_location")] + value: &*self.value, + marker: PhantomData, + } + } + + /// Converts from `&mut MaybeLocation` to `MaybeLocation<&mut T::Target>`. + #[inline] + pub fn as_deref_mut(&mut self) -> MaybeLocation<&mut T::Target> + where + T: DerefMut, + { + MaybeLocation { + #[cfg(feature = "track_location")] + value: &mut *self.value, + marker: PhantomData, + } + } +} + +impl MaybeLocation { + /// Returns the source location of the caller of this function. If that function's caller is + /// annotated then its call location will be returned, and so on up the stack to the first call + /// within a non-tracked function body. + #[inline] + #[track_caller] + pub fn caller() -> Self { + // Note that this cannot use `new_with`, since `FnOnce` invocations cannot be annotated with `#[track_caller]`. + MaybeLocation { + #[cfg(feature = "track_location")] + value: Location::caller(), + marker: PhantomData, + } + } +} #[cfg(test)] mod tests { @@ -1264,12 +1455,11 @@ mod tests { use bevy_ptr::PtrMut; use bevy_reflect::{FromType, ReflectFromPtr}; use core::ops::{Deref, DerefMut}; - #[cfg(feature = "track_location")] - use core::panic::Location; use crate::{ change_detection::{ - Mut, NonSendMut, Ref, ResMut, TicksMut, CHECK_TICK_THRESHOLD, MAX_CHANGE_AGE, + MaybeLocation, Mut, NonSendMut, Ref, ResMut, TicksMut, CHECK_TICK_THRESHOLD, + MAX_CHANGE_AGE, }, component::{Component, ComponentTicks, Tick}, system::{IntoSystem, Single, System}, @@ -1395,14 +1585,12 @@ mod tests { this_run: Tick::new(4), }; let mut res = R {}; - #[cfg(feature = "track_location")] - let mut caller = Location::caller(); + let mut caller = MaybeLocation::caller(); let res_mut = ResMut { value: &mut res, ticks, - #[cfg(feature = "track_location")] - changed_by: &mut caller, + changed_by: caller.as_mut(), }; let into_mut: Mut = res_mut.into(); @@ -1419,8 +1607,7 @@ mod tests { changed: Tick::new(3), }; let mut res = R {}; - #[cfg(feature = "track_location")] - let mut caller = Location::caller(); + let mut caller = MaybeLocation::caller(); let val = Mut::new( &mut res, @@ -1428,8 +1615,7 @@ mod tests { &mut component_ticks.changed, Tick::new(2), // last_run Tick::new(4), // this_run - #[cfg(feature = "track_location")] - &mut caller, + caller.as_mut(), ); assert!(!val.is_added()); @@ -1449,14 +1635,12 @@ mod tests { this_run: Tick::new(4), }; let mut res = R {}; - #[cfg(feature = "track_location")] - let mut caller = Location::caller(); + let mut caller = MaybeLocation::caller(); let non_send_mut = NonSendMut { value: &mut res, ticks, - #[cfg(feature = "track_location")] - changed_by: &mut caller, + changed_by: caller.as_mut(), }; let into_mut: Mut = non_send_mut.into(); @@ -1485,14 +1669,12 @@ mod tests { }; let mut outer = Outer(0); - #[cfg(feature = "track_location")] - let mut caller = Location::caller(); + let mut caller = MaybeLocation::caller(); let ptr = Mut { value: &mut outer, ticks, - #[cfg(feature = "track_location")] - changed_by: &mut caller, + changed_by: caller.as_mut(), }; assert!(!ptr.is_changed()); @@ -1575,14 +1757,12 @@ mod tests { }; let mut value: i32 = 5; - #[cfg(feature = "track_location")] - let mut caller = Location::caller(); + let mut caller = MaybeLocation::caller(); let value = MutUntyped { value: PtrMut::from(&mut value), ticks, - #[cfg(feature = "track_location")] - changed_by: &mut caller, + changed_by: caller.as_mut(), }; let reflect_from_ptr = >::from_type(); @@ -1613,14 +1793,12 @@ mod tests { this_run: Tick::new(4), }; let mut c = C {}; - #[cfg(feature = "track_location")] - let mut caller = Location::caller(); + let mut caller = MaybeLocation::caller(); let mut_typed = Mut { value: &mut c, ticks, - #[cfg(feature = "track_location")] - changed_by: &mut caller, + changed_by: caller.as_mut(), }; let into_mut: MutUntyped = mut_typed.into(); diff --git a/crates/bevy_ecs/src/component.rs b/crates/bevy_ecs/src/component.rs index f8e1ee5a69..0eaa329c61 100644 --- a/crates/bevy_ecs/src/component.rs +++ b/crates/bevy_ecs/src/component.rs @@ -3,7 +3,7 @@ use crate::{ archetype::ArchetypeFlags, bundle::BundleInfo, - change_detection::MAX_CHANGE_AGE, + change_detection::{MaybeLocation, MAX_CHANGE_AGE}, entity::{ComponentCloneCtx, Entity}, query::DebugCheckedUnwrap, resource::Resource, @@ -28,7 +28,6 @@ use core::{ fmt::Debug, marker::PhantomData, mem::needs_drop, - panic::Location, }; use disqualified::ShortName; use thiserror::Error; @@ -544,7 +543,7 @@ pub struct HookContext { /// The [`ComponentId`] this hook was invoked for. pub component_id: ComponentId, /// The caller location is `Some` if the `track_caller` feature is enabled. - pub caller: Option<&'static Location<'static>>, + pub caller: MaybeLocation, } /// [`World`]-mutating functions that run as part of lifecycle events of a [`Component`]. @@ -1936,17 +1935,9 @@ pub enum RequiredComponentsError { } /// A Required Component constructor. See [`Component`] for details. -#[cfg(feature = "track_location")] #[derive(Clone)] pub struct RequiredComponentConstructor( - pub Arc)>, -); - -/// A Required Component constructor. See [`Component`] for details. -#[cfg(not(feature = "track_location"))] -#[derive(Clone)] -pub struct RequiredComponentConstructor( - pub Arc, + pub Arc, ); impl RequiredComponentConstructor { @@ -1966,17 +1957,9 @@ impl RequiredComponentConstructor { change_tick: Tick, table_row: TableRow, entity: Entity, - #[cfg(feature = "track_location")] caller: &'static Location<'static>, + caller: MaybeLocation, ) { - (self.0)( - table, - sparse_sets, - change_tick, - table_row, - entity, - #[cfg(feature = "track_location")] - caller, - ); + (self.0)(table, sparse_sets, change_tick, table_row, entity, caller); } } @@ -2080,20 +2063,15 @@ impl RequiredComponents { #[cfg(feature = "portable-atomic")] use alloc::boxed::Box; - #[cfg(feature = "track_location")] type Constructor = dyn for<'a, 'b> Fn( &'a mut Table, &'b mut SparseSets, Tick, TableRow, Entity, - &'static Location<'static>, + MaybeLocation, ); - #[cfg(not(feature = "track_location"))] - type Constructor = - dyn for<'a, 'b> Fn(&'a mut Table, &'b mut SparseSets, Tick, TableRow, Entity); - #[cfg(feature = "portable-atomic")] type Intermediate = Box; @@ -2101,12 +2079,7 @@ impl RequiredComponents { type Intermediate = Arc; let boxed: Intermediate = Intermediate::new( - move |table, - sparse_sets, - change_tick, - table_row, - entity, - #[cfg(feature = "track_location")] caller| { + move |table, sparse_sets, change_tick, table_row, entity, caller| { OwningPtr::make(constructor(), |ptr| { // SAFETY: This will only be called in the context of `BundleInfo::write_components`, which will // pass in a valid table_row and entity requiring a C constructor @@ -2122,7 +2095,6 @@ impl RequiredComponents { component_id, C::STORAGE_TYPE, ptr, - #[cfg(feature = "track_location")] caller, ); } diff --git a/crates/bevy_ecs/src/entity/mod.rs b/crates/bevy_ecs/src/entity/mod.rs index 3f3ef9370b..e3a1f71606 100644 --- a/crates/bevy_ecs/src/entity/mod.rs +++ b/crates/bevy_ecs/src/entity/mod.rs @@ -72,6 +72,7 @@ pub use unique_slice::*; use crate::{ archetype::{ArchetypeId, ArchetypeRow}, + change_detection::MaybeLocation, identifier::{ error::IdentifierError, kinds::IdKind, @@ -82,12 +83,9 @@ use crate::{ }; use alloc::vec::Vec; use bevy_platform_support::sync::atomic::Ordering; -use core::{fmt, hash::Hash, mem, num::NonZero}; +use core::{fmt, hash::Hash, mem, num::NonZero, panic::Location}; use log::warn; -#[cfg(feature = "track_location")] -use core::panic::Location; - #[cfg(feature = "serialize")] use serde::{Deserialize, Serialize}; @@ -984,32 +982,35 @@ impl Entities { /// Sets the source code location from which this entity has last been spawned /// or despawned. - #[cfg(feature = "track_location")] #[inline] - pub(crate) fn set_spawned_or_despawned_by(&mut self, index: u32, caller: &'static Location) { - let meta = self - .meta - .get_mut(index as usize) - .expect("Entity index invalid"); - meta.spawned_or_despawned_by = Some(caller); + pub(crate) fn set_spawned_or_despawned_by(&mut self, index: u32, caller: MaybeLocation) { + caller.map(|caller| { + let meta = self + .meta + .get_mut(index as usize) + .expect("Entity index invalid"); + meta.spawned_or_despawned_by = MaybeLocation::new(Some(caller)); + }); } /// Returns the source code location from which this entity has last been spawned /// or despawned. Returns `None` if its index has been reused by another entity /// or if this entity has never existed. - #[cfg(feature = "track_location")] pub fn entity_get_spawned_or_despawned_by( &self, entity: Entity, - ) -> Option<&'static Location<'static>> { - self.meta - .get(entity.index() as usize) - .filter(|meta| + ) -> MaybeLocation>> { + MaybeLocation::new_with_flattened(|| { + self.meta + .get(entity.index() as usize) + .filter(|meta| // Generation is incremented immediately upon despawn (meta.generation == entity.generation) || (meta.location.archetype_id == ArchetypeId::INVALID) && (meta.generation == IdentifierMask::inc_masked_high_by(entity.generation, 1))) - .and_then(|meta| meta.spawned_or_despawned_by) + .map(|meta| meta.spawned_or_despawned_by) + }) + .map(Option::flatten) } /// Constructs a message explaining why an entity does not exists, if known. @@ -1018,7 +1019,6 @@ impl Entities { _entity: Entity, ) -> EntityDoesNotExistDetails { EntityDoesNotExistDetails { - #[cfg(feature = "track_location")] location: self.entity_get_spawned_or_despawned_by(_entity), } } @@ -1028,26 +1028,22 @@ impl Entities { /// regarding an entity that did not exist. #[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct EntityDoesNotExistDetails { - #[cfg(feature = "track_location")] - location: Option<&'static Location<'static>>, + location: MaybeLocation>>, } impl fmt::Display for EntityDoesNotExistDetails { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - #[cfg(feature = "track_location")] - if let Some(location) = self.location { - write!(f, "was despawned by {location}") - } else { - write!( + match self.location.into_option() { + Some(Some(location)) => write!(f, "was despawned by {location}"), + Some(None) => write!( f, "does not exist (index has been reused or was never spawned)" - ) + ), + None => write!( + f, + "does not exist (enable `track_location` feature for more details)" + ), } - #[cfg(not(feature = "track_location"))] - write!( - f, - "does not exist (enable `track_location` feature for more details)" - ) } } @@ -1058,8 +1054,7 @@ struct EntityMeta { /// The current location of the [`Entity`] pub location: EntityLocation, /// Location of the last spawn or despawn of this entity - #[cfg(feature = "track_location")] - spawned_or_despawned_by: Option<&'static Location<'static>>, + spawned_or_despawned_by: MaybeLocation>>, } impl EntityMeta { @@ -1067,8 +1062,7 @@ impl EntityMeta { const EMPTY: EntityMeta = EntityMeta { generation: NonZero::::MIN, location: EntityLocation::INVALID, - #[cfg(feature = "track_location")] - spawned_or_despawned_by: None, + spawned_or_despawned_by: MaybeLocation::new(None), }; } diff --git a/crates/bevy_ecs/src/event/base.rs b/crates/bevy_ecs/src/event/base.rs index 2b5c090504..42443d38b2 100644 --- a/crates/bevy_ecs/src/event/base.rs +++ b/crates/bevy_ecs/src/event/base.rs @@ -1,10 +1,9 @@ +use crate::change_detection::MaybeLocation; use crate::component::ComponentId; use crate::world::World; use crate::{component::Component, traversal::Traversal}; #[cfg(feature = "bevy_reflect")] use bevy_reflect::Reflect; -#[cfg(feature = "track_location")] -use core::panic::Location; use core::{ cmp::Ordering, fmt, @@ -105,8 +104,7 @@ pub struct EventId { // This value corresponds to the order in which each event was added to the world. pub id: usize, /// The source code location that triggered this event. - #[cfg(feature = "track_location")] - pub caller: &'static Location<'static>, + pub caller: MaybeLocation, #[cfg_attr(feature = "bevy_reflect", reflect(ignore))] pub(super) _marker: PhantomData, } diff --git a/crates/bevy_ecs/src/event/collections.rs b/crates/bevy_ecs/src/event/collections.rs index a62dcfb30c..faa52ecd18 100644 --- a/crates/bevy_ecs/src/event/collections.rs +++ b/crates/bevy_ecs/src/event/collections.rs @@ -1,10 +1,9 @@ use alloc::vec::Vec; use bevy_ecs::{ + change_detection::MaybeLocation, event::{Event, EventCursor, EventId, EventInstance}, resource::Resource, }; -#[cfg(feature = "track_location")] -use core::panic::Location; use core::{ marker::PhantomData, ops::{Deref, DerefMut}, @@ -123,21 +122,12 @@ impl Events { /// This method returns the [ID](`EventId`) of the sent `event`. #[track_caller] pub fn send(&mut self, event: E) -> EventId { - self.send_with_caller( - event, - #[cfg(feature = "track_location")] - Location::caller(), - ) + self.send_with_caller(event, MaybeLocation::caller()) } - pub(crate) fn send_with_caller( - &mut self, - event: E, - #[cfg(feature = "track_location")] caller: &'static Location<'static>, - ) -> EventId { + pub(crate) fn send_with_caller(&mut self, event: E, caller: MaybeLocation) -> EventId { let event_id = EventId { id: self.event_count, - #[cfg(feature = "track_location")] caller, _marker: PhantomData, }; @@ -307,8 +297,7 @@ impl Extend for Events { let events = iter.into_iter().map(|event| { let event_id = EventId { id: event_count, - #[cfg(feature = "track_location")] - caller: Location::caller(), + caller: MaybeLocation::caller(), _marker: PhantomData, }; event_count += 1; @@ -378,8 +367,7 @@ impl Iterator for SendBatchIds { let result = Some(EventId { id: self.last_count, - #[cfg(feature = "track_location")] - caller: Location::caller(), + caller: MaybeLocation::caller(), _marker: PhantomData, }); diff --git a/crates/bevy_ecs/src/observer/mod.rs b/crates/bevy_ecs/src/observer/mod.rs index 8c22ace911..c44e961368 100644 --- a/crates/bevy_ecs/src/observer/mod.rs +++ b/crates/bevy_ecs/src/observer/mod.rs @@ -8,6 +8,7 @@ pub use runner::*; use crate::{ archetype::ArchetypeFlags, + change_detection::MaybeLocation, component::ComponentId, entity::hash_map::EntityHashMap, prelude::*, @@ -24,9 +25,6 @@ use core::{ }; use smallvec::SmallVec; -#[cfg(feature = "track_location")] -use core::panic::Location; - /// Type containing triggered [`Event`] information for a given run of an [`Observer`]. This contains the /// [`Event`] data itself. If it was triggered for a specific [`Entity`], it includes that as well. It also /// contains event propagation information. See [`Trigger::propagate`] for more information. @@ -143,8 +141,7 @@ impl<'w, E, B: Bundle> Trigger<'w, E, B> { } /// Returns the source code location that triggered this observer. - #[cfg(feature = "track_location")] - pub fn caller(&self) -> &'static Location<'static> { + pub fn caller(&self) -> MaybeLocation { self.trigger.caller } } @@ -335,10 +332,8 @@ pub struct ObserverTrigger { components: SmallVec<[ComponentId; 2]>, /// The entity the trigger targeted. pub target: Entity, - /// The location of the source code that triggered the obserer. - #[cfg(feature = "track_location")] - pub caller: &'static Location<'static>, + pub caller: MaybeLocation, } impl ObserverTrigger { @@ -415,7 +410,7 @@ impl Observers { components: impl Iterator + Clone, data: &mut T, propagate: &mut bool, - #[cfg(feature = "track_location")] caller: &'static Location<'static>, + caller: MaybeLocation, ) { // SAFETY: You cannot get a mutable reference to `observers` from `DeferredWorld` let (mut world, observers) = unsafe { @@ -440,7 +435,6 @@ impl Observers { event_type, components: components.clone().collect(), target, - #[cfg(feature = "track_location")] caller, }, data.into(), @@ -565,28 +559,14 @@ impl World { /// If you need to use the event after triggering it, use [`World::trigger_ref`] instead. #[track_caller] pub fn trigger(&mut self, event: E) { - self.trigger_with_caller( - event, - #[cfg(feature = "track_location")] - Location::caller(), - ); + self.trigger_with_caller(event, MaybeLocation::caller()); } - pub(crate) fn trigger_with_caller( - &mut self, - mut event: E, - #[cfg(feature = "track_location")] caller: &'static Location<'static>, - ) { + pub(crate) fn trigger_with_caller(&mut self, mut event: E, caller: MaybeLocation) { let event_id = E::register_component_id(self); // SAFETY: We just registered `event_id` with the type of `event` unsafe { - self.trigger_targets_dynamic_ref_with_caller( - event_id, - &mut event, - (), - #[cfg(feature = "track_location")] - caller, - ); + self.trigger_targets_dynamic_ref_with_caller(event_id, &mut event, (), caller); } } @@ -608,30 +588,19 @@ impl World { /// If you need to use the event after triggering it, use [`World::trigger_targets_ref`] instead. #[track_caller] pub fn trigger_targets(&mut self, event: E, targets: impl TriggerTargets) { - self.trigger_targets_with_caller( - event, - targets, - #[cfg(feature = "track_location")] - Location::caller(), - ); + self.trigger_targets_with_caller(event, targets, MaybeLocation::caller()); } pub(crate) fn trigger_targets_with_caller( &mut self, mut event: E, targets: impl TriggerTargets, - #[cfg(feature = "track_location")] caller: &'static Location<'static>, + caller: MaybeLocation, ) { let event_id = E::register_component_id(self); // SAFETY: We just registered `event_id` with the type of `event` unsafe { - self.trigger_targets_dynamic_ref_with_caller( - event_id, - &mut event, - targets, - #[cfg(feature = "track_location")] - caller, - ); + self.trigger_targets_dynamic_ref_with_caller(event_id, &mut event, targets, caller); } } @@ -689,8 +658,7 @@ impl World { event_id, event_data, targets, - #[cfg(feature = "track_location")] - Location::caller(), + MaybeLocation::caller(), ); } @@ -702,7 +670,7 @@ impl World { event_id: ComponentId, event_data: &mut E, targets: Targets, - #[cfg(feature = "track_location")] caller: &'static Location<'static>, + caller: MaybeLocation, ) { let mut world = DeferredWorld::from(self); if targets.entities().is_empty() { @@ -714,7 +682,6 @@ impl World { targets.components(), event_data, false, - #[cfg(feature = "track_location")] caller, ); }; @@ -728,7 +695,6 @@ impl World { targets.components(), event_data, E::AUTO_PROPAGATE, - #[cfg(feature = "track_location")] caller, ); }; @@ -858,14 +824,13 @@ impl World { #[cfg(test)] mod tests { use alloc::{vec, vec::Vec}; - #[cfg(feature = "track_location")] - use core::panic::Location; use bevy_platform_support::collections::HashMap; use bevy_ptr::OwningPtr; use crate::component::ComponentId; use crate::{ + change_detection::MaybeLocation, observer::{Observer, ObserverDescriptor, ObserverState, OnReplace}, prelude::*, traversal::Traversal, @@ -1615,13 +1580,12 @@ mod tests { } #[test] - #[cfg(feature = "track_location")] #[track_caller] fn observer_caller_location_event() { #[derive(Event)] struct EventA; - let caller = Location::caller(); + let caller = MaybeLocation::caller(); let mut world = World::new(); world.add_observer(move |trigger: Trigger| { assert_eq!(trigger.caller(), caller); @@ -1630,13 +1594,12 @@ mod tests { } #[test] - #[cfg(feature = "track_location")] #[track_caller] fn observer_caller_location_command_archetype_move() { #[derive(Component)] struct Component; - let caller = Location::caller(); + let caller = MaybeLocation::caller(); let mut world = World::new(); world.add_observer(move |trigger: Trigger| { assert_eq!(trigger.caller(), caller); diff --git a/crates/bevy_ecs/src/query/fetch.rs b/crates/bevy_ecs/src/query/fetch.rs index bf9b41e1d4..08e06d03f4 100644 --- a/crates/bevy_ecs/src/query/fetch.rs +++ b/crates/bevy_ecs/src/query/fetch.rs @@ -1,7 +1,7 @@ use crate::{ archetype::{Archetype, Archetypes}, bundle::Bundle, - change_detection::{MaybeThinSlicePtrLocation, Ticks, TicksMut}, + change_detection::{MaybeLocation, Ticks, TicksMut}, component::{Component, ComponentId, Components, Mutable, StorageType, Tick}, entity::{Entities, Entity, EntityLocation}, query::{Access, DebugCheckedUnwrap, FilteredAccess, WorldQuery}, @@ -12,7 +12,7 @@ use crate::{ }, }; use bevy_ptr::{ThinSlicePtr, UnsafeCellDeref}; -use core::{cell::UnsafeCell, marker::PhantomData}; +use core::{cell::UnsafeCell, marker::PhantomData, panic::Location}; use smallvec::SmallVec; use variadics_please::all_tuples; @@ -1251,7 +1251,7 @@ pub struct RefFetch<'w, T: Component> { ThinSlicePtr<'w, UnsafeCell>, ThinSlicePtr<'w, UnsafeCell>, ThinSlicePtr<'w, UnsafeCell>, - MaybeThinSlicePtrLocation<'w>, + MaybeLocation>>>, )>, // T::STORAGE_TYPE = StorageType::SparseSet // Can be `None` when the component has never been inserted @@ -1337,10 +1337,9 @@ unsafe impl<'__w, T: Component> WorldQuery for Ref<'__w, T> { column.get_data_slice(table.entity_count()).into(), column.get_added_ticks_slice(table.entity_count()).into(), column.get_changed_ticks_slice(table.entity_count()).into(), - #[cfg(feature = "track_location")] - column.get_changed_by_slice(table.entity_count()).into(), - #[cfg(not(feature = "track_location"))] - (), + column + .get_changed_by_slice(table.entity_count()) + .map(Into::into), )); // SAFETY: set_table is only called when T::STORAGE_TYPE = StorageType::Table unsafe { fetch.components.set_table(table_data) }; @@ -1392,7 +1391,7 @@ unsafe impl<'__w, T: Component> QueryData for Ref<'__w, T> { fetch.components.extract( |table| { // SAFETY: set_table was previously called - let (table_components, added_ticks, changed_ticks, _callers) = + let (table_components, added_ticks, changed_ticks, callers) = unsafe { table.debug_checked_unwrap() }; // SAFETY: The caller ensures `table_row` is in range. @@ -1402,8 +1401,7 @@ unsafe impl<'__w, T: Component> QueryData for Ref<'__w, T> { // SAFETY: The caller ensures `table_row` is in range. let changed = unsafe { changed_ticks.get(table_row.as_usize()) }; // SAFETY: The caller ensures `table_row` is in range. - #[cfg(feature = "track_location")] - let caller = unsafe { _callers.get(table_row.as_usize()) }; + let caller = callers.map(|callers| unsafe { callers.get(table_row.as_usize()) }); Ref { value: component.deref(), @@ -1413,13 +1411,12 @@ unsafe impl<'__w, T: Component> QueryData for Ref<'__w, T> { this_run: fetch.this_run, last_run: fetch.last_run, }, - #[cfg(feature = "track_location")] - changed_by: caller.deref(), + changed_by: caller.map(|caller| caller.deref()), } }, |sparse_set| { // SAFETY: The caller ensures `entity` is in range and has the component. - let (component, ticks, _caller) = unsafe { + let (component, ticks, caller) = unsafe { sparse_set .debug_checked_unwrap() .get_with_ticks(entity) @@ -1429,8 +1426,7 @@ unsafe impl<'__w, T: Component> QueryData for Ref<'__w, T> { Ref { value: component.deref(), ticks: Ticks::from_tick_cells(ticks, fetch.last_run, fetch.this_run), - #[cfg(feature = "track_location")] - changed_by: _caller.deref(), + changed_by: caller.map(|caller| caller.deref()), } }, ) @@ -1449,7 +1445,7 @@ pub struct WriteFetch<'w, T: Component> { ThinSlicePtr<'w, UnsafeCell>, ThinSlicePtr<'w, UnsafeCell>, ThinSlicePtr<'w, UnsafeCell>, - MaybeThinSlicePtrLocation<'w>, + MaybeLocation>>>, )>, // T::STORAGE_TYPE = StorageType::SparseSet // Can be `None` when the component has never been inserted @@ -1535,10 +1531,9 @@ unsafe impl<'__w, T: Component> WorldQuery for &'__w mut T { column.get_data_slice(table.entity_count()).into(), column.get_added_ticks_slice(table.entity_count()).into(), column.get_changed_ticks_slice(table.entity_count()).into(), - #[cfg(feature = "track_location")] - column.get_changed_by_slice(table.entity_count()).into(), - #[cfg(not(feature = "track_location"))] - (), + column + .get_changed_by_slice(table.entity_count()) + .map(Into::into), )); // SAFETY: set_table is only called when T::STORAGE_TYPE = StorageType::Table unsafe { fetch.components.set_table(table_data) }; @@ -1590,7 +1585,7 @@ unsafe impl<'__w, T: Component> QueryData for &'__w mut T fetch.components.extract( |table| { // SAFETY: set_table was previously called - let (table_components, added_ticks, changed_ticks, _callers) = + let (table_components, added_ticks, changed_ticks, callers) = unsafe { table.debug_checked_unwrap() }; // SAFETY: The caller ensures `table_row` is in range. @@ -1600,8 +1595,7 @@ unsafe impl<'__w, T: Component> QueryData for &'__w mut T // SAFETY: The caller ensures `table_row` is in range. let changed = unsafe { changed_ticks.get(table_row.as_usize()) }; // SAFETY: The caller ensures `table_row` is in range. - #[cfg(feature = "track_location")] - let caller = unsafe { _callers.get(table_row.as_usize()) }; + let caller = callers.map(|callers| unsafe { callers.get(table_row.as_usize()) }); Mut { value: component.deref_mut(), @@ -1611,13 +1605,12 @@ unsafe impl<'__w, T: Component> QueryData for &'__w mut T this_run: fetch.this_run, last_run: fetch.last_run, }, - #[cfg(feature = "track_location")] - changed_by: caller.deref_mut(), + changed_by: caller.map(|caller| caller.deref_mut()), } }, |sparse_set| { // SAFETY: The caller ensures `entity` is in range and has the component. - let (component, ticks, _caller) = unsafe { + let (component, ticks, caller) = unsafe { sparse_set .debug_checked_unwrap() .get_with_ticks(entity) @@ -1627,8 +1620,7 @@ unsafe impl<'__w, T: Component> QueryData for &'__w mut T Mut { value: component.assert_unique().deref_mut(), ticks: TicksMut::from_tick_cells(ticks, fetch.last_run, fetch.this_run), - #[cfg(feature = "track_location")] - changed_by: _caller.deref_mut(), + changed_by: caller.map(|caller| caller.deref_mut()), } }, ) diff --git a/crates/bevy_ecs/src/storage/resource.rs b/crates/bevy_ecs/src/storage/resource.rs index ac9bbfa54d..078acade7d 100644 --- a/crates/bevy_ecs/src/storage/resource.rs +++ b/crates/bevy_ecs/src/storage/resource.rs @@ -1,14 +1,12 @@ use crate::{ archetype::ArchetypeComponentId, - change_detection::{MaybeLocation, MaybeUnsafeCellLocation, MutUntyped, TicksMut}, + change_detection::{MaybeLocation, MutUntyped, TicksMut}, component::{ComponentId, ComponentTicks, Components, Tick, TickCells}, storage::{blob_vec::BlobVec, SparseSet}, }; use alloc::string::String; use bevy_ptr::{OwningPtr, Ptr, UnsafeCellDeref}; -#[cfg(feature = "track_location")] -use core::panic::Location; -use core::{cell::UnsafeCell, mem::ManuallyDrop}; +use core::{cell::UnsafeCell, mem::ManuallyDrop, panic::Location}; #[cfg(feature = "std")] use std::thread::ThreadId; @@ -30,8 +28,7 @@ pub struct ResourceData { id: ArchetypeComponentId, #[cfg(feature = "std")] origin_thread_id: Option, - #[cfg(feature = "track_location")] - changed_by: UnsafeCell<&'static Location<'static>>, + changed_by: MaybeLocation>>, } impl Drop for ResourceData { @@ -136,7 +133,11 @@ impl ResourceData { #[inline] pub(crate) fn get_with_ticks( &self, - ) -> Option<(Ptr<'_>, TickCells<'_>, MaybeUnsafeCellLocation<'_>)> { + ) -> Option<( + Ptr<'_>, + TickCells<'_>, + MaybeLocation<&UnsafeCell<&'static Location<'static>>>, + )> { self.is_present().then(|| { self.validate_access(); ( @@ -146,10 +147,7 @@ impl ResourceData { added: &self.added_ticks, changed: &self.changed_ticks, }, - #[cfg(feature = "track_location")] - &self.changed_by, - #[cfg(not(feature = "track_location"))] - (), + self.changed_by.as_ref(), ) }) } @@ -160,15 +158,14 @@ impl ResourceData { /// If `SEND` is false, this will panic if a value is present and is not accessed from the /// original thread it was inserted in. pub(crate) fn get_mut(&mut self, last_run: Tick, this_run: Tick) -> Option> { - let (ptr, ticks, _caller) = self.get_with_ticks()?; + let (ptr, ticks, caller) = self.get_with_ticks()?; Some(MutUntyped { // SAFETY: We have exclusive access to the underlying storage. value: unsafe { ptr.assert_unique() }, // SAFETY: We have exclusive access to the underlying storage. ticks: unsafe { TicksMut::from_tick_cells(ticks, last_run, this_run) }, - #[cfg(feature = "track_location")] // SAFETY: We have exclusive access to the underlying storage. - changed_by: unsafe { _caller.deref_mut() }, + changed_by: unsafe { caller.map(|caller| caller.deref_mut()) }, }) } @@ -186,7 +183,7 @@ impl ResourceData { &mut self, value: OwningPtr<'_>, change_tick: Tick, - #[cfg(feature = "track_location")] caller: &'static Location, + caller: MaybeLocation, ) { if self.is_present() { self.validate_access(); @@ -205,10 +202,11 @@ impl ResourceData { *self.added_ticks.deref_mut() = change_tick; } *self.changed_ticks.deref_mut() = change_tick; - #[cfg(feature = "track_location")] - { - *self.changed_by.deref_mut() = caller; - } + + self.changed_by + .as_ref() + .map(|changed_by| changed_by.deref_mut()) + .assign(caller); } /// Inserts a value into the resource with a pre-existing change tick. If a @@ -225,7 +223,7 @@ impl ResourceData { &mut self, value: OwningPtr<'_>, change_ticks: ComponentTicks, - #[cfg(feature = "track_location")] caller: &'static Location, + caller: MaybeLocation, ) { if self.is_present() { self.validate_access(); @@ -244,10 +242,10 @@ impl ResourceData { } *self.added_ticks.deref_mut() = change_ticks.added; *self.changed_ticks.deref_mut() = change_ticks.changed; - #[cfg(feature = "track_location")] - { - *self.changed_by.deref_mut() = caller; - } + self.changed_by + .as_ref() + .map(|changed_by| changed_by.deref_mut()) + .assign(caller); } /// Removes a value from the resource, if present. @@ -267,11 +265,11 @@ impl ResourceData { // SAFETY: We've already validated that the row is present. let res = unsafe { self.data.swap_remove_and_forget_unchecked(Self::ROW) }; - // SAFETY: This function is being called through an exclusive mutable reference to Self - #[cfg(feature = "track_location")] - let caller = unsafe { *self.changed_by.deref_mut() }; - #[cfg(not(feature = "track_location"))] - let caller = (); + let caller = self + .changed_by + .as_ref() + // SAFETY: This function is being called through an exclusive mutable reference to Self + .map(|changed_by| unsafe { *changed_by.deref_mut() }); // SAFETY: This function is being called through an exclusive mutable reference to Self, which // makes it sound to read these ticks. @@ -392,8 +390,7 @@ impl Resources { id: f(), #[cfg(feature = "std")] origin_thread_id: None, - #[cfg(feature = "track_location")] - changed_by: UnsafeCell::new(Location::caller()) + changed_by: MaybeLocation::caller().map(UnsafeCell::new), } }) } diff --git a/crates/bevy_ecs/src/storage/sparse_set.rs b/crates/bevy_ecs/src/storage/sparse_set.rs index d6ea6c59ec..671cd7c161 100644 --- a/crates/bevy_ecs/src/storage/sparse_set.rs +++ b/crates/bevy_ecs/src/storage/sparse_set.rs @@ -1,14 +1,12 @@ use crate::{ - change_detection::MaybeUnsafeCellLocation, + change_detection::MaybeLocation, component::{ComponentId, ComponentInfo, ComponentTicks, Tick, TickCells}, entity::Entity, storage::{Column, TableRow}, }; use alloc::{boxed::Box, vec::Vec}; use bevy_ptr::{OwningPtr, Ptr}; -#[cfg(feature = "track_location")] -use core::panic::Location; -use core::{cell::UnsafeCell, hash::Hash, marker::PhantomData}; +use core::{cell::UnsafeCell, hash::Hash, marker::PhantomData, panic::Location}; use nonmax::NonMaxUsize; type EntityIndex = u32; @@ -170,26 +168,16 @@ impl ComponentSparseSet { entity: Entity, value: OwningPtr<'_>, change_tick: Tick, - #[cfg(feature = "track_location")] caller: &'static Location<'static>, + caller: MaybeLocation, ) { if let Some(&dense_index) = self.sparse.get(entity.index()) { #[cfg(debug_assertions)] assert_eq!(entity, self.entities[dense_index.as_usize()]); - self.dense.replace( - dense_index, - value, - change_tick, - #[cfg(feature = "track_location")] - caller, - ); + self.dense.replace(dense_index, value, change_tick, caller); } else { let dense_index = self.dense.len(); - self.dense.push( - value, - ComponentTicks::new(change_tick), - #[cfg(feature = "track_location")] - caller, - ); + self.dense + .push(value, ComponentTicks::new(change_tick), caller); self.sparse .insert(entity.index(), TableRow::from_usize(dense_index)); #[cfg(debug_assertions)] @@ -238,7 +226,11 @@ impl ComponentSparseSet { pub fn get_with_ticks( &self, entity: Entity, - ) -> Option<(Ptr<'_>, TickCells<'_>, MaybeUnsafeCellLocation<'_>)> { + ) -> Option<( + Ptr<'_>, + TickCells<'_>, + MaybeLocation<&UnsafeCell<&'static Location<'static>>>, + )> { let dense_index = *self.sparse.get(entity.index())?; #[cfg(debug_assertions)] assert_eq!(entity, self.entities[dense_index.as_usize()]); @@ -250,10 +242,7 @@ impl ComponentSparseSet { added: self.dense.get_added_tick_unchecked(dense_index), changed: self.dense.get_changed_tick_unchecked(dense_index), }, - #[cfg(feature = "track_location")] self.dense.get_changed_by_unchecked(dense_index), - #[cfg(not(feature = "track_location"))] - (), )) } } @@ -298,16 +287,17 @@ impl ComponentSparseSet { /// /// Returns `None` if `entity` does not have a component in the sparse set. #[inline] - #[cfg(feature = "track_location")] pub fn get_changed_by( &self, entity: Entity, - ) -> Option<&UnsafeCell<&'static Location<'static>>> { - let dense_index = *self.sparse.get(entity.index())?; - #[cfg(debug_assertions)] - assert_eq!(entity, self.entities[dense_index.as_usize()]); - // SAFETY: if the sparse index points to something in the dense vec, it exists - unsafe { Some(self.dense.get_changed_by_unchecked(dense_index)) } + ) -> MaybeLocation>>> { + MaybeLocation::new_with_flattened(|| { + let dense_index = *self.sparse.get(entity.index())?; + #[cfg(debug_assertions)] + assert_eq!(entity, self.entities[dense_index.as_usize()]); + // SAFETY: if the sparse index points to something in the dense vec, it exists + unsafe { Some(self.dense.get_changed_by_unchecked(dense_index)) } + }) } /// Removes the `entity` from this sparse set and returns a pointer to the associated value (if diff --git a/crates/bevy_ecs/src/storage/table/column.rs b/crates/bevy_ecs/src/storage/table/column.rs index 4054b5c15f..d4690d264c 100644 --- a/crates/bevy_ecs/src/storage/table/column.rs +++ b/crates/bevy_ecs/src/storage/table/column.rs @@ -1,10 +1,12 @@ use super::*; use crate::{ + change_detection::MaybeLocation, component::TickCells, storage::{blob_array::BlobArray, thin_array_ptr::ThinArrayPtr}, }; use alloc::vec::Vec; use bevy_ptr::PtrMut; +use core::panic::Location; /// Very similar to a normal [`Column`], but with the capacities and lengths cut out for performance reasons. /// @@ -17,8 +19,7 @@ pub struct ThinColumn { pub(super) data: BlobArray, pub(super) added_ticks: ThinArrayPtr>, pub(super) changed_ticks: ThinArrayPtr>, - #[cfg(feature = "track_location")] - pub(super) changed_by: ThinArrayPtr>>, + pub(super) changed_by: MaybeLocation>>>, } impl ThinColumn { @@ -31,8 +32,7 @@ impl ThinColumn { }, added_ticks: ThinArrayPtr::with_capacity(capacity), changed_ticks: ThinArrayPtr::with_capacity(capacity), - #[cfg(feature = "track_location")] - changed_by: ThinArrayPtr::with_capacity(capacity), + changed_by: MaybeLocation::new_with(|| ThinArrayPtr::with_capacity(capacity)), } } @@ -54,9 +54,9 @@ impl ThinColumn { .swap_remove_unchecked_nonoverlapping(row.as_usize(), last_element_index); self.changed_ticks .swap_remove_unchecked_nonoverlapping(row.as_usize(), last_element_index); - #[cfg(feature = "track_location")] - self.changed_by - .swap_remove_unchecked_nonoverlapping(row.as_usize(), last_element_index); + self.changed_by.as_mut().map(|changed_by| { + changed_by.swap_remove_unchecked_nonoverlapping(row.as_usize(), last_element_index); + }); } /// Swap-remove and drop the removed element. @@ -76,9 +76,9 @@ impl ThinColumn { .swap_remove_and_drop_unchecked(row.as_usize(), last_element_index); self.changed_ticks .swap_remove_and_drop_unchecked(row.as_usize(), last_element_index); - #[cfg(feature = "track_location")] - self.changed_by - .swap_remove_and_drop_unchecked(row.as_usize(), last_element_index); + self.changed_by.as_mut().map(|changed_by| { + changed_by.swap_remove_and_drop_unchecked(row.as_usize(), last_element_index); + }); } /// Swap-remove and forget the removed element. @@ -99,9 +99,9 @@ impl ThinColumn { .swap_remove_unchecked(row.as_usize(), last_element_index); self.changed_ticks .swap_remove_unchecked(row.as_usize(), last_element_index); - #[cfg(feature = "track_location")] self.changed_by - .swap_remove_unchecked(row.as_usize(), last_element_index); + .as_mut() + .map(|changed_by| changed_by.swap_remove_unchecked(row.as_usize(), last_element_index)); } /// Call [`realloc`](std::alloc::realloc) to expand / shrink the memory allocation for this [`ThinColumn`] @@ -117,8 +117,9 @@ impl ThinColumn { self.data.realloc(current_capacity, new_capacity); self.added_ticks.realloc(current_capacity, new_capacity); self.changed_ticks.realloc(current_capacity, new_capacity); - #[cfg(feature = "track_location")] - self.changed_by.realloc(current_capacity, new_capacity); + self.changed_by + .as_mut() + .map(|changed_by| changed_by.realloc(current_capacity, new_capacity)); } /// Call [`alloc`](std::alloc::alloc) to allocate memory for this [`ThinColumn`] @@ -127,8 +128,9 @@ impl ThinColumn { self.data.alloc(new_capacity); self.added_ticks.alloc(new_capacity); self.changed_ticks.alloc(new_capacity); - #[cfg(feature = "track_location")] - self.changed_by.alloc(new_capacity); + self.changed_by + .as_mut() + .map(|changed_by| changed_by.alloc(new_capacity)); } /// Writes component data to the column at the given row. @@ -144,7 +146,7 @@ impl ThinColumn { row: TableRow, data: OwningPtr<'_>, tick: Tick, - #[cfg(feature = "track_location")] caller: &'static Location<'static>, + caller: MaybeLocation, ) { self.data.initialize_unchecked(row.as_usize(), data); *self.added_ticks.get_unchecked_mut(row.as_usize()).get_mut() = tick; @@ -152,10 +154,10 @@ impl ThinColumn { .changed_ticks .get_unchecked_mut(row.as_usize()) .get_mut() = tick; - #[cfg(feature = "track_location")] - { - *self.changed_by.get_unchecked_mut(row.as_usize()).get_mut() = caller; - } + self.changed_by + .as_mut() + .map(|changed_by| changed_by.get_unchecked_mut(row.as_usize()).get_mut()) + .assign(caller); } /// Writes component data to the column at given row. Assumes the slot is initialized, drops the previous value. @@ -169,17 +171,17 @@ impl ThinColumn { row: TableRow, data: OwningPtr<'_>, change_tick: Tick, - #[cfg(feature = "track_location")] caller: &'static Location<'static>, + caller: MaybeLocation, ) { self.data.replace_unchecked(row.as_usize(), data); *self .changed_ticks .get_unchecked_mut(row.as_usize()) .get_mut() = change_tick; - #[cfg(feature = "track_location")] - { - *self.changed_by.get_unchecked_mut(row.as_usize()).get_mut() = caller; - } + self.changed_by + .as_mut() + .map(|changed_by| changed_by.get_unchecked_mut(row.as_usize()).get_mut()) + .assign(caller); } /// Removes the element from `other` at `src_row` and inserts it @@ -218,13 +220,13 @@ impl ThinColumn { .swap_remove_unchecked(src_row.as_usize(), other_last_element_index); self.changed_ticks .initialize_unchecked(dst_row.as_usize(), changed_tick); - #[cfg(feature = "track_location")] - let changed_by = other - .changed_by - .swap_remove_unchecked(src_row.as_usize(), other_last_element_index); - #[cfg(feature = "track_location")] - self.changed_by - .initialize_unchecked(dst_row.as_usize(), changed_by); + self.changed_by.as_mut().zip(other.changed_by.as_mut()).map( + |(self_changed_by, other_changed_by)| { + let changed_by = other_changed_by + .swap_remove_unchecked(src_row.as_usize(), other_last_element_index); + self_changed_by.initialize_unchecked(dst_row.as_usize(), changed_by); + }, + ); } /// Call [`Tick::check_tick`] on all of the ticks stored in this column. @@ -258,8 +260,9 @@ impl ThinColumn { self.added_ticks.clear_elements(len); self.changed_ticks.clear_elements(len); self.data.clear(len); - #[cfg(feature = "track_location")] - self.changed_by.clear_elements(len); + self.changed_by + .as_mut() + .map(|changed_by| changed_by.clear_elements(len)); } /// Because this method needs parameters, it can't be the implementation of the `Drop` trait. @@ -273,8 +276,9 @@ impl ThinColumn { self.added_ticks.drop(cap, len); self.changed_ticks.drop(cap, len); self.data.drop(cap, len); - #[cfg(feature = "track_location")] - self.changed_by.drop(cap, len); + self.changed_by + .as_mut() + .map(|changed_by| changed_by.drop(cap, len)); } /// Drops the last component in this column. @@ -285,8 +289,9 @@ impl ThinColumn { pub(crate) unsafe fn drop_last_component(&mut self, last_element_index: usize) { core::ptr::drop_in_place(self.added_ticks.get_unchecked_raw(last_element_index)); core::ptr::drop_in_place(self.changed_ticks.get_unchecked_raw(last_element_index)); - #[cfg(feature = "track_location")] - core::ptr::drop_in_place(self.changed_by.get_unchecked_raw(last_element_index)); + self.changed_by.as_mut().map(|changed_by| { + core::ptr::drop_in_place(changed_by.get_unchecked_raw(last_element_index)); + }); self.data.drop_last_element(last_element_index); } @@ -319,12 +324,13 @@ impl ThinColumn { /// /// # Safety /// - `len` must match the actual length of this column (number of elements stored) - #[cfg(feature = "track_location")] pub unsafe fn get_changed_by_slice( &self, len: usize, - ) -> &[UnsafeCell<&'static Location<'static>>] { - self.changed_by.as_slice(len) + ) -> MaybeLocation<&[UnsafeCell<&'static Location<'static>>]> { + self.changed_by + .as_ref() + .map(|changed_by| changed_by.as_slice(len)) } } @@ -343,8 +349,7 @@ pub struct Column { pub(super) data: BlobVec, pub(super) added_ticks: Vec>, pub(super) changed_ticks: Vec>, - #[cfg(feature = "track_location")] - changed_by: Vec>>, + changed_by: MaybeLocation>>>, } impl Column { @@ -356,8 +361,7 @@ impl Column { data: unsafe { BlobVec::new(component_info.layout(), component_info.drop(), capacity) }, added_ticks: Vec::with_capacity(capacity), changed_ticks: Vec::with_capacity(capacity), - #[cfg(feature = "track_location")] - changed_by: Vec::with_capacity(capacity), + changed_by: MaybeLocation::new_with(|| Vec::with_capacity(capacity)), } } @@ -378,7 +382,7 @@ impl Column { row: TableRow, data: OwningPtr<'_>, change_tick: Tick, - #[cfg(feature = "track_location")] caller: &'static Location<'static>, + caller: MaybeLocation, ) { debug_assert!(row.as_usize() < self.len()); self.data.replace_unchecked(row.as_usize(), data); @@ -386,10 +390,10 @@ impl Column { .changed_ticks .get_unchecked_mut(row.as_usize()) .get_mut() = change_tick; - #[cfg(feature = "track_location")] - { - *self.changed_by.get_unchecked_mut(row.as_usize()).get_mut() = caller; - } + self.changed_by + .as_mut() + .map(|changed_by| changed_by.get_unchecked_mut(row.as_usize()).get_mut()) + .assign(caller); } /// Gets the current number of elements stored in the column. @@ -418,8 +422,9 @@ impl Column { self.data.swap_remove_and_drop_unchecked(row.as_usize()); self.added_ticks.swap_remove(row.as_usize()); self.changed_ticks.swap_remove(row.as_usize()); - #[cfg(feature = "track_location")] - self.changed_by.swap_remove(row.as_usize()); + self.changed_by + .as_mut() + .map(|changed_by| changed_by.swap_remove(row.as_usize())); } /// Removes an element from the [`Column`] and returns it and its change detection ticks. @@ -442,10 +447,10 @@ impl Column { let data = self.data.swap_remove_and_forget_unchecked(row.as_usize()); let added = self.added_ticks.swap_remove(row.as_usize()).into_inner(); let changed = self.changed_ticks.swap_remove(row.as_usize()).into_inner(); - #[cfg(feature = "track_location")] - let caller = self.changed_by.swap_remove(row.as_usize()).into_inner(); - #[cfg(not(feature = "track_location"))] - let caller = (); + let caller = self + .changed_by + .as_mut() + .map(|changed_by| changed_by.swap_remove(row.as_usize()).into_inner()); (data, ComponentTicks { added, changed }, caller) } @@ -457,13 +462,15 @@ impl Column { &mut self, ptr: OwningPtr<'_>, ticks: ComponentTicks, - #[cfg(feature = "track_location")] caller: &'static Location<'static>, + caller: MaybeLocation, ) { self.data.push(ptr); self.added_ticks.push(UnsafeCell::new(ticks.added)); self.changed_ticks.push(UnsafeCell::new(ticks.changed)); - #[cfg(feature = "track_location")] - self.changed_by.push(UnsafeCell::new(caller)); + self.changed_by + .as_mut() + .zip(caller) + .map(|(changed_by, caller)| changed_by.push(UnsafeCell::new(caller))); } /// Fetches the data pointer to the first element of the [`Column`]. @@ -644,8 +651,7 @@ impl Column { self.data.clear(); self.added_ticks.clear(); self.changed_ticks.clear(); - #[cfg(feature = "track_location")] - self.changed_by.clear(); + self.changed_by.as_mut().map(Vec::clear); } #[inline] @@ -666,9 +672,13 @@ impl Column { /// Users of this API must ensure that accesses to each individual element /// adhere to the safety invariants of [`UnsafeCell`]. #[inline] - #[cfg(feature = "track_location")] - pub fn get_changed_by(&self, row: TableRow) -> Option<&UnsafeCell<&'static Location<'static>>> { - self.changed_by.get(row.as_usize()) + pub fn get_changed_by( + &self, + row: TableRow, + ) -> MaybeLocation>>> { + self.changed_by + .as_ref() + .map(|changed_by| changed_by.get(row.as_usize())) } /// Fetches the calling location that last changed the value at `row`. @@ -678,12 +688,13 @@ impl Column { /// # Safety /// `row` must be within the range `[0, self.len())`. #[inline] - #[cfg(feature = "track_location")] pub unsafe fn get_changed_by_unchecked( &self, row: TableRow, - ) -> &UnsafeCell<&'static Location<'static>> { - debug_assert!(row.as_usize() < self.changed_by.len()); - self.changed_by.get_unchecked(row.as_usize()) + ) -> MaybeLocation<&UnsafeCell<&'static Location<'static>>> { + self.changed_by.as_ref().map(|changed_by| { + debug_assert!(row.as_usize() < changed_by.len()); + changed_by.get_unchecked(row.as_usize()) + }) } } diff --git a/crates/bevy_ecs/src/storage/table/mod.rs b/crates/bevy_ecs/src/storage/table/mod.rs index 4a9d795128..159a716c4e 100644 --- a/crates/bevy_ecs/src/storage/table/mod.rs +++ b/crates/bevy_ecs/src/storage/table/mod.rs @@ -9,13 +9,12 @@ use alloc::{boxed::Box, vec, vec::Vec}; use bevy_platform_support::collections::HashMap; use bevy_ptr::{OwningPtr, Ptr, UnsafeCellDeref}; pub use column::*; -#[cfg(feature = "track_location")] -use core::panic::Location; use core::{ alloc::Layout, cell::UnsafeCell, num::NonZeroUsize, ops::{Index, IndexMut}, + panic::Location, }; mod column; @@ -390,14 +389,15 @@ impl Table { } /// Fetches the calling locations that last changed the each component - #[cfg(feature = "track_location")] pub fn get_changed_by_slice_for( &self, component_id: ComponentId, - ) -> Option<&[UnsafeCell<&'static Location<'static>>]> { - self.get_column(component_id) - // SAFETY: `self.len()` is guaranteed to be the len of the locations array - .map(|col| unsafe { col.get_changed_by_slice(self.entity_count()) }) + ) -> MaybeLocation>]>> { + MaybeLocation::new_with_flattened(|| { + self.get_column(component_id) + // SAFETY: `self.len()` is guaranteed to be the len of the locations array + .map(|col| unsafe { col.get_changed_by_slice(self.entity_count()) }) + }) } /// Get the specific [`change tick`](Tick) of the component matching `component_id` in `row`. @@ -433,20 +433,22 @@ impl Table { } /// Get the specific calling location that changed the component matching `component_id` in `row` - #[cfg(feature = "track_location")] pub fn get_changed_by( &self, component_id: ComponentId, row: TableRow, - ) -> Option<&UnsafeCell<&'static Location<'static>>> { - (row.as_usize() < self.entity_count()).then_some( - // SAFETY: `row.as_usize()` < `len` - unsafe { - self.get_column(component_id)? - .changed_by - .get_unchecked(row.as_usize()) - }, - ) + ) -> MaybeLocation>>> { + MaybeLocation::new_with_flattened(|| { + (row.as_usize() < self.entity_count()).then_some( + // SAFETY: `row.as_usize()` < `len` + unsafe { + self.get_column(component_id)? + .changed_by + .as_ref() + .map(|changed_by| changed_by.get_unchecked(row.as_usize())) + }, + ) + }) } /// Get the [`ComponentTicks`] of the component matching `component_id` in `row`. @@ -571,9 +573,12 @@ impl Table { .initialize_unchecked(len, UnsafeCell::new(Tick::new(0))); col.changed_ticks .initialize_unchecked(len, UnsafeCell::new(Tick::new(0))); - #[cfg(feature = "track_location")] col.changed_by - .initialize_unchecked(len, UnsafeCell::new(Location::caller())); + .as_mut() + .zip(MaybeLocation::caller()) + .map(|(changed_by, caller)| { + changed_by.initialize_unchecked(len, UnsafeCell::new(caller)); + }); } TableRow::from_usize(len) } @@ -816,6 +821,7 @@ impl Drop for Table { #[cfg(test)] mod tests { use crate::{ + change_detection::MaybeLocation, component::{Component, Components, Tick}, entity::Entity, ptr::OwningPtr, @@ -823,9 +829,6 @@ mod tests { }; use alloc::vec::Vec; - #[cfg(feature = "track_location")] - use core::panic::Location; - #[derive(Component)] struct W(T); @@ -860,8 +863,7 @@ mod tests { row, value_ptr, Tick::new(0), - #[cfg(feature = "track_location")] - Location::caller(), + MaybeLocation::caller(), ); }); }; diff --git a/crates/bevy_ecs/src/system/commands/command.rs b/crates/bevy_ecs/src/system/commands/command.rs index c9383a1ee0..68035560d0 100644 --- a/crates/bevy_ecs/src/system/commands/command.rs +++ b/crates/bevy_ecs/src/system/commands/command.rs @@ -4,11 +4,9 @@ //! It also contains functions that return closures for use with //! [`Commands`](crate::system::Commands). -#[cfg(feature = "track_location")] -use core::panic::Location; - use crate::{ bundle::{Bundle, InsertMode, NoBundleEffect}, + change_detection::MaybeLocation, entity::Entity, event::{Event, Events}, observer::TriggerTargets, @@ -113,15 +111,9 @@ where I: IntoIterator + Send + Sync + 'static, I::Item: Bundle, { - #[cfg(feature = "track_location")] - let caller = Location::caller(); + let caller = MaybeLocation::caller(); move |world: &mut World| { - SpawnBatchIter::new( - world, - bundles_iter.into_iter(), - #[cfg(feature = "track_location")] - caller, - ); + SpawnBatchIter::new(world, bundles_iter.into_iter(), caller); } } @@ -137,15 +129,9 @@ where I: IntoIterator + Send + Sync + 'static, B: Bundle, { - #[cfg(feature = "track_location")] - let caller = Location::caller(); + let caller = MaybeLocation::caller(); move |world: &mut World| -> Result { - world.try_insert_batch_with_caller( - batch, - insert_mode, - #[cfg(feature = "track_location")] - caller, - )?; + world.try_insert_batch_with_caller(batch, insert_mode, caller)?; Ok(()) } } @@ -162,14 +148,9 @@ pub fn init_resource() -> impl Command { /// A [`Command`] that inserts a [`Resource`] into the world. #[track_caller] pub fn insert_resource(resource: R) -> impl Command { - #[cfg(feature = "track_location")] - let caller = Location::caller(); + let caller = MaybeLocation::caller(); move |world: &mut World| { - world.insert_resource_with_caller( - resource, - #[cfg(feature = "track_location")] - caller, - ); + world.insert_resource_with_caller(resource, caller); } } @@ -267,14 +248,9 @@ pub fn run_schedule(label: impl ScheduleLabel) -> impl Command { /// A [`Command`] that sends a global [`Trigger`](crate::observer::Trigger) without any targets. #[track_caller] pub fn trigger(event: impl Event) -> impl Command { - #[cfg(feature = "track_location")] - let caller = Location::caller(); + let caller = MaybeLocation::caller(); move |world: &mut World| { - world.trigger_with_caller( - event, - #[cfg(feature = "track_location")] - caller, - ); + world.trigger_with_caller(event, caller); } } @@ -283,29 +259,18 @@ pub fn trigger_targets( event: impl Event, targets: impl TriggerTargets + Send + Sync + 'static, ) -> impl Command { - #[cfg(feature = "track_location")] - let caller = Location::caller(); + let caller = MaybeLocation::caller(); move |world: &mut World| { - world.trigger_targets_with_caller( - event, - targets, - #[cfg(feature = "track_location")] - caller, - ); + world.trigger_targets_with_caller(event, targets, caller); } } /// A [`Command`] that sends an arbitrary [`Event`]. #[track_caller] pub fn send_event(event: E) -> impl Command { - #[cfg(feature = "track_location")] - let caller = Location::caller(); + let caller = MaybeLocation::caller(); move |world: &mut World| { let mut events = world.resource_mut::>(); - events.send_with_caller( - event, - #[cfg(feature = "track_location")] - caller, - ); + events.send_with_caller(event, caller); } } diff --git a/crates/bevy_ecs/src/system/commands/entity_command.rs b/crates/bevy_ecs/src/system/commands/entity_command.rs index e0b0bffc69..bbc8cb88ba 100644 --- a/crates/bevy_ecs/src/system/commands/entity_command.rs +++ b/crates/bevy_ecs/src/system/commands/entity_command.rs @@ -7,11 +7,9 @@ use alloc::vec::Vec; use log::info; -#[cfg(feature = "track_location")] -use core::panic::Location; - use crate::{ bundle::{Bundle, InsertMode}, + change_detection::MaybeLocation, component::{Component, ComponentId, ComponentInfo}, entity::{Entity, EntityClonerBuilder}, event::Event, @@ -155,15 +153,9 @@ where /// replacing any that were already present. #[track_caller] pub fn insert(bundle: impl Bundle) -> impl EntityCommand { - #[cfg(feature = "track_location")] - let caller = Location::caller(); + let caller = MaybeLocation::caller(); move |mut entity: EntityWorldMut| { - entity.insert_with_caller( - bundle, - InsertMode::Replace, - #[cfg(feature = "track_location")] - caller, - ); + entity.insert_with_caller(bundle, InsertMode::Replace, caller); } } @@ -171,34 +163,22 @@ pub fn insert(bundle: impl Bundle) -> impl EntityCommand { /// except for any that were already present. #[track_caller] pub fn insert_if_new(bundle: impl Bundle) -> impl EntityCommand { - #[cfg(feature = "track_location")] - let caller = Location::caller(); + let caller = MaybeLocation::caller(); move |mut entity: EntityWorldMut| { - entity.insert_with_caller( - bundle, - InsertMode::Keep, - #[cfg(feature = "track_location")] - caller, - ); + entity.insert_with_caller(bundle, InsertMode::Keep, caller); } } /// An [`EntityCommand`] that adds a dynamic component to an entity. #[track_caller] pub fn insert_by_id(component_id: ComponentId, value: T) -> impl EntityCommand { - #[cfg(feature = "track_location")] - let caller = Location::caller(); + let caller = MaybeLocation::caller(); move |mut entity: EntityWorldMut| { // SAFETY: // - `component_id` safety is ensured by the caller // - `ptr` is valid within the `make` block OwningPtr::make(value, |ptr| unsafe { - entity.insert_by_id_with_caller( - component_id, - ptr, - #[cfg(feature = "track_location")] - caller, - ); + entity.insert_by_id_with_caller(component_id, ptr, caller); }); } } @@ -207,29 +187,19 @@ pub fn insert_by_id(component_id: ComponentId, value: T) -> i /// the component's [`FromWorld`] implementation. #[track_caller] pub fn insert_from_world(mode: InsertMode) -> impl EntityCommand { - #[cfg(feature = "track_location")] - let caller = Location::caller(); + let caller = MaybeLocation::caller(); move |mut entity: EntityWorldMut| { let value = entity.world_scope(|world| T::from_world(world)); - entity.insert_with_caller( - value, - mode, - #[cfg(feature = "track_location")] - caller, - ); + entity.insert_with_caller(value, mode, caller); } } /// An [`EntityCommand`] that removes the components in a [`Bundle`] from an entity. #[track_caller] pub fn remove() -> impl EntityCommand { - #[cfg(feature = "track_location")] - let caller = Location::caller(); + let caller = MaybeLocation::caller(); move |mut entity: EntityWorldMut| { - entity.remove_with_caller::( - #[cfg(feature = "track_location")] - caller, - ); + entity.remove_with_caller::(caller); } } @@ -237,40 +207,27 @@ pub fn remove() -> impl EntityCommand { /// as well as the required components for each component removed. #[track_caller] pub fn remove_with_requires() -> impl EntityCommand { - #[cfg(feature = "track_location")] - let caller = Location::caller(); + let caller = MaybeLocation::caller(); move |mut entity: EntityWorldMut| { - entity.remove_with_requires_with_caller::( - #[cfg(feature = "track_location")] - caller, - ); + entity.remove_with_requires_with_caller::(caller); } } /// An [`EntityCommand`] that removes a dynamic component from an entity. #[track_caller] pub fn remove_by_id(component_id: ComponentId) -> impl EntityCommand { - #[cfg(feature = "track_location")] - let caller = Location::caller(); + let caller = MaybeLocation::caller(); move |mut entity: EntityWorldMut| { - entity.remove_by_id_with_caller( - component_id, - #[cfg(feature = "track_location")] - caller, - ); + entity.remove_by_id_with_caller(component_id, caller); } } /// An [`EntityCommand`] that removes all components from an entity. #[track_caller] pub fn clear() -> impl EntityCommand { - #[cfg(feature = "track_location")] - let caller = Location::caller(); + let caller = MaybeLocation::caller(); move |mut entity: EntityWorldMut| { - entity.clear_with_caller( - #[cfg(feature = "track_location")] - caller, - ); + entity.clear_with_caller(caller); } } @@ -278,13 +235,9 @@ pub fn clear() -> impl EntityCommand { /// except for those in the given [`Bundle`]. #[track_caller] pub fn retain() -> impl EntityCommand { - #[cfg(feature = "track_location")] - let caller = Location::caller(); + let caller = MaybeLocation::caller(); move |mut entity: EntityWorldMut| { - entity.retain_with_caller::( - #[cfg(feature = "track_location")] - caller, - ); + entity.retain_with_caller::(caller); } } @@ -296,13 +249,9 @@ pub fn retain() -> impl EntityCommand { /// to despawn descendants. This results in "recursive despawn" behavior. #[track_caller] pub fn despawn() -> impl EntityCommand { - #[cfg(feature = "track_location")] - let caller = Location::caller(); + let caller = MaybeLocation::caller(); move |entity: EntityWorldMut| { - entity.despawn_with_caller( - #[cfg(feature = "track_location")] - caller, - ); + entity.despawn_with_caller(caller); } } @@ -312,14 +261,9 @@ pub fn despawn() -> impl EntityCommand { pub fn observe( observer: impl IntoObserverSystem, ) -> impl EntityCommand { - #[cfg(feature = "track_location")] - let caller = Location::caller(); + let caller = MaybeLocation::caller(); move |mut entity: EntityWorldMut| { - entity.observe_with_caller( - observer, - #[cfg(feature = "track_location")] - caller, - ); + entity.observe_with_caller(observer, caller); } } diff --git a/crates/bevy_ecs/src/system/commands/mod.rs b/crates/bevy_ecs/src/system/commands/mod.rs index 4292ccb0c5..04d0940a44 100644 --- a/crates/bevy_ecs/src/system/commands/mod.rs +++ b/crates/bevy_ecs/src/system/commands/mod.rs @@ -13,13 +13,12 @@ pub use parallel_scope::*; use alloc::boxed::Box; use core::marker::PhantomData; -use core::panic::Location; use log::error; use crate::{ self as bevy_ecs, bundle::{Bundle, InsertMode, NoBundleEffect}, - change_detection::Mut, + change_detection::{MaybeLocation, Mut}, component::{Component, ComponentId, Mutable}, entity::{Entities, Entity, EntityClonerBuilder}, event::Event, @@ -683,11 +682,10 @@ impl<'w, 's> Commands<'w, 's> { I: IntoIterator + Send + Sync + 'static, B: Bundle, { - let caller = Location::caller(); + let caller = MaybeLocation::caller(); self.queue(move |world: &mut World| { if let Err(invalid_entities) = world.insert_or_spawn_batch_with_caller( bundles_iter, - #[cfg(feature = "track_location")] caller, ) { error!( diff --git a/crates/bevy_ecs/src/system/system_param.rs b/crates/bevy_ecs/src/system/system_param.rs index 3a464e82a1..a12323e264 100644 --- a/crates/bevy_ecs/src/system/system_param.rs +++ b/crates/bevy_ecs/src/system/system_param.rs @@ -2,7 +2,7 @@ pub use crate::change_detection::{NonSendMut, Res, ResMut}; use crate::{ archetype::{Archetype, Archetypes}, bundle::Bundles, - change_detection::{Ticks, TicksMut}, + change_detection::{MaybeLocation, Ticks, TicksMut}, component::{ComponentId, ComponentTicks, Components, Tick}, entity::Entities, query::{ @@ -21,13 +21,12 @@ use alloc::{borrow::ToOwned, boxed::Box, vec::Vec}; pub use bevy_ecs_macros::SystemParam; use bevy_ptr::UnsafeCellDeref; use bevy_utils::synccell::SyncCell; -#[cfg(feature = "track_location")] -use core::panic::Location; use core::{ any::Any, fmt::Debug, marker::PhantomData, ops::{Deref, DerefMut}, + panic::Location, }; use disqualified::ShortName; @@ -857,7 +856,7 @@ unsafe impl<'a, T: Resource> SystemParam for Res<'a, T> { world: UnsafeWorldCell<'w>, change_tick: Tick, ) -> Self::Item<'w, 's> { - let (ptr, ticks, _caller) = + let (ptr, ticks, caller) = world .get_resource_with_ticks(component_id) .unwrap_or_else(|| { @@ -875,8 +874,7 @@ unsafe impl<'a, T: Resource> SystemParam for Res<'a, T> { last_run: system_meta.last_run, this_run: change_tick, }, - #[cfg(feature = "track_location")] - changed_by: _caller.deref(), + changed_by: caller.map(|caller| caller.deref()), } } } @@ -902,7 +900,7 @@ unsafe impl<'a, T: Resource> SystemParam for Option> { ) -> Self::Item<'w, 's> { world .get_resource_with_ticks(component_id) - .map(|(ptr, ticks, _caller)| Res { + .map(|(ptr, ticks, caller)| Res { value: ptr.deref(), ticks: Ticks { added: ticks.added.deref(), @@ -910,8 +908,7 @@ unsafe impl<'a, T: Resource> SystemParam for Option> { last_run: system_meta.last_run, this_run: change_tick, }, - #[cfg(feature = "track_location")] - changed_by: _caller.deref(), + changed_by: caller.map(|caller| caller.deref()), }) } } @@ -988,7 +985,6 @@ unsafe impl<'a, T: Resource> SystemParam for ResMut<'a, T> { last_run: system_meta.last_run, this_run: change_tick, }, - #[cfg(feature = "track_location")] changed_by: value.changed_by, } } @@ -1020,7 +1016,6 @@ unsafe impl<'a, T: Resource> SystemParam for Option> { last_run: system_meta.last_run, this_run: change_tick, }, - #[cfg(feature = "track_location")] changed_by: value.changed_by, }) } @@ -1435,8 +1430,7 @@ pub struct NonSend<'w, T: 'static> { ticks: ComponentTicks, last_run: Tick, this_run: Tick, - #[cfg(feature = "track_location")] - changed_by: &'static Location<'static>, + changed_by: MaybeLocation<&'w &'static Location<'static>>, } // SAFETY: Only reads a single World non-send resource @@ -1463,9 +1457,8 @@ impl<'w, T: 'static> NonSend<'w, T> { } /// The location that last caused this to change. - #[cfg(feature = "track_location")] - pub fn changed_by(&self) -> &'static Location<'static> { - self.changed_by + pub fn changed_by(&self) -> MaybeLocation { + self.changed_by.copied() } } @@ -1486,8 +1479,7 @@ impl<'a, T> From> for NonSend<'a, T> { }, this_run: nsm.ticks.this_run, last_run: nsm.ticks.last_run, - #[cfg(feature = "track_location")] - changed_by: nsm.changed_by, + changed_by: nsm.changed_by.map(|changed_by| &*changed_by), } } } @@ -1546,7 +1538,7 @@ unsafe impl<'a, T: 'static> SystemParam for NonSend<'a, T> { world: UnsafeWorldCell<'w>, change_tick: Tick, ) -> Self::Item<'w, 's> { - let (ptr, ticks, _caller) = + let (ptr, ticks, caller) = world .get_non_send_with_ticks(component_id) .unwrap_or_else(|| { @@ -1562,8 +1554,7 @@ unsafe impl<'a, T: 'static> SystemParam for NonSend<'a, T> { ticks: ticks.read(), last_run: system_meta.last_run, this_run: change_tick, - #[cfg(feature = "track_location")] - changed_by: _caller.deref(), + changed_by: caller.map(|caller| caller.deref()), } } } @@ -1589,13 +1580,12 @@ unsafe impl SystemParam for Option> { ) -> Self::Item<'w, 's> { world .get_non_send_with_ticks(component_id) - .map(|(ptr, ticks, _caller)| NonSend { + .map(|(ptr, ticks, caller)| NonSend { value: ptr.deref(), ticks: ticks.read(), last_run: system_meta.last_run, this_run: change_tick, - #[cfg(feature = "track_location")] - changed_by: _caller.deref(), + changed_by: caller.map(|caller| caller.deref()), }) } } @@ -1657,7 +1647,7 @@ unsafe impl<'a, T: 'static> SystemParam for NonSendMut<'a, T> { world: UnsafeWorldCell<'w>, change_tick: Tick, ) -> Self::Item<'w, 's> { - let (ptr, ticks, _caller) = + let (ptr, ticks, caller) = world .get_non_send_with_ticks(component_id) .unwrap_or_else(|| { @@ -1670,8 +1660,7 @@ unsafe impl<'a, T: 'static> SystemParam for NonSendMut<'a, T> { NonSendMut { value: ptr.assert_unique().deref_mut(), ticks: TicksMut::from_tick_cells(ticks, system_meta.last_run, change_tick), - #[cfg(feature = "track_location")] - changed_by: _caller.deref_mut(), + changed_by: caller.map(|caller| caller.deref_mut()), } } } @@ -1694,11 +1683,10 @@ unsafe impl<'a, T: 'static> SystemParam for Option> { ) -> Self::Item<'w, 's> { world .get_non_send_with_ticks(component_id) - .map(|(ptr, ticks, _caller)| NonSendMut { + .map(|(ptr, ticks, caller)| NonSendMut { value: ptr.assert_unique().deref_mut(), ticks: TicksMut::from_tick_cells(ticks, system_meta.last_run, change_tick), - #[cfg(feature = "track_location")] - changed_by: _caller.deref_mut(), + changed_by: caller.map(|caller| caller.deref_mut()), }) } } diff --git a/crates/bevy_ecs/src/world/deferred_world.rs b/crates/bevy_ecs/src/world/deferred_world.rs index 4697dc9d30..b149761bd5 100644 --- a/crates/bevy_ecs/src/world/deferred_world.rs +++ b/crates/bevy_ecs/src/world/deferred_world.rs @@ -1,10 +1,8 @@ use core::ops::Deref; -#[cfg(feature = "track_location")] -use core::panic::Location; use crate::{ archetype::Archetype, - change_detection::MutUntyped, + change_detection::{MaybeLocation, MutUntyped}, component::{ComponentId, HookContext, Mutable}, entity::Entity, event::{Event, EventId, Events, SendBatchIds}, @@ -132,16 +130,14 @@ impl<'w> DeferredWorld<'w> { archetype, entity, [component_id].into_iter(), - #[cfg(feature = "track_location")] - Location::caller(), + MaybeLocation::caller(), ); if archetype.has_replace_observer() { self.trigger_observers( ON_REPLACE, entity, [component_id].into_iter(), - #[cfg(feature = "track_location")] - Location::caller(), + MaybeLocation::caller(), ); } } @@ -173,16 +169,14 @@ impl<'w> DeferredWorld<'w> { archetype, entity, [component_id].into_iter(), - #[cfg(feature = "track_location")] - Location::caller(), + MaybeLocation::caller(), ); if archetype.has_insert_observer() { self.trigger_observers( ON_INSERT, entity, [component_id].into_iter(), - #[cfg(feature = "track_location")] - Location::caller(), + MaybeLocation::caller(), ); } } @@ -519,7 +513,7 @@ impl<'w> DeferredWorld<'w> { archetype: &Archetype, entity: Entity, targets: impl Iterator, - #[cfg(feature = "track_location")] caller: &'static Location<'static>, + caller: MaybeLocation, ) { if archetype.has_add_hook() { for component_id in targets { @@ -531,10 +525,7 @@ impl<'w> DeferredWorld<'w> { HookContext { entity, component_id, - #[cfg(feature = "track_location")] - caller: Some(caller), - #[cfg(not(feature = "track_location"))] - caller: None, + caller, }, ); } @@ -552,7 +543,7 @@ impl<'w> DeferredWorld<'w> { archetype: &Archetype, entity: Entity, targets: impl Iterator, - #[cfg(feature = "track_location")] caller: &'static Location<'static>, + caller: MaybeLocation, ) { if archetype.has_insert_hook() { for component_id in targets { @@ -564,10 +555,7 @@ impl<'w> DeferredWorld<'w> { HookContext { entity, component_id, - #[cfg(feature = "track_location")] - caller: Some(caller), - #[cfg(not(feature = "track_location"))] - caller: None, + caller, }, ); } @@ -585,7 +573,7 @@ impl<'w> DeferredWorld<'w> { archetype: &Archetype, entity: Entity, targets: impl Iterator, - #[cfg(feature = "track_location")] caller: &'static Location<'static>, + caller: MaybeLocation, ) { if archetype.has_replace_hook() { for component_id in targets { @@ -597,10 +585,7 @@ impl<'w> DeferredWorld<'w> { HookContext { entity, component_id, - #[cfg(feature = "track_location")] - caller: Some(caller), - #[cfg(not(feature = "track_location"))] - caller: None, + caller, }, ); } @@ -618,7 +603,7 @@ impl<'w> DeferredWorld<'w> { archetype: &Archetype, entity: Entity, targets: impl Iterator, - #[cfg(feature = "track_location")] caller: &'static Location<'static>, + caller: MaybeLocation, ) { if archetype.has_remove_hook() { for component_id in targets { @@ -630,10 +615,7 @@ impl<'w> DeferredWorld<'w> { HookContext { entity, component_id, - #[cfg(feature = "track_location")] - caller: Some(caller), - #[cfg(not(feature = "track_location"))] - caller: None, + caller, }, ); } @@ -651,7 +633,7 @@ impl<'w> DeferredWorld<'w> { archetype: &Archetype, entity: Entity, targets: impl Iterator, - #[cfg(feature = "track_location")] caller: &'static Location<'static>, + caller: MaybeLocation, ) { if archetype.has_despawn_hook() { for component_id in targets { @@ -663,10 +645,7 @@ impl<'w> DeferredWorld<'w> { HookContext { entity, component_id, - #[cfg(feature = "track_location")] - caller: Some(caller), - #[cfg(not(feature = "track_location"))] - caller: None, + caller, }, ); } @@ -684,7 +663,7 @@ impl<'w> DeferredWorld<'w> { event: ComponentId, target: Entity, components: impl Iterator + Clone, - #[cfg(feature = "track_location")] caller: &'static Location<'static>, + caller: MaybeLocation, ) { Observers::invoke::<_>( self.reborrow(), @@ -693,7 +672,6 @@ impl<'w> DeferredWorld<'w> { components, &mut (), &mut false, - #[cfg(feature = "track_location")] caller, ); } @@ -710,7 +688,7 @@ impl<'w> DeferredWorld<'w> { components: &[ComponentId], data: &mut E, mut propagate: bool, - #[cfg(feature = "track_location")] caller: &'static Location<'static>, + caller: MaybeLocation, ) where T: Traversal, { @@ -722,7 +700,6 @@ impl<'w> DeferredWorld<'w> { components.iter().copied(), data, &mut propagate, - #[cfg(feature = "track_location")] caller, ); if !propagate { diff --git a/crates/bevy_ecs/src/world/entity_ref.rs b/crates/bevy_ecs/src/world/entity_ref.rs index 3a9a49e7fa..ef043d0acc 100644 --- a/crates/bevy_ecs/src/world/entity_ref.rs +++ b/crates/bevy_ecs/src/world/entity_ref.rs @@ -4,7 +4,7 @@ use crate::{ Bundle, BundleEffect, BundleFromComponents, BundleId, BundleInfo, BundleInserter, DynamicBundle, InsertMode, }, - change_detection::MutUntyped, + change_detection::{MaybeLocation, MutUntyped}, component::{Component, ComponentId, ComponentTicks, Components, Mutable, StorageType}, entity::{ Entities, Entity, EntityBorrow, EntityCloner, EntityClonerBuilder, EntityLocation, @@ -25,8 +25,6 @@ use crate::{ use alloc::vec::Vec; use bevy_platform_support::collections::{HashMap, HashSet}; use bevy_ptr::{OwningPtr, Ptr}; -#[cfg(feature = "track_location")] -use core::panic::Location; use core::{ any::TypeId, cmp::Ordering, @@ -294,8 +292,7 @@ impl<'w> EntityRef<'w> { } /// Returns the source code location from which this entity has been spawned. - #[cfg(feature = "track_location")] - pub fn spawned_by(&self) -> &'static Location<'static> { + pub fn spawned_by(&self) -> MaybeLocation { self.cell.spawned_by() } } @@ -885,8 +882,7 @@ impl<'w> EntityMut<'w> { } /// Returns the source code location from which this entity has been spawned. - #[cfg(feature = "track_location")] - pub fn spawned_by(&self) -> &'static Location<'static> { + pub fn spawned_by(&self) -> MaybeLocation { self.cell.spawned_by() } } @@ -1530,12 +1526,7 @@ impl<'w> EntityWorldMut<'w> { /// If the entity has been despawned while this `EntityWorldMut` is still alive. #[track_caller] pub fn insert(&mut self, bundle: T) -> &mut Self { - self.insert_with_caller( - bundle, - InsertMode::Replace, - #[cfg(feature = "track_location")] - Location::caller(), - ) + self.insert_with_caller(bundle, InsertMode::Replace, MaybeLocation::caller()) } /// Adds a [`Bundle`] of components to the entity without overwriting. @@ -1548,12 +1539,7 @@ impl<'w> EntityWorldMut<'w> { /// If the entity has been despawned while this `EntityWorldMut` is still alive. #[track_caller] pub fn insert_if_new(&mut self, bundle: T) -> &mut Self { - self.insert_with_caller( - bundle, - InsertMode::Keep, - #[cfg(feature = "track_location")] - Location::caller(), - ) + self.insert_with_caller(bundle, InsertMode::Keep, MaybeLocation::caller()) } /// Split into a new function so we can pass the calling location into the function when using @@ -1563,23 +1549,15 @@ impl<'w> EntityWorldMut<'w> { &mut self, bundle: T, mode: InsertMode, - #[cfg(feature = "track_location")] caller: &'static Location, + caller: MaybeLocation, ) -> &mut Self { self.assert_not_despawned(); let change_tick = self.world.change_tick(); let mut bundle_inserter = BundleInserter::new::(self.world, self.location.archetype_id, change_tick); // SAFETY: location matches current entity. `T` matches `bundle_info` - let (location, after_effect) = unsafe { - bundle_inserter.insert( - self.entity, - self.location, - bundle, - mode, - #[cfg(feature = "track_location")] - caller, - ) - }; + let (location, after_effect) = + unsafe { bundle_inserter.insert(self.entity, self.location, bundle, mode, caller) }; self.location = location; self.world.flush(); self.update_location(); @@ -1607,12 +1585,7 @@ impl<'w> EntityWorldMut<'w> { component_id: ComponentId, component: OwningPtr<'_>, ) -> &mut Self { - self.insert_by_id_with_caller( - component_id, - component, - #[cfg(feature = "track_location")] - Location::caller(), - ) + self.insert_by_id_with_caller(component_id, component, MaybeLocation::caller()) } /// # Safety @@ -1622,7 +1595,7 @@ impl<'w> EntityWorldMut<'w> { &mut self, component_id: ComponentId, component: OwningPtr<'_>, - #[cfg(feature = "track_location")] caller: &'static Location<'static>, + caller: MaybeLocation, ) -> &mut Self { self.assert_not_despawned(); let change_tick = self.world.change_tick(); @@ -1646,7 +1619,6 @@ impl<'w> EntityWorldMut<'w> { self.location, Some(component).into_iter(), Some(storage_type).iter().cloned(), - #[cfg(feature = "track_location")] caller, ); self.world.flush(); @@ -1698,8 +1670,7 @@ impl<'w> EntityWorldMut<'w> { self.location, iter_components, (*storage_types).iter().cloned(), - #[cfg(feature = "track_location")] - Location::caller(), + MaybeLocation::caller(), ); *self.world.bundles.get_storages_unchecked(bundle_id) = core::mem::take(&mut storage_types); self.world.flush(); @@ -1763,8 +1734,7 @@ impl<'w> EntityWorldMut<'w> { old_archetype, entity, bundle_info, - #[cfg(feature = "track_location")] - Location::caller(), + MaybeLocation::caller(), ); } @@ -1906,11 +1876,7 @@ impl<'w> EntityWorldMut<'w> { /// /// # Safety /// - A `BundleInfo` with the corresponding `BundleId` must have been initialized. - unsafe fn remove_bundle( - &mut self, - bundle: BundleId, - #[cfg(feature = "track_location")] caller: &'static Location<'static>, - ) -> EntityLocation { + unsafe fn remove_bundle(&mut self, bundle: BundleId, caller: MaybeLocation) -> EntityLocation { let entity = self.entity; let world = &mut self.world; let location = self.location; @@ -1953,7 +1919,6 @@ impl<'w> EntityWorldMut<'w> { old_archetype, entity, bundle_info, - #[cfg(feature = "track_location")] caller, ); } @@ -2004,30 +1969,18 @@ impl<'w> EntityWorldMut<'w> { // TODO: BundleRemover? #[track_caller] pub fn remove(&mut self) -> &mut Self { - self.remove_with_caller::( - #[cfg(feature = "track_location")] - Location::caller(), - ) + self.remove_with_caller::(MaybeLocation::caller()) } #[inline] - pub(crate) fn remove_with_caller( - &mut self, - #[cfg(feature = "track_location")] caller: &'static Location<'static>, - ) -> &mut Self { + pub(crate) fn remove_with_caller(&mut self, caller: MaybeLocation) -> &mut Self { self.assert_not_despawned(); let storages = &mut self.world.storages; let components = &mut self.world.components; let bundle_info = self.world.bundles.register_info::(components, storages); // SAFETY: the `BundleInfo` is initialized above - self.location = unsafe { - self.remove_bundle( - bundle_info, - #[cfg(feature = "track_location")] - caller, - ) - }; + self.location = unsafe { self.remove_bundle(bundle_info, caller) }; self.world.flush(); self.update_location(); self @@ -2040,15 +1993,12 @@ impl<'w> EntityWorldMut<'w> { /// If the entity has been despawned while this `EntityWorldMut` is still alive. #[track_caller] pub fn remove_with_requires(&mut self) -> &mut Self { - self.remove_with_requires_with_caller::( - #[cfg(feature = "track_location")] - Location::caller(), - ) + self.remove_with_requires_with_caller::(MaybeLocation::caller()) } pub(crate) fn remove_with_requires_with_caller( &mut self, - #[cfg(feature = "track_location")] caller: &'static Location<'static>, + caller: MaybeLocation, ) -> &mut Self { self.assert_not_despawned(); let storages = &mut self.world.storages; @@ -2058,13 +2008,7 @@ impl<'w> EntityWorldMut<'w> { let bundle_id = bundles.register_contributed_bundle_info::(components, storages); // SAFETY: the dynamic `BundleInfo` is initialized above - self.location = unsafe { - self.remove_bundle( - bundle_id, - #[cfg(feature = "track_location")] - caller, - ) - }; + self.location = unsafe { self.remove_bundle(bundle_id, caller) }; self.world.flush(); self.update_location(); self @@ -2079,17 +2023,11 @@ impl<'w> EntityWorldMut<'w> { /// If the entity has been despawned while this `EntityWorldMut` is still alive. #[track_caller] pub fn retain(&mut self) -> &mut Self { - self.retain_with_caller::( - #[cfg(feature = "track_location")] - Location::caller(), - ) + self.retain_with_caller::(MaybeLocation::caller()) } #[inline] - pub(crate) fn retain_with_caller( - &mut self, - #[cfg(feature = "track_location")] caller: &'static Location<'static>, - ) -> &mut Self { + pub(crate) fn retain_with_caller(&mut self, caller: MaybeLocation) -> &mut Self { self.assert_not_despawned(); let archetypes = &mut self.world.archetypes; let storages = &mut self.world.storages; @@ -2112,13 +2050,7 @@ impl<'w> EntityWorldMut<'w> { .init_dynamic_info(&mut self.world.storages, components, to_remove); // SAFETY: the `BundleInfo` for the components to remove is initialized above - self.location = unsafe { - self.remove_bundle( - remove_bundle, - #[cfg(feature = "track_location")] - caller, - ) - }; + self.location = unsafe { self.remove_bundle(remove_bundle, caller) }; self.world.flush(); self.update_location(); self @@ -2134,18 +2066,14 @@ impl<'w> EntityWorldMut<'w> { /// entity has been despawned while this `EntityWorldMut` is still alive. #[track_caller] pub fn remove_by_id(&mut self, component_id: ComponentId) -> &mut Self { - self.remove_by_id_with_caller( - component_id, - #[cfg(feature = "track_location")] - Location::caller(), - ) + self.remove_by_id_with_caller(component_id, MaybeLocation::caller()) } #[inline] pub(crate) fn remove_by_id_with_caller( &mut self, component_id: ComponentId, - #[cfg(feature = "track_location")] caller: &'static Location<'static>, + caller: MaybeLocation, ) -> &mut Self { self.assert_not_despawned(); let components = &mut self.world.components; @@ -2157,13 +2085,7 @@ impl<'w> EntityWorldMut<'w> { ); // SAFETY: the `BundleInfo` for this `component_id` is initialized above - self.location = unsafe { - self.remove_bundle( - bundle_id, - #[cfg(feature = "track_location")] - caller, - ) - }; + self.location = unsafe { self.remove_bundle(bundle_id, caller) }; self.world.flush(); self.update_location(); self @@ -2189,13 +2111,7 @@ impl<'w> EntityWorldMut<'w> { ); // SAFETY: the `BundleInfo` for this `bundle_id` is initialized above - unsafe { - self.remove_bundle( - bundle_id, - #[cfg(feature = "track_location")] - Location::caller(), - ) - }; + unsafe { self.remove_bundle(bundle_id, MaybeLocation::caller()) }; self.world.flush(); self.update_location(); @@ -2209,17 +2125,11 @@ impl<'w> EntityWorldMut<'w> { /// If the entity has been despawned while this `EntityWorldMut` is still alive. #[track_caller] pub fn clear(&mut self) -> &mut Self { - self.clear_with_caller( - #[cfg(feature = "track_location")] - Location::caller(), - ) + self.clear_with_caller(MaybeLocation::caller()) } #[inline] - pub(crate) fn clear_with_caller( - &mut self, - #[cfg(feature = "track_location")] caller: &'static Location<'static>, - ) -> &mut Self { + pub(crate) fn clear_with_caller(&mut self, caller: MaybeLocation) -> &mut Self { self.assert_not_despawned(); let component_ids: Vec = self.archetype().components().collect(); let components = &mut self.world.components; @@ -2231,13 +2141,7 @@ impl<'w> EntityWorldMut<'w> { ); // SAFETY: the `BundleInfo` for this `component_id` is initialized above - self.location = unsafe { - self.remove_bundle( - bundle_id, - #[cfg(feature = "track_location")] - caller, - ) - }; + self.location = unsafe { self.remove_bundle(bundle_id, caller) }; self.world.flush(); self.update_location(); self @@ -2257,10 +2161,7 @@ impl<'w> EntityWorldMut<'w> { /// If the entity has been despawned while this `EntityWorldMut` is still alive. #[track_caller] pub fn despawn(self) { - self.despawn_with_caller( - #[cfg(feature = "track_location")] - Location::caller(), - ); + self.despawn_with_caller(MaybeLocation::caller()); } /// Despawns the provided entity and its descendants. @@ -2272,10 +2173,7 @@ impl<'w> EntityWorldMut<'w> { self.despawn(); } - pub(crate) fn despawn_with_caller( - self, - #[cfg(feature = "track_location")] caller: &'static Location, - ) { + pub(crate) fn despawn_with_caller(self, caller: MaybeLocation) { self.assert_not_despawned(); let world = self.world; let archetype = &world.archetypes[self.location.archetype_id]; @@ -2294,7 +2192,6 @@ impl<'w> EntityWorldMut<'w> { ON_DESPAWN, self.entity, archetype.components(), - #[cfg(feature = "track_location")] caller, ); } @@ -2302,7 +2199,6 @@ impl<'w> EntityWorldMut<'w> { archetype, self.entity, archetype.components(), - #[cfg(feature = "track_location")] caller, ); if archetype.has_replace_observer() { @@ -2310,7 +2206,6 @@ impl<'w> EntityWorldMut<'w> { ON_REPLACE, self.entity, archetype.components(), - #[cfg(feature = "track_location")] caller, ); } @@ -2318,7 +2213,6 @@ impl<'w> EntityWorldMut<'w> { archetype, self.entity, archetype.components(), - #[cfg(feature = "track_location")] caller, ); if archetype.has_remove_observer() { @@ -2326,7 +2220,6 @@ impl<'w> EntityWorldMut<'w> { ON_REMOVE, self.entity, archetype.components(), - #[cfg(feature = "track_location")] caller, ); } @@ -2334,7 +2227,6 @@ impl<'w> EntityWorldMut<'w> { archetype, self.entity, archetype.components(), - #[cfg(feature = "track_location")] caller, ); } @@ -2406,14 +2298,11 @@ impl<'w> EntityWorldMut<'w> { } world.flush(); - #[cfg(feature = "track_location")] - { - // SAFETY: No structural changes - unsafe { - world - .entities_mut() - .set_spawned_or_despawned_by(self.entity.index(), caller); - } + // SAFETY: No structural changes + unsafe { + world + .entities_mut() + .set_spawned_or_despawned_by(self.entity.index(), caller); } } @@ -2579,24 +2468,17 @@ impl<'w> EntityWorldMut<'w> { &mut self, observer: impl IntoObserverSystem, ) -> &mut Self { - self.observe_with_caller( - observer, - #[cfg(feature = "track_location")] - Location::caller(), - ) + self.observe_with_caller(observer, MaybeLocation::caller()) } pub(crate) fn observe_with_caller( &mut self, observer: impl IntoObserverSystem, - #[cfg(feature = "track_location")] caller: &'static Location<'static>, + caller: MaybeLocation, ) -> &mut Self { self.assert_not_despawned(); - self.world.spawn_with_caller( - Observer::new(observer).with_entity(self.entity), - #[cfg(feature = "track_location")] - caller, - ); + self.world + .spawn_with_caller(Observer::new(observer).with_entity(self.entity), caller); self.world.flush(); self.update_location(); self @@ -2754,12 +2636,11 @@ impl<'w> EntityWorldMut<'w> { } /// Returns the source code location from which this entity has last been spawned. - #[cfg(feature = "track_location")] - pub fn spawned_by(&self) -> &'static Location<'static> { + pub fn spawned_by(&self) -> MaybeLocation { self.world() .entities() .entity_get_spawned_or_despawned_by(self.entity) - .unwrap() + .map(|location| location.unwrap()) } } @@ -2770,14 +2651,13 @@ unsafe fn trigger_on_replace_and_on_remove_hooks_and_observers( archetype: &Archetype, entity: Entity, bundle_info: &BundleInfo, - #[cfg(feature = "track_location")] caller: &'static Location<'static>, + caller: MaybeLocation, ) { if archetype.has_replace_observer() { deferred_world.trigger_observers( ON_REPLACE, entity, bundle_info.iter_explicit_components(), - #[cfg(feature = "track_location")] caller, ); } @@ -2785,7 +2665,6 @@ unsafe fn trigger_on_replace_and_on_remove_hooks_and_observers( archetype, entity, bundle_info.iter_explicit_components(), - #[cfg(feature = "track_location")] caller, ); if archetype.has_remove_observer() { @@ -2793,7 +2672,6 @@ unsafe fn trigger_on_replace_and_on_remove_hooks_and_observers( ON_REMOVE, entity, bundle_info.iter_explicit_components(), - #[cfg(feature = "track_location")] caller, ); } @@ -2801,7 +2679,6 @@ unsafe fn trigger_on_replace_and_on_remove_hooks_and_observers( archetype, entity, bundle_info.iter_explicit_components(), - #[cfg(feature = "track_location")] caller, ); } @@ -3315,8 +3192,7 @@ impl<'w> FilteredEntityRef<'w> { } /// Returns the source code location from which this entity has been spawned. - #[cfg(feature = "track_location")] - pub fn spawned_by(&self) -> &'static Location<'static> { + pub fn spawned_by(&self) -> MaybeLocation { self.entity.spawned_by() } } @@ -3679,8 +3555,7 @@ impl<'w> FilteredEntityMut<'w> { } /// Returns the source code location from which this entity has last been spawned. - #[cfg(feature = "track_location")] - pub fn spawned_by(&self) -> &'static Location<'static> { + pub fn spawned_by(&self) -> MaybeLocation { self.entity.spawned_by() } } @@ -3859,8 +3734,7 @@ where } /// Returns the source code location from which this entity has been spawned. - #[cfg(feature = "track_location")] - pub fn spawned_by(&self) -> &'static Location<'static> { + pub fn spawned_by(&self) -> MaybeLocation { self.entity.spawned_by() } } @@ -4017,8 +3891,7 @@ where } /// Returns the source code location from which this entity has been spawned. - #[cfg(feature = "track_location")] - pub fn spawned_by(&self) -> &'static Location<'static> { + pub fn spawned_by(&self) -> MaybeLocation { self.entity.spawned_by() } } @@ -4090,7 +3963,7 @@ unsafe fn insert_dynamic_bundle< location: EntityLocation, components: I, storage_types: S, - #[cfg(feature = "track_location")] caller: &'static Location<'static>, + caller: MaybeLocation, ) -> EntityLocation { struct DynamicInsertBundle<'a, I: Iterator)>> { components: I, @@ -4112,14 +3985,7 @@ unsafe fn insert_dynamic_bundle< // SAFETY: location matches current entity. unsafe { bundle_inserter - .insert( - entity, - location, - bundle, - InsertMode::Replace, - #[cfg(feature = "track_location")] - caller, - ) + .insert(entity, location, bundle, InsertMode::Replace, caller) .0 } } @@ -4425,13 +4291,11 @@ mod tests { use alloc::{vec, vec::Vec}; use bevy_ptr::{OwningPtr, Ptr}; use core::panic::AssertUnwindSafe; - - #[cfg(feature = "track_location")] - use {core::panic::Location, std::sync::OnceLock}; + use std::sync::OnceLock; use crate::component::HookContext; use crate::{ - change_detection::MutUntyped, + change_detection::{MaybeLocation, MutUntyped}, component::ComponentId, prelude::*, system::{assert_is_system, RunSystemOnce as _}, @@ -5710,7 +5574,6 @@ mod tests { } #[test] - #[cfg(feature = "track_location")] fn update_despawned_by_after_observers() { let mut world = World::new(); @@ -5718,19 +5581,19 @@ mod tests { #[component(on_remove = get_tracked)] struct C; - static TRACKED: OnceLock<&'static Location<'static>> = OnceLock::new(); + static TRACKED: OnceLock = OnceLock::new(); fn get_tracked(world: DeferredWorld, HookContext { entity, .. }: HookContext) { TRACKED.get_or_init(|| { world .entities .entity_get_spawned_or_despawned_by(entity) - .unwrap() + .map(|l| l.unwrap()) }); } #[track_caller] - fn caller_spawn(world: &mut World) -> (Entity, &'static Location<'static>) { - let caller = Location::caller(); + fn caller_spawn(world: &mut World) -> (Entity, MaybeLocation) { + let caller = MaybeLocation::caller(); (world.spawn(C).id(), caller) } let (entity, spawner) = caller_spawn(&mut world); @@ -5740,13 +5603,13 @@ mod tests { world .entities() .entity_get_spawned_or_despawned_by(entity) - .unwrap() + .map(|l| l.unwrap()) ); #[track_caller] - fn caller_despawn(world: &mut World, entity: Entity) -> &'static Location<'static> { + fn caller_despawn(world: &mut World, entity: Entity) -> MaybeLocation { world.despawn(entity); - Location::caller() + MaybeLocation::caller() } let despawner = caller_despawn(&mut world, entity); @@ -5756,7 +5619,7 @@ mod tests { world .entities() .entity_get_spawned_or_despawned_by(entity) - .unwrap() + .map(|l| l.unwrap()) ); } diff --git a/crates/bevy_ecs/src/world/filtered_resource.rs b/crates/bevy_ecs/src/world/filtered_resource.rs index 78794866e8..e5197e0570 100644 --- a/crates/bevy_ecs/src/world/filtered_resource.rs +++ b/crates/bevy_ecs/src/world/filtered_resource.rs @@ -5,9 +5,7 @@ use crate::{ resource::Resource, world::{unsafe_world_cell::UnsafeWorldCell, World}, }; -use bevy_ptr::Ptr; -#[cfg(feature = "track_location")] -use bevy_ptr::UnsafeCellDeref; +use bevy_ptr::{Ptr, UnsafeCellDeref}; /// Provides read-only access to a set of [`Resource`]s defined by the contained [`Access`]. /// @@ -159,17 +157,16 @@ impl<'w, 's> FilteredResources<'w, 's> { return None; } // SAFETY: We have read access to this resource - unsafe { self.world.get_resource_with_ticks(component_id) }.map( - |(value, ticks, _caller)| Ref { + unsafe { self.world.get_resource_with_ticks(component_id) }.map(|(value, ticks, caller)| { + Ref { // SAFETY: `component_id` was obtained from the type ID of `R`. value: unsafe { value.deref() }, // SAFETY: We have read access to the resource, so no mutable reference can exist. ticks: unsafe { Ticks::from_tick_cells(ticks, self.last_run, self.this_run) }, - #[cfg(feature = "track_location")] // SAFETY: We have read access to the resource, so no mutable reference can exist. - changed_by: unsafe { _caller.deref() }, - }, - ) + changed_by: unsafe { caller.map(|caller| caller.deref()) }, + } + }) } /// Gets a pointer to the resource with the given [`ComponentId`] if it exists and the `FilteredResources` has access to it. @@ -477,17 +474,16 @@ impl<'w, 's> FilteredResourcesMut<'w, 's> { return None; } // SAFETY: We have access to this resource in `access`, and the caller ensures that there are no conflicting borrows for the duration of the returned value. - unsafe { self.world.get_resource_with_ticks(component_id) }.map( - |(value, ticks, _caller)| MutUntyped { + unsafe { self.world.get_resource_with_ticks(component_id) }.map(|(value, ticks, caller)| { + MutUntyped { // SAFETY: We have exclusive access to the underlying storage. value: unsafe { value.assert_unique() }, // SAFETY: We have exclusive access to the underlying storage. ticks: unsafe { TicksMut::from_tick_cells(ticks, self.last_run, self.this_run) }, - #[cfg(feature = "track_location")] // SAFETY: We have exclusive access to the underlying storage. - changed_by: unsafe { _caller.deref_mut() }, - }, - ) + changed_by: unsafe { caller.map(|caller| caller.deref_mut()) }, + } + }) } } diff --git a/crates/bevy_ecs/src/world/mod.rs b/crates/bevy_ecs/src/world/mod.rs index 71f581d6e0..2c5ea2ef17 100644 --- a/crates/bevy_ecs/src/world/mod.rs +++ b/crates/bevy_ecs/src/world/mod.rs @@ -36,7 +36,7 @@ use crate::{ Bundle, BundleEffect, BundleInfo, BundleInserter, BundleSpawner, Bundles, InsertMode, NoBundleEffect, }, - change_detection::{MutUntyped, TicksMut}, + change_detection::{MaybeLocation, MutUntyped, TicksMut}, component::{ Component, ComponentDescriptor, ComponentHooks, ComponentId, ComponentInfo, ComponentTicks, Components, Mutable, RequiredComponents, RequiredComponentsError, Tick, @@ -59,17 +59,11 @@ use crate::{ }; use alloc::{boxed::Box, vec::Vec}; use bevy_platform_support::sync::atomic::{AtomicU32, Ordering}; -use bevy_ptr::{OwningPtr, Ptr}; +use bevy_ptr::{OwningPtr, Ptr, UnsafeCellDeref}; use core::{any::TypeId, fmt}; use log::warn; use unsafe_world_cell::{UnsafeEntityCell, UnsafeWorldCell}; -#[cfg(feature = "track_location")] -use bevy_ptr::UnsafeCellDeref; - -#[cfg(feature = "track_location")] -use core::panic::Location; - /// Stores and exposes operations on [entities](Entity), [components](Component), resources, /// and their associated metadata. /// @@ -1013,13 +1007,7 @@ impl World { self.flush(); let entity = self.entities.alloc(); // SAFETY: entity was just allocated - unsafe { - self.spawn_at_empty_internal( - entity, - #[cfg(feature = "track_location")] - Location::caller(), - ) - } + unsafe { self.spawn_at_empty_internal(entity, MaybeLocation::caller()) } } /// Spawns a new [`Entity`] with a given [`Bundle`] of [components](`Component`) and returns @@ -1084,31 +1072,21 @@ impl World { /// ``` #[track_caller] pub fn spawn(&mut self, bundle: B) -> EntityWorldMut { - self.spawn_with_caller( - bundle, - #[cfg(feature = "track_location")] - Location::caller(), - ) + self.spawn_with_caller(bundle, MaybeLocation::caller()) } pub(crate) fn spawn_with_caller( &mut self, bundle: B, - #[cfg(feature = "track_location")] caller: &'static Location<'static>, + caller: MaybeLocation, ) -> EntityWorldMut { self.flush(); let change_tick = self.change_tick(); let entity = self.entities.alloc(); let mut bundle_spawner = BundleSpawner::new::(self, change_tick); // SAFETY: bundle's type matches `bundle_info`, entity is allocated but non-existent - let (mut entity_location, after_effect) = unsafe { - bundle_spawner.spawn_non_existent( - entity, - bundle, - #[cfg(feature = "track_location")] - caller, - ) - }; + let (mut entity_location, after_effect) = + unsafe { bundle_spawner.spawn_non_existent(entity, bundle, caller) }; // SAFETY: command_queue is not referenced anywhere else if !unsafe { self.command_queue.is_empty() } { @@ -1119,7 +1097,6 @@ impl World { .unwrap_or(EntityLocation::INVALID); } - #[cfg(feature = "track_location")] self.entities .set_spawned_or_despawned_by(entity.index(), caller); @@ -1134,7 +1111,7 @@ impl World { unsafe fn spawn_at_empty_internal( &mut self, entity: Entity, - #[cfg(feature = "track_location")] caller: &'static Location, + caller: MaybeLocation, ) -> EntityWorldMut { let archetype = self.archetypes.empty_mut(); // PERF: consider avoiding allocating entities in the empty archetype unless needed @@ -1144,7 +1121,6 @@ impl World { let location = unsafe { archetype.allocate(entity, table_row) }; self.entities.set(entity.index(), location); - #[cfg(feature = "track_location")] self.entities .set_spawned_or_despawned_by(entity.index(), caller); @@ -1179,12 +1155,7 @@ impl World { I: IntoIterator, I::Item: Bundle, { - SpawnBatchIter::new( - self, - iter.into_iter(), - #[cfg(feature = "track_location")] - Location::caller(), - ) + SpawnBatchIter::new(self, iter.into_iter(), MaybeLocation::caller()) } /// Retrieves a reference to the given `entity`'s [`Component`] of the given type. @@ -1317,11 +1288,7 @@ impl World { #[track_caller] #[inline] pub fn despawn(&mut self, entity: Entity) -> bool { - if let Err(error) = self.despawn_with_caller( - entity, - #[cfg(feature = "track_location")] - Location::caller(), - ) { + if let Err(error) = self.despawn_with_caller(entity, MaybeLocation::caller()) { warn!("{error}"); false } else { @@ -1341,25 +1308,18 @@ impl World { #[track_caller] #[inline] pub fn try_despawn(&mut self, entity: Entity) -> Result<(), TryDespawnError> { - self.despawn_with_caller( - entity, - #[cfg(feature = "track_location")] - Location::caller(), - ) + self.despawn_with_caller(entity, MaybeLocation::caller()) } #[inline] pub(crate) fn despawn_with_caller( &mut self, entity: Entity, - #[cfg(feature = "track_location")] caller: &'static Location, + caller: MaybeLocation, ) -> Result<(), TryDespawnError> { self.flush(); if let Ok(entity) = self.get_entity_mut(entity) { - entity.despawn_with_caller( - #[cfg(feature = "track_location")] - caller, - ); + entity.despawn_with_caller(caller); Ok(()) } else { Err(TryDespawnError { @@ -1630,8 +1590,7 @@ impl World { #[inline] #[track_caller] pub fn init_resource(&mut self) -> ComponentId { - #[cfg(feature = "track_location")] - let caller = Location::caller(); + let caller = MaybeLocation::caller(); let component_id = self.components.register_resource::(); if self .storages @@ -1643,12 +1602,7 @@ impl World { OwningPtr::make(value, |ptr| { // SAFETY: component_id was just initialized and corresponds to resource of type R. unsafe { - self.insert_resource_by_id( - component_id, - ptr, - #[cfg(feature = "track_location")] - caller, - ); + self.insert_resource_by_id(component_id, ptr, caller); } }); } @@ -1663,11 +1617,7 @@ impl World { #[inline] #[track_caller] pub fn insert_resource(&mut self, value: R) { - self.insert_resource_with_caller( - value, - #[cfg(feature = "track_location")] - Location::caller(), - ); + self.insert_resource_with_caller(value, MaybeLocation::caller()); } /// Split into a new function so we can pass the calling location into the function when using @@ -1676,18 +1626,13 @@ impl World { pub(crate) fn insert_resource_with_caller( &mut self, value: R, - #[cfg(feature = "track_location")] caller: &'static Location, + caller: MaybeLocation, ) { let component_id = self.components.register_resource::(); OwningPtr::make(value, |ptr| { // SAFETY: component_id was just initialized and corresponds to resource of type R. unsafe { - self.insert_resource_by_id( - component_id, - ptr, - #[cfg(feature = "track_location")] - caller, - ); + self.insert_resource_by_id(component_id, ptr, caller); } }); } @@ -1706,8 +1651,7 @@ impl World { #[inline] #[track_caller] pub fn init_non_send_resource(&mut self) -> ComponentId { - #[cfg(feature = "track_location")] - let caller = Location::caller(); + let caller = MaybeLocation::caller(); let component_id = self.components.register_non_send::(); if self .storages @@ -1719,12 +1663,7 @@ impl World { OwningPtr::make(value, |ptr| { // SAFETY: component_id was just initialized and corresponds to resource of type R. unsafe { - self.insert_non_send_by_id( - component_id, - ptr, - #[cfg(feature = "track_location")] - caller, - ); + self.insert_non_send_by_id(component_id, ptr, caller); } }); } @@ -1743,18 +1682,12 @@ impl World { #[inline] #[track_caller] pub fn insert_non_send_resource(&mut self, value: R) { - #[cfg(feature = "track_location")] - let caller = Location::caller(); + let caller = MaybeLocation::caller(); let component_id = self.components.register_non_send::(); OwningPtr::make(value, |ptr| { // SAFETY: component_id was just initialized and corresponds to resource of type R. unsafe { - self.insert_non_send_by_id( - component_id, - ptr, - #[cfg(feature = "track_location")] - caller, - ); + self.insert_non_send_by_id(component_id, ptr, caller); } }); } @@ -2029,8 +1962,7 @@ impl World { &mut self, func: impl FnOnce() -> R, ) -> Mut<'_, R> { - #[cfg(feature = "track_location")] - let caller = Location::caller(); + let caller = MaybeLocation::caller(); let change_tick = self.change_tick(); let last_change_tick = self.last_change_tick(); @@ -2040,12 +1972,7 @@ impl World { OwningPtr::make(func(), |ptr| { // SAFETY: component_id was just initialized and corresponds to resource of type R. unsafe { - data.insert( - ptr, - change_tick, - #[cfg(feature = "track_location")] - caller, - ); + data.insert(ptr, change_tick, caller); } }); } @@ -2093,8 +2020,7 @@ impl World { /// ``` #[track_caller] pub fn get_resource_or_init(&mut self) -> Mut<'_, R> { - #[cfg(feature = "track_location")] - let caller = Location::caller(); + let caller = MaybeLocation::caller(); let change_tick = self.change_tick(); let last_change_tick = self.last_change_tick(); @@ -2109,12 +2035,7 @@ impl World { OwningPtr::make(value, |ptr| { // SAFETY: component_id was just initialized and corresponds to resource of type R. unsafe { - self.insert_resource_by_id( - component_id, - ptr, - #[cfg(feature = "track_location")] - caller, - ); + self.insert_resource_by_id(component_id, ptr, caller); } }); } @@ -2241,11 +2162,7 @@ impl World { I::IntoIter: Iterator, B: Bundle, { - self.insert_or_spawn_batch_with_caller( - iter, - #[cfg(feature = "track_location")] - Location::caller(), - ) + self.insert_or_spawn_batch_with_caller(iter, MaybeLocation::caller()) } /// Split into a new function so we can pass the calling location into the function when using @@ -2254,7 +2171,7 @@ impl World { pub(crate) fn insert_or_spawn_batch_with_caller( &mut self, iter: I, - #[cfg(feature = "track_location")] caller: &'static Location, + caller: MaybeLocation, ) -> Result<(), Vec> where I: IntoIterator, @@ -2304,7 +2221,6 @@ impl World { location, bundle, InsertMode::Replace, - #[cfg(feature = "track_location")] caller, ) }; @@ -2326,7 +2242,6 @@ impl World { location, bundle, InsertMode::Replace, - #[cfg(feature = "track_location")] caller, ) }; @@ -2338,27 +2253,13 @@ impl World { AllocAtWithoutReplacement::DidNotExist => { if let SpawnOrInsert::Spawn(ref mut spawner) = spawn_or_insert { // SAFETY: `entity` is allocated (but non existent), bundle matches inserter - unsafe { - spawner.spawn_non_existent( - entity, - bundle, - #[cfg(feature = "track_location")] - caller, - ) - }; + unsafe { spawner.spawn_non_existent(entity, bundle, caller) }; } else { // SAFETY: we initialized this bundle_id in `init_info` let mut spawner = unsafe { BundleSpawner::new_with_id(self, bundle_id, change_tick) }; // SAFETY: `entity` is valid, `location` matches entity, bundle matches inserter - unsafe { - spawner.spawn_non_existent( - entity, - bundle, - #[cfg(feature = "track_location")] - caller, - ) - }; + unsafe { spawner.spawn_non_existent(entity, bundle, caller) }; spawn_or_insert = SpawnOrInsert::Spawn(spawner); } } @@ -2397,12 +2298,7 @@ impl World { I::IntoIter: Iterator, B: Bundle, { - self.insert_batch_with_caller( - batch, - InsertMode::Replace, - #[cfg(feature = "track_location")] - Location::caller(), - ); + self.insert_batch_with_caller(batch, InsertMode::Replace, MaybeLocation::caller()); } /// For a given batch of ([`Entity`], [`Bundle`]) pairs, @@ -2427,12 +2323,7 @@ impl World { I::IntoIter: Iterator, B: Bundle, { - self.insert_batch_with_caller( - batch, - InsertMode::Keep, - #[cfg(feature = "track_location")] - Location::caller(), - ); + self.insert_batch_with_caller(batch, InsertMode::Keep, MaybeLocation::caller()); } /// Split into a new function so we can differentiate the calling location. @@ -2445,7 +2336,7 @@ impl World { &mut self, batch: I, insert_mode: InsertMode, - #[cfg(feature = "track_location")] caller: &'static Location, + caller: MaybeLocation, ) where I: IntoIterator, I::IntoIter: Iterator, @@ -2485,7 +2376,6 @@ impl World { first_location, first_bundle, insert_mode, - #[cfg(feature = "track_location")] caller, ) }; @@ -2508,14 +2398,9 @@ impl World { } // SAFETY: `entity` is valid, `location` matches entity, bundle matches inserter unsafe { - cache.inserter.insert( - entity, - location, - bundle, - insert_mode, - #[cfg(feature = "track_location")] - caller, - ) + cache + .inserter + .insert(entity, location, bundle, insert_mode, caller) }; } else { panic!("error[B0003]: Could not insert a bundle (of type `{}`) for entity {entity}, which {}. See: https://bevyengine.org/learn/errors/b0003", core::any::type_name::(), self.entities.entity_does_not_exist_error_details(entity)); @@ -2547,12 +2432,7 @@ impl World { I::IntoIter: Iterator, B: Bundle, { - self.try_insert_batch_with_caller( - batch, - InsertMode::Replace, - #[cfg(feature = "track_location")] - Location::caller(), - ) + self.try_insert_batch_with_caller(batch, InsertMode::Replace, MaybeLocation::caller()) } /// For a given batch of ([`Entity`], [`Bundle`]) pairs, /// adds the `Bundle` of components to each `Entity` without overwriting. @@ -2574,12 +2454,7 @@ impl World { I::IntoIter: Iterator, B: Bundle, { - self.try_insert_batch_with_caller( - batch, - InsertMode::Keep, - #[cfg(feature = "track_location")] - Location::caller(), - ) + self.try_insert_batch_with_caller(batch, InsertMode::Keep, MaybeLocation::caller()) } /// Split into a new function so we can differentiate the calling location. @@ -2596,7 +2471,7 @@ impl World { &mut self, batch: I, insert_mode: InsertMode, - #[cfg(feature = "track_location")] caller: &'static Location, + caller: MaybeLocation, ) -> Result<(), TryInsertBatchError> where I: IntoIterator, @@ -2642,7 +2517,6 @@ impl World { first_location, first_bundle, insert_mode, - #[cfg(feature = "track_location")] caller, ) }; @@ -2674,14 +2548,9 @@ impl World { } // SAFETY: `entity` is valid, `location` matches entity, bundle matches inserter unsafe { - cache.inserter.insert( - entity, - location, - bundle, - insert_mode, - #[cfg(feature = "track_location")] - caller, - ) + cache + .inserter + .insert(entity, location, bundle, insert_mode, caller) }; } else { invalid_entities.push(entity); @@ -2745,7 +2614,7 @@ impl World { let change_tick = self.change_tick(); let component_id = self.components.get_resource_id(TypeId::of::())?; - let (ptr, mut ticks, mut _caller) = self + let (ptr, mut ticks, mut caller) = self .storages .resources .get_mut(component_id) @@ -2761,8 +2630,7 @@ impl World { last_run: last_change_tick, this_run: change_tick, }, - #[cfg(feature = "track_location")] - changed_by: &mut _caller, + changed_by: caller.as_mut(), }; let result = f(self, value_mut); assert!(!self.contains_resource::(), @@ -2774,12 +2642,7 @@ impl World { // SAFETY: pointer is of type R unsafe { self.storages.resources.get_mut(component_id).map(|info| { - info.insert_with_ticks( - ptr, - ticks, - #[cfg(feature = "track_location")] - _caller, - ); + info.insert_with_ticks(ptr, ticks, caller); }) } })?; @@ -2834,19 +2697,14 @@ impl World { &mut self, component_id: ComponentId, value: OwningPtr<'_>, - #[cfg(feature = "track_location")] caller: &'static Location, + caller: MaybeLocation, ) { let change_tick = self.change_tick(); let resource = self.initialize_resource_internal(component_id); // SAFETY: `value` is valid for `component_id`, ensured by caller unsafe { - resource.insert( - value, - change_tick, - #[cfg(feature = "track_location")] - caller, - ); + resource.insert(value, change_tick, caller); } } @@ -2868,19 +2726,14 @@ impl World { &mut self, component_id: ComponentId, value: OwningPtr<'_>, - #[cfg(feature = "track_location")] caller: &'static Location, + caller: MaybeLocation, ) { let change_tick = self.change_tick(); let resource = self.initialize_non_send_internal(component_id); // SAFETY: `value` is valid for `component_id`, ensured by caller unsafe { - resource.insert( - value, - change_tick, - #[cfg(feature = "track_location")] - caller, - ); + resource.insert(value, change_tick, caller); } } @@ -3410,7 +3263,7 @@ impl World { .get_info(component_id) .debug_checked_unwrap() }; - let (ptr, ticks, _caller) = data.get_with_ticks()?; + let (ptr, ticks, caller) = data.get_with_ticks()?; // SAFETY: // - We have exclusive access to the world, so no other code can be aliasing the `TickCells` @@ -3429,11 +3282,10 @@ impl World { // - We iterate one resource at a time, and we let go of each `PtrMut` before getting the next one value: unsafe { ptr.assert_unique() }, ticks, - #[cfg(feature = "track_location")] // SAFETY: // - We have exclusive access to the world, so no other code can be aliasing the `Ptr` // - We iterate one resource at a time, and we let go of each `PtrMut` before getting the next one - changed_by: unsafe { _caller.deref_mut() }, + changed_by: unsafe { caller.map(|caller| caller.deref_mut()) }, }; Some((component_info, mut_untyped)) @@ -3744,7 +3596,7 @@ impl FromWorld for T { mod tests { use super::{FromWorld, World}; use crate::{ - change_detection::DetectChangesMut, + change_detection::{DetectChangesMut, MaybeLocation}, component::{ComponentCloneBehavior, ComponentDescriptor, ComponentInfo, StorageType}, entity::hash_set::EntityHashSet, entity_disabling::{DefaultQueryFilters, Disabled}, @@ -4007,12 +3859,7 @@ mod tests { OwningPtr::make(value, |ptr| { // SAFETY: value is valid for the layout of `TestResource` unsafe { - world.insert_resource_by_id( - component_id, - ptr, - #[cfg(feature = "track_location")] - panic::Location::caller(), - ); + world.insert_resource_by_id(component_id, ptr, MaybeLocation::caller()); } }); @@ -4056,12 +3903,7 @@ mod tests { OwningPtr::make(value, |ptr| { // SAFETY: value is valid for the component layout unsafe { - world.insert_resource_by_id( - component_id, - ptr, - #[cfg(feature = "track_location")] - panic::Location::caller(), - ); + world.insert_resource_by_id(component_id, ptr, MaybeLocation::caller()); } }); @@ -4402,7 +4244,6 @@ mod tests { Err(EntityFetchError::NoSuchEntity(e, ..)) if e == e1)); } - #[cfg(feature = "track_location")] #[test] #[track_caller] fn entity_spawn_despawn_tracking() { @@ -4412,23 +4253,23 @@ mod tests { let entity = world.spawn_empty().id(); assert_eq!( world.entities.entity_get_spawned_or_despawned_by(entity), - Some(Location::caller()) + MaybeLocation::new(Some(Location::caller())) ); world.despawn(entity); assert_eq!( world.entities.entity_get_spawned_or_despawned_by(entity), - Some(Location::caller()) + MaybeLocation::new(Some(Location::caller())) ); let new = world.spawn_empty().id(); assert_eq!(entity.index(), new.index()); assert_eq!( world.entities.entity_get_spawned_or_despawned_by(entity), - None + MaybeLocation::new(None) ); world.despawn(new); assert_eq!( world.entities.entity_get_spawned_or_despawned_by(entity), - None + MaybeLocation::new(None) ); } diff --git a/crates/bevy_ecs/src/world/spawn_batch.rs b/crates/bevy_ecs/src/world/spawn_batch.rs index cbeaf8f4ad..16bd9bb805 100644 --- a/crates/bevy_ecs/src/world/spawn_batch.rs +++ b/crates/bevy_ecs/src/world/spawn_batch.rs @@ -1,11 +1,10 @@ use crate::{ bundle::{Bundle, BundleSpawner, NoBundleEffect}, + change_detection::MaybeLocation, entity::{Entity, EntitySetIterator}, world::World, }; use core::iter::FusedIterator; -#[cfg(feature = "track_location")] -use core::panic::Location; /// An iterator that spawns a series of entities and returns the [ID](Entity) of /// each spawned entity. @@ -18,8 +17,7 @@ where { inner: I, spawner: BundleSpawner<'w>, - #[cfg(feature = "track_location")] - caller: &'static Location<'static>, + caller: MaybeLocation, } impl<'w, I> SpawnBatchIter<'w, I> @@ -29,11 +27,7 @@ where { #[inline] #[track_caller] - pub(crate) fn new( - world: &'w mut World, - iter: I, - #[cfg(feature = "track_location")] caller: &'static Location, - ) -> Self { + pub(crate) fn new(world: &'w mut World, iter: I, caller: MaybeLocation) -> Self { // Ensure all entity allocations are accounted for so `self.entities` can realloc if // necessary world.flush(); @@ -50,7 +44,6 @@ where Self { inner: iter, spawner, - #[cfg(feature = "track_location")] caller, } } @@ -80,17 +73,7 @@ where fn next(&mut self) -> Option { let bundle = self.inner.next()?; // SAFETY: bundle matches spawner type - unsafe { - Some( - self.spawner - .spawn( - bundle, - #[cfg(feature = "track_location")] - self.caller, - ) - .0, - ) - } + unsafe { Some(self.spawner.spawn(bundle, self.caller).0) } } fn size_hint(&self) -> (usize, Option) { diff --git a/crates/bevy_ecs/src/world/unsafe_world_cell.rs b/crates/bevy_ecs/src/world/unsafe_world_cell.rs index 5ad2a3504e..570d33eeb2 100644 --- a/crates/bevy_ecs/src/world/unsafe_world_cell.rs +++ b/crates/bevy_ecs/src/world/unsafe_world_cell.rs @@ -4,7 +4,7 @@ use super::{Mut, Ref, World, WorldId}; use crate::{ archetype::{Archetype, Archetypes}, bundle::Bundles, - change_detection::{MaybeUnsafeCellLocation, MutUntyped, Ticks, TicksMut}, + change_detection::{MaybeLocation, MutUntyped, Ticks, TicksMut}, component::{ComponentId, ComponentTicks, Components, Mutable, StorageType, Tick, TickCells}, entity::{Entities, Entity, EntityBorrow, EntityLocation}, observer::Observers, @@ -16,13 +16,10 @@ use crate::{ world::RawCommandQueue, }; use bevy_platform_support::sync::atomic::Ordering; -use bevy_ptr::Ptr; -use core::{any::TypeId, cell::UnsafeCell, fmt::Debug, marker::PhantomData, ptr}; +use bevy_ptr::{Ptr, UnsafeCellDeref}; +use core::{any::TypeId, cell::UnsafeCell, fmt::Debug, marker::PhantomData, panic::Location, ptr}; use thiserror::Error; -#[cfg(feature = "track_location")] -use {bevy_ptr::UnsafeCellDeref, core::panic::Location}; - /// Variant of the [`World`] where resource and component accesses take `&self`, and the responsibility to avoid /// aliasing violations are given to the caller instead of being checked at compile-time by rust's unique XOR shared rule. /// @@ -389,7 +386,7 @@ impl<'w> UnsafeWorldCell<'w> { // SAFETY: caller ensures `self` has permission to access the resource // caller also ensure that no mutable reference to the resource exists - let (ptr, ticks, _caller) = unsafe { self.get_resource_with_ticks(component_id)? }; + let (ptr, ticks, caller) = unsafe { self.get_resource_with_ticks(component_id)? }; // SAFETY: `component_id` was obtained from the type ID of `R` let value = unsafe { ptr.deref::() }; @@ -399,13 +396,11 @@ impl<'w> UnsafeWorldCell<'w> { unsafe { Ticks::from_tick_cells(ticks, self.last_change_tick(), self.change_tick()) }; // SAFETY: caller ensures that no mutable reference to the resource exists - #[cfg(feature = "track_location")] - let caller = unsafe { _caller.deref() }; + let caller = caller.map(|caller| unsafe { caller.deref() }); Some(Ref { value, ticks, - #[cfg(feature = "track_location")] changed_by: caller, }) } @@ -512,7 +507,7 @@ impl<'w> UnsafeWorldCell<'w> { self.assert_allows_mutable_access(); // SAFETY: we only access data that the caller has ensured is unaliased and `self` // has permission to access. - let (ptr, ticks, _caller) = unsafe { self.storages() } + let (ptr, ticks, caller) = unsafe { self.storages() } .resources .get(component_id)? .get_with_ticks()?; @@ -530,11 +525,10 @@ impl<'w> UnsafeWorldCell<'w> { // - caller ensures that the resource is unaliased value: unsafe { ptr.assert_unique() }, ticks, - #[cfg(feature = "track_location")] // SAFETY: // - caller ensures that `self` has permission to access the resource // - caller ensures that the resource is unaliased - changed_by: unsafe { _caller.deref_mut() }, + changed_by: unsafe { caller.map(|caller| caller.deref_mut()) }, }) } @@ -581,7 +575,7 @@ impl<'w> UnsafeWorldCell<'w> { let change_tick = self.change_tick(); // SAFETY: we only access data that the caller has ensured is unaliased and `self` // has permission to access. - let (ptr, ticks, _caller) = unsafe { self.storages() } + let (ptr, ticks, caller) = unsafe { self.storages() } .non_send_resources .get(component_id)? .get_with_ticks()?; @@ -596,9 +590,8 @@ impl<'w> UnsafeWorldCell<'w> { // SAFETY: This function has exclusive access to the world so nothing aliases `ptr`. value: unsafe { ptr.assert_unique() }, ticks, - #[cfg(feature = "track_location")] // SAFETY: This function has exclusive access to the world - changed_by: unsafe { _caller.deref_mut() }, + changed_by: unsafe { caller.map(|caller| caller.deref_mut()) }, }) } @@ -611,7 +604,11 @@ impl<'w> UnsafeWorldCell<'w> { pub(crate) unsafe fn get_resource_with_ticks( self, component_id: ComponentId, - ) -> Option<(Ptr<'w>, TickCells<'w>, MaybeUnsafeCellLocation<'w>)> { + ) -> Option<( + Ptr<'w>, + TickCells<'w>, + MaybeLocation<&'w UnsafeCell<&'static Location<'static>>>, + )> { // SAFETY: // - caller ensures there is no `&mut World` // - caller ensures there are no mutable borrows of this resource @@ -634,7 +631,11 @@ impl<'w> UnsafeWorldCell<'w> { pub(crate) unsafe fn get_non_send_with_ticks( self, component_id: ComponentId, - ) -> Option<(Ptr<'w>, TickCells<'w>, MaybeUnsafeCellLocation<'w>)> { + ) -> Option<( + Ptr<'w>, + TickCells<'w>, + MaybeLocation<&'w UnsafeCell<&'static Location<'static>>>, + )> { // SAFETY: // - caller ensures there is no `&mut World` // - caller ensures there are no mutable borrows of this resource @@ -808,12 +809,11 @@ impl<'w> UnsafeEntityCell<'w> { self.entity, self.location, ) - .map(|(value, cells, _caller)| Ref { + .map(|(value, cells, caller)| Ref { // SAFETY: returned component is of type T value: value.deref::(), ticks: Ticks::from_tick_cells(cells, last_change_tick, change_tick), - #[cfg(feature = "track_location")] - changed_by: _caller.deref(), + changed_by: caller.map(|caller| caller.deref()), }) } } @@ -930,12 +930,11 @@ impl<'w> UnsafeEntityCell<'w> { self.entity, self.location, ) - .map(|(value, cells, _caller)| Mut { + .map(|(value, cells, caller)| Mut { // SAFETY: returned component is of type T value: value.assert_unique().deref_mut::(), ticks: TicksMut::from_tick_cells(cells, last_change_tick, change_tick), - #[cfg(feature = "track_location")] - changed_by: _caller.deref_mut(), + changed_by: caller.map(|caller| caller.deref_mut()), }) } } @@ -1054,7 +1053,7 @@ impl<'w> UnsafeEntityCell<'w> { self.entity, self.location, ) - .map(|(value, cells, _caller)| MutUntyped { + .map(|(value, cells, caller)| MutUntyped { // SAFETY: world access validated by caller and ties world lifetime to `MutUntyped` lifetime value: value.assert_unique(), ticks: TicksMut::from_tick_cells( @@ -1062,20 +1061,18 @@ impl<'w> UnsafeEntityCell<'w> { self.world.last_change_tick(), self.world.change_tick(), ), - #[cfg(feature = "track_location")] - changed_by: _caller.deref_mut(), + changed_by: caller.map(|caller| caller.deref_mut()), }) .ok_or(GetEntityMutByIdError::ComponentNotFound) } } /// Returns the source code location from which this entity has been spawned. - #[cfg(feature = "track_location")] - pub fn spawned_by(self) -> &'static Location<'static> { + pub fn spawned_by(self) -> MaybeLocation { self.world() .entities() .entity_get_spawned_or_despawned_by(self.entity) - .unwrap() + .map(|o| o.unwrap()) } } @@ -1159,7 +1156,11 @@ unsafe fn get_component_and_ticks( storage_type: StorageType, entity: Entity, location: EntityLocation, -) -> Option<(Ptr<'_>, TickCells<'_>, MaybeUnsafeCellLocation<'_>)> { +) -> Option<( + Ptr<'_>, + TickCells<'_>, + MaybeLocation<&UnsafeCell<&'static Location<'static>>>, +)> { match storage_type { StorageType::Table => { let table = world.fetch_table(location)?; @@ -1175,12 +1176,9 @@ unsafe fn get_component_and_ticks( .get_changed_tick(component_id, location.table_row) .debug_checked_unwrap(), }, - #[cfg(feature = "track_location")] table .get_changed_by(component_id, location.table_row) - .debug_checked_unwrap(), - #[cfg(not(feature = "track_location"))] - (), + .map(|changed_by| changed_by.debug_checked_unwrap()), )) } StorageType::SparseSet => world.fetch_sparse_set(component_id)?.get_with_ticks(entity),