Fix unsound lifetimes in Query::join
and Query::join_filtered
(#17972)
# Objective Fix unsound lifetimes in `Query::join` and `Query::join_filtered`. The joined query allowed access from either input query, but it only took the `'world` lifetime from `self`, not from `other`. This meant that after the borrow of `other` ended, the joined query would unsoundly alias `other`. ## Solution Change the lifetimes on `join` and `join_filtered` to require mutable borrows of the *same* lifetime for the input queries. This ensures both input queries are borrowed for the full lifetime of the joined query. Change `join_inner` to take `other` by value instead of reference so that the returned query is still usable without needing to borrow from a local variable. ## Testing Added a compile-fail test.
This commit is contained in:
parent
387f1c593b
commit
6df711ce7f
@ -1,24 +1,46 @@
|
|||||||
use bevy_ecs::prelude::*;
|
use bevy_ecs::prelude::*;
|
||||||
use bevy_ecs::system::SystemState;
|
use bevy_ecs::system::{QueryLens, SystemState};
|
||||||
|
|
||||||
#[derive(Component, Eq, PartialEq, Debug)]
|
#[derive(Component, Eq, PartialEq, Debug)]
|
||||||
struct Foo(u32);
|
struct Foo(u32);
|
||||||
|
|
||||||
|
#[derive(Component, Eq, PartialEq, Debug)]
|
||||||
|
struct Bar(u32);
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let mut world = World::default();
|
let mut world = World::default();
|
||||||
let e = world.spawn(Foo(10_u32)).id();
|
let e = world.spawn((Foo(10_u32), Bar(10_u32))).id();
|
||||||
|
|
||||||
let mut system_state = SystemState::<Query<&mut Foo>>::new(&mut world);
|
let mut system_state = SystemState::<(Query<&mut Foo>, Query<&mut Bar>)>::new(&mut world);
|
||||||
{
|
{
|
||||||
let mut query = system_state.get_mut(&mut world);
|
let (mut foo_query, mut bar_query) = system_state.get_mut(&mut world);
|
||||||
let mut lens = query.as_query_lens();
|
|
||||||
dbg!("hi");
|
dbg!("hi");
|
||||||
{
|
{
|
||||||
|
let mut lens = foo_query.as_query_lens();
|
||||||
let mut data: Mut<Foo> = lens.query().get_inner(e).unwrap();
|
let mut data: Mut<Foo> = lens.query().get_inner(e).unwrap();
|
||||||
let mut data2: Mut<Foo> = lens.query().get_inner(e).unwrap();
|
let mut data2: Mut<Foo> = lens.query().get_inner(e).unwrap();
|
||||||
//~^ E0499
|
//~^ E0499
|
||||||
assert_eq!(&mut *data, &mut *data2); // oops UB
|
assert_eq!(&mut *data, &mut *data2); // oops UB
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut join: QueryLens<(&mut Foo, &mut Bar)> = foo_query.join(&mut bar_query);
|
||||||
|
let mut query = join.query();
|
||||||
|
let (_, mut data) = query.single_mut().unwrap();
|
||||||
|
let mut data2 = bar_query.single_mut().unwrap();
|
||||||
|
//~^ E0499
|
||||||
|
assert_eq!(&mut *data, &mut *data2); // oops UB
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut join: QueryLens<(&mut Foo, &mut Bar)> =
|
||||||
|
foo_query.join_inner(bar_query.reborrow());
|
||||||
|
let mut query = join.query();
|
||||||
|
let (_, mut data) = query.single_mut().unwrap();
|
||||||
|
let mut data2 = bar_query.single_mut().unwrap();
|
||||||
|
//~^ E0499
|
||||||
|
assert_eq!(&mut *data, &mut *data2); // oops UB
|
||||||
|
}
|
||||||
dbg!("bye");
|
dbg!("bye");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,38 @@
|
|||||||
error[E0499]: cannot borrow `lens` as mutable more than once at a time
|
error[E0499]: cannot borrow `lens` as mutable more than once at a time
|
||||||
--> tests/ui/query_lens_lifetime_safety.rs:18:39
|
--> tests/ui/query_lens_lifetime_safety.rs:21:39
|
||||||
|
|
|
|
||||||
17 | let mut data: Mut<Foo> = lens.query().get_inner(e).unwrap();
|
20 | let mut data: Mut<Foo> = lens.query().get_inner(e).unwrap();
|
||||||
| ---- first mutable borrow occurs here
|
| ---- first mutable borrow occurs here
|
||||||
18 | let mut data2: Mut<Foo> = lens.query().get_inner(e).unwrap();
|
21 | let mut data2: Mut<Foo> = lens.query().get_inner(e).unwrap();
|
||||||
| ^^^^ second mutable borrow occurs here
|
| ^^^^ second mutable borrow occurs here
|
||||||
19 |
|
22 |
|
||||||
20 | assert_eq!(&mut *data, &mut *data2); // oops UB
|
23 | assert_eq!(&mut *data, &mut *data2); // oops UB
|
||||||
| ---- first borrow later used here
|
| ---- first borrow later used here
|
||||||
|
|
||||||
error: aborting due to 1 previous error
|
error[E0499]: cannot borrow `bar_query` as mutable more than once at a time
|
||||||
|
--> tests/ui/query_lens_lifetime_safety.rs:30:29
|
||||||
|
|
|
||||||
|
27 | let mut join: QueryLens<(&mut Foo, &mut Bar)> = foo_query.join(&mut bar_query);
|
||||||
|
| -------------- first mutable borrow occurs here
|
||||||
|
...
|
||||||
|
30 | let mut data2 = bar_query.single_mut().unwrap();
|
||||||
|
| ^^^^^^^^^ second mutable borrow occurs here
|
||||||
|
31 |
|
||||||
|
32 | assert_eq!(&mut *data, &mut *data2); // oops UB
|
||||||
|
| ---- first borrow later used here
|
||||||
|
|
||||||
|
error[E0499]: cannot borrow `bar_query` as mutable more than once at a time
|
||||||
|
--> tests/ui/query_lens_lifetime_safety.rs:40:29
|
||||||
|
|
|
||||||
|
37 | foo_query.join_inner(bar_query.reborrow());
|
||||||
|
| --------- first mutable borrow occurs here
|
||||||
|
...
|
||||||
|
40 | let mut data2 = bar_query.single_mut().unwrap();
|
||||||
|
| ^^^^^^^^^ second mutable borrow occurs here
|
||||||
|
41 |
|
||||||
|
42 | assert_eq!(&mut *data, &mut *data2); // oops UB
|
||||||
|
| ---- first borrow later used here
|
||||||
|
|
||||||
|
error: aborting due to 3 previous errors
|
||||||
|
|
||||||
For more information about this error, try `rustc --explain E0499`.
|
For more information about this error, try `rustc --explain E0499`.
|
||||||
|
@ -2234,10 +2234,10 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> {
|
|||||||
///
|
///
|
||||||
/// Like `transmute_lens` the query terms can be changed with some restrictions.
|
/// Like `transmute_lens` the query terms can be changed with some restrictions.
|
||||||
/// See [`Self::transmute_lens`] for more details.
|
/// See [`Self::transmute_lens`] for more details.
|
||||||
pub fn join<OtherD: QueryData, NewD: QueryData>(
|
pub fn join<'a, OtherD: QueryData, NewD: QueryData>(
|
||||||
&mut self,
|
&'a mut self,
|
||||||
other: &mut Query<OtherD>,
|
other: &'a mut Query<OtherD>,
|
||||||
) -> QueryLens<'_, NewD> {
|
) -> QueryLens<'a, NewD> {
|
||||||
self.join_filtered(other)
|
self.join_filtered(other)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2263,7 +2263,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> {
|
|||||||
/// - [`join`](Self::join) to join using a mutable borrow of the [`Query`].
|
/// - [`join`](Self::join) to join using a mutable borrow of the [`Query`].
|
||||||
pub fn join_inner<OtherD: QueryData, NewD: QueryData>(
|
pub fn join_inner<OtherD: QueryData, NewD: QueryData>(
|
||||||
self,
|
self,
|
||||||
other: &mut Query<OtherD>,
|
other: Query<'w, '_, OtherD>,
|
||||||
) -> QueryLens<'w, NewD> {
|
) -> QueryLens<'w, NewD> {
|
||||||
self.join_filtered_inner(other)
|
self.join_filtered_inner(other)
|
||||||
}
|
}
|
||||||
@ -2276,15 +2276,16 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> {
|
|||||||
/// terms like `Added` and `Changed` will only be respected if they are in
|
/// terms like `Added` and `Changed` will only be respected if they are in
|
||||||
/// the type signature.
|
/// the type signature.
|
||||||
pub fn join_filtered<
|
pub fn join_filtered<
|
||||||
|
'a,
|
||||||
OtherD: QueryData,
|
OtherD: QueryData,
|
||||||
OtherF: QueryFilter,
|
OtherF: QueryFilter,
|
||||||
NewD: QueryData,
|
NewD: QueryData,
|
||||||
NewF: QueryFilter,
|
NewF: QueryFilter,
|
||||||
>(
|
>(
|
||||||
&mut self,
|
&'a mut self,
|
||||||
other: &mut Query<OtherD, OtherF>,
|
other: &'a mut Query<OtherD, OtherF>,
|
||||||
) -> QueryLens<'_, NewD, NewF> {
|
) -> QueryLens<'a, NewD, NewF> {
|
||||||
self.reborrow().join_filtered_inner(other)
|
self.reborrow().join_filtered_inner(other.reborrow())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Equivalent to [`Self::join_inner`] but also includes a [`QueryFilter`] type.
|
/// Equivalent to [`Self::join_inner`] but also includes a [`QueryFilter`] type.
|
||||||
@ -2306,7 +2307,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> {
|
|||||||
NewF: QueryFilter,
|
NewF: QueryFilter,
|
||||||
>(
|
>(
|
||||||
self,
|
self,
|
||||||
other: &mut Query<OtherD, OtherF>,
|
other: Query<'w, '_, OtherD, OtherF>,
|
||||||
) -> QueryLens<'w, NewD, NewF> {
|
) -> QueryLens<'w, NewD, NewF> {
|
||||||
let state = self
|
let state = self
|
||||||
.state
|
.state
|
||||||
|
Loading…
Reference in New Issue
Block a user