Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
d98db38
Improve error for when you try running a one-shot system with wrong `…
hukasu May 1, 2025
bc4edbb
Add test to check for possible recursion on `run_system`
hukasu May 1, 2025
2e21b08
Redo logic for recursion on `run_system`
hukasu May 1, 2025
a22c7f2
Guard insertion and removal from `RunSystemStack`
hukasu May 1, 2025
5f46942
Rewrite docs for `RegisteredSystemError::MaybeWrongType`
hukasu May 1, 2025
51f8362
Fix tests with `should_panic` having things that would panic when on …
hukasu May 1, 2025
b6587af
Replace `&mut World` for `Commands` to prevent possible UB reported b…
hukasu May 1, 2025
988a0cc
Apply style changes from sugestions
hukasu May 1, 2025
2dbcb00
Change handling of recursion and wrong type on `run_system` and variants
hukasu May 5, 2025
2875dff
Use `disqualified::ShortName` for `RegisteredSystemError::IncorrectType`
hukasu May 5, 2025
380d6e7
Add migration guide for #19011
hukasu May 5, 2025
079fdeb
Use `ShortName` for all parameters of `RegisteredSystemError::Incorre…
hukasu May 5, 2025
7b08ffe
Merge branch 'main' into issue19005
hukasu Jul 26, 2025
4b16a03
Nit on `IntoResult`
hukasu Jul 26, 2025
a7ced95
Make `TypeIdAndName` into named struct
hukasu Jul 28, 2025
17ebb36
Add `debug` feature to `bevy_ecs`
hukasu Jul 28, 2025
741513c
Side: Fix `ci` tool when running with `--test-threads`
hukasu Jul 28, 2025
6a45a78
Put strings behind `debug` feature
hukasu Jul 28, 2025
148135e
Fix `DebugTuple` using wrong name
hukasu Jul 28, 2025
8329109
Small fix on `bevy_ecs`'s `Cargo.toml`
hukasu Jul 28, 2025
e633141
Remove `debug` feature from `bevy_ecs` and `Reflect` derive from `Sys…
hukasu Jul 29, 2025
1b28d49
Merge branch 'main' into issue19005
alice-i-cecile Jul 30, 2025
29fd32d
Merge branch 'main' into issue19005
hukasu Aug 2, 2025
af81061
Revert "Nit on `IntoResult`"
hukasu Aug 2, 2025
d56b7b3
Move test of system id type only if taking the `RegisteredSystem` failed
hukasu Aug 2, 2025
b79e20c
Change error type
hukasu Aug 2, 2025
2bd8557
Remove redundant test
hukasu Aug 2, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
103 changes: 87 additions & 16 deletions crates/bevy_ecs/src/system/system_registry.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
#[cfg(feature = "bevy_reflect")]
use crate::reflect::ReflectComponent;
#[cfg(feature = "hotpatching")]
use crate::{change_detection::DetectChanges, HotPatchChanges};
use crate::{
Expand All @@ -14,14 +12,13 @@ use crate::{
};
use alloc::boxed::Box;
use bevy_ecs_macros::{Component, Resource};
#[cfg(feature = "bevy_reflect")]
use bevy_reflect::{std_traits::ReflectDefault, Reflect};
use core::marker::PhantomData;
use bevy_utils::prelude::DebugName;
use core::{any::TypeId, marker::PhantomData};
use thiserror::Error;

/// A small wrapper for [`BoxedSystem`] that also keeps track whether or not the system has been initialized.
#[derive(Component)]
#[require(SystemIdMarker, Internal)]
#[require(SystemIdMarker = SystemIdMarker::typed_system_id_marker::<I, O>(), Internal)]
pub(crate) struct RegisteredSystem<I, O> {
initialized: bool,
system: BoxedSystem<I, O>,
Expand All @@ -36,11 +33,45 @@ impl<I, O> RegisteredSystem<I, O> {
}
}

#[derive(Debug, Clone)]
struct TypeIdAndName {
type_id: TypeId,
name: DebugName,
}

impl TypeIdAndName {
fn new<T: 'static>() -> Self {
Self {
type_id: TypeId::of::<T>(),
name: DebugName::type_name::<T>(),
}
}
}

impl Default for TypeIdAndName {
fn default() -> Self {
Self {
type_id: TypeId::of::<()>(),
name: DebugName::type_name::<()>(),
}
}
}

/// Marker [`Component`](bevy_ecs::component::Component) for identifying [`SystemId`] [`Entity`]s.
#[derive(Component, Default)]
#[cfg_attr(feature = "bevy_reflect", derive(Reflect))]
#[cfg_attr(feature = "bevy_reflect", reflect(Component, Default))]
pub struct SystemIdMarker;
#[derive(Debug, Default, Clone, Component)]
pub struct SystemIdMarker {
input_type_id: TypeIdAndName,
output_type_id: TypeIdAndName,
}

impl SystemIdMarker {
fn typed_system_id_marker<I: 'static, O: 'static>() -> Self {
Self {
input_type_id: TypeIdAndName::new::<I>(),
output_type_id: TypeIdAndName::new::<O>(),
}
}
}

/// A system that has been removed from the registry.
/// It contains the system and whether or not it has been initialized.
Expand Down Expand Up @@ -344,14 +375,26 @@ impl World {
.map_err(|_| RegisteredSystemError::SystemIdNotRegistered(id))?;

// Take ownership of system trait object
let RegisteredSystem {
let Some(RegisteredSystem {
mut initialized,
mut system,
} = entity
.take::<RegisteredSystem<I, O>>()
.ok_or(RegisteredSystemError::Recursive(id))?;
}) = entity.take::<RegisteredSystem<I, O>>()
else {
let Some(system_id_marker) = entity.get::<SystemIdMarker>() else {
return Err(RegisteredSystemError::SystemIdNotRegistered(id));
};
if system_id_marker.input_type_id.type_id != TypeId::of::<I>()
|| system_id_marker.output_type_id.type_id != TypeId::of::<O>()
{
return Err(RegisteredSystemError::IncorrectType(
id,
system_id_marker.clone(),
));
}
return Err(RegisteredSystemError::Recursive(id));
};

// Run the system
// Initialize the system
if !initialized {
system.initialize(self);
initialized = true;
Expand Down Expand Up @@ -510,6 +553,9 @@ pub enum RegisteredSystemError<I: SystemInput = (), O = ()> {
/// System returned an error or failed required parameter validation.
#[error("System returned error: {0}")]
Failed(BevyError),
/// [`SystemId`] had different input and/or output types than [`SystemIdMarker`]
#[error("Could not get system from `{}`, entity was `SystemId<{}, {}>`", DebugName::type_name::<SystemId<I, O>>(), .1.input_type_id.name, .1.output_type_id.name)]
IncorrectType(SystemId<I, O>, SystemIdMarker),
}

impl<I: SystemInput, O> From<RunSystemError> for RegisteredSystemError<I, O> {
Expand All @@ -532,6 +578,11 @@ impl<I: SystemInput, O> core::fmt::Debug for RegisteredSystemError<I, O> {
Self::SelfRemove(arg0) => f.debug_tuple("SelfRemove").field(arg0).finish(),
Self::Skipped(arg0) => f.debug_tuple("Skipped").field(arg0).finish(),
Self::Failed(arg0) => f.debug_tuple("Failed").field(arg0).finish(),
Self::IncorrectType(arg0, arg1) => f
.debug_tuple("IncorrectType")
.field(arg0)
.field(arg1)
.finish(),
}
}
}
Expand All @@ -542,7 +593,10 @@ mod tests {

use bevy_utils::default;

use crate::{prelude::*, system::SystemId};
use crate::{
prelude::*,
system::{RegisteredSystemError, SystemId},
};

#[derive(Resource, Default, PartialEq, Debug)]
struct Counter(u8);
Expand Down Expand Up @@ -986,4 +1040,21 @@ mod tests {
world.run_system_cached(system.pipe(system)).unwrap();
world.run_system_cached(system.map(|()| {})).unwrap();
}

#[test]
fn wrong_system_type() {
fn test() -> Result<(), u8> {
Ok(())
}

let mut world = World::new();

let entity = world.register_system_cached(test).entity();

match world.run_system::<u8>(SystemId::from_entity(entity)) {
Ok(_) => panic!("Should fail since called `run_system` with wrong SystemId type."),
Err(RegisteredSystemError::IncorrectType(_, _)) => (),
Err(err) => panic!("Failed with wrong error. `{:?}`", err),
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
title: Improve error when using `run_system` command with a `SystemId` of wrong type
pull_requests: [19011]
---

Added a new error to `RegisteredSystemError` to inform of use of `run_system` command and its
variants with a `SystemId` of wrong type.
Loading