Skip to content

Commit 4c6b6fc

Browse files
bushrat011899ZacHarroldC5tguichaoua
authored
Moved get_component(_unchecked_mut) from Query to QueryState (#9686)
# Objective - Fixes #9683 ## Solution - Moved `get_component` from `Query` to `QueryState`. - Moved `get_component_unchecked_mut` from `Query` to `QueryState`. - Moved `QueryComponentError` from `bevy_ecs::system` to `bevy_ecs::query`. Minor Breaking Change. - Narrowed scope of `unsafe` blocks in `Query` methods. --- ## Migration Guide - `use bevy_ecs::system::QueryComponentError;` -> `use bevy_ecs::query::QueryComponentError;` ## Notes I am not very familiar with unsafe Rust nor its use within Bevy, so I may have committed a Rust faux pas during the migration. --------- Co-authored-by: Zac Harrold <[email protected]> Co-authored-by: Tristan Guichaoua <[email protected]>
1 parent 625d386 commit 4c6b6fc

File tree

5 files changed

+297
-215
lines changed

5 files changed

+297
-215
lines changed

crates/bevy_ecs/src/query/error.rs

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
use std::fmt;
2+
3+
use crate::entity::Entity;
4+
5+
/// An error that occurs when retrieving a specific [`Entity`]'s query result from [`Query`](crate::system::Query) or [`QueryState`](crate::query::QueryState).
6+
// TODO: return the type_name as part of this error
7+
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
8+
pub enum QueryEntityError {
9+
/// The given [`Entity`]'s components do not match the query.
10+
///
11+
/// Either it does not have a requested component, or it has a component which the query filters out.
12+
QueryDoesNotMatch(Entity),
13+
/// The given [`Entity`] does not exist.
14+
NoSuchEntity(Entity),
15+
/// The [`Entity`] was requested mutably more than once.
16+
///
17+
/// See [`QueryState::get_many_mut`](crate::query::QueryState::get_many_mut) for an example.
18+
AliasedMutability(Entity),
19+
}
20+
21+
impl std::error::Error for QueryEntityError {}
22+
23+
impl fmt::Display for QueryEntityError {
24+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
25+
match self {
26+
QueryEntityError::QueryDoesNotMatch(_) => {
27+
write!(f, "The given entity's components do not match the query.")
28+
}
29+
QueryEntityError::NoSuchEntity(_) => write!(f, "The requested entity does not exist."),
30+
QueryEntityError::AliasedMutability(_) => {
31+
write!(f, "The entity was requested mutably more than once.")
32+
}
33+
}
34+
}
35+
}
36+
37+
/// An error that occurs when retrieving a specific [`Entity`]'s component from a [`Query`](crate::system::Query).
38+
#[derive(Debug, PartialEq, Eq)]
39+
pub enum QueryComponentError {
40+
/// The [`Query`](crate::system::Query) does not have read access to the requested component.
41+
///
42+
/// This error occurs when the requested component is not included in the original query.
43+
///
44+
/// # Example
45+
///
46+
/// ```
47+
/// # use bevy_ecs::{prelude::*, query::QueryComponentError};
48+
/// #
49+
/// # #[derive(Component)]
50+
/// # struct OtherComponent;
51+
/// #
52+
/// # #[derive(Component, PartialEq, Debug)]
53+
/// # struct RequestedComponent;
54+
/// #
55+
/// # #[derive(Resource)]
56+
/// # struct SpecificEntity {
57+
/// # entity: Entity,
58+
/// # }
59+
/// #
60+
/// fn get_missing_read_access_error(query: Query<&OtherComponent>, res: Res<SpecificEntity>) {
61+
/// assert_eq!(
62+
/// query.get_component::<RequestedComponent>(res.entity),
63+
/// Err(QueryComponentError::MissingReadAccess),
64+
/// );
65+
/// println!("query doesn't have read access to RequestedComponent because it does not appear in Query<&OtherComponent>");
66+
/// }
67+
/// # bevy_ecs::system::assert_is_system(get_missing_read_access_error);
68+
/// ```
69+
MissingReadAccess,
70+
/// The [`Query`](crate::system::Query) does not have write access to the requested component.
71+
///
72+
/// This error occurs when the requested component is not included in the original query, or the mutability of the requested component is mismatched with the original query.
73+
///
74+
/// # Example
75+
///
76+
/// ```
77+
/// # use bevy_ecs::{prelude::*, query::QueryComponentError};
78+
/// #
79+
/// # #[derive(Component, PartialEq, Debug)]
80+
/// # struct RequestedComponent;
81+
/// #
82+
/// # #[derive(Resource)]
83+
/// # struct SpecificEntity {
84+
/// # entity: Entity,
85+
/// # }
86+
/// #
87+
/// fn get_missing_write_access_error(mut query: Query<&RequestedComponent>, res: Res<SpecificEntity>) {
88+
/// assert_eq!(
89+
/// query.get_component::<RequestedComponent>(res.entity),
90+
/// Err(QueryComponentError::MissingWriteAccess),
91+
/// );
92+
/// println!("query doesn't have write access to RequestedComponent because it doesn't have &mut in Query<&RequestedComponent>");
93+
/// }
94+
/// # bevy_ecs::system::assert_is_system(get_missing_write_access_error);
95+
/// ```
96+
MissingWriteAccess,
97+
/// The given [`Entity`] does not have the requested component.
98+
MissingComponent,
99+
/// The requested [`Entity`] does not exist.
100+
NoSuchEntity,
101+
}
102+
103+
impl std::error::Error for QueryComponentError {}
104+
105+
impl std::fmt::Display for QueryComponentError {
106+
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
107+
match self {
108+
QueryComponentError::MissingReadAccess => {
109+
write!(
110+
f,
111+
"This query does not have read access to the requested component."
112+
)
113+
}
114+
QueryComponentError::MissingWriteAccess => {
115+
write!(
116+
f,
117+
"This query does not have write access to the requested component."
118+
)
119+
}
120+
QueryComponentError::MissingComponent => {
121+
write!(f, "The given entity does not have the requested component.")
122+
}
123+
QueryComponentError::NoSuchEntity => {
124+
write!(f, "The requested entity does not exist.")
125+
}
126+
}
127+
}
128+
}
129+
130+
/// An error that occurs when evaluating a [`Query`](crate::system::Query) or [`QueryState`](crate::query::QueryState) as a single expected result via
131+
/// [`get_single`](crate::system::Query::get_single) or [`get_single_mut`](crate::system::Query::get_single_mut).
132+
#[derive(Debug)]
133+
pub enum QuerySingleError {
134+
/// No entity fits the query.
135+
NoEntities(&'static str),
136+
/// Multiple entities fit the query.
137+
MultipleEntities(&'static str),
138+
}
139+
140+
impl std::error::Error for QuerySingleError {}
141+
142+
impl std::fmt::Display for QuerySingleError {
143+
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
144+
match self {
145+
QuerySingleError::NoEntities(query) => write!(f, "No entities fit the query {query}"),
146+
QuerySingleError::MultipleEntities(query) => {
147+
write!(f, "Multiple entities fit the query {query}!")
148+
}
149+
}
150+
}
151+
}

crates/bevy_ecs/src/query/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
//! Contains APIs for retrieving component data from the world.
22
33
mod access;
4+
mod error;
45
mod fetch;
56
mod filter;
67
mod iter;
78
mod par_iter;
89
mod state;
910

1011
pub use access::*;
12+
pub use error::*;
1113
pub use fetch::*;
1214
pub use filter::*;
1315
pub use iter::*;

crates/bevy_ecs/src/query/state.rs

Lines changed: 119 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
use crate::{
22
archetype::{Archetype, ArchetypeComponentId, ArchetypeGeneration, ArchetypeId},
3+
change_detection::Mut,
34
component::{ComponentId, Tick},
45
entity::Entity,
5-
prelude::FromWorld,
6+
prelude::{Component, FromWorld},
67
query::{
78
Access, BatchingStrategy, DebugCheckedUnwrap, FilteredAccess, QueryCombinationIter,
89
QueryIter, QueryParIter, WorldQuery,
@@ -13,9 +14,12 @@ use crate::{
1314
#[cfg(feature = "trace")]
1415
use bevy_utils::tracing::Instrument;
1516
use fixedbitset::FixedBitSet;
16-
use std::{borrow::Borrow, fmt, mem::MaybeUninit};
17+
use std::{any::TypeId, borrow::Borrow, fmt, mem::MaybeUninit};
1718

18-
use super::{NopWorldQuery, QueryManyIter, ROQueryItem, ReadOnlyWorldQuery};
19+
use super::{
20+
NopWorldQuery, QueryComponentError, QueryEntityError, QueryManyIter, QuerySingleError,
21+
ROQueryItem, ReadOnlyWorldQuery,
22+
};
1923

2024
/// Provides scoped access to a [`World`] state according to a given [`WorldQuery`] and query filter.
2125
#[repr(C)]
@@ -506,6 +510,110 @@ impl<Q: WorldQuery, F: ReadOnlyWorldQuery> QueryState<Q, F> {
506510
}
507511
}
508512

513+
/// Returns a shared reference to the component `T` of the given [`Entity`].
514+
///
515+
/// In case of a nonexisting entity or mismatched component, a [`QueryEntityError`] is returned instead.
516+
#[inline]
517+
pub(crate) fn get_component<'w, 's, 'r, T: Component>(
518+
&'s self,
519+
world: UnsafeWorldCell<'w>,
520+
entity: Entity,
521+
) -> Result<&'r T, QueryComponentError>
522+
where
523+
'w: 'r,
524+
{
525+
let entity_ref = world
526+
.get_entity(entity)
527+
.ok_or(QueryComponentError::NoSuchEntity)?;
528+
let component_id = world
529+
.components()
530+
.get_id(TypeId::of::<T>())
531+
.ok_or(QueryComponentError::MissingComponent)?;
532+
let archetype_component = entity_ref
533+
.archetype()
534+
.get_archetype_component_id(component_id)
535+
.ok_or(QueryComponentError::MissingComponent)?;
536+
if self
537+
.archetype_component_access
538+
.has_read(archetype_component)
539+
{
540+
// SAFETY: `world` must have access to the component `T` for this entity,
541+
// since it was registered in `self`'s archetype component access set.
542+
unsafe { entity_ref.get::<T>() }.ok_or(QueryComponentError::MissingComponent)
543+
} else {
544+
Err(QueryComponentError::MissingReadAccess)
545+
}
546+
}
547+
548+
/// Returns a shared reference to the component `T` of the given [`Entity`].
549+
///
550+
/// # Panics
551+
///
552+
/// If given a nonexisting entity or mismatched component, this will panic.
553+
#[inline]
554+
pub(crate) fn component<'w, 's, 'r, T: Component>(
555+
&'s self,
556+
world: UnsafeWorldCell<'w>,
557+
entity: Entity,
558+
) -> &'r T
559+
where
560+
'w: 'r,
561+
{
562+
match self.get_component(world, entity) {
563+
Ok(component) => component,
564+
Err(error) => {
565+
panic!(
566+
"Cannot get component `{:?}` from {entity:?}: {error}",
567+
TypeId::of::<T>()
568+
)
569+
}
570+
}
571+
}
572+
573+
/// Returns a mutable reference to the component `T` of the given entity.
574+
///
575+
/// In case of a nonexisting entity or mismatched component, a [`QueryComponentError`] is returned instead.
576+
///
577+
/// # Safety
578+
///
579+
/// This function makes it possible to violate Rust's aliasing guarantees.
580+
/// You must make sure this call does not result in multiple mutable references to the same component.
581+
#[inline]
582+
pub unsafe fn get_component_unchecked_mut<'w, 's, 'r, T: Component>(
583+
&'s self,
584+
world: UnsafeWorldCell<'w>,
585+
entity: Entity,
586+
last_run: Tick,
587+
this_run: Tick,
588+
) -> Result<Mut<'r, T>, QueryComponentError>
589+
where
590+
'w: 'r,
591+
{
592+
let entity_ref = world
593+
.get_entity(entity)
594+
.ok_or(QueryComponentError::NoSuchEntity)?;
595+
let component_id = world
596+
.components()
597+
.get_id(TypeId::of::<T>())
598+
.ok_or(QueryComponentError::MissingComponent)?;
599+
let archetype_component = entity_ref
600+
.archetype()
601+
.get_archetype_component_id(component_id)
602+
.ok_or(QueryComponentError::MissingComponent)?;
603+
if self
604+
.archetype_component_access
605+
.has_write(archetype_component)
606+
{
607+
// SAFETY: It is the responsibility of the caller to ensure it is sound to get a
608+
// mutable reference to this entity's component `T`.
609+
let result = unsafe { entity_ref.get_mut_using_ticks::<T>(last_run, this_run) };
610+
611+
result.ok_or(QueryComponentError::MissingComponent)
612+
} else {
613+
Err(QueryComponentError::MissingWriteAccess)
614+
}
615+
}
616+
509617
/// Gets the read-only query results for the given [`World`] and array of [`Entity`], where the last change and
510618
/// the current change tick are given.
511619
///
@@ -1196,7 +1304,10 @@ impl<Q: WorldQuery, F: ReadOnlyWorldQuery> QueryState<Q, F> {
11961304
#[track_caller]
11971305
#[inline]
11981306
pub fn single<'w>(&mut self, world: &'w World) -> ROQueryItem<'w, Q> {
1199-
self.get_single(world).unwrap()
1307+
match self.get_single(world) {
1308+
Ok(items) => items,
1309+
Err(error) => panic!("Cannot get single mutable query result: {error}"),
1310+
}
12001311
}
12011312

