-
-
Notifications
You must be signed in to change notification settings - Fork 3.9k
Shorten the 'world lifetime returned from QueryLens::query()
.
#17694
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
Shorten the 'world lifetime returned from QueryLens::query()
.
#17694
Conversation
Add a new `QueryLens::query_inner()` to handle cases that were relying on the longer lifetime.
@Victoronz @chescock @13ros27 take a look at this please :) |
I'll be honest the way |
Yes, definitely! I have never written a compile-fail test, though. Can anyone point me in the right direction to get started? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As I mentioned I can't fully review the lifetime changes but the compile-fail test seems good and compiles successfully on main (where it shouldn't of course).
{ | ||
let mut query = system_state.get_mut(&mut world); | ||
let mut lens = query.as_query_lens(); | ||
dbg!("hi"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did you mean to leave these in?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I copied the skeleton from https://github.com/bevyengine/bevy/blob/f2a65c2dd3a3ef75964a8aaed67e1f24618bf19f/crates/bevy_ecs/compile_fail/tests/ui/query_lifetime_safety.rs . So, I did mean to leave them there, but not for any particularly good reason :). I'm inclined to leave it like this for consistency with the other file, but I'm happy to change it if there are strong objections.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Huh, I must have got my search filtering wrong because I thought none of the other compile-fail tests had dbg
in them, feel free to leave it then.
@@ -2097,7 +2097,21 @@ pub struct QueryLens<'w, Q: QueryData, F: QueryFilter = ()> { | |||
|
|||
impl<'w, Q: QueryData, F: QueryFilter> QueryLens<'w, Q, F> { | |||
/// Create a [`Query`] from the underlying [`QueryState`]. | |||
pub fn query(&mut self) -> Query<'w, '_, Q, F> { | |||
pub fn query(&mut self) -> Query<'_, '_, Q, F> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So the new flow is
query
->Query<'w, s'>
lens = &'a query.as_query_lens()
->QueryLens<'a>
lens.query()
->Query<'a, 'a>
get_inner()
->QueryItem<'a>
and the old one is
lens.query()
->Query<'w, 'a>
get_inner()
->QueryItem<'w>
So in the old case, the item we get only depends on the 'w lifetime, so the compiler doesn't stop us from creating a second object with the 'a lifetime via lens.query()
.
I think the change makes sense; out of curoisity, would this PR #15396 also work to fix your compile-fail test?
Since the QueryItem we would get would have been QueryItem<'w, 'a>
, we might not be able to call lens.query()
a second time because the 'a
lifetime is present in the return value
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I did't completely follow your notation, but your conclusion sounds right! We had an &'a mut QueryLens<'w>
, and currently that gives a Query<'w, 'a>
which we can use with get_inner()
to get a QueryItem<'w>
, but with this change it gives a Query<'a, 'a>
that we can only use to get a QueryItem<'a>
.
I believe the compile-fail test would still compile with #15396, because the compiler would see that the concrete type of QueryItem<'w, 'a>
is Mut<'w, Foo>
, which doesn't involve the 'a
lifetime.
Objective
Fix unsoundness introduced by #15858.
QueryLens::query()
would hand out aQuery
with the full'w
lifetime, and the new_inner
methods would let the results outlive theQuery
. This could be used to create aliasing mutable references, likeFixes #17693
Solution
Restrict the
'world
lifetime in theQuery
returned byQueryLens::query()
to'_
, the lifetime of the borrow of theQueryLens
.The model here is that
Query<'w, 's, D, F>
andQueryLens<'w, D, F>
have permission to access their components for the lifetime'w
. So going from&'a mut QueryLens<'w>
toQuery<'w, 'a>
would borrow the permission only for the'a
lifetime, but incorrectly give it out for the full'w
lifetime.To handle any cases where users were calling
get_inner()
oriter_inner()
on theQuery
and expecting the full'w
lifetime, we introduce a newQueryLens::query_inner()
method. This is only valid forReadOnlyQueryData
, so it may safely hand out a copy of the permission for the full'w
lifetime. Sinceget_inner()
anditer_inner()
were only valid onReadOnlyQueryData
prior to #15858, that should cover any uses that relied on the longer lifetime.Migration Guide
Users of
QueryLens::query()
who were callingget_inner()
oriter_inner()
will need to replace the call withQueryLens::query_inner()
.