Skip to content

Commit eee7fd5

Browse files
authored
Encapsulate cfg(feature = "track_location") in a type. (#17602)
# Objective Eliminate the need to write `cfg(feature = "track_location")` every time one uses an API that may use location tracking. It's verbose, and a little intimidating. And it requires code outside of `bevy_ecs` that wants to use location tracking needs to either unconditionally enable the feature, or include conditional compilation of its own. It would be good for users to be able to log locations when they are available without needing to add feature flags to their own crates. Reduce the number of cases where code compiles with the `track_location` feature enabled, but not with it disabled, or vice versa. It can be hard to remember to test it both ways! Remove the need to store a `None` in `HookContext` when the `track_location` feature is disabled. ## Solution Create an `MaybeLocation<T>` type that contains a `T` if the `track_location` feature is enabled, and is a ZST if it is not. 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. Default `T` to `&'static Location<'static>`, since that is the most common case. Remove all `cfg(feature = "track_location")` blocks outside of the implementation of that type, and instead call methods on it. When `track_location` is disabled, `MaybeLocation` is a ZST and all methods are `#[inline]` and empty, so they should be entirely removed by the compiler. But the code will still be visible to the compiler and checked, so if it compiles with the feature disabled then it should also compile with it enabled, and vice versa. ## Open Questions Where should these types live? I put them in `change_detection` because that's where the existing `MaybeLocation` types were, but we now use these outside of change detection. While I believe that the compiler should be able to remove all of these calls, I have not actually tested anything. If we want to take this approach, what testing is required to ensure it doesn't impact performance? ## Migration Guide Methods like `Ref::changed_by()` that return a `&'static Location<'static>` will now be available even when the `track_location` feature is disabled, but they will return a new `MaybeLocation` type. `MaybeLocation` wraps a `&'static Location<'static>` when the feature is enabled, and is a ZST when the feature is disabled. Existing code that needs a `&Location` can call `into_option().unwrap()` to recover it. Many trait impls are forwarded, so if you only need `Display` then no changes will be necessary. If that code was conditionally compiled, you may instead want to use the methods on `MaybeLocation` to remove the need for conditional compilation. Code that constructs a `Ref`, `Mut`, `Res`, or `ResMut` will now need to provide location information unconditionally. If you are creating them from existing Bevy types, you can obtain a `MaybeLocation` from methods like `Table::get_changed_by_slice_for()` or `ComponentSparseSet::get_with_ticks`. Otherwise, you will need to store a `MaybeLocation` next to your data and use methods like `as_ref()` or `as_mut()` to obtain wrapped references.
1 parent 4ecbe00 commit eee7fd5

22 files changed

+801
-1214
lines changed

crates/bevy_ecs/src/bundle.rs

Lines changed: 16 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use crate::{
99
Archetype, ArchetypeAfterBundleInsert, ArchetypeId, Archetypes, BundleComponentStatus,
1010
ComponentStatus, SpawnBundleStatus,
1111
},
12+
change_detection::MaybeLocation,
1213
component::{
1314
Component, ComponentId, Components, RequiredComponentConstructor, RequiredComponents,
1415
StorageType, Tick,
@@ -24,8 +25,6 @@ use alloc::{boxed::Box, vec, vec::Vec};
2425
use bevy_platform_support::collections::{HashMap, HashSet};
2526
use bevy_ptr::{ConstNonNull, OwningPtr};
2627
use bevy_utils::TypeIdMap;
27-
#[cfg(feature = "track_location")]
28-
use core::panic::Location;
2928
use core::{any::TypeId, ptr::NonNull};
3029
use variadics_please::all_tuples;
3130

@@ -623,7 +622,7 @@ impl BundleInfo {
623622
change_tick: Tick,
624623
bundle: T,
625624
insert_mode: InsertMode,
626-
#[cfg(feature = "track_location")] caller: &'static Location<'static>,
625+
caller: MaybeLocation,
627626
) -> T::Effect {
628627
// NOTE: get_components calls this closure on each component in "bundle order".
629628
// bundle_info.component_ids are also in "bundle order"
@@ -638,20 +637,12 @@ impl BundleInfo {
638637
// the target table contains the component.
639638
let column = table.get_column_mut(component_id).debug_checked_unwrap();
640639
match (status, insert_mode) {
641-
(ComponentStatus::Added, _) => column.initialize(
642-
table_row,
643-
component_ptr,
644-
change_tick,
645-
#[cfg(feature = "track_location")]
646-
caller,
647-
),
648-
(ComponentStatus::Existing, InsertMode::Replace) => column.replace(
649-
table_row,
650-
component_ptr,
651-
change_tick,
652-
#[cfg(feature = "track_location")]
653-
caller,
654-
),
640+
(ComponentStatus::Added, _) => {
641+
column.initialize(table_row, component_ptr, change_tick, caller);
642+
}
643+
(ComponentStatus::Existing, InsertMode::Replace) => {
644+
column.replace(table_row, component_ptr, change_tick, caller);
645+
}
655646
(ComponentStatus::Existing, InsertMode::Keep) => {
656647
if let Some(drop_fn) = table.get_drop_for(component_id) {
657648
drop_fn(component_ptr);
@@ -664,13 +655,7 @@ impl BundleInfo {
664655
// SAFETY: If component_id is in self.component_ids, BundleInfo::new ensures that
665656
// a sparse set exists for the component.
666657
unsafe { sparse_sets.get_mut(component_id).debug_checked_unwrap() };
667-
sparse_set.insert(
668-
entity,
669-
component_ptr,
670-
change_tick,
671-
#[cfg(feature = "track_location")]
672-
caller,
673-
);
658+
sparse_set.insert(entity, component_ptr, change_tick, caller);
674659
}
675660
}
676661
bundle_component += 1;
@@ -683,7 +668,6 @@ impl BundleInfo {
683668
change_tick,
684669
table_row,
685670
entity,
686-
#[cfg(feature = "track_location")]
687671
caller,
688672
);
689673
}
@@ -712,7 +696,7 @@ impl BundleInfo {
712696
component_id: ComponentId,
713697
storage_type: StorageType,
714698
component_ptr: OwningPtr,
715-
#[cfg(feature = "track_location")] caller: &'static Location<'static>,
699+
caller: MaybeLocation,
716700
) {
717701
{
718702
match storage_type {
@@ -721,26 +705,14 @@ impl BundleInfo {
721705
// SAFETY: If component_id is in required_components, BundleInfo::new requires that
722706
// the target table contains the component.
723707
unsafe { table.get_column_mut(component_id).debug_checked_unwrap() };
724-
column.initialize(
725-
table_row,
726-
component_ptr,
727-
change_tick,
728-
#[cfg(feature = "track_location")]
729-
caller,
730-
);
708+
column.initialize(table_row, component_ptr, change_tick, caller);
731709
}
732710
StorageType::SparseSet => {
733711
let sparse_set =
734712
// SAFETY: If component_id is in required_components, BundleInfo::new requires that
735713
// a sparse set exists for the component.
736714
unsafe { sparse_sets.get_mut(component_id).debug_checked_unwrap() };
737-
sparse_set.insert(
738-
entity,
739-
component_ptr,
740-
change_tick,
741-
#[cfg(feature = "track_location")]
742-
caller,
743-
);
715+
sparse_set.insert(entity, component_ptr, change_tick, caller);
744716
}
745717
}
746718
}
@@ -1127,7 +1099,7 @@ impl<'w> BundleInserter<'w> {
11271099
location: EntityLocation,
11281100
bundle: T,
11291101
insert_mode: InsertMode,
1130-
#[cfg(feature = "track_location")] caller: &'static Location<'static>,
1102+
caller: MaybeLocation,
11311103
) -> (EntityLocation, T::Effect) {
11321104
let bundle_info = self.bundle_info.as_ref();
11331105
let archetype_after_insert = self.archetype_after_insert.as_ref();
@@ -1145,15 +1117,13 @@ impl<'w> BundleInserter<'w> {
11451117
ON_REPLACE,
11461118
entity,
11471119
archetype_after_insert.iter_existing(),
1148-
#[cfg(feature = "track_location")]
11491120
caller,
11501121
);
11511122
}
11521123
deferred_world.trigger_on_replace(
11531124
archetype,
11541125
entity,
11551126
archetype_after_insert.iter_existing(),
1156-
#[cfg(feature = "track_location")]
11571127
caller,
11581128
);
11591129
}
@@ -1183,7 +1153,6 @@ impl<'w> BundleInserter<'w> {
11831153
self.change_tick,
11841154
bundle,
11851155
insert_mode,
1186-
#[cfg(feature = "track_location")]
11871156
caller,
11881157
);
11891158

@@ -1225,7 +1194,6 @@ impl<'w> BundleInserter<'w> {
12251194
self.change_tick,
12261195
bundle,
12271196
insert_mode,
1228-
#[cfg(feature = "track_location")]
12291197
caller,
12301198
);
12311199

@@ -1308,7 +1276,6 @@ impl<'w> BundleInserter<'w> {
13081276
self.change_tick,
13091277
bundle,
13101278
insert_mode,
1311-
#[cfg(feature = "track_location")]
13121279
caller,
13131280
);
13141281

@@ -1327,15 +1294,13 @@ impl<'w> BundleInserter<'w> {
13271294
new_archetype,
13281295
entity,
13291296
archetype_after_insert.iter_added(),
1330-
#[cfg(feature = "track_location")]
13311297
caller,
13321298
);
13331299
if new_archetype.has_add_observer() {
13341300
deferred_world.trigger_observers(
13351301
ON_ADD,
13361302
entity,
13371303
archetype_after_insert.iter_added(),
1338-
#[cfg(feature = "track_location")]
13391304
caller,
13401305
);
13411306
}
@@ -1346,15 +1311,13 @@ impl<'w> BundleInserter<'w> {
13461311
new_archetype,
13471312
entity,
13481313
archetype_after_insert.iter_inserted(),
1349-
#[cfg(feature = "track_location")]
13501314
caller,
13511315
);
13521316
if new_archetype.has_insert_observer() {
13531317
deferred_world.trigger_observers(
13541318
ON_INSERT,
13551319
entity,
13561320
archetype_after_insert.iter_inserted(),
1357-
#[cfg(feature = "track_location")]
13581321
caller,
13591322
);
13601323
}
@@ -1366,15 +1329,13 @@ impl<'w> BundleInserter<'w> {
13661329
new_archetype,
13671330
entity,
13681331
archetype_after_insert.iter_added(),
1369-
#[cfg(feature = "track_location")]
13701332
caller,
13711333
);
13721334
if new_archetype.has_insert_observer() {
13731335
deferred_world.trigger_observers(
13741336
ON_INSERT,
13751337
entity,
13761338
archetype_after_insert.iter_added(),
1377-
#[cfg(feature = "track_location")]
13781339
caller,
13791340
);
13801341
}
@@ -1456,7 +1417,7 @@ impl<'w> BundleSpawner<'w> {
14561417
&mut self,
14571418
entity: Entity,
14581419
bundle: T,
1459-
#[cfg(feature = "track_location")] caller: &'static Location<'static>,
1420+
caller: MaybeLocation,
14601421
) -> (EntityLocation, T::Effect) {
14611422
// SAFETY: We do not make any structural changes to the archetype graph through self.world so these pointers always remain valid
14621423
let bundle_info = self.bundle_info.as_ref();
@@ -1481,7 +1442,6 @@ impl<'w> BundleSpawner<'w> {
14811442
self.change_tick,
14821443
bundle,
14831444
InsertMode::Replace,
1484-
#[cfg(feature = "track_location")]
14851445
caller,
14861446
);
14871447
entities.set(entity.index(), location);
@@ -1499,31 +1459,27 @@ impl<'w> BundleSpawner<'w> {
14991459
archetype,
15001460
entity,
15011461
bundle_info.iter_contributed_components(),
1502-
#[cfg(feature = "track_location")]
15031462
caller,
15041463
);
15051464
if archetype.has_add_observer() {
15061465
deferred_world.trigger_observers(
15071466
ON_ADD,
15081467
entity,
15091468
bundle_info.iter_contributed_components(),
1510-
#[cfg(feature = "track_location")]
15111469
caller,
15121470
);
15131471
}
15141472
deferred_world.trigger_on_insert(
15151473
archetype,
15161474
entity,
15171475
bundle_info.iter_contributed_components(),
1518-
#[cfg(feature = "track_location")]
15191476
caller,
15201477
);
15211478
if archetype.has_insert_observer() {
15221479
deferred_world.trigger_observers(
15231480
ON_INSERT,
15241481
entity,
15251482
bundle_info.iter_contributed_components(),
1526-
#[cfg(feature = "track_location")]
15271483
caller,
15281484
);
15291485
}
@@ -1538,18 +1494,11 @@ impl<'w> BundleSpawner<'w> {
15381494
pub unsafe fn spawn<T: Bundle>(
15391495
&mut self,
15401496
bundle: T,
1541-
#[cfg(feature = "track_location")] caller: &'static Location<'static>,
1497+
caller: MaybeLocation,
15421498
) -> (Entity, T::Effect) {
15431499
let entity = self.entities().alloc();
15441500
// SAFETY: entity is allocated (but non-existent), `T` matches this BundleInfo's type
1545-
let (_, after_effect) = unsafe {
1546-
self.spawn_non_existent(
1547-
entity,
1548-
bundle,
1549-
#[cfg(feature = "track_location")]
1550-
caller,
1551-
)
1552-
};
1501+
let (_, after_effect) = unsafe { self.spawn_non_existent(entity, bundle, caller) };
15531502
(entity, after_effect)
15541503
}
15551504

0 commit comments

Comments
 (0)