From ea17503e2985f73fa348c849ce564e2615b99a40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Mockers?= Date: Mon, 9 Jun 2025 00:18:37 +0200 Subject: [PATCH 01/17] feature some string usages --- Cargo.toml | 6 +- crates/bevy_ecs/Cargo.toml | 2 + crates/bevy_ecs/src/bundle.rs | 25 +++-- crates/bevy_ecs/src/component.rs | 32 ++++++- crates/bevy_ecs/src/error/command_handling.rs | 10 +- crates/bevy_ecs/src/error/handler.rs | 20 ++++ crates/bevy_ecs/src/observer/runner.rs | 15 +++ crates/bevy_ecs/src/query/access.rs | 8 +- crates/bevy_ecs/src/schedule/condition.rs | 65 +++++++++++-- crates/bevy_ecs/src/schedule/config.rs | 6 ++ crates/bevy_ecs/src/schedule/executor/mod.rs | 5 +- .../src/schedule/executor/multi_threaded.rs | 19 +++- .../bevy_ecs/src/schedule/executor/simple.rs | 12 ++- .../src/schedule/executor/single_threaded.rs | 12 ++- crates/bevy_ecs/src/schedule/mod.rs | 7 +- crates/bevy_ecs/src/schedule/schedule.rs | 72 +++++++++++++-- crates/bevy_ecs/src/schedule/set.rs | 7 +- crates/bevy_ecs/src/schedule/stepping.rs | 2 +- crates/bevy_ecs/src/storage/resource.rs | 16 ++++ crates/bevy_ecs/src/system/adapter_system.rs | 27 +++++- crates/bevy_ecs/src/system/builder.rs | 22 +++-- crates/bevy_ecs/src/system/combinator.rs | 29 +++++- .../src/system/commands/entity_command.rs | 11 ++- .../src/system/exclusive_function_system.rs | 14 ++- crates/bevy_ecs/src/system/function_system.rs | 20 +++- crates/bevy_ecs/src/system/mod.rs | 2 + crates/bevy_ecs/src/system/observer_system.rs | 5 +- crates/bevy_ecs/src/system/schedule_system.rs | 7 +- crates/bevy_ecs/src/system/system.rs | 23 ++++- crates/bevy_ecs/src/system/system_param.rs | 91 ++++++++++++++++--- crates/bevy_ecs/src/world/mod.rs | 21 +++-- crates/bevy_ecs/src/world/reflect.rs | 14 ++- crates/bevy_internal/Cargo.toml | 2 + crates/bevy_remote/Cargo.toml | 1 + crates/bevy_scene/Cargo.toml | 2 + crates/bevy_scene/src/scene.rs | 23 +++-- crates/bevy_scene/src/scene_spawner.rs | 7 ++ docs/cargo_features.md | 1 + examples/games/stepping.rs | 13 ++- 39 files changed, 567 insertions(+), 109 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 000bebe13496d..c702feacb7098 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -494,7 +494,7 @@ file_watcher = ["bevy_internal/file_watcher"] embedded_watcher = ["bevy_internal/embedded_watcher"] # Enable stepping-based debugging of Bevy systems -bevy_debug_stepping = ["bevy_internal/bevy_debug_stepping"] +bevy_debug_stepping = ["bevy_internal/bevy_debug_stepping", "bevy_internal/debug"] # Enables the meshlet renderer for dense high-poly scenes (experimental) meshlet = ["bevy_internal/meshlet"] @@ -538,6 +538,9 @@ web = ["bevy_internal/web"] # Enable hotpatching of Bevy systems hotpatching = ["bevy_internal/hotpatching"] +# Enable collecting debug information about systems and components to help with diagnostics +debug = ["bevy_internal/debug"] + [dependencies] bevy_internal = { path = "crates/bevy_internal", version = "0.16.0-dev", default-features = false } tracing = { version = "0.1", default-features = false, optional = true } @@ -2073,6 +2076,7 @@ wasm = false name = "dynamic" path = "examples/ecs/dynamic.rs" doc-scrape-examples = true +required-features = ["debug"] [package.metadata.example.dynamic] name = "Dynamic ECS" diff --git a/crates/bevy_ecs/Cargo.toml b/crates/bevy_ecs/Cargo.toml index 27498f58bc25c..debe2c4133218 100644 --- a/crates/bevy_ecs/Cargo.toml +++ b/crates/bevy_ecs/Cargo.toml @@ -85,6 +85,8 @@ critical-section = [ hotpatching = ["dep:subsecond"] +debug = [] + [dependencies] bevy_ptr = { path = "../bevy_ptr", version = "0.16.0-dev" } bevy_reflect = { path = "../bevy_reflect", version = "0.16.0-dev", features = [ diff --git a/crates/bevy_ecs/src/bundle.rs b/crates/bevy_ecs/src/bundle.rs index f0641ff80c859..6d4d5623dbd41 100644 --- a/crates/bevy_ecs/src/bundle.rs +++ b/crates/bevy_ecs/src/bundle.rs @@ -495,16 +495,21 @@ impl BundleInfo { } } - let names = dups - .into_iter() - .map(|id| { - // SAFETY: the caller ensures component_id is valid. - unsafe { components.get_info_unchecked(id).name() } - }) - .collect::>() - .join(", "); - - panic!("Bundle {bundle_type_name} has duplicate components: {names}"); + #[cfg(feature = "debug")] + { + let names = dups + .into_iter() + .map(|id| { + // SAFETY: the caller ensures component_id is valid. + unsafe { components.get_info_unchecked(id).name() } + }) + .collect::>() + .join(", "); + + panic!("Bundle {bundle_type_name} has duplicate components: {names}"); + } + #[cfg(not(feature = "debug"))] + panic!("Bundle {bundle_type_name} has duplicate components"); } // handle explicit components diff --git a/crates/bevy_ecs/src/component.rs b/crates/bevy_ecs/src/component.rs index d083901ccc129..3802874f380a6 100644 --- a/crates/bevy_ecs/src/component.rs +++ b/crates/bevy_ecs/src/component.rs @@ -13,7 +13,9 @@ use crate::{ world::{DeferredWorld, FromWorld, World}, }; use alloc::boxed::Box; -use alloc::{borrow::Cow, format, vec::Vec}; +#[cfg(feature = "debug")] +use alloc::format; +use alloc::{borrow::Cow, vec::Vec}; pub use bevy_ecs_macros::Component; use bevy_platform::sync::Arc; use bevy_platform::{ @@ -33,6 +35,7 @@ use core::{ mem::needs_drop, ops::{Deref, DerefMut}, }; +#[cfg(feature = "debug")] use disqualified::ShortName; use smallvec::SmallVec; use thiserror::Error; @@ -895,7 +898,8 @@ impl ComponentHooks { } /// Stores metadata for a type of component or resource stored in a specific [`World`]. -#[derive(Debug, Clone)] +#[derive(Clone)] +#[cfg_attr(feature = "debug", derive(Debug))] pub struct ComponentInfo { id: ComponentId, descriptor: ComponentDescriptor, @@ -912,6 +916,7 @@ impl ComponentInfo { } /// Returns the name of the current component. + #[cfg(feature = "debug")] #[inline] pub fn name(&self) -> &str { &self.descriptor.name @@ -1071,6 +1076,7 @@ impl SparseSetIndex for ComponentId { /// A value describing a component or resource, which may or may not correspond to a Rust type. #[derive(Clone)] pub struct ComponentDescriptor { + #[cfg(feature = "debug")] name: Cow<'static, str>, // SAFETY: This must remain private. It must match the statically known StorageType of the // associated rust component type if one exists. @@ -1089,6 +1095,7 @@ pub struct ComponentDescriptor { } // We need to ignore the `drop` field in our `Debug` impl +#[cfg(feature = "debug")] impl Debug for ComponentDescriptor { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { f.debug_struct("ComponentDescriptor") @@ -1117,6 +1124,7 @@ impl ComponentDescriptor { /// Create a new `ComponentDescriptor` for the type `T`. pub fn new() -> Self { Self { + #[cfg(feature = "debug")] name: Cow::Borrowed(core::any::type_name::()), storage_type: T::STORAGE_TYPE, is_send_and_sync: true, @@ -1133,6 +1141,7 @@ impl ComponentDescriptor { /// # Safety /// - the `drop` fn must be usable on a pointer with a value of the layout `layout` /// - the component type must be safe to access from any thread (Send + Sync in rust terms) + #[cfg_attr(not(feature = "debug"), expect(unused_variables))] pub unsafe fn new_with_layout( name: impl Into>, storage_type: StorageType, @@ -1142,6 +1151,7 @@ impl ComponentDescriptor { clone_behavior: ComponentCloneBehavior, ) -> Self { Self { + #[cfg(feature = "debug")] name: name.into(), storage_type, is_send_and_sync: true, @@ -1158,6 +1168,7 @@ impl ComponentDescriptor { /// The [`StorageType`] for resources is always [`StorageType::Table`]. pub fn new_resource() -> Self { Self { + #[cfg(feature = "debug")] name: Cow::Borrowed(core::any::type_name::()), // PERF: `SparseStorage` may actually be a more // reasonable choice as `storage_type` for resources. @@ -1173,6 +1184,7 @@ impl ComponentDescriptor { fn new_non_send(storage_type: StorageType) -> Self { Self { + #[cfg(feature = "debug")] name: Cow::Borrowed(core::any::type_name::()), storage_type, is_send_and_sync: false, @@ -1199,6 +1211,7 @@ impl ComponentDescriptor { /// Returns the name of the current component. #[inline] + #[cfg(feature = "debug")] pub fn name(&self) -> &str { self.name.as_ref() } @@ -1965,7 +1978,8 @@ impl<'w> ComponentsRegistrator<'w> { } /// Stores metadata associated with each kind of [`Component`] in a given [`World`]. -#[derive(Debug, Default)] +#[derive(Default)] +#[cfg_attr(feature = "debug", derive(Debug))] pub struct Components { components: Vec>, indices: TypeIdMap, @@ -2089,6 +2103,7 @@ impl Components { /// /// This will return an incorrect result if `id` did not come from the same world as `self`. It may return `None` or a garbage value. #[inline] + #[cfg(feature = "debug")] pub fn get_name<'a>(&'a self, id: ComponentId) -> Option> { self.components .get(id.0) @@ -2999,6 +3014,7 @@ impl RequiredComponents { // This exists as a standalone function instead of being inlined into the component derive macro so as // to reduce the amount of generated code. #[doc(hidden)] +#[cfg_attr(not(feature = "debug"), expect(unused_variables))] pub fn enforce_no_required_components_recursion( components: &Components, recursion_check_stack: &[ComponentId], @@ -3009,6 +3025,7 @@ pub fn enforce_no_required_components_recursion( .position(|&id| id == requiree) .map(|index| index == check.len() - 1) { + #[cfg(feature = "debug")] panic!( "Recursive required components detected: {}\nhelp: {}", recursion_check_stack @@ -3025,6 +3042,15 @@ pub fn enforce_no_required_components_recursion( "If this is intentional, consider merging the components.".into() } ); + #[cfg(not(feature = "debug"))] + panic!( + "Recursive required components detected\n help: {}\nEnable the default feature to get more details", + if direct_recursion { + "Remove the recursing required component." + } else { + "If this is intentional, consider merging the components." + } + ); } } } diff --git a/crates/bevy_ecs/src/error/command_handling.rs b/crates/bevy_ecs/src/error/command_handling.rs index bf2741d3766e5..13dd6cd0622d6 100644 --- a/crates/bevy_ecs/src/error/command_handling.rs +++ b/crates/bevy_ecs/src/error/command_handling.rs @@ -1,4 +1,6 @@ -use core::{any::type_name, fmt}; +#[cfg(feature = "debug")] +use core::any::type_name; +use core::fmt; use crate::{ entity::Entity, @@ -30,9 +32,12 @@ where Ok(_) => {} Err(err) => (error_handler)( err.into(), + #[cfg(feature = "debug")] ErrorContext::Command { name: type_name::().into(), }, + #[cfg(not(feature = "debug"))] + ErrorContext::Anonymous, ), } } @@ -42,9 +47,12 @@ where Ok(_) => {} Err(err) => world.default_error_handler()( err.into(), + #[cfg(feature = "debug")] ErrorContext::Command { name: type_name::().into(), }, + #[cfg(not(feature = "debug"))] + ErrorContext::Anonymous, ), } } diff --git a/crates/bevy_ecs/src/error/handler.rs b/crates/bevy_ecs/src/error/handler.rs index c89408b2505c2..3d2daa16acbc5 100644 --- a/crates/bevy_ecs/src/error/handler.rs +++ b/crates/bevy_ecs/src/error/handler.rs @@ -33,6 +33,11 @@ pub enum ErrorContext { /// The last tick that the observer was run. last_run: Tick, }, + /// The error occurred in anonymous. + /// + /// This can happen when the debug feature is not enabled + #[cfg(not(feature = "debug"))] + Anonymous, } impl Display for ErrorContext { @@ -48,12 +53,17 @@ impl Display for ErrorContext { Self::RunCondition { name, .. } => { write!(f, "Run condition `{name}` failed") } + #[cfg(not(feature = "debug"))] + Self::Anonymous { .. } => { + write!(f, "Anonymous failed") + } } } } impl ErrorContext { /// The name of the ECS construct that failed. + #[cfg(feature = "debug")] pub fn name(&self) -> &str { match self { Self::System { name, .. } @@ -72,10 +82,13 @@ impl ErrorContext { Self::Command { .. } => "command", Self::Observer { .. } => "observer", Self::RunCondition { .. } => "run condition", + #[cfg(not(feature = "debug"))] + Self::Anonymous { .. } => "anonymous", } } } +#[cfg(feature = "debug")] macro_rules! inner { ($call:path, $e:ident, $c:ident) => { $call!( @@ -87,6 +100,13 @@ macro_rules! inner { }; } +#[cfg(not(feature = "debug"))] +macro_rules! inner { + ($call:path, $e:ident, $c:ident) => { + $call!("Encountered an error in {} `{}`: {}", $c.kind(), "", $e); + }; +} + /// Defines how Bevy reacts to errors. pub type ErrorHandler = fn(BevyError, ErrorContext); diff --git a/crates/bevy_ecs/src/observer/runner.rs b/crates/bevy_ecs/src/observer/runner.rs index 43ece18ff5151..db7896ab2ca57 100644 --- a/crates/bevy_ecs/src/observer/runner.rs +++ b/crates/bevy_ecs/src/observer/runner.rs @@ -207,6 +207,7 @@ impl Observer { /// Panics if the given system is an exclusive system. pub fn new>(system: I) -> Self { let system = Box::new(IntoObserverSystem::into_system(system)); + #[cfg(feature = "debug")] assert!( !system.is_exclusive(), concat!( @@ -215,6 +216,14 @@ impl Observer { ), system.name() ); + #[cfg(not(feature = "debug"))] + assert!( + !system.is_exclusive(), + concat!( + "Exclusive system may not be used as observer.\n", + "Instead of `&mut World`, use either `DeferredWorld` if you do not need structural changes, or `Commands` if you do." + ), + ); Self { system, descriptor: Default::default(), @@ -384,10 +393,13 @@ fn observer_system_runner>( .unwrap_or_else(|| world.default_error_handler()); handler( err, + #[cfg(feature = "debug")] ErrorContext::Observer { name: (*system).name(), last_run: (*system).get_last_run(), }, + #[cfg(not(feature = "debug"))] + ErrorContext::Anonymous, ); }; (*system).queue_deferred(world.into_deferred()); @@ -399,10 +411,13 @@ fn observer_system_runner>( .unwrap_or_else(|| world.default_error_handler()); handler( e.into(), + #[cfg(feature = "debug")] ErrorContext::Observer { name: (*system).name(), last_run: (*system).get_last_run(), }, + #[cfg(not(feature = "debug"))] + ErrorContext::Anonymous, ); } } diff --git a/crates/bevy_ecs/src/query/access.rs b/crates/bevy_ecs/src/query/access.rs index 9c63cb5a74a59..bcd280185e8dc 100644 --- a/crates/bevy_ecs/src/query/access.rs +++ b/crates/bevy_ecs/src/query/access.rs @@ -1,9 +1,14 @@ +#[cfg(feature = "debug")] use crate::component::ComponentId; use crate::storage::SparseSetIndex; +#[cfg(feature = "debug")] use crate::world::World; -use alloc::{format, string::String, vec, vec::Vec}; +#[cfg(feature = "debug")] +use alloc::{format, string::String}; +use alloc::{vec, vec::Vec}; use core::{fmt, fmt::Debug, marker::PhantomData}; use derive_more::From; +#[cfg(feature = "debug")] use disqualified::ShortName; use fixedbitset::FixedBitSet; use thiserror::Error; @@ -991,6 +996,7 @@ impl AccessConflicts { } } + #[cfg(feature = "debug")] pub(crate) fn format_conflict_list(&self, world: &World) -> String { match self { AccessConflicts::All => String::new(), diff --git a/crates/bevy_ecs/src/schedule/condition.rs b/crates/bevy_ecs/src/schedule/condition.rs index 2b31ad50c7605..b3e1fa95a8255 100644 --- a/crates/bevy_ecs/src/schedule/condition.rs +++ b/crates/bevy_ecs/src/schedule/condition.rs @@ -1,4 +1,6 @@ -use alloc::{borrow::Cow, boxed::Box, format}; +use alloc::boxed::Box; +#[cfg(feature = "debug")] +use alloc::{borrow::Cow, format}; use core::ops::Not; use crate::system::{ @@ -121,8 +123,14 @@ pub trait SystemCondition: fn and>(self, and: C) -> And { let a = IntoSystem::into_system(self); let b = IntoSystem::into_system(and); + #[cfg(feature = "debug")] let name = format!("{} && {}", a.name(), b.name()); - CombinatorSystem::new(a, b, Cow::Owned(name)) + CombinatorSystem::new( + a, + b, + #[cfg(feature = "debug")] + Cow::Owned(name), + ) } /// Returns a new run condition that only returns `false` @@ -173,8 +181,14 @@ pub trait SystemCondition: fn nand>(self, nand: C) -> Nand { let a = IntoSystem::into_system(self); let b = IntoSystem::into_system(nand); + #[cfg(feature = "debug")] let name = format!("!({} && {})", a.name(), b.name()); - CombinatorSystem::new(a, b, Cow::Owned(name)) + CombinatorSystem::new( + a, + b, + #[cfg(feature = "debug")] + Cow::Owned(name), + ) } /// Returns a new run condition that only returns `true` @@ -225,8 +239,14 @@ pub trait SystemCondition: fn nor>(self, nor: C) -> Nor { let a = IntoSystem::into_system(self); let b = IntoSystem::into_system(nor); + #[cfg(feature = "debug")] let name = format!("!({} || {})", a.name(), b.name()); - CombinatorSystem::new(a, b, Cow::Owned(name)) + CombinatorSystem::new( + a, + b, + #[cfg(feature = "debug")] + Cow::Owned(name), + ) } /// Returns a new run condition that returns `true` @@ -272,8 +292,14 @@ pub trait SystemCondition: fn or>(self, or: C) -> Or { let a = IntoSystem::into_system(self); let b = IntoSystem::into_system(or); + #[cfg(feature = "debug")] let name = format!("{} || {}", a.name(), b.name()); - CombinatorSystem::new(a, b, Cow::Owned(name)) + CombinatorSystem::new( + a, + b, + #[cfg(feature = "debug")] + Cow::Owned(name), + ) } /// Returns a new run condition that only returns `true` @@ -324,8 +350,14 @@ pub trait SystemCondition: fn xnor>(self, xnor: C) -> Xnor { let a = IntoSystem::into_system(self); let b = IntoSystem::into_system(xnor); + #[cfg(feature = "debug")] let name = format!("!({} ^ {})", a.name(), b.name()); - CombinatorSystem::new(a, b, Cow::Owned(name)) + CombinatorSystem::new( + a, + b, + #[cfg(feature = "debug")] + Cow::Owned(name), + ) } /// Returns a new run condition that only returns `true` @@ -366,8 +398,14 @@ pub trait SystemCondition: fn xor>(self, xor: C) -> Xor { let a = IntoSystem::into_system(self); let b = IntoSystem::into_system(xor); + #[cfg(feature = "debug")] let name = format!("({} ^ {})", a.name(), b.name()); - CombinatorSystem::new(a, b, Cow::Owned(name)) + CombinatorSystem::new( + a, + b, + #[cfg(feature = "debug")] + Cow::Owned(name), + ) } } @@ -399,6 +437,8 @@ mod sealed { /// A collection of [run conditions](SystemCondition) that may be useful in any bevy app. pub mod common_conditions { use super::{NotSystem, SystemCondition}; + #[cfg(feature = "debug")] + use crate::system::System; use crate::{ change_detection::DetectChanges, event::{Event, EventReader}, @@ -406,8 +446,9 @@ pub mod common_conditions { query::QueryFilter, removal_detection::RemovedComponents, resource::Resource, - system::{In, IntoSystem, Local, Res, System, SystemInput}, + system::{In, IntoSystem, Local, Res, SystemInput}, }; + #[cfg(feature = "debug")] use alloc::format; /// A [`SystemCondition`]-satisfying system that returns `true` @@ -977,8 +1018,14 @@ pub mod common_conditions { T: IntoSystem<(), TOut, Marker>, { let condition = IntoSystem::into_system(condition); + #[cfg(feature = "debug")] let name = format!("!{}", condition.name()); - NotSystem::new(super::NotMarker, condition, name.into()) + NotSystem::new( + super::NotMarker, + condition, + #[cfg(feature = "debug")] + name.into(), + ) } /// Generates a [`SystemCondition`] that returns true when the passed one changes. diff --git a/crates/bevy_ecs/src/schedule/config.rs b/crates/bevy_ecs/src/schedule/config.rs index f1a48e432b82d..a451b6d51b8a5 100644 --- a/crates/bevy_ecs/src/schedule/config.rs +++ b/crates/bevy_ecs/src/schedule/config.rs @@ -16,11 +16,17 @@ use crate::{ fn new_condition(condition: impl SystemCondition) -> BoxedCondition { let condition_system = IntoSystem::into_system(condition); + #[cfg(feature = "debug")] assert!( condition_system.is_send(), "SystemCondition `{}` accesses `NonSend` resources. This is not currently supported.", condition_system.name() ); + #[cfg(not(feature = "debug"))] + assert!( + condition_system.is_send(), + "A SystemCondition accesses `NonSend` resources. This is not currently supported.", + ); Box::new(condition_system) } diff --git a/crates/bevy_ecs/src/schedule/executor/mod.rs b/crates/bevy_ecs/src/schedule/executor/mod.rs index b73ffdfd33928..f0dc811f4003b 100644 --- a/crates/bevy_ecs/src/schedule/executor/mod.rs +++ b/crates/bevy_ecs/src/schedule/executor/mod.rs @@ -3,7 +3,9 @@ mod multi_threaded; mod simple; mod single_threaded; -use alloc::{borrow::Cow, vec, vec::Vec}; +#[cfg(feature = "debug")] +use alloc::borrow::Cow; +use alloc::{vec, vec::Vec}; use core::any::TypeId; #[expect(deprecated, reason = "We still need to support this.")] @@ -158,6 +160,7 @@ impl System for ApplyDeferred { type In = (); type Out = Result<()>; + #[cfg(feature = "debug")] fn name(&self) -> Cow<'static, str> { Cow::Borrowed("bevy_ecs::apply_deferred") } diff --git a/crates/bevy_ecs/src/schedule/executor/multi_threaded.rs b/crates/bevy_ecs/src/schedule/executor/multi_threaded.rs index 62a10298c9b83..773d25cfcda09 100644 --- a/crates/bevy_ecs/src/schedule/executor/multi_threaded.rs +++ b/crates/bevy_ecs/src/schedule/executor/multi_threaded.rs @@ -5,7 +5,7 @@ use bevy_tasks::{ComputeTaskPool, Scope, TaskPool, ThreadExecutor}; use concurrent_queue::ConcurrentQueue; use core::{any::Any, panic::AssertUnwindSafe}; use fixedbitset::FixedBitSet; -#[cfg(feature = "std")] +#[cfg(all(feature = "std", feature = "debug"))] use std::eprintln; use std::sync::{Mutex, MutexGuard}; @@ -328,6 +328,7 @@ impl SystemExecutor for MultiThreadedExecutor { } impl<'scope, 'env: 'scope, 'sys> Context<'scope, 'env, 'sys> { + #[cfg_attr(not(feature = "debug"), expect(unused_variables))] fn system_completed( &self, system_index: usize, @@ -341,7 +342,7 @@ impl<'scope, 'env: 'scope, 'sys> Context<'scope, 'env, 'sys> { .push(SystemResult { system_index }) .unwrap_or_else(|error| unreachable!("{}", error)); if let Err(payload) = res { - #[cfg(feature = "std")] + #[cfg(all(feature = "std", feature = "debug"))] #[expect(clippy::print_stderr, reason = "Allowed behind `std` feature gate.")] { eprintln!("Encountered a panic in system `{}`!", &*system.name()); @@ -635,10 +636,13 @@ impl ExecutorState { if !e.skipped { error_handler( e.into(), + #[cfg(feature = "debug")] ErrorContext::System { name: system.name(), last_run: system.get_last_run(), }, + #[cfg(not(feature = "debug"))] + ErrorContext::Anonymous, ); } false @@ -680,10 +684,13 @@ impl ExecutorState { ) { (context.error_handler)( err, + #[cfg(feature = "debug")] ErrorContext::System { name: system.name(), last_run: system.get_last_run(), }, + #[cfg(not(feature = "debug"))] + ErrorContext::Anonymous, ); } }; @@ -729,10 +736,13 @@ impl ExecutorState { if let Err(err) = __rust_begin_short_backtrace::run(system, world) { (context.error_handler)( err, + #[cfg(feature = "debug")] ErrorContext::System { name: system.name(), last_run: system.get_last_run(), }, + #[cfg(not(feature = "debug"))] + ErrorContext::Anonymous, ); } })); @@ -795,7 +805,7 @@ fn apply_deferred( system.apply_deferred(world); })); if let Err(payload) = res { - #[cfg(feature = "std")] + #[cfg(all(feature = "std", feature = "debug"))] #[expect(clippy::print_stderr, reason = "Allowed behind `std` feature gate.")] { eprintln!( @@ -833,10 +843,13 @@ unsafe fn evaluate_and_fold_conditions( if !e.skipped { error_handler( e.into(), + #[cfg(feature = "debug")] ErrorContext::System { name: condition.name(), last_run: condition.get_last_run(), }, + #[cfg(not(feature = "debug"))] + ErrorContext::Anonymous, ); } return false; diff --git a/crates/bevy_ecs/src/schedule/executor/simple.rs b/crates/bevy_ecs/src/schedule/executor/simple.rs index d9069aa6e871d..bbb76693150c5 100644 --- a/crates/bevy_ecs/src/schedule/executor/simple.rs +++ b/crates/bevy_ecs/src/schedule/executor/simple.rs @@ -6,7 +6,7 @@ use fixedbitset::FixedBitSet; #[cfg(feature = "trace")] use tracing::info_span; -#[cfg(feature = "std")] +#[cfg(all(feature = "std", feature = "debug"))] use std::eprintln; use crate::{ @@ -113,10 +113,13 @@ impl SystemExecutor for SimpleExecutor { if !e.skipped { error_handler( e.into(), + #[cfg(feature = "debug")] ErrorContext::System { name: system.name(), last_run: system.get_last_run(), }, + #[cfg(not(feature = "debug"))] + ErrorContext::Anonymous, ); } false @@ -148,10 +151,13 @@ impl SystemExecutor for SimpleExecutor { if let Err(err) = __rust_begin_short_backtrace::run(system, world) { error_handler( err, + #[cfg(feature = "debug")] ErrorContext::System { name: system.name(), last_run: system.get_last_run(), }, + #[cfg(not(feature = "debug"))] + ErrorContext::Anonymous, ); } }); @@ -160,6 +166,7 @@ impl SystemExecutor for SimpleExecutor { #[expect(clippy::print_stderr, reason = "Allowed behind `std` feature gate.")] { if let Err(payload) = std::panic::catch_unwind(f) { + #[cfg(feature = "debug")] eprintln!("Encountered a panic in system `{}`!", &*system.name()); std::panic::resume_unwind(payload); } @@ -218,10 +225,13 @@ fn evaluate_and_fold_conditions( if !e.skipped { error_handler( e.into(), + #[cfg(feature = "debug")] ErrorContext::System { name: condition.name(), last_run: condition.get_last_run(), }, + #[cfg(not(feature = "debug"))] + ErrorContext::Anonymous, ); } return false; diff --git a/crates/bevy_ecs/src/schedule/executor/single_threaded.rs b/crates/bevy_ecs/src/schedule/executor/single_threaded.rs index 68af623b408df..d9168955f09a4 100644 --- a/crates/bevy_ecs/src/schedule/executor/single_threaded.rs +++ b/crates/bevy_ecs/src/schedule/executor/single_threaded.rs @@ -4,7 +4,7 @@ use fixedbitset::FixedBitSet; #[cfg(feature = "trace")] use tracing::info_span; -#[cfg(feature = "std")] +#[cfg(all(feature = "std", feature = "debug"))] use std::eprintln; use crate::{ @@ -113,10 +113,13 @@ impl SystemExecutor for SingleThreadedExecutor { if !e.skipped { error_handler( e.into(), + #[cfg(feature = "debug")] ErrorContext::System { name: system.name(), last_run: system.get_last_run(), }, + #[cfg(not(feature = "debug"))] + ErrorContext::Anonymous, ); } false @@ -152,10 +155,13 @@ impl SystemExecutor for SingleThreadedExecutor { { error_handler( err, + #[cfg(feature = "debug")] ErrorContext::System { name: system.name(), last_run: system.get_last_run(), }, + #[cfg(not(feature = "debug"))] + ErrorContext::Anonymous, ); } }); @@ -164,6 +170,7 @@ impl SystemExecutor for SingleThreadedExecutor { #[expect(clippy::print_stderr, reason = "Allowed behind `std` feature gate.")] { if let Err(payload) = std::panic::catch_unwind(f) { + #[cfg(feature = "debug")] eprintln!("Encountered a panic in system `{}`!", &*system.name()); std::panic::resume_unwind(payload); } @@ -236,10 +243,13 @@ fn evaluate_and_fold_conditions( if !e.skipped { error_handler( e.into(), + #[cfg(feature = "debug")] ErrorContext::System { name: condition.name(), last_run: condition.get_last_run(), }, + #[cfg(not(feature = "debug"))] + ErrorContext::Anonymous, ); } return false; diff --git a/crates/bevy_ecs/src/schedule/mod.rs b/crates/bevy_ecs/src/schedule/mod.rs index 81912d2f72b5f..ec808e2c15a17 100644 --- a/crates/bevy_ecs/src/schedule/mod.rs +++ b/crates/bevy_ecs/src/schedule/mod.rs @@ -26,7 +26,9 @@ pub mod passes { #[cfg(test)] mod tests { use super::*; - use alloc::{string::ToString, vec, vec::Vec}; + #[cfg(feature = "debug")] + use alloc::string::ToString; + use alloc::{vec, vec::Vec}; use core::sync::atomic::{AtomicU32, Ordering}; pub use crate::{ @@ -727,6 +729,7 @@ mod tests { } mod system_ambiguity { + #[cfg(feature = "debug")] use alloc::collections::BTreeSet; use super::*; @@ -1069,6 +1072,7 @@ mod tests { // Tests that the correct ambiguities were reported in the correct order. #[test] + #[cfg(feature = "debug")] fn correct_ambiguities() { fn system_a(_res: ResMut) {} fn system_b(_res: ResMut) {} @@ -1142,6 +1146,7 @@ mod tests { // Test that anonymous set names work properly // Related issue https://github.com/bevyengine/bevy/issues/9641 #[test] + #[cfg(feature = "debug")] fn anonymous_set_name() { let mut schedule = Schedule::new(TestSchedule); schedule.add_systems((resmut_system, resmut_system).run_if(|| true)); diff --git a/crates/bevy_ecs/src/schedule/schedule.rs b/crates/bevy_ecs/src/schedule/schedule.rs index 144ce6516c057..704a5ee5fc545 100644 --- a/crates/bevy_ecs/src/schedule/schedule.rs +++ b/crates/bevy_ecs/src/schedule/schedule.rs @@ -2,31 +2,43 @@ clippy::module_inception, reason = "This instance of module inception is being discussed; see #17344." )] +#[cfg(feature = "debug")] use alloc::borrow::Cow; +#[cfg(feature = "debug")] +use alloc::string::ToString; use alloc::{ boxed::Box, collections::{BTreeMap, BTreeSet}, format, - string::{String, ToString}, + string::String, vec, vec::Vec, }; use bevy_platform::collections::{HashMap, HashSet}; use bevy_utils::{default, TypeIdMap}; +#[cfg(feature = "debug")] +use core::fmt::Write; use core::{ any::{Any, TypeId}, - fmt::{Debug, Write}, + fmt::Debug, }; +#[cfg(feature = "debug")] use disqualified::ShortName; use fixedbitset::FixedBitSet; -use log::{error, info, warn}; +use log::error; +#[cfg(feature = "debug")] +use log::{info, warn}; use pass::ScheduleBuildPassObj; +#[cfg(not(feature = "debug"))] +use std::string::ToString; use thiserror::Error; -#[cfg(feature = "trace")] +#[cfg(all(feature = "trace", feature = "debug"))] use tracing::info_span; +#[cfg(feature = "debug")] +use crate::component::Components; use crate::{ - component::{ComponentId, Components, Tick}, + component::{ComponentId, Tick}, prelude::Component, resource::Resource, schedule::*, @@ -158,6 +170,7 @@ impl Schedules { /// /// May panic or retrieve incorrect names if [`Components`] is not from the same /// world + #[cfg(feature = "debug")] pub fn print_ignored_ambiguities(&self, components: &Components) { let mut message = "System order ambiguities caused by conflicts on the following types are ignored:\n" @@ -664,6 +677,7 @@ impl SystemSetNode { Self { inner: set } } + #[cfg(feature = "debug")] pub fn name(&self) -> String { format!("{:?}", &self.inner) } @@ -672,6 +686,7 @@ impl SystemSetNode { self.inner.system_type().is_some() } + #[cfg(feature = "debug")] pub fn is_anonymous(&self) -> bool { self.inner.is_anonymous() } @@ -1068,7 +1083,10 @@ impl ScheduleGraph { match self.system_set_ids.get(&set) { Some(set_id) => { if id == set_id { + #[cfg(feature = "debug")] return Err(ScheduleBuildError::HierarchyLoop(self.get_node_name(id))); + #[cfg(not(feature = "debug"))] + return Err(ScheduleBuildError::Anonymous); } } None => { @@ -1110,7 +1128,10 @@ impl ScheduleGraph { match self.system_set_ids.get(set) { Some(set_id) => { if id == set_id { + #[cfg(feature = "debug")] return Err(ScheduleBuildError::DependencyLoop(self.get_node_name(id))); + #[cfg(not(feature = "debug"))] + return Err(ScheduleBuildError::Anonymous); } } None => { @@ -1281,6 +1302,7 @@ impl ScheduleGraph { &ambiguous_with_flattened, ignored_ambiguities, ); + #[cfg(feature = "debug")] self.optionally_check_conflicts(&conflicting_systems, world.components(), schedule_label)?; self.conflicting_systems = conflicting_systems; @@ -1634,11 +1656,13 @@ pub enum ReportCycles { // methods for reporting errors impl ScheduleGraph { + #[cfg(feature = "debug")] fn get_node_name(&self, id: &NodeId) -> String { self.get_node_name_inner(id, self.settings.report_sets) } #[inline] + #[cfg(feature = "debug")] fn get_node_name_inner(&self, id: &NodeId, report_sets: bool) -> String { let name = match id { NodeId::System(_) => { @@ -1672,6 +1696,7 @@ impl ScheduleGraph { } } + #[cfg(feature = "debug")] fn anonymous_set_name(&self, id: &NodeId) -> String { format!( "({})", @@ -1685,6 +1710,7 @@ impl ScheduleGraph { ) } + #[cfg(feature = "debug")] fn get_node_kind(&self, id: &NodeId) -> &'static str { match id { NodeId::System(_) => "system", @@ -1703,7 +1729,10 @@ impl ScheduleGraph { return Ok(()); } + #[cfg(feature = "debug")] let message = self.get_hierarchy_conflicts_error_message(transitive_edges); + #[cfg(not(feature = "debug"))] + let message = "Enable debug feature for more informations".to_string(); match self.settings.hierarchy_detection { LogLevel::Ignore => unreachable!(), LogLevel::Warn => { @@ -1717,6 +1746,7 @@ impl ScheduleGraph { } } + #[cfg(feature = "debug")] fn get_hierarchy_conflicts_error_message( &self, transitive_edges: &[(NodeId, NodeId)], @@ -1745,6 +1775,7 @@ impl ScheduleGraph { /// # Errors /// /// If the graph contain cycles, then an error is returned. + #[cfg_attr(not(feature = "debug"), expect(unused_variables))] pub fn topsort_graph( &self, graph: &DiGraph, @@ -1774,6 +1805,7 @@ impl ScheduleGraph { cycles.append(&mut simple_cycles_in_component(graph, scc)); } + #[cfg(feature = "debug")] let error = match report { ReportCycles::Hierarchy => ScheduleBuildError::HierarchyCycle( self.get_hierarchy_cycles_error_message(&cycles), @@ -1782,12 +1814,15 @@ impl ScheduleGraph { self.get_dependency_cycles_error_message(&cycles), ), }; + #[cfg(not(feature = "debug"))] + let error = ScheduleBuildError::Anonymous; Err(error) } } /// Logs details of cycles in the hierarchy graph. + #[cfg(feature = "debug")] fn get_hierarchy_cycles_error_message(&self, cycles: &[Vec]) -> String { let mut message = format!("schedule has {} in_set cycle(s):\n", cycles.len()); for (i, cycle) in cycles.iter().enumerate() { @@ -1810,6 +1845,7 @@ impl ScheduleGraph { } /// Logs details of cycles in the dependency graph. + #[cfg(feature = "debug")] fn get_dependency_cycles_error_message(&self, cycles: &[Vec]) -> String { let mut message = format!("schedule has {} before/after cycle(s):\n", cycles.len()); for (i, cycle) in cycles.iter().enumerate() { @@ -1841,9 +1877,14 @@ impl ScheduleGraph { for &(a, b) in &dep_results.connected { if hier_results_connected.contains(&(a, b)) || hier_results_connected.contains(&(b, a)) { - let name_a = self.get_node_name(&a); - let name_b = self.get_node_name(&b); - return Err(ScheduleBuildError::CrossDependency(name_a, name_b)); + #[cfg(feature = "debug")] + { + let name_a = self.get_node_name(&a); + let name_b = self.get_node_name(&b); + return Err(ScheduleBuildError::CrossDependency(name_a, name_b)); + } + #[cfg(not(feature = "debug"))] + return Err(ScheduleBuildError::Anonymous); } } @@ -1865,10 +1906,13 @@ impl ScheduleGraph { let b_systems = set_system_bitsets.get(b).unwrap(); if !a_systems.is_disjoint(b_systems) { + #[cfg(feature = "debug")] return Err(ScheduleBuildError::SetsHaveOrderButIntersect( self.get_node_name(a), self.get_node_name(b), )); + #[cfg(not(feature = "debug"))] + return Err(ScheduleBuildError::Anonymous); } } @@ -1888,9 +1932,12 @@ impl ScheduleGraph { let after = self.dependency.graph.edges_directed(id, Outgoing); let relations = before.count() + after.count() + ambiguous_with.count(); if instances > 1 && relations > 0 { + #[cfg(feature = "debug")] return Err(ScheduleBuildError::SystemTypeSetAmbiguity( self.get_node_name(&id), )); + #[cfg(not(feature = "debug"))] + return Err(ScheduleBuildError::Anonymous); } } } @@ -1898,6 +1945,7 @@ impl ScheduleGraph { } /// if [`ScheduleBuildSettings::ambiguity_detection`] is [`LogLevel::Ignore`], this check is skipped + #[cfg(feature = "debug")] fn optionally_check_conflicts( &self, conflicts: &[(NodeId, NodeId, Vec)], @@ -1919,6 +1967,7 @@ impl ScheduleGraph { } } + #[cfg(feature = "debug")] fn get_conflicts_error_message( &self, ambiguities: &[(NodeId, NodeId, Vec)], @@ -1947,6 +1996,7 @@ impl ScheduleGraph { } /// convert conflicts to human readable format + #[cfg(feature = "debug")] pub fn conflicts_to_string<'a>( &'a self, ambiguities: &'a [(NodeId, NodeId, Vec)], @@ -1970,6 +2020,7 @@ impl ScheduleGraph { }) } + #[cfg(feature = "debug")] fn traverse_sets_containing_node(&self, id: NodeId, f: &mut impl FnMut(NodeId) -> bool) { for (set_id, _) in self.hierarchy.graph.edges_directed(id, Incoming) { if f(set_id) { @@ -1978,6 +2029,7 @@ impl ScheduleGraph { } } + #[cfg(feature = "debug")] fn names_of_sets_containing_node(&self, id: &NodeId) -> Vec { let mut sets = >::default(); self.traverse_sets_containing_node(*id, &mut |set_id| { @@ -1996,6 +2048,10 @@ impl ScheduleGraph { #[derive(Error, Debug)] #[non_exhaustive] pub enum ScheduleBuildError { + /// Anonymous error. Build with the debug feature to know more + #[error("Error building schedule.")] + #[cfg(not(feature = "debug"))] + Anonymous, /// A system set contains itself. #[error("System set `{0}` contains itself.")] HierarchyLoop(String), diff --git a/crates/bevy_ecs/src/schedule/set.rs b/crates/bevy_ecs/src/schedule/set.rs index 4974be5d43a68..ba9cb57a78a80 100644 --- a/crates/bevy_ecs/src/schedule/set.rs +++ b/crates/bevy_ecs/src/schedule/set.rs @@ -109,9 +109,10 @@ impl SystemTypeSet { impl Debug for SystemTypeSet { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - f.debug_tuple("SystemTypeSet") - .field(&format_args!("fn {}()", &core::any::type_name::())) - .finish() + let mut debugging = f.debug_tuple("SystemTypeSet"); + #[cfg(feature = "debug")] + debugging.field(&format_args!("fn {}()", &core::any::type_name::())); + debugging.finish() } } diff --git a/crates/bevy_ecs/src/schedule/stepping.rs b/crates/bevy_ecs/src/schedule/stepping.rs index 222dfdfcafe15..8560332c42091 100644 --- a/crates/bevy_ecs/src/schedule/stepping.rs +++ b/crates/bevy_ecs/src/schedule/stepping.rs @@ -726,7 +726,7 @@ impl ScheduleState { .get(&node_id) .unwrap_or(&SystemBehavior::Continue); - #[cfg(test)] + #[cfg(all(test, feature = "debug"))] debug!( "skipped_systems(): systems[{}], pos {}, Action::{:?}, Behavior::{:?}, {}", i, diff --git a/crates/bevy_ecs/src/storage/resource.rs b/crates/bevy_ecs/src/storage/resource.rs index fa58610bdf361..7119041c8ee43 100644 --- a/crates/bevy_ecs/src/storage/resource.rs +++ b/crates/bevy_ecs/src/storage/resource.rs @@ -3,6 +3,7 @@ use crate::{ component::{ComponentId, ComponentTicks, Components, Tick, TickCells}, storage::{blob_vec::BlobVec, SparseSet}, }; +#[cfg(feature = "debug")] use alloc::string::String; use bevy_ptr::{OwningPtr, Ptr, UnsafeCellDeref}; use core::{cell::UnsafeCell, mem::ManuallyDrop, panic::Location}; @@ -23,6 +24,7 @@ pub struct ResourceData { not(feature = "std"), expect(dead_code, reason = "currently only used with the std feature") )] + #[cfg(feature = "debug")] type_name: String, #[cfg(feature = "std")] origin_thread_id: Option, @@ -78,12 +80,19 @@ impl ResourceData { #[cfg(feature = "std")] if self.origin_thread_id != Some(std::thread::current().id()) { // Panic in tests, as testing for aborting is nearly impossible + #[cfg(feature = "debug")] panic!( "Attempted to access or drop non-send resource {} from thread {:?} on a thread {:?}. This is not allowed. Aborting.", self.type_name, self.origin_thread_id, std::thread::current().id() ); + #[cfg(not(feature = "debug"))] + panic!( + "Attempted to access or drop non-send resource from thread {:?} on a thread {:?}. This is not allowed. Aborting.", + self.origin_thread_id, + std::thread::current().id() + ); } // TODO: Handle no_std non-send. @@ -367,11 +376,17 @@ impl Resources { self.resources.get_or_insert_with(component_id, || { let component_info = components.get_info(component_id).unwrap(); if SEND { + #[cfg(feature = "debug")] assert!( component_info.is_send_and_sync(), "Send + Sync resource {} initialized as non_send. It may have been inserted via World::insert_non_send_resource by accident. Try using World::insert_resource instead.", component_info.name(), ); + #[cfg(not(feature = "debug"))] + assert!( + component_info.is_send_and_sync(), + "Send + Sync resource initialized as non_send. It may have been inserted via World::insert_non_send_resource by accident. Try using World::insert_resource instead.", + ); } // SAFETY: component_info.drop() is valid for the types that will be inserted. let data = unsafe { @@ -385,6 +400,7 @@ impl Resources { data: ManuallyDrop::new(data), added_ticks: UnsafeCell::new(Tick::new(0)), changed_ticks: UnsafeCell::new(Tick::new(0)), + #[cfg(feature = "debug")] type_name: String::from(component_info.name()), #[cfg(feature = "std")] origin_thread_id: None, diff --git a/crates/bevy_ecs/src/system/adapter_system.rs b/crates/bevy_ecs/src/system/adapter_system.rs index 062fbb3d5b2cb..2c96d1b90db57 100644 --- a/crates/bevy_ecs/src/system/adapter_system.rs +++ b/crates/bevy_ecs/src/system/adapter_system.rs @@ -1,4 +1,6 @@ -use alloc::{borrow::Cow, vec::Vec}; +#[cfg(feature = "debug")] +use alloc::borrow::Cow; +use alloc::vec::Vec; use super::{IntoSystem, ReadOnlySystem, System, SystemParamValidationError}; use crate::{ @@ -91,8 +93,14 @@ where // Required method fn into_system(this: Self) -> Self::System { let system = IntoSystem::into_system(this.system); + #[cfg(feature = "debug")] let name = system.name(); - AdapterSystem::new(this.func, system, name) + AdapterSystem::new( + this.func, + system, + #[cfg(feature = "debug")] + name, + ) } } @@ -101,6 +109,7 @@ where pub struct AdapterSystem { func: Func, system: S, + #[cfg(feature = "debug")] name: Cow<'static, str>, } @@ -110,8 +119,17 @@ where S: System, { /// Creates a new [`System`] that uses `func` to adapt `system`, via the [`Adapt`] trait. - pub const fn new(func: Func, system: S, name: Cow<'static, str>) -> Self { - Self { func, system, name } + pub const fn new( + func: Func, + system: S, + #[cfg(feature = "debug")] name: Cow<'static, str>, + ) -> Self { + Self { + func, + system, + #[cfg(feature = "debug")] + name, + } } } @@ -123,6 +141,7 @@ where type In = Func::In; type Out = Func::Out; + #[cfg(feature = "debug")] fn name(&self) -> Cow<'static, str> { self.name.clone() } diff --git a/crates/bevy_ecs/src/system/builder.rs b/crates/bevy_ecs/src/system/builder.rs index 6536c9cc1e89d..f5d421c79e5da 100644 --- a/crates/bevy_ecs/src/system/builder.rs +++ b/crates/bevy_ecs/src/system/builder.rs @@ -602,9 +602,14 @@ unsafe impl<'w, 's, T: FnOnce(&mut FilteredResourcesBuilder)> let combined_access = meta.component_access_set.combined_access(); let conflicts = combined_access.get_conflicts(&access); if !conflicts.is_empty() { - let accesses = conflicts.format_conflict_list(world); - let system_name = &meta.name; - panic!("error[B0002]: FilteredResources in system {system_name} accesses resources(s){accesses} in a way that conflicts with a previous system parameter. Consider removing the duplicate access. See: https://bevy.org/learn/errors/b0002"); + #[cfg(feature = "debug")] + { + let accesses = conflicts.format_conflict_list(world); + let system_name = &meta.name; + panic!("error[B0002]: FilteredResources in system {system_name} accesses resources(s){accesses} in a way that conflicts with a previous system parameter. Consider removing the duplicate access. See: https://bevy.org/learn/errors/b0002"); + } + #[cfg(not(feature = "debug"))] + panic!("error[B0002]: FilteredResources in a system accesses resources(s) in a way that conflicts with a previous system parameter. Consider removing the duplicate access. See: https://bevy.org/learn/errors/b0002"); } if access.has_read_all_resources() { @@ -661,9 +666,14 @@ unsafe impl<'w, 's, T: FnOnce(&mut FilteredResourcesMutBuilder)> let combined_access = meta.component_access_set.combined_access(); let conflicts = combined_access.get_conflicts(&access); if !conflicts.is_empty() { - let accesses = conflicts.format_conflict_list(world); - let system_name = &meta.name; - panic!("error[B0002]: FilteredResourcesMut in system {system_name} accesses resources(s){accesses} in a way that conflicts with a previous system parameter. Consider removing the duplicate access. See: https://bevy.org/learn/errors/b0002"); + #[cfg(feature = "debug")] + { + let accesses = conflicts.format_conflict_list(world); + let system_name = &meta.name; + panic!("error[B0002]: FilteredResourcesMut in system {system_name} accesses resources(s){accesses} in a way that conflicts with a previous system parameter. Consider removing the duplicate access. See: https://bevy.org/learn/errors/b0002"); + } + #[cfg(not(feature = "debug"))] + panic!("error[B0002]: FilteredResourcesMut in a system accesses resources(s) in a way that conflicts with a previous system parameter. Consider removing the duplicate access. See: https://bevy.org/learn/errors/b0002"); } if access.has_read_all_resources() { diff --git a/crates/bevy_ecs/src/system/combinator.rs b/crates/bevy_ecs/src/system/combinator.rs index 18de14126338f..1f2009f94bda5 100644 --- a/crates/bevy_ecs/src/system/combinator.rs +++ b/crates/bevy_ecs/src/system/combinator.rs @@ -1,4 +1,6 @@ -use alloc::{borrow::Cow, format, vec::Vec}; +use alloc::vec::Vec; +#[cfg(feature = "debug")] +use alloc::{borrow::Cow, format}; use core::marker::PhantomData; use crate::{ @@ -112,6 +114,7 @@ pub struct CombinatorSystem { _marker: PhantomData Func>, a: A, b: B, + #[cfg(feature = "debug")] name: Cow<'static, str>, component_access_set: FilteredAccessSet, } @@ -120,11 +123,12 @@ impl CombinatorSystem { /// Creates a new system that combines two inner systems. /// /// The returned system will only be usable if `Func` implements [`Combine`]. - pub fn new(a: A, b: B, name: Cow<'static, str>) -> Self { + pub fn new(a: A, b: B, #[cfg(feature = "debug")] name: Cow<'static, str>) -> Self { Self { _marker: PhantomData, a, b, + #[cfg(feature = "debug")] name, component_access_set: FilteredAccessSet::default(), } @@ -140,6 +144,7 @@ where type In = Func::In; type Out = Func::Out; + #[cfg(feature = "debug")] fn name(&self) -> Cow<'static, str> { self.name.clone() } @@ -249,7 +254,12 @@ where { /// Clone the combined system. The cloned instance must be `.initialize()`d before it can run. fn clone(&self) -> Self { - CombinatorSystem::new(self.a.clone(), self.b.clone(), self.name.clone()) + CombinatorSystem::new( + self.a.clone(), + self.b.clone(), + #[cfg(feature = "debug")] + self.name.clone(), + ) } } @@ -282,8 +292,14 @@ where fn into_system(this: Self) -> Self::System { let system_a = IntoSystem::into_system(this.a); let system_b = IntoSystem::into_system(this.b); + #[cfg(feature = "debug")] let name = format!("Pipe({}, {})", system_a.name(), system_b.name()); - PipeSystem::new(system_a, system_b, Cow::Owned(name)) + PipeSystem::new( + system_a, + system_b, + #[cfg(feature = "debug")] + Cow::Owned(name), + ) } } @@ -329,6 +345,7 @@ where pub struct PipeSystem { a: A, b: B, + #[cfg(feature = "debug")] name: Cow<'static, str>, component_access_set: FilteredAccessSet, } @@ -340,10 +357,11 @@ where for<'a> B::In: SystemInput = A::Out>, { /// Creates a new system that pipes two inner systems. - pub fn new(a: A, b: B, name: Cow<'static, str>) -> Self { + pub fn new(a: A, b: B, #[cfg(feature = "debug")] name: Cow<'static, str>) -> Self { Self { a, b, + #[cfg(feature = "debug")] name, component_access_set: FilteredAccessSet::default(), } @@ -359,6 +377,7 @@ where type In = A::In; type Out = B::Out; + #[cfg(feature = "debug")] fn name(&self) -> Cow<'static, str> { self.name.clone() } diff --git a/crates/bevy_ecs/src/system/commands/entity_command.rs b/crates/bevy_ecs/src/system/commands/entity_command.rs index 317ad8476abe8..ea9175fd4330c 100644 --- a/crates/bevy_ecs/src/system/commands/entity_command.rs +++ b/crates/bevy_ecs/src/system/commands/entity_command.rs @@ -4,13 +4,16 @@ //! It also contains functions that return closures for use with //! [`EntityCommands`](crate::system::EntityCommands). +// #[cfg(feature = "debug")] use alloc::vec::Vec; use log::info; +#[cfg(feature = "debug")] +use crate::component::ComponentInfo; use crate::{ bundle::{Bundle, InsertMode}, change_detection::MaybeLocation, - component::{Component, ComponentId, ComponentInfo}, + component::{Component, ComponentId}, entity::{Entity, EntityClonerBuilder}, event::Event, relationship::RelationshipHookMode, @@ -271,12 +274,18 @@ pub fn move_components(target: Entity) -> impl EntityCommand { /// An [`EntityCommand`] that logs the components of an entity. pub fn log_components() -> impl EntityCommand { move |entity: EntityWorldMut| { + #[cfg(feature = "debug")] let debug_infos: Vec<_> = entity .world() .inspect_entity(entity.id()) .expect("Entity existence is verified before an EntityCommand is executed") .map(ComponentInfo::name) .collect(); + #[cfg(not(feature = "debug"))] + let debug_infos = { + let mut disabled = Vec::new(); + disabled.push("debug feature not enabled"); + }; info!("Entity {}: {debug_infos:?}", entity.id()); } } diff --git a/crates/bevy_ecs/src/system/exclusive_function_system.rs b/crates/bevy_ecs/src/system/exclusive_function_system.rs index f7e362a6709b9..2224a5c2aba6b 100644 --- a/crates/bevy_ecs/src/system/exclusive_function_system.rs +++ b/crates/bevy_ecs/src/system/exclusive_function_system.rs @@ -1,15 +1,19 @@ +#[cfg(feature = "debug")] +use crate::system::check_system_change_tick; use crate::{ component::{ComponentId, Tick}, query::{Access, FilteredAccessSet}, schedule::{InternedSystemSet, SystemSet}, system::{ - check_system_change_tick, ExclusiveSystemParam, ExclusiveSystemParamItem, IntoSystem, - System, SystemIn, SystemInput, SystemMeta, + ExclusiveSystemParam, ExclusiveSystemParamItem, IntoSystem, System, SystemIn, SystemInput, + SystemMeta, }, world::{unsafe_world_cell::UnsafeWorldCell, World}, }; -use alloc::{borrow::Cow, vec, vec::Vec}; +#[cfg(feature = "debug")] +use alloc::borrow::Cow; +use alloc::{vec, vec::Vec}; use core::marker::PhantomData; use variadics_please::all_tuples; @@ -41,6 +45,7 @@ where /// Return this system with a new name. /// /// Useful to give closure systems more readable and unique names for debugging and tracing. + #[cfg(feature = "debug")] pub fn with_name(mut self, new_name: impl Into>) -> Self { self.system_meta.set_name(new_name.into()); self @@ -83,6 +88,7 @@ where type Out = F::Out; #[inline] + #[cfg(feature = "debug")] fn name(&self) -> Cow<'static, str> { self.system_meta.name.clone() } @@ -186,7 +192,9 @@ where } #[inline] + #[cfg_attr(not(feature = "debug"), expect(unused_variables))] fn check_change_tick(&mut self, change_tick: Tick) { + #[cfg(feature = "debug")] check_system_change_tick( &mut self.system_meta.last_run, change_tick, diff --git a/crates/bevy_ecs/src/system/function_system.rs b/crates/bevy_ecs/src/system/function_system.rs index d257a9f0799fe..5782637fd902a 100644 --- a/crates/bevy_ecs/src/system/function_system.rs +++ b/crates/bevy_ecs/src/system/function_system.rs @@ -1,16 +1,17 @@ +#[cfg(feature = "debug")] +use crate::system::check_system_change_tick; use crate::{ component::{ComponentId, Tick}, prelude::FromWorld, query::{Access, FilteredAccessSet}, schedule::{InternedSystemSet, SystemSet}, - system::{ - check_system_change_tick, ReadOnlySystemParam, System, SystemIn, SystemInput, SystemParam, - SystemParamItem, - }, + system::{ReadOnlySystemParam, System, SystemIn, SystemInput, SystemParam, SystemParamItem}, world::{unsafe_world_cell::UnsafeWorldCell, DeferredWorld, World, WorldId}, }; -use alloc::{borrow::Cow, vec, vec::Vec}; +#[cfg(feature = "debug")] +use alloc::borrow::Cow; +use alloc::{vec, vec::Vec}; use core::marker::PhantomData; use variadics_please::all_tuples; @@ -24,6 +25,7 @@ use super::{ /// The metadata of a [`System`]. #[derive(Clone)] pub struct SystemMeta { + #[cfg(feature = "debug")] pub(crate) name: Cow<'static, str>, /// The set of component accesses for this system. This is used to determine /// - soundness issues (e.g. multiple [`SystemParam`]s mutably accessing the same component) @@ -41,8 +43,10 @@ pub struct SystemMeta { impl SystemMeta { pub(crate) fn new() -> Self { + #[cfg(feature = "debug")] let name = core::any::type_name::(); Self { + #[cfg(feature = "debug")] name: name.into(), component_access_set: FilteredAccessSet::default(), flags: SystemStateFlags::empty(), @@ -56,6 +60,7 @@ impl SystemMeta { /// Returns the system's name #[inline] + #[cfg(feature = "debug")] pub fn name(&self) -> &str { &self.name } @@ -64,6 +69,7 @@ impl SystemMeta { /// /// Useful to give closure systems more readable and unique names for debugging and tracing. #[inline] + #[cfg(feature = "debug")] pub fn set_name(&mut self, new_name: impl Into>) { let new_name: Cow<'static, str> = new_name.into(); #[cfg(feature = "trace")] @@ -549,6 +555,7 @@ where /// Return this system with a new name. /// /// Useful to give closure systems more readable and unique names for debugging and tracing. + #[cfg(feature = "debug")] pub fn with_name(mut self, new_name: impl Into>) -> Self { self.system_meta.set_name(new_name.into()); self @@ -616,6 +623,7 @@ where type Out = F::Out; #[inline] + #[cfg(feature = "debug")] fn name(&self) -> Cow<'static, str> { self.system_meta.name.clone() } @@ -727,7 +735,9 @@ where } #[inline] + #[cfg_attr(not(feature = "debug"), expect(unused_variables))] fn check_change_tick(&mut self, change_tick: Tick) { + #[cfg(feature = "debug")] check_system_change_tick( &mut self.system_meta.last_run, change_tick, diff --git a/crates/bevy_ecs/src/system/mod.rs b/crates/bevy_ecs/src/system/mod.rs index 54e5a781ab3eb..0ad31d98e36ba 100644 --- a/crates/bevy_ecs/src/system/mod.rs +++ b/crates/bevy_ecs/src/system/mod.rs @@ -131,6 +131,7 @@ mod observer_system; mod query; mod schedule_system; mod system; +#[cfg(feature = "debug")] mod system_name; mod system_param; mod system_registry; @@ -149,6 +150,7 @@ pub use observer_system::*; pub use query::*; pub use schedule_system::*; pub use system::*; +#[cfg(feature = "debug")] pub use system_name::*; pub use system_param::*; pub use system_registry::*; diff --git a/crates/bevy_ecs/src/system/observer_system.rs b/crates/bevy_ecs/src/system/observer_system.rs index b123301a08401..57a9d7052c088 100644 --- a/crates/bevy_ecs/src/system/observer_system.rs +++ b/crates/bevy_ecs/src/system/observer_system.rs @@ -1,4 +1,6 @@ -use alloc::{borrow::Cow, vec::Vec}; +#[cfg(feature = "debug")] +use alloc::borrow::Cow; +use alloc::vec::Vec; use core::marker::PhantomData; use crate::{ @@ -112,6 +114,7 @@ where type Out = Result; #[inline] + #[cfg(feature = "debug")] fn name(&self) -> Cow<'static, str> { self.observer.name() } diff --git a/crates/bevy_ecs/src/system/schedule_system.rs b/crates/bevy_ecs/src/system/schedule_system.rs index 17bd3f46de8a7..5e8ff88bd2282 100644 --- a/crates/bevy_ecs/src/system/schedule_system.rs +++ b/crates/bevy_ecs/src/system/schedule_system.rs @@ -1,4 +1,6 @@ -use alloc::{borrow::Cow, vec::Vec}; +#[cfg(feature = "debug")] +use alloc::borrow::Cow; +use alloc::vec::Vec; use crate::{ component::{ComponentId, Tick}, @@ -25,6 +27,7 @@ impl> System for InfallibleSystemWrapper { type Out = Result; #[inline] + #[cfg(feature = "debug")] fn name(&self) -> Cow<'static, str> { self.0.name() } @@ -150,6 +153,7 @@ where type Out = S::Out; + #[cfg(feature = "debug")] fn name(&self) -> Cow<'static, str> { self.system.name() } @@ -252,6 +256,7 @@ where type Out = S::Out; + #[cfg(feature = "debug")] fn name(&self) -> Cow<'static, str> { self.system.name() } diff --git a/crates/bevy_ecs/src/system/system.rs b/crates/bevy_ecs/src/system/system.rs index 211152f3d877c..596bc44eb4bbb 100644 --- a/crates/bevy_ecs/src/system/system.rs +++ b/crates/bevy_ecs/src/system/system.rs @@ -4,6 +4,7 @@ )] use bitflags::bitflags; use core::fmt::Debug; +#[cfg(feature = "debug")] use log::warn; use thiserror::Error; @@ -49,7 +50,9 @@ pub trait System: Send + Sync + 'static { type In: SystemInput; /// The system's output. type Out; + /// Returns the system's name. + #[cfg(feature = "debug")] fn name(&self) -> Cow<'static, str>; /// Returns the [`TypeId`] of the underlying system type. #[inline] @@ -228,6 +231,7 @@ pub unsafe trait ReadOnlySystem: System { /// A convenience type alias for a boxed [`System`] trait object. pub type BoxedSystem = Box>; +#[cfg(feature = "debug")] pub(crate) fn check_system_change_tick(last_run: &mut Tick, this_run: Tick, system_name: &str) { if last_run.check_tick(this_run) { let age = this_run.relative_to(*last_run).get(); @@ -239,6 +243,7 @@ pub(crate) fn check_system_change_tick(last_run: &mut Tick, this_run: Tick, syst } } +#[cfg(feature = "debug")] impl Debug for dyn System where In: SystemInput + 'static, @@ -380,12 +385,15 @@ impl RunSystemOnce for &mut World { { let mut system: T::System = IntoSystem::into_system(system); system.initialize(self); - system - .validate_param(self) - .map_err(|err| RunSystemError::InvalidParams { + system.validate_param(self).map_err(|err| { + #[cfg(feature = "debug")] + return RunSystemError::InvalidParams { system: system.name(), err, - })?; + }; + #[cfg(not(feature = "debug"))] + return RunSystemError::AnonymousInvalidParams { err }; + })?; Ok(system.run(input, self)) } } @@ -402,6 +410,13 @@ pub enum RunSystemError { /// The returned parameter validation error. err: SystemParamValidationError, }, + /// System could not be run due to parameters that failed validation. + /// This should not be considered an error if [`field@SystemParamValidationError::skipped`] is `true`. + #[error("A system did not run due to failed parameter validation: {err}")] + AnonymousInvalidParams { + /// The returned parameter validation error. + err: SystemParamValidationError, + }, } #[cfg(test)] diff --git a/crates/bevy_ecs/src/system/system_param.rs b/crates/bevy_ecs/src/system/system_param.rs index fc795abf59d7a..67d13134cbd69 100644 --- a/crates/bevy_ecs/src/system/system_param.rs +++ b/crates/bevy_ecs/src/system/system_param.rs @@ -32,6 +32,7 @@ use core::{ ops::{Deref, DerefMut}, panic::Location, }; +#[cfg(feature = "debug")] use disqualified::ShortName; use thiserror::Error; @@ -351,8 +352,11 @@ pub(crate) fn init_query_param state: &QueryState, ) { assert_component_access_compatibility( + #[cfg(feature = "debug")] &system_meta.name, + #[cfg(feature = "debug")] core::any::type_name::(), + #[cfg(feature = "debug")] core::any::type_name::(), &system_meta.component_access_set, &state.component_access, @@ -363,10 +367,11 @@ pub(crate) fn init_query_param .add(state.component_access.clone()); } +#[cfg_attr(not(feature = "debug"), expect(unused_variables))] fn assert_component_access_compatibility( - system_name: &str, - query_type: &'static str, - filter_type: &'static str, + #[cfg(feature = "debug")] system_name: &str, + #[cfg(feature = "debug")] query_type: &'static str, + #[cfg(feature = "debug")] filter_type: &'static str, system_access: &FilteredAccessSet, current: &FilteredAccess, world: &World, @@ -375,12 +380,17 @@ fn assert_component_access_compatibility( if conflicts.is_empty() { return; } - let mut accesses = conflicts.format_conflict_list(world); - // Access list may be empty (if access to all components requested) - if !accesses.is_empty() { - accesses.push(' '); + #[cfg(feature = "debug")] + { + let mut accesses = conflicts.format_conflict_list(world); + // Access list may be empty (if access to all components requested) + if !accesses.is_empty() { + accesses.push(' '); + } + panic!("error[B0001]: Query<{}, {}> in system {system_name} accesses component(s) {accesses}in a way that conflicts with a previous system parameter. Consider using `Without` to create disjoint Queries or merging conflicting Queries into a `ParamSet`. See: https://bevy.org/learn/errors/b0001", ShortName(query_type), ShortName(filter_type)); } - panic!("error[B0001]: Query<{}, {}> in system {system_name} accesses component(s) {accesses}in a way that conflicts with a previous system parameter. Consider using `Without` to create disjoint Queries or merging conflicting Queries into a `ParamSet`. See: https://bevy.org/learn/errors/b0001", ShortName(query_type), ShortName(filter_type)); + #[cfg(not(feature = "debug"))] + panic!("error[B0001]: A query in a system accesses component(s) in a way that conflicts with a previous system parameter. Consider using `Without` to create disjoint Queries or merging conflicting Queries into a `ParamSet`. See: https://bevy.org/learn/errors/b0001"); } // SAFETY: Relevant query ComponentId access is applied to SystemMeta. If @@ -726,12 +736,18 @@ unsafe impl<'a, T: Resource> SystemParam for Res<'a, T> { let component_id = world.components_registrator().register_resource::(); let combined_access = system_meta.component_access_set.combined_access(); + #[cfg(feature = "debug")] assert!( !combined_access.has_resource_write(component_id), "error[B0002]: Res<{}> in system {} conflicts with a previous ResMut<{0}> access. Consider removing the duplicate access. See: https://bevy.org/learn/errors/b0002", core::any::type_name::(), system_meta.name, ); + #[cfg(not(feature = "debug"))] + assert!( + !combined_access.has_resource_write(component_id), + "error[B0002]: Res in a system conflicts with a previous ResMut access. Consider removing the duplicate access. See: https://bevy.org/learn/errors/b0002", + ); system_meta .component_access_set .add_unfiltered_resource_read(component_id); @@ -770,11 +786,14 @@ unsafe impl<'a, T: Resource> SystemParam for Res<'a, T> { world .get_resource_with_ticks(component_id) .unwrap_or_else(|| { + #[cfg(feature = "debug")] panic!( "Resource requested by {} does not exist: {}", system_meta.name, core::any::type_name::() - ) + ); + #[cfg(not(feature = "debug"))] + panic!("Resource requested by a system does not exist",); }); Res { value: ptr.deref(), @@ -800,13 +819,23 @@ unsafe impl<'a, T: Resource> SystemParam for ResMut<'a, T> { let combined_access = system_meta.component_access_set.combined_access(); if combined_access.has_resource_write(component_id) { + #[cfg(feature = "debug")] panic!( "error[B0002]: ResMut<{}> in system {} conflicts with a previous ResMut<{0}> access. Consider removing the duplicate access. See: https://bevy.org/learn/errors/b0002", core::any::type_name::(), system_meta.name); + #[cfg(not(feature = "debug"))] + panic!( + "error[B0002]: ResMut in a system conflicts with a previous ResMut access. Consider removing the duplicate access. See: https://bevy.org/learn/errors/b0002", + ); } else if combined_access.has_resource_read(component_id) { + #[cfg(feature = "debug")] panic!( "error[B0002]: ResMut<{}> in system {} conflicts with a previous Res<{0}> access. Consider removing the duplicate access. See: https://bevy.org/learn/errors/b0002", core::any::type_name::(), system_meta.name); + #[cfg(not(feature = "debug"))] + panic!( + "error[B0002]: ResMut in a system conflicts with a previous Res access. Consider removing the duplicate access. See: https://bevy.org/learn/errors/b0002", + ); } system_meta .component_access_set @@ -845,11 +874,14 @@ unsafe impl<'a, T: Resource> SystemParam for ResMut<'a, T> { let value = world .get_resource_mut_by_id(component_id) .unwrap_or_else(|| { + #[cfg(feature = "debug")] panic!( "Resource requested by {} does not exist: {}", system_meta.name, core::any::type_name::() - ) + ); + #[cfg(not(feature = "debug"))] + panic!("Resource requested by a system does not exist"); }); ResMut { value: value.value.deref_mut::(), @@ -904,6 +936,7 @@ unsafe impl<'w> SystemParam for DeferredWorld<'w> { type Item<'world, 'state> = DeferredWorld<'world>; fn init_state(_world: &mut World, system_meta: &mut SystemMeta) -> Self::State { + #[cfg(feature = "debug")] assert!( !system_meta .component_access_set @@ -912,6 +945,14 @@ unsafe impl<'w> SystemParam for DeferredWorld<'w> { "DeferredWorld in system {} conflicts with a previous access.", system_meta.name, ); + #[cfg(not(feature = "debug"))] + assert!( + !system_meta + .component_access_set + .combined_access() + .has_any_read(), + "DeferredWorld in a system conflicts with a previous access.", + ); system_meta.component_access_set.write_all(); } @@ -1355,12 +1396,19 @@ unsafe impl<'a, T: 'static> SystemParam for NonSend<'a, T> { let component_id = world.components_registrator().register_non_send::(); let combined_access = system_meta.component_access_set.combined_access(); + #[cfg(feature = "debug")] assert!( !combined_access.has_resource_write(component_id), "error[B0002]: NonSend<{}> in system {} conflicts with a previous mutable resource access ({0}). Consider removing the duplicate access. See: https://bevy.org/learn/errors/b0002", core::any::type_name::(), system_meta.name, ); + + #[cfg(not(feature = "debug"))] + assert!( + !combined_access.has_resource_write(component_id), + "error[B0002]: NonSend in a system conflicts with a previous mutable resource access. Consider removing the duplicate access. See: https://bevy.org/learn/errors/b0002", + ); system_meta .component_access_set .add_unfiltered_resource_read(component_id); @@ -1399,11 +1447,14 @@ unsafe impl<'a, T: 'static> SystemParam for NonSend<'a, T> { world .get_non_send_with_ticks(component_id) .unwrap_or_else(|| { + #[cfg(feature = "debug")] panic!( "Non-send resource requested by {} does not exist: {}", system_meta.name, core::any::type_name::() - ) + ); + #[cfg(not(feature = "debug"))] + panic!("Non-send resource requested by a system does not exist"); }); NonSend { @@ -1429,13 +1480,23 @@ unsafe impl<'a, T: 'static> SystemParam for NonSendMut<'a, T> { let combined_access = system_meta.component_access_set.combined_access(); if combined_access.has_component_write(component_id) { + #[cfg(feature = "debug")] panic!( "error[B0002]: NonSendMut<{}> in system {} conflicts with a previous mutable resource access ({0}). Consider removing the duplicate access. See: https://bevy.org/learn/errors/b0002", core::any::type_name::(), system_meta.name); + #[cfg(not(feature = "debug"))] + panic!( + "error[B0002]: NonSendMut in a system conflicts with a previous mutable resource access. Consider removing the duplicate access. See: https://bevy.org/learn/errors/b0002", + ); } else if combined_access.has_component_read(component_id) { + #[cfg(feature = "debug")] panic!( "error[B0002]: NonSendMut<{}> in system {} conflicts with a previous immutable resource access ({0}). Consider removing the duplicate access. See: https://bevy.org/learn/errors/b0002", core::any::type_name::(), system_meta.name); + #[cfg(not(feature = "debug"))] + panic!( + "error[B0002]: NonSendMut in a system conflicts with a previous immutable resource access. Consider removing the duplicate access. See: https://bevy.org/learn/errors/b0002", + ); } system_meta .component_access_set @@ -1475,11 +1536,14 @@ unsafe impl<'a, T: 'static> SystemParam for NonSendMut<'a, T> { world .get_non_send_with_ticks(component_id) .unwrap_or_else(|| { + #[cfg(feature = "debug")] panic!( "Non-send resource requested by {} does not exist: {}", system_meta.name, core::any::type_name::() - ) + ); + #[cfg(not(feature = "debug"))] + panic!("Non-send resource requested by a system does not exist",); }); NonSendMut { value: ptr.assert_unique().deref_mut(), @@ -2511,6 +2575,7 @@ pub struct SystemParamValidationError { /// A string identifying the invalid parameter. /// This is usually the type name of the parameter. + #[cfg(feature = "debug")] pub param: Cow<'static, str>, /// A string identifying the field within a parameter using `#[derive(SystemParam)]`. @@ -2543,6 +2608,7 @@ impl SystemParamValidationError { Self { skipped, message: message.into(), + #[cfg(feature = "debug")] param: Cow::Borrowed(core::any::type_name::()), field: field.into(), } @@ -2551,6 +2617,7 @@ impl SystemParamValidationError { impl Display for SystemParamValidationError { fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> { + #[cfg(feature = "debug")] write!( fmt, "Parameter `{}{}` failed validation: {}", diff --git a/crates/bevy_ecs/src/world/mod.rs b/crates/bevy_ecs/src/world/mod.rs index 4172f0b31d22d..da7acd0474c8f 100644 --- a/crates/bevy_ecs/src/world/mod.rs +++ b/crates/bevy_ecs/src/world/mod.rs @@ -3429,6 +3429,7 @@ impl World { let old = self.resource_mut::().insert(schedule); if old.is_some() { + #[cfg(feature = "debug")] warn!("Schedule `{label:?}` was inserted during a call to `World::schedule_scope`: its value has been overwritten"); } @@ -3796,13 +3797,15 @@ mod tests { let mut iter = world.iter_resources(); - let (info, ptr) = iter.next().unwrap(); - assert_eq!(info.name(), core::any::type_name::()); + let (_info, ptr) = iter.next().unwrap(); + #[cfg(feature = "debug")] + assert_eq!(_info.name(), core::any::type_name::()); // SAFETY: We know that the resource is of type `TestResource` assert_eq!(unsafe { ptr.deref::().0 }, 42); - let (info, ptr) = iter.next().unwrap(); - assert_eq!(info.name(), core::any::type_name::()); + let (_info, ptr) = iter.next().unwrap(); + #[cfg(feature = "debug")] + assert_eq!(_info.name(), core::any::type_name::()); assert_eq!( // SAFETY: We know that the resource is of type `TestResource2` unsafe { &ptr.deref::().0 }, @@ -3824,15 +3827,17 @@ mod tests { let mut iter = world.iter_resources_mut(); - let (info, mut mut_untyped) = iter.next().unwrap(); - assert_eq!(info.name(), core::any::type_name::()); + let (_info, mut mut_untyped) = iter.next().unwrap(); + #[cfg(feature = "debug")] + assert_eq!(_info.name(), core::any::type_name::()); // SAFETY: We know that the resource is of type `TestResource` unsafe { mut_untyped.as_mut().deref_mut::().0 = 43; }; - let (info, mut mut_untyped) = iter.next().unwrap(); - assert_eq!(info.name(), core::any::type_name::()); + let (_info, mut mut_untyped) = iter.next().unwrap(); + #[cfg(feature = "debug")] + assert_eq!(_info.name(), core::any::type_name::()); // SAFETY: We know that the resource is of type `TestResource2` unsafe { mut_untyped.as_mut().deref_mut::().0 = "Hello, world?".to_string(); diff --git a/crates/bevy_ecs/src/world/reflect.rs b/crates/bevy_ecs/src/world/reflect.rs index 5ecdf881563e4..3d8b94bd60e1c 100644 --- a/crates/bevy_ecs/src/world/reflect.rs +++ b/crates/bevy_ecs/src/world/reflect.rs @@ -2,10 +2,11 @@ use core::any::TypeId; -use thiserror::Error; - -use alloc::string::{String, ToString}; +use alloc::string::String; +#[cfg(feature = "debug")] +use alloc::string::ToString; use bevy_reflect::{Reflect, ReflectFromPtr}; +use thiserror::Error; use crate::{prelude::*, world::ComponentId}; @@ -77,10 +78,13 @@ impl World { }; let Some(comp_ptr) = self.get_by_id(entity, component_id) else { + #[cfg(feature = "debug")] let component_name = self .components() .get_name(component_id) .map(|name| name.to_string()); + #[cfg(not(feature = "debug"))] + let component_name = None; return Err(GetComponentReflectError::EntityDoesNotHaveComponent { entity, @@ -166,12 +170,16 @@ impl World { // HACK: Only required for the `None`-case/`else`-branch, but it borrows `self`, which will // already be mutably borrowed by `self.get_mut_by_id()`, and I didn't find a way around it. + #[cfg(feature = "debug")] let component_name = self .components() .get_name(component_id) .map(|name| name.to_string()); let Some(comp_mut_untyped) = self.get_mut_by_id(entity, component_id) else { + #[cfg(not(feature = "debug"))] + let component_name = None; + return Err(GetComponentReflectError::EntityDoesNotHaveComponent { entity, type_id, diff --git a/crates/bevy_internal/Cargo.toml b/crates/bevy_internal/Cargo.toml index 9f78fc009dcb1..6db5a32149011 100644 --- a/crates/bevy_internal/Cargo.toml +++ b/crates/bevy_internal/Cargo.toml @@ -346,6 +346,8 @@ web = [ hotpatching = ["bevy_app/hotpatching", "bevy_ecs/hotpatching"] +debug = ["bevy_ecs/debug", "bevy_scene/debug"] + [dependencies] # bevy (no_std) bevy_app = { path = "../bevy_app", version = "0.16.0-dev", default-features = false, features = [ diff --git a/crates/bevy_remote/Cargo.toml b/crates/bevy_remote/Cargo.toml index ca84c2916e972..21325eb3b5833 100644 --- a/crates/bevy_remote/Cargo.toml +++ b/crates/bevy_remote/Cargo.toml @@ -17,6 +17,7 @@ http = ["dep:async-io", "dep:smol-hyper"] bevy_app = { path = "../bevy_app", version = "0.16.0-dev" } bevy_derive = { path = "../bevy_derive", version = "0.16.0-dev" } bevy_ecs = { path = "../bevy_ecs", version = "0.16.0-dev", features = [ + "debug", "serialize", ] } bevy_reflect = { path = "../bevy_reflect", version = "0.16.0-dev" } diff --git a/crates/bevy_scene/Cargo.toml b/crates/bevy_scene/Cargo.toml index 8a6fe517fde04..c4cde29040107 100644 --- a/crates/bevy_scene/Cargo.toml +++ b/crates/bevy_scene/Cargo.toml @@ -17,6 +17,8 @@ serialize = [ "bevy_platform/serialize", ] +debug = ["bevy_ecs/debug"] + [dependencies] # bevy bevy_app = { path = "../bevy_app", version = "0.16.0-dev" } diff --git a/crates/bevy_scene/src/scene.rs b/crates/bevy_scene/src/scene.rs index 1d684c9dac2bb..f0b3a421abf0a 100644 --- a/crates/bevy_scene/src/scene.rs +++ b/crates/bevy_scene/src/scene.rs @@ -89,12 +89,14 @@ impl Scene { .type_id() .expect("reflected resources must have a type_id"); - let registration = - type_registry - .get(type_id) - .ok_or_else(|| SceneSpawnError::UnregisteredType { - std_type_name: component_info.name().to_string(), - })?; + let registration = type_registry.get(type_id).ok_or_else(|| { + #[cfg(feature = "debug")] + return SceneSpawnError::UnregisteredType { + std_type_name: component_info.name().to_string(), + }; + #[cfg(not(feature = "debug"))] + return SceneSpawnError::AnonymousUnregisteredType; + })?; let reflect_resource = registration.data::().ok_or_else(|| { SceneSpawnError::UnregisteredResource { type_path: registration.type_info().type_path().to_string(), @@ -132,8 +134,13 @@ impl Scene { let registration = type_registry .get(component_info.type_id().unwrap()) - .ok_or_else(|| SceneSpawnError::UnregisteredType { - std_type_name: component_info.name().to_string(), + .ok_or_else(|| { + #[cfg(feature = "debug")] + return SceneSpawnError::UnregisteredType { + std_type_name: component_info.name().to_string(), + }; + #[cfg(not(feature = "debug"))] + return SceneSpawnError::AnonymousUnregisteredType; })?; let reflect_component = registration.data::().ok_or_else(|| { diff --git a/crates/bevy_scene/src/scene_spawner.rs b/crates/bevy_scene/src/scene_spawner.rs index 1daa0158b3e49..19ec974cda78b 100644 --- a/crates/bevy_scene/src/scene_spawner.rs +++ b/crates/bevy_scene/src/scene_spawner.rs @@ -107,6 +107,13 @@ pub enum SceneSpawnError { /// The [type name](std::any::type_name) for the unregistered type. std_type_name: String, }, + /// Scene contains an unregistered type. + #[error( + "scene contains an unregistered type. \ + consider reflecting it with `#[derive(Reflect)]` \ + and registering the type using `app.register_type::()`" + )] + AnonymousUnregisteredType, /// Scene contains an unregistered type which has a `TypePath`. #[error( "scene contains the reflected type `{type_path}` but it was not found in the type registry. \ diff --git a/docs/cargo_features.md b/docs/cargo_features.md index e0f00f2f3de27..802fafabd5597 100644 --- a/docs/cargo_features.md +++ b/docs/cargo_features.md @@ -72,6 +72,7 @@ The default feature set enables most of the expected features of a game engine, |bmp|BMP image format support| |critical-section|`critical-section` provides the building blocks for synchronization primitives on all platforms, including `no_std`.| |dds|DDS compressed texture support| +|debug|Enable collecting debug information about systems and components to help with diagnostics| |debug_glam_assert|Enable assertions in debug builds to check the validity of parameters passed to glam| |default_no_std|Recommended defaults for no_std applications| |detailed_trace|Enable detailed trace event logging. These trace events are expensive even when off, thus they require compile time opt-in| diff --git a/examples/games/stepping.rs b/examples/games/stepping.rs index 653b7a12ff8f3..f9adef7b9bab4 100644 --- a/examples/games/stepping.rs +++ b/examples/games/stepping.rs @@ -104,7 +104,10 @@ fn build_ui( mut state: ResMut, ) { let mut text_spans = Vec::new(); - let mut always_run = Vec::new(); + let mut always_run: Vec<( + bevy_ecs::intern::Interned, + NodeId, + )> = Vec::new(); let Ok(schedule_order) = stepping.schedules() else { return; @@ -129,9 +132,10 @@ fn build_ui( return; }; - for (node_id, system) in systems { + for (node_id, _system) in systems { // skip bevy default systems; we don't want to step those - if system.name().starts_with("bevy") { + #[cfg(feature = "debug")] + if _system.name().starts_with("bevy") { always_run.push((*label, node_id)); continue; } @@ -149,8 +153,9 @@ fn build_ui( )); // add the name of the system to the ui + #[cfg(feature = "debug")] text_spans.push(( - TextSpan(format!("{}\n", system.name())), + TextSpan(format!("{}\n", _system.name())), TextFont::default(), TextColor(FONT_COLOR), )); From ea0afde8f9aa2514c19787f2c414b6d78ed5d18d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Mockers?= Date: Mon, 9 Jun 2025 19:01:52 +0200 Subject: [PATCH 02/17] enable it by default --- Cargo.toml | 1 + docs/cargo_features.md | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index c702feacb7098..7c9970e4ca648 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -164,6 +164,7 @@ default = [ "vorbis", "webgl2", "x11", + "debug", ] # Recommended defaults for no_std applications diff --git a/docs/cargo_features.md b/docs/cargo_features.md index 802fafabd5597..641e33aea9ba6 100644 --- a/docs/cargo_features.md +++ b/docs/cargo_features.md @@ -40,6 +40,7 @@ The default feature set enables most of the expected features of a game engine, |bevy_window|Windowing layer| |bevy_winit|winit window and input backend| |custom_cursor|Enable winit custom cursor support| +|debug|Enable collecting debug information about systems and components to help with diagnostics| |default_font|Include a default font, containing only ASCII characters, at the cost of a 20kB binary size increase| |hdr|HDR image format support| |ktx2|KTX2 compressed texture support| @@ -72,7 +73,6 @@ The default feature set enables most of the expected features of a game engine, |bmp|BMP image format support| |critical-section|`critical-section` provides the building blocks for synchronization primitives on all platforms, including `no_std`.| |dds|DDS compressed texture support| -|debug|Enable collecting debug information about systems and components to help with diagnostics| |debug_glam_assert|Enable assertions in debug builds to check the validity of parameters passed to glam| |default_no_std|Recommended defaults for no_std applications| |detailed_trace|Enable detailed trace event logging. These trace events are expensive even when off, thus they require compile time opt-in| From 05badceb3d4a68779359544e16d3dab1d07128ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Mockers?= Date: Mon, 9 Jun 2025 19:07:26 +0200 Subject: [PATCH 03/17] typo --- crates/bevy_ecs/src/schedule/schedule.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_ecs/src/schedule/schedule.rs b/crates/bevy_ecs/src/schedule/schedule.rs index 704a5ee5fc545..cd7c0d17913d4 100644 --- a/crates/bevy_ecs/src/schedule/schedule.rs +++ b/crates/bevy_ecs/src/schedule/schedule.rs @@ -1732,7 +1732,7 @@ impl ScheduleGraph { #[cfg(feature = "debug")] let message = self.get_hierarchy_conflicts_error_message(transitive_edges); #[cfg(not(feature = "debug"))] - let message = "Enable debug feature for more informations".to_string(); + let message = "Enable debug feature for more information".to_string(); match self.settings.hierarchy_detection { LogLevel::Ignore => unreachable!(), LogLevel::Warn => { From 0fff7e1d9e6c656069782785ce50396e95390a55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Mockers?= Date: Mon, 9 Jun 2025 19:08:06 +0200 Subject: [PATCH 04/17] format --- Cargo.toml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 7c9970e4ca648..561b97dfd065f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -495,7 +495,10 @@ file_watcher = ["bevy_internal/file_watcher"] embedded_watcher = ["bevy_internal/embedded_watcher"] # Enable stepping-based debugging of Bevy systems -bevy_debug_stepping = ["bevy_internal/bevy_debug_stepping", "bevy_internal/debug"] +bevy_debug_stepping = [ + "bevy_internal/bevy_debug_stepping", + "bevy_internal/debug" +] # Enables the meshlet renderer for dense high-poly scenes (experimental) meshlet = ["bevy_internal/meshlet"] From e71b6dfb08f649faa761a4e4e9813b888ce18310 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Mockers?= Date: Mon, 9 Jun 2025 19:36:47 +0200 Subject: [PATCH 05/17] format --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 561b97dfd065f..3a008ce2cb35d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -497,7 +497,7 @@ embedded_watcher = ["bevy_internal/embedded_watcher"] # Enable stepping-based debugging of Bevy systems bevy_debug_stepping = [ "bevy_internal/bevy_debug_stepping", - "bevy_internal/debug" + "bevy_internal/debug", ] # Enables the meshlet renderer for dense high-poly scenes (experimental) From c09a29a184503652a80d663afd40f64419d54752 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Mockers?= Date: Mon, 9 Jun 2025 19:39:41 +0200 Subject: [PATCH 06/17] no std --- crates/bevy_ecs/src/schedule/schedule.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/crates/bevy_ecs/src/schedule/schedule.rs b/crates/bevy_ecs/src/schedule/schedule.rs index cd7c0d17913d4..f8266127e18fb 100644 --- a/crates/bevy_ecs/src/schedule/schedule.rs +++ b/crates/bevy_ecs/src/schedule/schedule.rs @@ -4,13 +4,11 @@ )] #[cfg(feature = "debug")] use alloc::borrow::Cow; -#[cfg(feature = "debug")] -use alloc::string::ToString; use alloc::{ boxed::Box, collections::{BTreeMap, BTreeSet}, format, - string::String, + string::{String, ToString}, vec, vec::Vec, }; @@ -29,8 +27,6 @@ use log::error; #[cfg(feature = "debug")] use log::{info, warn}; use pass::ScheduleBuildPassObj; -#[cfg(not(feature = "debug"))] -use std::string::ToString; use thiserror::Error; #[cfg(all(feature = "trace", feature = "debug"))] use tracing::info_span; From c50c6190927596ac8c3fe7343b68781e64043867 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Mockers?= Date: Mon, 9 Jun 2025 19:41:30 +0200 Subject: [PATCH 07/17] debug for miri --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 37db848558d6f..7f86699b39a4c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -95,7 +95,7 @@ jobs: - name: CI job # To run the tests one item at a time for troubleshooting, use # cargo --quiet test --lib -- --list | sed 's/: test$//' | MIRIFLAGS="-Zmiri-disable-isolation -Zmiri-disable-weak-memory-emulation" xargs -n1 cargo miri test -p bevy_ecs --lib -- --exact - run: cargo miri test -p bevy_ecs + run: cargo miri test -p bevy_ecs --features debug env: # -Zrandomize-layout makes sure we dont rely on the layout of anything that might change RUSTFLAGS: -Zrandomize-layout From f540e11e407625205f5f61749d93b0616bf8166c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Mockers?= Date: Mon, 9 Jun 2025 23:57:30 +0200 Subject: [PATCH 08/17] feature name --- crates/bevy_ecs/src/component.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_ecs/src/component.rs b/crates/bevy_ecs/src/component.rs index 3802874f380a6..fe3a0becfbee2 100644 --- a/crates/bevy_ecs/src/component.rs +++ b/crates/bevy_ecs/src/component.rs @@ -3044,7 +3044,7 @@ pub fn enforce_no_required_components_recursion( ); #[cfg(not(feature = "debug"))] panic!( - "Recursive required components detected\n help: {}\nEnable the default feature to get more details", + "Recursive required components detected\n help: {}\nEnable the debug feature to get more details", if direct_recursion { "Remove the recursing required component." } else { From 463c0b053eea9e7d95fce44a90c874edfcd66ecf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Mockers?= Date: Sat, 14 Jun 2025 02:52:04 +0200 Subject: [PATCH 09/17] fix merge --- crates/bevy_ecs/src/schedule/schedule.rs | 8 +-- crates/bevy_ecs/src/system/builder.rs | 68 ------------------- .../src/system/exclusive_function_system.rs | 6 +- crates/bevy_ecs/src/system/function_system.rs | 6 +- crates/bevy_ecs/src/system/system.rs | 6 -- crates/bevy_ecs/src/system/system_param.rs | 43 ++---------- 6 files changed, 7 insertions(+), 130 deletions(-) diff --git a/crates/bevy_ecs/src/schedule/schedule.rs b/crates/bevy_ecs/src/schedule/schedule.rs index db9bd69cde74a..135ecf8a9bfe9 100644 --- a/crates/bevy_ecs/src/schedule/schedule.rs +++ b/crates/bevy_ecs/src/schedule/schedule.rs @@ -31,16 +31,10 @@ use thiserror::Error; #[cfg(all(feature = "trace", feature = "debug"))] use tracing::info_span; -<<<<<<< remove-debug-strings #[cfg(feature = "debug")] use crate::component::Components; use crate::{ - component::{ComponentId, Tick}, -======= -use crate::component::CheckChangeTicks; -use crate::{ - component::{ComponentId, Components}, ->>>>>>> main + component::{CheckChangeTicks, ComponentId}, prelude::Component, query::FilteredAccessSet, resource::Resource, diff --git a/crates/bevy_ecs/src/system/builder.rs b/crates/bevy_ecs/src/system/builder.rs index 11a70046781b5..7aea7313143a4 100644 --- a/crates/bevy_ecs/src/system/builder.rs +++ b/crates/bevy_ecs/src/system/builder.rs @@ -538,36 +538,7 @@ unsafe impl<'w, 's, T: FnOnce(&mut FilteredResourcesBuilder)> fn build(self, world: &mut World) -> as SystemParam>::State { let mut builder = FilteredResourcesBuilder::new(world); (self.0)(&mut builder); -<<<<<<< remove-debug-strings - let access = builder.build(); - - let combined_access = meta.component_access_set.combined_access(); - let conflicts = combined_access.get_conflicts(&access); - if !conflicts.is_empty() { - #[cfg(feature = "debug")] - { - let accesses = conflicts.format_conflict_list(world); - let system_name = &meta.name; - panic!("error[B0002]: FilteredResources in system {system_name} accesses resources(s){accesses} in a way that conflicts with a previous system parameter. Consider removing the duplicate access. See: https://bevy.org/learn/errors/b0002"); - } - #[cfg(not(feature = "debug"))] - panic!("error[B0002]: FilteredResources in a system accesses resources(s) in a way that conflicts with a previous system parameter. Consider removing the duplicate access. See: https://bevy.org/learn/errors/b0002"); - } - - if access.has_read_all_resources() { - meta.component_access_set - .add_unfiltered_read_all_resources(); - } else { - for component_id in access.resource_reads_and_writes() { - meta.component_access_set - .add_unfiltered_resource_read(component_id); - } - } - - access -======= builder.build() ->>>>>>> main } } @@ -601,46 +572,7 @@ unsafe impl<'w, 's, T: FnOnce(&mut FilteredResourcesMutBuilder)> fn build(self, world: &mut World) -> as SystemParam>::State { let mut builder = FilteredResourcesMutBuilder::new(world); (self.0)(&mut builder); -<<<<<<< remove-debug-strings - let access = builder.build(); - - let combined_access = meta.component_access_set.combined_access(); - let conflicts = combined_access.get_conflicts(&access); - if !conflicts.is_empty() { - #[cfg(feature = "debug")] - { - let accesses = conflicts.format_conflict_list(world); - let system_name = &meta.name; - panic!("error[B0002]: FilteredResourcesMut in system {system_name} accesses resources(s){accesses} in a way that conflicts with a previous system parameter. Consider removing the duplicate access. See: https://bevy.org/learn/errors/b0002"); - } - #[cfg(not(feature = "debug"))] - panic!("error[B0002]: FilteredResourcesMut in a system accesses resources(s) in a way that conflicts with a previous system parameter. Consider removing the duplicate access. See: https://bevy.org/learn/errors/b0002"); - } - - if access.has_read_all_resources() { - meta.component_access_set - .add_unfiltered_read_all_resources(); - } else { - for component_id in access.resource_reads() { - meta.component_access_set - .add_unfiltered_resource_read(component_id); - } - } - - if access.has_write_all_resources() { - meta.component_access_set - .add_unfiltered_write_all_resources(); - } else { - for component_id in access.resource_writes() { - meta.component_access_set - .add_unfiltered_resource_write(component_id); - } - } - - access -======= builder.build() ->>>>>>> main } } diff --git a/crates/bevy_ecs/src/system/exclusive_function_system.rs b/crates/bevy_ecs/src/system/exclusive_function_system.rs index caa563d89e4bc..1211b56c2e174 100644 --- a/crates/bevy_ecs/src/system/exclusive_function_system.rs +++ b/crates/bevy_ecs/src/system/exclusive_function_system.rs @@ -183,13 +183,9 @@ where } #[inline] -<<<<<<< remove-debug-strings #[cfg_attr(not(feature = "debug"), expect(unused_variables))] - fn check_change_tick(&mut self, change_tick: Tick) { - #[cfg(feature = "debug")] -======= fn check_change_tick(&mut self, check: CheckChangeTicks) { ->>>>>>> main + #[cfg(feature = "debug")] check_system_change_tick( &mut self.system_meta.last_run, check, diff --git a/crates/bevy_ecs/src/system/function_system.rs b/crates/bevy_ecs/src/system/function_system.rs index 4317829b6c18a..236adfe6a159a 100644 --- a/crates/bevy_ecs/src/system/function_system.rs +++ b/crates/bevy_ecs/src/system/function_system.rs @@ -716,13 +716,9 @@ where } #[inline] -<<<<<<< remove-debug-strings #[cfg_attr(not(feature = "debug"), expect(unused_variables))] - fn check_change_tick(&mut self, change_tick: Tick) { - #[cfg(feature = "debug")] -======= fn check_change_tick(&mut self, check: CheckChangeTicks) { ->>>>>>> main + #[cfg(feature = "debug")] check_system_change_tick( &mut self.system_meta.last_run, check, diff --git a/crates/bevy_ecs/src/system/system.rs b/crates/bevy_ecs/src/system/system.rs index c88d298b1e5dd..cb6efe38a689f 100644 --- a/crates/bevy_ecs/src/system/system.rs +++ b/crates/bevy_ecs/src/system/system.rs @@ -227,12 +227,7 @@ pub unsafe trait ReadOnlySystem: System { /// A convenience type alias for a boxed [`System`] trait object. pub type BoxedSystem = Box>; -<<<<<<< remove-debug-strings #[cfg(feature = "debug")] -pub(crate) fn check_system_change_tick(last_run: &mut Tick, this_run: Tick, system_name: &str) { - if last_run.check_tick(this_run) { - let age = this_run.relative_to(*last_run).get(); -======= pub(crate) fn check_system_change_tick( last_run: &mut Tick, check: CheckChangeTicks, @@ -240,7 +235,6 @@ pub(crate) fn check_system_change_tick( ) { if last_run.check_tick(check) { let age = check.present_tick().relative_to(*last_run).get(); ->>>>>>> main warn!( "System '{system_name}' has not run for {age} ticks. \ Changes older than {} ticks will not be detected.", diff --git a/crates/bevy_ecs/src/system/system_param.rs b/crates/bevy_ecs/src/system/system_param.rs index 0b2d60cdaecf9..8353337ef316f 100644 --- a/crates/bevy_ecs/src/system/system_param.rs +++ b/crates/bevy_ecs/src/system/system_param.rs @@ -761,10 +761,6 @@ unsafe impl<'a, T: Resource> SystemParam for Res<'a, T> { world.components_registrator().register_resource::() } -<<<<<<< remove-debug-strings - let combined_access = system_meta.component_access_set.combined_access(); - #[cfg(feature = "debug")] -======= fn init_access( &component_id: &Self::State, system_meta: &mut SystemMeta, @@ -772,27 +768,20 @@ unsafe impl<'a, T: Resource> SystemParam for Res<'a, T> { _world: &mut World, ) { let combined_access = component_access_set.combined_access(); ->>>>>>> main + #[cfg(feature = "debug")] assert!( !combined_access.has_resource_write(component_id), "error[B0002]: Res<{}> in system {} conflicts with a previous ResMut<{0}> access. Consider removing the duplicate access. See: https://bevy.org/learn/errors/b0002", core::any::type_name::(), system_meta.name, ); -<<<<<<< remove-debug-strings #[cfg(not(feature = "debug"))] assert!( !combined_access.has_resource_write(component_id), "error[B0002]: Res in a system conflicts with a previous ResMut access. Consider removing the duplicate access. See: https://bevy.org/learn/errors/b0002", ); - system_meta - .component_access_set - .add_unfiltered_resource_read(component_id); - component_id -======= component_access_set.add_unfiltered_resource_read(component_id); ->>>>>>> main } #[inline] @@ -984,10 +973,6 @@ unsafe impl<'w> SystemParam for DeferredWorld<'w> { type State = (); type Item<'world, 'state> = DeferredWorld<'world>; -<<<<<<< remove-debug-strings - fn init_state(_world: &mut World, system_meta: &mut SystemMeta) -> Self::State { - #[cfg(feature = "debug")] -======= fn init_state(_world: &mut World) -> Self::State {} fn init_access( @@ -996,25 +981,18 @@ unsafe impl<'w> SystemParam for DeferredWorld<'w> { component_access_set: &mut FilteredAccessSet, _world: &mut World, ) { ->>>>>>> main + #[cfg(feature = "debug")] assert!( !component_access_set.combined_access().has_any_read(), "DeferredWorld in system {} conflicts with a previous access.", system_meta.name, ); -<<<<<<< remove-debug-strings #[cfg(not(feature = "debug"))] assert!( - !system_meta - .component_access_set - .combined_access() - .has_any_read(), + !component_access_set.combined_access().has_any_read(), "DeferredWorld in a system conflicts with a previous access.", ); - system_meta.component_access_set.write_all(); -======= component_access_set.write_all(); ->>>>>>> main } unsafe fn get_param<'world, 'state>( @@ -1486,33 +1464,20 @@ unsafe impl<'a, T: 'static> SystemParam for NonSend<'a, T> { ) { system_meta.set_non_send(); -<<<<<<< remove-debug-strings - let combined_access = system_meta.component_access_set.combined_access(); - #[cfg(feature = "debug")] -======= let combined_access = component_access_set.combined_access(); ->>>>>>> main + #[cfg(feature = "debug")] assert!( !combined_access.has_resource_write(component_id), "error[B0002]: NonSend<{}> in system {} conflicts with a previous mutable resource access ({0}). Consider removing the duplicate access. See: https://bevy.org/learn/errors/b0002", core::any::type_name::(), system_meta.name, ); -<<<<<<< remove-debug-strings - #[cfg(not(feature = "debug"))] assert!( !combined_access.has_resource_write(component_id), "error[B0002]: NonSend in a system conflicts with a previous mutable resource access. Consider removing the duplicate access. See: https://bevy.org/learn/errors/b0002", ); - system_meta - .component_access_set - .add_unfiltered_resource_read(component_id); - - component_id -======= component_access_set.add_unfiltered_resource_read(component_id); ->>>>>>> main } #[inline] From d059d15adb97f1f0c35194b23d4f57ae84966988 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Mockers?= Date: Sat, 14 Jun 2025 03:13:26 +0200 Subject: [PATCH 10/17] some more merge fixes --- crates/bevy_ecs/src/system/system_param.rs | 32 ++++++++++++++++++---- 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/crates/bevy_ecs/src/system/system_param.rs b/crates/bevy_ecs/src/system/system_param.rs index 8353337ef316f..d480799273810 100644 --- a/crates/bevy_ecs/src/system/system_param.rs +++ b/crates/bevy_ecs/src/system/system_param.rs @@ -336,6 +336,7 @@ unsafe impl SystemParam for Qu QueryState::new(world) } + #[cfg_attr(not(feature = "debug"), expect(unused_variables))] fn init_access( state: &Self::State, system_meta: &mut SystemMeta, @@ -343,8 +344,11 @@ unsafe impl SystemParam for Qu world: &mut World, ) { assert_component_access_compatibility( + #[cfg(feature = "debug")] &system_meta.name, + #[cfg(feature = "debug")] core::any::type_name::(), + #[cfg(feature = "debug")] core::any::type_name::(), component_access_set, &state.component_access, @@ -368,6 +372,7 @@ unsafe impl SystemParam for Qu } } +#[cfg_attr(not(feature = "debug"), expect(unused_variables))] fn assert_component_access_compatibility( #[cfg(feature = "debug")] system_name: &str, #[cfg(feature = "debug")] query_type: &'static str, @@ -761,6 +766,7 @@ unsafe impl<'a, T: Resource> SystemParam for Res<'a, T> { world.components_registrator().register_resource::() } + #[cfg_attr(not(feature = "debug"), expect(unused_variables))] fn init_access( &component_id: &Self::State, system_meta: &mut SystemMeta, @@ -847,6 +853,7 @@ unsafe impl<'a, T: Resource> SystemParam for ResMut<'a, T> { world.components_registrator().register_resource::() } + #[cfg_attr(not(feature = "debug"), expect(unused_variables))] fn init_access( &component_id: &Self::State, system_meta: &mut SystemMeta, @@ -975,6 +982,7 @@ unsafe impl<'w> SystemParam for DeferredWorld<'w> { fn init_state(_world: &mut World) -> Self::State {} + #[cfg_attr(not(feature = "debug"), expect(unused_variables))] fn init_access( _state: &Self::State, system_meta: &mut SystemMeta, @@ -2723,6 +2731,7 @@ unsafe impl SystemParam for FilteredResources<'_, '_> { Access::new() } + #[cfg_attr(not(feature = "debug"), expect(unused_variables))] fn init_access( access: &Self::State, system_meta: &mut SystemMeta, @@ -2732,9 +2741,14 @@ unsafe impl SystemParam for FilteredResources<'_, '_> { let combined_access = component_access_set.combined_access(); let conflicts = combined_access.get_conflicts(access); if !conflicts.is_empty() { - let accesses = conflicts.format_conflict_list(world); - let system_name = &system_meta.name; - panic!("error[B0002]: FilteredResources in system {system_name} accesses resources(s){accesses} in a way that conflicts with a previous system parameter. Consider removing the duplicate access. See: https://bevy.org/learn/errors/b0002"); + #[cfg(feature = "debug")] + { + let accesses = conflicts.format_conflict_list(world); + let system_name = &system_meta.name; + panic!("error[B0002]: FilteredResources in system {system_name} accesses resources(s){accesses} in a way that conflicts with a previous system parameter. Consider removing the duplicate access. See: https://bevy.org/learn/errors/b0002"); + } + #[cfg(not(feature = "debug"))] + panic!("error[B0002]: FilteredResources in a system accesses resources(s) in a way that conflicts with a previous system parameter. Consider removing the duplicate access. See: https://bevy.org/learn/errors/b0002"); } if access.has_read_all_resources() { @@ -2772,6 +2786,7 @@ unsafe impl SystemParam for FilteredResourcesMut<'_, '_> { Access::new() } + #[cfg_attr(not(feature = "debug"), expect(unused_variables))] fn init_access( access: &Self::State, system_meta: &mut SystemMeta, @@ -2781,9 +2796,14 @@ unsafe impl SystemParam for FilteredResourcesMut<'_, '_> { let combined_access = component_access_set.combined_access(); let conflicts = combined_access.get_conflicts(access); if !conflicts.is_empty() { - let accesses = conflicts.format_conflict_list(world); - let system_name = &system_meta.name; - panic!("error[B0002]: FilteredResourcesMut in system {system_name} accesses resources(s){accesses} in a way that conflicts with a previous system parameter. Consider removing the duplicate access. See: https://bevy.org/learn/errors/b0002"); + #[cfg(feature = "debug")] + { + let accesses = conflicts.format_conflict_list(world); + let system_name = &system_meta.name; + panic!("error[B0002]: FilteredResourcesMut in system {system_name} accesses resources(s){accesses} in a way that conflicts with a previous system parameter. Consider removing the duplicate access. See: https://bevy.org/learn/errors/b0002"); + } + #[cfg(not(feature = "debug"))] + panic!("error[B0002]: FilteredResourcesMut in a system accesses resources(s) in a way that conflicts with a previous system parameter. Consider removing the duplicate access. See: https://bevy.org/learn/errors/b0002"); } if access.has_read_all_resources() { From 6d8765d98ed210dfedb217c364acea55ac5592ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Mockers?= Date: Tue, 17 Jun 2025 01:42:10 +0200 Subject: [PATCH 11/17] DebugName --- crates/bevy_ecs/Cargo.toml | 5 +- crates/bevy_ecs/src/bundle.rs | 24 ++- crates/bevy_ecs/src/component.rs | 64 +++----- crates/bevy_ecs/src/entity/clone_entities.rs | 4 +- crates/bevy_ecs/src/error/command_handling.rs | 14 +- crates/bevy_ecs/src/error/handler.rs | 34 +---- crates/bevy_ecs/src/hierarchy.rs | 5 +- crates/bevy_ecs/src/observer/runner.rs | 15 -- crates/bevy_ecs/src/query/access.rs | 20 +-- crates/bevy_ecs/src/query/error.rs | 5 +- crates/bevy_ecs/src/query/fetch.rs | 15 +- crates/bevy_ecs/src/query/filter.rs | 5 +- crates/bevy_ecs/src/query/state.rs | 5 +- crates/bevy_ecs/src/reflect/bundle.rs | 5 +- crates/bevy_ecs/src/reflect/component.rs | 11 +- crates/bevy_ecs/src/reflect/mod.rs | 3 +- crates/bevy_ecs/src/relationship/mod.rs | 9 +- crates/bevy_ecs/src/schedule/condition.rs | 66 ++------ crates/bevy_ecs/src/schedule/config.rs | 6 - crates/bevy_ecs/src/schedule/executor/mod.rs | 8 +- .../src/schedule/executor/multi_threaded.rs | 23 +-- .../bevy_ecs/src/schedule/executor/simple.rs | 14 +- .../src/schedule/executor/single_threaded.rs | 14 +- crates/bevy_ecs/src/schedule/mod.rs | 7 +- crates/bevy_ecs/src/schedule/schedule.rs | 91 +++-------- crates/bevy_ecs/src/schedule/set.rs | 8 +- crates/bevy_ecs/src/schedule/stepping.rs | 2 +- crates/bevy_ecs/src/storage/resource.rs | 22 +-- crates/bevy_ecs/src/system/adapter_system.rs | 30 +--- crates/bevy_ecs/src/system/combinator.rs | 44 ++---- .../src/system/commands/entity_command.rs | 11 +- .../src/system/exclusive_function_system.rs | 21 +-- crates/bevy_ecs/src/system/function_system.rs | 34 ++--- crates/bevy_ecs/src/system/mod.rs | 2 - crates/bevy_ecs/src/system/observer_system.rs | 6 +- crates/bevy_ecs/src/system/query.rs | 6 +- crates/bevy_ecs/src/system/schedule_system.rs | 12 +- crates/bevy_ecs/src/system/system.rs | 31 ++-- crates/bevy_ecs/src/system/system_name.rs | 29 ++-- crates/bevy_ecs/src/system/system_param.rs | 141 ++++-------------- crates/bevy_ecs/src/world/deferred_world.rs | 8 +- crates/bevy_ecs/src/world/error.rs | 3 +- crates/bevy_ecs/src/world/mod.rs | 45 +++--- crates/bevy_ecs/src/world/reflect.rs | 26 +--- crates/bevy_internal/Cargo.toml | 2 +- crates/bevy_remote/Cargo.toml | 5 +- crates/bevy_scene/Cargo.toml | 2 - crates/bevy_scene/src/scene.rs | 10 +- crates/bevy_scene/src/scene_spawner.rs | 3 +- crates/bevy_utils/Cargo.toml | 5 + crates/bevy_utils/src/debug_info.rs | 98 ++++++++++++ crates/bevy_utils/src/lib.rs | 2 + examples/games/stepping.rs | 8 +- 53 files changed, 379 insertions(+), 709 deletions(-) create mode 100644 crates/bevy_utils/src/debug_info.rs diff --git a/crates/bevy_ecs/Cargo.toml b/crates/bevy_ecs/Cargo.toml index debe2c4133218..4f96e4c4ed4d2 100644 --- a/crates/bevy_ecs/Cargo.toml +++ b/crates/bevy_ecs/Cargo.toml @@ -63,9 +63,9 @@ std = [ "bevy_reflect?/std", "bevy_tasks/std", "bevy_utils/parallel", + "bevy_utils/std", "bitflags/std", "concurrent-queue/std", - "disqualified/alloc", "fixedbitset/std", "indexmap/std", "serde?/std", @@ -85,8 +85,6 @@ critical-section = [ hotpatching = ["dep:subsecond"] -debug = [] - [dependencies] bevy_ptr = { path = "../bevy_ptr", version = "0.16.0-dev" } bevy_reflect = { path = "../bevy_reflect", version = "0.16.0-dev", features = [ @@ -100,7 +98,6 @@ bevy_platform = { path = "../bevy_platform", version = "0.16.0-dev", default-fea ] } bitflags = { version = "2.3", default-features = false } -disqualified = { version = "1.0", default-features = false } fixedbitset = { version = "0.5", default-features = false } serde = { version = "1", default-features = false, features = [ "alloc", diff --git a/crates/bevy_ecs/src/bundle.rs b/crates/bevy_ecs/src/bundle.rs index f371acb5ce5a9..2687f7eb16ad0 100644 --- a/crates/bevy_ecs/src/bundle.rs +++ b/crates/bevy_ecs/src/bundle.rs @@ -544,21 +544,15 @@ impl BundleInfo { } } - #[cfg(feature = "debug")] - { - let names = dups - .into_iter() - .map(|id| { - // SAFETY: the caller ensures component_id is valid. - unsafe { components.get_info_unchecked(id).name() } - }) - .collect::>() - .join(", "); - - panic!("Bundle {bundle_type_name} has duplicate components: {names}"); - } - #[cfg(not(feature = "debug"))] - panic!("Bundle {bundle_type_name} has duplicate components"); + let names = dups + .into_iter() + .map(|id| { + // SAFETY: the caller ensures component_id is valid. + unsafe { components.get_info_unchecked(id).name() } + }) + .collect::>(); + + panic!("Bundle {bundle_type_name} has duplicate components: {names:?}"); } // handle explicit components diff --git a/crates/bevy_ecs/src/component.rs b/crates/bevy_ecs/src/component.rs index 810dcc22058ed..cfcde29ab2ff1 100644 --- a/crates/bevy_ecs/src/component.rs +++ b/crates/bevy_ecs/src/component.rs @@ -13,9 +13,7 @@ use crate::{ world::{FromWorld, World}, }; use alloc::boxed::Box; -#[cfg(feature = "debug")] -use alloc::format; -use alloc::{borrow::Cow, vec::Vec}; +use alloc::{borrow::Cow, format, vec::Vec}; pub use bevy_ecs_macros::Component; use bevy_ecs_macros::Event; use bevy_platform::sync::Arc; @@ -26,7 +24,7 @@ use bevy_platform::{ use bevy_ptr::{OwningPtr, UnsafeCellDeref}; #[cfg(feature = "bevy_reflect")] use bevy_reflect::Reflect; -use bevy_utils::TypeIdMap; +use bevy_utils::{prelude::DebugName, TypeIdMap}; use core::{ alloc::Layout, any::{Any, TypeId}, @@ -36,8 +34,6 @@ use core::{ mem::needs_drop, ops::{Deref, DerefMut}, }; -#[cfg(feature = "debug")] -use disqualified::ShortName; use smallvec::SmallVec; use thiserror::Error; @@ -663,8 +659,7 @@ pub enum StorageType { } /// Stores metadata for a type of component or resource stored in a specific [`World`]. -#[derive(Clone)] -#[cfg_attr(feature = "debug", derive(Debug))] +#[derive(Debug, Clone)] pub struct ComponentInfo { id: ComponentId, descriptor: ComponentDescriptor, @@ -681,10 +676,9 @@ impl ComponentInfo { } /// Returns the name of the current component. - #[cfg(feature = "debug")] #[inline] - pub fn name(&self) -> &str { - &self.descriptor.name + pub fn name(&self) -> DebugName { + self.descriptor.name.clone() } /// Returns `true` if the current component is mutable. @@ -841,8 +835,7 @@ impl SparseSetIndex for ComponentId { /// A value describing a component or resource, which may or may not correspond to a Rust type. #[derive(Clone)] pub struct ComponentDescriptor { - #[cfg(feature = "debug")] - name: Cow<'static, str>, + name: DebugName, // SAFETY: This must remain private. It must match the statically known StorageType of the // associated rust component type if one exists. storage_type: StorageType, @@ -860,7 +853,6 @@ pub struct ComponentDescriptor { } // We need to ignore the `drop` field in our `Debug` impl -#[cfg(feature = "debug")] impl Debug for ComponentDescriptor { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { f.debug_struct("ComponentDescriptor") @@ -889,8 +881,7 @@ impl ComponentDescriptor { /// Create a new `ComponentDescriptor` for the type `T`. pub fn new() -> Self { Self { - #[cfg(feature = "debug")] - name: Cow::Borrowed(core::any::type_name::()), + name: DebugName::type_name::(), storage_type: T::STORAGE_TYPE, is_send_and_sync: true, type_id: Some(TypeId::of::()), @@ -906,7 +897,6 @@ impl ComponentDescriptor { /// # Safety /// - the `drop` fn must be usable on a pointer with a value of the layout `layout` /// - the component type must be safe to access from any thread (Send + Sync in rust terms) - #[cfg_attr(not(feature = "debug"), expect(unused_variables))] pub unsafe fn new_with_layout( name: impl Into>, storage_type: StorageType, @@ -916,8 +906,7 @@ impl ComponentDescriptor { clone_behavior: ComponentCloneBehavior, ) -> Self { Self { - #[cfg(feature = "debug")] - name: name.into(), + name: name.into().into(), storage_type, is_send_and_sync: true, type_id: None, @@ -933,8 +922,7 @@ impl ComponentDescriptor { /// The [`StorageType`] for resources is always [`StorageType::Table`]. pub fn new_resource() -> Self { Self { - #[cfg(feature = "debug")] - name: Cow::Borrowed(core::any::type_name::()), + name: DebugName::type_name::(), // PERF: `SparseStorage` may actually be a more // reasonable choice as `storage_type` for resources. storage_type: StorageType::Table, @@ -949,8 +937,7 @@ impl ComponentDescriptor { fn new_non_send(storage_type: StorageType) -> Self { Self { - #[cfg(feature = "debug")] - name: Cow::Borrowed(core::any::type_name::()), + name: DebugName::type_name::(), storage_type, is_send_and_sync: false, type_id: Some(TypeId::of::()), @@ -976,9 +963,8 @@ impl ComponentDescriptor { /// Returns the name of the current component. #[inline] - #[cfg(feature = "debug")] - pub fn name(&self) -> &str { - self.name.as_ref() + pub fn name(&self) -> DebugName { + self.name.clone() } /// Returns whether this component is mutable. @@ -1743,8 +1729,7 @@ impl<'w> ComponentsRegistrator<'w> { } /// Stores metadata associated with each kind of [`Component`] in a given [`World`]. -#[derive(Default)] -#[cfg_attr(feature = "debug", derive(Debug))] +#[derive(Debug, Default)] pub struct Components { components: Vec>, indices: TypeIdMap, @@ -1868,14 +1853,10 @@ impl Components { /// /// This will return an incorrect result if `id` did not come from the same world as `self`. It may return `None` or a garbage value. #[inline] - #[cfg(feature = "debug")] - pub fn get_name<'a>(&'a self, id: ComponentId) -> Option> { + pub fn get_name<'a>(&'a self, id: ComponentId) -> Option { self.components .get(id.0) - .and_then(|info| { - info.as_ref() - .map(|info| Cow::Borrowed(info.descriptor.name())) - }) + .and_then(|info| info.as_ref().map(|info| info.descriptor.name())) .or_else(|| { let queued = self.queued.read().unwrap_or_else(PoisonError::into_inner); // first check components, then resources, then dynamic @@ -2814,7 +2795,6 @@ impl RequiredComponents { // This exists as a standalone function instead of being inlined into the component derive macro so as // to reduce the amount of generated code. #[doc(hidden)] -#[cfg_attr(not(feature = "debug"), expect(unused_variables))] pub fn enforce_no_required_components_recursion( components: &Components, recursion_check_stack: &[ComponentId], @@ -2825,32 +2805,22 @@ pub fn enforce_no_required_components_recursion( .position(|&id| id == requiree) .map(|index| index == check.len() - 1) { - #[cfg(feature = "debug")] panic!( "Recursive required components detected: {}\nhelp: {}", recursion_check_stack .iter() - .map(|id| format!("{}", ShortName(&components.get_name(*id).unwrap()))) + .map(|id| format!("{}", components.get_name(*id).unwrap().shortname())) .collect::>() .join(" → "), if direct_recursion { format!( "Remove require({}).", - ShortName(&components.get_name(requiree).unwrap()) + components.get_name(requiree).unwrap().shortname() ) } else { "If this is intentional, consider merging the components.".into() } ); - #[cfg(not(feature = "debug"))] - panic!( - "Recursive required components detected\n help: {}\nEnable the debug feature to get more details", - if direct_recursion { - "Remove the recursing required component." - } else { - "If this is intentional, consider merging the components." - } - ); } } } diff --git a/crates/bevy_ecs/src/entity/clone_entities.rs b/crates/bevy_ecs/src/entity/clone_entities.rs index b124055d16ac6..02d2491b7a6c3 100644 --- a/crates/bevy_ecs/src/entity/clone_entities.rs +++ b/crates/bevy_ecs/src/entity/clone_entities.rs @@ -1,6 +1,7 @@ use alloc::{borrow::ToOwned, boxed::Box, collections::VecDeque, vec::Vec}; use bevy_platform::collections::{HashMap, HashSet}; use bevy_ptr::{Ptr, PtrMut}; +use bevy_utils::prelude::DebugName; use bumpalo::Bump; use core::any::TypeId; @@ -171,7 +172,8 @@ impl<'a, 'b> ComponentCloneCtx<'a, 'b> { /// - `ComponentId` of component being written does not match expected `ComponentId`. pub fn write_target_component(&mut self, mut component: C) { C::map_entities(&mut component, &mut self.mapper); - let short_name = disqualified::ShortName::of::(); + let debug_name = DebugName::type_name::(); + let short_name = debug_name.shortname(); if self.target_component_written { panic!("Trying to write component '{short_name}' multiple times") } diff --git a/crates/bevy_ecs/src/error/command_handling.rs b/crates/bevy_ecs/src/error/command_handling.rs index 13dd6cd0622d6..c303b76d170b2 100644 --- a/crates/bevy_ecs/src/error/command_handling.rs +++ b/crates/bevy_ecs/src/error/command_handling.rs @@ -1,7 +1,7 @@ -#[cfg(feature = "debug")] -use core::any::type_name; use core::fmt; +use bevy_utils::prelude::DebugName; + use crate::{ entity::Entity, never::Never, @@ -32,12 +32,9 @@ where Ok(_) => {} Err(err) => (error_handler)( err.into(), - #[cfg(feature = "debug")] ErrorContext::Command { - name: type_name::().into(), + name: DebugName::type_name::(), }, - #[cfg(not(feature = "debug"))] - ErrorContext::Anonymous, ), } } @@ -47,12 +44,9 @@ where Ok(_) => {} Err(err) => world.default_error_handler()( err.into(), - #[cfg(feature = "debug")] ErrorContext::Command { - name: type_name::().into(), + name: DebugName::type_name::(), }, - #[cfg(not(feature = "debug"))] - ErrorContext::Anonymous, ), } } diff --git a/crates/bevy_ecs/src/error/handler.rs b/crates/bevy_ecs/src/error/handler.rs index 3d2daa16acbc5..85a5a13297d22 100644 --- a/crates/bevy_ecs/src/error/handler.rs +++ b/crates/bevy_ecs/src/error/handler.rs @@ -1,7 +1,7 @@ use core::fmt::Display; use crate::{component::Tick, error::BevyError, prelude::Resource}; -use alloc::borrow::Cow; +use bevy_utils::prelude::DebugName; use derive_more::derive::{Deref, DerefMut}; /// Context for a [`BevyError`] to aid in debugging. @@ -10,34 +10,29 @@ pub enum ErrorContext { /// The error occurred in a system. System { /// The name of the system that failed. - name: Cow<'static, str>, + name: DebugName, /// The last tick that the system was run. last_run: Tick, }, /// The error occurred in a run condition. RunCondition { /// The name of the run condition that failed. - name: Cow<'static, str>, + name: DebugName, /// The last tick that the run condition was evaluated. last_run: Tick, }, /// The error occurred in a command. Command { /// The name of the command that failed. - name: Cow<'static, str>, + name: DebugName, }, /// The error occurred in an observer. Observer { /// The name of the observer that failed. - name: Cow<'static, str>, + name: DebugName, /// The last tick that the observer was run. last_run: Tick, }, - /// The error occurred in anonymous. - /// - /// This can happen when the debug feature is not enabled - #[cfg(not(feature = "debug"))] - Anonymous, } impl Display for ErrorContext { @@ -53,23 +48,18 @@ impl Display for ErrorContext { Self::RunCondition { name, .. } => { write!(f, "Run condition `{name}` failed") } - #[cfg(not(feature = "debug"))] - Self::Anonymous { .. } => { - write!(f, "Anonymous failed") - } } } } impl ErrorContext { /// The name of the ECS construct that failed. - #[cfg(feature = "debug")] - pub fn name(&self) -> &str { + pub fn name(&self) -> DebugName { match self { Self::System { name, .. } | Self::Command { name, .. } | Self::Observer { name, .. } - | Self::RunCondition { name, .. } => name, + | Self::RunCondition { name, .. } => name.clone(), } } @@ -82,13 +72,10 @@ impl ErrorContext { Self::Command { .. } => "command", Self::Observer { .. } => "observer", Self::RunCondition { .. } => "run condition", - #[cfg(not(feature = "debug"))] - Self::Anonymous { .. } => "anonymous", } } } -#[cfg(feature = "debug")] macro_rules! inner { ($call:path, $e:ident, $c:ident) => { $call!( @@ -100,13 +87,6 @@ macro_rules! inner { }; } -#[cfg(not(feature = "debug"))] -macro_rules! inner { - ($call:path, $e:ident, $c:ident) => { - $call!("Encountered an error in {} `{}`: {}", $c.kind(), "", $e); - }; -} - /// Defines how Bevy reacts to errors. pub type ErrorHandler = fn(BevyError, ErrorContext); diff --git a/crates/bevy_ecs/src/hierarchy.rs b/crates/bevy_ecs/src/hierarchy.rs index dfc32e60dbd2b..5adff7d9fecf0 100644 --- a/crates/bevy_ecs/src/hierarchy.rs +++ b/crates/bevy_ecs/src/hierarchy.rs @@ -22,9 +22,9 @@ use alloc::{format, string::String, vec::Vec}; use bevy_reflect::std_traits::ReflectDefault; #[cfg(all(feature = "serialize", feature = "bevy_reflect"))] use bevy_reflect::{ReflectDeserialize, ReflectSerialize}; +use bevy_utils::prelude::DebugName; use core::ops::Deref; use core::slice; -use disqualified::ShortName; use log::warn; /// Stores the parent entity of this child entity with this component. @@ -439,11 +439,12 @@ pub fn validate_parent_has_component( { // TODO: print name here once Name lives in bevy_ecs let name: Option = None; + let debug_name = DebugName::type_name::(); warn!( "warning[B0004]: {}{name} with the {ty_name} component has a parent without {ty_name}.\n\ This will cause inconsistent behaviors! See: https://bevy.org/learn/errors/b0004", caller.map(|c| format!("{c}: ")).unwrap_or_default(), - ty_name = ShortName::of::(), + ty_name = debug_name.shortname(), name = name.map_or_else( || format!("Entity {entity}"), |s| format!("The {s} entity") diff --git a/crates/bevy_ecs/src/observer/runner.rs b/crates/bevy_ecs/src/observer/runner.rs index f700452f606c6..283f516d51a1c 100644 --- a/crates/bevy_ecs/src/observer/runner.rs +++ b/crates/bevy_ecs/src/observer/runner.rs @@ -207,7 +207,6 @@ impl Observer { /// Panics if the given system is an exclusive system. pub fn new>(system: I) -> Self { let system = Box::new(IntoObserverSystem::into_system(system)); - #[cfg(feature = "debug")] assert!( !system.is_exclusive(), concat!( @@ -216,14 +215,6 @@ impl Observer { ), system.name() ); - #[cfg(not(feature = "debug"))] - assert!( - !system.is_exclusive(), - concat!( - "Exclusive system may not be used as observer.\n", - "Instead of `&mut World`, use either `DeferredWorld` if you do not need structural changes, or `Commands` if you do." - ), - ); Self { system, descriptor: Default::default(), @@ -393,13 +384,10 @@ fn observer_system_runner>( .unwrap_or_else(|| world.default_error_handler()); handler( err, - #[cfg(feature = "debug")] ErrorContext::Observer { name: (*system).name(), last_run: (*system).get_last_run(), }, - #[cfg(not(feature = "debug"))] - ErrorContext::Anonymous, ); }; (*system).queue_deferred(world.into_deferred()); @@ -411,13 +399,10 @@ fn observer_system_runner>( .unwrap_or_else(|| world.default_error_handler()); handler( e.into(), - #[cfg(feature = "debug")] ErrorContext::Observer { name: (*system).name(), last_run: (*system).get_last_run(), }, - #[cfg(not(feature = "debug"))] - ErrorContext::Anonymous, ); } } diff --git a/crates/bevy_ecs/src/query/access.rs b/crates/bevy_ecs/src/query/access.rs index bcd280185e8dc..0c5b29f715c7e 100644 --- a/crates/bevy_ecs/src/query/access.rs +++ b/crates/bevy_ecs/src/query/access.rs @@ -1,15 +1,9 @@ -#[cfg(feature = "debug")] use crate::component::ComponentId; use crate::storage::SparseSetIndex; -#[cfg(feature = "debug")] use crate::world::World; -#[cfg(feature = "debug")] -use alloc::{format, string::String}; -use alloc::{vec, vec::Vec}; +use alloc::{format, string::String, vec, vec::Vec}; use core::{fmt, fmt::Debug, marker::PhantomData}; use derive_more::From; -#[cfg(feature = "debug")] -use disqualified::ShortName; use fixedbitset::FixedBitSet; use thiserror::Error; @@ -996,7 +990,6 @@ impl AccessConflicts { } } - #[cfg(feature = "debug")] pub(crate) fn format_conflict_list(&self, world: &World) -> String { match self { AccessConflicts::All => String::new(), @@ -1005,12 +998,11 @@ impl AccessConflicts { .map(|index| { format!( "{}", - ShortName( - &world - .components - .get_name(ComponentId::get_sparse_set_index(index)) - .unwrap() - ) + world + .components + .get_name(ComponentId::get_sparse_set_index(index)) + .unwrap() + .shortname() ) }) .collect::>() diff --git a/crates/bevy_ecs/src/query/error.rs b/crates/bevy_ecs/src/query/error.rs index 6d0b149b86058..fd431f4be1dc1 100644 --- a/crates/bevy_ecs/src/query/error.rs +++ b/crates/bevy_ecs/src/query/error.rs @@ -1,3 +1,4 @@ +use bevy_utils::prelude::DebugName; use thiserror::Error; use crate::{ @@ -54,10 +55,10 @@ impl core::fmt::Display for QueryEntityError { pub enum QuerySingleError { /// No entity fits the query. #[error("No entities fit the query {0}")] - NoEntities(&'static str), + NoEntities(DebugName), /// Multiple entities fit the query. #[error("Multiple entities fit the query {0}")] - MultipleEntities(&'static str), + MultipleEntities(DebugName), } #[cfg(test)] diff --git a/crates/bevy_ecs/src/query/fetch.rs b/crates/bevy_ecs/src/query/fetch.rs index 55fa42c41eaac..b716e479b1612 100644 --- a/crates/bevy_ecs/src/query/fetch.rs +++ b/crates/bevy_ecs/src/query/fetch.rs @@ -12,6 +12,7 @@ use crate::{ }, }; use bevy_ptr::{ThinSlicePtr, UnsafeCellDeref}; +use bevy_utils::prelude::DebugName; use core::{cell::UnsafeCell, marker::PhantomData, panic::Location}; use smallvec::SmallVec; use variadics_please::all_tuples; @@ -1130,7 +1131,7 @@ where assert!( access.is_compatible(&my_access), "`EntityRefExcept<{}>` conflicts with a previous access in this query.", - core::any::type_name::(), + DebugName::type_name::(), ); access.extend(&my_access); } @@ -1237,7 +1238,7 @@ where assert!( access.is_compatible(&my_access), "`EntityMutExcept<{}>` conflicts with a previous access in this query.", - core::any::type_name::() + DebugName::type_name::() ); access.extend(&my_access); } @@ -1465,7 +1466,7 @@ unsafe impl WorldQuery for &T { assert!( !access.access().has_component_write(component_id), "&{} conflicts with a previous access in this query. Shared access cannot coincide with exclusive access.", - core::any::type_name::(), + DebugName::type_name::(), ); access.add_component_read(component_id); } @@ -1641,7 +1642,7 @@ unsafe impl<'__w, T: Component> WorldQuery for Ref<'__w, T> { assert!( !access.access().has_component_write(component_id), "&{} conflicts with a previous access in this query. Shared access cannot coincide with exclusive access.", - core::any::type_name::(), + DebugName::type_name::(), ); access.add_component_read(component_id); } @@ -1840,7 +1841,7 @@ unsafe impl<'__w, T: Component> WorldQuery for &'__w mut T { assert!( !access.access().has_component_read(component_id), "&mut {} conflicts with a previous access in this query. Mutable component access must be unique.", - core::any::type_name::(), + DebugName::type_name::(), ); access.add_component_write(component_id); } @@ -1980,7 +1981,7 @@ unsafe impl<'__w, T: Component> WorldQuery for Mut<'__w, T> { assert!( !access.access().has_component_read(component_id), "Mut<{}> conflicts with a previous access in this query. Mutable component access mut be unique.", - core::any::type_name::(), + DebugName::type_name::(), ); access.add_component_write(component_id); } @@ -2225,7 +2226,7 @@ pub struct Has(PhantomData); impl core::fmt::Debug for Has { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> { - write!(f, "Has<{}>", core::any::type_name::()) + write!(f, "Has<{}>", DebugName::type_name::()) } } diff --git a/crates/bevy_ecs/src/query/filter.rs b/crates/bevy_ecs/src/query/filter.rs index eccc819ca7cb3..6d5b9774d50f5 100644 --- a/crates/bevy_ecs/src/query/filter.rs +++ b/crates/bevy_ecs/src/query/filter.rs @@ -7,6 +7,7 @@ use crate::{ world::{unsafe_world_cell::UnsafeWorldCell, World}, }; use bevy_ptr::{ThinSlicePtr, UnsafeCellDeref}; +use bevy_utils::prelude::DebugName; use core::{cell::UnsafeCell, marker::PhantomData}; use variadics_please::all_tuples; @@ -781,7 +782,7 @@ unsafe impl WorldQuery for Added { #[inline] fn update_component_access(&id: &ComponentId, access: &mut FilteredAccess) { if access.access().has_component_write(id) { - panic!("$state_name<{}> conflicts with a previous access in this query. Shared access cannot coincide with exclusive access.",core::any::type_name::()); + panic!("$state_name<{}> conflicts with a previous access in this query. Shared access cannot coincide with exclusive access.", DebugName::type_name::()); } access.add_component_read(id); } @@ -1007,7 +1008,7 @@ unsafe impl WorldQuery for Changed { #[inline] fn update_component_access(&id: &ComponentId, access: &mut FilteredAccess) { if access.access().has_component_write(id) { - panic!("$state_name<{}> conflicts with a previous access in this query. Shared access cannot coincide with exclusive access.",core::any::type_name::()); + panic!("$state_name<{}> conflicts with a previous access in this query. Shared access cannot coincide with exclusive access.", DebugName::type_name::()); } access.add_component_read(id); } diff --git a/crates/bevy_ecs/src/query/state.rs b/crates/bevy_ecs/src/query/state.rs index 836fefbdfd837..2658eab088b2d 100644 --- a/crates/bevy_ecs/src/query/state.rs +++ b/crates/bevy_ecs/src/query/state.rs @@ -14,6 +14,7 @@ use crate::{ use crate::entity::UniqueEntityEquivalentSlice; use alloc::vec::Vec; +use bevy_utils::prelude::DebugName; use core::{fmt, ptr}; use fixedbitset::FixedBitSet; use log::warn; @@ -672,7 +673,7 @@ impl QueryState { assert!( component_access.is_subset(&self_access), "Transmuted state for {} attempts to access terms that are not allowed by original state {}.", - core::any::type_name::<(NewD, NewF)>(), core::any::type_name::<(D, F)>() + DebugName::type_name::<(NewD, NewF)>(), DebugName::type_name::<(D, F)>() ); QueryState { @@ -791,7 +792,7 @@ impl QueryState { assert!( component_access.is_subset(&joined_component_access), "Joined state for {} attempts to access terms that are not allowed by state {} joined with {}.", - core::any::type_name::<(NewD, NewF)>(), core::any::type_name::<(D, F)>(), core::any::type_name::<(OtherD, OtherF)>() + DebugName::type_name::<(NewD, NewF)>(), DebugName::type_name::<(D, F)>(), DebugName::type_name::<(OtherD, OtherF)>() ); if self.archetype_generation != other.archetype_generation { diff --git a/crates/bevy_ecs/src/reflect/bundle.rs b/crates/bevy_ecs/src/reflect/bundle.rs index ee02aff86e7fe..133591c405ebf 100644 --- a/crates/bevy_ecs/src/reflect/bundle.rs +++ b/crates/bevy_ecs/src/reflect/bundle.rs @@ -5,6 +5,7 @@ //! //! Same as [`super::component`], but for bundles. use alloc::boxed::Box; +use bevy_utils::prelude::DebugName; use core::any::{Any, TypeId}; use crate::{ @@ -172,7 +173,7 @@ impl FromType for Refl _ => panic!( "expected bundle `{}` to be named struct or tuple", // FIXME: once we have unique reflect, use `TypePath`. - core::any::type_name::(), + DebugName::type_name::(), ), } } @@ -215,7 +216,7 @@ impl FromType for Refl _ => panic!( "expected bundle `{}` to be a named struct or tuple", // FIXME: once we have unique reflect, use `TypePath`. - core::any::type_name::(), + DebugName::type_name::(), ), } } diff --git a/crates/bevy_ecs/src/reflect/component.rs b/crates/bevy_ecs/src/reflect/component.rs index 893e9b13fa8e3..8a5c17179e1ff 100644 --- a/crates/bevy_ecs/src/reflect/component.rs +++ b/crates/bevy_ecs/src/reflect/component.rs @@ -70,7 +70,7 @@ use crate::{ }, }; use bevy_reflect::{FromReflect, FromType, PartialReflect, Reflect, TypePath, TypeRegistry}; -use disqualified::ShortName; +use bevy_utils::prelude::DebugName; /// A struct used to operate on reflected [`Component`] trait of a type. /// @@ -308,7 +308,8 @@ impl FromType for ReflectComponent { }, apply: |mut entity, reflected_component| { if !C::Mutability::MUTABLE { - let name = ShortName::of::(); + let name = DebugName::type_name::(); + let name = name.shortname(); panic!("Cannot call `ReflectComponent::apply` on component {name}. It is immutable, and cannot modified through reflection"); } @@ -357,7 +358,8 @@ impl FromType for ReflectComponent { reflect: |entity| entity.get::().map(|c| c as &dyn Reflect), reflect_mut: |entity| { if !C::Mutability::MUTABLE { - let name = ShortName::of::(); + let name = DebugName::type_name::(); + let name = name.shortname(); panic!("Cannot call `ReflectComponent::reflect_mut` on component {name}. It is immutable, and cannot modified through reflection"); } @@ -370,7 +372,8 @@ impl FromType for ReflectComponent { }, reflect_unchecked_mut: |entity| { if !C::Mutability::MUTABLE { - let name = ShortName::of::(); + let name = DebugName::type_name::(); + let name = name.shortname(); panic!("Cannot call `ReflectComponent::reflect_unchecked_mut` on component {name}. It is immutable, and cannot modified through reflection"); } diff --git a/crates/bevy_ecs/src/reflect/mod.rs b/crates/bevy_ecs/src/reflect/mod.rs index b630f587197d7..24e1449e61a8d 100644 --- a/crates/bevy_ecs/src/reflect/mod.rs +++ b/crates/bevy_ecs/src/reflect/mod.rs @@ -18,6 +18,7 @@ mod from_world; mod map_entities; mod resource; +use bevy_utils::prelude::DebugName; pub use bundle::{ReflectBundle, ReflectBundleFns}; pub use component::{ReflectComponent, ReflectComponentFns}; pub use entity_commands::ReflectCommandExt; @@ -136,7 +137,7 @@ pub fn from_reflect_with_fallback( `Default` or `FromWorld` traits. Are you perhaps missing a `#[reflect(Default)]` \ or `#[reflect(FromWorld)]`?", // FIXME: once we have unique reflect, use `TypePath`. - core::any::type_name::(), + DebugName::type_name::(), ); }; diff --git a/crates/bevy_ecs/src/relationship/mod.rs b/crates/bevy_ecs/src/relationship/mod.rs index 00334cb6d0063..d570b9fabc670 100644 --- a/crates/bevy_ecs/src/relationship/mod.rs +++ b/crates/bevy_ecs/src/relationship/mod.rs @@ -6,6 +6,7 @@ mod relationship_source_collection; use alloc::format; +use bevy_utils::prelude::DebugName; pub use related_methods::*; pub use relationship_query::*; pub use relationship_source_collection::*; @@ -105,8 +106,8 @@ pub trait Relationship: Component + Sized { warn!( "{}The {}({target_entity:?}) relationship on entity {entity:?} points to itself. The invalid {} relationship has been removed.", caller.map(|location|format!("{location}: ")).unwrap_or_default(), - core::any::type_name::(), - core::any::type_name::() + DebugName::type_name::(), + DebugName::type_name::() ); world.commands().entity(entity).remove::(); return; @@ -125,8 +126,8 @@ pub trait Relationship: Component + Sized { warn!( "{}The {}({target_entity:?}) relationship on entity {entity:?} relates to an entity that does not exist. The invalid {} relationship has been removed.", caller.map(|location|format!("{location}: ")).unwrap_or_default(), - core::any::type_name::(), - core::any::type_name::() + DebugName::type_name::(), + DebugName::type_name::() ); world.commands().entity(entity).remove::(); } diff --git a/crates/bevy_ecs/src/schedule/condition.rs b/crates/bevy_ecs/src/schedule/condition.rs index a3005cfd23ab0..ebf8cdbcda98c 100644 --- a/crates/bevy_ecs/src/schedule/condition.rs +++ b/crates/bevy_ecs/src/schedule/condition.rs @@ -1,6 +1,5 @@ -use alloc::boxed::Box; -#[cfg(feature = "debug")] -use alloc::{borrow::Cow, format}; +use alloc::{boxed::Box, format}; +use bevy_utils::prelude::DebugName; use core::ops::Not; use crate::system::{ @@ -155,14 +154,8 @@ pub trait SystemCondition: fn and>(self, and: C) -> And { let a = IntoSystem::into_system(self); let b = IntoSystem::into_system(and); - #[cfg(feature = "debug")] let name = format!("{} && {}", a.name(), b.name()); - CombinatorSystem::new( - a, - b, - #[cfg(feature = "debug")] - Cow::Owned(name), - ) + CombinatorSystem::new(a, b, DebugName::owned(name)) } /// Returns a new run condition that only returns `false` @@ -213,14 +206,8 @@ pub trait SystemCondition: fn nand>(self, nand: C) -> Nand { let a = IntoSystem::into_system(self); let b = IntoSystem::into_system(nand); - #[cfg(feature = "debug")] let name = format!("!({} && {})", a.name(), b.name()); - CombinatorSystem::new( - a, - b, - #[cfg(feature = "debug")] - Cow::Owned(name), - ) + CombinatorSystem::new(a, b, DebugName::owned(name)) } /// Returns a new run condition that only returns `true` @@ -271,14 +258,8 @@ pub trait SystemCondition: fn nor>(self, nor: C) -> Nor { let a = IntoSystem::into_system(self); let b = IntoSystem::into_system(nor); - #[cfg(feature = "debug")] let name = format!("!({} || {})", a.name(), b.name()); - CombinatorSystem::new( - a, - b, - #[cfg(feature = "debug")] - Cow::Owned(name), - ) + CombinatorSystem::new(a, b, DebugName::owned(name)) } /// Returns a new run condition that returns `true` @@ -324,14 +305,8 @@ pub trait SystemCondition: fn or>(self, or: C) -> Or { let a = IntoSystem::into_system(self); let b = IntoSystem::into_system(or); - #[cfg(feature = "debug")] let name = format!("{} || {}", a.name(), b.name()); - CombinatorSystem::new( - a, - b, - #[cfg(feature = "debug")] - Cow::Owned(name), - ) + CombinatorSystem::new(a, b, DebugName::owned(name)) } /// Returns a new run condition that only returns `true` @@ -382,14 +357,8 @@ pub trait SystemCondition: fn xnor>(self, xnor: C) -> Xnor { let a = IntoSystem::into_system(self); let b = IntoSystem::into_system(xnor); - #[cfg(feature = "debug")] let name = format!("!({} ^ {})", a.name(), b.name()); - CombinatorSystem::new( - a, - b, - #[cfg(feature = "debug")] - Cow::Owned(name), - ) + CombinatorSystem::new(a, b, DebugName::owned(name)) } /// Returns a new run condition that only returns `true` @@ -430,14 +399,8 @@ pub trait SystemCondition: fn xor>(self, xor: C) -> Xor { let a = IntoSystem::into_system(self); let b = IntoSystem::into_system(xor); - #[cfg(feature = "debug")] let name = format!("({} ^ {})", a.name(), b.name()); - CombinatorSystem::new( - a, - b, - #[cfg(feature = "debug")] - Cow::Owned(name), - ) + CombinatorSystem::new(a, b, DebugName::owned(name)) } } @@ -502,8 +465,6 @@ mod sealed { /// A collection of [run conditions](SystemCondition) that may be useful in any bevy app. pub mod common_conditions { use super::{NotSystem, SystemCondition}; - #[cfg(feature = "debug")] - use crate::system::System; use crate::{ change_detection::DetectChanges, event::{Event, EventReader}, @@ -511,9 +472,8 @@ pub mod common_conditions { prelude::{Component, Query, With}, query::QueryFilter, resource::Resource, - system::{In, IntoSystem, Local, Res, SystemInput}, + system::{In, IntoSystem, Local, Res, System, SystemInput}, }; - #[cfg(feature = "debug")] use alloc::format; /// A [`SystemCondition`]-satisfying system that returns `true` @@ -1083,14 +1043,8 @@ pub mod common_conditions { T: IntoSystem<(), TOut, Marker>, { let condition = IntoSystem::into_system(condition); - #[cfg(feature = "debug")] let name = format!("!{}", condition.name()); - NotSystem::new( - super::NotMarker, - condition, - #[cfg(feature = "debug")] - name.into(), - ) + NotSystem::new(super::NotMarker, condition, name.into()) } /// Generates a [`SystemCondition`] that returns true when the passed one changes. diff --git a/crates/bevy_ecs/src/schedule/config.rs b/crates/bevy_ecs/src/schedule/config.rs index e15ff6e7028d5..4826d0a66df49 100644 --- a/crates/bevy_ecs/src/schedule/config.rs +++ b/crates/bevy_ecs/src/schedule/config.rs @@ -16,17 +16,11 @@ use crate::{ fn new_condition(condition: impl SystemCondition) -> BoxedCondition { let condition_system = condition.into_condition_system(); - #[cfg(feature = "debug")] assert!( condition_system.is_send(), "SystemCondition `{}` accesses `NonSend` resources. This is not currently supported.", condition_system.name() ); - #[cfg(not(feature = "debug"))] - assert!( - condition_system.is_send(), - "A SystemCondition accesses `NonSend` resources. This is not currently supported.", - ); Box::new(condition_system) } diff --git a/crates/bevy_ecs/src/schedule/executor/mod.rs b/crates/bevy_ecs/src/schedule/executor/mod.rs index 26e3ad4faaf0f..9156fc34a35a6 100644 --- a/crates/bevy_ecs/src/schedule/executor/mod.rs +++ b/crates/bevy_ecs/src/schedule/executor/mod.rs @@ -3,9 +3,8 @@ mod multi_threaded; mod simple; mod single_threaded; -#[cfg(feature = "debug")] -use alloc::borrow::Cow; use alloc::{vec, vec::Vec}; +use bevy_utils::prelude::DebugName; use core::any::TypeId; #[expect(deprecated, reason = "We still need to support this.")] @@ -160,9 +159,8 @@ impl System for ApplyDeferred { type In = (); type Out = Result<()>; - #[cfg(feature = "debug")] - fn name(&self) -> Cow<'static, str> { - Cow::Borrowed("bevy_ecs::apply_deferred") + fn name(&self) -> DebugName { + DebugName::borrowed("bevy_ecs::apply_deferred") } fn flags(&self) -> SystemStateFlags { diff --git a/crates/bevy_ecs/src/schedule/executor/multi_threaded.rs b/crates/bevy_ecs/src/schedule/executor/multi_threaded.rs index f91cfb68868c6..b6036ee76b744 100644 --- a/crates/bevy_ecs/src/schedule/executor/multi_threaded.rs +++ b/crates/bevy_ecs/src/schedule/executor/multi_threaded.rs @@ -5,7 +5,7 @@ use bevy_tasks::{ComputeTaskPool, Scope, TaskPool, ThreadExecutor}; use concurrent_queue::ConcurrentQueue; use core::{any::Any, panic::AssertUnwindSafe}; use fixedbitset::FixedBitSet; -#[cfg(all(feature = "std", feature = "debug"))] +#[cfg(feature = "std")] use std::eprintln; use std::sync::{Mutex, MutexGuard}; @@ -326,7 +326,6 @@ impl SystemExecutor for MultiThreadedExecutor { } impl<'scope, 'env: 'scope, 'sys> Context<'scope, 'env, 'sys> { - #[cfg_attr(not(feature = "debug"), expect(unused_variables))] fn system_completed( &self, system_index: usize, @@ -340,10 +339,10 @@ impl<'scope, 'env: 'scope, 'sys> Context<'scope, 'env, 'sys> { .push(SystemResult { system_index }) .unwrap_or_else(|error| unreachable!("{}", error)); if let Err(payload) = res { - #[cfg(all(feature = "std", feature = "debug"))] + #[cfg(feature = "std")] #[expect(clippy::print_stderr, reason = "Allowed behind `std` feature gate.")] { - eprintln!("Encountered a panic in system `{}`!", &*system.name()); + eprintln!("Encountered a panic in system `{}`!", system.name()); } // set the payload to propagate the error { @@ -635,13 +634,10 @@ impl ExecutorState { if !e.skipped { error_handler( e.into(), - #[cfg(feature = "debug")] ErrorContext::System { name: system.name(), last_run: system.get_last_run(), }, - #[cfg(not(feature = "debug"))] - ErrorContext::Anonymous, ); } false @@ -683,13 +679,10 @@ impl ExecutorState { ) { (context.error_handler)( err, - #[cfg(feature = "debug")] ErrorContext::System { name: system.name(), last_run: system.get_last_run(), }, - #[cfg(not(feature = "debug"))] - ErrorContext::Anonymous, ); } }; @@ -735,13 +728,10 @@ impl ExecutorState { if let Err(err) = __rust_begin_short_backtrace::run(system, world) { (context.error_handler)( err, - #[cfg(feature = "debug")] ErrorContext::System { name: system.name(), last_run: system.get_last_run(), }, - #[cfg(not(feature = "debug"))] - ErrorContext::Anonymous, ); } })); @@ -804,12 +794,12 @@ fn apply_deferred( system.apply_deferred(world); })); if let Err(payload) = res { - #[cfg(all(feature = "std", feature = "debug"))] + #[cfg(feature = "std")] #[expect(clippy::print_stderr, reason = "Allowed behind `std` feature gate.")] { eprintln!( "Encountered a panic when applying buffers for system `{}`!", - &*system.name() + system.name() ); } return Err(payload); @@ -842,13 +832,10 @@ unsafe fn evaluate_and_fold_conditions( if !e.skipped { error_handler( e.into(), - #[cfg(feature = "debug")] ErrorContext::System { name: condition.name(), last_run: condition.get_last_run(), }, - #[cfg(not(feature = "debug"))] - ErrorContext::Anonymous, ); } return false; diff --git a/crates/bevy_ecs/src/schedule/executor/simple.rs b/crates/bevy_ecs/src/schedule/executor/simple.rs index 342d92cfbde4c..b7ea2c7119980 100644 --- a/crates/bevy_ecs/src/schedule/executor/simple.rs +++ b/crates/bevy_ecs/src/schedule/executor/simple.rs @@ -6,7 +6,7 @@ use fixedbitset::FixedBitSet; #[cfg(feature = "trace")] use tracing::info_span; -#[cfg(all(feature = "std", feature = "debug"))] +#[cfg(feature = "std")] use std::eprintln; use crate::{ @@ -114,13 +114,10 @@ impl SystemExecutor for SimpleExecutor { if !e.skipped { error_handler( e.into(), - #[cfg(feature = "debug")] ErrorContext::System { name: system.name(), last_run: system.get_last_run(), }, - #[cfg(not(feature = "debug"))] - ErrorContext::Anonymous, ); } false @@ -152,13 +149,10 @@ impl SystemExecutor for SimpleExecutor { if let Err(err) = __rust_begin_short_backtrace::run(system, world) { error_handler( err, - #[cfg(feature = "debug")] ErrorContext::System { name: system.name(), last_run: system.get_last_run(), }, - #[cfg(not(feature = "debug"))] - ErrorContext::Anonymous, ); } }); @@ -167,8 +161,7 @@ impl SystemExecutor for SimpleExecutor { #[expect(clippy::print_stderr, reason = "Allowed behind `std` feature gate.")] { if let Err(payload) = std::panic::catch_unwind(f) { - #[cfg(feature = "debug")] - eprintln!("Encountered a panic in system `{}`!", &*system.name()); + eprintln!("Encountered a panic in system `{}`!", system.name()); std::panic::resume_unwind(payload); } } @@ -226,13 +219,10 @@ fn evaluate_and_fold_conditions( if !e.skipped { error_handler( e.into(), - #[cfg(feature = "debug")] ErrorContext::System { name: condition.name(), last_run: condition.get_last_run(), }, - #[cfg(not(feature = "debug"))] - ErrorContext::Anonymous, ); } return false; diff --git a/crates/bevy_ecs/src/schedule/executor/single_threaded.rs b/crates/bevy_ecs/src/schedule/executor/single_threaded.rs index 8196498a6caa1..89aacbcdb3963 100644 --- a/crates/bevy_ecs/src/schedule/executor/single_threaded.rs +++ b/crates/bevy_ecs/src/schedule/executor/single_threaded.rs @@ -4,7 +4,7 @@ use fixedbitset::FixedBitSet; #[cfg(feature = "trace")] use tracing::info_span; -#[cfg(all(feature = "std", feature = "debug"))] +#[cfg(feature = "std")] use std::eprintln; use crate::{ @@ -115,13 +115,10 @@ impl SystemExecutor for SingleThreadedExecutor { if !e.skipped { error_handler( e.into(), - #[cfg(feature = "debug")] ErrorContext::System { name: system.name(), last_run: system.get_last_run(), }, - #[cfg(not(feature = "debug"))] - ErrorContext::Anonymous, ); } false @@ -157,13 +154,10 @@ impl SystemExecutor for SingleThreadedExecutor { { error_handler( err, - #[cfg(feature = "debug")] ErrorContext::System { name: system.name(), last_run: system.get_last_run(), }, - #[cfg(not(feature = "debug"))] - ErrorContext::Anonymous, ); } }); @@ -172,8 +166,7 @@ impl SystemExecutor for SingleThreadedExecutor { #[expect(clippy::print_stderr, reason = "Allowed behind `std` feature gate.")] { if let Err(payload) = std::panic::catch_unwind(f) { - #[cfg(feature = "debug")] - eprintln!("Encountered a panic in system `{}`!", &*system.name()); + eprintln!("Encountered a panic in system `{}`!", system.name()); std::panic::resume_unwind(payload); } } @@ -245,13 +238,10 @@ fn evaluate_and_fold_conditions( if !e.skipped { error_handler( e.into(), - #[cfg(feature = "debug")] ErrorContext::System { name: condition.name(), last_run: condition.get_last_run(), }, - #[cfg(not(feature = "debug"))] - ErrorContext::Anonymous, ); } return false; diff --git a/crates/bevy_ecs/src/schedule/mod.rs b/crates/bevy_ecs/src/schedule/mod.rs index d4135c537f50f..8c5aa1d6fb0af 100644 --- a/crates/bevy_ecs/src/schedule/mod.rs +++ b/crates/bevy_ecs/src/schedule/mod.rs @@ -26,9 +26,7 @@ pub mod passes { #[cfg(test)] mod tests { use super::*; - #[cfg(feature = "debug")] - use alloc::string::ToString; - use alloc::{vec, vec::Vec}; + use alloc::{string::ToString, vec, vec::Vec}; use core::sync::atomic::{AtomicU32, Ordering}; use crate::error::BevyError; @@ -772,7 +770,6 @@ mod tests { } mod system_ambiguity { - #[cfg(feature = "debug")] use alloc::collections::BTreeSet; use super::*; @@ -1114,7 +1111,6 @@ mod tests { // Tests that the correct ambiguities were reported in the correct order. #[test] - #[cfg(feature = "debug")] fn correct_ambiguities() { fn system_a(_res: ResMut) {} fn system_b(_res: ResMut) {} @@ -1188,7 +1184,6 @@ mod tests { // Test that anonymous set names work properly // Related issue https://github.com/bevyengine/bevy/issues/9641 #[test] - #[cfg(feature = "debug")] fn anonymous_set_name() { let mut schedule = Schedule::new(TestSchedule); schedule.add_systems((resmut_system, resmut_system).run_if(|| true)); diff --git a/crates/bevy_ecs/src/schedule/schedule.rs b/crates/bevy_ecs/src/schedule/schedule.rs index 135ecf8a9bfe9..d9d39fceec3bb 100644 --- a/crates/bevy_ecs/src/schedule/schedule.rs +++ b/crates/bevy_ecs/src/schedule/schedule.rs @@ -2,8 +2,6 @@ clippy::module_inception, reason = "This instance of module inception is being discussed; see #17344." )] -#[cfg(feature = "debug")] -use alloc::borrow::Cow; use alloc::{ boxed::Box, collections::{BTreeMap, BTreeSet}, @@ -13,28 +11,21 @@ use alloc::{ vec::Vec, }; use bevy_platform::collections::{HashMap, HashSet}; -use bevy_utils::{default, TypeIdMap}; -#[cfg(feature = "debug")] -use core::fmt::Write; +use bevy_utils::{default, prelude::DebugName, TypeIdMap}; use core::{ any::{Any, TypeId}, - fmt::Debug, + fmt::{Debug, Write}, }; -#[cfg(feature = "debug")] -use disqualified::ShortName; use fixedbitset::FixedBitSet; -use log::error; -#[cfg(feature = "debug")] -use log::{info, warn}; +use log::{error, info, warn}; use pass::ScheduleBuildPassObj; use thiserror::Error; -#[cfg(all(feature = "trace", feature = "debug"))] +#[cfg(feature = "trace")] use tracing::info_span; -#[cfg(feature = "debug")] -use crate::component::Components; +use crate::component::CheckChangeTicks; use crate::{ - component::{CheckChangeTicks, ComponentId}, + component::{ComponentId, Components}, prelude::Component, query::FilteredAccessSet, resource::Resource, @@ -167,7 +158,6 @@ impl Schedules { /// /// May panic or retrieve incorrect names if [`Components`] is not from the same /// world - #[cfg(feature = "debug")] pub fn print_ignored_ambiguities(&self, components: &Components) { let mut message = "System order ambiguities caused by conflicts on the following types are ignored:\n" @@ -674,7 +664,6 @@ impl SystemSetNode { Self { inner: set } } - #[cfg(feature = "debug")] pub fn name(&self) -> String { format!("{:?}", &self.inner) } @@ -683,7 +672,6 @@ impl SystemSetNode { self.inner.system_type().is_some() } - #[cfg(feature = "debug")] pub fn is_anonymous(&self) -> bool { self.inner.is_anonymous() } @@ -1131,10 +1119,7 @@ impl ScheduleGraph { match self.system_set_ids.get(&set) { Some(set_id) => { if id == set_id { - #[cfg(feature = "debug")] return Err(ScheduleBuildError::HierarchyLoop(self.get_node_name(id))); - #[cfg(not(feature = "debug"))] - return Err(ScheduleBuildError::Anonymous); } } None => { @@ -1176,10 +1161,7 @@ impl ScheduleGraph { match self.system_set_ids.get(set) { Some(set_id) => { if id == set_id { - #[cfg(feature = "debug")] return Err(ScheduleBuildError::DependencyLoop(self.get_node_name(id))); - #[cfg(not(feature = "debug"))] - return Err(ScheduleBuildError::Anonymous); } } None => { @@ -1351,7 +1333,6 @@ impl ScheduleGraph { &ambiguous_with_flattened, ignored_ambiguities, ); - #[cfg(feature = "debug")] self.optionally_check_conflicts(&conflicting_systems, world.components(), schedule_label)?; self.conflicting_systems = conflicting_systems; @@ -1705,22 +1686,20 @@ pub enum ReportCycles { // methods for reporting errors impl ScheduleGraph { - #[cfg(feature = "debug")] fn get_node_name(&self, id: &NodeId) -> String { self.get_node_name_inner(id, self.settings.report_sets) } #[inline] - #[cfg(feature = "debug")] fn get_node_name_inner(&self, id: &NodeId, report_sets: bool) -> String { let name = match id { NodeId::System(_) => { - let name = self.systems[id.index()] - .get() - .unwrap() - .system - .name() - .to_string(); + let name = self.systems[id.index()].get().unwrap().system.name(); + let name = if self.settings.use_shortnames { + name.shortname().to_string() + } else { + name.to_string() + }; if report_sets { let sets = self.names_of_sets_containing_node(id); if sets.is_empty() { @@ -1743,14 +1722,9 @@ impl ScheduleGraph { } } }; - if self.settings.use_shortnames { - ShortName(&name).to_string() - } else { - name - } + name } - #[cfg(feature = "debug")] fn anonymous_set_name(&self, id: &NodeId) -> String { format!( "({})", @@ -1764,7 +1738,6 @@ impl ScheduleGraph { ) } - #[cfg(feature = "debug")] fn get_node_kind(&self, id: &NodeId) -> &'static str { match id { NodeId::System(_) => "system", @@ -1783,10 +1756,7 @@ impl ScheduleGraph { return Ok(()); } - #[cfg(feature = "debug")] let message = self.get_hierarchy_conflicts_error_message(transitive_edges); - #[cfg(not(feature = "debug"))] - let message = "Enable debug feature for more information".to_string(); match self.settings.hierarchy_detection { LogLevel::Ignore => unreachable!(), LogLevel::Warn => { @@ -1797,7 +1767,6 @@ impl ScheduleGraph { } } - #[cfg(feature = "debug")] fn get_hierarchy_conflicts_error_message( &self, transitive_edges: &[(NodeId, NodeId)], @@ -1826,7 +1795,6 @@ impl ScheduleGraph { /// # Errors /// /// If the graph contain cycles, then an error is returned. - #[cfg_attr(not(feature = "debug"), expect(unused_variables))] pub fn topsort_graph( &self, graph: &DiGraph, @@ -1856,7 +1824,6 @@ impl ScheduleGraph { cycles.append(&mut simple_cycles_in_component(graph, scc)); } - #[cfg(feature = "debug")] let error = match report { ReportCycles::Hierarchy => ScheduleBuildError::HierarchyCycle( self.get_hierarchy_cycles_error_message(&cycles), @@ -1865,15 +1832,12 @@ impl ScheduleGraph { self.get_dependency_cycles_error_message(&cycles), ), }; - #[cfg(not(feature = "debug"))] - let error = ScheduleBuildError::Anonymous; Err(error) } } /// Logs details of cycles in the hierarchy graph. - #[cfg(feature = "debug")] fn get_hierarchy_cycles_error_message(&self, cycles: &[Vec]) -> String { let mut message = format!("schedule has {} in_set cycle(s):\n", cycles.len()); for (i, cycle) in cycles.iter().enumerate() { @@ -1896,7 +1860,6 @@ impl ScheduleGraph { } /// Logs details of cycles in the dependency graph. - #[cfg(feature = "debug")] fn get_dependency_cycles_error_message(&self, cycles: &[Vec]) -> String { let mut message = format!("schedule has {} before/after cycle(s):\n", cycles.len()); for (i, cycle) in cycles.iter().enumerate() { @@ -1928,14 +1891,9 @@ impl ScheduleGraph { for &(a, b) in &dep_results.connected { if hier_results_connected.contains(&(a, b)) || hier_results_connected.contains(&(b, a)) { - #[cfg(feature = "debug")] - { - let name_a = self.get_node_name(&a); - let name_b = self.get_node_name(&b); - return Err(ScheduleBuildError::CrossDependency(name_a, name_b)); - } - #[cfg(not(feature = "debug"))] - return Err(ScheduleBuildError::Anonymous); + let name_a = self.get_node_name(&a); + let name_b = self.get_node_name(&b); + return Err(ScheduleBuildError::CrossDependency(name_a, name_b)); } } @@ -1957,13 +1915,10 @@ impl ScheduleGraph { let b_systems = set_system_bitsets.get(b).unwrap(); if !a_systems.is_disjoint(b_systems) { - #[cfg(feature = "debug")] return Err(ScheduleBuildError::SetsHaveOrderButIntersect( self.get_node_name(a), self.get_node_name(b), )); - #[cfg(not(feature = "debug"))] - return Err(ScheduleBuildError::Anonymous); } } @@ -1983,12 +1938,9 @@ impl ScheduleGraph { let after = self.dependency.graph.edges_directed(id, Outgoing); let relations = before.count() + after.count() + ambiguous_with.count(); if instances > 1 && relations > 0 { - #[cfg(feature = "debug")] return Err(ScheduleBuildError::SystemTypeSetAmbiguity( self.get_node_name(&id), )); - #[cfg(not(feature = "debug"))] - return Err(ScheduleBuildError::Anonymous); } } } @@ -1996,7 +1948,6 @@ impl ScheduleGraph { } /// if [`ScheduleBuildSettings::ambiguity_detection`] is [`LogLevel::Ignore`], this check is skipped - #[cfg(feature = "debug")] fn optionally_check_conflicts( &self, conflicts: &[(NodeId, NodeId, Vec)], @@ -2018,7 +1969,6 @@ impl ScheduleGraph { } } - #[cfg(feature = "debug")] fn get_conflicts_error_message( &self, ambiguities: &[(NodeId, NodeId, Vec)], @@ -2047,12 +1997,11 @@ impl ScheduleGraph { } /// convert conflicts to human readable format - #[cfg(feature = "debug")] pub fn conflicts_to_string<'a>( &'a self, ambiguities: &'a [(NodeId, NodeId, Vec)], components: &'a Components, - ) -> impl Iterator>)> + 'a { + ) -> impl Iterator)> + 'a { ambiguities .iter() .map(move |(system_a, system_b, conflicts)| { @@ -2071,7 +2020,6 @@ impl ScheduleGraph { }) } - #[cfg(feature = "debug")] fn traverse_sets_containing_node(&self, id: NodeId, f: &mut impl FnMut(NodeId) -> bool) { for (set_id, _) in self.hierarchy.graph.edges_directed(id, Incoming) { if f(set_id) { @@ -2080,7 +2028,6 @@ impl ScheduleGraph { } } - #[cfg(feature = "debug")] fn names_of_sets_containing_node(&self, id: &NodeId) -> Vec { let mut sets = >::default(); self.traverse_sets_containing_node(*id, &mut |set_id| { @@ -2099,10 +2046,6 @@ impl ScheduleGraph { #[derive(Error, Debug)] #[non_exhaustive] pub enum ScheduleBuildError { - /// Anonymous error. Build with the debug feature to know more - #[error("Error building schedule.")] - #[cfg(not(feature = "debug"))] - Anonymous, /// A system set contains itself. #[error("System set `{0}` contains itself.")] HierarchyLoop(String), diff --git a/crates/bevy_ecs/src/schedule/set.rs b/crates/bevy_ecs/src/schedule/set.rs index a2d66dc321ff9..da91f616e9b42 100644 --- a/crates/bevy_ecs/src/schedule/set.rs +++ b/crates/bevy_ecs/src/schedule/set.rs @@ -1,4 +1,5 @@ use alloc::boxed::Box; +use bevy_utils::prelude::DebugName; use core::{ any::TypeId, fmt::Debug, @@ -195,10 +196,9 @@ impl SystemTypeSet { impl Debug for SystemTypeSet { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - let mut debugging = f.debug_tuple("SystemTypeSet"); - #[cfg(feature = "debug")] - debugging.field(&format_args!("fn {}()", &core::any::type_name::())); - debugging.finish() + f.debug_tuple("SystemTypeSet") + .field(&format_args!("fn {}()", DebugName::type_name::())) + .finish() } } diff --git a/crates/bevy_ecs/src/schedule/stepping.rs b/crates/bevy_ecs/src/schedule/stepping.rs index f26e907c4925a..b6de7c8215463 100644 --- a/crates/bevy_ecs/src/schedule/stepping.rs +++ b/crates/bevy_ecs/src/schedule/stepping.rs @@ -723,7 +723,7 @@ impl ScheduleState { .get(&node_id) .unwrap_or(&SystemBehavior::Continue); - #[cfg(all(test, feature = "debug"))] + #[cfg(test)] debug!( "skipped_systems(): systems[{}], pos {}, Action::{:?}, Behavior::{:?}, {}", i, diff --git a/crates/bevy_ecs/src/storage/resource.rs b/crates/bevy_ecs/src/storage/resource.rs index 0dacd2428659f..29752ae2e5518 100644 --- a/crates/bevy_ecs/src/storage/resource.rs +++ b/crates/bevy_ecs/src/storage/resource.rs @@ -3,9 +3,8 @@ use crate::{ component::{CheckChangeTicks, ComponentId, ComponentTicks, Components, Tick, TickCells}, storage::{blob_vec::BlobVec, SparseSet}, }; -#[cfg(feature = "debug")] -use alloc::string::String; use bevy_ptr::{OwningPtr, Ptr, UnsafeCellDeref}; +use bevy_utils::prelude::DebugName; use core::{cell::UnsafeCell, mem::ManuallyDrop, panic::Location}; #[cfg(feature = "std")] @@ -24,8 +23,7 @@ pub struct ResourceData { not(feature = "std"), expect(dead_code, reason = "currently only used with the std feature") )] - #[cfg(feature = "debug")] - type_name: String, + type_name: DebugName, #[cfg(feature = "std")] origin_thread_id: Option, changed_by: MaybeLocation>>, @@ -80,19 +78,12 @@ impl ResourceData { #[cfg(feature = "std")] if self.origin_thread_id != Some(std::thread::current().id()) { // Panic in tests, as testing for aborting is nearly impossible - #[cfg(feature = "debug")] panic!( "Attempted to access or drop non-send resource {} from thread {:?} on a thread {:?}. This is not allowed. Aborting.", self.type_name, self.origin_thread_id, std::thread::current().id() ); - #[cfg(not(feature = "debug"))] - panic!( - "Attempted to access or drop non-send resource from thread {:?} on a thread {:?}. This is not allowed. Aborting.", - self.origin_thread_id, - std::thread::current().id() - ); } // TODO: Handle no_std non-send. @@ -376,17 +367,11 @@ impl Resources { self.resources.get_or_insert_with(component_id, || { let component_info = components.get_info(component_id).unwrap(); if SEND { - #[cfg(feature = "debug")] assert!( component_info.is_send_and_sync(), "Send + Sync resource {} initialized as non_send. It may have been inserted via World::insert_non_send_resource by accident. Try using World::insert_resource instead.", component_info.name(), ); - #[cfg(not(feature = "debug"))] - assert!( - component_info.is_send_and_sync(), - "Send + Sync resource initialized as non_send. It may have been inserted via World::insert_non_send_resource by accident. Try using World::insert_resource instead.", - ); } // SAFETY: component_info.drop() is valid for the types that will be inserted. let data = unsafe { @@ -400,8 +385,7 @@ impl Resources { data: ManuallyDrop::new(data), added_ticks: UnsafeCell::new(Tick::new(0)), changed_ticks: UnsafeCell::new(Tick::new(0)), - #[cfg(feature = "debug")] - type_name: String::from(component_info.name()), + type_name: component_info.name(), #[cfg(feature = "std")] origin_thread_id: None, changed_by: MaybeLocation::caller().map(UnsafeCell::new), diff --git a/crates/bevy_ecs/src/system/adapter_system.rs b/crates/bevy_ecs/src/system/adapter_system.rs index dd23f584617a7..c655ed9407f41 100644 --- a/crates/bevy_ecs/src/system/adapter_system.rs +++ b/crates/bevy_ecs/src/system/adapter_system.rs @@ -1,6 +1,5 @@ -#[cfg(feature = "debug")] -use alloc::borrow::Cow; use alloc::vec::Vec; +use bevy_utils::prelude::DebugName; use super::{IntoSystem, ReadOnlySystem, System, SystemParamValidationError}; use crate::{ @@ -93,14 +92,8 @@ where // Required method fn into_system(this: Self) -> Self::System { let system = IntoSystem::into_system(this.system); - #[cfg(feature = "debug")] let name = system.name(); - AdapterSystem::new( - this.func, - system, - #[cfg(feature = "debug")] - name, - ) + AdapterSystem::new(this.func, system, name) } } @@ -109,8 +102,7 @@ where pub struct AdapterSystem { func: Func, system: S, - #[cfg(feature = "debug")] - name: Cow<'static, str>, + name: DebugName, } impl AdapterSystem @@ -119,17 +111,8 @@ where S: System, { /// Creates a new [`System`] that uses `func` to adapt `system`, via the [`Adapt`] trait. - pub const fn new( - func: Func, - system: S, - #[cfg(feature = "debug")] name: Cow<'static, str>, - ) -> Self { - Self { - func, - system, - #[cfg(feature = "debug")] - name, - } + pub const fn new(func: Func, system: S, name: DebugName) -> Self { + Self { func, system, name } } } @@ -141,8 +124,7 @@ where type In = Func::In; type Out = Func::Out; - #[cfg(feature = "debug")] - fn name(&self) -> Cow<'static, str> { + fn name(&self) -> DebugName { self.name.clone() } diff --git a/crates/bevy_ecs/src/system/combinator.rs b/crates/bevy_ecs/src/system/combinator.rs index 2d12593c3c2af..4005dfb8a4dd8 100644 --- a/crates/bevy_ecs/src/system/combinator.rs +++ b/crates/bevy_ecs/src/system/combinator.rs @@ -1,6 +1,5 @@ -use alloc::vec::Vec; -#[cfg(feature = "debug")] -use alloc::{borrow::Cow, format}; +use alloc::{format, vec::Vec}; +use bevy_utils::prelude::DebugName; use core::marker::PhantomData; use crate::{ @@ -114,20 +113,18 @@ pub struct CombinatorSystem { _marker: PhantomData Func>, a: A, b: B, - #[cfg(feature = "debug")] - name: Cow<'static, str>, + name: DebugName, } impl CombinatorSystem { /// Creates a new system that combines two inner systems. /// /// The returned system will only be usable if `Func` implements [`Combine`]. - pub fn new(a: A, b: B, #[cfg(feature = "debug")] name: Cow<'static, str>) -> Self { + pub fn new(a: A, b: B, name: DebugName) -> Self { Self { _marker: PhantomData, a, b, - #[cfg(feature = "debug")] name, } } @@ -142,8 +139,7 @@ where type In = Func::In; type Out = Func::Out; - #[cfg(feature = "debug")] - fn name(&self) -> Cow<'static, str> { + fn name(&self) -> DebugName { self.name.clone() } @@ -242,12 +238,7 @@ where { /// Clone the combined system. The cloned instance must be `.initialize()`d before it can run. fn clone(&self) -> Self { - CombinatorSystem::new( - self.a.clone(), - self.b.clone(), - #[cfg(feature = "debug")] - self.name.clone(), - ) + CombinatorSystem::new(self.a.clone(), self.b.clone(), self.name.clone()) } } @@ -280,14 +271,8 @@ where fn into_system(this: Self) -> Self::System { let system_a = IntoSystem::into_system(this.a); let system_b = IntoSystem::into_system(this.b); - #[cfg(feature = "debug")] let name = format!("Pipe({}, {})", system_a.name(), system_b.name()); - PipeSystem::new( - system_a, - system_b, - #[cfg(feature = "debug")] - Cow::Owned(name), - ) + PipeSystem::new(system_a, system_b, DebugName::owned(name)) } } @@ -333,8 +318,7 @@ where pub struct PipeSystem { a: A, b: B, - #[cfg(feature = "debug")] - name: Cow<'static, str>, + name: DebugName, } impl PipeSystem @@ -344,13 +328,8 @@ where for<'a> B::In: SystemInput = A::Out>, { /// Creates a new system that pipes two inner systems. - pub fn new(a: A, b: B, #[cfg(feature = "debug")] name: Cow<'static, str>) -> Self { - Self { - a, - b, - #[cfg(feature = "debug")] - name, - } + pub fn new(a: A, b: B, name: DebugName) -> Self { + Self { a, b, name } } } @@ -363,8 +342,7 @@ where type In = A::In; type Out = B::Out; - #[cfg(feature = "debug")] - fn name(&self) -> Cow<'static, str> { + fn name(&self) -> DebugName { self.name.clone() } diff --git a/crates/bevy_ecs/src/system/commands/entity_command.rs b/crates/bevy_ecs/src/system/commands/entity_command.rs index 975076a07d0c8..7414b85461dbb 100644 --- a/crates/bevy_ecs/src/system/commands/entity_command.rs +++ b/crates/bevy_ecs/src/system/commands/entity_command.rs @@ -4,16 +4,13 @@ //! It also contains functions that return closures for use with //! [`EntityCommands`](crate::system::EntityCommands). -// #[cfg(feature = "debug")] use alloc::vec::Vec; use log::info; -#[cfg(feature = "debug")] -use crate::component::ComponentInfo; use crate::{ bundle::{Bundle, InsertMode}, change_detection::MaybeLocation, - component::{Component, ComponentId}, + component::{Component, ComponentId, ComponentInfo}, entity::{Entity, EntityClonerBuilder}, event::Event, relationship::RelationshipHookMode, @@ -274,18 +271,12 @@ pub fn move_components(target: Entity) -> impl EntityCommand { /// An [`EntityCommand`] that logs the components of an entity. pub fn log_components() -> impl EntityCommand { move |entity: EntityWorldMut| { - #[cfg(feature = "debug")] let debug_infos: Vec<_> = entity .world() .inspect_entity(entity.id()) .expect("Entity existence is verified before an EntityCommand is executed") .map(ComponentInfo::name) .collect(); - #[cfg(not(feature = "debug"))] - let debug_infos = { - let mut disabled = Vec::new(); - disabled.push("debug feature not enabled"); - }; info!("Entity {}: {debug_infos:?}", entity.id()); } } diff --git a/crates/bevy_ecs/src/system/exclusive_function_system.rs b/crates/bevy_ecs/src/system/exclusive_function_system.rs index 1211b56c2e174..3a053f89d5ac4 100644 --- a/crates/bevy_ecs/src/system/exclusive_function_system.rs +++ b/crates/bevy_ecs/src/system/exclusive_function_system.rs @@ -1,19 +1,16 @@ -#[cfg(feature = "debug")] -use crate::system::check_system_change_tick; use crate::{ component::{CheckChangeTicks, ComponentId, Tick}, query::FilteredAccessSet, schedule::{InternedSystemSet, SystemSet}, system::{ - ExclusiveSystemParam, ExclusiveSystemParamItem, IntoSystem, System, SystemIn, SystemInput, - SystemMeta, + check_system_change_tick, ExclusiveSystemParam, ExclusiveSystemParamItem, IntoSystem, + System, SystemIn, SystemInput, SystemMeta, }, world::{unsafe_world_cell::UnsafeWorldCell, World}, }; -#[cfg(feature = "debug")] -use alloc::borrow::Cow; -use alloc::{vec, vec::Vec}; +use alloc::{borrow::Cow, vec, vec::Vec}; +use bevy_utils::prelude::DebugName; use core::marker::PhantomData; use variadics_please::all_tuples; @@ -45,9 +42,8 @@ where /// Return this system with a new name. /// /// Useful to give closure systems more readable and unique names for debugging and tracing. - #[cfg(feature = "debug")] pub fn with_name(mut self, new_name: impl Into>) -> Self { - self.system_meta.set_name(new_name.into()); + self.system_meta.set_name(new_name); self } } @@ -88,8 +84,7 @@ where type Out = F::Out; #[inline] - #[cfg(feature = "debug")] - fn name(&self) -> Cow<'static, str> { + fn name(&self) -> DebugName { self.system_meta.name.clone() } @@ -183,13 +178,11 @@ where } #[inline] - #[cfg_attr(not(feature = "debug"), expect(unused_variables))] fn check_change_tick(&mut self, check: CheckChangeTicks) { - #[cfg(feature = "debug")] check_system_change_tick( &mut self.system_meta.last_run, check, - self.system_meta.name.as_ref(), + self.system_meta.name.clone(), ); } diff --git a/crates/bevy_ecs/src/system/function_system.rs b/crates/bevy_ecs/src/system/function_system.rs index 236adfe6a159a..340b9738f1dc6 100644 --- a/crates/bevy_ecs/src/system/function_system.rs +++ b/crates/bevy_ecs/src/system/function_system.rs @@ -1,17 +1,17 @@ -#[cfg(feature = "debug")] -use crate::system::check_system_change_tick; use crate::{ component::{CheckChangeTicks, ComponentId, Tick}, prelude::FromWorld, query::FilteredAccessSet, schedule::{InternedSystemSet, SystemSet}, - system::{ReadOnlySystemParam, System, SystemIn, SystemInput, SystemParam, SystemParamItem}, + system::{ + check_system_change_tick, ReadOnlySystemParam, System, SystemIn, SystemInput, SystemParam, + SystemParamItem, + }, world::{unsafe_world_cell::UnsafeWorldCell, DeferredWorld, World, WorldId}, }; -#[cfg(feature = "debug")] -use alloc::borrow::Cow; -use alloc::{vec, vec::Vec}; +use alloc::{borrow::Cow, vec, vec::Vec}; +use bevy_utils::prelude::DebugName; use core::marker::PhantomData; use variadics_please::all_tuples; @@ -25,8 +25,7 @@ use super::{ /// The metadata of a [`System`]. #[derive(Clone)] pub struct SystemMeta { - #[cfg(feature = "debug")] - pub(crate) name: Cow<'static, str>, + pub(crate) name: DebugName, // NOTE: this must be kept private. making a SystemMeta non-send is irreversible to prevent // SystemParams from overriding each other flags: SystemStateFlags, @@ -39,11 +38,8 @@ pub struct SystemMeta { impl SystemMeta { pub(crate) fn new() -> Self { - #[cfg(feature = "debug")] - let name = core::any::type_name::(); Self { - #[cfg(feature = "debug")] - name: name.into(), + name: DebugName::type_name::(), flags: SystemStateFlags::empty(), last_run: Tick::new(0), #[cfg(feature = "trace")] @@ -55,8 +51,7 @@ impl SystemMeta { /// Returns the system's name #[inline] - #[cfg(feature = "debug")] - pub fn name(&self) -> &str { + pub fn name(&self) -> &DebugName { &self.name } @@ -64,7 +59,6 @@ impl SystemMeta { /// /// Useful to give closure systems more readable and unique names for debugging and tracing. #[inline] - #[cfg(feature = "debug")] pub fn set_name(&mut self, new_name: impl Into>) { let new_name: Cow<'static, str> = new_name.into(); #[cfg(feature = "trace")] @@ -73,7 +67,7 @@ impl SystemMeta { self.system_span = info_span!("system", name = name); self.commands_span = info_span!("system_commands", name = name); } - self.name = new_name; + self.name = new_name.into(); } /// Returns true if the system is [`Send`]. @@ -539,7 +533,6 @@ where /// Return this system with a new name. /// /// Useful to give closure systems more readable and unique names for debugging and tracing. - #[cfg(feature = "debug")] pub fn with_name(mut self, new_name: impl Into>) -> Self { self.system_meta.set_name(new_name.into()); self @@ -607,8 +600,7 @@ where type Out = F::Out; #[inline] - #[cfg(feature = "debug")] - fn name(&self) -> Cow<'static, str> { + fn name(&self) -> DebugName { self.system_meta.name.clone() } @@ -716,13 +708,11 @@ where } #[inline] - #[cfg_attr(not(feature = "debug"), expect(unused_variables))] fn check_change_tick(&mut self, check: CheckChangeTicks) { - #[cfg(feature = "debug")] check_system_change_tick( &mut self.system_meta.last_run, check, - self.system_meta.name.as_ref(), + self.system_meta.name.clone(), ); } diff --git a/crates/bevy_ecs/src/system/mod.rs b/crates/bevy_ecs/src/system/mod.rs index 5d56f968e65d4..db4cd452c0b99 100644 --- a/crates/bevy_ecs/src/system/mod.rs +++ b/crates/bevy_ecs/src/system/mod.rs @@ -131,7 +131,6 @@ mod observer_system; mod query; mod schedule_system; mod system; -#[cfg(feature = "debug")] mod system_name; mod system_param; mod system_registry; @@ -150,7 +149,6 @@ pub use observer_system::*; pub use query::*; pub use schedule_system::*; pub use system::*; -#[cfg(feature = "debug")] pub use system_name::*; pub use system_param::*; pub use system_registry::*; diff --git a/crates/bevy_ecs/src/system/observer_system.rs b/crates/bevy_ecs/src/system/observer_system.rs index c3c99a56f22bc..8e927d9529a32 100644 --- a/crates/bevy_ecs/src/system/observer_system.rs +++ b/crates/bevy_ecs/src/system/observer_system.rs @@ -1,6 +1,5 @@ -#[cfg(feature = "debug")] -use alloc::borrow::Cow; use alloc::vec::Vec; +use bevy_utils::prelude::DebugName; use core::marker::PhantomData; use crate::{ @@ -114,8 +113,7 @@ where type Out = Result; #[inline] - #[cfg(feature = "debug")] - fn name(&self) -> Cow<'static, str> { + fn name(&self) -> DebugName { self.observer.name() } diff --git a/crates/bevy_ecs/src/system/query.rs b/crates/bevy_ecs/src/system/query.rs index dfc07d23f1bb0..b7a5241453dd3 100644 --- a/crates/bevy_ecs/src/system/query.rs +++ b/crates/bevy_ecs/src/system/query.rs @@ -1,3 +1,5 @@ +use bevy_utils::prelude::DebugName; + use crate::{ batching::BatchingStrategy, component::Tick, @@ -1946,8 +1948,8 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> { match (first, extra) { (Some(r), false) => Ok(r), - (None, _) => Err(QuerySingleError::NoEntities(core::any::type_name::())), - (Some(_), _) => Err(QuerySingleError::MultipleEntities(core::any::type_name::< + (None, _) => Err(QuerySingleError::NoEntities(DebugName::type_name::())), + (Some(_), _) => Err(QuerySingleError::MultipleEntities(DebugName::type_name::< Self, >())), } diff --git a/crates/bevy_ecs/src/system/schedule_system.rs b/crates/bevy_ecs/src/system/schedule_system.rs index 6aeea09513df9..bbf4a7dcd6f1b 100644 --- a/crates/bevy_ecs/src/system/schedule_system.rs +++ b/crates/bevy_ecs/src/system/schedule_system.rs @@ -1,6 +1,5 @@ -#[cfg(feature = "debug")] -use alloc::borrow::Cow; use alloc::vec::Vec; +use bevy_utils::prelude::DebugName; use crate::{ component::{CheckChangeTicks, ComponentId, Tick}, @@ -27,8 +26,7 @@ impl> System for InfallibleSystemWrapper { type Out = Result; #[inline] - #[cfg(feature = "debug")] - fn name(&self) -> Cow<'static, str> { + fn name(&self) -> DebugName { self.0.name() } @@ -143,8 +141,7 @@ where type Out = S::Out; - #[cfg(feature = "debug")] - fn name(&self) -> Cow<'static, str> { + fn name(&self) -> DebugName { self.system.name() } @@ -238,8 +235,7 @@ where type Out = S::Out; - #[cfg(feature = "debug")] - fn name(&self) -> Cow<'static, str> { + fn name(&self) -> DebugName { self.system.name() } diff --git a/crates/bevy_ecs/src/system/system.rs b/crates/bevy_ecs/src/system/system.rs index cb6efe38a689f..8527f3d3b84ce 100644 --- a/crates/bevy_ecs/src/system/system.rs +++ b/crates/bevy_ecs/src/system/system.rs @@ -2,9 +2,9 @@ clippy::module_inception, reason = "This instance of module inception is being discussed; see #17353." )] +use bevy_utils::prelude::DebugName; use bitflags::bitflags; use core::fmt::Debug; -#[cfg(feature = "debug")] use log::warn; use thiserror::Error; @@ -16,7 +16,7 @@ use crate::{ world::{unsafe_world_cell::UnsafeWorldCell, DeferredWorld, World}, }; -use alloc::{borrow::Cow, boxed::Box, vec::Vec}; +use alloc::{boxed::Box, vec::Vec}; use core::any::TypeId; use super::{IntoSystem, SystemParamValidationError}; @@ -52,8 +52,7 @@ pub trait System: Send + Sync + 'static { type Out; /// Returns the system's name. - #[cfg(feature = "debug")] - fn name(&self) -> Cow<'static, str>; + fn name(&self) -> DebugName; /// Returns the [`TypeId`] of the underlying system type. #[inline] fn type_id(&self) -> TypeId { @@ -227,11 +226,10 @@ pub unsafe trait ReadOnlySystem: System { /// A convenience type alias for a boxed [`System`] trait object. pub type BoxedSystem = Box>; -#[cfg(feature = "debug")] pub(crate) fn check_system_change_tick( last_run: &mut Tick, check: CheckChangeTicks, - system_name: &str, + system_name: DebugName, ) { if last_run.check_tick(check) { let age = check.present_tick().relative_to(*last_run).get(); @@ -243,7 +241,6 @@ pub(crate) fn check_system_change_tick( } } -#[cfg(feature = "debug")] impl Debug for dyn System where In: SystemInput + 'static, @@ -385,15 +382,12 @@ impl RunSystemOnce for &mut World { { let mut system: T::System = IntoSystem::into_system(system); system.initialize(self); - system.validate_param(self).map_err(|err| { - #[cfg(feature = "debug")] - return RunSystemError::InvalidParams { + system + .validate_param(self) + .map_err(|err| RunSystemError::InvalidParams { system: system.name(), err, - }; - #[cfg(not(feature = "debug"))] - return RunSystemError::AnonymousInvalidParams { err }; - })?; + })?; Ok(system.run(input, self)) } } @@ -406,14 +400,7 @@ pub enum RunSystemError { #[error("System {system} did not run due to failed parameter validation: {err}")] InvalidParams { /// The identifier of the system that was run. - system: Cow<'static, str>, - /// The returned parameter validation error. - err: SystemParamValidationError, - }, - /// System could not be run due to parameters that failed validation. - /// This should not be considered an error if [`field@SystemParamValidationError::skipped`] is `true`. - #[error("A system did not run due to failed parameter validation: {err}")] - AnonymousInvalidParams { + system: DebugName, /// The returned parameter validation error. err: SystemParamValidationError, }, diff --git a/crates/bevy_ecs/src/system/system_name.rs b/crates/bevy_ecs/src/system/system_name.rs index 1205ff055c953..f38a5eb1aa72d 100644 --- a/crates/bevy_ecs/src/system/system_name.rs +++ b/crates/bevy_ecs/src/system/system_name.rs @@ -5,9 +5,8 @@ use crate::{ system::{ExclusiveSystemParam, ReadOnlySystemParam, SystemMeta, SystemParam}, world::unsafe_world_cell::UnsafeWorldCell, }; -use alloc::borrow::Cow; -use core::ops::Deref; -use derive_more::derive::{AsRef, Display, Into}; +use bevy_utils::prelude::DebugName; +use derive_more::derive::{Display, Into}; /// [`SystemParam`] that returns the name of the system which it is used in. /// @@ -35,21 +34,13 @@ use derive_more::derive::{AsRef, Display, Into}; /// logger.log("Hello"); /// } /// ``` -#[derive(Debug, Into, Display, AsRef)] -#[as_ref(str)] -pub struct SystemName(Cow<'static, str>); +#[derive(Debug, Into, Display)] +pub struct SystemName(DebugName); impl SystemName { /// Gets the name of the system. - pub fn name(&self) -> &str { - &self.0 - } -} - -impl Deref for SystemName { - type Target = str; - fn deref(&self) -> &Self::Target { - self.name() + pub fn name(&self) -> DebugName { + self.0.clone() } } @@ -104,7 +95,7 @@ mod tests { #[test] fn test_system_name_regular_param() { fn testing(name: SystemName) -> String { - name.name().to_owned() + name.name().as_string() } let mut world = World::default(); @@ -116,7 +107,7 @@ mod tests { #[test] fn test_system_name_exclusive_param() { fn testing(_world: &mut World, name: SystemName) -> String { - name.name().to_owned() + name.name().as_string() } let mut world = World::default(); @@ -130,7 +121,7 @@ mod tests { let mut world = World::default(); let system = IntoSystem::into_system(|name: SystemName| name.name().to_owned()).with_name("testing"); - let name = world.run_system_once(system).unwrap(); + let name = world.run_system_once(system).unwrap().as_string(); assert_eq!(name, "testing"); } @@ -140,7 +131,7 @@ mod tests { let system = IntoSystem::into_system(|_world: &mut World, name: SystemName| name.name().to_owned()) .with_name("testing"); - let name = world.run_system_once(system).unwrap(); + let name = world.run_system_once(system).unwrap().as_string(); assert_eq!(name, "testing"); } } diff --git a/crates/bevy_ecs/src/system/system_param.rs b/crates/bevy_ecs/src/system/system_param.rs index d480799273810..5d1b09c1eb46b 100644 --- a/crates/bevy_ecs/src/system/system_param.rs +++ b/crates/bevy_ecs/src/system/system_param.rs @@ -25,6 +25,7 @@ use alloc::{ pub use bevy_ecs_macros::SystemParam; use bevy_platform::cell::SyncCell; use bevy_ptr::UnsafeCellDeref; +use bevy_utils::prelude::DebugName; use core::{ any::Any, fmt::{Debug, Display}, @@ -32,8 +33,6 @@ use core::{ ops::{Deref, DerefMut}, panic::Location, }; -#[cfg(feature = "debug")] -use disqualified::ShortName; use thiserror::Error; use super::Populated; @@ -336,7 +335,6 @@ unsafe impl SystemParam for Qu QueryState::new(world) } - #[cfg_attr(not(feature = "debug"), expect(unused_variables))] fn init_access( state: &Self::State, system_meta: &mut SystemMeta, @@ -344,12 +342,9 @@ unsafe impl SystemParam for Qu world: &mut World, ) { assert_component_access_compatibility( - #[cfg(feature = "debug")] &system_meta.name, - #[cfg(feature = "debug")] - core::any::type_name::(), - #[cfg(feature = "debug")] - core::any::type_name::(), + DebugName::type_name::(), + DebugName::type_name::(), component_access_set, &state.component_access, world, @@ -372,11 +367,10 @@ unsafe impl SystemParam for Qu } } -#[cfg_attr(not(feature = "debug"), expect(unused_variables))] fn assert_component_access_compatibility( - #[cfg(feature = "debug")] system_name: &str, - #[cfg(feature = "debug")] query_type: &'static str, - #[cfg(feature = "debug")] filter_type: &'static str, + system_name: &DebugName, + query_type: DebugName, + filter_type: DebugName, system_access: &FilteredAccessSet, current: &FilteredAccess, world: &World, @@ -385,17 +379,12 @@ fn assert_component_access_compatibility( if conflicts.is_empty() { return; } - #[cfg(feature = "debug")] - { - let mut accesses = conflicts.format_conflict_list(world); - // Access list may be empty (if access to all components requested) - if !accesses.is_empty() { - accesses.push(' '); - } - panic!("error[B0001]: Query<{}, {}> in system {system_name} accesses component(s) {accesses}in a way that conflicts with a previous system parameter. Consider using `Without` to create disjoint Queries or merging conflicting Queries into a `ParamSet`. See: https://bevy.org/learn/errors/b0001", ShortName(query_type), ShortName(filter_type)); + let mut accesses = conflicts.format_conflict_list(world); + // Access list may be empty (if access to all components requested) + if !accesses.is_empty() { + accesses.push(' '); } - #[cfg(not(feature = "debug"))] - panic!("error[B0001]: A query in a system accesses component(s) in a way that conflicts with a previous system parameter. Consider using `Without` to create disjoint Queries or merging conflicting Queries into a `ParamSet`. See: https://bevy.org/learn/errors/b0001"); + panic!("error[B0001]: Query<{}, {}> in system {system_name} accesses component(s) {accesses}in a way that conflicts with a previous system parameter. Consider using `Without` to create disjoint Queries or merging conflicting Queries into a `ParamSet`. See: https://bevy.org/learn/errors/b0001", query_type.shortname(), filter_type.shortname()); } // SAFETY: Relevant query ComponentId access is applied to SystemMeta. If @@ -766,7 +755,6 @@ unsafe impl<'a, T: Resource> SystemParam for Res<'a, T> { world.components_registrator().register_resource::() } - #[cfg_attr(not(feature = "debug"), expect(unused_variables))] fn init_access( &component_id: &Self::State, system_meta: &mut SystemMeta, @@ -774,18 +762,12 @@ unsafe impl<'a, T: Resource> SystemParam for Res<'a, T> { _world: &mut World, ) { let combined_access = component_access_set.combined_access(); - #[cfg(feature = "debug")] assert!( !combined_access.has_resource_write(component_id), "error[B0002]: Res<{}> in system {} conflicts with a previous ResMut<{0}> access. Consider removing the duplicate access. See: https://bevy.org/learn/errors/b0002", - core::any::type_name::(), + DebugName::type_name::(), system_meta.name, ); - #[cfg(not(feature = "debug"))] - assert!( - !combined_access.has_resource_write(component_id), - "error[B0002]: Res in a system conflicts with a previous ResMut access. Consider removing the duplicate access. See: https://bevy.org/learn/errors/b0002", - ); component_access_set.add_unfiltered_resource_read(component_id); } @@ -821,14 +803,11 @@ unsafe impl<'a, T: Resource> SystemParam for Res<'a, T> { world .get_resource_with_ticks(component_id) .unwrap_or_else(|| { - #[cfg(feature = "debug")] panic!( "Resource requested by {} does not exist: {}", system_meta.name, - core::any::type_name::() + DebugName::type_name::() ); - #[cfg(not(feature = "debug"))] - panic!("Resource requested by a system does not exist",); }); Res { value: ptr.deref(), @@ -853,7 +832,6 @@ unsafe impl<'a, T: Resource> SystemParam for ResMut<'a, T> { world.components_registrator().register_resource::() } - #[cfg_attr(not(feature = "debug"), expect(unused_variables))] fn init_access( &component_id: &Self::State, system_meta: &mut SystemMeta, @@ -862,23 +840,13 @@ unsafe impl<'a, T: Resource> SystemParam for ResMut<'a, T> { ) { let combined_access = component_access_set.combined_access(); if combined_access.has_resource_write(component_id) { - #[cfg(feature = "debug")] panic!( "error[B0002]: ResMut<{}> in system {} conflicts with a previous ResMut<{0}> access. Consider removing the duplicate access. See: https://bevy.org/learn/errors/b0002", - core::any::type_name::(), system_meta.name); - #[cfg(not(feature = "debug"))] - panic!( - "error[B0002]: ResMut in a system conflicts with a previous ResMut access. Consider removing the duplicate access. See: https://bevy.org/learn/errors/b0002", - ); + DebugName::type_name::(), system_meta.name); } else if combined_access.has_resource_read(component_id) { - #[cfg(feature = "debug")] panic!( "error[B0002]: ResMut<{}> in system {} conflicts with a previous Res<{0}> access. Consider removing the duplicate access. See: https://bevy.org/learn/errors/b0002", - core::any::type_name::(), system_meta.name); - #[cfg(not(feature = "debug"))] - panic!( - "error[B0002]: ResMut in a system conflicts with a previous Res access. Consider removing the duplicate access. See: https://bevy.org/learn/errors/b0002", - ); + DebugName::type_name::(), system_meta.name); } component_access_set.add_unfiltered_resource_write(component_id); } @@ -913,14 +881,11 @@ unsafe impl<'a, T: Resource> SystemParam for ResMut<'a, T> { let value = world .get_resource_mut_by_id(component_id) .unwrap_or_else(|| { - #[cfg(feature = "debug")] panic!( "Resource requested by {} does not exist: {}", system_meta.name, - core::any::type_name::() + DebugName::type_name::() ); - #[cfg(not(feature = "debug"))] - panic!("Resource requested by a system does not exist"); }); ResMut { value: value.value.deref_mut::(), @@ -982,24 +947,17 @@ unsafe impl<'w> SystemParam for DeferredWorld<'w> { fn init_state(_world: &mut World) -> Self::State {} - #[cfg_attr(not(feature = "debug"), expect(unused_variables))] fn init_access( _state: &Self::State, system_meta: &mut SystemMeta, component_access_set: &mut FilteredAccessSet, _world: &mut World, ) { - #[cfg(feature = "debug")] assert!( !component_access_set.combined_access().has_any_read(), "DeferredWorld in system {} conflicts with a previous access.", system_meta.name, ); - #[cfg(not(feature = "debug"))] - assert!( - !component_access_set.combined_access().has_any_read(), - "DeferredWorld in a system conflicts with a previous access.", - ); component_access_set.write_all(); } @@ -1473,18 +1431,12 @@ unsafe impl<'a, T: 'static> SystemParam for NonSend<'a, T> { system_meta.set_non_send(); let combined_access = component_access_set.combined_access(); - #[cfg(feature = "debug")] assert!( !combined_access.has_resource_write(component_id), "error[B0002]: NonSend<{}> in system {} conflicts with a previous mutable resource access ({0}). Consider removing the duplicate access. See: https://bevy.org/learn/errors/b0002", - core::any::type_name::(), + DebugName::type_name::(), system_meta.name, ); - #[cfg(not(feature = "debug"))] - assert!( - !combined_access.has_resource_write(component_id), - "error[B0002]: NonSend in a system conflicts with a previous mutable resource access. Consider removing the duplicate access. See: https://bevy.org/learn/errors/b0002", - ); component_access_set.add_unfiltered_resource_read(component_id); } @@ -1519,14 +1471,11 @@ unsafe impl<'a, T: 'static> SystemParam for NonSend<'a, T> { world .get_non_send_with_ticks(component_id) .unwrap_or_else(|| { - #[cfg(feature = "debug")] panic!( "Non-send resource requested by {} does not exist: {}", system_meta.name, - core::any::type_name::() - ); - #[cfg(not(feature = "debug"))] - panic!("Non-send resource requested by a system does not exist"); + DebugName::type_name::() + ) }); NonSend { @@ -1559,23 +1508,13 @@ unsafe impl<'a, T: 'static> SystemParam for NonSendMut<'a, T> { let combined_access = component_access_set.combined_access(); if combined_access.has_component_write(component_id) { - #[cfg(feature = "debug")] panic!( "error[B0002]: NonSendMut<{}> in system {} conflicts with a previous mutable resource access ({0}). Consider removing the duplicate access. See: https://bevy.org/learn/errors/b0002", - core::any::type_name::(), system_meta.name); - #[cfg(not(feature = "debug"))] - panic!( - "error[B0002]: NonSendMut in a system conflicts with a previous mutable resource access. Consider removing the duplicate access. See: https://bevy.org/learn/errors/b0002", - ); + DebugName::type_name::(), system_meta.name); } else if combined_access.has_component_read(component_id) { - #[cfg(feature = "debug")] panic!( "error[B0002]: NonSendMut<{}> in system {} conflicts with a previous immutable resource access ({0}). Consider removing the duplicate access. See: https://bevy.org/learn/errors/b0002", - core::any::type_name::(), system_meta.name); - #[cfg(not(feature = "debug"))] - panic!( - "error[B0002]: NonSendMut in a system conflicts with a previous immutable resource access. Consider removing the duplicate access. See: https://bevy.org/learn/errors/b0002", - ); + DebugName::type_name::(), system_meta.name); } component_access_set.add_unfiltered_resource_write(component_id); } @@ -1611,14 +1550,11 @@ unsafe impl<'a, T: 'static> SystemParam for NonSendMut<'a, T> { world .get_non_send_with_ticks(component_id) .unwrap_or_else(|| { - #[cfg(feature = "debug")] panic!( "Non-send resource requested by {} does not exist: {}", system_meta.name, - core::any::type_name::() + DebugName::type_name::() ); - #[cfg(not(feature = "debug"))] - panic!("Non-send resource requested by a system does not exist",); }); NonSendMut { value: ptr.assert_unique().deref_mut(), @@ -2731,7 +2667,6 @@ unsafe impl SystemParam for FilteredResources<'_, '_> { Access::new() } - #[cfg_attr(not(feature = "debug"), expect(unused_variables))] fn init_access( access: &Self::State, system_meta: &mut SystemMeta, @@ -2741,14 +2676,9 @@ unsafe impl SystemParam for FilteredResources<'_, '_> { let combined_access = component_access_set.combined_access(); let conflicts = combined_access.get_conflicts(access); if !conflicts.is_empty() { - #[cfg(feature = "debug")] - { - let accesses = conflicts.format_conflict_list(world); - let system_name = &system_meta.name; - panic!("error[B0002]: FilteredResources in system {system_name} accesses resources(s){accesses} in a way that conflicts with a previous system parameter. Consider removing the duplicate access. See: https://bevy.org/learn/errors/b0002"); - } - #[cfg(not(feature = "debug"))] - panic!("error[B0002]: FilteredResources in a system accesses resources(s) in a way that conflicts with a previous system parameter. Consider removing the duplicate access. See: https://bevy.org/learn/errors/b0002"); + let accesses = conflicts.format_conflict_list(world); + let system_name = &system_meta.name; + panic!("error[B0002]: FilteredResources in system {system_name} accesses resources(s){accesses} in a way that conflicts with a previous system parameter. Consider removing the duplicate access. See: https://bevy.org/learn/errors/b0002"); } if access.has_read_all_resources() { @@ -2786,7 +2716,6 @@ unsafe impl SystemParam for FilteredResourcesMut<'_, '_> { Access::new() } - #[cfg_attr(not(feature = "debug"), expect(unused_variables))] fn init_access( access: &Self::State, system_meta: &mut SystemMeta, @@ -2796,14 +2725,9 @@ unsafe impl SystemParam for FilteredResourcesMut<'_, '_> { let combined_access = component_access_set.combined_access(); let conflicts = combined_access.get_conflicts(access); if !conflicts.is_empty() { - #[cfg(feature = "debug")] - { - let accesses = conflicts.format_conflict_list(world); - let system_name = &system_meta.name; - panic!("error[B0002]: FilteredResourcesMut in system {system_name} accesses resources(s){accesses} in a way that conflicts with a previous system parameter. Consider removing the duplicate access. See: https://bevy.org/learn/errors/b0002"); - } - #[cfg(not(feature = "debug"))] - panic!("error[B0002]: FilteredResourcesMut in a system accesses resources(s) in a way that conflicts with a previous system parameter. Consider removing the duplicate access. See: https://bevy.org/learn/errors/b0002"); + let accesses = conflicts.format_conflict_list(world); + let system_name = &system_meta.name; + panic!("error[B0002]: FilteredResourcesMut in system {system_name} accesses resources(s){accesses} in a way that conflicts with a previous system parameter. Consider removing the duplicate access. See: https://bevy.org/learn/errors/b0002"); } if access.has_read_all_resources() { @@ -2861,8 +2785,7 @@ pub struct SystemParamValidationError { /// A string identifying the invalid parameter. /// This is usually the type name of the parameter. - #[cfg(feature = "debug")] - pub param: Cow<'static, str>, + pub param: DebugName, /// A string identifying the field within a parameter using `#[derive(SystemParam)]`. /// This will be an empty string for other parameters. @@ -2894,8 +2817,7 @@ impl SystemParamValidationError { Self { skipped, message: message.into(), - #[cfg(feature = "debug")] - param: Cow::Borrowed(core::any::type_name::()), + param: DebugName::type_name::(), field: field.into(), } } @@ -2903,11 +2825,10 @@ impl SystemParamValidationError { impl Display for SystemParamValidationError { fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> { - #[cfg(feature = "debug")] write!( fmt, "Parameter `{}{}` failed validation: {}", - ShortName(&self.param), + self.param.shortname(), self.field, self.message )?; diff --git a/crates/bevy_ecs/src/world/deferred_world.rs b/crates/bevy_ecs/src/world/deferred_world.rs index acf48812c182c..2fa3471e9bc76 100644 --- a/crates/bevy_ecs/src/world/deferred_world.rs +++ b/crates/bevy_ecs/src/world/deferred_world.rs @@ -1,5 +1,7 @@ use core::ops::Deref; +use bevy_utils::prelude::DebugName; + use crate::{ archetype::Archetype, change_detection::{MaybeLocation, MutUntyped}, @@ -451,7 +453,7 @@ impl<'w> DeferredWorld<'w> { Did you forget to add it using `app.insert_resource` / `app.init_resource`? Resources are also implicitly added via `app.add_event`, and can be added by plugins.", - core::any::type_name::() + DebugName::type_name::() ), } } @@ -480,7 +482,7 @@ impl<'w> DeferredWorld<'w> { "Requested non-send resource {} does not exist in the `World`. Did you forget to add it using `app.insert_non_send_resource` / `app.init_non_send_resource`? Non-send resources can also be added by plugins.", - core::any::type_name::() + DebugName::type_name::() ), } } @@ -523,7 +525,7 @@ impl<'w> DeferredWorld<'w> { let Some(mut events_resource) = self.get_resource_mut::>() else { log::error!( "Unable to send event `{}`\n\tEvent must be added to the app with `add_event()`\n\thttps://docs.rs/bevy/*/bevy/app/struct.App.html#method.add_event ", - core::any::type_name::() + DebugName::type_name::() ); return None; }; diff --git a/crates/bevy_ecs/src/world/error.rs b/crates/bevy_ecs/src/world/error.rs index 3527967942a94..03574331f2329 100644 --- a/crates/bevy_ecs/src/world/error.rs +++ b/crates/bevy_ecs/src/world/error.rs @@ -1,6 +1,7 @@ //! Contains error types returned by bevy's schedule. use alloc::vec::Vec; +use bevy_utils::prelude::DebugName; use crate::{ component::ComponentId, @@ -24,7 +25,7 @@ pub struct TryRunScheduleError(pub InternedScheduleLabel); #[error("Could not insert bundles of type {bundle_type} into the entities with the following IDs because they do not exist: {entities:?}")] pub struct TryInsertBatchError { /// The bundles' type name. - pub bundle_type: &'static str, + pub bundle_type: DebugName, /// The IDs of the provided entities that do not exist. pub entities: Vec, } diff --git a/crates/bevy_ecs/src/world/mod.rs b/crates/bevy_ecs/src/world/mod.rs index 7cffac4f2146c..a5ead032dbc94 100644 --- a/crates/bevy_ecs/src/world/mod.rs +++ b/crates/bevy_ecs/src/world/mod.rs @@ -23,6 +23,7 @@ use crate::{ prelude::{Add, Despawn, Insert, Remove, Replace}, }; pub use bevy_ecs_macros::FromWorld; +use bevy_utils::prelude::DebugName; pub use deferred_world::DeferredWorld; pub use entity_fetch::{EntityFetcher, WorldEntityFetch}; pub use entity_ref::{ @@ -1941,7 +1942,7 @@ impl World { Did you forget to add it using `app.insert_resource` / `app.init_resource`? Resources are also implicitly added via `app.add_event`, and can be added by plugins.", - core::any::type_name::() + DebugName::type_name::() ), } } @@ -1965,7 +1966,7 @@ impl World { Did you forget to add it using `app.insert_resource` / `app.init_resource`? Resources are also implicitly added via `app.add_event`, and can be added by plugins.", - core::any::type_name::() + DebugName::type_name::() ), } } @@ -1989,7 +1990,7 @@ impl World { Did you forget to add it using `app.insert_resource` / `app.init_resource`? Resources are also implicitly added via `app.add_event`, and can be added by plugins.", - core::any::type_name::() + DebugName::type_name::() ), } } @@ -2153,7 +2154,7 @@ impl World { "Requested non-send resource {} does not exist in the `World`. Did you forget to add it using `app.insert_non_send_resource` / `app.init_non_send_resource`? Non-send resources can also be added by plugins.", - core::any::type_name::() + DebugName::type_name::() ), } } @@ -2175,7 +2176,7 @@ impl World { "Requested non-send resource {} does not exist in the `World`. Did you forget to add it using `app.insert_non_send_resource` / `app.init_non_send_resource`? Non-send resources can also be added by plugins.", - core::any::type_name::() + DebugName::type_name::() ), } } @@ -2342,11 +2343,11 @@ impl World { ) }; } else { - panic!("error[B0003]: Could not insert a bundle (of type `{}`) for entity {entity}, which {}. See: https://bevy.org/learn/errors/b0003", core::any::type_name::(), self.entities.entity_does_not_exist_error_details(entity)); + panic!("error[B0003]: Could not insert a bundle (of type `{}`) for entity {entity}, which {}. See: https://bevy.org/learn/errors/b0003", DebugName::type_name::(), self.entities.entity_does_not_exist_error_details(entity)); } } } else { - panic!("error[B0003]: Could not insert a bundle (of type `{}`) for entity {first_entity}, which {}. See: https://bevy.org/learn/errors/b0003", core::any::type_name::(), self.entities.entity_does_not_exist_error_details(first_entity)); + panic!("error[B0003]: Could not insert a bundle (of type `{}`) for entity {first_entity}, which {}. See: https://bevy.org/learn/errors/b0003", DebugName::type_name::(), self.entities.entity_does_not_exist_error_details(first_entity)); } } } @@ -2510,7 +2511,7 @@ impl World { Ok(()) } else { Err(TryInsertBatchError { - bundle_type: core::any::type_name::(), + bundle_type: DebugName::type_name::(), entities: invalid_entities, }) } @@ -2544,7 +2545,7 @@ impl World { #[track_caller] pub fn resource_scope(&mut self, f: impl FnOnce(&mut World, Mut) -> U) -> U { self.try_resource_scope(f) - .unwrap_or_else(|| panic!("resource does not exist: {}", core::any::type_name::())) + .unwrap_or_else(|| panic!("resource does not exist: {}", DebugName::type_name::())) } /// Temporarily removes the requested resource from this [`World`] if it exists, runs custom user code, @@ -2584,7 +2585,7 @@ impl World { assert!(!self.contains_resource::(), "Resource `{}` was inserted during a call to World::resource_scope.\n\ This is not allowed as the original resource is reinserted to the world after the closure is invoked.", - core::any::type_name::()); + DebugName::type_name::()); OwningPtr::make(value, |ptr| { // SAFETY: pointer is of type R @@ -2625,7 +2626,7 @@ impl World { let Some(mut events_resource) = self.get_resource_mut::>() else { log::error!( "Unable to send event `{}`\n\tEvent must be added to the app with `add_event()`\n\thttps://docs.rs/bevy/*/bevy/app/struct.App.html#method.add_event ", - core::any::type_name::() + DebugName::type_name::() ); return None; }; @@ -3441,7 +3442,6 @@ impl World { let old = self.resource_mut::().insert(schedule); if old.is_some() { - #[cfg(feature = "debug")] warn!("Schedule `{label:?}` was inserted during a call to `World::schedule_scope`: its value has been overwritten"); } @@ -3627,6 +3627,7 @@ mod tests { }; use bevy_ecs_macros::Component; use bevy_platform::collections::{HashMap, HashSet}; + use bevy_utils::prelude::DebugName; use core::{ any::TypeId, panic, @@ -3809,15 +3810,13 @@ mod tests { let mut iter = world.iter_resources(); - let (_info, ptr) = iter.next().unwrap(); - #[cfg(feature = "debug")] - assert_eq!(_info.name(), core::any::type_name::()); + let (info, ptr) = iter.next().unwrap(); + assert_eq!(info.name(), DebugName::type_name::()); // SAFETY: We know that the resource is of type `TestResource` assert_eq!(unsafe { ptr.deref::().0 }, 42); - let (_info, ptr) = iter.next().unwrap(); - #[cfg(feature = "debug")] - assert_eq!(_info.name(), core::any::type_name::()); + let (info, ptr) = iter.next().unwrap(); + assert_eq!(info.name(), DebugName::type_name::()); assert_eq!( // SAFETY: We know that the resource is of type `TestResource2` unsafe { &ptr.deref::().0 }, @@ -3839,17 +3838,15 @@ mod tests { let mut iter = world.iter_resources_mut(); - let (_info, mut mut_untyped) = iter.next().unwrap(); - #[cfg(feature = "debug")] - assert_eq!(_info.name(), core::any::type_name::()); + let (info, mut mut_untyped) = iter.next().unwrap(); + assert_eq!(info.name(), DebugName::type_name::()); // SAFETY: We know that the resource is of type `TestResource` unsafe { mut_untyped.as_mut().deref_mut::().0 = 43; }; - let (_info, mut mut_untyped) = iter.next().unwrap(); - #[cfg(feature = "debug")] - assert_eq!(_info.name(), core::any::type_name::()); + let (info, mut mut_untyped) = iter.next().unwrap(); + assert_eq!(info.name(), DebugName::type_name::()); // SAFETY: We know that the resource is of type `TestResource2` unsafe { mut_untyped.as_mut().deref_mut::().0 = "Hello, world?".to_string(); diff --git a/crates/bevy_ecs/src/world/reflect.rs b/crates/bevy_ecs/src/world/reflect.rs index 3d8b94bd60e1c..aada63bf6131c 100644 --- a/crates/bevy_ecs/src/world/reflect.rs +++ b/crates/bevy_ecs/src/world/reflect.rs @@ -2,12 +2,11 @@ use core::any::TypeId; -use alloc::string::String; -#[cfg(feature = "debug")] -use alloc::string::ToString; -use bevy_reflect::{Reflect, ReflectFromPtr}; use thiserror::Error; +use bevy_reflect::{Reflect, ReflectFromPtr}; +use bevy_utils::prelude::DebugName; + use crate::{prelude::*, world::ComponentId}; impl World { @@ -78,13 +77,7 @@ impl World { }; let Some(comp_ptr) = self.get_by_id(entity, component_id) else { - #[cfg(feature = "debug")] - let component_name = self - .components() - .get_name(component_id) - .map(|name| name.to_string()); - #[cfg(not(feature = "debug"))] - let component_name = None; + let component_name = self.components().get_name(component_id); return Err(GetComponentReflectError::EntityDoesNotHaveComponent { entity, @@ -170,16 +163,9 @@ impl World { // HACK: Only required for the `None`-case/`else`-branch, but it borrows `self`, which will // already be mutably borrowed by `self.get_mut_by_id()`, and I didn't find a way around it. - #[cfg(feature = "debug")] - let component_name = self - .components() - .get_name(component_id) - .map(|name| name.to_string()); + let component_name = self.components().get_name(component_id).clone(); let Some(comp_mut_untyped) = self.get_mut_by_id(entity, component_id) else { - #[cfg(not(feature = "debug"))] - let component_name = None; - return Err(GetComponentReflectError::EntityDoesNotHaveComponent { entity, type_id, @@ -231,7 +217,7 @@ pub enum GetComponentReflectError { component_id: ComponentId, /// The name corresponding to the [`Component`] with the given [`TypeId`], or `None` /// if not available. - component_name: Option, + component_name: Option, }, /// The [`World`] was missing the [`AppTypeRegistry`] resource. diff --git a/crates/bevy_internal/Cargo.toml b/crates/bevy_internal/Cargo.toml index bb46e0dcc261d..c9639f1950aec 100644 --- a/crates/bevy_internal/Cargo.toml +++ b/crates/bevy_internal/Cargo.toml @@ -346,7 +346,7 @@ web = [ hotpatching = ["bevy_app/hotpatching", "bevy_ecs/hotpatching"] -debug = ["bevy_ecs/debug", "bevy_scene/debug"] +debug = ["bevy_utils/debug"] [dependencies] # bevy (no_std) diff --git a/crates/bevy_remote/Cargo.toml b/crates/bevy_remote/Cargo.toml index 21325eb3b5833..5e4c2ba924ff4 100644 --- a/crates/bevy_remote/Cargo.toml +++ b/crates/bevy_remote/Cargo.toml @@ -17,12 +17,13 @@ http = ["dep:async-io", "dep:smol-hyper"] bevy_app = { path = "../bevy_app", version = "0.16.0-dev" } bevy_derive = { path = "../bevy_derive", version = "0.16.0-dev" } bevy_ecs = { path = "../bevy_ecs", version = "0.16.0-dev", features = [ - "debug", "serialize", ] } bevy_reflect = { path = "../bevy_reflect", version = "0.16.0-dev" } bevy_tasks = { path = "../bevy_tasks", version = "0.16.0-dev" } -bevy_utils = { path = "../bevy_utils", version = "0.16.0-dev" } +bevy_utils = { path = "../bevy_utils", version = "0.16.0-dev", features = [ + "debug" +] } bevy_platform = { path = "../bevy_platform", version = "0.16.0-dev", default-features = false, features = [ "std", "serialize", diff --git a/crates/bevy_scene/Cargo.toml b/crates/bevy_scene/Cargo.toml index c4cde29040107..8a6fe517fde04 100644 --- a/crates/bevy_scene/Cargo.toml +++ b/crates/bevy_scene/Cargo.toml @@ -17,8 +17,6 @@ serialize = [ "bevy_platform/serialize", ] -debug = ["bevy_ecs/debug"] - [dependencies] # bevy bevy_app = { path = "../bevy_app", version = "0.16.0-dev" } diff --git a/crates/bevy_scene/src/scene.rs b/crates/bevy_scene/src/scene.rs index f0b3a421abf0a..89919fbfa645e 100644 --- a/crates/bevy_scene/src/scene.rs +++ b/crates/bevy_scene/src/scene.rs @@ -90,12 +90,9 @@ impl Scene { .expect("reflected resources must have a type_id"); let registration = type_registry.get(type_id).ok_or_else(|| { - #[cfg(feature = "debug")] return SceneSpawnError::UnregisteredType { - std_type_name: component_info.name().to_string(), + std_type_name: component_info.name(), }; - #[cfg(not(feature = "debug"))] - return SceneSpawnError::AnonymousUnregisteredType; })?; let reflect_resource = registration.data::().ok_or_else(|| { SceneSpawnError::UnregisteredResource { @@ -135,12 +132,9 @@ impl Scene { let registration = type_registry .get(component_info.type_id().unwrap()) .ok_or_else(|| { - #[cfg(feature = "debug")] return SceneSpawnError::UnregisteredType { - std_type_name: component_info.name().to_string(), + std_type_name: component_info.name(), }; - #[cfg(not(feature = "debug"))] - return SceneSpawnError::AnonymousUnregisteredType; })?; let reflect_component = registration.data::().ok_or_else(|| { diff --git a/crates/bevy_scene/src/scene_spawner.rs b/crates/bevy_scene/src/scene_spawner.rs index 9f62b50a6ce18..efe1ec7732fa2 100644 --- a/crates/bevy_scene/src/scene_spawner.rs +++ b/crates/bevy_scene/src/scene_spawner.rs @@ -10,6 +10,7 @@ use bevy_ecs::{ }; use bevy_platform::collections::{HashMap, HashSet}; use bevy_reflect::Reflect; +use bevy_utils::prelude::DebugName; use thiserror::Error; use uuid::Uuid; @@ -105,7 +106,7 @@ pub enum SceneSpawnError { )] UnregisteredType { /// The [type name](std::any::type_name) for the unregistered type. - std_type_name: String, + std_type_name: DebugName, }, /// Scene contains an unregistered type. #[error( diff --git a/crates/bevy_utils/Cargo.toml b/crates/bevy_utils/Cargo.toml index 53eda0d3584e8..aaf6a0834e4dc 100644 --- a/crates/bevy_utils/Cargo.toml +++ b/crates/bevy_utils/Cargo.toml @@ -16,9 +16,14 @@ wgpu_wrapper = ["dep:send_wrapper"] # Provides access to the `Parallel` type. parallel = ["bevy_platform/std", "dep:thread_local"] +std = ["disqualified/alloc"] + +debug = [] + [dependencies] bevy_platform = { path = "../bevy_platform", version = "0.16.0-dev", default-features = false } +disqualified = { version = "1.0", default-features = false } thread_local = { version = "1.0", optional = true } [target.'cfg(all(target_arch = "wasm32", target_feature = "atomics"))'.dependencies] diff --git a/crates/bevy_utils/src/debug_info.rs b/crates/bevy_utils/src/debug_info.rs new file mode 100644 index 0000000000000..d93447324a78d --- /dev/null +++ b/crates/bevy_utils/src/debug_info.rs @@ -0,0 +1,98 @@ +use alloc::borrow::Cow; +use alloc::string::String; +#[cfg(feature = "debug")] +use core::any::type_name; +use disqualified::ShortName; +use std::fmt; + +#[cfg(not(feature = "debug"))] +const FEATURE_DISABLED: &'static str = "Enable the debug feature to see the name"; + +/// Wrapper to help debugging ECS issues. This is used to display the names of systems, components, ... +/// +/// * If the `debug` feature is enabled, the actual name will be used +/// * If it is disabled, a string mentionning the disabled feature will be used +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct DebugName { + #[cfg(feature = "debug")] + name: Cow<'static, str>, +} + +impl fmt::Display for DebugName { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + #[cfg(feature = "debug")] + f.write_str(self.name.as_ref())?; + #[cfg(not(feature = "debug"))] + f.write_str(FEATURE_DISABLED)?; + + Ok(()) + } +} + +impl DebugName { + /// Create a new `DebugName` from a `&str` + /// + /// The value will be ignored if the `debug` feature is not enabled + #[cfg_attr(not(feature = "debug"), expect(unused_variables))] + pub fn borrowed(value: &'static str) -> Self { + DebugName { + #[cfg(feature = "debug")] + name: Cow::Borrowed(value), + } + } + + /// Create a new `DebugName` from a `String` + /// + /// The value will be ignored if the `debug` feature is not enabled + #[cfg_attr(not(feature = "debug"), expect(unused_variables))] + pub fn owned(value: String) -> Self { + DebugName { + #[cfg(feature = "debug")] + name: Cow::Owned(value), + } + } + + /// Create a new `DebugName` from a type by using its [`core::any::type_name`] + /// + /// The value will be ignored if the `debug` feature is not enabled + pub fn type_name() -> Self { + DebugName { + #[cfg(feature = "debug")] + name: Cow::Borrowed(type_name::()), + } + } + + /// Get the [`ShortName`] corresping to this debug name + /// + /// The value will be a static string if the `debug` feature is not enabled + pub fn shortname(&self) -> ShortName { + #[cfg(feature = "debug")] + return ShortName(self.name.as_ref()); + #[cfg(not(feature = "debug"))] + return ShortName(FEATURE_DISABLED); + } + + /// Return the string hold by this `DebugName` + /// + /// This is intended for debugging purpose, and only available if the `debug` feature is enabled + #[cfg(feature = "debug")] + pub fn as_string(&self) -> String { + self.name.clone().into_owned() + } +} + +impl From> for DebugName { + #[cfg_attr(not(feature = "debug"), expect(unused_variables))] + fn from(value: Cow<'static, str>) -> Self { + Self { + #[cfg(feature = "debug")] + name: value, + } + } +} + +impl From for DebugName { + fn from(value: String) -> Self { + Self::owned(value) + } +} diff --git a/crates/bevy_utils/src/lib.rs b/crates/bevy_utils/src/lib.rs index e3bb07a5122b3..58979139bbebf 100644 --- a/crates/bevy_utils/src/lib.rs +++ b/crates/bevy_utils/src/lib.rs @@ -43,12 +43,14 @@ cfg::parallel! { /// /// This includes the most common types in this crate, re-exported for your convenience. pub mod prelude { + pub use crate::debug_info::DebugName; pub use crate::default; } #[cfg(feature = "wgpu_wrapper")] mod wgpu_wrapper; +mod debug_info; mod default; mod once; diff --git a/examples/games/stepping.rs b/examples/games/stepping.rs index f9adef7b9bab4..d6dca8b612934 100644 --- a/examples/games/stepping.rs +++ b/examples/games/stepping.rs @@ -132,10 +132,9 @@ fn build_ui( return; }; - for (node_id, _system) in systems { + for (node_id, system) in systems { // skip bevy default systems; we don't want to step those - #[cfg(feature = "debug")] - if _system.name().starts_with("bevy") { + if system.name().as_string().starts_with("bevy") { always_run.push((*label, node_id)); continue; } @@ -153,9 +152,8 @@ fn build_ui( )); // add the name of the system to the ui - #[cfg(feature = "debug")] text_spans.push(( - TextSpan(format!("{}\n", _system.name())), + TextSpan(format!("{}\n", system.name())), TextFont::default(), TextColor(FONT_COLOR), )); From bf853346879118e1ac318828c683c555e309abe9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Mockers?= Date: Tue, 17 Jun 2025 03:03:31 +0200 Subject: [PATCH 12/17] CI --- .github/workflows/ci.yml | 2 +- crates/bevy_ecs/Cargo.toml | 2 +- crates/bevy_ecs/src/schedule/executor/simple.rs | 2 +- .../bevy_ecs/src/schedule/executor/single_threaded.rs | 2 +- crates/bevy_ecs/src/system/function_system.rs | 11 ++++++----- crates/bevy_remote/Cargo.toml | 2 +- crates/bevy_remote/src/builtin_methods.rs | 6 +++--- crates/bevy_utils/src/debug_info.rs | 6 ++---- examples/games/stepping.rs | 1 + 9 files changed, 17 insertions(+), 17 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 135a1103f101a..526ac4c4013de 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -95,7 +95,7 @@ jobs: - name: CI job # To run the tests one item at a time for troubleshooting, use # cargo --quiet test --lib -- --list | sed 's/: test$//' | MIRIFLAGS="-Zmiri-disable-isolation -Zmiri-disable-weak-memory-emulation" xargs -n1 cargo miri test -p bevy_ecs --lib -- --exact - run: cargo miri test -p bevy_ecs --features debug + run: cargo miri test -p bevy_ecs --features bevy_utils/debug env: # -Zrandomize-layout makes sure we dont rely on the layout of anything that might change RUSTFLAGS: -Zrandomize-layout diff --git a/crates/bevy_ecs/Cargo.toml b/crates/bevy_ecs/Cargo.toml index 4f96e4c4ed4d2..c2c663b7d7fb5 100644 --- a/crates/bevy_ecs/Cargo.toml +++ b/crates/bevy_ecs/Cargo.toml @@ -35,7 +35,7 @@ backtrace = ["std"] ## Enables `tracing` integration, allowing spans and other metrics to be reported ## through that framework. -trace = ["std", "dep:tracing"] +trace = ["std", "dep:tracing", "bevy_utils/debug"] ## Enables a more detailed set of traces which may be noisy if left on by default. detailed_trace = ["trace"] diff --git a/crates/bevy_ecs/src/schedule/executor/simple.rs b/crates/bevy_ecs/src/schedule/executor/simple.rs index b7ea2c7119980..f0d655eab8ce7 100644 --- a/crates/bevy_ecs/src/schedule/executor/simple.rs +++ b/crates/bevy_ecs/src/schedule/executor/simple.rs @@ -73,7 +73,7 @@ impl SystemExecutor for SimpleExecutor { #[cfg(feature = "trace")] let name = schedule.systems[system_index].system.name(); #[cfg(feature = "trace")] - let should_run_span = info_span!("check_conditions", name = &*name).entered(); + let should_run_span = info_span!("check_conditions", name = name.as_string()).entered(); let mut should_run = !self.completed_systems.contains(system_index); for set_idx in schedule.sets_with_conditions_of_systems[system_index].ones() { diff --git a/crates/bevy_ecs/src/schedule/executor/single_threaded.rs b/crates/bevy_ecs/src/schedule/executor/single_threaded.rs index 89aacbcdb3963..21b8d2289dd0e 100644 --- a/crates/bevy_ecs/src/schedule/executor/single_threaded.rs +++ b/crates/bevy_ecs/src/schedule/executor/single_threaded.rs @@ -74,7 +74,7 @@ impl SystemExecutor for SingleThreadedExecutor { #[cfg(feature = "trace")] let name = schedule.systems[system_index].system.name(); #[cfg(feature = "trace")] - let should_run_span = info_span!("check_conditions", name = &*name).entered(); + let should_run_span = info_span!("check_conditions", name = name.as_string()).entered(); let mut should_run = !self.completed_systems.contains(system_index); for set_idx in schedule.sets_with_conditions_of_systems[system_index].ones() { diff --git a/crates/bevy_ecs/src/system/function_system.rs b/crates/bevy_ecs/src/system/function_system.rs index 340b9738f1dc6..95b683218c837 100644 --- a/crates/bevy_ecs/src/system/function_system.rs +++ b/crates/bevy_ecs/src/system/function_system.rs @@ -38,14 +38,15 @@ pub struct SystemMeta { impl SystemMeta { pub(crate) fn new() -> Self { + let name = DebugName::type_name::(); Self { - name: DebugName::type_name::(), - flags: SystemStateFlags::empty(), - last_run: Tick::new(0), #[cfg(feature = "trace")] - system_span: info_span!("system", name = name), + system_span: info_span!("system", name = name.clone().as_string()), #[cfg(feature = "trace")] - commands_span: info_span!("system_commands", name = name), + commands_span: info_span!("system_commands", name = name.clone().as_string()), + name, + flags: SystemStateFlags::empty(), + last_run: Tick::new(0), } } diff --git a/crates/bevy_remote/Cargo.toml b/crates/bevy_remote/Cargo.toml index 5e4c2ba924ff4..176e5ed47f179 100644 --- a/crates/bevy_remote/Cargo.toml +++ b/crates/bevy_remote/Cargo.toml @@ -22,7 +22,7 @@ bevy_ecs = { path = "../bevy_ecs", version = "0.16.0-dev", features = [ bevy_reflect = { path = "../bevy_reflect", version = "0.16.0-dev" } bevy_tasks = { path = "../bevy_tasks", version = "0.16.0-dev" } bevy_utils = { path = "../bevy_utils", version = "0.16.0-dev", features = [ - "debug" + "debug", ] } bevy_platform = { path = "../bevy_platform", version = "0.16.0-dev", default-features = false, features = [ "std", diff --git a/crates/bevy_remote/src/builtin_methods.rs b/crates/bevy_remote/src/builtin_methods.rs index 62ba8af661cbe..e390f448c4489 100644 --- a/crates/bevy_remote/src/builtin_methods.rs +++ b/crates/bevy_remote/src/builtin_methods.rs @@ -1130,7 +1130,7 @@ pub fn process_remote_list_request(In(params): In>, world: &World) let Some(component_info) = world.components().get_info(component_id) else { continue; }; - response.push(component_info.name().to_owned()); + response.push(component_info.name().to_string()); } } // If `None`, list all registered components. @@ -1189,7 +1189,7 @@ pub fn process_remote_list_watching_request( let Some(component_info) = world.components().get_info(component_id) else { continue; }; - response.added.push(component_info.name().to_owned()); + response.added.push(component_info.name().to_string()); } } @@ -1202,7 +1202,7 @@ pub fn process_remote_list_watching_request( let Some(component_info) = world.components().get_info(*component_id) else { continue; }; - response.removed.push(component_info.name().to_owned()); + response.removed.push(component_info.name().to_string()); } } } diff --git a/crates/bevy_utils/src/debug_info.rs b/crates/bevy_utils/src/debug_info.rs index d93447324a78d..568e6049a7e8d 100644 --- a/crates/bevy_utils/src/debug_info.rs +++ b/crates/bevy_utils/src/debug_info.rs @@ -1,9 +1,7 @@ -use alloc::borrow::Cow; -use alloc::string::String; +use alloc::{borrow::Cow, fmt, string::String}; #[cfg(feature = "debug")] use core::any::type_name; use disqualified::ShortName; -use std::fmt; #[cfg(not(feature = "debug"))] const FEATURE_DISABLED: &'static str = "Enable the debug feature to see the name"; @@ -11,7 +9,7 @@ const FEATURE_DISABLED: &'static str = "Enable the debug feature to see the name /// Wrapper to help debugging ECS issues. This is used to display the names of systems, components, ... /// /// * If the `debug` feature is enabled, the actual name will be used -/// * If it is disabled, a string mentionning the disabled feature will be used +/// * If it is disabled, a string mentioning the disabled feature will be used #[derive(Clone, Debug, PartialEq, Eq)] pub struct DebugName { #[cfg(feature = "debug")] diff --git a/examples/games/stepping.rs b/examples/games/stepping.rs index d6dca8b612934..dce37d0842dc2 100644 --- a/examples/games/stepping.rs +++ b/examples/games/stepping.rs @@ -134,6 +134,7 @@ fn build_ui( for (node_id, system) in systems { // skip bevy default systems; we don't want to step those + #[cfg(feature = "debug")] if system.name().as_string().starts_with("bevy") { always_run.push((*label, node_id)); continue; From fc07250924d151bbde14cc603fb8a9785b787ac7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Mockers?= Date: Tue, 17 Jun 2025 03:43:51 +0200 Subject: [PATCH 13/17] some more CI --- crates/bevy_ecs/src/schedule/schedule.rs | 5 ++--- crates/bevy_ecs/src/system/combinator.rs | 2 +- crates/bevy_utils/src/debug_info.rs | 6 ++++++ 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/crates/bevy_ecs/src/schedule/schedule.rs b/crates/bevy_ecs/src/schedule/schedule.rs index d9d39fceec3bb..3c67f1ea0782e 100644 --- a/crates/bevy_ecs/src/schedule/schedule.rs +++ b/crates/bevy_ecs/src/schedule/schedule.rs @@ -1692,7 +1692,7 @@ impl ScheduleGraph { #[inline] fn get_node_name_inner(&self, id: &NodeId, report_sets: bool) -> String { - let name = match id { + match id { NodeId::System(_) => { let name = self.systems[id.index()].get().unwrap().system.name(); let name = if self.settings.use_shortnames { @@ -1721,8 +1721,7 @@ impl ScheduleGraph { set.name() } } - }; - name + } } fn anonymous_set_name(&self, id: &NodeId) -> String { diff --git a/crates/bevy_ecs/src/system/combinator.rs b/crates/bevy_ecs/src/system/combinator.rs index 4005dfb8a4dd8..d48c599f2dc32 100644 --- a/crates/bevy_ecs/src/system/combinator.rs +++ b/crates/bevy_ecs/src/system/combinator.rs @@ -56,7 +56,7 @@ use super::{IntoSystem, ReadOnlySystem, System}; /// IntoSystem::into_system(resource_equals(A(1))), /// IntoSystem::into_system(resource_equals(B(1))), /// // The name of the combined system. -/// std::borrow::Cow::Borrowed("a ^ b"), +/// "a ^ b".into(), /// ))); /// # fn my_system(mut flag: ResMut) { flag.0 = true; } /// # diff --git a/crates/bevy_utils/src/debug_info.rs b/crates/bevy_utils/src/debug_info.rs index 568e6049a7e8d..c79c5ebe60754 100644 --- a/crates/bevy_utils/src/debug_info.rs +++ b/crates/bevy_utils/src/debug_info.rs @@ -94,3 +94,9 @@ impl From for DebugName { Self::owned(value) } } + +impl From<&'static str> for DebugName { + fn from(value: &'static str) -> Self { + Self::borrowed(value) + } +} From 4df783432282b079a115d36bcfe7e40df2f7c7e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Mockers?= Date: Tue, 17 Jun 2025 03:50:53 +0200 Subject: [PATCH 14/17] CI again --- crates/bevy_scene/src/scene.rs | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/crates/bevy_scene/src/scene.rs b/crates/bevy_scene/src/scene.rs index 89919fbfa645e..2293beef1e9a2 100644 --- a/crates/bevy_scene/src/scene.rs +++ b/crates/bevy_scene/src/scene.rs @@ -89,11 +89,12 @@ impl Scene { .type_id() .expect("reflected resources must have a type_id"); - let registration = type_registry.get(type_id).ok_or_else(|| { - return SceneSpawnError::UnregisteredType { - std_type_name: component_info.name(), - }; - })?; + let registration = + type_registry + .get(type_id) + .ok_or_else(|| SceneSpawnError::UnregisteredType { + std_type_name: component_info.name(), + })?; let reflect_resource = registration.data::().ok_or_else(|| { SceneSpawnError::UnregisteredResource { type_path: registration.type_info().type_path().to_string(), @@ -131,10 +132,8 @@ impl Scene { let registration = type_registry .get(component_info.type_id().unwrap()) - .ok_or_else(|| { - return SceneSpawnError::UnregisteredType { - std_type_name: component_info.name(), - }; + .ok_or_else(|| SceneSpawnError::UnregisteredType { + std_type_name: component_info.name(), })?; let reflect_component = registration.data::().ok_or_else(|| { From 09c51ed0c1dd0901ffca4c940a8c52fa346e3d1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Mockers?= Date: Tue, 17 Jun 2025 04:16:43 +0200 Subject: [PATCH 15/17] stepping --- crates/bevy_ecs/src/schedule/stepping.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/bevy_ecs/src/schedule/stepping.rs b/crates/bevy_ecs/src/schedule/stepping.rs index b6de7c8215463..b0d8b57fb0dcd 100644 --- a/crates/bevy_ecs/src/schedule/stepping.rs +++ b/crates/bevy_ecs/src/schedule/stepping.rs @@ -895,9 +895,9 @@ mod tests { ($schedule:expr, $skipped_systems:expr, $($system:expr),*) => { // pull an ordered list of systems in the schedule, and save the // system TypeId, and name. - let systems: Vec<(TypeId, alloc::borrow::Cow<'static, str>)> = $schedule.systems().unwrap() + let systems: Vec<(TypeId, alloc::string::String)> = $schedule.systems().unwrap() .map(|(_, system)| { - (system.type_id(), system.name()) + (system.type_id(), system.name().as_string()) }) .collect(); From 78bee7638b359230d47d9fe9be57ed17ae8d275c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Mockers?= Date: Tue, 17 Jun 2025 12:26:52 +0200 Subject: [PATCH 16/17] remove anonymous variant --- crates/bevy_scene/src/scene_spawner.rs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/crates/bevy_scene/src/scene_spawner.rs b/crates/bevy_scene/src/scene_spawner.rs index ce450509109d0..71cd848751894 100644 --- a/crates/bevy_scene/src/scene_spawner.rs +++ b/crates/bevy_scene/src/scene_spawner.rs @@ -108,13 +108,6 @@ pub enum SceneSpawnError { /// The [type name](std::any::type_name) for the unregistered type. std_type_name: DebugName, }, - /// Scene contains an unregistered type. - #[error( - "scene contains an unregistered type. \ - consider reflecting it with `#[derive(Reflect)]` \ - and registering the type using `app.register_type::()`" - )] - AnonymousUnregisteredType, /// Scene contains an unregistered type which has a `TypePath`. #[error( "scene contains the reflected type `{type_path}` but it was not found in the type registry. \ From 2b73c53a783c41513a4c2f678f5c8856709f7243 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Mockers?= Date: Wed, 18 Jun 2025 01:03:35 +0200 Subject: [PATCH 17/17] debug name for named observers --- crates/bevy_ecs/src/observer/runner.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/crates/bevy_ecs/src/observer/runner.rs b/crates/bevy_ecs/src/observer/runner.rs index b6f10de2e4bb7..d6bffd8f22ddf 100644 --- a/crates/bevy_ecs/src/observer/runner.rs +++ b/crates/bevy_ecs/src/observer/runner.rs @@ -1,4 +1,5 @@ -use alloc::{borrow::Cow, boxed::Box, vec}; +use alloc::{boxed::Box, vec}; +use bevy_utils::prelude::DebugName; use core::any::Any; use crate::{ @@ -301,7 +302,7 @@ impl Observer { } /// Returns the name of the [`Observer`]'s system . - pub fn system_name(&self) -> Cow<'static, str> { + pub fn system_name(&self) -> DebugName { self.system.system_name() } } @@ -420,11 +421,11 @@ fn observer_system_runner>( } trait AnyNamedSystem: Any + Send + Sync + 'static { - fn system_name(&self) -> Cow<'static, str>; + fn system_name(&self) -> DebugName; } impl AnyNamedSystem for T { - fn system_name(&self) -> Cow<'static, str> { + fn system_name(&self) -> DebugName { self.name() } }