diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index 9318ebb40b09b..34cb193e51e6c 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -314,8 +314,10 @@ language_item_table! { ControlFlowBreak, sym::Break, cf_break_variant, Target::Variant, GenericRequirement::None; IntoFutureIntoFuture, sym::into_future, into_future_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None; - IntoIterIntoIter, sym::into_iter, into_iter_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None; - IteratorNext, sym::next, next_fn, Target::Method(MethodKind::Trait { body: false}), GenericRequirement::None; + + IntoIterIntoIter, sym::into_iter, into_iter_fn, Target::Method(MethodKind::Inherent), GenericRequirement::None; + IteratorNext, sym::next, next_fn, Target::Method(MethodKind::Inherent), GenericRequirement::None; + PinNewUnchecked, sym::new_unchecked, new_unchecked_fn, Target::Method(MethodKind::Inherent), GenericRequirement::None; diff --git a/library/alloc/src/collections/vec_deque/iter.rs b/library/alloc/src/collections/vec_deque/iter.rs index e8290809276fb..efa0d3ea9a651 100644 --- a/library/alloc/src/collections/vec_deque/iter.rs +++ b/library/alloc/src/collections/vec_deque/iter.rs @@ -1,5 +1,5 @@ use core::fmt; -use core::iter::{FusedIterator, TrustedLen, TrustedRandomAccess, TrustedRandomAccessNoCoerce}; +use core::iter::{FusedIterator, TrustedLen, TrustedRandomAccess}; use core::mem::MaybeUninit; use core::ops::Try; @@ -205,10 +205,12 @@ unsafe impl TrustedLen for Iter<'_, T> {} #[doc(hidden)] #[unstable(feature = "trusted_random_access", issue = "none")] -unsafe impl TrustedRandomAccess for Iter<'_, T> {} +unsafe impl TrustedRandomAccess for Iter<'_, T> { + fn cleanup_front(&mut self, num: usize) { + let _ = self.advance_by(num); + } -#[doc(hidden)] -#[unstable(feature = "trusted_random_access", issue = "none")] -unsafe impl TrustedRandomAccessNoCoerce for Iter<'_, T> { - const MAY_HAVE_SIDE_EFFECT: bool = false; + fn cleanup_back(&mut self, num: usize) { + let _ = self.advance_back_by(num); + } } diff --git a/library/alloc/src/collections/vec_deque/iter_mut.rs b/library/alloc/src/collections/vec_deque/iter_mut.rs index ee2df0d516012..9de8d1643335e 100644 --- a/library/alloc/src/collections/vec_deque/iter_mut.rs +++ b/library/alloc/src/collections/vec_deque/iter_mut.rs @@ -1,5 +1,5 @@ use core::fmt; -use core::iter::{FusedIterator, TrustedLen, TrustedRandomAccess, TrustedRandomAccessNoCoerce}; +use core::iter::{FusedIterator, TrustedLen, TrustedRandomAccess}; use core::marker::PhantomData; use super::{count, wrap_index, RingSlices}; @@ -154,10 +154,12 @@ unsafe impl TrustedLen for IterMut<'_, T> {} #[doc(hidden)] #[unstable(feature = "trusted_random_access", issue = "none")] -unsafe impl TrustedRandomAccess for IterMut<'_, T> {} +unsafe impl TrustedRandomAccess for IterMut<'_, T> { + fn cleanup_front(&mut self, num: usize) { + let _ = self.advance_by(num); + } -#[doc(hidden)] -#[unstable(feature = "trusted_random_access", issue = "none")] -unsafe impl TrustedRandomAccessNoCoerce for IterMut<'_, T> { - const MAY_HAVE_SIDE_EFFECT: bool = false; + fn cleanup_back(&mut self, num: usize) { + let _ = self.advance_back_by(num); + } } diff --git a/library/alloc/src/vec/in_place_collect.rs b/library/alloc/src/vec/in_place_collect.rs index 282af8cc33fdd..a7f251d12245d 100644 --- a/library/alloc/src/vec/in_place_collect.rs +++ b/library/alloc/src/vec/in_place_collect.rs @@ -60,7 +60,7 @@ //! # O(1) collect //! //! The main iteration itself is further specialized when the iterator implements -//! [`TrustedRandomAccessNoCoerce`] to let the optimizer see that it is a counted loop with a single +//! [`TrustedRandomAccess`] to let the optimizer see that it is a counted loop with a single //! [induction variable]. This can turn some iterators into a noop, i.e. it reduces them from O(n) to //! O(1). This particular optimization is quite fickle and doesn't always work, see [#79308] //! @@ -70,7 +70,7 @@ //! Since unchecked accesses through that trait do not advance the read pointer of `IntoIter` //! this would interact unsoundly with the requirements about dropping the tail described above. //! But since the normal `Drop` implementation of `IntoIter` would suffer from the same problem it -//! is only correct for `TrustedRandomAccessNoCoerce` to be implemented when the items don't +//! is only correct for `TrustedRandomAccess` to be implemented when the items don't //! have a destructor. Thus that implicit requirement also makes the specialization safe to use for //! in-place collection. //! Note that this safety concern is about the correctness of `impl Drop for IntoIter`, @@ -134,7 +134,7 @@ //! } //! vec.truncate(write_idx); //! ``` -use core::iter::{InPlaceIterable, SourceIter, TrustedRandomAccessNoCoerce}; +use core::iter::{InPlaceIterable, SourceIter, TrustedRandomAccess}; use core::mem::{self, ManuallyDrop}; use core::ptr::{self}; @@ -195,7 +195,7 @@ where // itself once IntoIter goes out of scope. // If the drop panics then we also leak any elements collected into dst_buf. // - // Note: This access to the source wouldn't be allowed by the TrustedRandomIteratorNoCoerce + // Note: This access to the source wouldn't be allowed by the TrustedRandomIterator // contract (used by SpecInPlaceCollect below). But see the "O(1) collect" section in the // module documenttation why this is ok anyway. src.forget_allocation_drop_remaining(); @@ -230,7 +230,7 @@ trait SpecInPlaceCollect: Iterator { /// collected. `end` is the last writable element of the allocation and used for bounds checks. /// /// This method is specialized and one of its implementations makes use of - /// `Iterator::__iterator_get_unchecked` calls with a `TrustedRandomAccessNoCoerce` bound + /// `Iterator::__iterator_get_unchecked` calls with a `TrustedRandomAccess` bound /// on `I` which means the caller of this method must take the safety conditions /// of that trait into consideration. fn collect_in_place(&mut self, dst: *mut T, end: *const T) -> usize; @@ -256,7 +256,7 @@ where impl SpecInPlaceCollect for I where - I: Iterator + TrustedRandomAccessNoCoerce, + I: Iterator + TrustedRandomAccess, { #[inline] fn collect_in_place(&mut self, dst_buf: *mut T, end: *const T) -> usize { @@ -275,6 +275,9 @@ where drop_guard.dst = dst.add(1); } } + // FIXME: this should run the TrustedRandomAccess cleanup code. + // currently not running it is ok because IntoIter only implements TRA for Copy types + // but if we relax that the cleanup will be needed mem::forget(drop_guard); len } diff --git a/library/alloc/src/vec/into_iter.rs b/library/alloc/src/vec/into_iter.rs index 03c532bb69769..82ccfeb4eebfd 100644 --- a/library/alloc/src/vec/into_iter.rs +++ b/library/alloc/src/vec/into_iter.rs @@ -5,7 +5,8 @@ use crate::raw_vec::RawVec; use core::fmt; use core::intrinsics::arith_offset; use core::iter::{ - FusedIterator, InPlaceIterable, SourceIter, TrustedLen, TrustedRandomAccessNoCoerce, + FusedIterator, InPlaceIterable, SourceIter, TrustedLen, TrustedRandomAccess, + TrustedRandomAccessNeedsCleanup, }; use core::marker::PhantomData; use core::mem::{self, ManuallyDrop}; @@ -200,7 +201,7 @@ impl Iterator for IntoIter { #[doc(hidden)] unsafe fn __iterator_get_unchecked(&mut self, i: usize) -> Self::Item where - Self: TrustedRandomAccessNoCoerce, + Self: TrustedRandomAccess, { // SAFETY: the caller must guarantee that `i` is in bounds of the // `Vec`, so `i` cannot overflow an `isize`, and the `self.ptr.add(i)` @@ -284,15 +285,39 @@ impl NonDrop for T {} #[doc(hidden)] #[unstable(issue = "none", feature = "std_internals")] -// TrustedRandomAccess (without NoCoerce) must not be implemented because -// subtypes/supertypes of `T` might not be `NonDrop` -unsafe impl TrustedRandomAccessNoCoerce for IntoIter +unsafe impl TrustedRandomAccess for IntoIter where T: NonDrop, { - const MAY_HAVE_SIDE_EFFECT: bool = false; + fn cleanup_front(&mut self, num: usize) { + if mem::size_of::() == 0 { + // SAFETY: due to unchecked casts of unsigned amounts to signed offsets the wraparound + // effectively results in unsigned pointers representing positions 0..usize::MAX, + // which is valid for ZSTs. + self.ptr = unsafe { arith_offset(self.ptr as *const i8, num as isize) as *mut T } + } else { + // SAFETY: the caller must guarantee that `num` is in bounds + self.ptr = unsafe { self.ptr.add(num) }; + } + } + + fn cleanup_back(&mut self, num: usize) { + if mem::size_of::() == 0 { + // SAFETY: same as above + self.end = unsafe { + arith_offset(self.end as *const i8, num.wrapping_neg() as isize) as *mut T + } + } else { + // SAFETY: same as above + self.end = unsafe { self.end.offset(num.wrapping_neg() as isize) }; + } + } } +#[doc(hidden)] +#[unstable(issue = "none", feature = "std_internals")] +unsafe impl TrustedRandomAccessNeedsCleanup for IntoIter where T: NonDrop {} + #[cfg(not(no_global_oom_handling))] #[stable(feature = "vec_into_iter_clone", since = "1.8.0")] impl Clone for IntoIter { diff --git a/library/core/benches/iter.rs b/library/core/benches/iter.rs index 24257ba98785d..af629794dcd78 100644 --- a/library/core/benches/iter.rs +++ b/library/core/benches/iter.rs @@ -146,12 +146,23 @@ fn bench_for_each_chain_ref_fold(b: &mut Bencher) { /// Helper to benchmark `sum` for iterators taken by value which /// can optimize `fold`, and by reference which cannot. macro_rules! bench_sums { - ($bench_sum:ident, $bench_ref_sum:ident, $iter:expr) => { + ($bench_sum:ident, $bench_sum_loop:ident, $bench_ref_sum:ident, $iter:expr) => { #[bench] fn $bench_sum(b: &mut Bencher) { b.iter(|| -> i64 { $iter.map(black_box).sum() }); } + #[bench] + fn $bench_sum_loop(b: &mut Bencher) { + b.iter(|| -> i64 { + let mut acc = 0; + for i in $iter.map(black_box) { + acc += i; + } + acc + }); + } + #[bench] fn $bench_ref_sum(b: &mut Bencher) { b.iter(|| -> i64 { $iter.map(black_box).by_ref().sum() }); @@ -161,138 +172,161 @@ macro_rules! bench_sums { bench_sums! { bench_flat_map_sum, + bench_flat_map_sum_loop, bench_flat_map_ref_sum, (0i64..1000).flat_map(|x| x..x+1000) } bench_sums! { bench_flat_map_chain_sum, + bench_flat_map_chain_sum_loop, bench_flat_map_chain_ref_sum, (0i64..1000000).flat_map(|x| once(x).chain(once(x))) } bench_sums! { bench_enumerate_sum, + bench_enumerate_sum_loop, bench_enumerate_ref_sum, (0i64..1000000).enumerate().map(|(i, x)| x * i as i64) } bench_sums! { bench_enumerate_chain_sum, + bench_enumerate_chain_sum_loop, bench_enumerate_chain_ref_sum, (0i64..1000000).chain(0..1000000).enumerate().map(|(i, x)| x * i as i64) } bench_sums! { bench_filter_sum, + bench_filter_sum_loop, bench_filter_ref_sum, (0i64..1000000).filter(|x| x % 3 == 0) } bench_sums! { bench_filter_chain_sum, + bench_filter_chain_sum_loop, bench_filter_chain_ref_sum, (0i64..1000000).chain(0..1000000).filter(|x| x % 3 == 0) } bench_sums! { bench_filter_map_sum, + bench_filter_map_sum_loop, bench_filter_map_ref_sum, (0i64..1000000).filter_map(|x| x.checked_mul(x)) } bench_sums! { bench_filter_map_chain_sum, + bench_filter_map_chain_sum_loop, bench_filter_map_chain_ref_sum, (0i64..1000000).chain(0..1000000).filter_map(|x| x.checked_mul(x)) } bench_sums! { bench_fuse_sum, + bench_fuse_sum_loop, bench_fuse_ref_sum, (0i64..1000000).fuse() } bench_sums! { bench_fuse_chain_sum, + bench_fuse_chain_sum_loop, bench_fuse_chain_ref_sum, (0i64..1000000).chain(0..1000000).fuse() } bench_sums! { bench_inspect_sum, + bench_inspect_sum_loop, bench_inspect_ref_sum, (0i64..1000000).inspect(|_| {}) } bench_sums! { bench_inspect_chain_sum, + bench_inspect_chain_sum_loop, bench_inspect_chain_ref_sum, (0i64..1000000).chain(0..1000000).inspect(|_| {}) } bench_sums! { bench_peekable_sum, + bench_peekable_sum_loop, bench_peekable_ref_sum, (0i64..1000000).peekable() } bench_sums! { bench_peekable_chain_sum, + bench_peekable_chain_sum_loop, bench_peekable_chain_ref_sum, (0i64..1000000).chain(0..1000000).peekable() } bench_sums! { bench_skip_sum, + bench_skip_sum_loop, bench_skip_ref_sum, (0i64..1000000).skip(1000) } bench_sums! { bench_skip_chain_sum, + bench_skip_chain_sum_loop, bench_skip_chain_ref_sum, (0i64..1000000).chain(0..1000000).skip(1000) } bench_sums! { bench_skip_while_sum, + bench_skip_while_sum_loop, bench_skip_while_ref_sum, (0i64..1000000).skip_while(|&x| x < 1000) } bench_sums! { bench_skip_while_chain_sum, + bench_skip_while_chain_sum_loop, bench_skip_while_chain_ref_sum, (0i64..1000000).chain(0..1000000).skip_while(|&x| x < 1000) } bench_sums! { bench_take_while_chain_sum, + bench_take_while_chain_sum_loop, bench_take_while_chain_ref_sum, (0i64..1000000).chain(1000000..).take_while(|&x| x < 1111111) } bench_sums! { bench_cycle_take_sum, + bench_cycle_take_sum_loop, bench_cycle_take_ref_sum, (0..10000).cycle().take(1000000) } bench_sums! { bench_cycle_skip_take_sum, + bench_cycle_skip_take_sum_loop, bench_cycle_skip_take_ref_sum, (0..100000).cycle().skip(1000000).take(1000000) } bench_sums! { bench_cycle_take_skip_sum, + bench_cycle_take_skip_sum_loop, bench_cycle_take_skip_ref_sum, (0..100000).cycle().take(1000000).skip(100000) } bench_sums! { bench_skip_cycle_skip_zip_add_sum, + bench_skip_cycle_skip_zip_add_sum_loop, bench_skip_cycle_skip_zip_add_ref_sum, (0..100000).skip(100).cycle().skip(100) .zip((0..100000).cycle().skip(10)) @@ -367,3 +401,47 @@ fn bench_partial_cmp(b: &mut Bencher) { fn bench_lt(b: &mut Bencher) { b.iter(|| (0..100000).map(black_box).lt((0..100000).map(black_box))) } + +#[bench] +fn bench_desugar(b: &mut Bencher) { + let vec1: Vec<_> = (0usize..100000).collect(); + let vec2 = black_box(vec1.clone()); + b.iter(|| { + let iter = vec1 + .iter() + .enumerate() + .map(|(idx, e)| idx.wrapping_add(*e)) + .zip(vec2.iter()) + .map(|(a, b)| a.wrapping_add(*b)) + .fuse(); + let mut acc = 0; + for i in iter { + acc += i; + } + acc + }) +} + +#[bench] +fn bench_trusted_random_access_adapters(b: &mut Bencher) { + let vec1: Vec<_> = (0usize..100000).collect(); + let vec2 = black_box(vec1.clone()); + b.iter(|| { + let mut iter = vec1 + .iter() + .copied() + .enumerate() + .map(|(idx, e)| idx.wrapping_add(e)) + .zip(vec2.iter().copied()) + .map(|(a, b)| a.wrapping_add(b)) + .fuse(); + let mut acc = 0; + let size = iter.size(); + for i in 0..size { + // SAFETY: TRA requirements are satisfied by 0..size iteration and then dropping the + // iterator. + acc += unsafe { iter.__iterator_get_unchecked(i) }; + } + acc + }) +} diff --git a/library/core/benches/lib.rs b/library/core/benches/lib.rs index d5e1ec083f95d..f1f1ae6e4635d 100644 --- a/library/core/benches/lib.rs +++ b/library/core/benches/lib.rs @@ -3,6 +3,7 @@ #![feature(flt2dec)] #![feature(int_log)] #![feature(test)] +#![feature(trusted_random_access)] extern crate test; diff --git a/library/core/src/iter/adapters/cloned.rs b/library/core/src/iter/adapters/cloned.rs index 71a5a4ea831ff..b0c46b7eb5182 100644 --- a/library/core/src/iter/adapters/cloned.rs +++ b/library/core/src/iter/adapters/cloned.rs @@ -1,5 +1,7 @@ -use crate::iter::adapters::{ - zip::try_get_unchecked, TrustedRandomAccess, TrustedRandomAccessNoCoerce, +use crate::iter::traits::trusted_random_access::try_get_unchecked; +use crate::iter::traits::{ + TrustedRandomAccess, TrustedRandomAccessNeedsCleanup, TrustedRandomAccessNeedsForwardSetup, + TrustedRandomAccessNeedsReverseSetup, }; use crate::iter::{FusedIterator, TrustedLen}; use crate::ops::Try; @@ -63,7 +65,7 @@ where #[doc(hidden)] unsafe fn __iterator_get_unchecked(&mut self, idx: usize) -> T where - Self: TrustedRandomAccessNoCoerce, + Self: TrustedRandomAccess, { // SAFETY: the caller must uphold the contract for // `Iterator::__iterator_get_unchecked`. @@ -123,15 +125,38 @@ where #[doc(hidden)] #[unstable(feature = "trusted_random_access", issue = "none")] -unsafe impl TrustedRandomAccess for Cloned where I: TrustedRandomAccess {} +unsafe impl TrustedRandomAccess for Cloned +where + I: TrustedRandomAccess, +{ + #[inline] + fn cleanup_front(&mut self, num: usize) { + self.it.cleanup_front(num); + } + + #[inline] + fn cleanup_back(&mut self, num: usize) { + self.it.cleanup_back(num); + } +} #[doc(hidden)] #[unstable(feature = "trusted_random_access", issue = "none")] -unsafe impl TrustedRandomAccessNoCoerce for Cloned -where - I: TrustedRandomAccessNoCoerce, +unsafe impl TrustedRandomAccessNeedsCleanup for Cloned where I: TrustedRandomAccessNeedsCleanup +{} + +#[doc(hidden)] +#[unstable(feature = "trusted_random_access", issue = "none")] +unsafe impl TrustedRandomAccessNeedsForwardSetup for Cloned where + I: TrustedRandomAccessNeedsForwardSetup +{ +} + +#[doc(hidden)] +#[unstable(feature = "trusted_random_access", issue = "none")] +unsafe impl TrustedRandomAccessNeedsReverseSetup for Cloned where + I: TrustedRandomAccessNeedsReverseSetup { - const MAY_HAVE_SIDE_EFFECT: bool = true; } #[unstable(feature = "trusted_len", issue = "37572")] diff --git a/library/core/src/iter/adapters/copied.rs b/library/core/src/iter/adapters/copied.rs index e5f2886dcafad..49cb150bd6642 100644 --- a/library/core/src/iter/adapters/copied.rs +++ b/library/core/src/iter/adapters/copied.rs @@ -1,5 +1,7 @@ -use crate::iter::adapters::{ - zip::try_get_unchecked, TrustedRandomAccess, TrustedRandomAccessNoCoerce, +use crate::iter::traits::trusted_random_access::try_get_unchecked; +use crate::iter::traits::{ + TrustedRandomAccess, TrustedRandomAccessNeedsCleanup, TrustedRandomAccessNeedsForwardSetup, + TrustedRandomAccessNeedsReverseSetup, }; use crate::iter::{FusedIterator, TrustedLen}; use crate::ops::Try; @@ -84,7 +86,7 @@ where #[doc(hidden)] unsafe fn __iterator_get_unchecked(&mut self, idx: usize) -> T where - Self: TrustedRandomAccessNoCoerce, + Self: TrustedRandomAccess, { // SAFETY: the caller must uphold the contract for // `Iterator::__iterator_get_unchecked`. @@ -149,15 +151,38 @@ where #[doc(hidden)] #[unstable(feature = "trusted_random_access", issue = "none")] -unsafe impl TrustedRandomAccess for Copied where I: TrustedRandomAccess {} +unsafe impl TrustedRandomAccess for Copied +where + I: TrustedRandomAccess, +{ + #[inline] + fn cleanup_front(&mut self, num: usize) { + self.it.cleanup_front(num); + } + + #[inline] + fn cleanup_back(&mut self, num: usize) { + self.it.cleanup_back(num); + } +} #[doc(hidden)] #[unstable(feature = "trusted_random_access", issue = "none")] -unsafe impl TrustedRandomAccessNoCoerce for Copied -where - I: TrustedRandomAccessNoCoerce, +unsafe impl TrustedRandomAccessNeedsCleanup for Copied where I: TrustedRandomAccessNeedsCleanup +{} + +#[doc(hidden)] +#[unstable(feature = "trusted_random_access", issue = "none")] +unsafe impl TrustedRandomAccessNeedsForwardSetup for Copied where + I: TrustedRandomAccessNeedsForwardSetup +{ +} + +#[doc(hidden)] +#[unstable(feature = "trusted_random_access", issue = "none")] +unsafe impl TrustedRandomAccessNeedsReverseSetup for Copied where + I: TrustedRandomAccessNeedsReverseSetup { - const MAY_HAVE_SIDE_EFFECT: bool = I::MAY_HAVE_SIDE_EFFECT; } #[stable(feature = "iter_copied", since = "1.36.0")] diff --git a/library/core/src/iter/adapters/enumerate.rs b/library/core/src/iter/adapters/enumerate.rs index 84e4618844a61..5f0e3b968e829 100644 --- a/library/core/src/iter/adapters/enumerate.rs +++ b/library/core/src/iter/adapters/enumerate.rs @@ -1,5 +1,8 @@ -use crate::iter::adapters::{ - zip::try_get_unchecked, SourceIter, TrustedRandomAccess, TrustedRandomAccessNoCoerce, +use crate::iter::adapters::SourceIter; +use crate::iter::traits::trusted_random_access::try_get_unchecked; +use crate::iter::traits::{ + TrustedRandomAccess, TrustedRandomAccessNeedsCleanup, TrustedRandomAccessNeedsForwardSetup, + TrustedRandomAccessNeedsReverseSetup, }; use crate::iter::{FusedIterator, InPlaceIterable, TrustedLen}; use crate::ops::Try; @@ -129,9 +132,10 @@ where #[rustc_inherit_overflow_checks] #[doc(hidden)] + #[inline] unsafe fn __iterator_get_unchecked(&mut self, idx: usize) -> ::Item where - Self: TrustedRandomAccessNoCoerce, + Self: TrustedRandomAccess, { // SAFETY: the caller must uphold the contract for // `Iterator::__iterator_get_unchecked`. @@ -231,15 +235,40 @@ where #[doc(hidden)] #[unstable(feature = "trusted_random_access", issue = "none")] -unsafe impl TrustedRandomAccess for Enumerate where I: TrustedRandomAccess {} +unsafe impl TrustedRandomAccess for Enumerate +where + I: TrustedRandomAccess, +{ + #[inline] + fn cleanup_front(&mut self, num: usize) { + self.iter.cleanup_front(num); + } + + #[inline] + fn cleanup_back(&mut self, num: usize) { + self.iter.cleanup_back(num); + } +} #[doc(hidden)] #[unstable(feature = "trusted_random_access", issue = "none")] -unsafe impl TrustedRandomAccessNoCoerce for Enumerate -where - I: TrustedRandomAccessNoCoerce, +unsafe impl TrustedRandomAccessNeedsCleanup for Enumerate where + I: TrustedRandomAccessNeedsCleanup +{ +} + +#[doc(hidden)] +#[unstable(feature = "trusted_random_access", issue = "none")] +unsafe impl TrustedRandomAccessNeedsForwardSetup for Enumerate where + I: TrustedRandomAccessNeedsForwardSetup +{ +} + +#[doc(hidden)] +#[unstable(feature = "trusted_random_access", issue = "none")] +unsafe impl TrustedRandomAccessNeedsReverseSetup for Enumerate where + I: TrustedRandomAccessNeedsReverseSetup { - const MAY_HAVE_SIDE_EFFECT: bool = I::MAY_HAVE_SIDE_EFFECT; } #[stable(feature = "fused", since = "1.26.0")] diff --git a/library/core/src/iter/adapters/fuse.rs b/library/core/src/iter/adapters/fuse.rs index fbf752c6f2024..4d48937273e65 100644 --- a/library/core/src/iter/adapters/fuse.rs +++ b/library/core/src/iter/adapters/fuse.rs @@ -1,8 +1,9 @@ use crate::intrinsics; -use crate::iter::adapters::zip::try_get_unchecked; +use crate::iter::traits::trusted_random_access::try_get_unchecked; use crate::iter::{ DoubleEndedIterator, ExactSizeIterator, FusedIterator, TrustedLen, TrustedRandomAccess, - TrustedRandomAccessNoCoerce, + TrustedRandomAccessNeedsCleanup, TrustedRandomAccessNeedsForwardSetup, + TrustedRandomAccessNeedsReverseSetup, }; use crate::ops::Try; @@ -132,7 +133,7 @@ where #[doc(hidden)] unsafe fn __iterator_get_unchecked(&mut self, idx: usize) -> Self::Item where - Self: TrustedRandomAccessNoCoerce, + Self: TrustedRandomAccess, { match self.iter { // SAFETY: the caller must uphold the contract for @@ -222,15 +223,41 @@ unsafe impl TrustedLen for Fuse where I: TrustedLen {} // // This is safe to implement as `Fuse` is just forwarding these to the wrapped iterator `I`, which // preserves these properties. -unsafe impl TrustedRandomAccess for Fuse where I: TrustedRandomAccess {} +unsafe impl TrustedRandomAccess for Fuse +where + I: TrustedRandomAccess, +{ + #[inline] + fn cleanup_front(&mut self, num: usize) { + if let Some(iter) = self.iter.as_mut() { + iter.cleanup_front(num); + } + } + + #[inline] + fn cleanup_back(&mut self, num: usize) { + if let Some(iter) = self.iter.as_mut() { + iter.cleanup_back(num); + } + } +} #[doc(hidden)] #[unstable(feature = "trusted_random_access", issue = "none")] -unsafe impl TrustedRandomAccessNoCoerce for Fuse -where - I: TrustedRandomAccessNoCoerce, +unsafe impl TrustedRandomAccessNeedsCleanup for Fuse where I: TrustedRandomAccessNeedsCleanup {} + +#[doc(hidden)] +#[unstable(feature = "trusted_random_access", issue = "none")] +unsafe impl TrustedRandomAccessNeedsForwardSetup for Fuse where + I: TrustedRandomAccessNeedsForwardSetup +{ +} + +#[doc(hidden)] +#[unstable(feature = "trusted_random_access", issue = "none")] +unsafe impl TrustedRandomAccessNeedsReverseSetup for Fuse where + I: TrustedRandomAccessNeedsReverseSetup { - const MAY_HAVE_SIDE_EFFECT: bool = I::MAY_HAVE_SIDE_EFFECT; } /// Fuse specialization trait diff --git a/library/core/src/iter/adapters/map.rs b/library/core/src/iter/adapters/map.rs index 4b03449972c9a..883afd1ee65a2 100644 --- a/library/core/src/iter/adapters/map.rs +++ b/library/core/src/iter/adapters/map.rs @@ -1,6 +1,9 @@ use crate::fmt; -use crate::iter::adapters::{ - zip::try_get_unchecked, SourceIter, TrustedRandomAccess, TrustedRandomAccessNoCoerce, +use crate::iter::adapters::SourceIter; +use crate::iter::traits::trusted_random_access::try_get_unchecked; +use crate::iter::traits::{ + TrustedRandomAccess, TrustedRandomAccessNeedsCleanup, TrustedRandomAccessNeedsForwardSetup, + TrustedRandomAccessNeedsReverseSetup, }; use crate::iter::{FusedIterator, InPlaceIterable, TrustedLen}; use crate::ops::Try; @@ -125,9 +128,10 @@ where } #[doc(hidden)] + #[inline] unsafe fn __iterator_get_unchecked(&mut self, idx: usize) -> B where - Self: TrustedRandomAccessNoCoerce, + Self: TrustedRandomAccess, { // SAFETY: the caller must uphold the contract for // `Iterator::__iterator_get_unchecked`. @@ -189,15 +193,40 @@ where #[doc(hidden)] #[unstable(feature = "trusted_random_access", issue = "none")] -unsafe impl TrustedRandomAccess for Map where I: TrustedRandomAccess {} +unsafe impl TrustedRandomAccess for Map +where + I: TrustedRandomAccess, +{ + #[inline] + fn cleanup_front(&mut self, num: usize) { + self.iter.cleanup_front(num); + } + + #[inline] + fn cleanup_back(&mut self, num: usize) { + self.iter.cleanup_back(num); + } +} #[doc(hidden)] #[unstable(feature = "trusted_random_access", issue = "none")] -unsafe impl TrustedRandomAccessNoCoerce for Map -where - I: TrustedRandomAccessNoCoerce, +unsafe impl TrustedRandomAccessNeedsCleanup for Map where + I: TrustedRandomAccessNeedsCleanup +{ +} + +#[doc(hidden)] +#[unstable(feature = "trusted_random_access", issue = "none")] +unsafe impl TrustedRandomAccessNeedsForwardSetup for Map where + I: TrustedRandomAccessNeedsForwardSetup +{ +} + +#[doc(hidden)] +#[unstable(feature = "trusted_random_access", issue = "none")] +unsafe impl TrustedRandomAccessNeedsReverseSetup for Map where + I: TrustedRandomAccessNeedsReverseSetup { - const MAY_HAVE_SIDE_EFFECT: bool = true; } #[unstable(issue = "none", feature = "inplace_iteration")] diff --git a/library/core/src/iter/adapters/mod.rs b/library/core/src/iter/adapters/mod.rs index 4500b44b7e9af..452dd1df746c5 100644 --- a/library/core/src/iter/adapters/mod.rs +++ b/library/core/src/iter/adapters/mod.rs @@ -52,12 +52,6 @@ pub use self::intersperse::{Intersperse, IntersperseWith}; #[stable(feature = "iter_map_while", since = "1.57.0")] pub use self::map_while::MapWhile; -#[unstable(feature = "trusted_random_access", issue = "none")] -pub use self::zip::TrustedRandomAccess; - -#[unstable(feature = "trusted_random_access", issue = "none")] -pub use self::zip::TrustedRandomAccessNoCoerce; - #[stable(feature = "iter_zip", since = "1.59.0")] pub use self::zip::zip; diff --git a/library/core/src/iter/adapters/zip.rs b/library/core/src/iter/adapters/zip.rs index f50e71da20f16..de34cd2f2e3d5 100644 --- a/library/core/src/iter/adapters/zip.rs +++ b/library/core/src/iter/adapters/zip.rs @@ -1,7 +1,12 @@ use crate::cmp; use crate::fmt::{self, Debug}; +use crate::iter::traits::trusted_random_access::try_get_unchecked; use crate::iter::{DoubleEndedIterator, ExactSizeIterator, FusedIterator, Iterator}; -use crate::iter::{InPlaceIterable, SourceIter, TrustedLen}; +use crate::iter::{ + InPlaceIterable, SourceIter, TrustedLen, TrustedRandomAccess, TrustedRandomAccessNeedsCleanup, + TrustedRandomAccessNeedsForwardSetup, TrustedRandomAccessNeedsReverseSetup, +}; +use crate::ops::Try; /// An iterator that iterates two other iterators simultaneously. /// @@ -13,23 +18,11 @@ use crate::iter::{InPlaceIterable, SourceIter, TrustedLen}; pub struct Zip { a: A, b: B, - // index, len and a_len are only used by the specialized version of zip - index: usize, - len: usize, - a_len: usize, } + impl Zip { pub(in crate::iter) fn new(a: A, b: B) -> Zip { - ZipImpl::new(a, b) - } - fn super_nth(&mut self, mut n: usize) -> Option<(A::Item, B::Item)> { - while let Some(x) = Iterator::next(self) { - if n == 0 { - return Some(x); - } - n -= 1; - } - None + Zip { a, b } } } @@ -68,7 +61,7 @@ where A: IntoIterator, B: IntoIterator, { - ZipImpl::new(a.into_iter(), b.into_iter()) + Zip::new(a.into_iter(), b.into_iter()) } #[stable(feature = "rust1", since = "1.0.0")] @@ -81,28 +74,33 @@ where #[inline] fn next(&mut self) -> Option { - ZipImpl::next(self) + let x = self.a.next()?; + let y = self.b.next()?; + Some((x, y)) } #[inline] fn size_hint(&self) -> (usize, Option) { - ZipImpl::size_hint(self) + self.spec_size_hint() } - #[inline] - fn nth(&mut self, n: usize) -> Option { - ZipImpl::nth(self, n) + fn fold(self, init: T, f: F) -> T + where + Self: Sized, + F: FnMut(T, Self::Item) -> T, + { + self.spec_fold(init, f) } #[inline] #[doc(hidden)] unsafe fn __iterator_get_unchecked(&mut self, idx: usize) -> Self::Item where - Self: TrustedRandomAccessNoCoerce, + Self: TrustedRandomAccess, { - // SAFETY: `ZipImpl::__iterator_get_unchecked` has same safety - // requirements as `Iterator::__iterator_get_unchecked`. - unsafe { ZipImpl::get_unchecked(self, idx) } + // SAFETY: the caller must uphold the contract for + // `Iterator::__iterator_get_unchecked`. + unsafe { (try_get_unchecked(&mut self.a, idx), try_get_unchecked(&mut self.b, idx)) } } } @@ -114,100 +112,75 @@ where { #[inline] fn next_back(&mut self) -> Option<(A::Item, B::Item)> { - ZipImpl::next_back(self) + let a_sz = self.a.len(); + let b_sz = self.b.len(); + if a_sz != b_sz { + // Adjust a, b to equal length + if a_sz > b_sz { + for _ in 0..a_sz - b_sz { + self.a.next_back(); + } + } else { + for _ in 0..b_sz - a_sz { + self.b.next_back(); + } + } + } + match (self.a.next_back(), self.b.next_back()) { + (Some(x), Some(y)) => Some((x, y)), + (None, None) => None, + _ => unreachable!(), + } } } -// Zip specialization trait -#[doc(hidden)] -trait ZipImpl { - type Item; - fn new(a: A, b: B) -> Self; - fn next(&mut self) -> Option; - fn size_hint(&self) -> (usize, Option); - fn nth(&mut self, n: usize) -> Option; - fn next_back(&mut self) -> Option - where - A: DoubleEndedIterator + ExactSizeIterator, - B: DoubleEndedIterator + ExactSizeIterator; - // This has the same safety requirements as `Iterator::__iterator_get_unchecked` - unsafe fn get_unchecked(&mut self, idx: usize) -> ::Item +trait ZipSpec: Iterator { + fn spec_fold(self, init: T, f: F) -> T where - Self: Iterator + TrustedRandomAccessNoCoerce; -} - -// Work around limitations of specialization, requiring `default` impls to be repeated -// in intermediary impls. -macro_rules! zip_impl_general_defaults { - () => { - default fn new(a: A, b: B) -> Self { - Zip { - a, - b, - index: 0, // unused - len: 0, // unused - a_len: 0, // unused - } - } - - #[inline] - default fn next(&mut self) -> Option<(A::Item, B::Item)> { - let x = self.a.next()?; - let y = self.b.next()?; - Some((x, y)) - } + Self: Sized, + F: FnMut(T, Self::Item) -> T; - #[inline] - default fn nth(&mut self, n: usize) -> Option { - self.super_nth(n) - } + fn spec_try_fold(&mut self, init: B, f: F) -> R + where + Self: Sized, + F: FnMut(B, Self::Item) -> R, + R: Try; - #[inline] - default fn next_back(&mut self) -> Option<(A::Item, B::Item)> - where - A: DoubleEndedIterator + ExactSizeIterator, - B: DoubleEndedIterator + ExactSizeIterator, - { - // The function body below only uses `self.a/b.len()` and `self.a/b.next_back()` - // and doesn’t call `next_back` too often, so this implementation is safe in - // the `TrustedRandomAccessNoCoerce` specialization - - let a_sz = self.a.len(); - let b_sz = self.b.len(); - if a_sz != b_sz { - // Adjust a, b to equal length - if a_sz > b_sz { - for _ in 0..a_sz - b_sz { - self.a.next_back(); - } - } else { - for _ in 0..b_sz - a_sz { - self.b.next_back(); - } - } - } - match (self.a.next_back(), self.b.next_back()) { - (Some(x), Some(y)) => Some((x, y)), - (None, None) => None, - _ => unreachable!(), - } - } - }; + fn spec_size_hint(&self) -> (usize, Option); } -// General Zip impl -#[doc(hidden)] -impl ZipImpl for Zip +impl ZipSpec for Zip where A: Iterator, B: Iterator, { - type Item = (A::Item, B::Item); + default fn spec_fold(mut self, init: T, mut f: F) -> T + where + Self: Sized, + F: FnMut(T, Self::Item) -> T, + { + let mut accum = init; + while let Some(x) = self.next() { + accum = f(accum, x); + } + accum + } - zip_impl_general_defaults! {} + default fn spec_try_fold(&mut self, init: T, mut f: F) -> R + where + Self: Sized, + F: FnMut(T, Self::Item) -> R, + R: Try, + { + let mut accum = init; + while let Some(x) = self.next() { + accum = f(accum, x)?; + } + try { accum } + } #[inline] - default fn size_hint(&self) -> (usize, Option) { + default fn spec_size_hint(&self) -> (usize, Option) { let (a_lower, a_upper) = self.a.size_hint(); let (b_lower, b_upper) = self.b.size_hint(); @@ -222,156 +195,55 @@ where (lower, upper) } - - default unsafe fn get_unchecked(&mut self, _idx: usize) -> ::Item - where - Self: TrustedRandomAccessNoCoerce, - { - unreachable!("Always specialized"); - } -} - -#[doc(hidden)] -impl ZipImpl for Zip -where - A: TrustedRandomAccessNoCoerce + Iterator, - B: TrustedRandomAccessNoCoerce + Iterator, -{ - zip_impl_general_defaults! {} - - #[inline] - default fn size_hint(&self) -> (usize, Option) { - let size = cmp::min(self.a.size(), self.b.size()); - (size, Some(size)) - } - - #[inline] - unsafe fn get_unchecked(&mut self, idx: usize) -> ::Item { - let idx = self.index + idx; - // SAFETY: the caller must uphold the contract for - // `Iterator::__iterator_get_unchecked`. - unsafe { (self.a.__iterator_get_unchecked(idx), self.b.__iterator_get_unchecked(idx)) } - } } -#[doc(hidden)] -impl ZipImpl for Zip +impl ZipSpec for Zip where - A: TrustedRandomAccess + Iterator, - B: TrustedRandomAccess + Iterator, + A: Iterator, + B: Iterator, + Self: TrustedRandomAccess, { - fn new(a: A, b: B) -> Self { - let a_len = a.size(); - let len = cmp::min(a_len, b.size()); - Zip { a, b, index: 0, len, a_len } - } - - #[inline] - fn next(&mut self) -> Option<(A::Item, B::Item)> { - if self.index < self.len { - let i = self.index; - // since get_unchecked executes code which can panic we increment the counters beforehand - // so that the same index won't be accessed twice, as required by TrustedRandomAccess - self.index += 1; - // SAFETY: `i` is smaller than `self.len`, thus smaller than `self.a.len()` and `self.b.len()` - unsafe { - Some((self.a.__iterator_get_unchecked(i), self.b.__iterator_get_unchecked(i))) - } - } else if A::MAY_HAVE_SIDE_EFFECT && self.index < self.a_len { - let i = self.index; - // as above, increment before executing code that may panic - self.index += 1; - self.len += 1; - // match the base implementation's potential side effects - // SAFETY: we just checked that `i` < `self.a.len()` - unsafe { - self.a.__iterator_get_unchecked(i); - } - None - } else { - None + fn spec_fold(mut self, init: T, mut f: F) -> T + where + Self: Sized, + F: FnMut(T, Self::Item) -> T, + { + let _ = self.advance_by(0); + let len = self.size(); + let mut accum = init; + for i in 0..len { + // SAFETY: each item is only accessed once and we run the cleanup function afterwards + let x = unsafe { self.__iterator_get_unchecked(i) }; + accum = f(accum, x); } + // FIXME drop-guard or use ForLoopDesugar + self.cleanup_front(len); + accum } - #[inline] - fn size_hint(&self) -> (usize, Option) { - let len = self.len - self.index; - (len, Some(len)) - } - - #[inline] - fn nth(&mut self, n: usize) -> Option { - let delta = cmp::min(n, self.len - self.index); - let end = self.index + delta; - while self.index < end { - let i = self.index; - // since get_unchecked executes code which can panic we increment the counters beforehand - // so that the same index won't be accessed twice, as required by TrustedRandomAccess - self.index += 1; - if A::MAY_HAVE_SIDE_EFFECT { - // SAFETY: the usage of `cmp::min` to calculate `delta` - // ensures that `end` is smaller than or equal to `self.len`, - // so `i` is also smaller than `self.len`. - unsafe { - self.a.__iterator_get_unchecked(i); - } - } - if B::MAY_HAVE_SIDE_EFFECT { - // SAFETY: same as above. - unsafe { - self.b.__iterator_get_unchecked(i); - } - } + fn spec_try_fold(&mut self, init: T, mut f: F) -> R + where + Self: Sized, + F: FnMut(T, Self::Item) -> R, + R: Try, + { + let _ = self.advance_by(0); + let len = self.size(); + let mut accum = init; + for i in 0..len { + // SAFETY: each item is only accessed once and we run the cleanup function afterwards + let x = unsafe { self.__iterator_get_unchecked(i) }; + accum = f(accum, x)?; } - - self.super_nth(n - delta) + // FIXME drop-guard or use ForLoopDesugar + self.cleanup_front(len); + try { accum } } #[inline] - fn next_back(&mut self) -> Option<(A::Item, B::Item)> - where - A: DoubleEndedIterator + ExactSizeIterator, - B: DoubleEndedIterator + ExactSizeIterator, - { - if A::MAY_HAVE_SIDE_EFFECT || B::MAY_HAVE_SIDE_EFFECT { - let sz_a = self.a.size(); - let sz_b = self.b.size(); - // Adjust a, b to equal length, make sure that only the first call - // of `next_back` does this, otherwise we will break the restriction - // on calls to `self.next_back()` after calling `get_unchecked()`. - if sz_a != sz_b { - let sz_a = self.a.size(); - if A::MAY_HAVE_SIDE_EFFECT && sz_a > self.len { - for _ in 0..sz_a - self.len { - // since next_back() may panic we increment the counters beforehand - // to keep Zip's state in sync with the underlying iterator source - self.a_len -= 1; - self.a.next_back(); - } - debug_assert_eq!(self.a_len, self.len); - } - let sz_b = self.b.size(); - if B::MAY_HAVE_SIDE_EFFECT && sz_b > self.len { - for _ in 0..sz_b - self.len { - self.b.next_back(); - } - } - } - } - if self.index < self.len { - // since get_unchecked executes code which can panic we increment the counters beforehand - // so that the same index won't be accessed twice, as required by TrustedRandomAccess - self.len -= 1; - self.a_len -= 1; - let i = self.len; - // SAFETY: `i` is smaller than the previous value of `self.len`, - // which is also smaller than or equal to `self.a.len()` and `self.b.len()` - unsafe { - Some((self.a.__iterator_get_unchecked(i), self.b.__iterator_get_unchecked(i))) - } - } else { - None - } + fn spec_size_hint(&self) -> (usize, Option) { + let size = cmp::min(self.a.size_hint().0, self.b.size_hint().0); + (size, Some(size)) } } @@ -389,19 +261,68 @@ unsafe impl TrustedRandomAccess for Zip where A: TrustedRandomAccess, B: TrustedRandomAccess, +{ + fn cleanup_front(&mut self, num: usize) { + self.a.cleanup_front(num); + self.b.cleanup_front(num); + } + + fn cleanup_back(&mut self, num: usize) { + self.a.cleanup_back(num); + self.b.cleanup_back(num); + } +} + +#[doc(hidden)] +#[unstable(feature = "trusted_random_access", issue = "none")] +unsafe impl TrustedRandomAccessNeedsCleanup for Zip where + A: TrustedRandomAccessNeedsCleanup +{ +} + +#[doc(hidden)] +#[unstable(feature = "trusted_random_access", issue = "none")] +unsafe impl TrustedRandomAccessNeedsCleanup for Zip where + B: TrustedRandomAccessNeedsCleanup +{ +} + +#[doc(hidden)] +#[unstable(feature = "trusted_random_access", issue = "none")] +unsafe impl TrustedRandomAccessNeedsCleanup for Zip +where + A: TrustedRandomAccessNeedsCleanup, + B: TrustedRandomAccessNeedsCleanup, +{ +} + +#[doc(hidden)] +#[unstable(feature = "trusted_random_access", issue = "none")] +unsafe impl TrustedRandomAccessNeedsForwardSetup for Zip where + A: TrustedRandomAccessNeedsForwardSetup +{ +} + +#[doc(hidden)] +#[unstable(feature = "trusted_random_access", issue = "none")] +unsafe impl TrustedRandomAccessNeedsForwardSetup for Zip where + B: TrustedRandomAccessNeedsForwardSetup { } #[doc(hidden)] #[unstable(feature = "trusted_random_access", issue = "none")] -unsafe impl TrustedRandomAccessNoCoerce for Zip +unsafe impl TrustedRandomAccessNeedsForwardSetup for Zip where - A: TrustedRandomAccessNoCoerce, - B: TrustedRandomAccessNoCoerce, + A: TrustedRandomAccessNeedsForwardSetup, + B: TrustedRandomAccessNeedsForwardSetup, { - const MAY_HAVE_SIDE_EFFECT: bool = A::MAY_HAVE_SIDE_EFFECT || B::MAY_HAVE_SIDE_EFFECT; } +#[doc(hidden)] +#[unstable(feature = "trusted_random_access", issue = "none")] +unsafe impl TrustedRandomAccessNeedsReverseSetup for Zip where Self: TrustedRandomAccess {} + #[stable(feature = "fused", since = "1.26.0")] impl FusedIterator for Zip where @@ -455,130 +376,10 @@ impl ZipFmt for Zip { } } -impl ZipFmt - for Zip -{ +impl ZipFmt for Zip { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { // It's *not safe* to call fmt on the contained iterators, since once // we start iterating they're in strange, potentially unsafe, states. f.debug_struct("Zip").finish() } } - -/// An iterator whose items are random-accessible efficiently -/// -/// # Safety -/// -/// The iterator's `size_hint` must be exact and cheap to call. -/// -/// `TrustedRandomAccessNoCoerce::size` may not be overridden. -/// -/// All subtypes and all supertypes of `Self` must also implement `TrustedRandomAccess`. -/// In particular, this means that types with non-invariant parameters usually can not have -/// an impl for `TrustedRandomAccess` that depends on any trait bounds on such parameters, except -/// for bounds that come from the respective struct/enum definition itself, or bounds involving -/// traits that themselves come with a guarantee similar to this one. -/// -/// If `Self: ExactSizeIterator` then `self.len()` must always produce results consistent -/// with `self.size()`. -/// -/// If `Self: Iterator`, then `::__iterator_get_unchecked(&mut self, idx)` -/// must be safe to call provided the following conditions are met. -/// -/// 1. `0 <= idx` and `idx < self.size()`. -/// 2. If `Self: !Clone`, then `self.__iterator_get_unchecked(idx)` is never called with the same -/// index on `self` more than once. -/// 3. After `self.__iterator_get_unchecked(idx)` has been called, then `self.next_back()` will -/// only be called at most `self.size() - idx - 1` times. If `Self: Clone` and `self` is cloned, -/// then this number is calculated for `self` and its clone individually, -/// but `self.next_back()` calls that happened before the cloning count for both `self` and the clone. -/// 4. After `self.__iterator_get_unchecked(idx)` has been called, then only the following methods -/// will be called on `self` or on any new clones of `self`: -/// * `std::clone::Clone::clone` -/// * `std::iter::Iterator::size_hint` -/// * `std::iter::DoubleEndedIterator::next_back` -/// * `std::iter::ExactSizeIterator::len` -/// * `std::iter::Iterator::__iterator_get_unchecked` -/// * `std::iter::TrustedRandomAccessNoCoerce::size` -/// 5. If `T` is a subtype of `Self`, then `self` is allowed to be coerced -/// to `T`. If `self` is coerced to `T` after `self.__iterator_get_unchecked(idx)` has already -/// been called, then no methods except for the ones listed under 4. are allowed to be called -/// on the resulting value of type `T`, either. Multiple such coercion steps are allowed. -/// Regarding 2. and 3., the number of times `__iterator_get_unchecked(idx)` or `next_back()` is -/// called on `self` and the resulting value of type `T` (and on further coercion results with -/// sub-subtypes) are added together and their sums must not exceed the specified bounds. -/// -/// Further, given that these conditions are met, it must guarantee that: -/// -/// * It does not change the value returned from `size_hint` -/// * It must be safe to call the methods listed above on `self` after calling -/// `self.__iterator_get_unchecked(idx)`, assuming that the required traits are implemented. -/// * It must also be safe to drop `self` after calling `self.__iterator_get_unchecked(idx)`. -/// * If `T` is a subtype of `Self`, then it must be safe to coerce `self` to `T`. -// -// FIXME: Clarify interaction with SourceIter/InPlaceIterable. Calling `SourceIter::as_inner` -// after `__iterator_get_unchecked` is supposed to be allowed. -#[doc(hidden)] -#[unstable(feature = "trusted_random_access", issue = "none")] -#[rustc_specialization_trait] -pub unsafe trait TrustedRandomAccess: TrustedRandomAccessNoCoerce {} - -/// Like [`TrustedRandomAccess`] but without any of the requirements / guarantees around -/// coercions to subtypes after `__iterator_get_unchecked` (they aren’t allowed here!), and -/// without the requirement that subtypes / supertypes implement `TrustedRandomAccessNoCoerce`. -/// -/// This trait was created in PR #85874 to fix soundness issue #85873 without performance regressions. -/// It is subject to change as we might want to build a more generally useful (for performance -/// optimizations) and more sophisticated trait or trait hierarchy that replaces or extends -/// [`TrustedRandomAccess`] and `TrustedRandomAccessNoCoerce`. -#[doc(hidden)] -#[unstable(feature = "trusted_random_access", issue = "none")] -#[rustc_specialization_trait] -pub unsafe trait TrustedRandomAccessNoCoerce: Sized { - // Convenience method. - fn size(&self) -> usize - where - Self: Iterator, - { - self.size_hint().0 - } - /// `true` if getting an iterator element may have side effects. - /// Remember to take inner iterators into account. - const MAY_HAVE_SIDE_EFFECT: bool; -} - -/// Like `Iterator::__iterator_get_unchecked`, but doesn't require the compiler to -/// know that `U: TrustedRandomAccess`. -/// -/// ## Safety -/// -/// Same requirements calling `get_unchecked` directly. -#[doc(hidden)] -pub(in crate::iter::adapters) unsafe fn try_get_unchecked(it: &mut I, idx: usize) -> I::Item -where - I: Iterator, -{ - // SAFETY: the caller must uphold the contract for - // `Iterator::__iterator_get_unchecked`. - unsafe { it.try_get_unchecked(idx) } -} - -unsafe trait SpecTrustedRandomAccess: Iterator { - /// If `Self: TrustedRandomAccess`, it must be safe to call - /// `Iterator::__iterator_get_unchecked(self, index)`. - unsafe fn try_get_unchecked(&mut self, index: usize) -> Self::Item; -} - -unsafe impl SpecTrustedRandomAccess for I { - default unsafe fn try_get_unchecked(&mut self, _: usize) -> Self::Item { - panic!("Should only be called on TrustedRandomAccess iterators"); - } -} - -unsafe impl SpecTrustedRandomAccess for I { - unsafe fn try_get_unchecked(&mut self, index: usize) -> Self::Item { - // SAFETY: the caller must uphold the contract for - // `Iterator::__iterator_get_unchecked`. - unsafe { self.__iterator_get_unchecked(index) } - } -} diff --git a/library/core/src/iter/loop_desugar.rs b/library/core/src/iter/loop_desugar.rs new file mode 100644 index 0000000000000..dd3c0ec649381 --- /dev/null +++ b/library/core/src/iter/loop_desugar.rs @@ -0,0 +1,115 @@ +use crate::iter::IntoIterator; +use crate::iter::{ + TrustedRandomAccess, TrustedRandomAccessNeedsCleanup, TrustedRandomAccessNeedsForwardSetup, +}; + +#[derive(Debug)] +#[doc(hidden)] +#[unstable(feature = "trusted_random_access", issue = "none")] +pub struct ForLoopDesugar { + iter: I, + idx: usize, +} + +impl ForLoopDesugar { + #[inline] + #[cfg_attr(not(bootstrap), lang = "into_iter")] + #[cfg_attr(bootstrap, allow(dead_code))] + pub fn new(it: impl IntoIterator) -> Self { + let mut desugar = ForLoopDesugar { iter: it.into_iter(), idx: 0 }; + desugar.setup(); + desugar + } + + #[inline] + #[cfg_attr(not(bootstrap), lang = "next")] + #[cfg_attr(bootstrap, allow(dead_code))] + pub fn next(&mut self) -> Option { + self.next_spec() + } +} + +unsafe impl<#[may_dangle] I: Iterator> Drop for ForLoopDesugar { + #[inline] + fn drop(&mut self) { + self.cleanup(); + } +} + +trait DesugarNext { + fn next_spec(&mut self) -> Option; +} + +trait DesugarSetup { + fn setup(&mut self); +} + +trait DesugarCleanup { + fn cleanup(&mut self); +} + +impl DesugarNext for ForLoopDesugar +where + I: Iterator, +{ + #[inline] + default fn next_spec(&mut self) -> Option { + self.iter.next() + } +} + +impl DesugarNext for ForLoopDesugar +where + I: TrustedRandomAccess + Iterator, +{ + #[inline] + fn next_spec(&mut self) -> Option { + let idx = self.idx; + if idx < self.iter.size() { + // SAFETY: idx can't overflow since size is a usize. idx is always + // less than size, so the index is always valid. + unsafe { + self.idx = idx.unchecked_add(1); + Some(self.iter.__iterator_get_unchecked(idx)) + } + } else { + None + } + } +} + +impl DesugarSetup for ForLoopDesugar +where + I: Iterator, +{ + #[inline] + default fn setup(&mut self) {} +} + +impl DesugarSetup for ForLoopDesugar +where + I: Iterator + TrustedRandomAccess + TrustedRandomAccessNeedsForwardSetup, +{ + #[inline] + fn setup(&mut self) { + let _ = self.iter.advance_by(0); + } +} + +impl DesugarCleanup for ForLoopDesugar +where + I: Iterator, +{ + #[inline] + default fn cleanup(&mut self) {} +} + +impl DesugarCleanup for ForLoopDesugar +where + I: Iterator + TrustedRandomAccessNeedsCleanup + TrustedRandomAccess, +{ + #[inline] + fn cleanup(&mut self) { + self.iter.cleanup_front(self.idx); + } +} diff --git a/library/core/src/iter/mod.rs b/library/core/src/iter/mod.rs index 22b76ea66ff15..410f6e00fe90f 100644 --- a/library/core/src/iter/mod.rs +++ b/library/core/src/iter/mod.rs @@ -389,6 +389,11 @@ pub use self::traits::TrustedStep; pub use self::traits::{ DoubleEndedIterator, ExactSizeIterator, Extend, FromIterator, IntoIterator, Product, Sum, }; +#[unstable(feature = "trusted_random_access", issue = "none")] +pub use self::traits::{ + TrustedRandomAccess, TrustedRandomAccessNeedsCleanup, TrustedRandomAccessNeedsForwardSetup, + TrustedRandomAccessNeedsReverseSetup, +}; #[stable(feature = "iter_zip", since = "1.59.0")] pub use self::adapters::zip; @@ -404,10 +409,6 @@ pub use self::adapters::MapWhile; pub use self::adapters::SourceIter; #[stable(feature = "iterator_step_by", since = "1.28.0")] pub use self::adapters::StepBy; -#[unstable(feature = "trusted_random_access", issue = "none")] -pub use self::adapters::TrustedRandomAccess; -#[unstable(feature = "trusted_random_access", issue = "none")] -pub use self::adapters::TrustedRandomAccessNoCoerce; #[stable(feature = "rust1", since = "1.0.0")] pub use self::adapters::{ Chain, Cycle, Enumerate, Filter, FilterMap, FlatMap, Fuse, Inspect, Map, Peekable, Rev, Scan, @@ -419,6 +420,7 @@ pub use self::adapters::{Intersperse, IntersperseWith}; pub(crate) use self::adapters::{try_process, ByRefSized}; mod adapters; +mod loop_desugar; mod range; mod sources; mod traits; diff --git a/library/core/src/iter/range.rs b/library/core/src/iter/range.rs index 0ae94c05da658..7afadb74cd50d 100644 --- a/library/core/src/iter/range.rs +++ b/library/core/src/iter/range.rs @@ -3,9 +3,7 @@ use crate::convert::TryFrom; use crate::mem; use crate::ops::{self, Try}; -use super::{ - FusedIterator, TrustedLen, TrustedRandomAccess, TrustedRandomAccessNoCoerce, TrustedStep, -}; +use super::{FusedIterator, TrustedLen, TrustedRandomAccess, TrustedStep}; // Safety: All invariants are upheld. macro_rules! unsafe_impl_trusted_step { @@ -497,12 +495,16 @@ macro_rules! unsafe_range_trusted_random_access_impl { ($($t:ty)*) => ($( #[doc(hidden)] #[unstable(feature = "trusted_random_access", issue = "none")] - unsafe impl TrustedRandomAccess for ops::Range<$t> {} + unsafe impl TrustedRandomAccess for ops::Range<$t> { + + fn cleanup_front(&mut self, num: usize) { + let _ = self.advance_by(num); + } + + fn cleanup_back(&mut self, num: usize) { + let _ = self.advance_back_by(num); + } - #[doc(hidden)] - #[unstable(feature = "trusted_random_access", issue = "none")] - unsafe impl TrustedRandomAccessNoCoerce for ops::Range<$t> { - const MAY_HAVE_SIDE_EFFECT: bool = false; } )*) } @@ -755,7 +757,7 @@ impl Iterator for ops::Range { #[doc(hidden)] unsafe fn __iterator_get_unchecked(&mut self, idx: usize) -> Self::Item where - Self: TrustedRandomAccessNoCoerce, + Self: TrustedRandomAccess, { // SAFETY: The TrustedRandomAccess contract requires that callers only pass an index // that is in bounds. diff --git a/library/core/src/iter/traits/collect.rs b/library/core/src/iter/traits/collect.rs index 7b75ab96ee7de..7a90aa1302892 100644 --- a/library/core/src/iter/traits/collect.rs +++ b/library/core/src/iter/traits/collect.rs @@ -262,7 +262,7 @@ pub trait IntoIterator { /// assert_eq!(Some(3), iter.next()); /// assert_eq!(None, iter.next()); /// ``` - #[lang = "into_iter"] + #[cfg_attr(bootstrap, lang = "into_iter")] #[stable(feature = "rust1", since = "1.0.0")] fn into_iter(self) -> Self::IntoIter; } diff --git a/library/core/src/iter/traits/iterator.rs b/library/core/src/iter/traits/iterator.rs index 69f06fb06ef5d..420f26946ce7c 100644 --- a/library/core/src/iter/traits/iterator.rs +++ b/library/core/src/iter/traits/iterator.rs @@ -3,7 +3,7 @@ use crate::ops::{ChangeOutputType, ControlFlow, FromResidual, Residual, Try}; use super::super::try_process; use super::super::ByRefSized; -use super::super::TrustedRandomAccessNoCoerce; +use super::super::TrustedRandomAccess; use super::super::{Chain, Cloned, Copied, Cycle, Enumerate, Filter, FilterMap, Fuse}; use super::super::{FlatMap, Flatten}; use super::super::{FromIterator, Intersperse, IntersperseWith, Product, Sum, Zip}; @@ -94,8 +94,8 @@ pub trait Iterator { /// assert_eq!(None, iter.next()); /// assert_eq!(None, iter.next()); /// ``` - #[lang = "next"] #[stable(feature = "rust1", since = "1.0.0")] + #[cfg_attr(bootstrap, lang = "next")] fn next(&mut self) -> Option; /// Returns the bounds on the remaining length of the iterator. @@ -3763,7 +3763,7 @@ pub trait Iterator { #[unstable(feature = "trusted_random_access", issue = "none")] unsafe fn __iterator_get_unchecked(&mut self, _idx: usize) -> Self::Item where - Self: TrustedRandomAccessNoCoerce, + Self: TrustedRandomAccess, { unreachable!("Always specialized"); } diff --git a/library/core/src/iter/traits/mod.rs b/library/core/src/iter/traits/mod.rs index ed0fb634dbf05..9ed50fdb2f885 100644 --- a/library/core/src/iter/traits/mod.rs +++ b/library/core/src/iter/traits/mod.rs @@ -4,6 +4,7 @@ mod double_ended; mod exact_size; mod iterator; mod marker; +pub(crate) mod trusted_random_access; #[stable(feature = "rust1", since = "1.0.0")] pub use self::{ @@ -19,3 +20,8 @@ pub use self::{ pub use self::marker::InPlaceIterable; #[unstable(feature = "trusted_step", issue = "85731")] pub use self::marker::TrustedStep; +#[unstable(feature = "trusted_random_access", issue = "none")] +pub use self::trusted_random_access::{ + TrustedRandomAccess, TrustedRandomAccessNeedsCleanup, TrustedRandomAccessNeedsForwardSetup, + TrustedRandomAccessNeedsReverseSetup, +}; diff --git a/library/core/src/iter/traits/trusted_random_access.rs b/library/core/src/iter/traits/trusted_random_access.rs new file mode 100644 index 0000000000000..eda1c5ff9392a --- /dev/null +++ b/library/core/src/iter/traits/trusted_random_access.rs @@ -0,0 +1,124 @@ +/// An iterator whose items are random-accessible efficiently +/// +/// # Safety +/// +/// The iterator's `size_hint` must be exact and cheap to call. +/// +/// `TrustedRandomAccess::size` may not be overridden. +/// +/// All subtypes and all supertypes of `Self` must also implement `TrustedRandomAccess`. +/// In particular, this means that types with non-invariant parameters usually can not have +/// an impl for `TrustedRandomAccess` that depends on any trait bounds on such parameters, except +/// for bounds that come from the respective struct/enum definition itself, or bounds involving +/// traits that themselves come with a guarantee similar to this one. +/// +/// If `Self: ExactSizeIterator` then `self.len()` must always produce results consistent +/// with `self.size()`. +/// +/// If `Self: Iterator`, then `::__iterator_get_unchecked(&mut self, idx)` +/// must be safe to call provided the following conditions are met. +/// +/// 1. `0 <= idx` and `idx < self.size()`. +/// 2. If `Self: !Clone`, then `self.__iterator_get_unchecked(idx)` is never called with the same +/// index on `self` more than once. +/// 3. After `self.__iterator_get_unchecked(idx)` has been called, then `self.next_back()` will +/// only be called at most `self.size() - idx - 1` times. If `Self: Clone` and `self` is cloned, +/// then this number is calculated for `self` and its clone individually, +/// but `self.next_back()` calls that happened before the cloning count for both `self` and the clone. +/// 4. After `self.__iterator_get_unchecked(idx)` has been called, then only the following methods +/// will be called on `self` or on any new clones of `self`: +/// * `std::clone::Clone::clone` +/// * `std::iter::Iterator::size_hint` +/// * `std::iter::DoubleEndedIterator::next_back` +/// * `std::iter::ExactSizeIterator::len` +/// * `std::iter::Iterator::__iterator_get_unchecked` +/// * `std::iter::TrustedRandomAccess::size` +/// +/// Further, given that these conditions are met, it must guarantee that: +/// +/// * It does not change the value returned from `size_hint` +/// * It must be safe to call the methods listed above on `self` after calling +/// `self.__iterator_get_unchecked(idx)`, assuming that the required traits are implemented. +/// * It must also be safe to drop `self` after calling `self.__iterator_get_unchecked(idx)`. +// +// FIXME: Clarify interaction with SourceIter/InPlaceIterable. Calling `SourceIter::as_inner` +// after `__iterator_get_unchecked` is supposed to be allowed. +#[doc(hidden)] +#[unstable(feature = "trusted_random_access", issue = "none")] +#[rustc_specialization_trait] +pub unsafe trait TrustedRandomAccess: Sized { + // Convenience method. + fn size(&self) -> usize + where + Self: Iterator, + { + self.size_hint().0 + } + + fn cleanup_front(&mut self, num: usize); + + fn cleanup_back(&mut self, num: usize); +} + +// The following marker traits exist because specializing on them currently is the only way to avoid +// emitting dead IR. Associated constants do not work because we currently don't have post-monomorphization +// DCE. +// +// Pulling in the setup and cleanup methods on every specialized `for _ in` loop leads to 10% IR bloat +// and LLVM won't eliminate it in debug mode. + +#[doc(hidden)] +#[unstable(feature = "trusted_random_access", issue = "none")] +#[rustc_specialization_trait] +#[marker] +pub unsafe trait TrustedRandomAccessNeedsCleanup {} + +#[doc(hidden)] +#[unstable(feature = "trusted_random_access", issue = "none")] +#[rustc_specialization_trait] +#[marker] +pub unsafe trait TrustedRandomAccessNeedsForwardSetup {} + +#[doc(hidden)] +#[unstable(feature = "trusted_random_access", issue = "none")] +#[rustc_specialization_trait] +#[marker] +pub unsafe trait TrustedRandomAccessNeedsReverseSetup {} + +/// Like `Iterator::__iterator_get_unchecked`, but doesn't require the compiler to +/// know that `U: TrustedRandomAccess`. +/// +/// ## Safety +/// +/// Same requirements calling `get_unchecked` directly. +#[doc(hidden)] +#[inline] +pub(in crate::iter) unsafe fn try_get_unchecked(it: &mut I, idx: usize) -> I::Item +where + I: Iterator, +{ + // SAFETY: the caller must uphold the contract for + // `Iterator::__iterator_get_unchecked`. + unsafe { it.try_get_unchecked(idx) } +} + +unsafe trait SpecTrustedRandomAccess: Iterator { + /// If `Self: TrustedRandomAccess`, it must be safe to call + /// `Iterator::__iterator_get_unchecked(self, index)`. + unsafe fn try_get_unchecked(&mut self, index: usize) -> Self::Item; +} + +unsafe impl SpecTrustedRandomAccess for I { + default unsafe fn try_get_unchecked(&mut self, _: usize) -> Self::Item { + panic!("Should only be called on TrustedRandomAccess iterators"); + } +} + +unsafe impl SpecTrustedRandomAccess for I { + #[inline] + unsafe fn try_get_unchecked(&mut self, index: usize) -> Self::Item { + // SAFETY: the caller must uphold the contract for + // `Iterator::__iterator_get_unchecked`. + unsafe { self.__iterator_get_unchecked(index) } + } +} diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 1612aa582ad17..b0a2063001672 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -174,6 +174,7 @@ #![feature(deprecated_suggestion)] #![feature(doc_cfg)] #![feature(doc_notable_trait)] +#![feature(dropck_eyepatch)] #![feature(rustdoc_internals)] #![feature(exhaustive_patterns)] #![feature(doc_cfg_hide)] @@ -185,6 +186,7 @@ #![feature(lang_items)] #![feature(link_llvm_intrinsics)] #![feature(macro_metavar_expr)] +#![feature(marker_trait_attr)] #![feature(min_specialization)] #![feature(mixed_integer_ops)] #![feature(must_not_suspend)] diff --git a/library/core/src/slice/iter.rs b/library/core/src/slice/iter.rs index 98dd1521d0e85..21d07479b03f6 100644 --- a/library/core/src/slice/iter.rs +++ b/library/core/src/slice/iter.rs @@ -7,7 +7,7 @@ use crate::cmp; use crate::cmp::Ordering; use crate::fmt; use crate::intrinsics::{assume, exact_div, unchecked_sub}; -use crate::iter::{FusedIterator, TrustedLen, TrustedRandomAccess, TrustedRandomAccessNoCoerce}; +use crate::iter::{FusedIterator, TrustedLen, TrustedRandomAccess}; use crate::marker::{PhantomData, Send, Sized, Sync}; use crate::mem; use crate::num::NonZeroUsize; @@ -1370,12 +1370,14 @@ impl FusedIterator for Windows<'_, T> {} #[doc(hidden)] #[unstable(feature = "trusted_random_access", issue = "none")] -unsafe impl<'a, T> TrustedRandomAccess for Windows<'a, T> {} +unsafe impl<'a, T> TrustedRandomAccess for Windows<'a, T> { + fn cleanup_front(&mut self, num: usize) { + self.v = &self.v[num..]; + } -#[doc(hidden)] -#[unstable(feature = "trusted_random_access", issue = "none")] -unsafe impl<'a, T> TrustedRandomAccessNoCoerce for Windows<'a, T> { - const MAY_HAVE_SIDE_EFFECT: bool = false; + fn cleanup_back(&mut self, num: usize) { + self.v = &self.v[..self.v.len() - num]; + } } /// An iterator over a slice in (non-overlapping) chunks (`chunk_size` elements at a @@ -1554,12 +1556,18 @@ impl FusedIterator for Chunks<'_, T> {} #[doc(hidden)] #[unstable(feature = "trusted_random_access", issue = "none")] -unsafe impl<'a, T> TrustedRandomAccess for Chunks<'a, T> {} +unsafe impl<'a, T> TrustedRandomAccess for Chunks<'a, T> { + fn cleanup_front(&mut self, num: usize) { + let len = self.v.len(); + let start = cmp::min(len, self.chunk_size * num); + self.v = &self.v[start..]; + } -#[doc(hidden)] -#[unstable(feature = "trusted_random_access", issue = "none")] -unsafe impl<'a, T> TrustedRandomAccessNoCoerce for Chunks<'a, T> { - const MAY_HAVE_SIDE_EFFECT: bool = false; + fn cleanup_back(&mut self, num: usize) { + let remaining = self.len() - num; + let end = remaining * self.chunk_size; + self.v = &self.v[..end]; + } } /// An iterator over a slice in (non-overlapping) mutable chunks (`chunk_size` @@ -1722,12 +1730,20 @@ impl FusedIterator for ChunksMut<'_, T> {} #[doc(hidden)] #[unstable(feature = "trusted_random_access", issue = "none")] -unsafe impl<'a, T> TrustedRandomAccess for ChunksMut<'a, T> {} +unsafe impl<'a, T> TrustedRandomAccess for ChunksMut<'a, T> { + fn cleanup_front(&mut self, num: usize) { + let v = mem::replace(&mut self.v, &mut []); + let start = self.chunk_size * num; + self.v = &mut v[start..]; + } -#[doc(hidden)] -#[unstable(feature = "trusted_random_access", issue = "none")] -unsafe impl<'a, T> TrustedRandomAccessNoCoerce for ChunksMut<'a, T> { - const MAY_HAVE_SIDE_EFFECT: bool = false; + fn cleanup_back(&mut self, num: usize) { + let len = self.len(); + let v = mem::replace(&mut self.v, &mut []); + let remaining = len - num; + let end = remaining * self.chunk_size; + self.v = &mut v[..end]; + } } /// An iterator over a slice in (non-overlapping) chunks (`chunk_size` elements at a @@ -1882,12 +1898,16 @@ impl FusedIterator for ChunksExact<'_, T> {} #[doc(hidden)] #[unstable(feature = "trusted_random_access", issue = "none")] -unsafe impl<'a, T> TrustedRandomAccess for ChunksExact<'a, T> {} +unsafe impl<'a, T> TrustedRandomAccess for ChunksExact<'a, T> { + fn cleanup_front(&mut self, num: usize) { + let start = self.chunk_size * num; + self.v = &self.v[start..]; + } -#[doc(hidden)] -#[unstable(feature = "trusted_random_access", issue = "none")] -unsafe impl<'a, T> TrustedRandomAccessNoCoerce for ChunksExact<'a, T> { - const MAY_HAVE_SIDE_EFFECT: bool = false; + fn cleanup_back(&mut self, num: usize) { + let end = self.v.len() - (self.chunk_size * num); + self.v = &self.v[..end]; + } } /// An iterator over a slice in (non-overlapping) mutable chunks (`chunk_size` @@ -2039,12 +2059,18 @@ impl FusedIterator for ChunksExactMut<'_, T> {} #[doc(hidden)] #[unstable(feature = "trusted_random_access", issue = "none")] -unsafe impl<'a, T> TrustedRandomAccess for ChunksExactMut<'a, T> {} +unsafe impl<'a, T> TrustedRandomAccess for ChunksExactMut<'a, T> { + fn cleanup_front(&mut self, num: usize) { + let v = mem::replace(&mut self.v, &mut []); + let start = self.chunk_size * num; + self.v = &mut v[start..]; + } -#[doc(hidden)] -#[unstable(feature = "trusted_random_access", issue = "none")] -unsafe impl<'a, T> TrustedRandomAccessNoCoerce for ChunksExactMut<'a, T> { - const MAY_HAVE_SIDE_EFFECT: bool = false; + fn cleanup_back(&mut self, num: usize) { + let v = mem::replace(&mut self.v, &mut []); + let end = v.len() - (self.chunk_size * num); + self.v = &mut v[..end]; + } } /// A windowed iterator over a slice in overlapping chunks (`N` elements at a @@ -2284,12 +2310,14 @@ impl FusedIterator for ArrayChunks<'_, T, N> {} #[doc(hidden)] #[unstable(feature = "array_chunks", issue = "74985")] -unsafe impl<'a, T, const N: usize> TrustedRandomAccess for ArrayChunks<'a, T, N> {} +unsafe impl<'a, T, const N: usize> TrustedRandomAccess for ArrayChunks<'a, T, N> { + fn cleanup_front(&mut self, num: usize) { + self.iter.cleanup_front(num); + } -#[doc(hidden)] -#[unstable(feature = "array_chunks", issue = "74985")] -unsafe impl<'a, T, const N: usize> TrustedRandomAccessNoCoerce for ArrayChunks<'a, T, N> { - const MAY_HAVE_SIDE_EFFECT: bool = false; + fn cleanup_back(&mut self, num: usize) { + self.iter.cleanup_back(num); + } } /// An iterator over a slice in (non-overlapping) mutable chunks (`N` elements @@ -2403,12 +2431,14 @@ impl FusedIterator for ArrayChunksMut<'_, T, N> {} #[doc(hidden)] #[unstable(feature = "array_chunks", issue = "74985")] -unsafe impl<'a, T, const N: usize> TrustedRandomAccess for ArrayChunksMut<'a, T, N> {} +unsafe impl<'a, T, const N: usize> TrustedRandomAccess for ArrayChunksMut<'a, T, N> { + fn cleanup_front(&mut self, num: usize) { + self.iter.cleanup_front(num); + } -#[doc(hidden)] -#[unstable(feature = "array_chunks", issue = "74985")] -unsafe impl<'a, T, const N: usize> TrustedRandomAccessNoCoerce for ArrayChunksMut<'a, T, N> { - const MAY_HAVE_SIDE_EFFECT: bool = false; + fn cleanup_back(&mut self, num: usize) { + self.iter.cleanup_back(num); + } } /// An iterator over a slice in (non-overlapping) chunks (`chunk_size` elements at a @@ -2577,12 +2607,17 @@ impl FusedIterator for RChunks<'_, T> {} #[doc(hidden)] #[unstable(feature = "trusted_random_access", issue = "none")] -unsafe impl<'a, T> TrustedRandomAccess for RChunks<'a, T> {} +unsafe impl<'a, T> TrustedRandomAccess for RChunks<'a, T> { + fn cleanup_front(&mut self, num: usize) { + let end = self.v.len().saturating_sub(self.chunk_size * num); + self.v = &self.v[..end]; + } -#[doc(hidden)] -#[unstable(feature = "trusted_random_access", issue = "none")] -unsafe impl<'a, T> TrustedRandomAccessNoCoerce for RChunks<'a, T> { - const MAY_HAVE_SIDE_EFFECT: bool = false; + fn cleanup_back(&mut self, num: usize) { + let remainder = self.len() - num; + let start = self.v.len() - remainder * self.chunk_size; + self.v = &self.v[start..]; + } } /// An iterator over a slice in (non-overlapping) mutable chunks (`chunk_size` @@ -2749,12 +2784,20 @@ impl FusedIterator for RChunksMut<'_, T> {} #[doc(hidden)] #[unstable(feature = "trusted_random_access", issue = "none")] -unsafe impl<'a, T> TrustedRandomAccess for RChunksMut<'a, T> {} +unsafe impl<'a, T> TrustedRandomAccess for RChunksMut<'a, T> { + fn cleanup_front(&mut self, num: usize) { + let v = mem::replace(&mut self.v, &mut []); + let end = v.len().saturating_sub(self.chunk_size * num); + self.v = &mut v[..end]; + } -#[doc(hidden)] -#[unstable(feature = "trusted_random_access", issue = "none")] -unsafe impl<'a, T> TrustedRandomAccessNoCoerce for RChunksMut<'a, T> { - const MAY_HAVE_SIDE_EFFECT: bool = false; + fn cleanup_back(&mut self, num: usize) { + let len = self.len(); + let v = mem::replace(&mut self.v, &mut []); + let remainder = len - num; + let start = v.len() - remainder * self.chunk_size; + self.v = &mut v[start..] + } } /// An iterator over a slice in (non-overlapping) chunks (`chunk_size` elements at a @@ -2913,12 +2956,16 @@ impl FusedIterator for RChunksExact<'_, T> {} #[doc(hidden)] #[unstable(feature = "trusted_random_access", issue = "none")] -unsafe impl<'a, T> TrustedRandomAccess for RChunksExact<'a, T> {} +unsafe impl<'a, T> TrustedRandomAccess for RChunksExact<'a, T> { + fn cleanup_front(&mut self, num: usize) { + let end = self.v.len() - (self.chunk_size * num); + self.v = &self.v[..end]; + } -#[doc(hidden)] -#[unstable(feature = "trusted_random_access", issue = "none")] -unsafe impl<'a, T> TrustedRandomAccessNoCoerce for RChunksExact<'a, T> { - const MAY_HAVE_SIDE_EFFECT: bool = false; + fn cleanup_back(&mut self, num: usize) { + let start = self.chunk_size * num; + self.v = &self.v[start..]; + } } /// An iterator over a slice in (non-overlapping) mutable chunks (`chunk_size` @@ -3074,32 +3121,46 @@ impl FusedIterator for RChunksExactMut<'_, T> {} #[doc(hidden)] #[unstable(feature = "trusted_random_access", issue = "none")] -unsafe impl<'a, T> TrustedRandomAccess for RChunksExactMut<'a, T> {} +unsafe impl<'a, T> TrustedRandomAccess for RChunksExactMut<'a, T> { + fn cleanup_front(&mut self, num: usize) { + let v = mem::replace(&mut self.v, &mut []); + let end = v.len() - (self.chunk_size * num); + self.v = &mut v[..end]; + } -#[doc(hidden)] -#[unstable(feature = "trusted_random_access", issue = "none")] -unsafe impl<'a, T> TrustedRandomAccessNoCoerce for RChunksExactMut<'a, T> { - const MAY_HAVE_SIDE_EFFECT: bool = false; + fn cleanup_back(&mut self, num: usize) { + let v = mem::replace(&mut self.v, &mut []); + let start = self.chunk_size * num; + self.v = &mut v[start..]; + } } #[doc(hidden)] #[unstable(feature = "trusted_random_access", issue = "none")] -unsafe impl<'a, T> TrustedRandomAccess for Iter<'a, T> {} +unsafe impl<'a, T> TrustedRandomAccess for Iter<'a, T> { + #[inline] + fn cleanup_front(&mut self, num: usize) { + let _ = self.advance_by(num); + } -#[doc(hidden)] -#[unstable(feature = "trusted_random_access", issue = "none")] -unsafe impl<'a, T> TrustedRandomAccessNoCoerce for Iter<'a, T> { - const MAY_HAVE_SIDE_EFFECT: bool = false; + #[inline] + fn cleanup_back(&mut self, num: usize) { + let _ = self.advance_back_by(num); + } } #[doc(hidden)] #[unstable(feature = "trusted_random_access", issue = "none")] -unsafe impl<'a, T> TrustedRandomAccess for IterMut<'a, T> {} +unsafe impl<'a, T> TrustedRandomAccess for IterMut<'a, T> { + #[inline] + fn cleanup_front(&mut self, num: usize) { + let _ = self.advance_by(num); + } -#[doc(hidden)] -#[unstable(feature = "trusted_random_access", issue = "none")] -unsafe impl<'a, T> TrustedRandomAccessNoCoerce for IterMut<'a, T> { - const MAY_HAVE_SIDE_EFFECT: bool = false; + #[inline] + fn cleanup_back(&mut self, num: usize) { + let _ = self.advance_back_by(num); + } } /// An iterator over slice in (non-overlapping) chunks separated by a predicate. diff --git a/library/core/src/slice/iter/macros.rs b/library/core/src/slice/iter/macros.rs index b74ab28fc092a..78bf3381b4d26 100644 --- a/library/core/src/slice/iter/macros.rs +++ b/library/core/src/slice/iter/macros.rs @@ -326,6 +326,7 @@ macro_rules! iterator { } #[doc(hidden)] + #[inline] unsafe fn __iterator_get_unchecked(&mut self, idx: usize) -> Self::Item { // SAFETY: the caller must guarantee that `i` is in bounds of // the underlying slice, so `i` cannot overflow an `isize`, and diff --git a/library/core/src/str/iter.rs b/library/core/src/str/iter.rs index e529bccbc7999..39a45f630d029 100644 --- a/library/core/src/str/iter.rs +++ b/library/core/src/str/iter.rs @@ -3,8 +3,7 @@ use crate::char; use crate::fmt::{self, Write}; use crate::iter::{Chain, FlatMap, Flatten}; -use crate::iter::{Copied, Filter, FusedIterator, Map, TrustedLen}; -use crate::iter::{TrustedRandomAccess, TrustedRandomAccessNoCoerce}; +use crate::iter::{Copied, Filter, FusedIterator, Map, TrustedLen, TrustedRandomAccess}; use crate::ops::Try; use crate::option; use crate::slice::{self, Split as SliceSplit}; @@ -348,12 +347,16 @@ unsafe impl TrustedLen for Bytes<'_> {} #[doc(hidden)] #[unstable(feature = "trusted_random_access", issue = "none")] -unsafe impl TrustedRandomAccess for Bytes<'_> {} +unsafe impl TrustedRandomAccess for Bytes<'_> { + #[inline] + fn cleanup_front(&mut self, num: usize) { + self.0.cleanup_front(num); + } -#[doc(hidden)] -#[unstable(feature = "trusted_random_access", issue = "none")] -unsafe impl TrustedRandomAccessNoCoerce for Bytes<'_> { - const MAY_HAVE_SIDE_EFFECT: bool = false; + #[inline] + fn cleanup_back(&mut self, num: usize) { + self.0.cleanup_back(num); + } } /// This macro generates a Clone impl for string pattern API diff --git a/library/core/tests/iter/adapters/cloned.rs b/library/core/tests/iter/adapters/cloned.rs index 78babb7feab18..24b6431f0a6a0 100644 --- a/library/core/tests/iter/adapters/cloned.rs +++ b/library/core/tests/iter/adapters/cloned.rs @@ -31,7 +31,7 @@ fn test_cloned_side_effects() { .zip(&[1]); for _ in iter {} } - assert_eq!(count, 2); + assert_eq!(count, 1); } #[test] diff --git a/library/core/tests/iter/adapters/zip.rs b/library/core/tests/iter/adapters/zip.rs index 585cfbb90e40c..70e049586c78b 100644 --- a/library/core/tests/iter/adapters/zip.rs +++ b/library/core/tests/iter/adapters/zip.rs @@ -119,7 +119,7 @@ fn test_zip_cloned_sideffectful() { for _ in xs.iter().cloned().zip(ys.iter().cloned()) {} - assert_eq!(&xs, &[1, 1, 1, 0][..]); + assert_eq!(&xs, &[1, 1, 0, 0][..]); assert_eq!(&ys, &[1, 1][..]); let xs = [CountClone::new(), CountClone::new()]; @@ -138,7 +138,7 @@ fn test_zip_map_sideffectful() { for _ in xs.iter_mut().map(|x| *x += 1).zip(ys.iter_mut().map(|y| *y += 1)) {} - assert_eq!(&xs, &[1, 1, 1, 1, 1, 0]); + assert_eq!(&xs, &[1, 1, 1, 1, 0, 0]); assert_eq!(&ys, &[1, 1, 1, 1]); let mut xs = [0; 4]; @@ -174,19 +174,6 @@ fn test_zip_map_rev_sideffectful() { assert_eq!(&ys, &[1, 1, 1, 1]); } -#[test] -fn test_zip_nested_sideffectful() { - let mut xs = [0; 6]; - let ys = [0; 4]; - - { - // test that it has the side effect nested inside enumerate - let it = xs.iter_mut().map(|x| *x = 1).enumerate().zip(&ys); - it.count(); - } - assert_eq!(&xs, &[1, 1, 1, 1, 1, 0]); -} - #[test] fn test_zip_nth_back_side_effects_exhausted() { let mut a = Vec::new(); diff --git a/src/test/mir-opt/remove_storage_markers.main.RemoveStorageMarkers.diff b/src/test/mir-opt/remove_storage_markers.main.RemoveStorageMarkers.diff index 5131e2f088d3d..7b23f466e0239 100644 --- a/src/test/mir-opt/remove_storage_markers.main.RemoveStorageMarkers.diff +++ b/src/test/mir-opt/remove_storage_markers.main.RemoveStorageMarkers.diff @@ -4,32 +4,37 @@ fn main() -> () { let mut _0: (); // return place in scope 0 at $DIR/remove_storage_markers.rs:6:11: 6:11 let mut _1: i32; // in scope 0 at $DIR/remove_storage_markers.rs:7:9: 7:16 - let mut _2: std::ops::Range; // in scope 0 at $DIR/remove_storage_markers.rs:8:14: 8:19 + let mut _2: std::iter::loop_desugar::ForLoopDesugar>; // in scope 0 at $DIR/remove_storage_markers.rs:8:14: 8:19 let mut _3: std::ops::Range; // in scope 0 at $DIR/remove_storage_markers.rs:8:14: 8:19 let mut _5: (); // in scope 0 at $DIR/remove_storage_markers.rs:6:1: 11:2 let _6: (); // in scope 0 at $DIR/remove_storage_markers.rs:8:14: 8:19 let mut _7: std::option::Option; // in scope 0 at $DIR/remove_storage_markers.rs:8:14: 8:19 - let mut _8: &mut std::ops::Range; // in scope 0 at $DIR/remove_storage_markers.rs:8:14: 8:19 - let mut _9: &mut std::ops::Range; // in scope 0 at $DIR/remove_storage_markers.rs:8:14: 8:19 + let mut _8: &mut std::iter::loop_desugar::ForLoopDesugar>; // in scope 0 at $DIR/remove_storage_markers.rs:8:14: 8:19 + let mut _9: &mut std::iter::loop_desugar::ForLoopDesugar>; // in scope 0 at $DIR/remove_storage_markers.rs:8:14: 8:19 let mut _10: isize; // in scope 0 at $DIR/remove_storage_markers.rs:8:5: 10:6 let mut _11: !; // in scope 0 at $DIR/remove_storage_markers.rs:8:5: 10:6 let mut _13: i32; // in scope 0 at $DIR/remove_storage_markers.rs:9:16: 9:17 scope 1 { debug sum => _1; // in scope 1 at $DIR/remove_storage_markers.rs:7:9: 7:16 - let mut _4: std::ops::Range; // in scope 1 at $DIR/remove_storage_markers.rs:8:14: 8:19 + let mut _4: std::iter::loop_desugar::ForLoopDesugar>; // in scope 1 at $DIR/remove_storage_markers.rs:8:14: 8:19 scope 2 { debug iter => _4; // in scope 2 at $DIR/remove_storage_markers.rs:8:14: 8:19 let _12: i32; // in scope 2 at $DIR/remove_storage_markers.rs:8:9: 8:10 scope 3 { debug i => _12; // in scope 3 at $DIR/remove_storage_markers.rs:8:9: 8:10 } - scope 5 (inlined iter::range::>::next) { // at $DIR/remove_storage_markers.rs:8:14: 8:19 - debug self => _8; // in scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL - let mut _14: &mut std::ops::Range; // in scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL + scope 6 (inlined > as Iterator>::next) { // at $DIR/remove_storage_markers.rs:8:14: 8:19 + debug self => _8; // in scope 6 at $SRC_DIR/core/src/iter/loop_desugar.rs:LL:COL + let mut _16: &mut std::iter::loop_desugar::ForLoopDesugar>; // in scope 6 at $SRC_DIR/core/src/iter/loop_desugar.rs:LL:COL } } - scope 4 (inlined as IntoIterator>::into_iter) { // at $DIR/remove_storage_markers.rs:8:14: 8:19 - debug self => _3; // in scope 4 at $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL + scope 4 (inlined as iter::loop_desugar::IntoIterator>::into_iter) { // at $DIR/remove_storage_markers.rs:8:14: 8:19 + debug self => _3; // in scope 4 at $SRC_DIR/core/src/iter/loop_desugar.rs:LL:COL + let mut _14: std::ops::Range; // in scope 4 at $SRC_DIR/core/src/iter/loop_desugar.rs:LL:COL + let mut _15: std::ops::Range; // in scope 4 at $SRC_DIR/core/src/iter/loop_desugar.rs:LL:COL + scope 5 (inlined as IntoIterator>::into_iter) { // at $SRC_DIR/core/src/iter/loop_desugar.rs:LL:COL + debug self => _15; // in scope 5 at $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL + } } } @@ -41,7 +46,14 @@ Deinit(_3); // scope 1 at $DIR/remove_storage_markers.rs:8:14: 8:19 (_3.0: i32) = const 0_i32; // scope 1 at $DIR/remove_storage_markers.rs:8:14: 8:19 (_3.1: i32) = const 10_i32; // scope 1 at $DIR/remove_storage_markers.rs:8:14: 8:19 - _2 = move _3; // scope 4 at $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL +- StorageLive(_14); // scope 4 at $SRC_DIR/core/src/iter/loop_desugar.rs:LL:COL +- StorageLive(_15); // scope 4 at $SRC_DIR/core/src/iter/loop_desugar.rs:LL:COL + _15 = move _3; // scope 4 at $SRC_DIR/core/src/iter/loop_desugar.rs:LL:COL + _14 = move _15; // scope 5 at $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL +- StorageDead(_15); // scope 4 at $SRC_DIR/core/src/iter/loop_desugar.rs:LL:COL + (_2.0: std::ops::Range) = move _14; // scope 4 at $SRC_DIR/core/src/iter/loop_desugar.rs:LL:COL + (_2.1: usize) = const 0_usize; // scope 4 at $SRC_DIR/core/src/iter/loop_desugar.rs:LL:COL +- StorageDead(_14); // scope 4 at $SRC_DIR/core/src/iter/loop_desugar.rs:LL:COL - StorageDead(_3); // scope 1 at $DIR/remove_storage_markers.rs:8:18: 8:19 - StorageLive(_4); // scope 1 at $DIR/remove_storage_markers.rs:8:14: 8:19 _4 = move _2; // scope 1 at $DIR/remove_storage_markers.rs:8:14: 8:19 @@ -55,12 +67,12 @@ - StorageLive(_9); // scope 2 at $DIR/remove_storage_markers.rs:8:14: 8:19 _9 = &mut _4; // scope 2 at $DIR/remove_storage_markers.rs:8:14: 8:19 _8 = &mut (*_9); // scope 2 at $DIR/remove_storage_markers.rs:8:14: 8:19 -- StorageLive(_14); // scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL - _14 = &mut (*_8); // scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL - _7 = as iter::range::RangeIteratorImpl>::spec_next(move _14) -> bb4; // scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL +- StorageLive(_16); // scope 6 at $SRC_DIR/core/src/iter/loop_desugar.rs:LL:COL + _16 = &mut (*_8); // scope 6 at $SRC_DIR/core/src/iter/loop_desugar.rs:LL:COL + _7 = > as iter::loop_desugar::DesugarSpec>::next_spec(move _16) -> bb4; // scope 6 at $SRC_DIR/core/src/iter/loop_desugar.rs:LL:COL // mir::Constant - // + span: $SRC_DIR/core/src/iter/range.rs:LL:COL - // + literal: Const { ty: for<'r> fn(&'r mut std::ops::Range) -> Option< as iter::range::RangeIteratorImpl>::Item> { as iter::range::RangeIteratorImpl>::spec_next}, val: Value(Scalar()) } + // + span: $SRC_DIR/core/src/iter/loop_desugar.rs:LL:COL + // + literal: Const { ty: for<'r> fn(&'r mut iter::loop_desugar::ForLoopDesugar>) -> Option {> as iter::loop_desugar::DesugarSpec>::next_spec}, val: Value(Scalar()) } } bb2: { @@ -91,7 +103,7 @@ } bb4: { -- StorageDead(_14); // scope 5 at $SRC_DIR/core/src/iter/range.rs:LL:COL +- StorageDead(_16); // scope 6 at $SRC_DIR/core/src/iter/loop_desugar.rs:LL:COL - StorageDead(_8); // scope 2 at $DIR/remove_storage_markers.rs:8:18: 8:19 _10 = discriminant(_7); // scope 2 at $DIR/remove_storage_markers.rs:8:14: 8:19 switchInt(move _10) -> [0_isize: bb3, otherwise: bb2]; // scope 2 at $DIR/remove_storage_markers.rs:8:14: 8:19 diff --git a/src/test/ui/consts/const-fn-error.stderr b/src/test/ui/consts/const-fn-error.stderr index 4d53cfc35e1c4..cc77e01d6a60a 100644 --- a/src/test/ui/consts/const-fn-error.stderr +++ b/src/test/ui/consts/const-fn-error.stderr @@ -35,7 +35,7 @@ LL | for i in 0..x { = note: see issue #57349 for more information = help: add `#![feature(const_mut_refs)]` to the crate attributes to enable -error[E0015]: cannot call non-const fn ` as Iterator>::next` in constant functions +error[E0015]: cannot call non-const fn `> as Iterator>::next` in constant functions --> $DIR/const-fn-error.rs:5:14 | LL | for i in 0..x { diff --git a/src/test/ui/consts/const-for.stderr b/src/test/ui/consts/const-for.stderr index b0dc43eb8e850..f4f803149f2e3 100644 --- a/src/test/ui/consts/const-for.stderr +++ b/src/test/ui/consts/const-for.stderr @@ -11,7 +11,7 @@ LL | impl IntoIterator for I { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = note: calls in constants are limited to constant functions, tuple structs and tuple variants -error[E0015]: cannot call non-const fn ` as Iterator>::next` in constants +error[E0015]: cannot call non-const fn `> as Iterator>::next` in constants --> $DIR/const-for.rs:5:14 | LL | for _ in 0..5 {} diff --git a/src/test/ui/issues/issue-20605.stderr b/src/test/ui/issues/issue-20605.stderr index 41eefe3f8e9b5..2cb9ef77769f8 100644 --- a/src/test/ui/issues/issue-20605.stderr +++ b/src/test/ui/issues/issue-20605.stderr @@ -2,10 +2,10 @@ error[E0277]: the size for values of type `dyn Iterator` cann --> $DIR/issue-20605.rs:2:17 | LL | for item in *things { *item = 0 } - | ^^^^^^^ expected an implementor of trait `IntoIterator` + | ^^^^^^^ expected an implementor of trait `iter::loop_desugar::IntoIterator` | - = note: the trait bound `dyn Iterator: IntoIterator` is not satisfied - = note: required because of the requirements on the impl of `IntoIterator` for `dyn Iterator` + = note: the trait bound `dyn Iterator: iter::loop_desugar::IntoIterator` is not satisfied + = note: required because of the requirements on the impl of `iter::loop_desugar::IntoIterator` for `dyn Iterator` help: consider mutably borrowing here | LL | for item in &mut *things { *item = 0 } diff --git a/src/test/ui/issues/issue-33941.rs b/src/test/ui/issues/issue-33941.rs index a1213623e6f94..f3b8b26f0c9dc 100644 --- a/src/test/ui/issues/issue-33941.rs +++ b/src/test/ui/issues/issue-33941.rs @@ -6,4 +6,5 @@ fn main() { for _ in HashMap::new().iter().cloned() {} //~ ERROR type mismatch //~^ ERROR type mismatch //~| ERROR type mismatch + //~| ERROR type mismatch } diff --git a/src/test/ui/issues/issue-33941.stderr b/src/test/ui/issues/issue-33941.stderr index e1ce6eed98efb..29265208364e6 100644 --- a/src/test/ui/issues/issue-33941.stderr +++ b/src/test/ui/issues/issue-33941.stderr @@ -33,6 +33,18 @@ LL | for _ in HashMap::new().iter().cloned() {} found reference `&_` = note: required because of the requirements on the impl of `Iterator` for `Cloned>` -error: aborting due to 3 previous errors +error[E0271]: type mismatch resolving ` as Iterator>::Item == &_` + --> $DIR/issue-33941.rs:6:14 + | +LL | for _ in HashMap::new().iter().cloned() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected tuple, found reference + | + = note: expected tuple `(&_, &_)` + found reference `&_` + = note: required because of the requirements on the impl of `Iterator` for `Cloned>` + = note: 1 redundant requirement hidden + = note: required because of the requirements on the impl of `Iterator` for `iter::loop_desugar::ForLoopDesugar>>` + +error: aborting due to 4 previous errors For more information about this error, try `rustc --explain E0271`. diff --git a/src/test/ui/issues/issue-61108.stderr b/src/test/ui/issues/issue-61108.stderr index e5b671d7b7ab0..23abb5de089e0 100644 --- a/src/test/ui/issues/issue-61108.stderr +++ b/src/test/ui/issues/issue-61108.stderr @@ -10,7 +10,7 @@ LL | bad_letters.push('s'); | ^^^^^^^^^^^^^^^^^^^^^ value borrowed here after move | note: this function takes ownership of the receiver `self`, which moves `bad_letters` - --> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL + --> $SRC_DIR/core/src/iter/loop_desugar.rs:LL:COL | LL | fn into_iter(self) -> Self::IntoIter; | ^^^^ diff --git a/src/test/ui/issues/issue-64559.stderr b/src/test/ui/issues/issue-64559.stderr index ef178bbd15538..274e1264005b0 100644 --- a/src/test/ui/issues/issue-64559.stderr +++ b/src/test/ui/issues/issue-64559.stderr @@ -11,7 +11,7 @@ LL | let _closure = || orig; | value used here after move | note: this function takes ownership of the receiver `self`, which moves `orig` - --> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL + --> $SRC_DIR/core/src/iter/loop_desugar.rs:LL:COL | LL | fn into_iter(self) -> Self::IntoIter; | ^^^^ diff --git a/src/test/ui/issues/issue-83924.stderr b/src/test/ui/issues/issue-83924.stderr index 767571cddbeea..77b901eea9cdd 100644 --- a/src/test/ui/issues/issue-83924.stderr +++ b/src/test/ui/issues/issue-83924.stderr @@ -11,7 +11,7 @@ LL | for n in v { | ^ value used here after move | note: this function takes ownership of the receiver `self`, which moves `v` - --> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL + --> $SRC_DIR/core/src/iter/loop_desugar.rs:LL:COL | LL | fn into_iter(self) -> Self::IntoIter; | ^^^^ diff --git a/src/test/ui/loops/issue-82916.stderr b/src/test/ui/loops/issue-82916.stderr index 57d76016c4539..efc8d7e0805cd 100644 --- a/src/test/ui/loops/issue-82916.stderr +++ b/src/test/ui/loops/issue-82916.stderr @@ -10,7 +10,7 @@ LL | let z = x; | ^ value used here after move | note: this function takes ownership of the receiver `self`, which moves `x` - --> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL + --> $SRC_DIR/core/src/iter/loop_desugar.rs:LL:COL | LL | fn into_iter(self) -> Self::IntoIter; | ^^^^ diff --git a/src/test/ui/moves/move-fn-self-receiver.stderr b/src/test/ui/moves/move-fn-self-receiver.stderr index 3a686121a9283..79568a2ed1f28 100644 --- a/src/test/ui/moves/move-fn-self-receiver.stderr +++ b/src/test/ui/moves/move-fn-self-receiver.stderr @@ -123,6 +123,11 @@ LL | for _val in implicit_into_iter {} LL | implicit_into_iter; | ^^^^^^^^^^^^^^^^^^ value used here after move | +note: this function takes ownership of the receiver `self`, which moves `implicit_into_iter` + --> $SRC_DIR/core/src/iter/loop_desugar.rs:LL:COL + | +LL | fn into_iter(self) -> Self::IntoIter; + | ^^^^ help: consider iterating over a slice of the `Vec`'s content to avoid moving into the `for` loop | LL | for _val in &implicit_into_iter {} diff --git a/src/test/ui/never_type/issue-52443.stderr b/src/test/ui/never_type/issue-52443.stderr index 8c1755205f025..af211b201ae9f 100644 --- a/src/test/ui/never_type/issue-52443.stderr +++ b/src/test/ui/never_type/issue-52443.stderr @@ -60,7 +60,7 @@ LL | [(); { for _ in 0usize.. {}; 0}]; = note: see issue #57349 for more information = help: add `#![feature(const_mut_refs)]` to the crate attributes to enable -error[E0015]: cannot call non-const fn ` as Iterator>::next` in constants +error[E0015]: cannot call non-const fn `> as Iterator>::next` in constants --> $DIR/issue-52443.rs:9:21 | LL | [(); { for _ in 0usize.. {}; 0}]; diff --git a/src/test/ui/suggestions/borrow-for-loop-head.stderr b/src/test/ui/suggestions/borrow-for-loop-head.stderr index 1059e3d1525aa..124e64bdb4f01 100644 --- a/src/test/ui/suggestions/borrow-for-loop-head.stderr +++ b/src/test/ui/suggestions/borrow-for-loop-head.stderr @@ -16,7 +16,7 @@ LL | for j in a { | ^ `a` moved due to this implicit call to `.into_iter()`, in previous iteration of loop | note: this function takes ownership of the receiver `self`, which moves `a` - --> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL + --> $SRC_DIR/core/src/iter/loop_desugar.rs:LL:COL | LL | fn into_iter(self) -> Self::IntoIter; | ^^^^ diff --git a/src/test/ui/suggestions/for-i-in-vec.stderr b/src/test/ui/suggestions/for-i-in-vec.stderr index 88be9e30a7641..3d2becd763de5 100644 --- a/src/test/ui/suggestions/for-i-in-vec.stderr +++ b/src/test/ui/suggestions/for-i-in-vec.stderr @@ -8,7 +8,7 @@ LL | for _ in self.v { | move occurs because `self.v` has type `Vec`, which does not implement the `Copy` trait | note: this function takes ownership of the receiver `self`, which moves `self.v` - --> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL + --> $SRC_DIR/core/src/iter/loop_desugar.rs:LL:COL | LL | fn into_iter(self) -> Self::IntoIter; | ^^^^ @@ -41,7 +41,7 @@ LL | for loader in *LOADERS { | move occurs because value has type `Vec<&u8>`, which does not implement the `Copy` trait | note: this function takes ownership of the receiver `self`, which moves value - --> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL + --> $SRC_DIR/core/src/iter/loop_desugar.rs:LL:COL | LL | fn into_iter(self) -> Self::IntoIter; | ^^^^ diff --git a/src/test/ui/suggestions/slice-issue-87994.stderr b/src/test/ui/suggestions/slice-issue-87994.stderr index fd2a44f9a82ba..e208208cf3a84 100644 --- a/src/test/ui/suggestions/slice-issue-87994.stderr +++ b/src/test/ui/suggestions/slice-issue-87994.stderr @@ -2,10 +2,10 @@ error[E0277]: the size for values of type `[i32]` cannot be known at compilation --> $DIR/slice-issue-87994.rs:3:12 | LL | for _ in v[1..] { - | ^^^^^^ expected an implementor of trait `IntoIterator` + | ^^^^^^ expected an implementor of trait `iter::loop_desugar::IntoIterator` | - = note: the trait bound `[i32]: IntoIterator` is not satisfied - = note: required because of the requirements on the impl of `IntoIterator` for `[i32]` + = note: the trait bound `[i32]: iter::loop_desugar::IntoIterator` is not satisfied + = note: required because of the requirements on the impl of `iter::loop_desugar::IntoIterator` for `[i32]` help: consider borrowing here | LL | for _ in &v[1..] { @@ -32,10 +32,10 @@ error[E0277]: the size for values of type `[K]` cannot be known at compilation t --> $DIR/slice-issue-87994.rs:11:13 | LL | for i2 in v2[1..] { - | ^^^^^^^ expected an implementor of trait `IntoIterator` + | ^^^^^^^ expected an implementor of trait `iter::loop_desugar::IntoIterator` | - = note: the trait bound `[K]: IntoIterator` is not satisfied - = note: required because of the requirements on the impl of `IntoIterator` for `[K]` + = note: the trait bound `[K]: iter::loop_desugar::IntoIterator` is not satisfied + = note: required because of the requirements on the impl of `iter::loop_desugar::IntoIterator` for `[K]` help: consider borrowing here | LL | for i2 in &v2[1..] {