From 65c80d9ee46dbc91c802cdde20b8b28e3ad1c47a Mon Sep 17 00:00:00 2001 From: Jonah Henriksson <33059163+JonahPlusPlus@users.noreply.github.com> Date: Sun, 11 Dec 2022 22:30:52 -0500 Subject: [PATCH 01/13] Created fallible system params --- crates/bevy_ecs/macros/src/lib.rs | 18 +- .../src/system/commands/parallel_scope.rs | 6 +- .../src/system/exclusive_system_param.rs | 6 +- crates/bevy_ecs/src/system/function_system.rs | 36 +- crates/bevy_ecs/src/system/system_param.rs | 443 ++++++++---------- crates/bevy_render/src/extract_param.rs | 18 +- crates/bevy_render/src/render_asset.rs | 4 +- crates/bevy_render/src/render_phase/draw.rs | 6 +- 8 files changed, 251 insertions(+), 286 deletions(-) diff --git a/crates/bevy_ecs/macros/src/lib.rs b/crates/bevy_ecs/macros/src/lib.rs index b8674daf0095f..a01d599a10542 100644 --- a/crates/bevy_ecs/macros/src/lib.rs +++ b/crates/bevy_ecs/macros/src/lib.rs @@ -228,7 +228,7 @@ pub fn impl_param_set(_input: TokenStream) -> TokenStream { // Conflicting params in ParamSet are not accessible at the same time // ParamSets are guaranteed to not conflict with other SystemParams unsafe { - <#param::State as SystemParamState>::get_param(&mut self.param_states.#index, &self.system_meta, self.world, self.change_tick) + <#param::State as SystemParamState>::get_param(&mut self.param_states.#index, &self.system_meta, self.world, self.change_tick) } } }); @@ -240,7 +240,7 @@ pub fn impl_param_set(_input: TokenStream) -> TokenStream { let meta = &metas[0..param_count]; let param_fn_mut = ¶m_fn_muts[0..param_count]; tokens.extend(TokenStream::from(quote! { - impl<'w, 's, #(#param: SystemParam,)*> SystemParam for ParamSet<'w, 's, (#(#param,)*)> + impl<'w, 's, #(#param: SystemParam,)*> SystemParam for ParamSet<'w, 's, (#(#param,)*)> { type State = ParamSetState<(#(#param::State,)*)>; } @@ -254,9 +254,9 @@ pub fn impl_param_set(_input: TokenStream) -> TokenStream { // SAFETY: Relevant parameter ComponentId and ArchetypeComponentId access is applied to SystemMeta. If any ParamState conflicts // with any prior access, a panic will occur. - unsafe impl<#(#param_state: SystemParamState,)*> SystemParamState for ParamSetState<(#(#param_state,)*)> + unsafe impl<#(#param_state: SystemParamState,)*> SystemParamState for ParamSetState<(#(#param_state,)*)> { - type Item<'w, 's> = ParamSet<'w, 's, (#(<#param_state as SystemParamState>::Item::<'w, 's>,)*)>; + type Item<'w, 's> = ParamSet<'w, 's, (#(<#param_state as SystemParamState>::Item::<'w, 's>,)*)>; fn init(world: &mut World, system_meta: &mut SystemMeta) -> Self { #( @@ -305,7 +305,7 @@ pub fn impl_param_set(_input: TokenStream) -> TokenStream { } } - impl<'w, 's, #(#param: SystemParam,)*> ParamSet<'w, 's, (#(#param,)*)> + impl<'w, 's, #(#param: SystemParam,)*> ParamSet<'w, 's, (#(#param,)*)> { #(#param_fn_mut)* @@ -434,13 +434,13 @@ pub fn derive_system_param(input: TokenStream) -> TokenStream { // The struct can still be accessed via SystemParam::State, e.g. EventReaderState can be accessed via // as SystemParam>::State const _: () = { - impl<'w, 's, #punctuated_generics> #path::system::SystemParam for #struct_name #ty_generics #where_clause { + impl<'w, 's, #punctuated_generics> #path::system::SystemParam<#path::system::Infallible> for #struct_name #ty_generics #where_clause { type State = State<'w, 's, #punctuated_generic_idents>; } #[doc(hidden)] type State<'w, 's, #punctuated_generic_idents> = FetchState< - (#(<#field_types as #path::system::SystemParam>::State,)*), + (#(<#field_types as #path::system::SystemParam<#path::system::Infallible>>::State,)*), #punctuated_generic_idents >; @@ -450,7 +450,7 @@ pub fn derive_system_param(input: TokenStream) -> TokenStream { marker: std::marker::PhantomData(#punctuated_generic_idents)> } - unsafe impl<'__w, '__s, #punctuated_generics> #path::system::SystemParamState for + unsafe impl<'__w, '__s, #punctuated_generics> #path::system::SystemParamState<#path::system::Infallible> for State<'__w, '__s, #punctuated_generic_idents> #where_clause { type Item<'w, 's> = #struct_name #ty_generics; @@ -477,7 +477,7 @@ pub fn derive_system_param(input: TokenStream) -> TokenStream { change_tick: u32, ) -> Self::Item<'w, 's> { #struct_name { - #(#fields: <<#field_types as #path::system::SystemParam>::State as #path::system::SystemParamState>::get_param(&mut state.state.#field_indices, system_meta, world, change_tick),)* + #(#fields: <<#field_types as #path::system::SystemParam<#path::system::Infallible>>::State as #path::system::SystemParamState<#path::system::Infallible>>::get_param(&mut state.state.#field_indices, system_meta, world, change_tick),)* #(#ignored_fields: <#ignored_field_types>::default(),)* } } diff --git a/crates/bevy_ecs/src/system/commands/parallel_scope.rs b/crates/bevy_ecs/src/system/commands/parallel_scope.rs index 72c8118aa5152..2f0d71efc9cf1 100644 --- a/crates/bevy_ecs/src/system/commands/parallel_scope.rs +++ b/crates/bevy_ecs/src/system/commands/parallel_scope.rs @@ -5,7 +5,7 @@ use thread_local::ThreadLocal; use crate::{ entity::Entities, prelude::World, - system::{SystemMeta, SystemParam, SystemParamState}, + system::{Infallible, SystemMeta, SystemParam, SystemParamState}, }; use super::{CommandQueue, Commands}; @@ -48,12 +48,12 @@ pub struct ParallelCommands<'w, 's> { entities: &'w Entities, } -impl SystemParam for ParallelCommands<'_, '_> { +impl SystemParam for ParallelCommands<'_, '_> { type State = ParallelCommandsState; } // SAFETY: no component or resource access to report -unsafe impl SystemParamState for ParallelCommandsState { +unsafe impl SystemParamState for ParallelCommandsState { type Item<'w, 's> = ParallelCommands<'w, 's>; fn init(_: &mut World, _: &mut crate::system::SystemMeta) -> Self { diff --git a/crates/bevy_ecs/src/system/exclusive_system_param.rs b/crates/bevy_ecs/src/system/exclusive_system_param.rs index 6c52738901254..f7babfa7b9185 100644 --- a/crates/bevy_ecs/src/system/exclusive_system_param.rs +++ b/crates/bevy_ecs/src/system/exclusive_system_param.rs @@ -1,7 +1,7 @@ use crate::{ prelude::{FromWorld, QueryState}, query::{ReadOnlyWorldQuery, WorldQuery}, - system::{Local, LocalState, SystemMeta, SystemParam, SystemState}, + system::{Infallible, Local, LocalState, SystemMeta, SystemParam, SystemState}, world::World, }; use bevy_ecs_macros::all_tuples; @@ -45,11 +45,11 @@ impl ExclusiveSystemPa } } -impl<'a, P: SystemParam + 'static> ExclusiveSystemParam for &'a mut SystemState

{ +impl<'a, P: SystemParam + 'static> ExclusiveSystemParam for &'a mut SystemState

{ type State = SystemState

; } -impl ExclusiveSystemParamState for SystemState

{ +impl> ExclusiveSystemParamState for SystemState

{ type Item<'s> = &'s mut SystemState

; fn init(world: &mut World, _system_meta: &mut SystemMeta) -> Self { diff --git a/crates/bevy_ecs/src/system/function_system.rs b/crates/bevy_ecs/src/system/function_system.rs index 2cc88f9e30b77..d2d49661fa66f 100644 --- a/crates/bevy_ecs/src/system/function_system.rs +++ b/crates/bevy_ecs/src/system/function_system.rs @@ -6,8 +6,8 @@ use crate::{ query::{Access, FilteredAccessSet}, schedule::{SystemLabel, SystemLabelId}, system::{ - check_system_change_tick, ReadOnlySystemParam, System, SystemParam, SystemParamItem, - SystemParamState, + check_system_change_tick, Infallible, ReadOnlySystemParam, System, SystemParam, + SystemParamItem, SystemParamState, }, world::{World, WorldId}, }; @@ -139,18 +139,18 @@ impl SystemMeta { /// }; /// }); /// ``` -pub struct SystemState { +pub struct SystemState + 'static> { meta: SystemMeta, - param_state: ::State, + param_state: >::State, world_id: WorldId, archetype_generation: ArchetypeGeneration, } -impl SystemState { +impl> SystemState { pub fn new(world: &mut World) -> Self { let mut meta = SystemMeta::new::(); meta.last_change_tick = world.change_tick().wrapping_sub(MAX_CHANGE_AGE); - let param_state = ::init(world, &mut meta); + let param_state = >::init(world, &mut meta); Self { meta, param_state, @@ -223,7 +223,7 @@ impl SystemState { world: &'w World, ) -> SystemParamItem<'w, 's, Param> { let change_tick = world.increment_change_tick(); - let param = ::get_param( + let param = >::get_param( &mut self.param_state, &self.meta, world, @@ -234,7 +234,7 @@ impl SystemState { } } -impl FromWorld for SystemState { +impl> FromWorld for SystemState { fn from_world(world: &mut World) -> Self { Self::new(world) } @@ -312,7 +312,7 @@ pub struct InputMarker; /// [`FunctionSystem`] must be `.initialized` before they can be run. pub struct FunctionSystem where - Param: SystemParam, + Param: SystemParam, { func: F, param_state: Option, @@ -329,7 +329,7 @@ impl IntoSystem + 'static, Marker: 'static, F: SystemParamFunction + Send + Sync + 'static, { @@ -348,7 +348,7 @@ where impl FunctionSystem where - Param: SystemParam, + Param: SystemParam, { /// Message shown when a system isn't initialised // When lines get too long, rustfmt can sometimes refuse to format them. @@ -360,7 +360,7 @@ impl System for FunctionSystem + 'static, Marker: 'static, F: SystemParamFunction + Send + Sync + 'static, { @@ -400,7 +400,7 @@ where // We update the archetype component access correctly based on `Param`'s requirements // in `update_archetype_component_access`. // Our caller upholds the requirements. - let params = ::State::get_param( + let params = >::State::get_param( self.param_state.as_mut().expect(Self::PARAM_MESSAGE), &self.system_meta, world, @@ -429,7 +429,7 @@ where fn initialize(&mut self, world: &mut World) { self.world_id = Some(world.id()); self.system_meta.last_change_tick = world.change_tick().wrapping_sub(MAX_CHANGE_AGE); - self.param_state = Some(::init( + self.param_state = Some(>::init( world, &mut self.system_meta, )); @@ -553,14 +553,14 @@ impl Copy for SystemTypeIdLabel {} /// ``` /// [`PipeSystem`]: crate::system::PipeSystem /// [`ParamSet`]: crate::system::ParamSet -pub trait SystemParamFunction: Send + Sync + 'static { +pub trait SystemParamFunction, Marker>: Send + Sync + 'static { fn run(&mut self, input: In, param_value: SystemParamItem) -> Out; } macro_rules! impl_system_function { ($($param: ident),*) => { #[allow(non_snake_case)] - impl SystemParamFunction<(), Out, ($($param,)*), ()> for Func + impl),*> SystemParamFunction<(), Out, ($($param,)*), ()> for Func where for <'a> &'a mut Func: FnMut($($param),*) -> Out + @@ -584,7 +584,7 @@ macro_rules! impl_system_function { } #[allow(non_snake_case)] - impl SystemParamFunction for Func + impl),*> SystemParamFunction for Func where for <'a> &'a mut Func: FnMut(In, $($param),*) -> Out + @@ -617,7 +617,7 @@ pub trait AsSystemLabel { fn as_system_label(&self) -> SystemLabelId; } -impl> +impl, Marker, T: SystemParamFunction> AsSystemLabel<(In, Out, Param, Marker)> for T { #[inline] diff --git a/crates/bevy_ecs/src/system/system_param.rs b/crates/bevy_ecs/src/system/system_param.rs index 63f356c4b592c..fd25e547b4a67 100644 --- a/crates/bevy_ecs/src/system/system_param.rs +++ b/crates/bevy_ecs/src/system/system_param.rs @@ -18,11 +18,32 @@ use bevy_ptr::UnsafeCellDeref; use bevy_utils::synccell::SyncCell; use std::{ borrow::Cow, + error::Error, fmt::Debug, marker::PhantomData, ops::{Deref, DerefMut}, }; +pub struct Infallible; +pub struct Optional; +pub struct Resultful; + +mod sealed { + use std::error::Error; + pub trait Fallibility { + type Scope; + } + impl Fallibility for super::Infallible { + type Scope = T; + } + impl Fallibility for super::Optional { + type Scope = Option; + } + impl Fallibility for super::Resultful { + type Scope = Result>; + } +} + /// A parameter that can be used in a [`System`](super::System). /// /// # Derive @@ -121,11 +142,138 @@ use std::{ /// /// [`SyncCell`]: bevy_utils::synccell::SyncCell /// [`Exclusive`]: https://doc.rust-lang.org/nightly/std/sync/struct.Exclusive.html -pub trait SystemParam: Sized { - type State: SystemParamState; +pub trait SystemParam: Sized { + type State: SystemParamState; +} + +impl> SystemParam for T { + type State = T::State; +} + +unsafe impl> SystemParamState for T { + fn init(world: &mut World, system_meta: &mut SystemMeta) -> Self { + T::init(world, system_meta) + } + #[inline] + fn new_archetype(&mut self, archetype: &Archetype, system_meta: &mut SystemMeta) { + self.new_archetype(archetype, system_meta); + } + #[inline] + fn apply(&mut self, system_meta: &SystemMeta, world: &mut World) { + self.apply(system_meta, world); + } + + type Item<'world, 'state> = T::Item<'world, 'state>; + + unsafe fn get_param<'world, 'state>( + state: &'state mut Self, + system_meta: &SystemMeta, + world: &'world World, + change_tick: u32, + ) -> Self::Item<'world, 'state> { + T::get_param(state, system_meta, world, change_tick).unwrap_or_else(|| { + panic!( + "Failed to get system param `{}`", + std::any::type_name::>() + ) + }) + } +} + +impl> SystemParam for Option { + type State = OptionState; +} + +#[doc(hidden)] +pub struct OptionState>(T); + +unsafe impl> SystemParamState for OptionState { + fn init(world: &mut World, system_meta: &mut SystemMeta) -> Self { + Self(T::init(world, system_meta)) + } + #[inline] + fn new_archetype(&mut self, archetype: &Archetype, system_meta: &mut SystemMeta) { + self.0.new_archetype(archetype, system_meta); + } + #[inline] + fn apply(&mut self, system_meta: &SystemMeta, world: &mut World) { + self.0.apply(system_meta, world); + } + + type Item<'world, 'state> = Option>; + + unsafe fn get_param<'world, 'state>( + state: &'state mut Self, + system_meta: &SystemMeta, + world: &'world World, + change_tick: u32, + ) -> Self::Item<'world, 'state> { + T::get_param(&mut state.0, system_meta, world, change_tick) + } +} + +impl> SystemParam for T { + type State = T::State; } -pub type SystemParamItem<'w, 's, P> = <

::State as SystemParamState>::Item<'w, 's>; +unsafe impl > SystemParamState for T { + fn init(world: &mut World, system_meta: &mut SystemMeta) -> Self { + T::init(world, system_meta) + } + #[inline] + fn new_archetype(&mut self, archetype: &Archetype, system_meta: &mut SystemMeta) { + self.new_archetype(archetype, system_meta); + } + #[inline] + fn apply(&mut self, system_meta: &SystemMeta, world: &mut World) { + self.apply(system_meta, world); + } + + type Item<'world, 'state> = T::Item<'world, 'state>; + + unsafe fn get_param<'world, 'state>( + state: &'state mut Self, + system_meta: &SystemMeta, + world: &'world World, + change_tick: u32, + ) -> Option> { + T::get_param(state, system_meta, world, change_tick).ok() + } +} + +impl> SystemParam for Result> { + type State = ResultState; +} + +#[doc(hidden)] +pub struct ResultState>(T); + +unsafe impl> SystemParamState for ResultState { + fn init(world: &mut World, system_meta: &mut SystemMeta) -> Self { + Self(T::init(world, system_meta)) + } + #[inline] + fn new_archetype(&mut self, archetype: &Archetype, system_meta: &mut SystemMeta) { + self.0.new_archetype(archetype, system_meta); + } + #[inline] + fn apply(&mut self, system_meta: &SystemMeta, world: &mut World) { + self.0.apply(system_meta, world); + } + + type Item<'world, 'state> = Result, Box>; + + unsafe fn get_param<'world, 'state>( + state: &'state mut Self, + system_meta: &SystemMeta, + world: &'world World, + change_tick: u32, + ) -> Self::Item<'world, 'state> { + T::get_param(&mut state.0, system_meta, world, change_tick) + } +} + +pub type SystemParamItem<'w, 's, P> = <

>::State as SystemParamState>::Item<'w, 's>; /// The state of a [`SystemParam`]. /// @@ -135,7 +283,7 @@ pub type SystemParamItem<'w, 's, P> = <

::State as SystemParamS /// [`World`] access used by the [`SystemParamState`]. /// Additionally, it is the implementor's responsibility to ensure there is no /// conflicting access across all [`SystemParam`]'s. -pub unsafe trait SystemParamState: Send + Sync + 'static { +pub unsafe trait SystemParamState: Send + Sync + 'static { fn init(world: &mut World, system_meta: &mut SystemMeta) -> Self; #[inline] fn new_archetype(&mut self, _archetype: &Archetype, _system_meta: &mut SystemMeta) {} @@ -143,7 +291,7 @@ pub unsafe trait SystemParamState: Send + Sync + 'static { #[allow(unused_variables)] fn apply(&mut self, system_meta: &SystemMeta, _world: &mut World) {} - type Item<'world, 'state>: SystemParam; + type Item<'world, 'state>: SystemParam; /// # Safety /// /// This call might access any of the input parameters in an unsafe way. Make sure the data @@ -153,16 +301,16 @@ pub unsafe trait SystemParamState: Send + Sync + 'static { system_meta: &SystemMeta, world: &'world World, change_tick: u32, - ) -> Self::Item<'world, 'state>; + ) -> T::Scope>; } /// A [`SystemParam`] that only reads a given [`World`]. /// /// # Safety /// This must only be implemented for [`SystemParam`] impls that exclusively read the World passed in to [`SystemParamState::get_param`] -pub unsafe trait ReadOnlySystemParam: SystemParam {} +pub unsafe trait ReadOnlySystemParam: SystemParam {} -impl<'w, 's, Q: WorldQuery + 'static, F: ReadOnlyWorldQuery + 'static> SystemParam +impl<'w, 's, Q: WorldQuery + 'static, F: ReadOnlyWorldQuery + 'static> SystemParam for Query<'w, 's, Q, F> { type State = QueryState; @@ -176,7 +324,7 @@ unsafe impl<'w, 's, Q: ReadOnlyWorldQuery + 'static, F: ReadOnlyWorldQuery + 'st // SAFETY: Relevant query ComponentId and ArchetypeComponentId access is applied to SystemMeta. If // this QueryState conflicts with any prior access, a panic will occur. -unsafe impl SystemParamState +unsafe impl SystemParamState for QueryState { type Item<'w, 's> = Query<'w, 's, Q, F>; @@ -239,14 +387,14 @@ fn assert_component_access_compatibility( query_type, filter_type, system_name, accesses); } -pub struct ParamSet<'w, 's, T: SystemParam> { +pub struct ParamSet<'w, 's, T: SystemParam> { param_states: &'s mut T::State, world: &'w World, system_meta: SystemMeta, change_tick: u32, } /// The [`SystemParamState`] of [`ParamSet`]. -pub struct ParamSetState(T); +pub struct ParamSetState>(T); impl_param_set!(); @@ -390,13 +538,13 @@ pub struct ResState { marker: PhantomData, } -impl<'a, T: Resource> SystemParam for Res<'a, T> { +impl<'a, T: Resource> SystemParam for Res<'a, T> { type State = ResState; } -// SAFETY: Res ComponentId and ArchetypeComponentId access is applied to SystemMeta. If this Res +// SAFETY: Res ComponentI d and ArchetypeComponentId access is applied to SystemMeta. If this Res // conflicts with any prior access, a panic will occur. -unsafe impl SystemParamState for ResState { +unsafe impl SystemParamState for ResState { type Item<'w, 's> = Res<'w, T>; fn init(world: &mut World, system_meta: &mut SystemMeta) -> Self { @@ -430,56 +578,9 @@ unsafe impl SystemParamState for ResState { system_meta: &SystemMeta, world: &'w World, change_tick: u32, - ) -> Self::Item<'w, 's> { - let (ptr, ticks) = world - .get_resource_with_ticks(state.component_id) - .unwrap_or_else(|| { - panic!( - "Resource requested by {} does not exist: {}", - system_meta.name, - std::any::type_name::() - ) - }); - Res { - value: ptr.deref(), - added: ticks.added.deref(), - changed: ticks.changed.deref(), - last_change_tick: system_meta.last_change_tick, - change_tick, - } - } -} - -/// The [`SystemParamState`] of [`Option>`]. -/// See: [`Res`] -#[doc(hidden)] -pub struct OptionResState(ResState); - -impl<'a, T: Resource> SystemParam for Option> { - type State = OptionResState; -} - -// SAFETY: Only reads a single World resource -unsafe impl<'a, T: Resource> ReadOnlySystemParam for Option> {} - -// SAFETY: this impl defers to `ResState`, which initializes -// and validates the correct world access -unsafe impl SystemParamState for OptionResState { - type Item<'w, 's> = Option>; - - fn init(world: &mut World, system_meta: &mut SystemMeta) -> Self { - Self(ResState::init(world, system_meta)) - } - - #[inline] - unsafe fn get_param<'w, 's>( - state: &'s mut Self, - system_meta: &SystemMeta, - world: &'w World, - change_tick: u32, - ) -> Self::Item<'w, 's> { + ) -> Option> { world - .get_resource_with_ticks(state.0.component_id) + .get_resource_with_ticks(state.component_id) .map(|(ptr, ticks)| Res { value: ptr.deref(), added: ticks.added.deref(), @@ -497,13 +598,13 @@ pub struct ResMutState { marker: PhantomData, } -impl<'a, T: Resource> SystemParam for ResMut<'a, T> { +impl<'a, T: Resource> SystemParam for ResMut<'a, T> { type State = ResMutState; } // SAFETY: Res ComponentId and ArchetypeComponentId access is applied to SystemMeta. If this Res // conflicts with any prior access, a panic will occur. -unsafe impl SystemParamState for ResMutState { +unsafe impl SystemParamState for ResMutState { type Item<'w, 's> = ResMut<'w, T>; fn init(world: &mut World, system_meta: &mut SystemMeta) -> Self { @@ -540,55 +641,9 @@ unsafe impl SystemParamState for ResMutState { system_meta: &SystemMeta, world: &'w World, change_tick: u32, - ) -> Self::Item<'w, 's> { - let value = world - .get_resource_unchecked_mut_with_id(state.component_id) - .unwrap_or_else(|| { - panic!( - "Resource requested by {} does not exist: {}", - system_meta.name, - std::any::type_name::() - ) - }); - ResMut { - value: value.value, - ticks: Ticks { - added: value.ticks.added, - changed: value.ticks.changed, - last_change_tick: system_meta.last_change_tick, - change_tick, - }, - } - } -} - -/// The [`SystemParamState`] of [`Option>`]. -/// See: [`ResMut`] -#[doc(hidden)] -pub struct OptionResMutState(ResMutState); - -impl<'a, T: Resource> SystemParam for Option> { - type State = OptionResMutState; -} - -// SAFETY: this impl defers to `ResMutState`, which initializes -// and validates the correct world access -unsafe impl SystemParamState for OptionResMutState { - type Item<'w, 's> = Option>; - - fn init(world: &mut World, system_meta: &mut SystemMeta) -> Self { - Self(ResMutState::init(world, system_meta)) - } - - #[inline] - unsafe fn get_param<'w, 's>( - state: &'s mut Self, - system_meta: &SystemMeta, - world: &'w World, - change_tick: u32, - ) -> Self::Item<'w, 's> { + ) -> Option> { world - .get_resource_unchecked_mut_with_id(state.0.component_id) + .get_resource_unchecked_mut_with_id(state.component_id) .map(|value| ResMut { value: value.value, ticks: Ticks { @@ -601,7 +656,7 @@ unsafe impl SystemParamState for OptionResMutState { } } -impl<'w, 's> SystemParam for Commands<'w, 's> { +impl<'w, 's> SystemParam for Commands<'w, 's> { type State = CommandQueue; } @@ -609,7 +664,7 @@ impl<'w, 's> SystemParam for Commands<'w, 's> { unsafe impl<'w, 's> ReadOnlySystemParam for Commands<'w, 's> {} // SAFETY: only local state is accessed -unsafe impl SystemParamState for CommandQueue { +unsafe impl SystemParamState for CommandQueue { type Item<'w, 's> = Commands<'w, 's>; fn init(_world: &mut World, _system_meta: &mut SystemMeta) -> Self { @@ -642,12 +697,12 @@ unsafe impl<'w> ReadOnlySystemParam for &'w World {} #[doc(hidden)] pub struct WorldState; -impl<'w> SystemParam for &'w World { +impl<'w> SystemParam for &'w World { type State = WorldState; } // SAFETY: `read_all` access is set and conflicts result in a panic -unsafe impl SystemParamState for WorldState { +unsafe impl SystemParamState for WorldState { type Item<'w, 's> = &'w World; fn init(_world: &mut World, system_meta: &mut SystemMeta) -> Self { @@ -787,12 +842,12 @@ where #[doc(hidden)] pub struct LocalState(pub(crate) SyncCell); -impl<'a, T: FromWorld + Send + 'static> SystemParam for Local<'a, T> { +impl<'a, T: FromWorld + Send + 'static> SystemParam for Local<'a, T> { type State = LocalState; } // SAFETY: only local state is accessed -unsafe impl SystemParamState for LocalState { +unsafe impl SystemParamState for LocalState { type Item<'w, 's> = Local<'s, T>; fn init(world: &mut World, _system_meta: &mut SystemMeta) -> Self { @@ -875,13 +930,13 @@ pub struct RemovedComponentsState { marker: PhantomData, } -impl<'a, T: Component> SystemParam for RemovedComponents<'a, T> { +impl<'a, T: Component> SystemParam for RemovedComponents<'a, T> { type State = RemovedComponentsState; } // SAFETY: no component access. removed component entity collections can be read in parallel and are // never mutably borrowed during system execution -unsafe impl SystemParamState for RemovedComponentsState { +unsafe impl SystemParamState for RemovedComponentsState { type Item<'w, 's> = RemovedComponents<'w, T>; fn init(world: &mut World, _system_meta: &mut SystemMeta) -> Self { @@ -978,13 +1033,13 @@ pub struct NonSendState { marker: PhantomData T>, } -impl<'a, T: 'static> SystemParam for NonSend<'a, T> { +impl<'a, T: 'static> SystemParam for NonSend<'a, T> { type State = NonSendState; } // SAFETY: NonSendComponentId and ArchetypeComponentId access is applied to SystemMeta. If this // NonSend conflicts with any prior access, a panic will occur. -unsafe impl SystemParamState for NonSendState { +unsafe impl SystemParamState for NonSendState { type Item<'w, 's> = NonSend<'w, T>; fn init(world: &mut World, system_meta: &mut SystemMeta) -> Self { @@ -1020,58 +1075,10 @@ unsafe impl SystemParamState for NonSendState { system_meta: &SystemMeta, world: &'w World, change_tick: u32, - ) -> Self::Item<'w, 's> { - world.validate_non_send_access::(); - let (ptr, ticks) = world - .get_resource_with_ticks(state.component_id) - .unwrap_or_else(|| { - panic!( - "Non-send resource requested by {} does not exist: {}", - system_meta.name, - std::any::type_name::() - ) - }); - - NonSend { - value: ptr.deref(), - ticks: ticks.read(), - last_change_tick: system_meta.last_change_tick, - change_tick, - } - } -} - -/// The [`SystemParamState`] of [`Option>`]. -/// See: [`NonSend`] -#[doc(hidden)] -pub struct OptionNonSendState(NonSendState); - -impl<'w, T: 'static> SystemParam for Option> { - type State = OptionNonSendState; -} - -// SAFETY: Only reads a single non-send resource -unsafe impl<'w, T: 'static> ReadOnlySystemParam for Option> {} - -// SAFETY: this impl defers to `NonSendState`, which initializes -// and validates the correct world access -unsafe impl SystemParamState for OptionNonSendState { - type Item<'w, 's> = Option>; - - fn init(world: &mut World, system_meta: &mut SystemMeta) -> Self { - Self(NonSendState::init(world, system_meta)) - } - - #[inline] - unsafe fn get_param<'w, 's>( - state: &'s mut Self, - system_meta: &SystemMeta, - world: &'w World, - change_tick: u32, - ) -> Self::Item<'w, 's> { + ) -> Option> { world.validate_non_send_access::(); world - .get_resource_with_ticks(state.0.component_id) + .get_resource_with_ticks(state.component_id) .map(|(ptr, ticks)| NonSend { value: ptr.deref(), ticks: ticks.read(), @@ -1088,13 +1095,13 @@ pub struct NonSendMutState { marker: PhantomData T>, } -impl<'a, T: 'static> SystemParam for NonSendMut<'a, T> { +impl<'a, T: 'static> SystemParam for NonSendMut<'a, T> { type State = NonSendMutState; } // SAFETY: NonSendMut ComponentId and ArchetypeComponentId access is applied to SystemMeta. If this // NonSendMut conflicts with any prior access, a panic will occur. -unsafe impl SystemParamState for NonSendMutState { +unsafe impl SystemParamState for NonSendMutState { type Item<'w, 's> = NonSendMut<'w, T>; fn init(world: &mut World, system_meta: &mut SystemMeta) -> Self { @@ -1133,52 +1140,10 @@ unsafe impl SystemParamState for NonSendMutState { system_meta: &SystemMeta, world: &'w World, change_tick: u32, - ) -> Self::Item<'w, 's> { - world.validate_non_send_access::(); - let (ptr, ticks) = world - .get_resource_with_ticks(state.component_id) - .unwrap_or_else(|| { - panic!( - "Non-send resource requested by {} does not exist: {}", - system_meta.name, - std::any::type_name::() - ) - }); - NonSendMut { - value: ptr.assert_unique().deref_mut(), - ticks: Ticks::from_tick_cells(ticks, system_meta.last_change_tick, change_tick), - } - } -} - -/// The [`SystemParamState`] of [`Option>`]. -/// See: [`NonSendMut`] -#[doc(hidden)] -pub struct OptionNonSendMutState(NonSendMutState); - -impl<'a, T: 'static> SystemParam for Option> { - type State = OptionNonSendMutState; -} - -// SAFETY: this impl defers to `NonSendMutState`, which initializes -// and validates the correct world access -unsafe impl SystemParamState for OptionNonSendMutState { - type Item<'w, 's> = Option>; - - fn init(world: &mut World, system_meta: &mut SystemMeta) -> Self { - Self(NonSendMutState::init(world, system_meta)) - } - - #[inline] - unsafe fn get_param<'w, 's>( - state: &'s mut Self, - system_meta: &SystemMeta, - world: &'w World, - change_tick: u32, - ) -> Self::Item<'w, 's> { + ) -> Option> { world.validate_non_send_access::(); world - .get_resource_with_ticks(state.0.component_id) + .get_resource_with_ticks(state.component_id) .map(|(ptr, ticks)| NonSendMut { value: ptr.assert_unique().deref_mut(), ticks: Ticks::from_tick_cells(ticks, system_meta.last_change_tick, change_tick), @@ -1186,7 +1151,7 @@ unsafe impl SystemParamState for OptionNonSendMutState { } } -impl<'a> SystemParam for &'a Archetypes { +impl<'a> SystemParam for &'a Archetypes { type State = ArchetypesState; } @@ -1198,7 +1163,7 @@ unsafe impl<'a> ReadOnlySystemParam for &'a Archetypes {} pub struct ArchetypesState; // SAFETY: no component value access -unsafe impl SystemParamState for ArchetypesState { +unsafe impl SystemParamState for ArchetypesState { type Item<'w, 's> = &'w Archetypes; fn init(_world: &mut World, _system_meta: &mut SystemMeta) -> Self { @@ -1216,7 +1181,7 @@ unsafe impl SystemParamState for ArchetypesState { } } -impl<'a> SystemParam for &'a Components { +impl<'a> SystemParam for &'a Components { type State = ComponentsState; } @@ -1228,7 +1193,7 @@ unsafe impl<'a> ReadOnlySystemParam for &'a Components {} pub struct ComponentsState; // SAFETY: no component value access -unsafe impl SystemParamState for ComponentsState { +unsafe impl SystemParamState for ComponentsState { type Item<'w, 's> = &'w Components; fn init(_world: &mut World, _system_meta: &mut SystemMeta) -> Self { @@ -1246,7 +1211,7 @@ unsafe impl SystemParamState for ComponentsState { } } -impl<'a> SystemParam for &'a Entities { +impl<'a> SystemParam for &'a Entities { type State = EntitiesState; } @@ -1258,7 +1223,7 @@ unsafe impl<'a> ReadOnlySystemParam for &'a Entities {} pub struct EntitiesState; // SAFETY: no component value access -unsafe impl SystemParamState for EntitiesState { +unsafe impl SystemParamState for EntitiesState { type Item<'w, 's> = &'w Entities; fn init(_world: &mut World, _system_meta: &mut SystemMeta) -> Self { @@ -1276,7 +1241,7 @@ unsafe impl SystemParamState for EntitiesState { } } -impl<'a> SystemParam for &'a Bundles { +impl<'a> SystemParam for &'a Bundles { type State = BundlesState; } @@ -1288,7 +1253,7 @@ unsafe impl<'a> ReadOnlySystemParam for &'a Bundles {} pub struct BundlesState; // SAFETY: no component value access -unsafe impl SystemParamState for BundlesState { +unsafe impl SystemParamState for BundlesState { type Item<'w, 's> = &'w Bundles; fn init(_world: &mut World, _system_meta: &mut SystemMeta) -> Self { @@ -1338,7 +1303,7 @@ impl SystemChangeTick { // SAFETY: Only reads internal system state unsafe impl ReadOnlySystemParam for SystemChangeTick {} -impl SystemParam for SystemChangeTick { +impl SystemParam for SystemChangeTick { type State = SystemChangeTickState; } @@ -1347,7 +1312,7 @@ impl SystemParam for SystemChangeTick { pub struct SystemChangeTickState {} // SAFETY: `SystemParamTickState` doesn't require any world access -unsafe impl SystemParamState for SystemChangeTickState { +unsafe impl SystemParamState for SystemChangeTickState { type Item<'w, 's> = SystemChangeTick; fn init(_world: &mut World, _system_meta: &mut SystemMeta) -> Self { @@ -1414,7 +1379,7 @@ impl<'s> std::fmt::Display for SystemName<'s> { } } -impl<'s> SystemParam for SystemName<'s> { +impl<'s> SystemParam for SystemName<'s> { type State = SystemNameState; } @@ -1428,7 +1393,7 @@ pub struct SystemNameState { } // SAFETY: no component value access -unsafe impl SystemParamState for SystemNameState { +unsafe impl SystemParamState for SystemNameState { type Item<'w, 's> = SystemName<'s>; fn init(_world: &mut World, system_meta: &mut SystemMeta) -> Self { @@ -1452,7 +1417,7 @@ unsafe impl SystemParamState for SystemNameState { macro_rules! impl_system_param_tuple { ($($param: ident),*) => { - impl<$($param: SystemParam),*> SystemParam for ($($param,)*) { + impl<$($param: SystemParam),*> SystemParam for ($($param,)*) { type State = ($($param::State,)*); } @@ -1463,7 +1428,7 @@ macro_rules! impl_system_param_tuple { // SAFETY: implementors of each `SystemParamState` in the tuple have validated their impls #[allow(clippy::undocumented_unsafe_blocks)] // false positive by clippy #[allow(non_snake_case)] - unsafe impl<$($param: SystemParamState),*> SystemParamState for ($($param,)*) { + unsafe impl<$($param: SystemParamState),*> SystemParamState for ($($param,)*) { type Item<'w, 's> = ($($param::Item::<'w, 's>,)*); #[inline] @@ -1563,9 +1528,9 @@ pub mod lifetimeless { /// # } /// ``` /// -pub struct StaticSystemParam<'w, 's, P: SystemParam>(SystemParamItem<'w, 's, P>); +pub struct StaticSystemParam<'w, 's, P: SystemParam>(SystemParamItem<'w, 's, P>); -impl<'w, 's, P: SystemParam> Deref for StaticSystemParam<'w, 's, P> { +impl<'w, 's, P: SystemParam> Deref for StaticSystemParam<'w, 's, P> { type Target = SystemParamItem<'w, 's, P>; fn deref(&self) -> &Self::Target { @@ -1573,13 +1538,13 @@ impl<'w, 's, P: SystemParam> Deref for StaticSystemParam<'w, 's, P> { } } -impl<'w, 's, P: SystemParam> DerefMut for StaticSystemParam<'w, 's, P> { +impl<'w, 's, P: SystemParam> DerefMut for StaticSystemParam<'w, 's, P> { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } } -impl<'w, 's, P: SystemParam> StaticSystemParam<'w, 's, P> { +impl<'w, 's, P: SystemParam> StaticSystemParam<'w, 's, P> { /// Get the value of the parameter pub fn into_inner(self) -> SystemParamItem<'w, 's, P> { self.0 @@ -1596,14 +1561,14 @@ unsafe impl<'w, 's, P: ReadOnlySystemParam + 'static> ReadOnlySystemParam { } -impl<'world, 'state, P: SystemParam + 'static> SystemParam +impl<'world, 'state, P: SystemParam + 'static> SystemParam for StaticSystemParam<'world, 'state, P> { type State = StaticSystemParamState; } // SAFETY: all methods are just delegated to `S`'s `SystemParamState` implementation -unsafe impl + 'static> SystemParamState +unsafe impl, P: SystemParam + 'static> SystemParamState for StaticSystemParamState { type Item<'world, 'state> = StaticSystemParam<'world, 'state, P>; diff --git a/crates/bevy_render/src/extract_param.rs b/crates/bevy_render/src/extract_param.rs index b3d4ba665a55a..1f8325bcf2168 100644 --- a/crates/bevy_render/src/extract_param.rs +++ b/crates/bevy_render/src/extract_param.rs @@ -3,7 +3,7 @@ use bevy_ecs::{ prelude::*, system::{ ReadOnlySystemParam, ResState, SystemMeta, SystemParam, SystemParamItem, SystemParamState, - SystemState, + SystemState, Infallible }, }; use std::ops::{Deref, DerefMut}; @@ -49,7 +49,7 @@ where item: SystemParamItem<'w, 's, P>, } -impl<'w, 's, P> SystemParam for Extract<'w, 's, P> +impl<'w, 's, P> SystemParam for Extract<'w, 's, P> where P: ReadOnlySystemParam, { @@ -57,14 +57,14 @@ where } #[doc(hidden)] -pub struct ExtractState { +pub struct ExtractState + 'static> { state: SystemState

, main_world_state: ResState, } // SAFETY: only accesses MainWorld resource with read only system params using ResState, // which is initialized in init() -unsafe impl SystemParamState for ExtractState

+unsafe impl + 'static> SystemParamState for ExtractState

where P: ReadOnlySystemParam + 'static, { @@ -74,7 +74,7 @@ where let mut main_world = world.resource_mut::(); Self { state: SystemState::new(&mut main_world), - main_world_state: ResState::init(world, system_meta), + main_world_state: as SystemParamState>::init(world, system_meta), } } @@ -84,7 +84,7 @@ where world: &'w World, change_tick: u32, ) -> Self::Item<'w, 's> { - let main_world = ResState::::get_param( + let main_world = as SystemParamState>::get_param( &mut state.main_world_state, system_meta, world, @@ -95,7 +95,7 @@ where } } -impl<'w, 's, P: SystemParam> Deref for Extract<'w, 's, P> +impl<'w, 's, P: SystemParam> Deref for Extract<'w, 's, P> where P: ReadOnlySystemParam, { @@ -107,7 +107,7 @@ where } } -impl<'w, 's, P: SystemParam> DerefMut for Extract<'w, 's, P> +impl<'w, 's, P: SystemParam> DerefMut for Extract<'w, 's, P> where P: ReadOnlySystemParam, { @@ -117,7 +117,7 @@ where } } -impl<'a, 'w, 's, P: SystemParam> IntoIterator for &'a Extract<'w, 's, P> +impl<'a, 'w, 's, P: SystemParam> IntoIterator for &'a Extract<'w, 's, P> where P: ReadOnlySystemParam, &'a SystemParamItem<'w, 's, P>: IntoIterator, diff --git a/crates/bevy_render/src/render_asset.rs b/crates/bevy_render/src/render_asset.rs index 9bdee8a4cd893..eab9d6648d1fb 100644 --- a/crates/bevy_render/src/render_asset.rs +++ b/crates/bevy_render/src/render_asset.rs @@ -4,7 +4,7 @@ use bevy_asset::{Asset, AssetEvent, Assets, Handle}; use bevy_derive::{Deref, DerefMut}; use bevy_ecs::{ prelude::*, - system::{StaticSystemParam, SystemParam, SystemParamItem}, + system::{Infallible, StaticSystemParam, SystemParam, SystemParamItem}, }; use bevy_utils::{HashMap, HashSet}; use std::marker::PhantomData; @@ -29,7 +29,7 @@ pub trait RenderAsset: Asset { type PreparedAsset: Send + Sync + 'static; /// Specifies all ECS data required by [`RenderAsset::prepare_asset`]. /// For convenience use the [`lifetimeless`](bevy_ecs::system::lifetimeless) [`SystemParam`]. - type Param: SystemParam; + type Param: SystemParam; /// Converts the asset into a [`RenderAsset::ExtractedAsset`]. fn extract_asset(&self) -> Self::ExtractedAsset; /// Prepares the `extracted asset` for the GPU by transforming it into diff --git a/crates/bevy_render/src/render_phase/draw.rs b/crates/bevy_render/src/render_phase/draw.rs index 67ff987b7972d..2a32ca4ede0a3 100644 --- a/crates/bevy_render/src/render_phase/draw.rs +++ b/crates/bevy_render/src/render_phase/draw.rs @@ -8,7 +8,7 @@ use bevy_ecs::{ entity::Entity, system::{ lifetimeless::SRes, ReadOnlySystemParam, Resource, SystemParam, SystemParamItem, - SystemState, + SystemState, Infallible, }, world::World, }; @@ -167,7 +167,7 @@ impl DrawFunctions

{ pub trait RenderCommand { /// Specifies all ECS data required by [`RenderCommand::render`]. /// All parameters have to be read only. - type Param: SystemParam + 'static; + type Param: SystemParam + 'static; /// Renders the [`PhaseItem`] by issuing draw calls via the [`TrackedRenderPass`]. fn render<'w>( @@ -184,7 +184,7 @@ pub enum RenderCommandResult { } pub trait EntityRenderCommand { - type Param: SystemParam + 'static; + type Param: SystemParam + 'static; fn render<'w>( view: Entity, item: Entity, From f2d168eeab12565f19462da3ffff91677e4012bb Mon Sep 17 00:00:00 2001 From: Jonah Henriksson <33059163+JonahPlusPlus@users.noreply.github.com> Date: Sun, 11 Dec 2022 22:34:45 -0500 Subject: [PATCH 02/13] `cargo fmt` --- crates/bevy_ecs/src/system/function_system.rs | 13 +++-- crates/bevy_ecs/src/system/system_param.rs | 49 ++++++++++--------- crates/bevy_render/src/extract_param.rs | 9 ++-- crates/bevy_render/src/render_phase/draw.rs | 4 +- 4 files changed, 43 insertions(+), 32 deletions(-) diff --git a/crates/bevy_ecs/src/system/function_system.rs b/crates/bevy_ecs/src/system/function_system.rs index d2d49661fa66f..2457be92beb1e 100644 --- a/crates/bevy_ecs/src/system/function_system.rs +++ b/crates/bevy_ecs/src/system/function_system.rs @@ -553,7 +553,9 @@ impl Copy for SystemTypeIdLabel {} /// ``` /// [`PipeSystem`]: crate::system::PipeSystem /// [`ParamSet`]: crate::system::ParamSet -pub trait SystemParamFunction, Marker>: Send + Sync + 'static { +pub trait SystemParamFunction, Marker>: + Send + Sync + 'static +{ fn run(&mut self, input: In, param_value: SystemParamItem) -> Out; } @@ -617,8 +619,13 @@ pub trait AsSystemLabel { fn as_system_label(&self) -> SystemLabelId; } -impl, Marker, T: SystemParamFunction> - AsSystemLabel<(In, Out, Param, Marker)> for T +impl< + In, + Out, + Param: SystemParam, + Marker, + T: SystemParamFunction, + > AsSystemLabel<(In, Out, Param, Marker)> for T { #[inline] fn as_system_label(&self) -> SystemLabelId { diff --git a/crates/bevy_ecs/src/system/system_param.rs b/crates/bevy_ecs/src/system/system_param.rs index fd25e547b4a67..3afa1af081c4d 100644 --- a/crates/bevy_ecs/src/system/system_param.rs +++ b/crates/bevy_ecs/src/system/system_param.rs @@ -166,11 +166,11 @@ unsafe impl> SystemParamState for T { type Item<'world, 'state> = T::Item<'world, 'state>; unsafe fn get_param<'world, 'state>( - state: &'state mut Self, - system_meta: &SystemMeta, - world: &'world World, - change_tick: u32, - ) -> Self::Item<'world, 'state> { + state: &'state mut Self, + system_meta: &SystemMeta, + world: &'world World, + change_tick: u32, + ) -> Self::Item<'world, 'state> { T::get_param(state, system_meta, world, change_tick).unwrap_or_else(|| { panic!( "Failed to get system param `{}`", @@ -203,11 +203,11 @@ unsafe impl> SystemParamState for Opti type Item<'world, 'state> = Option>; unsafe fn get_param<'world, 'state>( - state: &'state mut Self, - system_meta: &SystemMeta, - world: &'world World, - change_tick: u32, - ) -> Self::Item<'world, 'state> { + state: &'state mut Self, + system_meta: &SystemMeta, + world: &'world World, + change_tick: u32, + ) -> Self::Item<'world, 'state> { T::get_param(&mut state.0, system_meta, world, change_tick) } } @@ -216,7 +216,7 @@ impl> SystemParam for T { type State = T::State; } -unsafe impl > SystemParamState for T { +unsafe impl> SystemParamState for T { fn init(world: &mut World, system_meta: &mut SystemMeta) -> Self { T::init(world, system_meta) } @@ -232,11 +232,11 @@ unsafe impl > SystemParamState for T { type Item<'world, 'state> = T::Item<'world, 'state>; unsafe fn get_param<'world, 'state>( - state: &'state mut Self, - system_meta: &SystemMeta, - world: &'world World, - change_tick: u32, - ) -> Option> { + state: &'state mut Self, + system_meta: &SystemMeta, + world: &'world World, + change_tick: u32, + ) -> Option> { T::get_param(state, system_meta, world, change_tick).ok() } } @@ -264,16 +264,17 @@ unsafe impl> SystemParamState for Res type Item<'world, 'state> = Result, Box>; unsafe fn get_param<'world, 'state>( - state: &'state mut Self, - system_meta: &SystemMeta, - world: &'world World, - change_tick: u32, - ) -> Self::Item<'world, 'state> { + state: &'state mut Self, + system_meta: &SystemMeta, + world: &'world World, + change_tick: u32, + ) -> Self::Item<'world, 'state> { T::get_param(&mut state.0, system_meta, world, change_tick) } } -pub type SystemParamItem<'w, 's, P> = <

>::State as SystemParamState>::Item<'w, 's>; +pub type SystemParamItem<'w, 's, P> = + <

>::State as SystemParamState>::Item<'w, 's>; /// The state of a [`SystemParam`]. /// @@ -1568,8 +1569,8 @@ impl<'world, 'state, P: SystemParam + 'static> SystemParam, P: SystemParam + 'static> SystemParamState - for StaticSystemParamState +unsafe impl, P: SystemParam + 'static> + SystemParamState for StaticSystemParamState { type Item<'world, 'state> = StaticSystemParam<'world, 'state, P>; diff --git a/crates/bevy_render/src/extract_param.rs b/crates/bevy_render/src/extract_param.rs index 1f8325bcf2168..a0b9cb1292d32 100644 --- a/crates/bevy_render/src/extract_param.rs +++ b/crates/bevy_render/src/extract_param.rs @@ -2,8 +2,8 @@ use crate::MainWorld; use bevy_ecs::{ prelude::*, system::{ - ReadOnlySystemParam, ResState, SystemMeta, SystemParam, SystemParamItem, SystemParamState, - SystemState, Infallible + Infallible, ReadOnlySystemParam, ResState, SystemMeta, SystemParam, SystemParamItem, + SystemParamState, SystemState, }, }; use std::ops::{Deref, DerefMut}; @@ -74,7 +74,10 @@ where let mut main_world = world.resource_mut::(); Self { state: SystemState::new(&mut main_world), - main_world_state: as SystemParamState>::init(world, system_meta), + main_world_state: as SystemParamState>::init( + world, + system_meta, + ), } } diff --git a/crates/bevy_render/src/render_phase/draw.rs b/crates/bevy_render/src/render_phase/draw.rs index 2a32ca4ede0a3..3a7f73e10ca07 100644 --- a/crates/bevy_render/src/render_phase/draw.rs +++ b/crates/bevy_render/src/render_phase/draw.rs @@ -7,8 +7,8 @@ use bevy_ecs::{ all_tuples, entity::Entity, system::{ - lifetimeless::SRes, ReadOnlySystemParam, Resource, SystemParam, SystemParamItem, - SystemState, Infallible, + lifetimeless::SRes, Infallible, ReadOnlySystemParam, Resource, SystemParam, + SystemParamItem, SystemState, }, world::World, }; From 991968a6febe6217a215a02a8c041e7fa430ff0b Mon Sep 17 00:00:00 2001 From: Jonah Henriksson <33059163+JonahPlusPlus@users.noreply.github.com> Date: Sun, 11 Dec 2022 22:47:47 -0500 Subject: [PATCH 03/13] Added safety comments --- crates/bevy_ecs/src/system/system_param.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/crates/bevy_ecs/src/system/system_param.rs b/crates/bevy_ecs/src/system/system_param.rs index 3afa1af081c4d..5a188d45b3067 100644 --- a/crates/bevy_ecs/src/system/system_param.rs +++ b/crates/bevy_ecs/src/system/system_param.rs @@ -150,6 +150,7 @@ impl> SystemParam for T { type State = T::State; } +// SAFETY: `SystemParamState` must be safely implemented. unsafe impl> SystemParamState for T { fn init(world: &mut World, system_meta: &mut SystemMeta) -> Self { T::init(world, system_meta) @@ -187,6 +188,7 @@ impl> SystemParam for Option { #[doc(hidden)] pub struct OptionState>(T); +// SAFETY: `SystemParamState` must be safely implemented. unsafe impl> SystemParamState for OptionState { fn init(world: &mut World, system_meta: &mut SystemMeta) -> Self { Self(T::init(world, system_meta)) @@ -216,6 +218,7 @@ impl> SystemParam for T { type State = T::State; } +// SAFETY: `SystemParamState` must be safely implemented. unsafe impl> SystemParamState for T { fn init(world: &mut World, system_meta: &mut SystemMeta) -> Self { T::init(world, system_meta) @@ -248,6 +251,7 @@ impl> SystemParam for Result>(T); +// SAFETY: `SystemParamState` must be safely implemented. unsafe impl> SystemParamState for ResultState { fn init(world: &mut World, system_meta: &mut SystemMeta) -> Self { Self(T::init(world, system_meta)) From 2b0a2115acf94c8d3e13e7f6519b87f1440571c7 Mon Sep 17 00:00:00 2001 From: Jonah Henriksson <33059163+JonahPlusPlus@users.noreply.github.com> Date: Sun, 11 Dec 2022 22:50:56 -0500 Subject: [PATCH 04/13] Fix doc tests --- crates/bevy_ecs/src/system/function_system.rs | 6 +++--- crates/bevy_ecs/src/system/system_param.rs | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/crates/bevy_ecs/src/system/function_system.rs b/crates/bevy_ecs/src/system/function_system.rs index 2457be92beb1e..2c8602c31ba4a 100644 --- a/crates/bevy_ecs/src/system/function_system.rs +++ b/crates/bevy_ecs/src/system/function_system.rs @@ -505,7 +505,7 @@ impl Copy for SystemTypeIdLabel {} /// use std::num::ParseIntError; /// /// use bevy_ecs::prelude::*; -/// use bevy_ecs::system::{SystemParam, SystemParamItem}; +/// use bevy_ecs::system::{Infallible, SystemParam, SystemParamItem}; /// /// // Unfortunately, we need all of these generics. `A` is the first system, with its /// // parameters and marker type required for coherence. `B` is the second system, and @@ -519,8 +519,8 @@ impl Copy for SystemTypeIdLabel {} /// // We need A and B to be systems, add those bounds /// A: SystemParamFunction, /// B: SystemParamFunction, -/// AParam: SystemParam, -/// BParam: SystemParam, +/// AParam: SystemParam, +/// BParam: SystemParam, /// { /// // The type of `params` is inferred based on the return of this function above /// move |In(a_in), mut params| { diff --git a/crates/bevy_ecs/src/system/system_param.rs b/crates/bevy_ecs/src/system/system_param.rs index 5a188d45b3067..b99564fb7d570 100644 --- a/crates/bevy_ecs/src/system/system_param.rs +++ b/crates/bevy_ecs/src/system/system_param.rs @@ -1493,14 +1493,14 @@ pub mod lifetimeless { /// /// ``` /// # use bevy_ecs::prelude::*; -/// use bevy_ecs::system::{SystemParam, StaticSystemParam}; +/// use bevy_ecs::system::{Infallible, SystemParam, StaticSystemParam}; /// #[derive(SystemParam)] -/// struct GenericParam<'w,'s, T: SystemParam + 'static> { +/// struct GenericParam<'w,'s, T: SystemParam + 'static> { /// field: StaticSystemParam<'w, 's, T>, /// } -/// fn do_thing_generically(t: StaticSystemParam) {} +/// fn do_thing_generically + 'static>(t: StaticSystemParam) {} /// -/// fn check_always_is_system(){ +/// fn check_always_is_system + 'static>(){ /// bevy_ecs::system::assert_is_system(do_thing_generically::); /// } /// ``` From 391b9c490a145d3b20752beade25c25b009d0ddc Mon Sep 17 00:00:00 2001 From: Jonah Henriksson <33059163+JonahPlusPlus@users.noreply.github.com> Date: Mon, 12 Dec 2022 00:46:06 -0500 Subject: [PATCH 05/13] Added documentation --- crates/bevy_ecs/src/system/system_param.rs | 218 +++++++++++++++++++++ 1 file changed, 218 insertions(+) diff --git a/crates/bevy_ecs/src/system/system_param.rs b/crates/bevy_ecs/src/system/system_param.rs index b99564fb7d570..04df3cb466f00 100644 --- a/crates/bevy_ecs/src/system/system_param.rs +++ b/crates/bevy_ecs/src/system/system_param.rs @@ -24,8 +24,220 @@ use std::{ ops::{Deref, DerefMut}, }; +/// A [`SystemParam`] that can be used as `T`. +/// +/// `SystemParam` is best implemented for parameters that can't fail. +/// +/// # Example +/// +/// ``` +/// use bevy::{ +/// ecs::system::{ +/// Infallible, ResState, SystemMeta, +/// SystemParam, SystemParamState, +/// }, +/// prelude::*, +/// }; +/// # #[derive(Resource)] +/// # struct SomeResource { +/// # value: u32, +/// # } +/// +/// #[derive(Debug)] +/// struct MyParam<'w> { +/// value: &'w u32, +/// } +/// +/// impl<'w> SystemParam for MyParam<'w> { +/// type State = MyParamState; +/// } +/// +/// struct MyParamState { +/// res_state: ResState, +/// } +/// +/// unsafe impl SystemParamState for MyParamState { +/// fn init(world: &mut World, system_meta: &mut bevy::ecs::system::SystemMeta) -> Self { +/// Self { +/// res_state: as SystemParamState>::init(world, system_meta), +/// } +/// } +/// +/// type Item<'w, 's> = MyParam<'w>; +/// +/// #[inline] +/// unsafe fn get_param<'w, 's>( +/// state: &'s mut Self, +/// system_meta: &SystemMeta, +/// world: &'w World, +/// change_tick: u32, +/// ) -> Self::Item<'w, 's> { +/// let some_res = < as SystemParam>::State as SystemParamState>::get_param( +/// &mut state.res_state, +/// system_meta, +/// world, +/// change_tick, +/// ); +/// let r = some_res.into_inner(); +/// MyParam { +/// value: &r.value, +/// } +/// } +/// } +/// ``` pub struct Infallible; + +/// A [`SystemParam`] that can be used as `Option` and `T`. +/// +/// `SystemParam` is best implemented for parameters that have a single point of failure. +/// +/// By implementing `SystemParam`, `SystemParam` is implemented for `Option` and `T`. +/// +/// # Example +/// +/// ``` +/// use bevy::{ +/// ecs::system::{ +/// Infallible, Optional, OptionState, ResState, SystemMeta, +/// SystemParam, SystemParamState, +/// }, +/// prelude::*, +/// }; +/// # #[derive(Resource)] +/// # struct SomeResource { +/// # value: u32, +/// # } +/// +/// #[derive(Debug)] +/// struct MyParam<'w> { +/// value: &'w u32, +/// } +/// +/// impl<'w> SystemParam for MyParam<'w> { +/// type State = MyParamState; +/// } +/// +/// struct MyParamState { +/// res_state: OptionState>, +/// } +/// +/// unsafe impl SystemParamState for MyParamState { +/// fn init(world: &mut World, system_meta: &mut bevy::ecs::system::SystemMeta) -> Self { +/// Self { +/// res_state: OptionState::init(world, system_meta), +/// } +/// } +/// +/// type Item<'w, 's> = MyParam<'w>; +/// +/// #[inline] +/// unsafe fn get_param<'w, 's>( +/// state: &'s mut Self, +/// system_meta: &SystemMeta, +/// world: &'w World, +/// change_tick: u32, +/// ) -> Option> { +/// let some_res = <> as SystemParam>::State as SystemParamState>::get_param( +/// &mut state.res_state, +/// system_meta, +/// world, +/// change_tick, +/// ); +/// some_res.map(|r| { +/// let r = r.into_inner(); +/// MyParam { +/// value: &r.value, +/// } +/// }) +/// } +/// } +/// ``` pub struct Optional; + +/// A [`SystemParam`] that can be used as `Result>`, `Option`, and `T`. +/// +/// `SystemParam` is best implemented for parameters that have multiple points of failure. +/// +/// By implementing `SystemParam`, `SystemParam` is implemented for `T` and `SystemParam` is implemented for `Result>`. +/// +/// # Example +/// +/// ``` +/// use bevy::{ +/// ecs::system::{ +/// Infallible, Resultful, OptionState, ResState, SystemMeta, +/// SystemParam, SystemParamState, +/// }, +/// prelude::*, +/// }; +/// use std::error::Error; +/// # #[derive(Resource)] +/// # struct SomeResource { +/// # value: u32, +/// # } +/// +/// #[derive(Debug)] +/// struct MyParam<'w> { +/// value: &'w u32, +/// } +/// +/// impl<'w> SystemParam for MyParam<'w> { +/// type State = MyParamState; +/// } +/// +/// struct MyParamState { +/// res_state: OptionState>, +/// } +/// +/// unsafe impl SystemParamState for MyParamState { +/// fn init(world: &mut World, system_meta: &mut bevy::ecs::system::SystemMeta) -> Self { +/// Self { +/// res_state: OptionState::init(world, system_meta), +/// } +/// } +/// +/// type Item<'w, 's> = MyParam<'w>; +/// +/// #[inline] +/// unsafe fn get_param<'w, 's>( +/// state: &'s mut Self, +/// system_meta: &SystemMeta, +/// world: &'w World, +/// change_tick: u32, +/// ) -> Result, Box> { +/// // Since `Res` doesn't implement `SystemParam`, we have to use `Option>` and map it to a result. +/// let some_res = <> as SystemParam>::State as SystemParamState>::get_param( +/// &mut state.res_state, +/// system_meta, +/// world, +/// change_tick, +/// ); +/// +/// match some_res { +/// Some(r) => { +/// let r = r.into_inner(); +/// Ok(MyParam { +/// value: &r.value, +/// }) +/// } +/// None => { +/// Err(Box::new(MyError(45))) +/// } +/// } +/// } +/// } +/// +/// #[derive(Debug)] +/// struct MyError(u32); +/// +/// impl std::fmt::Display for MyError { +/// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +/// write!(f, "MyError has {}!", self.0) +/// } +/// } +/// +/// impl Error for MyError {} +/// ``` pub struct Resultful; mod sealed { @@ -46,6 +258,12 @@ mod sealed { /// A parameter that can be used in a [`System`](super::System). /// +/// This type comes in three variants: +/// +/// - [`Infallible`]: The base case, which can be used as `T`. +/// - [`Optional`]: Which can be used as `Option` or `T`. +/// - [`Resultful`]: Which can be used as `Result>`, `Option`, or `T`. +/// /// # Derive /// /// This trait can be derived with the [`derive@super::SystemParam`] macro. From 847a75e695d66788c7e668c7edc26648b2daae7d Mon Sep 17 00:00:00 2001 From: Jonah Henriksson <33059163+JonahPlusPlus@users.noreply.github.com> Date: Mon, 12 Dec 2022 01:03:55 -0500 Subject: [PATCH 06/13] `cargo clippy` --- crates/bevy_ecs/src/system/system_param.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_ecs/src/system/system_param.rs b/crates/bevy_ecs/src/system/system_param.rs index 04df3cb466f00..9fdeab3b24b75 100644 --- a/crates/bevy_ecs/src/system/system_param.rs +++ b/crates/bevy_ecs/src/system/system_param.rs @@ -63,7 +63,7 @@ use std::{ /// } /// } /// -/// type Item<'w, 's> = MyParam<'w>; +/// type Item<'w, 's> = MyParam<'w>; /// /// #[inline] /// unsafe fn get_param<'w, 's>( From 986e60c1d0484513a4bf5f881f8c59cbc7e79543 Mon Sep 17 00:00:00 2001 From: Jonah Henriksson <33059163+JonahPlusPlus@users.noreply.github.com> Date: Mon, 12 Dec 2022 01:33:22 -0500 Subject: [PATCH 07/13] Fixed doc tests --- crates/bevy_ecs/src/system/system_param.rs | 36 +++++++++------------- 1 file changed, 15 insertions(+), 21 deletions(-) diff --git a/crates/bevy_ecs/src/system/system_param.rs b/crates/bevy_ecs/src/system/system_param.rs index 9fdeab3b24b75..189485c7168cb 100644 --- a/crates/bevy_ecs/src/system/system_param.rs +++ b/crates/bevy_ecs/src/system/system_param.rs @@ -31,12 +31,10 @@ use std::{ /// # Example /// /// ``` -/// use bevy::{ -/// ecs::system::{ -/// Infallible, ResState, SystemMeta, -/// SystemParam, SystemParamState, -/// }, -/// prelude::*, +/// # use bevy_ecs::prelude::*; +/// use bevy_ecs::system::{ +/// Infallible, ResState, SystemMeta, +/// SystemParam, SystemParamState, /// }; /// # #[derive(Resource)] /// # struct SomeResource { @@ -57,7 +55,7 @@ use std::{ /// } /// /// unsafe impl SystemParamState for MyParamState { -/// fn init(world: &mut World, system_meta: &mut bevy::ecs::system::SystemMeta) -> Self { +/// fn init(world: &mut World, system_meta: &mut SystemMeta) -> Self { /// Self { /// res_state: as SystemParamState>::init(world, system_meta), /// } @@ -96,12 +94,10 @@ pub struct Infallible; /// # Example /// /// ``` -/// use bevy::{ -/// ecs::system::{ -/// Infallible, Optional, OptionState, ResState, SystemMeta, -/// SystemParam, SystemParamState, -/// }, -/// prelude::*, +/// # use bevy_ecs::prelude::*; +/// use bevy_ecs::system::{ +/// Infallible, Optional, OptionState, ResState, SystemMeta, +/// SystemParam, SystemParamState, /// }; /// # #[derive(Resource)] /// # struct SomeResource { @@ -122,7 +118,7 @@ pub struct Infallible; /// } /// /// unsafe impl SystemParamState for MyParamState { -/// fn init(world: &mut World, system_meta: &mut bevy::ecs::system::SystemMeta) -> Self { +/// fn init(world: &mut World, system_meta: &mut SystemMeta) -> Self { /// Self { /// res_state: OptionState::init(world, system_meta), /// } @@ -163,12 +159,10 @@ pub struct Optional; /// # Example /// /// ``` -/// use bevy::{ -/// ecs::system::{ -/// Infallible, Resultful, OptionState, ResState, SystemMeta, -/// SystemParam, SystemParamState, -/// }, -/// prelude::*, +/// # use bevy_ecs::prelude::*; +/// use bevy_ecs::system::{ +/// Infallible, Resultful, OptionState, ResState, SystemMeta, +/// SystemParam, SystemParamState, /// }; /// use std::error::Error; /// # #[derive(Resource)] @@ -190,7 +184,7 @@ pub struct Optional; /// } /// /// unsafe impl SystemParamState for MyParamState { -/// fn init(world: &mut World, system_meta: &mut bevy::ecs::system::SystemMeta) -> Self { +/// fn init(world: &mut World, system_meta: &mut SystemMeta) -> Self { /// Self { /// res_state: OptionState::init(world, system_meta), /// } From 86843bdd8d377294196ee5a77eecd5a821567ee3 Mon Sep 17 00:00:00 2001 From: Jonah Henriksson <33059163+JonahPlusPlus@users.noreply.github.com> Date: Mon, 12 Dec 2022 17:28:06 -0500 Subject: [PATCH 08/13] Change `Box` to `&dyn Error` --- crates/bevy_ecs/src/system/system_param.rs | 24 +++++++++++----------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/crates/bevy_ecs/src/system/system_param.rs b/crates/bevy_ecs/src/system/system_param.rs index 189485c7168cb..b19774fc7bc88 100644 --- a/crates/bevy_ecs/src/system/system_param.rs +++ b/crates/bevy_ecs/src/system/system_param.rs @@ -150,11 +150,11 @@ pub struct Infallible; /// ``` pub struct Optional; -/// A [`SystemParam`] that can be used as `Result>`, `Option`, and `T`. +/// A [`SystemParam`] that can be used as `Result`, `Option`, and `T`. /// /// `SystemParam` is best implemented for parameters that have multiple points of failure. /// -/// By implementing `SystemParam`, `SystemParam` is implemented for `T` and `SystemParam` is implemented for `Result>`. +/// By implementing `SystemParam`, `SystemParam` is implemented for `T` and `SystemParam` is implemented for `Result`. /// /// # Example /// @@ -198,7 +198,7 @@ pub struct Optional; /// system_meta: &SystemMeta, /// world: &'w World, /// change_tick: u32, -/// ) -> Result, Box> { +/// ) -> Result, &'s dyn Error> { /// // Since `Res` doesn't implement `SystemParam`, we have to use `Option>` and map it to a result. /// let some_res = <> as SystemParam>::State as SystemParamState>::get_param( /// &mut state.res_state, @@ -215,7 +215,7 @@ pub struct Optional; /// }) /// } /// None => { -/// Err(Box::new(MyError(45))) +/// Err(&MyError(45)) /// } /// } /// } @@ -237,16 +237,16 @@ pub struct Resultful; mod sealed { use std::error::Error; pub trait Fallibility { - type Scope; + type Scope<'s, T>; } impl Fallibility for super::Infallible { - type Scope = T; + type Scope<'s, T> = T; } impl Fallibility for super::Optional { - type Scope = Option; + type Scope<'s, T> = Option; } impl Fallibility for super::Resultful { - type Scope = Result>; + type Scope<'s, T> = Result; } } @@ -256,7 +256,7 @@ mod sealed { /// /// - [`Infallible`]: The base case, which can be used as `T`. /// - [`Optional`]: Which can be used as `Option` or `T`. -/// - [`Resultful`]: Which can be used as `Result>`, `Option`, or `T`. +/// - [`Resultful`]: Which can be used as `Result`, `Option`, or `T`. /// /// # Derive /// @@ -456,7 +456,7 @@ unsafe impl> SystemParamState for T { } } -impl> SystemParam for Result> { +impl<'s, T: SystemParam> SystemParam for Result { type State = ResultState; } @@ -477,7 +477,7 @@ unsafe impl> SystemParamState for Res self.0.apply(system_meta, world); } - type Item<'world, 'state> = Result, Box>; + type Item<'world, 'state> = Result, &'state dyn Error>; unsafe fn get_param<'world, 'state>( state: &'state mut Self, @@ -518,7 +518,7 @@ pub unsafe trait SystemParamState: Send + Sync + 'static system_meta: &SystemMeta, world: &'world World, change_tick: u32, - ) -> T::Scope>; + ) -> T::Scope<'state, Self::Item<'world, 'state>>; } /// A [`SystemParam`] that only reads a given [`World`]. From 943099fadbc10896694cdb8d53f910a83d750612 Mon Sep 17 00:00:00 2001 From: Jonah Henriksson <33059163+JonahPlusPlus@users.noreply.github.com> Date: Tue, 13 Dec 2022 00:37:36 -0500 Subject: [PATCH 09/13] Fix typo Co-authored-by: Alice Cecile --- crates/bevy_ecs/src/system/system_param.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_ecs/src/system/system_param.rs b/crates/bevy_ecs/src/system/system_param.rs index b19774fc7bc88..7c584574800bf 100644 --- a/crates/bevy_ecs/src/system/system_param.rs +++ b/crates/bevy_ecs/src/system/system_param.rs @@ -759,7 +759,7 @@ impl<'a, T: Resource> SystemParam for Res<'a, T> { type State = ResState; } -// SAFETY: Res ComponentI d and ArchetypeComponentId access is applied to SystemMeta. If this Res +// SAFETY: Res ComponentId and ArchetypeComponentId access is applied to SystemMeta. If this Res // conflicts with any prior access, a panic will occur. unsafe impl SystemParamState for ResState { type Item<'w, 's> = Res<'w, T>; From f8db43a14a69724efba7e10e73b3ac7fe008c051 Mon Sep 17 00:00:00 2001 From: Jonah Henriksson <33059163+JonahPlusPlus@users.noreply.github.com> Date: Tue, 13 Dec 2022 01:21:56 -0500 Subject: [PATCH 10/13] Changed `Scope` to `Fallible` and added docs --- crates/bevy_ecs/src/system/system_param.rs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/crates/bevy_ecs/src/system/system_param.rs b/crates/bevy_ecs/src/system/system_param.rs index 7c584574800bf..a544910a0e281 100644 --- a/crates/bevy_ecs/src/system/system_param.rs +++ b/crates/bevy_ecs/src/system/system_param.rs @@ -236,17 +236,20 @@ pub struct Resultful; mod sealed { use std::error::Error; + + /// Describes a [`SystemParam`](super::SystemParam)'s level of fallibility. pub trait Fallibility { - type Scope<'s, T>; + /// Maps `T` to some form of error-handling. + type Fallible<'s, T>; } impl Fallibility for super::Infallible { - type Scope<'s, T> = T; + type Fallible<'s, T> = T; } impl Fallibility for super::Optional { - type Scope<'s, T> = Option; + type Fallible<'s, T> = Option; } impl Fallibility for super::Resultful { - type Scope<'s, T> = Result; + type Fallible<'s, T> = Result; } } @@ -518,7 +521,7 @@ pub unsafe trait SystemParamState: Send + Sync + 'static system_meta: &SystemMeta, world: &'world World, change_tick: u32, - ) -> T::Scope<'state, Self::Item<'world, 'state>>; + ) -> T::Fallible<'state, Self::Item<'world, 'state>>; } /// A [`SystemParam`] that only reads a given [`World`]. From e65f935cddfcedb76e97c50f9aeb75c7a85f24a2 Mon Sep 17 00:00:00 2001 From: Jonah Henriksson <33059163+JonahPlusPlus@users.noreply.github.com> Date: Tue, 13 Dec 2022 13:28:48 -0500 Subject: [PATCH 11/13] Added macro attribute and documentation --- crates/bevy_ecs/macros/src/lib.rs | 121 +++++++++++++++++++-- crates/bevy_ecs/src/system/system_param.rs | 16 ++- 2 files changed, 125 insertions(+), 12 deletions(-) diff --git a/crates/bevy_ecs/macros/src/lib.rs b/crates/bevy_ecs/macros/src/lib.rs index a01d599a10542..b765f1a9de476 100644 --- a/crates/bevy_ecs/macros/src/lib.rs +++ b/crates/bevy_ecs/macros/src/lib.rs @@ -322,9 +322,17 @@ struct SystemParamFieldAttributes { } static SYSTEM_PARAM_ATTRIBUTE_NAME: &str = "system_param"; +static FALLIBILITY_ATTRIBUTE_NAME: &str = "fallibility"; + +#[derive(PartialEq)] +enum Fallibility { + Infallible, + Optional, + Resultful, +} /// Implement `SystemParam` to use a struct as a parameter in a system -#[proc_macro_derive(SystemParam, attributes(system_param))] +#[proc_macro_derive(SystemParam, attributes(fallibility, system_param))] pub fn derive_system_param(input: TokenStream) -> TokenStream { let ast = parse_macro_input!(input as DeriveInput); let fields = match get_named_struct_fields(&ast.data) { @@ -333,6 +341,66 @@ pub fn derive_system_param(input: TokenStream) -> TokenStream { }; let path = bevy_ecs_path(); + let mut fallibility = None; + + // Read struct-level attributes + for attr in &ast.attrs { + let Some(attr_ident) = attr.path.get_ident() else { continue }; + if attr_ident == FALLIBILITY_ATTRIBUTE_NAME { + if fallibility == None { + if let Ok(fallible_ident) = + attr.parse_args_with(|input: ParseStream| input.parse::()) + { + match fallible_ident.to_string().as_str() { + "Infallible" => { + fallibility = Some(Fallibility::Infallible); + } + "Optional" => { + fallibility = Some(Fallibility::Optional); + } + "Resultful" => { + fallibility = Some(Fallibility::Resultful); + }, + _ => return syn::Error::new_spanned( + attr, + "The content of `fallibility` should be either `Infallible`, `Optional`, or `Resultful`." + ) + .into_compile_error() + .into() + } + } else { + return syn::Error::new_spanned( + attr, + "The content of `fallibility` should be a single identifier." + ) + .into_compile_error() + .into(); + } + } else { + return syn::Error::new_spanned( + attr, + "There should only be one `fallibility` attribute." + ) + .into_compile_error() + .into(); + } + } + } + + let fallibility = fallibility.unwrap_or(Fallibility::Infallible); + + let fallible = { + let mut f = path.clone(); + f.segments.push(format_ident!("system").into()); + match fallibility { + Fallibility::Infallible => f.segments.push(format_ident!("Infallible").into()), + Fallibility::Optional => f.segments.push(format_ident!("Optional").into()), + Fallibility::Resultful => f.segments.push(format_ident!("Resultful").into()), + } + f + }; + + // Read field-level attributes let field_attributes = fields .iter() .map(|field| { @@ -429,18 +497,54 @@ pub fn derive_system_param(input: TokenStream) -> TokenStream { let struct_name = &ast.ident; let state_struct_visibility = &ast.vis; + let returned_struct = quote! { + #struct_name { + #(#fields,)* + #(#ignored_fields: <#ignored_field_types>::default(),)* + } + }; + + let (get_param_body, get_param_return) = match fallibility { + Fallibility::Infallible => ( + quote! { + #(let #fields = <<#field_types as #path::system::SystemParam<#fallible>>::State as #path::system::SystemParamState<#fallible>>::get_param(&mut state.state.#field_indices, system_meta, world, change_tick);)* + + #returned_struct + }, + quote! { Self::Item<'w, 's> } + ), + Fallibility::Optional => ( + quote! { + #(let Some(#fields) = <<#field_types as #path::system::SystemParam<#fallible>>::State as #path::system::SystemParamState<#fallible>>::get_param(&mut state.state.#field_indices, system_meta, world, change_tick) else { + return None; + };)* + + Some(#returned_struct) + }, + quote! { Option> } + ), + Fallibility::Resultful => ( + quote! { + #(let #fields = <<#field_types as #path::system::SystemParam<#fallible>>::State as #path::system::SystemParamState<#fallible>>::get_param(&mut state.state.#field_indices, system_meta, world, change_tick)?;)* + + Ok(#returned_struct) + }, + quote! { Result, &'s dyn std::error::Error> } + ) + }; + TokenStream::from(quote! { // We define the FetchState struct in an anonymous scope to avoid polluting the user namespace. // The struct can still be accessed via SystemParam::State, e.g. EventReaderState can be accessed via // as SystemParam>::State const _: () = { - impl<'w, 's, #punctuated_generics> #path::system::SystemParam<#path::system::Infallible> for #struct_name #ty_generics #where_clause { + impl<'w, 's, #punctuated_generics> #path::system::SystemParam<#fallible> for #struct_name #ty_generics #where_clause { type State = State<'w, 's, #punctuated_generic_idents>; } #[doc(hidden)] type State<'w, 's, #punctuated_generic_idents> = FetchState< - (#(<#field_types as #path::system::SystemParam<#path::system::Infallible>>::State,)*), + (#(<#field_types as #path::system::SystemParam<#fallible>>::State,)*), #punctuated_generic_idents >; @@ -450,7 +554,7 @@ pub fn derive_system_param(input: TokenStream) -> TokenStream { marker: std::marker::PhantomData(#punctuated_generic_idents)> } - unsafe impl<'__w, '__s, #punctuated_generics> #path::system::SystemParamState<#path::system::Infallible> for + unsafe impl<'__w, '__s, #punctuated_generics> #path::system::SystemParamState<#fallible> for State<'__w, '__s, #punctuated_generic_idents> #where_clause { type Item<'w, 's> = #struct_name #ty_generics; @@ -467,7 +571,7 @@ pub fn derive_system_param(input: TokenStream) -> TokenStream { } fn apply(&mut self, system_meta: &#path::system::SystemMeta, world: &mut #path::world::World) { - self.state.apply(system_meta, world) + #path::system::SystemParamState::apply(&mut self.state, system_meta, world) } unsafe fn get_param<'w, 's>( @@ -475,11 +579,8 @@ pub fn derive_system_param(input: TokenStream) -> TokenStream { system_meta: &#path::system::SystemMeta, world: &'w #path::world::World, change_tick: u32, - ) -> Self::Item<'w, 's> { - #struct_name { - #(#fields: <<#field_types as #path::system::SystemParam<#path::system::Infallible>>::State as #path::system::SystemParamState<#path::system::Infallible>>::get_param(&mut state.state.#field_indices, system_meta, world, change_tick),)* - #(#ignored_fields: <#ignored_field_types>::default(),)* - } + ) -> #get_param_return { + #get_param_body } } diff --git a/crates/bevy_ecs/src/system/system_param.rs b/crates/bevy_ecs/src/system/system_param.rs index a544910a0e281..42c60017cedca 100644 --- a/crates/bevy_ecs/src/system/system_param.rs +++ b/crates/bevy_ecs/src/system/system_param.rs @@ -255,9 +255,9 @@ mod sealed { /// A parameter that can be used in a [`System`](super::System). /// -/// This type comes in three variants: +/// This trait comes in three variants, where [`Infallible`] is the base variant: /// -/// - [`Infallible`]: The base case, which can be used as `T`. +/// - [`Infallible`]: Which can be used as `T`. /// - [`Optional`]: Which can be used as `Option` or `T`. /// - [`Resultful`]: Which can be used as `Result`, `Option`, or `T`. /// @@ -274,6 +274,11 @@ mod sealed { /// /// ## Attributes /// +/// `#[fallibility(T)]`: +/// Can be added to the struct. `T` can be one of [`Infallible`], [`Optional`], or [`Resultful`]. +/// This requires the fields to have the same fallibility. +/// If the attribute is not specified, the fallibility defaults to [`Infallible`]. +/// /// `#[system_param(ignore)]`: /// Can be added to any field in the struct. Fields decorated with this attribute /// will be created with the default value upon realisation. @@ -289,6 +294,7 @@ mod sealed { /// use bevy_ecs::system::SystemParam; /// /// #[derive(SystemParam)] +/// #[fallibility(Optional)] /// struct MyParam<'w, Marker: 'static> { /// foo: Res<'w, SomeResource>, /// #[system_param(ignore)] @@ -1848,4 +1854,10 @@ mod tests { #[derive(SystemParam)] pub struct UnitParam {} + + #[derive(SystemParam)] + #[fallibility(Optional)] + pub struct OptionalParam<'w, T: Resource> { + _res: Res<'w, T>, + } } From dbab791122e94832407f61291fb23d28fec1a433 Mon Sep 17 00:00:00 2001 From: Jonah Henriksson <33059163+JonahPlusPlus@users.noreply.github.com> Date: Tue, 13 Dec 2022 13:32:47 -0500 Subject: [PATCH 12/13] `cargo fmt` --- crates/bevy_ecs/macros/src/lib.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/crates/bevy_ecs/macros/src/lib.rs b/crates/bevy_ecs/macros/src/lib.rs index b765f1a9de476..392c1e0b61e99 100644 --- a/crates/bevy_ecs/macros/src/lib.rs +++ b/crates/bevy_ecs/macros/src/lib.rs @@ -371,7 +371,7 @@ pub fn derive_system_param(input: TokenStream) -> TokenStream { } else { return syn::Error::new_spanned( attr, - "The content of `fallibility` should be a single identifier." + "The content of `fallibility` should be a single identifier.", ) .into_compile_error() .into(); @@ -379,7 +379,7 @@ pub fn derive_system_param(input: TokenStream) -> TokenStream { } else { return syn::Error::new_spanned( attr, - "There should only be one `fallibility` attribute." + "There should only be one `fallibility` attribute.", ) .into_compile_error() .into(); @@ -400,7 +400,7 @@ pub fn derive_system_param(input: TokenStream) -> TokenStream { f }; - // Read field-level attributes + // Read field-level attributes let field_attributes = fields .iter() .map(|field| { @@ -511,7 +511,7 @@ pub fn derive_system_param(input: TokenStream) -> TokenStream { #returned_struct }, - quote! { Self::Item<'w, 's> } + quote! { Self::Item<'w, 's> }, ), Fallibility::Optional => ( quote! { @@ -521,16 +521,16 @@ pub fn derive_system_param(input: TokenStream) -> TokenStream { Some(#returned_struct) }, - quote! { Option> } - ), + quote! { Option> }, + ), Fallibility::Resultful => ( quote! { #(let #fields = <<#field_types as #path::system::SystemParam<#fallible>>::State as #path::system::SystemParamState<#fallible>>::get_param(&mut state.state.#field_indices, system_meta, world, change_tick)?;)* Ok(#returned_struct) }, - quote! { Result, &'s dyn std::error::Error> } - ) + quote! { Result, &'s dyn std::error::Error> }, + ), }; TokenStream::from(quote! { From 5c14c1dbff937644b2b886b8ed9f0585341adaab Mon Sep 17 00:00:00 2001 From: Jonah Henriksson <33059163+JonahPlusPlus@users.noreply.github.com> Date: Tue, 13 Dec 2022 13:38:03 -0500 Subject: [PATCH 13/13] Fixed clippy issue --- crates/bevy_ecs/macros/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_ecs/macros/src/lib.rs b/crates/bevy_ecs/macros/src/lib.rs index 392c1e0b61e99..4166e00de037c 100644 --- a/crates/bevy_ecs/macros/src/lib.rs +++ b/crates/bevy_ecs/macros/src/lib.rs @@ -347,7 +347,7 @@ pub fn derive_system_param(input: TokenStream) -> TokenStream { for attr in &ast.attrs { let Some(attr_ident) = attr.path.get_ident() else { continue }; if attr_ident == FALLIBILITY_ATTRIBUTE_NAME { - if fallibility == None { + if fallibility.is_none() { if let Ok(fallible_ident) = attr.parse_args_with(|input: ParseStream| input.parse::()) {