12021313
/// Returns a single immutable query result when there is exactly one entity matching
@@ -1235,7 +1346,10 @@ impl<Q: WorldQuery, F: ReadOnlyWorldQuery> QueryState<Q, F> {
12351346
#[inline]
12361347
pub fn single_mut<'w>(&mut self, world: &'w mut World) -> Q::Item<'w> {
12371348
// SAFETY: query has unique world access
1238-
self.get_single_mut(world).unwrap()
1349+
match self.get_single_mut(world) {
1350+
Ok(items) => items,
1351+
Err(error) => panic!("Cannot get single query result: {error}"),
1352+
}
12391353
}
12401354

12411355
/// Returns a single mutable query result when there is exactly one entity matching
@@ -1311,38 +1425,6 @@ impl<Q: WorldQuery, F: ReadOnlyWorldQuery> QueryState<Q, F> {
13111425
}
13121426
}
13131427

1314-
/// An error that occurs when retrieving a specific [`Entity`]'s query result from [`Query`](crate::system::Query) or [`QueryState`].
1315-
// TODO: return the type_name as part of this error
1316-
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
1317-
pub enum QueryEntityError {
1318-
/// The given [`Entity`]'s components do not match the query.
1319-
///
1320-
/// Either it does not have a requested component, or it has a component which the query filters out.
1321-
QueryDoesNotMatch(Entity),
1322-
/// The given [`Entity`] does not exist.
1323-
NoSuchEntity(Entity),
1324-
/// The [`Entity`] was requested mutably more than once.
1325-
///
1326-
/// See [`QueryState::get_many_mut`] for an example.
1327-
AliasedMutability(Entity),
1328-
}
1329-
1330-
impl std::error::Error for QueryEntityError {}
1331-
1332-
impl fmt::Display for QueryEntityError {
1333-
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1334-
match self {
1335-
QueryEntityError::QueryDoesNotMatch(_) => {
1336-
write!(f, "The given entity's components do not match the query.")
1337-
}
1338-
QueryEntityError::NoSuchEntity(_) => write!(f, "The requested entity does not exist."),
1339-
QueryEntityError::AliasedMutability(_) => {
1340-
write!(f, "The entity was requested mutably more than once.")
1341-
}
1342-
}
1343-
}
1344-
}
1345-
13461428
#[cfg(test)]
13471429
mod tests {
13481430
use crate::{prelude::*, query::QueryEntityError};
@@ -1451,26 +1533,3 @@ mod tests {
14511533
let _panics = query_state.get_many_mut(&mut world_2, []);
14521534
}
14531535
}
1454-
1455-
/// An error that occurs when evaluating a [`Query`](crate::system::Query) or [`QueryState`] as a single expected result via
1456-
/// [`get_single`](crate::system::Query::get_single) or [`get_single_mut`](crate::system::Query::get_single_mut).
1457-
#[derive(Debug)]
1458-
pub enum QuerySingleError {
1459-
/// No entity fits the query.
1460-
NoEntities(&'static str),
1461-
/// Multiple entities fit the query.
1462-
MultipleEntities(&'static str),
1463-
}
1464-
1465-
impl std::error::Error for QuerySingleError {}
1466-
1467-
impl std::fmt::Display for QuerySingleError {
1468-
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
1469-
match self {
1470-
QuerySingleError::NoEntities(query) => write!(f, "No entities fit the query {query}"),
1471-
QuerySingleError::MultipleEntities(query) => {
1472-
write!(f, "Multiple entities fit the query {query}!")
1473-
}
1474-
}
1475-
}
1476-
}

0 commit comments

Comments
 (0)