Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix error in AnyOf #14027

Merged
merged 1 commit into from
Jun 27, 2024
Merged

Conversation

cBournhonesque
Copy link
Contributor

@cBournhonesque cBournhonesque commented Jun 26, 2024

Objective

Solution

I've been playing around a lot of with the access code and I realized that I introduced a soundness error when trying to simplify the code. When we have a Or<(With<A>, With<B>)> filter, we cannot call

  let mut intermediate = FilteredAccess::default();
  $name::update_component_access($name, &mut intermediate);
  _new_access.append_or(&intermediate);

because that's just equivalent to adding the new components as Or clauses.
For example if the existing filter_sets was vec![With<C>], we would then get vec![With<C>, With<A>, With<B>] which translates to C or A or B.
Instead what we want is (C and A) or (C and B), so we need to have each new OR clause compose with the existing access like so:

let mut intermediate = _access.clone();
// if we previously had a With<C> in the filter_set, this will become `With<C> AND With<A>`
$name::update_component_access($name, &mut intermediate);
_new_access.append_or(&intermediate);

Testing

  • Added a unit test that is broken in main, but passes in this PR

@cBournhonesque cBournhonesque added A-ECS Entities, components, systems, and events P-Unsound A bug that results in undefined compiler behavior labels Jun 26, 2024
@alice-i-cecile alice-i-cecile added this to the 0.14 milestone Jun 26, 2024
@alice-i-cecile alice-i-cecile added C-Bug An unexpected or incorrect behavior S-Needs-Review Needs reviewer attention (from anyone!) to move forward labels Jun 26, 2024
@alice-i-cecile alice-i-cecile requested a review from hymm June 26, 2024 03:06
@alice-i-cecile
Copy link
Member

@SkiFire13 can I get your review here too?

@cBournhonesque cBournhonesque changed the title Fix soundness error in AnyOf Fix error in AnyOf Jun 26, 2024
@cBournhonesque cBournhonesque removed the P-Unsound A bug that results in undefined compiler behavior label Jun 26, 2024
@mockersf mockersf added this pull request to the merge queue Jun 27, 2024
Copy link
Contributor

@SkiFire13 SkiFire13 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I missed the github notification. I guess it's a bit late but still LGTM

Merged via the queue into bevyengine:main with commit 6573887 Jun 27, 2024
32 checks passed
mockersf pushed a commit that referenced this pull request Jun 27, 2024
# Objective

- Fixes a correctness error introduced in
#14013 ...

## Solution

I've been playing around a lot of with the access code and I realized
that I introduced a soundness error when trying to simplify the code.
When we have a `Or<(With<A>, With<B>)>` filter, we cannot call
```
  let mut intermediate = FilteredAccess::default();
  $name::update_component_access($name, &mut intermediate);
  _new_access.append_or(&intermediate);
```
because that's just equivalent to adding the new components as `Or`
clauses.
For example if the existing `filter_sets` was `vec![With<C>]`, we would
then get `vec![With<C>, With<A>, With<B>]` which translates to `A or B
or C`.
Instead what we want is `(A and B) or (A and C)`, so we need to have
each new OR clause compose with the existing access like so:
```
let mut intermediate = _access.clone();
// if we previously had a With<C> in the filter_set, this will become `With<C> AND With<A>`
$name::update_component_access($name, &mut intermediate);
_new_access.append_or(&intermediate);
```

## Testing

- Added a unit test that is broken in main, but passes in this PR
@BD103
Copy link
Member

BD103 commented Jun 29, 2024

This PR caused a regression in World::query.

image

Since the regression is only by 3%, and this was an important soundness bug, no further action needs to be taken.

Benchmark code, Alert graph

@cBournhonesque
Copy link
Contributor Author

I think the PR might indeed have a perf impact for AnyOf, since we do an extra access check.
However I think the benchmark you linked might be just noise? It doesn't use an AnyOf query

@BD103
Copy link
Member

BD103 commented Jun 29, 2024

I think the PR might indeed have a perf impact for AnyOf, since we do an extra access check. However I think the benchmark you linked might be just noise? It doesn't use an AnyOf query

There's a chance! The automatic alerts are run in Github Actions, which are pretty noisy. The screenshot, though, is from my local computer, which is a lot quieter. Did you change any code called by World::query / QueryState::iter? I can't easily tell by the diff.

@SkiFire13
Copy link
Contributor

Did you change any code called by World::query / QueryState::iter? I can't easily tell by the diff.

This is code that is called by World::query if the query contains an AnyOf.

@BD103
Copy link
Member

BD103 commented Jun 30, 2024

Did you change any code called by World::query / QueryState::iter? I can't easily tell by the diff.

This is code that is called by World::query if the query contains an AnyOf.

Ah, so it's just noise then. Sorry! (I got too excited that my fun side project was actually doing something lol)

zmbush pushed a commit to zmbush/bevy that referenced this pull request Jul 3, 2024
# Objective

- Fixes a correctness error introduced in
bevyengine#14013 ...

## Solution

I've been playing around a lot of with the access code and I realized
that I introduced a soundness error when trying to simplify the code.
When we have a `Or<(With<A>, With<B>)>` filter, we cannot call
```
  let mut intermediate = FilteredAccess::default();
  $name::update_component_access($name, &mut intermediate);
  _new_access.append_or(&intermediate);
```
because that's just equivalent to adding the new components as `Or`
clauses.
For example if the existing `filter_sets` was `vec![With<C>]`, we would
then get `vec![With<C>, With<A>, With<B>]` which translates to `A or B
or C`.
Instead what we want is `(A and B) or (A and C)`, so we need to have
each new OR clause compose with the existing access like so:
```
let mut intermediate = _access.clone();
// if we previously had a With<C> in the filter_set, this will become `With<C> AND With<A>`
$name::update_component_access($name, &mut intermediate);
_new_access.append_or(&intermediate);
```

## Testing

- Added a unit test that is broken in main, but passes in this PR
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-ECS Entities, components, systems, and events C-Bug An unexpected or incorrect behavior S-Needs-Review Needs reviewer attention (from anyone!) to move forward
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants