Path: blob/main/crates/bevy_ecs/src/query/access_iter.rs
9334 views
use core::fmt::Display;12use crate::{3component::{ComponentId, Components},4query::{Access, QueryData},5};6use bevy_utils::BloomFilter;78// found by benchmarking9// too low, and smaller queries do unnecessary work10// maintaining the bloom filter for a handful of checks11// too high, and the benefit of a simpler loop12// is outweighed by the n^2 check13const USE_FILTER_THRESHOLD: usize = 4;1415/// Check if `Q` has any internal conflicts.16#[inline(never)]17pub fn has_conflicts<Q: QueryData>(components: &Components) -> Result<(), QueryAccessError> {18let Some(state) = Q::get_state(components) else {19return Err(QueryAccessError::ComponentNotRegistered);20};2122let result = if let Some(size) = Q::iter_access(&state).size_hint().123&& size <= USE_FILTER_THRESHOLD24{25has_conflicts_small::<Q>(&state)26} else {27has_conflicts_large::<Q>(&state)28};29if let Err(e) = result {30panic!("{e}");31}3233Ok(())34}3536/// Check if `Q` has any internal conflicts by checking all pairs of accesses.37///38/// This is intended for queries with fewer components than [`USE_FILTER_THRESHOLD`].39/// Split from [`has_conflicts`] for easier testing.40fn has_conflicts_small<'a, Q: QueryData>(41state: &'a Q::State,42) -> Result<(), AccessConflictError<'a>> {43// we can optimize small sizes by caching the iteration result in an array on the stack44let mut inner_access = [EcsAccessType::Empty; USE_FILTER_THRESHOLD];45for (i, access) in Q::iter_access(state).enumerate() {46for access_other in inner_access.iter().take(i) {47if access.is_compatible(*access_other).is_err() {48return Err(AccessConflictError(access, *access_other));49}50}51inner_access[i] = access;52}5354Ok(())55}5657/// Check if `Q` has any internal conflicts using a bloom filter for efficiency.58///59/// This is intended for queries with more components than [`USE_FILTER_THRESHOLD`].60/// Split from [`has_conflicts`] for easier testing.61fn has_conflicts_large<'a, Q: QueryData>(62state: &'a Q::State,63) -> Result<(), AccessConflictError<'a>> {64// use a bloom filter as a linear time check if we need to run the longer, exact check65let mut filter = BloomFilter::<8, 2>::new();66for (i, access) in Q::iter_access(state).enumerate() {67let needs_check = match access {68EcsAccessType::Component(EcsAccessLevel::Read(component_id))69| EcsAccessType::Component(EcsAccessLevel::Write(component_id)) => {70filter.check_insert(&component_id.index())71}72EcsAccessType::Component(EcsAccessLevel::ReadAll)73| EcsAccessType::Component(EcsAccessLevel::WriteAll) => true,74EcsAccessType::Resource(ResourceAccessLevel::Read(resource_id))75| EcsAccessType::Resource(ResourceAccessLevel::Write(resource_id)) => {76filter.check_insert(&resource_id.index())77}78EcsAccessType::Access(access) => {79if access.has_read_all_resources() || access.has_write_all_resources() {80true81} else if let Ok(component_iter) = access.try_iter_component_access() {82let mut needs_check = false;83for kind in component_iter {84let index = match kind {85crate::query::ComponentAccessKind::Shared(id)86| crate::query::ComponentAccessKind::Exclusive(id)87| crate::query::ComponentAccessKind::Archetypal(id) => id.index(),88};89if filter.check_insert(&index) {90needs_check = true;91}92}93for resource_id in access.resource_reads_and_writes() {94if filter.check_insert(&resource_id.index()) {95needs_check = true;96}97}98needs_check99} else {100true101}102}103EcsAccessType::Empty => continue,104};105if needs_check {106// we MIGHT have a conflict, fallback to slow check107for (j, access_other) in Q::iter_access(state).enumerate() {108if i == j {109continue;110}111if access.is_compatible(access_other).is_err() {112return Err(AccessConflictError(access, access_other));113}114}115}116}117Ok(())118}119120/// The data storage type that is being accessed.121#[derive(Copy, Clone, Debug, PartialEq, Hash)]122pub enum EcsAccessType<'a> {123/// Accesses [`Component`](crate::prelude::Component) data124Component(EcsAccessLevel),125/// Accesses [`Resource`](crate::prelude::Resource) data126Resource(ResourceAccessLevel),127/// borrowed access from [`WorldQuery::State`](crate::query::WorldQuery)128Access(&'a Access),129/// Does not access any data that can conflict.130Empty,131}132133impl<'a> EcsAccessType<'a> {134/// Returns `Ok(())` if `self` and `other` are compatible. Returns a [`AccessConflictError`] otherwise.135#[inline(never)]136pub fn is_compatible(&self, other: Self) -> Result<(), AccessConflictError<'_>> {137use EcsAccessLevel::*;138use EcsAccessType::*;139140match (*self, other) {141(Component(ReadAll), Component(Write(_)))142| (Component(Write(_)), Component(ReadAll))143| (Component(_), Component(WriteAll))144| (Component(WriteAll), Component(_)) => Err(AccessConflictError(*self, other)),145146(Empty, _)147| (_, Empty)148| (Component(_), Resource(_))149| (Resource(_), Component(_))150// read only access doesn't conflict151| (Component(Read(_)), Component(Read(_)))152| (Component(ReadAll), Component(Read(_)))153| (Component(Read(_)), Component(ReadAll))154| (Component(ReadAll), Component(ReadAll))155| (Resource(ResourceAccessLevel::Read(_)), Resource(ResourceAccessLevel::Read(_))) => {156Ok(())157}158159(Component(Read(id)), Component(Write(id_other)))160| (Component(Write(id)), Component(Read(id_other)))161| (Component(Write(id)), Component(Write(id_other)))162| (163Resource(ResourceAccessLevel::Read(id)),164Resource(ResourceAccessLevel::Write(id_other)),165)166| (167Resource(ResourceAccessLevel::Write(id)),168Resource(ResourceAccessLevel::Read(id_other)),169)170| (171Resource(ResourceAccessLevel::Write(id)),172Resource(ResourceAccessLevel::Write(id_other)),173) => if id == id_other {174Err(AccessConflictError(*self, other))175} else {176Ok(())177},178179// Borrowed Access180(Component(Read(component_id)), Access(access))181| (Access(access), Component(Read(component_id))) => if access.has_component_write(component_id) {182Err(AccessConflictError(*self, other))183} else {184Ok(())185},186187(Component(Write(component_id)), Access(access))188| (Access(access), Component(Write(component_id))) => if access.has_component_read(component_id) {189Err(AccessConflictError(*self, other))190} else {191Ok(())192},193194(Component(ReadAll), Access(access))195| (Access(access), Component(ReadAll)) => if access.has_any_component_write() {196Err(AccessConflictError(*self, other))197} else {198Ok(())199},200201(Component(WriteAll), Access(access))202| (Access(access), Component(WriteAll))=> if access.has_any_component_read() {203Err(AccessConflictError(*self, other))204} else {205Ok(())206},207208(Resource(ResourceAccessLevel::Read(component_id)), Access(access))209| (Access(access), Resource(ResourceAccessLevel::Read(component_id))) => if access.has_resource_write(component_id) {210Err(AccessConflictError(*self, other))211} else {212Ok(())213},214(Resource(ResourceAccessLevel::Write(component_id)), Access(access))215| (Access(access), Resource(ResourceAccessLevel::Write(component_id))) => if access.has_resource_read(component_id) {216Err(AccessConflictError(*self, other))217} else {218Ok(())219},220221(Access(access), Access(other_access)) => if access.is_compatible(other_access) {222Ok(())223} else {224Err(AccessConflictError(*self, other))225},226}227}228}229230/// The way the data will be accessed and whether we take access on all the components on231/// an entity or just one component.232#[derive(Clone, Copy, Debug, PartialEq, Hash)]233pub enum EcsAccessLevel {234/// Reads [`Component`](crate::prelude::Component) with [`ComponentId`]235Read(ComponentId),236/// Writes [`Component`](crate::prelude::Component) with [`ComponentId`]237Write(ComponentId),238/// Potentially reads all [`Component`](crate::prelude::Component)'s in the [`World`](crate::prelude::World)239ReadAll,240/// Potentially writes all [`Component`](crate::prelude::Component)'s in the [`World`](crate::prelude::World)241WriteAll,242}243244/// Access level needed by [`QueryData`] fetch to the resource.245#[derive(Copy, Clone, Debug, PartialEq, Hash)]246pub enum ResourceAccessLevel {247/// Reads the resource with [`ComponentId`]248Read(ComponentId),249/// Writes the resource with [`ComponentId`]250Write(ComponentId),251}252253/// Error returned from [`EcsAccessType::is_compatible`]254pub struct AccessConflictError<'a>(EcsAccessType<'a>, EcsAccessType<'a>);255256impl Display for AccessConflictError<'_> {257fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {258use EcsAccessLevel::*;259use EcsAccessType::*;260261let AccessConflictError(a, b) = self;262match (a, b) {263// ReadAll/WriteAll + Component conflicts264(Component(ReadAll), Component(Write(id)))265| (Component(Write(id)), Component(ReadAll)) => {266write!(267f,268"Component read all access conflicts with component {id:?} write."269)270}271(Component(WriteAll), Component(Write(id)))272| (Component(Write(id)), Component(WriteAll)) => {273write!(274f,275"Component write all access conflicts with component {id:?} write."276)277}278(Component(WriteAll), Component(Read(id)))279| (Component(Read(id)), Component(WriteAll)) => {280write!(281f,282"Component write all access conflicts with component {id:?} read."283)284}285(Component(WriteAll), Component(ReadAll))286| (Component(ReadAll), Component(WriteAll)) => {287write!(f, "Component write all conflicts with component read all.")288}289(Component(WriteAll), Component(WriteAll)) => {290write!(f, "Component write all conflicts with component write all.")291}292293// Component + Component conflicts294(Component(Read(id)), Component(Write(id_other)))295| (Component(Write(id_other)), Component(Read(id))) => write!(296f,297"Component {id:?} read conflicts with component {id_other:?} write."298),299(Component(Write(id)), Component(Write(id_other))) => write!(300f,301"Component {id:?} write conflicts with component {id_other:?} write."302),303304// Borrowed Access conflicts305(Access(_), Component(Read(id))) | (Component(Read(id)), Access(_)) => write!(306f,307"Access has a write that conflicts with component {id:?} read."308),309(Access(_), Component(Write(id))) | (Component(Write(id)), Access(_)) => write!(310f,311"Access has a read that conflicts with component {id:?} write."312),313(Access(_), Component(ReadAll)) | (Component(ReadAll), Access(_)) => write!(314f,315"Access has a write that conflicts with component read all"316),317(Access(_), Component(WriteAll)) | (Component(WriteAll), Access(_)) => write!(318f,319"Access has a read that conflicts with component write all"320),321(Access(_), Resource(ResourceAccessLevel::Read(id)))322| (Resource(ResourceAccessLevel::Read(id)), Access(_)) => write!(323f,324"Access has a write that conflicts with resource {id:?} read."325),326(Access(_), Resource(ResourceAccessLevel::Write(id)))327| (Resource(ResourceAccessLevel::Write(id)), Access(_)) => write!(328f,329"Access has a read that conflicts with resource {id:?} write."330),331(Access(_), Access(_)) => write!(f, "Access conflicts with other Access"),332333_ => {334unreachable!("Other accesses should be compatible");335}336}337}338}339340/// Error returned from [`has_conflicts`].341#[derive(Clone, Copy, Debug, PartialEq)]342pub enum QueryAccessError {343/// Component was not registered on world344ComponentNotRegistered,345/// Entity did not have the requested components346EntityDoesNotMatch,347}348349impl core::error::Error for QueryAccessError {}350351impl Display for QueryAccessError {352fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {353match *self {354QueryAccessError::ComponentNotRegistered => {355write!(356f,357"At least one component in Q was not registered in world.358Consider calling `World::register_component`"359)360}361QueryAccessError::EntityDoesNotMatch => {362write!(f, "Entity does not match Q")363}364}365}366}367368#[cfg(test)]369mod tests {370use super::*;371use crate::{372prelude::Component,373query::WorldQuery,374world::{EntityMut, EntityMutExcept, EntityRef, EntityRefExcept, World},375};376377#[derive(Component)]378struct C1;379380#[derive(Component)]381struct C2;382383fn setup_world() -> World {384let world = World::new();385let mut world = world;386world.register_component::<C1>();387world.register_component::<C2>();388world389}390391#[test]392fn simple_compatible() {393let world = setup_world();394let c = world.components();395396// Compatible397let state = <&mut C1 as WorldQuery>::get_state(c).unwrap();398assert!(has_conflicts_small::<&mut C1>(&state).is_ok());399assert!(has_conflicts_large::<&mut C1>(&state).is_ok());400assert!(has_conflicts::<&mut C1>(c).is_ok());401402let state = <&C1 as WorldQuery>::get_state(c).unwrap();403assert!(has_conflicts_small::<&C1>(&state).is_ok());404assert!(has_conflicts_large::<&C1>(&state).is_ok());405assert!(has_conflicts::<&C1>(c).is_ok());406407let state = <(&C1, &C1) as WorldQuery>::get_state(c).unwrap();408assert!(has_conflicts_small::<(&C1, &C1)>(&state).is_ok());409assert!(has_conflicts_large::<(&C1, &C1)>(&state).is_ok());410assert!(has_conflicts::<(&C1, &C1)>(c).is_ok());411}412413#[test]414#[should_panic(expected = "conflicts")]415fn conflict_component_read_conflicts_write() {416let world = setup_world();417let c = world.components();418let state = <(&C1, &mut C1) as WorldQuery>::get_state(c).unwrap();419assert!(has_conflicts_small::<(&C1, &mut C1)>(&state).is_err());420assert!(has_conflicts_large::<(&C1, &mut C1)>(&state).is_err());421let _ = has_conflicts::<(&C1, &mut C1)>(c);422}423424#[test]425#[should_panic(expected = "conflicts")]426fn conflict_component_write_conflicts_read() {427let world = setup_world();428let c = world.components();429let state = <(&mut C1, &C1) as WorldQuery>::get_state(c).unwrap();430assert!(has_conflicts_small::<(&mut C1, &C1)>(&state).is_err());431assert!(has_conflicts_large::<(&mut C1, &C1)>(&state).is_err());432let _ = has_conflicts::<(&mut C1, &C1)>(c);433}434435#[test]436#[should_panic(expected = "conflicts")]437fn conflict_component_write_conflicts_write() {438let world = setup_world();439let c = world.components();440let state = <(&mut C1, &mut C1) as WorldQuery>::get_state(c).unwrap();441assert!(has_conflicts_small::<(&mut C1, &mut C1)>(&state).is_err());442assert!(has_conflicts_large::<(&mut C1, &mut C1)>(&state).is_err());443let _ = has_conflicts::<(&mut C1, &mut C1)>(c);444}445446#[test]447fn entity_ref_compatible() {448let world = setup_world();449let c = world.components();450451// Compatible452let state = <(EntityRef, &C1) as WorldQuery>::get_state(c).unwrap();453assert!(has_conflicts_small::<(EntityRef, &C1)>(&state).is_ok());454assert!(has_conflicts_large::<(EntityRef, &C1)>(&state).is_ok());455assert!(has_conflicts::<(EntityRef, &C1)>(c).is_ok());456457let state = <(&C1, EntityRef) as WorldQuery>::get_state(c).unwrap();458assert!(has_conflicts_small::<(&C1, EntityRef)>(&state).is_ok());459assert!(has_conflicts_large::<(&C1, EntityRef)>(&state).is_ok());460assert!(has_conflicts::<(&C1, EntityRef)>(c).is_ok());461462let state = <(EntityRef, EntityRef) as WorldQuery>::get_state(c).unwrap();463assert!(has_conflicts_small::<(EntityRef, EntityRef)>(&state).is_ok());464assert!(has_conflicts_large::<(EntityRef, EntityRef)>(&state).is_ok());465assert!(has_conflicts::<(EntityRef, EntityRef)>(c).is_ok());466}467468#[test]469#[should_panic(expected = "conflicts")]470fn entity_ref_conflicts_component_write() {471let world = setup_world();472let c = world.components();473let state = <(EntityRef, &mut C1) as WorldQuery>::get_state(c).unwrap();474assert!(has_conflicts_small::<(EntityRef, &mut C1)>(&state).is_err());475assert!(has_conflicts_large::<(EntityRef, &mut C1)>(&state).is_err());476let _ = has_conflicts::<(EntityRef, &mut C1)>(c);477}478479#[test]480#[should_panic(expected = "conflicts")]481fn component_write_conflicts_entity_ref() {482let world = setup_world();483let c = world.components();484let state = <(&mut C1, EntityRef) as WorldQuery>::get_state(c).unwrap();485assert!(has_conflicts_small::<(&mut C1, EntityRef)>(&state).is_err());486assert!(has_conflicts_large::<(&mut C1, EntityRef)>(&state).is_err());487let _ = has_conflicts::<(&mut C1, EntityRef)>(c);488}489490#[test]491#[should_panic(expected = "conflicts")]492fn entity_mut_conflicts_component_read() {493let world = setup_world();494let c = world.components();495let state = <(EntityMut, &C1) as WorldQuery>::get_state(c).unwrap();496assert!(has_conflicts_small::<(EntityMut, &C1)>(&state).is_err());497assert!(has_conflicts_large::<(EntityMut, &C1)>(&state).is_err());498let _ = has_conflicts::<(EntityMut, &C1)>(c);499}500501#[test]502#[should_panic(expected = "conflicts")]503fn component_read_conflicts_entity_mut() {504let world = setup_world();505let c = world.components();506let state = <(&C1, EntityMut) as WorldQuery>::get_state(c).unwrap();507assert!(has_conflicts_small::<(&C1, EntityMut)>(&state).is_err());508assert!(has_conflicts_large::<(&C1, EntityMut)>(&state).is_err());509let _ = has_conflicts::<(&C1, EntityMut)>(c);510}511512#[test]513#[should_panic(expected = "conflicts")]514fn entity_mut_conflicts_component_write() {515let world = setup_world();516let c = world.components();517let state = <(EntityMut, &mut C1) as WorldQuery>::get_state(c).unwrap();518assert!(has_conflicts_small::<(EntityMut, &mut C1)>(&state).is_err());519assert!(has_conflicts_large::<(EntityMut, &mut C1)>(&state).is_err());520let _ = has_conflicts::<(EntityMut, &mut C1)>(c);521}522523#[test]524#[should_panic(expected = "conflicts")]525fn component_write_conflicts_entity_mut() {526let world = setup_world();527let c = world.components();528let state = <(&mut C1, EntityMut) as WorldQuery>::get_state(c).unwrap();529assert!(has_conflicts_small::<(&mut C1, EntityMut)>(&state).is_err());530assert!(has_conflicts_large::<(&mut C1, EntityMut)>(&state).is_err());531let _ = has_conflicts::<(&mut C1, EntityMut)>(c);532}533534#[test]535#[should_panic(expected = "conflicts")]536fn entity_mut_conflicts_entity_ref() {537let world = setup_world();538let c = world.components();539let state = <(EntityMut, EntityRef) as WorldQuery>::get_state(c).unwrap();540assert!(has_conflicts_small::<(EntityMut, EntityRef)>(&state).is_err());541assert!(has_conflicts_large::<(EntityMut, EntityRef)>(&state).is_err());542let _ = has_conflicts::<(EntityMut, EntityRef)>(c);543}544545#[test]546#[should_panic(expected = "conflicts")]547fn entity_ref_conflicts_entity_mut() {548let world = setup_world();549let c = world.components();550let state = <(EntityRef, EntityMut) as WorldQuery>::get_state(c).unwrap();551assert!(has_conflicts_small::<(EntityRef, EntityMut)>(&state).is_err());552assert!(has_conflicts_large::<(EntityRef, EntityMut)>(&state).is_err());553let _ = has_conflicts::<(EntityRef, EntityMut)>(c);554}555556#[test]557fn entity_ref_except_compatible() {558let world = setup_world();559let c = world.components();560561// Compatible562let state = <(EntityRefExcept<C1>, &mut C1) as WorldQuery>::get_state(c).unwrap();563assert!(has_conflicts_small::<(EntityRefExcept<C1>, &mut C1)>(&state).is_ok());564assert!(has_conflicts_large::<(EntityRefExcept<C1>, &mut C1)>(&state).is_ok());565assert!(has_conflicts::<(EntityRefExcept<C1>, &mut C1)>(c).is_ok());566567let state = <(&mut C1, EntityRefExcept<C1>) as WorldQuery>::get_state(c).unwrap();568assert!(has_conflicts_small::<(&mut C1, EntityRefExcept<C1>)>(&state).is_ok());569assert!(has_conflicts_large::<(&mut C1, EntityRefExcept<C1>)>(&state).is_ok());570assert!(has_conflicts::<(&mut C1, EntityRefExcept<C1>)>(c).is_ok());571572let state = <(&C2, EntityRefExcept<C1>) as WorldQuery>::get_state(c).unwrap();573assert!(has_conflicts_small::<(&C2, EntityRefExcept<C1>)>(&state).is_ok());574assert!(has_conflicts_large::<(&C2, EntityRefExcept<C1>)>(&state).is_ok());575assert!(has_conflicts::<(&C2, EntityRefExcept<C1>)>(c).is_ok());576577let state = <(&mut C1, EntityRefExcept<(C1, C2)>) as WorldQuery>::get_state(c).unwrap();578assert!(has_conflicts_small::<(&mut C1, EntityRefExcept<(C1, C2)>)>(&state).is_ok());579assert!(has_conflicts_large::<(&mut C1, EntityRefExcept<(C1, C2)>)>(&state).is_ok());580assert!(has_conflicts::<(&mut C1, EntityRefExcept<(C1, C2)>)>(c).is_ok());581582let state = <(EntityRefExcept<(C1, C2)>, &mut C1) as WorldQuery>::get_state(c).unwrap();583assert!(has_conflicts_small::<(EntityRefExcept<(C1, C2)>, &mut C1)>(&state).is_ok());584assert!(has_conflicts_large::<(EntityRefExcept<(C1, C2)>, &mut C1)>(&state).is_ok());585assert!(has_conflicts::<(EntityRefExcept<(C1, C2)>, &mut C1)>(c).is_ok());586587let state =588<(&mut C1, &mut C2, EntityRefExcept<(C1, C2)>) as WorldQuery>::get_state(c).unwrap();589assert!(590has_conflicts_small::<(&mut C1, &mut C2, EntityRefExcept<(C1, C2)>)>(&state).is_ok()591);592assert!(593has_conflicts_large::<(&mut C1, &mut C2, EntityRefExcept<(C1, C2)>)>(&state).is_ok()594);595assert!(has_conflicts::<(&mut C1, &mut C2, EntityRefExcept<(C1, C2)>)>(c).is_ok());596597let state =598<(&mut C1, EntityRefExcept<(C1, C2)>, &mut C2) as WorldQuery>::get_state(c).unwrap();599assert!(600has_conflicts_small::<(&mut C1, EntityRefExcept<(C1, C2)>, &mut C2)>(&state).is_ok()601);602assert!(603has_conflicts_large::<(&mut C1, EntityRefExcept<(C1, C2)>, &mut C2)>(&state).is_ok()604);605assert!(has_conflicts::<(&mut C1, EntityRefExcept<(C1, C2)>, &mut C2)>(c).is_ok());606607let state =608<(EntityRefExcept<(C1, C2)>, &mut C1, &mut C2) as WorldQuery>::get_state(c).unwrap();609assert!(610has_conflicts_small::<(EntityRefExcept<(C1, C2)>, &mut C1, &mut C2)>(&state).is_ok()611);612assert!(613has_conflicts_large::<(EntityRefExcept<(C1, C2)>, &mut C1, &mut C2)>(&state).is_ok()614);615assert!(has_conflicts::<(EntityRefExcept<(C1, C2)>, &mut C1, &mut C2)>(c).is_ok());616}617618#[test]619#[should_panic(expected = "conflicts")]620fn entity_ref_except_conflicts_component_write() {621let world = setup_world();622let c = world.components();623let state = <(EntityRefExcept<C1>, &mut C2) as WorldQuery>::get_state(c).unwrap();624assert!(has_conflicts_small::<(EntityRefExcept<C1>, &mut C2)>(&state).is_err());625assert!(has_conflicts_large::<(EntityRefExcept<C1>, &mut C2)>(&state).is_err());626let _ = has_conflicts::<(EntityRefExcept<C1>, &mut C2)>(c);627}628629#[test]630#[should_panic(expected = "conflicts")]631fn component_write_conflicts_entity_ref_except() {632let world = setup_world();633let c = world.components();634let state = <(&mut C2, EntityRefExcept<C1>) as WorldQuery>::get_state(c).unwrap();635assert!(has_conflicts_small::<(&mut C2, EntityRefExcept<C1>)>(&state).is_err());636assert!(has_conflicts_large::<(&mut C2, EntityRefExcept<C1>)>(&state).is_err());637let _ = has_conflicts::<(&mut C2, EntityRefExcept<C1>)>(c);638}639640#[test]641fn entity_mut_except_compatible() {642let world = setup_world();643let c = world.components();644645// Compatible646let state = <(EntityMutExcept<C1>, &mut C1) as WorldQuery>::get_state(c).unwrap();647assert!(has_conflicts_small::<(EntityMutExcept<C1>, &mut C1)>(&state).is_ok());648assert!(has_conflicts_large::<(EntityMutExcept<C1>, &mut C1)>(&state).is_ok());649assert!(has_conflicts::<(EntityMutExcept<C1>, &mut C1)>(c).is_ok());650651let state = <(&mut C1, EntityMutExcept<C1>) as WorldQuery>::get_state(c).unwrap();652assert!(has_conflicts_small::<(&mut C1, EntityMutExcept<C1>)>(&state).is_ok());653assert!(has_conflicts_large::<(&mut C1, EntityMutExcept<C1>)>(&state).is_ok());654assert!(has_conflicts::<(&mut C1, EntityMutExcept<C1>)>(c).is_ok());655656let state = <(&mut C1, EntityMutExcept<(C1, C2)>) as WorldQuery>::get_state(c).unwrap();657assert!(has_conflicts_small::<(&mut C1, EntityMutExcept<(C1, C2)>)>(&state).is_ok());658assert!(has_conflicts_large::<(&mut C1, EntityMutExcept<(C1, C2)>)>(&state).is_ok());659assert!(has_conflicts::<(&mut C1, EntityMutExcept<(C1, C2)>)>(c).is_ok());660661let state = <(EntityMutExcept<(C1, C2)>, &mut C1) as WorldQuery>::get_state(c).unwrap();662assert!(has_conflicts_small::<(EntityMutExcept<(C1, C2)>, &mut C1)>(&state).is_ok());663assert!(has_conflicts_large::<(EntityMutExcept<(C1, C2)>, &mut C1)>(&state).is_ok());664assert!(has_conflicts::<(EntityMutExcept<(C1, C2)>, &mut C1)>(c).is_ok());665666let state =667<(&mut C1, &mut C2, EntityMutExcept<(C1, C2)>) as WorldQuery>::get_state(c).unwrap();668assert!(669has_conflicts_small::<(&mut C1, &mut C2, EntityMutExcept<(C1, C2)>)>(&state).is_ok()670);671assert!(672has_conflicts_large::<(&mut C1, &mut C2, EntityMutExcept<(C1, C2)>)>(&state).is_ok()673);674assert!(has_conflicts::<(&mut C1, &mut C2, EntityMutExcept<(C1, C2)>)>(c).is_ok());675676let state =677<(&mut C1, EntityMutExcept<(C1, C2)>, &mut C2) as WorldQuery>::get_state(c).unwrap();678assert!(679has_conflicts_small::<(&mut C1, EntityMutExcept<(C1, C2)>, &mut C2)>(&state).is_ok()680);681assert!(682has_conflicts_large::<(&mut C1, EntityMutExcept<(C1, C2)>, &mut C2)>(&state).is_ok()683);684assert!(has_conflicts::<(&mut C1, EntityMutExcept<(C1, C2)>, &mut C2)>(c).is_ok());685686let state =687<(EntityMutExcept<(C1, C2)>, &mut C1, &mut C2) as WorldQuery>::get_state(c).unwrap();688assert!(689has_conflicts_small::<(EntityMutExcept<(C1, C2)>, &mut C1, &mut C2)>(&state).is_ok()690);691assert!(692has_conflicts_large::<(EntityMutExcept<(C1, C2)>, &mut C1, &mut C2)>(&state).is_ok()693);694assert!(has_conflicts::<(EntityMutExcept<(C1, C2)>, &mut C1, &mut C2)>(c).is_ok());695}696697#[test]698#[should_panic(expected = "conflicts")]699fn entity_mut_except_conflicts_component_read() {700let world = setup_world();701let c = world.components();702let state = <(EntityMutExcept<C1>, &C2) as WorldQuery>::get_state(c).unwrap();703assert!(has_conflicts_small::<(EntityMutExcept<C1>, &C2)>(&state).is_err());704assert!(has_conflicts_large::<(EntityMutExcept<C1>, &C2)>(&state).is_err());705let _ = has_conflicts::<(EntityMutExcept<C1>, &C2)>(c);706}707708#[test]709#[should_panic(expected = "conflicts")]710fn component_read_conflicts_entity_mut_except() {711let world = setup_world();712let c = world.components();713let state = <(&C2, EntityMutExcept<C1>) as WorldQuery>::get_state(c).unwrap();714assert!(has_conflicts_small::<(&C2, EntityMutExcept<C1>)>(&state).is_err());715assert!(has_conflicts_large::<(&C2, EntityMutExcept<C1>)>(&state).is_err());716let _ = has_conflicts::<(&C2, EntityMutExcept<C1>)>(c);717}718719#[test]720#[should_panic(expected = "conflicts")]721fn entity_mut_except_conflicts_component_write() {722let world = setup_world();723let c = world.components();724let state = <(EntityMutExcept<C1>, &mut C2) as WorldQuery>::get_state(c).unwrap();725assert!(has_conflicts_small::<(EntityMutExcept<C1>, &mut C2)>(&state).is_err());726assert!(has_conflicts_large::<(EntityMutExcept<C1>, &mut C2)>(&state).is_err());727let _ = has_conflicts::<(EntityMutExcept<C1>, &mut C2)>(c);728}729730#[test]731#[should_panic(expected = "conflicts")]732fn component_write_conflicts_entity_mut_except() {733let world = setup_world();734let c = world.components();735let state = <(&mut C2, EntityMutExcept<C1>) as WorldQuery>::get_state(c).unwrap();736assert!(has_conflicts_small::<(&mut C2, EntityMutExcept<C1>)>(&state).is_err());737assert!(has_conflicts_large::<(&mut C2, EntityMutExcept<C1>)>(&state).is_err());738let _ = has_conflicts::<(&mut C2, EntityMutExcept<C1>)>(c);739}740}741742743