From dbad8c93680710e80c54cbaf8416821f7a5750c8 Mon Sep 17 00:00:00 2001 From: Matthew Jasper Date: Mon, 17 Aug 2020 19:34:44 +0100 Subject: [PATCH] Use min_specialization in libcore --- library/core/src/iter/adapters/fuse.rs | 30 +-- library/core/src/iter/adapters/mod.rs | 93 ++++---- library/core/src/iter/adapters/zip.rs | 181 +++++++++++++--- library/core/src/iter/traits/iterator.rs | 12 ++ library/core/src/lib.rs | 2 +- library/core/src/slice/mod.rs | 256 +++++++++++++---------- library/core/src/str/mod.rs | 16 +- library/core/tests/iter.rs | 97 +++++++++ 8 files changed, 487 insertions(+), 200 deletions(-) diff --git a/library/core/src/iter/adapters/fuse.rs b/library/core/src/iter/adapters/fuse.rs index d2e2fc04a2b..94ba6f56476 100644 --- a/library/core/src/iter/adapters/fuse.rs +++ b/library/core/src/iter/adapters/fuse.rs @@ -1,7 +1,7 @@ use crate::intrinsics; -use crate::iter::{ - DoubleEndedIterator, ExactSizeIterator, FusedIterator, Iterator, TrustedRandomAccess, -}; +use crate::iter::adapters::zip::try_get_unchecked; +use crate::iter::TrustedRandomAccess; +use crate::iter::{DoubleEndedIterator, ExactSizeIterator, FusedIterator, Iterator}; use crate::ops::Try; /// An iterator that yields `None` forever after the underlying iterator @@ -114,6 +114,19 @@ where { FuseImpl::find(self, predicate) } + + #[inline] + unsafe fn get_unchecked(&mut self, idx: usize) -> Self::Item + where + Self: TrustedRandomAccess, + { + match self.iter { + // SAFETY: the caller must uphold the contract for `Iterator::get_unchecked`. + Some(ref mut iter) => unsafe { try_get_unchecked(iter, idx) }, + // SAFETY: the caller asserts there is an item at `i`, so we're not exhausted. + None => unsafe { intrinsics::unreachable() }, + } + } } #[stable(feature = "rust1", since = "1.0.0")] @@ -172,19 +185,12 @@ where } } +#[doc(hidden)] +#[unstable(feature = "trusted_random_access", issue = "none")] unsafe impl TrustedRandomAccess for Fuse where I: TrustedRandomAccess, { - unsafe fn get_unchecked(&mut self, i: usize) -> I::Item { - match self.iter { - // SAFETY: the caller must uphold the contract for `TrustedRandomAccess::get_unchecked`. - Some(ref mut iter) => unsafe { iter.get_unchecked(i) }, - // SAFETY: the caller asserts there is an item at `i`, so we're not exhausted. - None => unsafe { intrinsics::unreachable() }, - } - } - fn may_have_side_effect() -> bool { I::may_have_side_effect() } diff --git a/library/core/src/iter/adapters/mod.rs b/library/core/src/iter/adapters/mod.rs index 133643a0c7f..9fcd137e1a6 100644 --- a/library/core/src/iter/adapters/mod.rs +++ b/library/core/src/iter/adapters/mod.rs @@ -15,6 +15,7 @@ pub use self::chain::Chain; #[stable(feature = "rust1", since = "1.0.0")] pub use self::flatten::{FlatMap, Flatten}; pub use self::fuse::Fuse; +use self::zip::try_get_unchecked; pub(crate) use self::zip::TrustedRandomAccess; pub use self::zip::Zip; @@ -213,6 +214,15 @@ where fn count(self) -> usize { self.it.count() } + + unsafe fn get_unchecked(&mut self, idx: usize) -> T + where + Self: TrustedRandomAccess, + { + // SAFETY: the caller must uphold the contract for + // `Iterator::get_unchecked`. + *unsafe { try_get_unchecked(&mut self.it, idx) } + } } #[stable(feature = "iter_copied", since = "1.36.0")] @@ -266,16 +276,11 @@ where } #[doc(hidden)] -unsafe impl<'a, I, T: 'a> TrustedRandomAccess for Copied +#[unstable(feature = "trusted_random_access", issue = "none")] +unsafe impl TrustedRandomAccess for Copied where - I: TrustedRandomAccess, - T: Copy, + I: TrustedRandomAccess, { - unsafe fn get_unchecked(&mut self, i: usize) -> Self::Item { - // SAFETY: the caller must uphold the contract for `TrustedRandomAccess::get_unchecked`. - unsafe { *self.it.get_unchecked(i) } - } - #[inline] fn may_have_side_effect() -> bool { I::may_have_side_effect() @@ -344,6 +349,15 @@ where { self.it.map(T::clone).fold(init, f) } + + unsafe fn get_unchecked(&mut self, idx: usize) -> T + where + Self: TrustedRandomAccess, + { + // SAFETY: the caller must uphold the contract for + // `Iterator::get_unchecked`. + unsafe { try_get_unchecked(&mut self.it, idx).clone() } + } } #[stable(feature = "iter_cloned", since = "1.1.0")] @@ -397,36 +411,14 @@ where } #[doc(hidden)] -unsafe impl<'a, I, T: 'a> TrustedRandomAccess for Cloned +#[unstable(feature = "trusted_random_access", issue = "none")] +unsafe impl TrustedRandomAccess for Cloned where - I: TrustedRandomAccess, - T: Clone, + I: TrustedRandomAccess, { - default unsafe fn get_unchecked(&mut self, i: usize) -> Self::Item { - // SAFETY: the caller must uphold the contract for `TrustedRandomAccess::get_unchecked`. - unsafe { self.it.get_unchecked(i) }.clone() - } - - #[inline] - default fn may_have_side_effect() -> bool { - true - } -} - -#[doc(hidden)] -unsafe impl<'a, I, T: 'a> TrustedRandomAccess for Cloned -where - I: TrustedRandomAccess, - T: Copy, -{ - unsafe fn get_unchecked(&mut self, i: usize) -> Self::Item { - // SAFETY: the caller must uphold the contract for `TrustedRandomAccess::get_unchecked`. - unsafe { *self.it.get_unchecked(i) } - } - #[inline] fn may_have_side_effect() -> bool { - I::may_have_side_effect() + true } } @@ -872,6 +864,15 @@ where { self.iter.fold(init, map_fold(self.f, g)) } + + unsafe fn get_unchecked(&mut self, idx: usize) -> B + where + Self: TrustedRandomAccess, + { + // SAFETY: the caller must uphold the contract for + // `Iterator::get_unchecked`. + unsafe { (self.f)(try_get_unchecked(&mut self.iter, idx)) } + } } #[stable(feature = "rust1", since = "1.0.0")] @@ -927,15 +928,11 @@ where } #[doc(hidden)] -unsafe impl TrustedRandomAccess for Map +#[unstable(feature = "trusted_random_access", issue = "none")] +unsafe impl TrustedRandomAccess for Map where I: TrustedRandomAccess, - F: FnMut(I::Item) -> B, { - unsafe fn get_unchecked(&mut self, i: usize) -> Self::Item { - // SAFETY: the caller must uphold the contract for `TrustedRandomAccess::get_unchecked`. - (self.f)(unsafe { self.iter.get_unchecked(i) }) - } #[inline] fn may_have_side_effect() -> bool { true @@ -1306,6 +1303,16 @@ where self.iter.fold(init, enumerate(self.count, fold)) } + + unsafe fn get_unchecked(&mut self, idx: usize) -> ::Item + where + Self: TrustedRandomAccess, + { + // SAFETY: the caller must uphold the contract for + // `Iterator::get_unchecked`. + let value = unsafe { try_get_unchecked(&mut self.iter, idx) }; + (Add::add(self.count, idx), value) + } } #[stable(feature = "rust1", since = "1.0.0")] @@ -1391,15 +1398,11 @@ where } #[doc(hidden)] +#[unstable(feature = "trusted_random_access", issue = "none")] unsafe impl TrustedRandomAccess for Enumerate where I: TrustedRandomAccess, { - unsafe fn get_unchecked(&mut self, i: usize) -> (usize, I::Item) { - // SAFETY: the caller must uphold the contract for `TrustedRandomAccess::get_unchecked`. - (self.count + i, unsafe { self.iter.get_unchecked(i) }) - } - fn may_have_side_effect() -> bool { I::may_have_side_effect() } diff --git a/library/core/src/iter/adapters/zip.rs b/library/core/src/iter/adapters/zip.rs index 985e6561665..6cb61896483 100644 --- a/library/core/src/iter/adapters/zip.rs +++ b/library/core/src/iter/adapters/zip.rs @@ -1,4 +1,5 @@ use crate::cmp; +use crate::fmt::{self, Debug}; use super::super::{DoubleEndedIterator, ExactSizeIterator, FusedIterator, Iterator, TrustedLen}; @@ -9,7 +10,7 @@ use super::super::{DoubleEndedIterator, ExactSizeIterator, FusedIterator, Iterat /// /// [`zip`]: trait.Iterator.html#method.zip /// [`Iterator`]: trait.Iterator.html -#[derive(Clone, Debug)] +#[derive(Clone)] #[must_use = "iterators are lazy and do nothing unless consumed"] #[stable(feature = "rust1", since = "1.0.0")] pub struct Zip { @@ -56,6 +57,16 @@ where fn nth(&mut self, n: usize) -> Option { ZipImpl::nth(self, n) } + + #[inline] + unsafe fn get_unchecked(&mut self, idx: usize) -> Self::Item + where + Self: TrustedRandomAccess, + { + // SAFETY: `ZipImpl::get_unchecked` has same safety requirements as + // `Iterator::get_unchecked`. + unsafe { ZipImpl::get_unchecked(self, idx) } + } } #[stable(feature = "rust1", since = "1.0.0")] @@ -82,6 +93,10 @@ trait ZipImpl { where A: DoubleEndedIterator + ExactSizeIterator, B: DoubleEndedIterator + ExactSizeIterator; + // This has the same safety requirements as `Iterator::get_unchecked` + unsafe fn get_unchecked(&mut self, idx: usize) -> ::Item + where + Self: Iterator + TrustedRandomAccess; } // General Zip impl @@ -156,16 +171,23 @@ where (lower, upper) } + + default unsafe fn get_unchecked(&mut self, _idx: usize) -> ::Item + where + Self: TrustedRandomAccess, + { + unreachable!("Always specialized"); + } } #[doc(hidden)] impl ZipImpl for Zip where - A: TrustedRandomAccess, - B: TrustedRandomAccess, + A: TrustedRandomAccess + Iterator, + B: TrustedRandomAccess + Iterator, { fn new(a: A, b: B) -> Self { - let len = cmp::min(a.len(), b.len()); + let len = cmp::min(a.size(), b.size()); Zip { a, b, index: 0, len } } @@ -176,7 +198,7 @@ where self.index += 1; // SAFETY: `i` is smaller than `self.len`, thus smaller than `self.a.len()` and `self.b.len()` unsafe { Some((self.a.get_unchecked(i), self.b.get_unchecked(i))) } - } else if A::may_have_side_effect() && self.index < self.a.len() { + } else if A::may_have_side_effect() && self.index < self.a.size() { // match the base implementation's potential side effects // SAFETY: we just checked that `self.index` < `self.a.len()` unsafe { @@ -227,20 +249,26 @@ where A: DoubleEndedIterator + ExactSizeIterator, B: DoubleEndedIterator + ExactSizeIterator, { - // Adjust a, b to equal length - if A::may_have_side_effect() { - let sz = self.a.len(); - if sz > self.len { - for _ in 0..sz - cmp::max(self.len, self.index) { - self.a.next_back(); + let a_side_effect = A::may_have_side_effect(); + let b_side_effect = B::may_have_side_effect(); + if a_side_effect || b_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_side_effect && sz_a > self.len { + for _ in 0..sz_a - cmp::max(self.len, self.index) { + self.a.next_back(); + } } - } - } - if B::may_have_side_effect() { - let sz = self.b.len(); - if sz > self.len { - for _ in 0..sz - self.len { - self.b.next_back(); + let sz_b = self.b.size(); + if b_side_effect && sz_b > self.len { + for _ in 0..sz_b - self.len { + self.b.next_back(); + } } } } @@ -254,6 +282,13 @@ where None } } + + #[inline] + unsafe fn get_unchecked(&mut self, idx: usize) -> ::Item { + // SAFETY: the caller must uphold the contract for + // `Iterator::get_unchecked`. + unsafe { (self.a.get_unchecked(idx), self.b.get_unchecked(idx)) } + } } #[stable(feature = "rust1", since = "1.0.0")] @@ -265,16 +300,12 @@ where } #[doc(hidden)] +#[unstable(feature = "trusted_random_access", issue = "none")] unsafe impl TrustedRandomAccess for Zip where A: TrustedRandomAccess, B: TrustedRandomAccess, { - unsafe fn get_unchecked(&mut self, i: usize) -> (A::Item, B::Item) { - // SAFETY: the caller must uphold the contract for `TrustedRandomAccess::get_unchecked`. - unsafe { (self.a.get_unchecked(i), self.b.get_unchecked(i)) } - } - fn may_have_side_effect() -> bool { A::may_have_side_effect() || B::may_have_side_effect() } @@ -296,19 +327,109 @@ where { } +#[stable(feature = "rust1", since = "1.0.0")] +impl Debug for Zip { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + ZipFmt::fmt(self, f) + } +} + +trait ZipFmt { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result; +} + +impl ZipFmt for Zip { + default fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Zip").field("a", &self.a).field("b", &self.b).finish() + } +} + +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 .len() and size_hint() must be exact. -/// `.len()` must be cheap to call. +/// The iterator's `size_hint` must be exact and cheap to call. /// -/// .get_unchecked() must return distinct mutable references for distinct -/// indices (if applicable), and must return a valid reference if index is in -/// 0..self.len(). -pub(crate) unsafe trait TrustedRandomAccess: ExactSizeIterator { - unsafe fn get_unchecked(&mut self, i: usize) -> Self::Item; +/// `size` may not be overridden. +/// +/// `::get_unchecked` must be safe to call provided the +/// following conditions are met. +/// +/// 1. `0 <= idx` and `idx < self.size()`. +/// 2. If `self: !Clone`, then `get_unchecked` is never called with the same +/// index on `self` more than once. +/// 3. After `self.get_unchecked(idx)` has been called then `next_back` will +/// only be called at most `self.size() - idx - 1` times. +/// 4. After `get_unchecked` is called, then only the following methods will be +/// called on `self`: +/// * `std::clone::Clone::clone` +/// * `std::iter::Iterator::size_hint()` +/// * `std::iter::Iterator::next_back()` +/// * `std::iter::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 +/// `get_unchecked`, assuming that the required traits are implemented. +/// * It must also be safe to drop `self` after calling `get_unchecked`. +#[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 + } /// Returns `true` if getting an iterator element may have /// side effects. Remember to take inner iterators into account. fn may_have_side_effect() -> bool; } + +/// Like `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::get_unchecked`. + unsafe { it.try_get_unchecked(idx) } +} + +unsafe trait SpecTrustedRandomAccess: Iterator { + /// If `Self: TrustedRandomAccess`, it must be safe to call a + /// `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::get_unchecked`. + unsafe { self.get_unchecked(index) } + } +} diff --git a/library/core/src/iter/traits/iterator.rs b/library/core/src/iter/traits/iterator.rs index f89b616c4e2..8204f835ba3 100644 --- a/library/core/src/iter/traits/iterator.rs +++ b/library/core/src/iter/traits/iterator.rs @@ -6,6 +6,7 @@ use crate::cmp::{self, Ordering}; use crate::ops::{Add, Try}; use super::super::LoopState; +use super::super::TrustedRandomAccess; use super::super::{Chain, Cloned, Copied, Cycle, Enumerate, Filter, FilterMap, Fuse}; use super::super::{FlatMap, Flatten}; use super::super::{FromIterator, Product, Sum, Zip}; @@ -3244,6 +3245,17 @@ pub trait Iterator { { self.map(f).is_sorted() } + + /// See [TrustedRandomAccess] + #[inline] + #[doc(hidden)] + #[unstable(feature = "trusted_random_access", issue = "none")] + unsafe fn get_unchecked(&mut self, _idx: usize) -> Self::Item + where + Self: TrustedRandomAccess, + { + unreachable!("Always specialized"); + } } #[stable(feature = "rust1", since = "1.0.0")] diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 763457d485d..ea461b1461e 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -117,7 +117,7 @@ #![feature(repr_simd, platform_intrinsics)] #![feature(rustc_attrs)] #![feature(simd_ffi)] -#![feature(specialization)] +#![feature(min_specialization)] #![feature(staged_api)] #![feature(std_internals)] #![feature(stmt_expr_attributes)] diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index 59c536bcff9..0d97ddb29af 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -3647,21 +3647,21 @@ macro_rules! iterator { struct $name:ident -> $ptr:ty, $elem:ty, $raw_mut:tt, - {$( $mut_:tt )*}, + {$( $mut_:tt )?}, {$($extra:tt)*} ) => { // Returns the first element and moves the start of the iterator forwards by 1. // Greatly improves performance compared to an inlined function. The iterator // must not be empty. macro_rules! next_unchecked { - ($self: ident) => {& $( $mut_ )* *$self.post_inc_start(1)} + ($self: ident) => {& $( $mut_ )? *$self.post_inc_start(1)} } // Returns the last element and moves the end of the iterator backwards by 1. // Greatly improves performance compared to an inlined function. The iterator // must not be empty. macro_rules! next_back_unchecked { - ($self: ident) => {& $( $mut_ )* *$self.pre_dec_end(1)} + ($self: ident) => {& $( $mut_ )? *$self.pre_dec_end(1)} } // Shrinks the iterator when T is a ZST, by moving the end of the iterator @@ -3921,6 +3921,21 @@ macro_rules! iterator { None } + #[doc(hidden)] + unsafe fn 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 + // the returned references is guaranteed to refer to an element + // of the slice and thus guaranteed to be valid. + // + // Also note that the caller also guarantees that we're never + // called with the same index again, and that no other methods + // that will access this subslice are called, so it is valid + // for the returned reference to be mutable in the case of + // `IterMut` + unsafe { & $( $mut_ )? * self.ptr.as_ptr().add(idx) } + } + $($extra)* } @@ -5005,6 +5020,15 @@ impl<'a, T> Iterator for Windows<'a, T> { Some(&self.v[start..]) } } + + #[doc(hidden)] + unsafe fn get_unchecked(&mut self, idx: usize) -> Self::Item { + // SAFETY: since the caller guarantees that `i` is in bounds, + // which means that `i` cannot overflow an `isize`, and the + // slice created by `from_raw_parts` is a subslice of `self.v` + // thus is guaranteed to be valid for the lifetime `'a` of `self.v`. + unsafe { from_raw_parts(self.v.as_ptr().add(idx), self.size) } + } } #[stable(feature = "rust1", since = "1.0.0")] @@ -5044,14 +5068,8 @@ unsafe impl TrustedLen for Windows<'_, T> {} impl FusedIterator for Windows<'_, T> {} #[doc(hidden)] +#[unstable(feature = "trusted_random_access", issue = "none")] unsafe impl<'a, T> TrustedRandomAccess for Windows<'a, T> { - unsafe fn get_unchecked(&mut self, i: usize) -> &'a [T] { - // SAFETY: since the caller guarantees that `i` is in bounds, - // which means that `i` cannot overflow an `isize`, and the - // slice created by `from_raw_parts` is a subslice of `self.v` - // thus is guaranteed to be valid for the lifetime `'a` of `self.v`. - unsafe { from_raw_parts(self.v.as_ptr().add(i), self.size) } - } fn may_have_side_effect() -> bool { false } @@ -5141,6 +5159,23 @@ impl<'a, T> Iterator for Chunks<'a, T> { Some(&self.v[start..]) } } + + #[doc(hidden)] + unsafe fn get_unchecked(&mut self, idx: usize) -> Self::Item { + let start = idx * self.chunk_size; + let end = match start.checked_add(self.chunk_size) { + None => self.v.len(), + Some(end) => cmp::min(end, self.v.len()), + }; + // SAFETY: the caller guarantees that `i` is in bounds, + // which means that `start` must be in bounds of the + // underlying `self.v` slice, and we made sure that `end` + // is also in bounds of `self.v`. Thus, `start` cannot overflow + // an `isize`, and the slice constructed by `from_raw_parts` + // is a subslice of `self.v` which is guaranteed to be valid + // for the lifetime `'a` of `self.v`. + unsafe { from_raw_parts(self.v.as_ptr().add(start), end - start) } + } } #[stable(feature = "rust1", since = "1.0.0")] @@ -5187,22 +5222,8 @@ unsafe impl TrustedLen for Chunks<'_, T> {} impl FusedIterator for Chunks<'_, T> {} #[doc(hidden)] +#[unstable(feature = "trusted_random_access", issue = "none")] unsafe impl<'a, T> TrustedRandomAccess for Chunks<'a, T> { - unsafe fn get_unchecked(&mut self, i: usize) -> &'a [T] { - let start = i * self.chunk_size; - let end = match start.checked_add(self.chunk_size) { - None => self.v.len(), - Some(end) => cmp::min(end, self.v.len()), - }; - // SAFETY: the caller guarantees that `i` is in bounds, - // which means that `start` must be in bounds of the - // underlying `self.v` slice, and we made sure that `end` - // is also in bounds of `self.v`. Thus, `start` cannot overflow - // an `isize`, and the slice constructed by `from_raw_parts` - // is a subslice of `self.v` which is guaranteed to be valid - // for the lifetime `'a` of `self.v`. - unsafe { from_raw_parts(self.v.as_ptr().add(start), end - start) } - } fn may_have_side_effect() -> bool { false } @@ -5287,6 +5308,22 @@ impl<'a, T> Iterator for ChunksMut<'a, T> { Some(&mut self.v[start..]) } } + + #[doc(hidden)] + unsafe fn get_unchecked(&mut self, idx: usize) -> Self::Item { + let start = idx * self.chunk_size; + let end = match start.checked_add(self.chunk_size) { + None => self.v.len(), + Some(end) => cmp::min(end, self.v.len()), + }; + // SAFETY: see comments for `Chunks::get_unchecked`. + // + // Also note that the caller also guarantees that we're never called + // with the same index again, and that no other methods that will + // access this subslice are called, so it is valid for the returned + // slice to be mutable. + unsafe { from_raw_parts_mut(self.v.as_mut_ptr().add(start), end - start) } + } } #[stable(feature = "rust1", since = "1.0.0")] @@ -5336,16 +5373,8 @@ unsafe impl TrustedLen for ChunksMut<'_, T> {} impl FusedIterator for ChunksMut<'_, T> {} #[doc(hidden)] +#[unstable(feature = "trusted_random_access", issue = "none")] unsafe impl<'a, T> TrustedRandomAccess for ChunksMut<'a, T> { - unsafe fn get_unchecked(&mut self, i: usize) -> &'a mut [T] { - let start = i * self.chunk_size; - let end = match start.checked_add(self.chunk_size) { - None => self.v.len(), - Some(end) => cmp::min(end, self.v.len()), - }; - // SAFETY: see comments for `Chunks::get_unchecked`. - unsafe { from_raw_parts_mut(self.v.as_mut_ptr().add(start), end - start) } - } fn may_have_side_effect() -> bool { false } @@ -5432,6 +5461,13 @@ impl<'a, T> Iterator for ChunksExact<'a, T> { fn last(mut self) -> Option { self.next_back() } + + #[doc(hidden)] + unsafe fn get_unchecked(&mut self, idx: usize) -> Self::Item { + let start = idx * self.chunk_size; + // SAFETY: mostly identical to `Chunks::get_unchecked`. + unsafe { from_raw_parts(self.v.as_ptr().add(start), self.chunk_size) } + } } #[stable(feature = "chunks_exact", since = "1.31.0")] @@ -5477,13 +5513,8 @@ unsafe impl TrustedLen for ChunksExact<'_, T> {} impl FusedIterator for ChunksExact<'_, T> {} #[doc(hidden)] -#[stable(feature = "chunks_exact", since = "1.31.0")] +#[unstable(feature = "trusted_random_access", issue = "none")] unsafe impl<'a, T> TrustedRandomAccess for ChunksExact<'a, T> { - unsafe fn get_unchecked(&mut self, i: usize) -> &'a [T] { - let start = i * self.chunk_size; - // SAFETY: mostly identical to `Chunks::get_unchecked`. - unsafe { from_raw_parts(self.v.as_ptr().add(start), self.chunk_size) } - } fn may_have_side_effect() -> bool { false } @@ -5564,6 +5595,13 @@ impl<'a, T> Iterator for ChunksExactMut<'a, T> { fn last(mut self) -> Option { self.next_back() } + + #[doc(hidden)] + unsafe fn get_unchecked(&mut self, idx: usize) -> Self::Item { + let start = idx * self.chunk_size; + // SAFETY: see comments for `ChunksMut::get_unchecked`. + unsafe { from_raw_parts_mut(self.v.as_mut_ptr().add(start), self.chunk_size) } + } } #[stable(feature = "chunks_exact", since = "1.31.0")] @@ -5612,13 +5650,8 @@ unsafe impl TrustedLen for ChunksExactMut<'_, T> {} impl FusedIterator for ChunksExactMut<'_, T> {} #[doc(hidden)] -#[stable(feature = "chunks_exact", since = "1.31.0")] +#[unstable(feature = "trusted_random_access", issue = "none")] unsafe impl<'a, T> TrustedRandomAccess for ChunksExactMut<'a, T> { - unsafe fn get_unchecked(&mut self, i: usize) -> &'a mut [T] { - let start = i * self.chunk_size; - // SAFETY: see comments for `ChunksExactMut::get_unchecked`. - unsafe { from_raw_parts_mut(self.v.as_mut_ptr().add(start), self.chunk_size) } - } fn may_have_side_effect() -> bool { false } @@ -5689,6 +5722,12 @@ impl<'a, T, const N: usize> Iterator for ArrayChunks<'a, T, N> { fn last(self) -> Option { self.iter.last() } + + unsafe fn get_unchecked(&mut self, i: usize) -> &'a [T; N] { + // SAFETY: The safety guarantees of `get_unchecked` are transferred to + // the caller. + unsafe { self.iter.get_unchecked(i) } + } } #[unstable(feature = "array_chunks", issue = "74985")] @@ -5720,11 +5759,6 @@ 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 fn get_unchecked(&mut self, i: usize) -> &'a [T; N] { - // SAFETY: The safety guarantees of `get_unchecked` are transferred to - // the caller. - unsafe { self.iter.get_unchecked(i) } - } fn may_have_side_effect() -> bool { false } @@ -5817,6 +5851,17 @@ impl<'a, T> Iterator for RChunks<'a, T> { Some(&self.v[0..end]) } } + + #[doc(hidden)] + unsafe fn get_unchecked(&mut self, idx: usize) -> Self::Item { + let end = self.v.len() - idx * self.chunk_size; + let start = match end.checked_sub(self.chunk_size) { + None => 0, + Some(start) => start, + }; + // SAFETY: mostly identical to `Chunks::get_unchecked`. + unsafe { from_raw_parts(self.v.as_ptr().add(start), end - start) } + } } #[stable(feature = "rchunks", since = "1.31.0")] @@ -5862,17 +5907,8 @@ unsafe impl TrustedLen for RChunks<'_, T> {} impl FusedIterator for RChunks<'_, T> {} #[doc(hidden)] -#[stable(feature = "rchunks", since = "1.31.0")] +#[unstable(feature = "trusted_random_access", issue = "none")] unsafe impl<'a, T> TrustedRandomAccess for RChunks<'a, T> { - unsafe fn get_unchecked(&mut self, i: usize) -> &'a [T] { - let end = self.v.len() - i * self.chunk_size; - let start = match end.checked_sub(self.chunk_size) { - None => 0, - Some(start) => start, - }; - // SAFETY: mostly identical to `Chunks::get_unchecked`. - unsafe { from_raw_parts(self.v.as_ptr().add(start), end - start) } - } fn may_have_side_effect() -> bool { false } @@ -5961,6 +5997,17 @@ impl<'a, T> Iterator for RChunksMut<'a, T> { Some(&mut self.v[0..end]) } } + + #[doc(hidden)] + unsafe fn get_unchecked(&mut self, idx: usize) -> Self::Item { + let end = self.v.len() - idx * self.chunk_size; + let start = match end.checked_sub(self.chunk_size) { + None => 0, + Some(start) => start, + }; + // SAFETY: see comments for `RChunks::get_unchecked` and `ChunksMut::get_unchecked` + unsafe { from_raw_parts_mut(self.v.as_mut_ptr().add(start), end - start) } + } } #[stable(feature = "rchunks", since = "1.31.0")] @@ -6008,17 +6055,8 @@ unsafe impl TrustedLen for RChunksMut<'_, T> {} impl FusedIterator for RChunksMut<'_, T> {} #[doc(hidden)] -#[stable(feature = "rchunks", since = "1.31.0")] +#[unstable(feature = "trusted_random_access", issue = "none")] unsafe impl<'a, T> TrustedRandomAccess for RChunksMut<'a, T> { - unsafe fn get_unchecked(&mut self, i: usize) -> &'a mut [T] { - let end = self.v.len() - i * self.chunk_size; - let start = match end.checked_sub(self.chunk_size) { - None => 0, - Some(start) => start, - }; - // SAFETY: see comments for `RChunks::get_unchecked`. - unsafe { from_raw_parts_mut(self.v.as_mut_ptr().add(start), end - start) } - } fn may_have_side_effect() -> bool { false } @@ -6105,6 +6143,15 @@ impl<'a, T> Iterator for RChunksExact<'a, T> { fn last(mut self) -> Option { self.next_back() } + + #[doc(hidden)] + unsafe fn get_unchecked(&mut self, idx: usize) -> Self::Item { + let end = self.v.len() - idx * self.chunk_size; + let start = end - self.chunk_size; + // SAFETY: + // SAFETY: mostmy identical to `Chunks::get_unchecked`. + unsafe { from_raw_parts(self.v.as_ptr().add(start), self.chunk_size) } + } } #[stable(feature = "rchunks", since = "1.31.0")] @@ -6153,14 +6200,8 @@ unsafe impl TrustedLen for RChunksExact<'_, T> {} impl FusedIterator for RChunksExact<'_, T> {} #[doc(hidden)] -#[stable(feature = "rchunks", since = "1.31.0")] +#[unstable(feature = "trusted_random_access", issue = "none")] unsafe impl<'a, T> TrustedRandomAccess for RChunksExact<'a, T> { - unsafe fn get_unchecked(&mut self, i: usize) -> &'a [T] { - let end = self.v.len() - i * self.chunk_size; - let start = end - self.chunk_size; - // SAFETY: mostmy identical to `Chunks::get_unchecked`. - unsafe { from_raw_parts(self.v.as_ptr().add(start), self.chunk_size) } - } fn may_have_side_effect() -> bool { false } @@ -6243,6 +6284,14 @@ impl<'a, T> Iterator for RChunksExactMut<'a, T> { fn last(mut self) -> Option { self.next_back() } + + #[doc(hidden)] + unsafe fn get_unchecked(&mut self, idx: usize) -> Self::Item { + let end = self.v.len() - idx * self.chunk_size; + let start = end - self.chunk_size; + // SAFETY: see comments for `RChunksMut::get_unchecked`. + unsafe { from_raw_parts_mut(self.v.as_mut_ptr().add(start), self.chunk_size) } + } } #[stable(feature = "rchunks", since = "1.31.0")] @@ -6293,14 +6342,8 @@ unsafe impl TrustedLen for RChunksExactMut<'_, T> {} impl FusedIterator for RChunksExactMut<'_, T> {} #[doc(hidden)] -#[stable(feature = "rchunks", since = "1.31.0")] +#[unstable(feature = "trusted_random_access", issue = "none")] unsafe impl<'a, T> TrustedRandomAccess for RChunksExactMut<'a, T> { - unsafe fn get_unchecked(&mut self, i: usize) -> &'a mut [T] { - let end = self.v.len() - i * self.chunk_size; - let start = end - self.chunk_size; - // SAFETY: see comments for `RChunksExact::get_unchecked`. - unsafe { from_raw_parts_mut(self.v.as_mut_ptr().add(start), self.chunk_size) } - } fn may_have_side_effect() -> bool { false } @@ -6543,18 +6586,20 @@ where } // Use an equal-pointer optimization when types are `Eq` -impl SlicePartialEq for [A] +// We can't make `A` and `B` the same type because `min_specialization` won't +// allow it. +impl SlicePartialEq for [A] where - A: PartialEq + Eq, + A: MarkerEq, { - default fn equal(&self, other: &[A]) -> bool { + default fn equal(&self, other: &[B]) -> bool { if self.len() != other.len() { return false; } // While performance would suffer if `guaranteed_eq` just returned `false` // for all arguments, correctness and return value of this function are not affected. - if self.as_ptr().guaranteed_eq(other.as_ptr()) { + if self.as_ptr().guaranteed_eq(other.as_ptr() as *const A) { return true; } @@ -6563,18 +6608,18 @@ where } // Use memcmp for bytewise equality when the types allow -impl SlicePartialEq for [A] +impl SlicePartialEq for [A] where - A: PartialEq + BytewiseEquality, + A: BytewiseEquality, { - fn equal(&self, other: &[A]) -> bool { + fn equal(&self, other: &[B]) -> bool { if self.len() != other.len() { return false; } // While performance would suffer if `guaranteed_eq` just returned `false` // for all arguments, correctness and return value of this function are not affected. - if self.as_ptr().guaranteed_eq(other.as_ptr()) { + if self.as_ptr().guaranteed_eq(other.as_ptr() as *const A) { return true; } // SAFETY: `self` and `other` are references and are thus guaranteed to be valid. @@ -6631,6 +6676,7 @@ impl SlicePartialOrd for A { } } +#[rustc_specialization_trait] trait AlwaysApplicableOrd: SliceOrd + Ord {} macro_rules! always_applicable_ord { @@ -6695,15 +6741,22 @@ impl SliceOrd for u8 { } } +// Hack to allow specializing on `Eq` even though `Eq` has a method. +#[rustc_unsafe_specialization_marker] +trait MarkerEq: PartialEq {} + +impl MarkerEq for T {} + #[doc(hidden)] /// Trait implemented for types that can be compared for equality using /// their bytewise representation -trait BytewiseEquality: Eq + Copy {} +#[rustc_specialization_trait] +trait BytewiseEquality: MarkerEq + Copy {} macro_rules! impl_marker_for { ($traitname:ident, $($ty:ty)*) => { $( - impl $traitname for $ty { } + impl $traitname<$ty> for $ty { } )* } } @@ -6712,25 +6765,16 @@ impl_marker_for!(BytewiseEquality, u8 i8 u16 i16 u32 i32 u64 i64 u128 i128 usize isize char bool); #[doc(hidden)] +#[unstable(feature = "trusted_random_access", issue = "none")] unsafe impl<'a, T> TrustedRandomAccess for Iter<'a, T> { - unsafe fn get_unchecked(&mut self, i: usize) -> &'a T { - // SAFETY: the caller must guarantee that `i` is in bounds - // of the underlying slice, so `i` cannot overflow an `isize`, - // and the returned references is guaranteed to refer to an element - // of the slice and thus guaranteed to be valid. - unsafe { &*self.ptr.as_ptr().add(i) } - } fn may_have_side_effect() -> bool { false } } #[doc(hidden)] +#[unstable(feature = "trusted_random_access", issue = "none")] unsafe impl<'a, T> TrustedRandomAccess for IterMut<'a, T> { - unsafe fn get_unchecked(&mut self, i: usize) -> &'a mut T { - // SAFETY: see comments for `Iter::get_unchecked`. - unsafe { &mut *self.ptr.as_ptr().add(i) } - } fn may_have_side_effect() -> bool { false } diff --git a/library/core/src/str/mod.rs b/library/core/src/str/mod.rs index 934f581f3fa..a7d4096a557 100644 --- a/library/core/src/str/mod.rs +++ b/library/core/src/str/mod.rs @@ -13,8 +13,9 @@ use self::pattern::{DoubleEndedSearcher, ReverseSearcher, Searcher}; use crate::char; use crate::fmt::{self, Write}; +use crate::iter::TrustedRandomAccess; use crate::iter::{Chain, FlatMap, Flatten}; -use crate::iter::{Copied, Filter, FusedIterator, Map, TrustedLen, TrustedRandomAccess}; +use crate::iter::{Copied, Filter, FusedIterator, Map, TrustedLen}; use crate::mem; use crate::ops::Try; use crate::option; @@ -819,6 +820,13 @@ impl Iterator for Bytes<'_> { { self.0.rposition(predicate) } + + #[inline] + unsafe fn get_unchecked(&mut self, idx: usize) -> u8 { + // SAFETY: the caller must uphold the safety contract + // for `Iterator::get_unchecked`. + unsafe { self.0.get_unchecked(idx) } + } } #[stable(feature = "rust1", since = "1.0.0")] @@ -862,12 +870,8 @@ impl FusedIterator for Bytes<'_> {} unsafe impl TrustedLen for Bytes<'_> {} #[doc(hidden)] +#[unstable(feature = "trusted_random_access", issue = "none")] unsafe impl TrustedRandomAccess for Bytes<'_> { - unsafe fn get_unchecked(&mut self, i: usize) -> u8 { - // SAFETY: the caller must uphold the safety contract - // for `TrustedRandomAccess::get_unchecked`. - unsafe { self.0.get_unchecked(i) } - } fn may_have_side_effect() -> bool { false } diff --git a/library/core/tests/iter.rs b/library/core/tests/iter.rs index 3b854b56c32..00e3972c42f 100644 --- a/library/core/tests/iter.rs +++ b/library/core/tests/iter.rs @@ -304,6 +304,103 @@ fn test_zip_nth_side_effects() { assert_eq!(b, vec![200, 300, 400, 500, 600]); } +#[test] +fn test_zip_next_back_side_effects() { + let mut a = Vec::new(); + let mut b = Vec::new(); + let mut iter = [1, 2, 3, 4, 5, 6] + .iter() + .cloned() + .map(|n| { + a.push(n); + n * 10 + }) + .zip([2, 3, 4, 5, 6, 7, 8].iter().cloned().map(|n| { + b.push(n * 100); + n * 1000 + })); + + // The second iterator is one item longer, so `next_back` is called on it + // one more time. + assert_eq!(iter.next_back(), Some((60, 7000))); + assert_eq!(iter.next_back(), Some((50, 6000))); + assert_eq!(iter.next_back(), Some((40, 5000))); + assert_eq!(iter.next_back(), Some((30, 4000))); + assert_eq!(a, vec![6, 5, 4, 3]); + assert_eq!(b, vec![800, 700, 600, 500, 400]); +} + +#[test] +fn test_zip_nth_back_side_effects() { + let mut a = Vec::new(); + let mut b = Vec::new(); + let value = [1, 2, 3, 4, 5, 6] + .iter() + .cloned() + .map(|n| { + a.push(n); + n * 10 + }) + .zip([2, 3, 4, 5, 6, 7, 8].iter().cloned().map(|n| { + b.push(n * 100); + n * 1000 + })) + .nth_back(3); + assert_eq!(value, Some((30, 4000))); + assert_eq!(a, vec![6, 5, 4, 3]); + assert_eq!(b, vec![800, 700, 600, 500, 400]); +} + +#[test] +fn test_zip_next_back_side_effects_exhausted() { + let mut a = Vec::new(); + let mut b = Vec::new(); + let mut iter = [1, 2, 3, 4, 5, 6] + .iter() + .cloned() + .map(|n| { + a.push(n); + n * 10 + }) + .zip([2, 3, 4].iter().cloned().map(|n| { + b.push(n * 100); + n * 1000 + })); + + iter.next(); + iter.next(); + iter.next(); + iter.next(); + assert_eq!(iter.next_back(), None); + assert_eq!(a, vec![1, 2, 3, 4, 6, 5]); + assert_eq!(b, vec![200, 300, 400]); +} + +#[test] +fn test_zip_nth_back_side_effects_exhausted() { + let mut a = Vec::new(); + let mut b = Vec::new(); + let mut iter = [1, 2, 3, 4, 5, 6] + .iter() + .cloned() + .map(|n| { + a.push(n); + n * 10 + }) + .zip([2, 3, 4].iter().cloned().map(|n| { + b.push(n * 100); + n * 1000 + })); + + iter.next(); + iter.next(); + iter.next(); + iter.next(); + assert_eq!(iter.nth_back(0), None); + assert_eq!(a, vec![1, 2, 3, 4, 6, 5]); + assert_eq!(b, vec![200, 300, 400]); +} + #[test] fn test_iterator_step_by() { // Identity