AnyOf soundness fix (#14013)

# Objective
Fixes https://github.com/bevyengine/bevy/issues/13993 
PR inspired by https://github.com/bevyengine/bevy/pull/14007 to
accomplish the same thing, but maybe in a clearer fashion.

@Gingeh feel free to take my changes and add them to your PR, I don't
want to steal any credit

---------

Co-authored-by: Gingeh <39150378+Gingeh@users.noreply.github.com>
Co-authored-by: Bob Gardner <rgardner@inworld.ai>
Co-authored-by: Martín Maita <47983254+mnmaita@users.noreply.github.com>
This commit is contained in:
Periwink 2024-06-25 19:54:50 -04:00 committed by GitHub
parent 336fddb101
commit 8308ad08a2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 62 additions and 5 deletions

View file

@ -1862,16 +1862,20 @@ macro_rules! impl_anytuple_fetch {
}
fn update_component_access(state: &Self::State, _access: &mut FilteredAccess<ComponentId>) {
let ($($name,)*) = state;
let mut _new_access = _access.clone();
// update the access (add the read/writes)
<($(Option<$name>,)*)>::update_component_access(state, _access);
// update the filters (Or<(With<$name>,)>)
let ($($name,)*) = state;
let mut _not_first = false;
$(
if _not_first {
let mut intermediate = _access.clone();
// we use an intermediate access because we only want to update the filters, not the access
let mut intermediate = FilteredAccess::default();
$name::update_component_access($name, &mut intermediate);
_new_access.append_or(&intermediate);
_new_access.extend_access(&intermediate);
} else {
$name::update_component_access($name, &mut _new_access);
_new_access.required = _access.required.clone();
@ -1879,7 +1883,7 @@ macro_rules! impl_anytuple_fetch {
}
)*
*_access = _new_access;
_access.filter_sets = _new_access.filter_sets;
}
#[allow(unused_variables)]
fn init_state(world: &mut World) -> Self::State {

View file

@ -556,6 +556,51 @@ mod tests {
run_system(&mut world, sys);
}
#[test]
fn any_of_working() {
fn sys(_: Query<AnyOf<(&mut A, &B)>>) {}
let mut world = World::default();
run_system(&mut world, sys);
}
#[test]
#[should_panic = "&bevy_ecs::system::tests::A conflicts with a previous access in this query."]
fn any_of_with_mut_and_ref() {
fn sys(_: Query<AnyOf<(&mut A, &A)>>) {}
let mut world = World::default();
run_system(&mut world, sys);
}
#[test]
#[should_panic = "&mut bevy_ecs::system::tests::A conflicts with a previous access in this query."]
fn any_of_with_ref_and_mut() {
fn sys(_: Query<AnyOf<(&A, &mut A)>>) {}
let mut world = World::default();
run_system(&mut world, sys);
}
#[test]
#[should_panic = "&bevy_ecs::system::tests::A conflicts with a previous access in this query."]
fn any_of_with_mut_and_option() {
fn sys(_: Query<AnyOf<(&mut A, Option<&A>)>>) {}
let mut world = World::default();
run_system(&mut world, sys);
}
#[test]
fn any_of_with_entity_and_mut() {
fn sys(_: Query<AnyOf<(Entity, &mut A)>>) {}
let mut world = World::default();
run_system(&mut world, sys);
}
#[test]
fn any_of_with_empty_and_mut() {
fn sys(_: Query<AnyOf<((), &mut A)>>) {}
let mut world = World::default();
run_system(&mut world, sys);
}
#[test]
#[should_panic = "error[B0001]"]
fn any_of_has_no_filter_with() {
@ -564,6 +609,14 @@ mod tests {
run_system(&mut world, sys);
}
#[test]
#[should_panic = "&mut bevy_ecs::system::tests::A conflicts with a previous access in this query."]
fn any_of_with_conflicting() {
fn sys(_: Query<AnyOf<(&mut A, &mut A)>>) {}
let mut world = World::default();
run_system(&mut world, sys);
}
#[test]
fn any_of_has_filter_with_when_both_have_it() {
fn sys(_: Query<(AnyOf<(&A, &A)>, &mut B)>, _: Query<&mut B, Without<A>>) {}