Skip to content

Commit a7d1a73

Browse files
MiniaczQmockersf
andcommitted
Set panic as default fallible system param behavior (#16638)
Fixes: #16578 This is a patch fix, proper fix requires a breaking change. Added `Panic` enum variant and using is as the system meta default. Warn once behavior can be enabled same way disabling panic (originally disabling wans) is. To fix an issue with the current architecture, where **all** combinator system params get checked together, combinator systems only check params of the first system. This will result in old, panicking behavior on subsequent systems and will be fixed in 0.16. Ran unit tests and `fallible_params` example. --------- Co-authored-by: François Mockers <[email protected]> Co-authored-by: François Mockers <[email protected]>
1 parent e9a07c5 commit a7d1a73

File tree

11 files changed

+53
-39
lines changed

11 files changed

+53
-39
lines changed

crates/bevy_ecs/src/change_detection.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -544,7 +544,7 @@ impl<'w> From<TicksMut<'w>> for Ticks<'w> {
544544
/// If you need a unique mutable borrow, use [`ResMut`] instead.
545545
///
546546
/// This [`SystemParam`](crate::system::SystemParam) fails validation if resource doesn't exist.
547-
/// This will cause systems that use this parameter to be skipped.
547+
/// This will cause a panic, but can be configured to do nothing or warn once.
548548
///
549549
/// Use [`Option<Res<T>>`] instead if the resource might not always exist.
550550
pub struct Res<'w, T: ?Sized + Resource> {
@@ -622,7 +622,7 @@ impl_debug!(Res<'w, T>, Resource);
622622
/// If you need a shared borrow, use [`Res`] instead.
623623
///
624624
/// This [`SystemParam`](crate::system::SystemParam) fails validation if resource doesn't exist.
625-
/// This will cause systems that use this parameter to be skipped.
625+
/// /// This will cause a panic, but can be configured to do nothing or warn once.
626626
///
627627
/// Use [`Option<ResMut<T>>`] instead if the resource might not always exist.
628628
pub struct ResMut<'w, T: ?Sized + Resource> {
@@ -683,7 +683,7 @@ impl<'w, T: Resource> From<ResMut<'w, T>> for Mut<'w, T> {
683683
/// over to another thread.
684684
///
685685
/// This [`SystemParam`](crate::system::SystemParam) fails validation if non-send resource doesn't exist.
686-
/// This will cause systems that use this parameter to be skipped.
686+
/// /// This will cause a panic, but can be configured to do nothing or warn once.
687687
///
688688
/// Use [`Option<NonSendMut<T>>`] instead if the resource might not always exist.
689689
pub struct NonSendMut<'w, T: ?Sized + 'static> {

crates/bevy_ecs/src/schedule/condition.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ pub trait Condition<Marker, In: SystemInput = ()>: sealed::Condition<Marker, In>
8080
///
8181
/// # Examples
8282
///
83-
/// ```
83+
/// ```should_panic
8484
/// use bevy_ecs::prelude::*;
8585
///
8686
/// #[derive(Resource, PartialEq)]
@@ -90,7 +90,7 @@ pub trait Condition<Marker, In: SystemInput = ()>: sealed::Condition<Marker, In>
9090
/// # let mut world = World::new();
9191
/// # fn my_system() {}
9292
/// app.add_systems(
93-
/// // The `resource_equals` run condition will fail since we don't initialize `R`,
93+
/// // The `resource_equals` run condition will panic since we don't initialize `R`,
9494
/// // just like if we used `Res<R>` in a system.
9595
/// my_system.run_if(resource_equals(R(0))),
9696
/// );

crates/bevy_ecs/src/schedule/executor/mod.rs

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ mod tests {
186186
self as bevy_ecs,
187187
prelude::{IntoSystemConfigs, IntoSystemSetConfigs, Resource, Schedule, SystemSet},
188188
schedule::ExecutorKind,
189-
system::{Commands, In, IntoSystem, Res},
189+
system::{Commands, Res, WithParamWarnPolicy},
190190
world::World,
191191
};
192192

@@ -215,15 +215,11 @@ mod tests {
215215
schedule.set_executor_kind(executor);
216216
schedule.add_systems(
217217
(
218-
// Combined systems get skipped together.
219-
(|mut commands: Commands| {
220-
commands.insert_resource(R1);
221-
})
222-
.pipe(|_: In<()>, _: Res<R1>| {}),
223218
// This system depends on a system that is always skipped.
224-
|mut commands: Commands| {
219+
(|mut commands: Commands| {
225220
commands.insert_resource(R2);
226-
},
221+
})
222+
.param_warn_once(),
227223
)
228224
.chain(),
229225
);
@@ -246,18 +242,20 @@ mod tests {
246242
let mut world = World::new();
247243
let mut schedule = Schedule::default();
248244
schedule.set_executor_kind(executor);
249-
schedule.configure_sets(S1.run_if(|_: Res<R1>| true));
245+
schedule.configure_sets(S1.run_if((|_: Res<R1>| true).param_warn_once()));
250246
schedule.add_systems((
251247
// System gets skipped if system set run conditions fail validation.
252248
(|mut commands: Commands| {
253249
commands.insert_resource(R1);
254250
})
251+
.param_warn_once()
255252
.in_set(S1),
256253
// System gets skipped if run conditions fail validation.
257254
(|mut commands: Commands| {
258255
commands.insert_resource(R2);
259256
})
260-
.run_if(|_: Res<R2>| true),
257+
.param_warn_once()
258+
.run_if((|_: Res<R2>| true).param_warn_once()),
261259
));
262260
schedule.run(&mut world);
263261
assert!(world.get_resource::<R1>().is_none());

crates/bevy_ecs/src/system/combinator.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,7 @@ where
214214
#[inline]
215215
unsafe fn validate_param_unsafe(&mut self, world: UnsafeWorldCell) -> bool {
216216
// SAFETY: Delegate to other `System` implementations.
217-
unsafe { self.a.validate_param_unsafe(world) && self.b.validate_param_unsafe(world) }
217+
unsafe { self.a.validate_param_unsafe(world) }
218218
}
219219

220220
fn initialize(&mut self, world: &mut World) {
@@ -433,7 +433,7 @@ where
433433

434434
unsafe fn validate_param_unsafe(&mut self, world: UnsafeWorldCell) -> bool {
435435
// SAFETY: Delegate to other `System` implementations.
436-
unsafe { self.a.validate_param_unsafe(world) && self.b.validate_param_unsafe(world) }
436+
unsafe { self.a.validate_param_unsafe(world) }
437437
}
438438

439439
fn validate_param(&mut self, world: &World) -> bool {

crates/bevy_ecs/src/system/function_system.rs

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ impl SystemMeta {
6060
is_send: true,
6161
has_deferred: false,
6262
last_run: Tick::new(0),
63-
param_warn_policy: ParamWarnPolicy::Once,
63+
param_warn_policy: ParamWarnPolicy::Panic,
6464
#[cfg(feature = "trace")]
6565
system_span: info_span!("system", name = name),
6666
#[cfg(feature = "trace")]
@@ -190,6 +190,8 @@ impl SystemMeta {
190190
/// State machine for emitting warnings when [system params are invalid](System::validate_param).
191191
#[derive(Clone, Copy)]
192192
pub enum ParamWarnPolicy {
193+
/// Stop app with a panic.
194+
Panic,
193195
/// No warning should ever be emitted.
194196
Never,
195197
/// The warning will be emitted once and status will update to [`Self::Never`].
@@ -200,6 +202,7 @@ impl ParamWarnPolicy {
200202
/// Advances the warn policy after validation failed.
201203
#[inline]
202204
fn advance(&mut self) {
205+
// Ignore `Panic` case, because it stops execution before this function gets called.
203206
*self = Self::Never;
204207
}
205208

@@ -209,15 +212,21 @@ impl ParamWarnPolicy {
209212
where
210213
P: SystemParam,
211214
{
212-
if matches!(self, Self::Never) {
213-
return;
215+
match self {
216+
Self::Panic => panic!(
217+
"{0} could not access system parameter {1}",
218+
name,
219+
disqualified::ShortName::of::<P>()
220+
),
221+
Self::Once => {
222+
bevy_utils::tracing::warn!(
223+
"{0} did not run because it requested inaccessible system parameter {1}",
224+
name,
225+
disqualified::ShortName::of::<P>()
226+
);
227+
}
228+
Self::Never => {}
214229
}
215-
216-
bevy_utils::tracing::warn!(
217-
"{0} did not run because it requested inaccessible system parameter {1}",
218-
name,
219-
disqualified::ShortName::of::<P>()
220-
);
221230
}
222231
}
223232

@@ -232,6 +241,11 @@ where
232241
/// Set warn policy.
233242
fn with_param_warn_policy(self, warn_policy: ParamWarnPolicy) -> FunctionSystem<M, F>;
234243

244+
/// Warn only once about invalid system parameters.
245+
fn param_warn_once(self) -> FunctionSystem<M, F> {
246+
self.with_param_warn_policy(ParamWarnPolicy::Once)
247+
}
248+
235249
/// Disable all param warnings.
236250
fn never_param_warn(self) -> FunctionSystem<M, F> {
237251
self.with_param_warn_policy(ParamWarnPolicy::Never)

crates/bevy_ecs/src/system/query.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1648,7 +1648,7 @@ impl<'w, 'q, Q: QueryData, F: QueryFilter> From<&'q mut Query<'w, '_, Q, F>>
16481648
/// [System parameter] that provides access to single entity's components, much like [`Query::single`]/[`Query::single_mut`].
16491649
///
16501650
/// This [`SystemParam`](crate::system::SystemParam) fails validation if zero or more than one matching entity exists.
1651-
/// This will cause systems that use this parameter to be skipped.
1651+
/// /// This will cause a panic, but can be configured to do nothing or warn once.
16521652
///
16531653
/// Use [`Option<Single<D, F>>`] instead if zero or one matching entities can exist.
16541654
///
@@ -1684,7 +1684,7 @@ impl<'w, D: QueryData, F: QueryFilter> Single<'w, D, F> {
16841684
/// [System parameter] that works very much like [`Query`] except it always contains at least one matching entity.
16851685
///
16861686
/// This [`SystemParam`](crate::system::SystemParam) fails validation if no matching entities exist.
1687-
/// This will cause systems that use this parameter to be skipped.
1687+
/// /// This will cause a panic, but can be configured to do nothing or warn once.
16881688
///
16891689
/// Much like [`Query::is_empty`] the worst case runtime will be `O(n)` where `n` is the number of *potential* matches.
16901690
/// This can be notably expensive for queries that rely on non-archetypal filters such as [`Added`](crate::query::Added) or [`Changed`](crate::query::Changed)

crates/bevy_ecs/src/system/system.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -451,7 +451,7 @@ mod tests {
451451

452452
let mut world = World::default();
453453
// This fails because `T` has not been added to the world yet.
454-
let result = world.run_system_once(system);
454+
let result = world.run_system_once(system.param_warn_once());
455455

456456
assert!(matches!(result, Err(RunSystemError::InvalidParams(_))));
457457
}

crates/bevy_ecs/src/system/system_param.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1335,7 +1335,7 @@ unsafe impl<T: SystemBuffer> SystemParam for Deferred<'_, T> {
13351335
/// over to another thread.
13361336
///
13371337
/// This [`SystemParam`] fails validation if non-send resource doesn't exist.
1338-
/// This will cause systems that use this parameter to be skipped.
1338+
/// /// This will cause a panic, but can be configured to do nothing or warn once.
13391339
///
13401340
/// Use [`Option<NonSend<T>>`] instead if the resource might not always exist.
13411341
pub struct NonSend<'w, T: 'static> {

crates/bevy_ecs/src/system/system_registry.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -965,7 +965,7 @@ mod tests {
965965
fn system(_: Res<T>) {}
966966

967967
let mut world = World::new();
968-
let id = world.register_system_cached(system);
968+
let id = world.register_system(system.param_warn_once());
969969
// This fails because `T` has not been added to the world yet.
970970
let result = world.run_system(id);
971971

crates/bevy_pbr/src/render/mesh.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,10 @@ impl Plugin for MeshRenderPlugin {
131131
load_internal_asset!(app, SKINNING_HANDLE, "skinning.wgsl", Shader::from_wgsl);
132132
load_internal_asset!(app, MORPH_HANDLE, "morph.wgsl", Shader::from_wgsl);
133133

134+
if app.get_sub_app(RenderApp).is_none() {
135+
return;
136+
}
137+
134138
app.add_systems(
135139
PostUpdate,
136140
(no_automatic_skin_batching, no_automatic_morph_batching),

0 commit comments

Comments
 (0)