diff --git a/crates/bevy_ecs/compile_fail/tests/ui/system_query_iter_sort_lifetime_safety.rs b/crates/bevy_ecs/compile_fail/tests/ui/system_query_iter_sort_lifetime_safety.rs new file mode 100644 index 0000000000000..118e732093179 --- /dev/null +++ b/crates/bevy_ecs/compile_fail/tests/ui/system_query_iter_sort_lifetime_safety.rs @@ -0,0 +1,19 @@ +use bevy_ecs::prelude::*; +use std::cmp::Ordering; + +#[derive(Component)] +struct A(usize); + +fn system(mut query: Query<&mut A>) { + let iter = query.iter_mut(); + let mut stored: Option<&A> = None; + let mut sorted = iter.sort_by::<&A>(|left, _right| { + // Try to smuggle the lens item out of the closure. + stored = Some(left); + //~^ E0521 + Ordering::Equal + }); + let r: &A = stored.unwrap(); + let m: &mut A = &mut sorted.next().unwrap(); + assert!(std::ptr::eq(r, m)); +} diff --git a/crates/bevy_ecs/compile_fail/tests/ui/system_query_iter_sort_lifetime_safety.stderr b/crates/bevy_ecs/compile_fail/tests/ui/system_query_iter_sort_lifetime_safety.stderr new file mode 100644 index 0000000000000..7e0ca6e4c624d --- /dev/null +++ b/crates/bevy_ecs/compile_fail/tests/ui/system_query_iter_sort_lifetime_safety.stderr @@ -0,0 +1,10 @@ +error[E0521]: borrowed data escapes outside of closure + --> tests/ui\system_query_iter_sort_lifetime_safety.rs:12:9 + | +9 | let mut stored: Option<&A> = None; + | ---------- `stored` declared here, outside of the closure body +10 | let mut sorted = iter.sort_by::<&A>(|left, _right| { + | ---- `left` is a reference that is only valid in the closure body +11 | // Try to smuggle the lens item out of the closure. +12 | stored = Some(left); + | ^^^^^^^^^^^^^^^^^^^ `left` escapes the closure body here diff --git a/crates/bevy_ecs/src/query/iter.rs b/crates/bevy_ecs/src/query/iter.rs index f68b9d45d168b..50624205d55b6 100644 --- a/crates/bevy_ecs/src/query/iter.rs +++ b/crates/bevy_ecs/src/query/iter.rs @@ -487,7 +487,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIter<'w, 's, D, F> { /// # schedule.add_systems((system_1, system_2, system_3)); /// # schedule.run(&mut world); /// ``` - pub fn sort: Ord> + 'w>( + pub fn sort( self, ) -> QuerySortedIter< 'w, @@ -495,7 +495,10 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIter<'w, 's, D, F> { D, F, impl ExactSizeIterator + DoubleEndedIterator + FusedIterator + 'w, - > { + > + where + for<'lw> L::Item<'lw>: Ord, + { self.sort_impl::(|keyed_query| keyed_query.sort()) } @@ -541,7 +544,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIter<'w, 's, D, F> { /// # schedule.add_systems((system_1)); /// # schedule.run(&mut world); /// ``` - pub fn sort_unstable: Ord> + 'w>( + pub fn sort_unstable( self, ) -> QuerySortedIter< 'w, @@ -549,7 +552,10 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIter<'w, 's, D, F> { D, F, impl ExactSizeIterator + DoubleEndedIterator + FusedIterator + 'w, - > { + > + where + for<'lw> L::Item<'lw>: Ord, + { self.sort_impl::(|keyed_query| keyed_query.sort_unstable()) } @@ -604,7 +610,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIter<'w, 's, D, F> { /// ``` pub fn sort_by( self, - mut compare: impl FnMut(&L::Item<'w>, &L::Item<'w>) -> Ordering, + mut compare: impl FnMut(&L::Item<'_>, &L::Item<'_>) -> Ordering, ) -> QuerySortedIter< 'w, 's, @@ -636,7 +642,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIter<'w, 's, D, F> { /// This will panic if `next` has been called on `QueryIter` before, unless the underlying `Query` is empty. pub fn sort_unstable_by( self, - mut compare: impl FnMut(&L::Item<'w>, &L::Item<'w>) -> Ordering, + mut compare: impl FnMut(&L::Item<'_>, &L::Item<'_>) -> Ordering, ) -> QuerySortedIter< 'w, 's, @@ -688,7 +694,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIter<'w, 's, D, F> { /// #[derive(Component)] /// struct AvailableMarker; /// - /// #[derive(Component, PartialEq, Eq, PartialOrd, Ord)] + /// #[derive(Component, PartialEq, Eq, PartialOrd, Ord, Copy, Clone)] /// enum Rarity { /// Common, /// Rare, @@ -716,7 +722,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIter<'w, 's, D, F> { /// .sort_by_key::(|entity_ref| { /// ( /// entity_ref.contains::(), - /// entity_ref.get::() + /// entity_ref.get::().copied() /// ) /// }) /// .rev() @@ -728,7 +734,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIter<'w, 's, D, F> { /// ``` pub fn sort_by_key( self, - mut f: impl FnMut(&L::Item<'w>) -> K, + mut f: impl FnMut(&L::Item<'_>) -> K, ) -> QuerySortedIter< 'w, 's, @@ -761,7 +767,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIter<'w, 's, D, F> { /// This will panic if `next` has been called on `QueryIter` before, unless the underlying `Query` is empty. pub fn sort_unstable_by_key( self, - mut f: impl FnMut(&L::Item<'w>) -> K, + mut f: impl FnMut(&L::Item<'_>) -> K, ) -> QuerySortedIter< 'w, 's, @@ -796,7 +802,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIter<'w, 's, D, F> { /// This will panic if `next` has been called on `QueryIter` before, unless the underlying `Query` is empty. pub fn sort_by_cached_key( self, - mut f: impl FnMut(&L::Item<'w>) -> K, + mut f: impl FnMut(&L::Item<'_>) -> K, ) -> QuerySortedIter< 'w, 's, @@ -826,7 +832,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIter<'w, 's, D, F> { /// This will panic if `next` has been called on `QueryIter` before, unless the underlying `Query` is empty. fn sort_impl( self, - f: impl FnOnce(&mut Vec<(L::Item<'w>, NeutralOrd)>), + f: impl FnOnce(&mut Vec<(L::Item<'_>, NeutralOrd)>), ) -> QuerySortedIter< 'w, 's, @@ -1333,7 +1339,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter, I: Iterator> impl ExactSizeIterator + DoubleEndedIterator + FusedIterator + 'w, > where - L::Item<'w>: Ord, + for<'lw> L::Item<'lw>: Ord, { self.sort_impl::(|keyed_query| keyed_query.sort()) } @@ -1391,7 +1397,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter, I: Iterator> impl ExactSizeIterator + DoubleEndedIterator + FusedIterator + 'w, > where - L::Item<'w>: Ord, + for<'lw> L::Item<'lw>: Ord, { self.sort_impl::(|keyed_query| keyed_query.sort_unstable()) } @@ -1448,7 +1454,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter, I: Iterator> /// ``` pub fn sort_by( self, - mut compare: impl FnMut(&L::Item<'w>, &L::Item<'w>) -> Ordering, + mut compare: impl FnMut(&L::Item<'_>, &L::Item<'_>) -> Ordering, ) -> QuerySortedManyIter< 'w, 's, @@ -1479,7 +1485,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter, I: Iterator> /// called on [`QueryManyIter`] before. pub fn sort_unstable_by( self, - mut compare: impl FnMut(&L::Item<'w>, &L::Item<'w>) -> Ordering, + mut compare: impl FnMut(&L::Item<'_>, &L::Item<'_>) -> Ordering, ) -> QuerySortedManyIter< 'w, 's, @@ -1531,7 +1537,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter, I: Iterator> /// #[derive(Component)] /// struct AvailableMarker; /// - /// #[derive(Component, PartialEq, Eq, PartialOrd, Ord)] + /// #[derive(Component, PartialEq, Eq, PartialOrd, Ord, Copy, Clone)] /// enum Rarity { /// Common, /// Rare, @@ -1561,7 +1567,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter, I: Iterator> /// .sort_by_key::(|entity_ref| { /// ( /// entity_ref.contains::(), - /// entity_ref.get::() + // entity_ref.get::().copied() /// ) /// }) /// .rev() @@ -1573,7 +1579,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter, I: Iterator> /// ``` pub fn sort_by_key( self, - mut f: impl FnMut(&L::Item<'w>) -> K, + mut f: impl FnMut(&L::Item<'_>) -> K, ) -> QuerySortedManyIter< 'w, 's, @@ -1605,7 +1611,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter, I: Iterator> /// called on [`QueryManyIter`] before. pub fn sort_unstable_by_key( self, - mut f: impl FnMut(&L::Item<'w>) -> K, + mut f: impl FnMut(&L::Item<'_>) -> K, ) -> QuerySortedManyIter< 'w, 's, @@ -1639,7 +1645,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter, I: Iterator> /// called on [`QueryManyIter`] before. pub fn sort_by_cached_key( self, - mut f: impl FnMut(&L::Item<'w>) -> K, + mut f: impl FnMut(&L::Item<'_>) -> K, ) -> QuerySortedManyIter< 'w, 's, @@ -1668,7 +1674,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter, I: Iterator> /// called on [`QueryManyIter`] before. fn sort_impl( self, - f: impl FnOnce(&mut Vec<(L::Item<'w>, NeutralOrd)>), + f: impl FnOnce(&mut Vec<(L::Item<'_>, NeutralOrd)>), ) -> QuerySortedManyIter< 'w, 's, @@ -2898,13 +2904,13 @@ mod tests { { let mut query = query_state .iter_many_mut(&mut world, [id, id]) - .sort_by::<&C>(Ord::cmp); + .sort_by::<&C>(|l, r| Ord::cmp(l, r)); while query.fetch_next().is_some() {} } { let mut query = query_state .iter_many_mut(&mut world, [id, id]) - .sort_unstable_by::<&C>(Ord::cmp); + .sort_unstable_by::<&C>(|l, r| Ord::cmp(l, r)); while query.fetch_next().is_some() {} } {