Skip to content

Remove unsound TrustedRandomAccess implementations #85874

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Jul 29, 2021
15 changes: 8 additions & 7 deletions library/alloc/src/collections/vec_deque/iter.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use core::fmt;
use core::iter::{FusedIterator, TrustedLen, TrustedRandomAccess};
use core::iter::{FusedIterator, TrustedLen, TrustedRandomAccess, TrustedRandomAccessNoCoerce};
use core::ops::Try;

use super::{count, wrap_index, RingSlices};
@@ -104,11 +104,8 @@ impl<'a, T> Iterator for Iter<'a, T> {

#[inline]
#[doc(hidden)]
unsafe fn __iterator_get_unchecked(&mut self, idx: usize) -> Self::Item
where
Self: TrustedRandomAccess,
{
// Safety: The TrustedRandomAccess contract requires that callers only pass an index
unsafe fn __iterator_get_unchecked(&mut self, idx: usize) -> Self::Item {
// Safety: The TrustedRandomAccess contract requires that callers only pass an index
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Disclaimer: I'm still unfamiliar with __iterator_get_unchecked and the related specializations so I'm going to be asking some probably silly questions:

Is this comment still relevant?

Also, why doesn't this need a where Self: TrustedRandomAccessNoCoerce bound?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this comment still relevant?

It's unsafe code so it should have a SAFETY comment. And it's still correct since this method should only be called by specializing impls relying on TrustedRandomAccess.

Also, why doesn't this need a where Self: TrustedRandomAccessNoCoerce bound?

TrustedRandomAccess is implemented unconditionally for this iterator so the bound would be always true.

But maybe it would still make sense for consistency or in case the trait impls are removed at some point. But I don't think it's needed for safety here.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Aah, I see. I'm assuming that means that all the other __iterator_get_unchecked impls that leave of that bound also have it similarly implied? If that's the case I'd like to see those all have the bound added explicitly though that doesn't need to happen in this PR, but them being left off in some cases and not others seems somewhat confusing and could add to the maintenance burden.

Copy link
Member Author

@steffahn steffahn Jul 27, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But maybe it would still make sense for consistency

Actually consistency is the reason I removed the bound. Initially I had left the Self: TrustedRandomAccess bounds in place and relied on type errors to point out all the places that needed changing (to Self: TrustedRandomAccessNoCoerce). Afterwards, grepping for and going through all the remaining Self: TrustedRandomAccess occurrences (just to be sure) I came acoss this case (and another one, too IIRC) that didn’t produce an error message: Surprised me as well, I eventually figured out that the bound was entirely useless anyways. Looking at other iterators in std, it seems to have be commonly done without any Self: TrustedRandomAccess bound before when those weren’t necessary. On all kinds of iterators, e.g. all the ones on slices (e.g. also the ones from .chunks(…) and similar). IIRC, with the changes of this PR all the superfluous bounds are consistently gone.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If that's the case I'd like to see those all have the bound added explicitly though that doesn't need to happen in this PR, but them being left off in some cases and not others seems somewhat confusing and could add to the maintenance burden.

So as far as I know, after this PR only those implementations that need the bound will have it. Don’t quote me on that, I’d need to double check if this is indeed true. This very PR demonstrated that leaving the bounds off can actually help maintainance in some cases, like it helped me being able to use error messages to find all the places that needed to change, like I described above. The redundant Self: TrustedRandomAccess binding didn’t generate any error messages though, so their existence made the refactor a bit harder (since I needed to grep for things in order to fix those redundant bindings (for now, by removing them instead of changing them to Self: TrustedRandomAccesNoCoerce)).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here’s a “grep” example. Looks like there are about 15 Iterators without the bound on the method (on the branch of this PR, so before this PR there were 13 Iterators without the bound)

~/forks/rust/library   fix_unsound_zip_optimization  rg 'fn __iterator_' -A3
core/src/iter/range.rs
677:    unsafe fn __iterator_get_unchecked(&mut self, idx: usize) -> Self::Item
678-    where
679-        Self: TrustedRandomAccessNoCoerce,
680-    {

core/src/str/iter.rs
299:    unsafe fn __iterator_get_unchecked(&mut self, idx: usize) -> u8 {
300-        // SAFETY: the caller must uphold the safety contract
301-        // for `Iterator::__iterator_get_unchecked`.
302-        unsafe { self.0.__iterator_get_unchecked(idx) }

core/src/iter/adapters/fuse.rs
119:    unsafe fn __iterator_get_unchecked(&mut self, idx: usize) -> Self::Item
120-    where
121-        Self: TrustedRandomAccessNoCoerce,
122-    {

core/src/iter/adapters/enumerate.rs
117:    unsafe fn __iterator_get_unchecked(&mut self, idx: usize) -> <Self as Iterator>::Item
118-    where
119-        Self: TrustedRandomAccessNoCoerce,
120-    {

core/src/iter/adapters/map.rs
128:    unsafe fn __iterator_get_unchecked(&mut self, idx: usize) -> B
129-    where
130-        Self: TrustedRandomAccessNoCoerce,
131-    {

core/src/iter/adapters/cloned.rs
64:    unsafe fn __iterator_get_unchecked(&mut self, idx: usize) -> T
65-    where
66-        Self: TrustedRandomAccessNoCoerce,
67-    {

core/src/slice/iter.rs
1268:    unsafe fn __iterator_get_unchecked(&mut self, idx: usize) -> Self::Item {
1269-        // SAFETY: since the caller guarantees that `i` is in bounds,
1270-        // which means that `i` cannot overflow an `isize`, and the
1271-        // slice created by `from_raw_parts` is a subslice of `self.v`
--
1423:    unsafe fn __iterator_get_unchecked(&mut self, idx: usize) -> Self::Item {
1424-        let start = idx * self.chunk_size;
1425-        let end = match start.checked_add(self.chunk_size) {
1426-            None => self.v.len(),
--
1588:    unsafe fn __iterator_get_unchecked(&mut self, idx: usize) -> Self::Item {
1589-        let start = idx * self.chunk_size;
1590-        let end = match start.checked_add(self.chunk_size) {
1591-            None => self.v.len(),
--
1759:    unsafe fn __iterator_get_unchecked(&mut self, idx: usize) -> Self::Item {
1760-        let start = idx * self.chunk_size;
1761-        // SAFETY: mostly identical to `Chunks::__iterator_get_unchecked`.
1762-        unsafe { from_raw_parts(self.v.as_ptr().add(start), self.chunk_size) }
--
1911:    unsafe fn __iterator_get_unchecked(&mut self, idx: usize) -> Self::Item {
1912-        let start = idx * self.chunk_size;
1913-        // SAFETY: see comments for `ChunksMut::__iterator_get_unchecked`.
1914-        unsafe { from_raw_parts_mut(self.v.as_mut_ptr().add(start), self.chunk_size) }
--
2172:    unsafe fn __iterator_get_unchecked(&mut self, i: usize) -> &'a [T; N] {
2173-        // SAFETY: The safety guarantees of `__iterator_get_unchecked` are
2174-        // transferred to the caller.
2175-        unsafe { self.iter.__iterator_get_unchecked(i) }
--
2289:    unsafe fn __iterator_get_unchecked(&mut self, i: usize) -> &'a mut [T; N] {
2290-        // SAFETY: The safety guarantees of `__iterator_get_unchecked` are transferred to
2291-        // the caller.
2292-        unsafe { self.iter.__iterator_get_unchecked(i) }
--
2435:    unsafe fn __iterator_get_unchecked(&mut self, idx: usize) -> Self::Item {
2436-        let end = self.v.len() - idx * self.chunk_size;
2437-        let start = match end.checked_sub(self.chunk_size) {
2438-            None => 0,
--
2597:    unsafe fn __iterator_get_unchecked(&mut self, idx: usize) -> Self::Item {
2598-        let end = self.v.len() - idx * self.chunk_size;
2599-        let start = match end.checked_sub(self.chunk_size) {
2600-            None => 0,
--
2761:    unsafe fn __iterator_get_unchecked(&mut self, idx: usize) -> Self::Item {
2762-        let end = self.v.len() - idx * self.chunk_size;
2763-        let start = end - self.chunk_size;
2764-        // SAFETY:
--
2919:    unsafe fn __iterator_get_unchecked(&mut self, idx: usize) -> Self::Item {
2920-        let end = self.v.len() - idx * self.chunk_size;
2921-        let start = end - self.chunk_size;
2922-        // SAFETY: see comments for `RChunksMut::__iterator_get_unchecked`.

core/src/iter/adapters/copied.rs
80:    unsafe fn __iterator_get_unchecked(&mut self, idx: usize) -> T
81-    where
82-        Self: TrustedRandomAccessNoCoerce,
83-    {

core/src/iter/adapters/zip.rs
92:    unsafe fn __iterator_get_unchecked(&mut self, idx: usize) -> Self::Item
93-    where
94-        Self: TrustedRandomAccessNoCoerce,
95-    {

core/src/iter/traits/iterator.rs
3492:    unsafe fn __iterator_get_unchecked(&mut self, _idx: usize) -> Self::Item
3493-    where
3494-        Self: TrustedRandomAccessNoCoerce,
3495-    {

core/src/slice/iter/macros.rs
321:            unsafe fn __iterator_get_unchecked(&mut self, idx: usize) -> Self::Item {
322-                // SAFETY: the caller must guarantee that `i` is in bounds of
323-                // the underlying slice, so `i` cannot overflow an `isize`, and
324-                // the returned references is guaranteed to refer to an element

alloc/src/collections/vec_deque/iter.rs
107:    unsafe fn __iterator_get_unchecked(&mut self, idx: usize) -> Self::Item {
108-        // Safety: The TrustedRandomAccess contract requires that callers only pass an index
109-        // that is in bounds.
110-        unsafe {

alloc/src/collections/vec_deque/iter_mut.rs
93:    unsafe fn __iterator_get_unchecked(&mut self, idx: usize) -> Self::Item {
94-        // Safety: The TrustedRandomAccess contract requires that callers only pass an index
95-        // that is in bounds.
96-        unsafe {

alloc/src/vec/into_iter.rs
169:    unsafe fn __iterator_get_unchecked(&mut self, i: usize) -> Self::Item
170-    where
171-        Self: TrustedRandomAccessNoCoerce,
172-    {

// that is in bounds.
unsafe {
let idx = wrap_index(self.tail.wrapping_add(idx), self.ring.len());
@@ -177,6 +174,10 @@ unsafe impl<T> TrustedLen for Iter<'_, T> {}

#[doc(hidden)]
#[unstable(feature = "trusted_random_access", issue = "none")]
unsafe impl<T> TrustedRandomAccess for Iter<'_, T> {
unsafe impl<T> TrustedRandomAccess for Iter<'_, T> {}

#[doc(hidden)]
#[unstable(feature = "trusted_random_access", issue = "none")]
unsafe impl<T> TrustedRandomAccessNoCoerce for Iter<'_, T> {
const MAY_HAVE_SIDE_EFFECT: bool = false;
}
15 changes: 8 additions & 7 deletions library/alloc/src/collections/vec_deque/iter_mut.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use core::fmt;
use core::iter::{FusedIterator, TrustedLen, TrustedRandomAccess};
use core::iter::{FusedIterator, TrustedLen, TrustedRandomAccess, TrustedRandomAccessNoCoerce};
use core::marker::PhantomData;

use super::{count, wrap_index, RingSlices};
@@ -90,11 +90,8 @@ impl<'a, T> Iterator for IterMut<'a, T> {

#[inline]
#[doc(hidden)]
unsafe fn __iterator_get_unchecked(&mut self, idx: usize) -> Self::Item
where
Self: TrustedRandomAccess,
{
// Safety: The TrustedRandomAccess contract requires that callers only pass an index
unsafe fn __iterator_get_unchecked(&mut self, idx: usize) -> Self::Item {
// Safety: The TrustedRandomAccess contract requires that callers only pass an index
// that is in bounds.
unsafe {
let idx = wrap_index(self.tail.wrapping_add(idx), self.ring.len());
@@ -146,6 +143,10 @@ unsafe impl<T> TrustedLen for IterMut<'_, T> {}

#[doc(hidden)]
#[unstable(feature = "trusted_random_access", issue = "none")]
unsafe impl<T> TrustedRandomAccess for IterMut<'_, T> {
unsafe impl<T> TrustedRandomAccess for IterMut<'_, T> {}

#[doc(hidden)]
#[unstable(feature = "trusted_random_access", issue = "none")]
unsafe impl<T> TrustedRandomAccessNoCoerce for IterMut<'_, T> {
const MAY_HAVE_SIDE_EFFECT: bool = false;
}
11 changes: 8 additions & 3 deletions library/alloc/src/vec/into_iter.rs
Original file line number Diff line number Diff line change
@@ -2,7 +2,9 @@ use crate::alloc::{Allocator, Global};
use crate::raw_vec::RawVec;
use core::fmt;
use core::intrinsics::arith_offset;
use core::iter::{FusedIterator, InPlaceIterable, SourceIter, TrustedLen, TrustedRandomAccess};
use core::iter::{
FusedIterator, InPlaceIterable, SourceIter, TrustedLen, TrustedRandomAccessNoCoerce,
};
use core::marker::PhantomData;
use core::mem::{self};
use core::ptr::{self, NonNull};
@@ -166,7 +168,7 @@ impl<T, A: Allocator> Iterator for IntoIter<T, A> {
#[doc(hidden)]
unsafe fn __iterator_get_unchecked(&mut self, i: usize) -> Self::Item
where
Self: TrustedRandomAccess,
Self: TrustedRandomAccessNoCoerce,
{
// SAFETY: the caller must guarantee that `i` is in bounds of the
// `Vec<T>`, so `i` cannot overflow an `isize`, and the `self.ptr.add(i)`
@@ -219,7 +221,10 @@ unsafe impl<T, A: Allocator> TrustedLen for IntoIter<T, A> {}
#[unstable(issue = "none", feature = "std_internals")]
// T: Copy as approximation for !Drop since get_unchecked does not advance self.ptr
// and thus we can't implement drop-handling
unsafe impl<T, A: Allocator> TrustedRandomAccess for IntoIter<T, A>
//
// TrustedRandomAccess (without NoCoerce) must not be implemented because
// subtypes/supertypes of `T` might not be `Copy`
unsafe impl<T, A: Allocator> TrustedRandomAccessNoCoerce for IntoIter<T, A>
where
T: Copy,
{
21 changes: 19 additions & 2 deletions library/alloc/src/vec/source_iter_marker.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use core::iter::{InPlaceIterable, SourceIter, TrustedRandomAccess};
use core::iter::{InPlaceIterable, SourceIter, TrustedRandomAccessNoCoerce};
use core::mem::{self, ManuallyDrop};
use core::ptr::{self};

@@ -71,6 +71,18 @@ where
// drop any remaining values at the tail of the source
// but prevent drop of the allocation itself once IntoIter goes out of scope
// if the drop panics then we also leak any elements collected into dst_buf
//
// FIXME: Since `SpecInPlaceCollect::collect_in_place` above might use
// `__iterator_get_unchecked` internally, this call might be operating on
// a `vec::IntoIter` with incorrect internal state regarding which elements
// have already been “consumed”. However, the `TrustedRandomIteratorNoCoerce`
// implementation of `vec::IntoIter` is only present if the `Vec` elements
// don’t have a destructor, so it doesn’t matter if elements are “dropped multiple times”
// in this case.
// This argument technically currently lacks justification from the `# Safety` docs for
// `SourceIter`/`InPlaceIterable` and/or `TrustedRandomAccess`, so it might be possible that
// someone could inadvertently create new library unsoundness
// involving this `.forget_allocation_drop_remaining()` call.
src.forget_allocation_drop_remaining();

let vec = unsafe { Vec::from_raw_parts(dst_buf, len, cap) };
@@ -101,6 +113,11 @@ fn write_in_place_with_drop<T>(
trait SpecInPlaceCollect<T, I>: Iterator<Item = T> {
/// Collects an iterator (`self`) into the destination buffer (`dst`) and returns the number of items
/// 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
/// 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;
}

@@ -124,7 +141,7 @@ where

impl<T, I> SpecInPlaceCollect<T, I> for I
where
I: Iterator<Item = T> + TrustedRandomAccess,
I: Iterator<Item = T> + TrustedRandomAccessNoCoerce,
{
#[inline]
fn collect_in_place(&mut self, dst_buf: *mut T, end: *const T) -> usize {
14 changes: 10 additions & 4 deletions library/core/src/iter/adapters/cloned.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
use crate::iter::adapters::{zip::try_get_unchecked, TrustedRandomAccess};
use crate::iter::adapters::{
zip::try_get_unchecked, TrustedRandomAccess, TrustedRandomAccessNoCoerce,
};
use crate::iter::{FusedIterator, TrustedLen};
use crate::ops::Try;

@@ -61,7 +63,7 @@ where
#[doc(hidden)]
unsafe fn __iterator_get_unchecked(&mut self, idx: usize) -> T
where
Self: TrustedRandomAccess,
Self: TrustedRandomAccessNoCoerce,
{
// SAFETY: the caller must uphold the contract for
// `Iterator::__iterator_get_unchecked`.
@@ -121,9 +123,13 @@ where

#[doc(hidden)]
#[unstable(feature = "trusted_random_access", issue = "none")]
unsafe impl<I> TrustedRandomAccess for Cloned<I>
unsafe impl<I> TrustedRandomAccess for Cloned<I> where I: TrustedRandomAccess {}

#[doc(hidden)]
#[unstable(feature = "trusted_random_access", issue = "none")]
unsafe impl<I> TrustedRandomAccessNoCoerce for Cloned<I>
where
I: TrustedRandomAccess,
I: TrustedRandomAccessNoCoerce,
{
const MAY_HAVE_SIDE_EFFECT: bool = true;
}
14 changes: 10 additions & 4 deletions library/core/src/iter/adapters/copied.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
use crate::iter::adapters::{zip::try_get_unchecked, TrustedRandomAccess};
use crate::iter::adapters::{
zip::try_get_unchecked, TrustedRandomAccess, TrustedRandomAccessNoCoerce,
};
use crate::iter::{FusedIterator, TrustedLen};
use crate::ops::Try;

@@ -77,7 +79,7 @@ where
#[doc(hidden)]
unsafe fn __iterator_get_unchecked(&mut self, idx: usize) -> T
where
Self: TrustedRandomAccess,
Self: TrustedRandomAccessNoCoerce,
{
// SAFETY: the caller must uphold the contract for
// `Iterator::__iterator_get_unchecked`.
@@ -137,9 +139,13 @@ where

#[doc(hidden)]
#[unstable(feature = "trusted_random_access", issue = "none")]
unsafe impl<I> TrustedRandomAccess for Copied<I>
unsafe impl<I> TrustedRandomAccess for Copied<I> where I: TrustedRandomAccess {}

#[doc(hidden)]
#[unstable(feature = "trusted_random_access", issue = "none")]
unsafe impl<I> TrustedRandomAccessNoCoerce for Copied<I>
where
I: TrustedRandomAccess,
I: TrustedRandomAccessNoCoerce,
{
const MAY_HAVE_SIDE_EFFECT: bool = I::MAY_HAVE_SIDE_EFFECT;
}
14 changes: 10 additions & 4 deletions library/core/src/iter/adapters/enumerate.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
use crate::iter::adapters::{zip::try_get_unchecked, SourceIter, TrustedRandomAccess};
use crate::iter::adapters::{
zip::try_get_unchecked, SourceIter, TrustedRandomAccess, TrustedRandomAccessNoCoerce,
};
use crate::iter::{FusedIterator, InPlaceIterable, TrustedLen};
use crate::ops::Try;

@@ -114,7 +116,7 @@ where
#[doc(hidden)]
unsafe fn __iterator_get_unchecked(&mut self, idx: usize) -> <Self as Iterator>::Item
where
Self: TrustedRandomAccess,
Self: TrustedRandomAccessNoCoerce,
{
// SAFETY: the caller must uphold the contract for
// `Iterator::__iterator_get_unchecked`.
@@ -207,9 +209,13 @@ where

#[doc(hidden)]
#[unstable(feature = "trusted_random_access", issue = "none")]
unsafe impl<I> TrustedRandomAccess for Enumerate<I>
unsafe impl<I> TrustedRandomAccess for Enumerate<I> where I: TrustedRandomAccess {}

#[doc(hidden)]
#[unstable(feature = "trusted_random_access", issue = "none")]
unsafe impl<I> TrustedRandomAccessNoCoerce for Enumerate<I>
where
I: TrustedRandomAccess,
I: TrustedRandomAccessNoCoerce,
{
const MAY_HAVE_SIDE_EFFECT: bool = I::MAY_HAVE_SIDE_EFFECT;
}
11 changes: 8 additions & 3 deletions library/core/src/iter/adapters/fuse.rs
Original file line number Diff line number Diff line change
@@ -2,6 +2,7 @@ use crate::intrinsics;
use crate::iter::adapters::zip::try_get_unchecked;
use crate::iter::{
DoubleEndedIterator, ExactSizeIterator, FusedIterator, TrustedLen, TrustedRandomAccess,
TrustedRandomAccessNoCoerce,
};
use crate::ops::Try;

@@ -131,7 +132,7 @@ where
#[doc(hidden)]
unsafe fn __iterator_get_unchecked(&mut self, idx: usize) -> Self::Item
where
Self: TrustedRandomAccess,
Self: TrustedRandomAccessNoCoerce,
{
match self.iter {
// SAFETY: the caller must uphold the contract for
@@ -221,9 +222,13 @@ unsafe impl<I> TrustedLen for Fuse<I> 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<I> TrustedRandomAccess for Fuse<I>
unsafe impl<I> TrustedRandomAccess for Fuse<I> where I: TrustedRandomAccess {}

#[doc(hidden)]
#[unstable(feature = "trusted_random_access", issue = "none")]
unsafe impl<I> TrustedRandomAccessNoCoerce for Fuse<I>
where
I: TrustedRandomAccess,
I: TrustedRandomAccessNoCoerce,
{
const MAY_HAVE_SIDE_EFFECT: bool = I::MAY_HAVE_SIDE_EFFECT;
}
14 changes: 10 additions & 4 deletions library/core/src/iter/adapters/map.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use crate::fmt;
use crate::iter::adapters::{zip::try_get_unchecked, SourceIter, TrustedRandomAccess};
use crate::iter::adapters::{
zip::try_get_unchecked, SourceIter, TrustedRandomAccess, TrustedRandomAccessNoCoerce,
};
use crate::iter::{FusedIterator, InPlaceIterable, TrustedLen};
use crate::ops::Try;

@@ -125,7 +127,7 @@ where
#[doc(hidden)]
unsafe fn __iterator_get_unchecked(&mut self, idx: usize) -> B
where
Self: TrustedRandomAccess,
Self: TrustedRandomAccessNoCoerce,
{
// SAFETY: the caller must uphold the contract for
// `Iterator::__iterator_get_unchecked`.
@@ -187,9 +189,13 @@ where

#[doc(hidden)]
#[unstable(feature = "trusted_random_access", issue = "none")]
unsafe impl<I, F> TrustedRandomAccess for Map<I, F>
unsafe impl<I, F> TrustedRandomAccess for Map<I, F> where I: TrustedRandomAccess {}

#[doc(hidden)]
#[unstable(feature = "trusted_random_access", issue = "none")]
unsafe impl<I, F> TrustedRandomAccessNoCoerce for Map<I, F>
where
I: TrustedRandomAccess,
I: TrustedRandomAccessNoCoerce,
{
const MAY_HAVE_SIDE_EFFECT: bool = true;
}
3 changes: 3 additions & 0 deletions library/core/src/iter/adapters/mod.rs
Original file line number Diff line number Diff line change
@@ -51,6 +51,9 @@ 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;

#[unstable(feature = "iter_zip", issue = "83574")]
pub use self::zip::zip;

227 changes: 151 additions & 76 deletions library/core/src/iter/adapters/zip.rs
Original file line number Diff line number Diff line change
@@ -91,7 +91,7 @@ where
#[doc(hidden)]
unsafe fn __iterator_get_unchecked(&mut self, idx: usize) -> Self::Item
where
Self: TrustedRandomAccess,
Self: TrustedRandomAccessNoCoerce,
{
// SAFETY: `ZipImpl::__iterator_get_unchecked` has same safety
// requirements as `Iterator::__iterator_get_unchecked`.
@@ -126,7 +126,66 @@ trait ZipImpl<A, B> {
// This has the same safety requirements as `Iterator::__iterator_get_unchecked`
unsafe fn get_unchecked(&mut self, idx: usize) -> <Self as Iterator>::Item
where
Self: Iterator + TrustedRandomAccess;
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))
}

#[inline]
default fn nth(&mut self, n: usize) -> Option<Self::Item> {
self.super_nth(n)
}

#[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!(),
}
}
};
}

// General Zip impl
@@ -137,54 +196,8 @@ where
B: Iterator,
{
type Item = (A::Item, B::Item);
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))
}

#[inline]
default fn nth(&mut self, n: usize) -> Option<Self::Item> {
self.super_nth(n)
}

#[inline]
default fn next_back(&mut self) -> Option<(A::Item, B::Item)>
where
A: DoubleEndedIterator + ExactSizeIterator,
B: DoubleEndedIterator + ExactSizeIterator,
{
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_impl_general_defaults! {}

#[inline]
default fn size_hint(&self) -> (usize, Option<usize>) {
@@ -205,12 +218,35 @@ where

default unsafe fn get_unchecked(&mut self, _idx: usize) -> <Self as Iterator>::Item
where
Self: TrustedRandomAccess,
Self: TrustedRandomAccessNoCoerce,
{
unreachable!("Always specialized");
}
}

#[doc(hidden)]
impl<A, B> ZipImpl<A, B> for Zip<A, B>
where
A: TrustedRandomAccessNoCoerce + Iterator,
B: TrustedRandomAccessNoCoerce + Iterator,
{
zip_impl_general_defaults! {}

#[inline]
default fn size_hint(&self) -> (usize, Option<usize>) {
let size = cmp::min(self.a.size(), self.b.size());
(size, Some(size))
}

#[inline]
unsafe fn get_unchecked(&mut self, idx: usize) -> <Self as Iterator>::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<A, B> ZipImpl<A, B> for Zip<A, B>
where
@@ -330,14 +366,6 @@ where
None
}
}

#[inline]
unsafe fn get_unchecked(&mut self, idx: usize) -> <Self as Iterator>::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)) }
}
}

#[stable(feature = "rust1", since = "1.0.0")]
@@ -354,6 +382,15 @@ unsafe impl<A, B> TrustedRandomAccess for Zip<A, B>
where
A: TrustedRandomAccess,
B: TrustedRandomAccess,
{
}

#[doc(hidden)]
#[unstable(feature = "trusted_random_access", issue = "none")]
unsafe impl<A, B> TrustedRandomAccessNoCoerce for Zip<A, B>
where
A: TrustedRandomAccessNoCoerce,
B: TrustedRandomAccessNoCoerce,
{
const MAY_HAVE_SIDE_EFFECT: bool = A::MAY_HAVE_SIDE_EFFECT || B::MAY_HAVE_SIDE_EFFECT;
}
@@ -417,7 +454,9 @@ impl<A: Debug, B: Debug> ZipFmt<A, B> for Zip<A, B> {
}
}

impl<A: Debug + TrustedRandomAccess, B: Debug + TrustedRandomAccess> ZipFmt<A, B> for Zip<A, B> {
impl<A: Debug + TrustedRandomAccessNoCoerce, B: Debug + TrustedRandomAccessNoCoerce> ZipFmt<A, B>
for Zip<A, B>
{
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.
@@ -431,34 +470,70 @@ impl<A: Debug + TrustedRandomAccess, B: Debug + TrustedRandomAccess> ZipFmt<A, B
///
/// The iterator's `size_hint` must be exact and cheap to call.
///
/// `size` may not be overridden.
/// `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()`.
///
/// `<Self as Iterator>::__iterator_get_unchecked` must be safe to call
/// provided the following conditions are met.
/// If `Self: Iterator`, then `<Self as Iterator>::__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 `get_unchecked` is never called with the same
/// 2. If `Self: !Clone`, then `self.__iterator_get_unchecked(idx)` is never called with the same
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have never quite understood the way this is phrased. I mean of course an index will be accessed multiple times if an iterator gets cloned, but that's happens across the different clones.
What would be the reason of having an index accessed multiple times without actually cloning an iterator?

/// 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::DoubleEndedIterator::next_back()`
/// * `std::iter::Iterator::__iterator_get_unchecked()`
/// * `std::iter::TrustedRandomAccess::size()`
/// 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,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't this assume that idx is incremented in forwards direction? I.e. next_back may not do the right thing if you're calling __iterator_get_unchecked(self.size() - 1)

Note that the trait is called TrusedRandomAccess, so theoretically some future new iterator method could take some shortcut by directly indexing into an iterator in a non-linear fashion.

Copy link
Member Author

@steffahn steffahn Jun 3, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, first of all note that I didn’t write the original text, I just clarified it w.r.t. Clone, but second: if __iterator_get_unchecked(self.size() - 1), then the requirement will be that

self.next_back() will only be called at most self.size() - idx - 1 times

so…

idx == size-1

size - idx - 1 == size - (size - 1) - 1 == 0

There you go, now you’re officially no longer allowed to call next_back at all.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess for all of this to make just a little bit more sense, there should also be the requirement (currently undocumented) that calling next_back decreases the size of the iterator exactly by 1.

/// 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
/// `get_unchecked`, assuming that the required traits are implemented.
/// * It must also be safe to drop `self` after calling `get_unchecked`.
/// `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 `SouceIter::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 TrustedRandomAccess: Sized {
pub unsafe trait TrustedRandomAccessNoCoerce: Sized {
// Convenience method.
fn size(&self) -> usize
where
@@ -499,7 +574,7 @@ unsafe impl<I: Iterator> SpecTrustedRandomAccess for I {
}
}

unsafe impl<I: Iterator + TrustedRandomAccess> SpecTrustedRandomAccess for I {
unsafe impl<I: Iterator + TrustedRandomAccessNoCoerce> 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`.
2 changes: 2 additions & 0 deletions library/core/src/iter/mod.rs
Original file line number Diff line number Diff line change
@@ -407,6 +407,8 @@ pub use self::adapters::SourceIter;
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,
12 changes: 9 additions & 3 deletions library/core/src/iter/range.rs
Original file line number Diff line number Diff line change
@@ -3,7 +3,9 @@ use crate::convert::TryFrom;
use crate::mem;
use crate::ops::{self, Try};

use super::{FusedIterator, TrustedLen, TrustedRandomAccess, TrustedStep};
use super::{
FusedIterator, TrustedLen, TrustedRandomAccess, TrustedRandomAccessNoCoerce, TrustedStep,
};

// Safety: All invariants are upheld.
macro_rules! unsafe_impl_trusted_step {
@@ -495,7 +497,11 @@ 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> {}

#[doc(hidden)]
#[unstable(feature = "trusted_random_access", issue = "none")]
unsafe impl TrustedRandomAccessNoCoerce for ops::Range<$t> {
const MAY_HAVE_SIDE_EFFECT: bool = false;
}
)*)
@@ -670,7 +676,7 @@ impl<A: Step> Iterator for ops::Range<A> {
#[doc(hidden)]
unsafe fn __iterator_get_unchecked(&mut self, idx: usize) -> Self::Item
where
Self: TrustedRandomAccess,
Self: TrustedRandomAccessNoCoerce,
{
// SAFETY: The TrustedRandomAccess contract requires that callers only pass an index
// that is in bounds.
4 changes: 2 additions & 2 deletions library/core/src/iter/traits/iterator.rs
Original file line number Diff line number Diff line change
@@ -5,7 +5,7 @@
use crate::cmp::{self, Ordering};
use crate::ops::{ControlFlow, Try};

use super::super::TrustedRandomAccess;
use super::super::TrustedRandomAccessNoCoerce;
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};
@@ -3464,7 +3464,7 @@ pub trait Iterator {
#[unstable(feature = "trusted_random_access", issue = "none")]
unsafe fn __iterator_get_unchecked(&mut self, _idx: usize) -> Self::Item
where
Self: TrustedRandomAccess,
Self: TrustedRandomAccessNoCoerce,
{
unreachable!("Always specialized");
}
80 changes: 66 additions & 14 deletions library/core/src/slice/iter.rs
Original file line number Diff line number Diff line change
@@ -8,7 +8,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};
use crate::iter::{FusedIterator, TrustedLen, TrustedRandomAccess, TrustedRandomAccessNoCoerce};
use crate::marker::{PhantomData, Send, Sized, Sync};
use crate::mem;
use crate::num::NonZeroUsize;
@@ -1312,7 +1312,11 @@ impl<T> 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> {}

#[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;
}

@@ -1477,7 +1481,11 @@ impl<T> 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> {}

#[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;
}

@@ -1639,7 +1647,11 @@ impl<T> 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> {}

#[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;
}

@@ -1793,7 +1805,11 @@ impl<T> 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> {}

#[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;
}

@@ -1944,7 +1960,11 @@ impl<T> 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> {}

#[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;
}

@@ -2182,7 +2202,11 @@ impl<T, const N: usize> 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> {}

#[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;
}

@@ -2295,7 +2319,11 @@ impl<T, const N: usize> 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> {}

#[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;
}

@@ -2457,7 +2485,11 @@ impl<T> 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> {}

#[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;
}

@@ -2618,7 +2650,11 @@ impl<T> 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> {}

#[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;
}

@@ -2776,7 +2812,11 @@ impl<T> 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> {}

#[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;
}

@@ -2931,19 +2971,31 @@ impl<T> 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> {}

#[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;
}

#[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> {}

#[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;
}

#[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> {}

#[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;
}

8 changes: 6 additions & 2 deletions library/core/src/str/iter.rs
Original file line number Diff line number Diff line change
@@ -2,9 +2,9 @@
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};
use crate::iter::{TrustedRandomAccess, TrustedRandomAccessNoCoerce};
use crate::ops::Try;
use crate::option;
use crate::slice::{self, Split as SliceSplit};
@@ -345,7 +345,11 @@ unsafe impl TrustedLen for Bytes<'_> {}

#[doc(hidden)]
#[unstable(feature = "trusted_random_access", issue = "none")]
unsafe impl TrustedRandomAccess for Bytes<'_> {
unsafe impl TrustedRandomAccess for Bytes<'_> {}

#[doc(hidden)]
#[unstable(feature = "trusted_random_access", issue = "none")]
unsafe impl TrustedRandomAccessNoCoerce for Bytes<'_> {
const MAY_HAVE_SIDE_EFFECT: bool = false;
}