diff --git a/library/alloc/src/collections/vec_deque/iter.rs b/library/alloc/src/collections/vec_deque/iter.rs index ae1b03c9a4d22..edadd666edce6 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}; +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 // that is in bounds. unsafe { let idx = wrap_index(self.tail.wrapping_add(idx), self.ring.len()); @@ -177,6 +174,10 @@ 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> {} + +#[doc(hidden)] +#[unstable(feature = "trusted_random_access", issue = "none")] +unsafe impl TrustedRandomAccessNoCoerce for Iter<'_, T> { const MAY_HAVE_SIDE_EFFECT: bool = false; } diff --git a/library/alloc/src/collections/vec_deque/iter_mut.rs b/library/alloc/src/collections/vec_deque/iter_mut.rs index df30c38652f72..7700b31cf5b46 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}; +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 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> {} + +#[doc(hidden)] +#[unstable(feature = "trusted_random_access", issue = "none")] +unsafe impl TrustedRandomAccessNoCoerce for IterMut<'_, T> { const MAY_HAVE_SIDE_EFFECT: bool = false; } diff --git a/library/alloc/src/vec/into_iter.rs b/library/alloc/src/vec/into_iter.rs index 7a08f4c6cbaac..0bd152f17a670 100644 --- a/library/alloc/src/vec/into_iter.rs +++ b/library/alloc/src/vec/into_iter.rs @@ -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 Iterator for IntoIter { #[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`, so `i` cannot overflow an `isize`, and the `self.ptr.add(i)` @@ -219,7 +221,10 @@ unsafe impl TrustedLen for IntoIter {} #[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 TrustedRandomAccess for IntoIter +// +// TrustedRandomAccess (without NoCoerce) must not be implemented because +// subtypes/supertypes of `T` might not be `Copy` +unsafe impl TrustedRandomAccessNoCoerce for IntoIter where T: Copy, { diff --git a/library/alloc/src/vec/source_iter_marker.rs b/library/alloc/src/vec/source_iter_marker.rs index d814d4ae355b0..23a2e313c0189 100644 --- a/library/alloc/src/vec/source_iter_marker.rs +++ b/library/alloc/src/vec/source_iter_marker.rs @@ -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( trait SpecInPlaceCollect: Iterator { /// 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 SpecInPlaceCollect for I where - I: Iterator + TrustedRandomAccess, + I: Iterator + TrustedRandomAccessNoCoerce, { #[inline] fn collect_in_place(&mut self, dst_buf: *mut T, end: *const T) -> usize { diff --git a/library/core/src/iter/adapters/cloned.rs b/library/core/src/iter/adapters/cloned.rs index 5cd65a9415fd7..71a5a4ea831ff 100644 --- a/library/core/src/iter/adapters/cloned.rs +++ b/library/core/src/iter/adapters/cloned.rs @@ -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 TrustedRandomAccess for Cloned +unsafe impl TrustedRandomAccess for Cloned where I: TrustedRandomAccess {} + +#[doc(hidden)] +#[unstable(feature = "trusted_random_access", issue = "none")] +unsafe impl TrustedRandomAccessNoCoerce for Cloned where - I: TrustedRandomAccess, + I: TrustedRandomAccessNoCoerce, { const MAY_HAVE_SIDE_EFFECT: bool = true; } diff --git a/library/core/src/iter/adapters/copied.rs b/library/core/src/iter/adapters/copied.rs index 07a3b5d245659..3d3c8da678b8b 100644 --- a/library/core/src/iter/adapters/copied.rs +++ b/library/core/src/iter/adapters/copied.rs @@ -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 TrustedRandomAccess for Copied +unsafe impl TrustedRandomAccess for Copied where I: TrustedRandomAccess {} + +#[doc(hidden)] +#[unstable(feature = "trusted_random_access", issue = "none")] +unsafe impl TrustedRandomAccessNoCoerce for Copied where - I: TrustedRandomAccess, + I: TrustedRandomAccessNoCoerce, { const MAY_HAVE_SIDE_EFFECT: bool = I::MAY_HAVE_SIDE_EFFECT; } diff --git a/library/core/src/iter/adapters/enumerate.rs b/library/core/src/iter/adapters/enumerate.rs index 8b27bdc60a705..3478a0cd40832 100644 --- a/library/core/src/iter/adapters/enumerate.rs +++ b/library/core/src/iter/adapters/enumerate.rs @@ -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) -> ::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 TrustedRandomAccess for Enumerate +unsafe impl TrustedRandomAccess for Enumerate where I: TrustedRandomAccess {} + +#[doc(hidden)] +#[unstable(feature = "trusted_random_access", issue = "none")] +unsafe impl TrustedRandomAccessNoCoerce for Enumerate where - I: TrustedRandomAccess, + I: TrustedRandomAccessNoCoerce, { const MAY_HAVE_SIDE_EFFECT: bool = I::MAY_HAVE_SIDE_EFFECT; } diff --git a/library/core/src/iter/adapters/fuse.rs b/library/core/src/iter/adapters/fuse.rs index 408328adeecf4..fbf752c6f2024 100644 --- a/library/core/src/iter/adapters/fuse.rs +++ b/library/core/src/iter/adapters/fuse.rs @@ -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 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 +unsafe impl TrustedRandomAccess for Fuse where I: TrustedRandomAccess {} + +#[doc(hidden)] +#[unstable(feature = "trusted_random_access", issue = "none")] +unsafe impl TrustedRandomAccessNoCoerce for Fuse where - I: TrustedRandomAccess, + I: TrustedRandomAccessNoCoerce, { const MAY_HAVE_SIDE_EFFECT: bool = I::MAY_HAVE_SIDE_EFFECT; } diff --git a/library/core/src/iter/adapters/map.rs b/library/core/src/iter/adapters/map.rs index dc86eccfcb82f..763e253e75a51 100644 --- a/library/core/src/iter/adapters/map.rs +++ b/library/core/src/iter/adapters/map.rs @@ -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 TrustedRandomAccess for Map +unsafe impl TrustedRandomAccess for Map where I: TrustedRandomAccess {} + +#[doc(hidden)] +#[unstable(feature = "trusted_random_access", issue = "none")] +unsafe impl TrustedRandomAccessNoCoerce for Map where - I: TrustedRandomAccess, + I: TrustedRandomAccessNoCoerce, { const MAY_HAVE_SIDE_EFFECT: bool = true; } diff --git a/library/core/src/iter/adapters/mod.rs b/library/core/src/iter/adapters/mod.rs index a3fbf4d9c38d8..056ccca1d01c9 100644 --- a/library/core/src/iter/adapters/mod.rs +++ b/library/core/src/iter/adapters/mod.rs @@ -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; diff --git a/library/core/src/iter/adapters/zip.rs b/library/core/src/iter/adapters/zip.rs index 3d0401cbebcf7..c7e69e922c137 100644 --- a/library/core/src/iter/adapters/zip.rs +++ b/library/core/src/iter/adapters/zip.rs @@ -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 { // This has the same safety requirements as `Iterator::__iterator_get_unchecked` unsafe fn get_unchecked(&mut self, idx: usize) -> ::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.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.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) { @@ -205,12 +218,35 @@ where default unsafe fn get_unchecked(&mut self, _idx: usize) -> ::Item where - Self: TrustedRandomAccess, + 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 where @@ -330,14 +366,6 @@ where None } } - - #[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)) } - } } #[stable(feature = "rust1", since = "1.0.0")] @@ -354,6 +382,15 @@ unsafe impl TrustedRandomAccess for Zip where A: TrustedRandomAccess, B: TrustedRandomAccess, +{ +} + +#[doc(hidden)] +#[unstable(feature = "trusted_random_access", issue = "none")] +unsafe impl TrustedRandomAccessNoCoerce for Zip +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 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. @@ -431,34 +470,70 @@ impl ZipFmt::__iterator_get_unchecked` must be safe to call -/// provided the following conditions are met. +/// 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 `get_unchecked` is never called with the same +/// 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.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, +/// 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 SpecTrustedRandomAccess for I { } } -unsafe impl SpecTrustedRandomAccess for I { +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`. diff --git a/library/core/src/iter/mod.rs b/library/core/src/iter/mod.rs index bfb27da505eaf..5f664b5f4a8cd 100644 --- a/library/core/src/iter/mod.rs +++ b/library/core/src/iter/mod.rs @@ -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, diff --git a/library/core/src/iter/range.rs b/library/core/src/iter/range.rs index b9387ef49e5fb..22782a8137811 100644 --- a/library/core/src/iter/range.rs +++ b/library/core/src/iter/range.rs @@ -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 Iterator for ops::Range { #[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. diff --git a/library/core/src/iter/traits/iterator.rs b/library/core/src/iter/traits/iterator.rs index 16efd2f0eaff9..a1a336a05740a 100644 --- a/library/core/src/iter/traits/iterator.rs +++ b/library/core/src/iter/traits/iterator.rs @@ -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"); } diff --git a/library/core/src/slice/iter.rs b/library/core/src/slice/iter.rs index 5cbc6343e3a77..419bf0e292ae1 100644 --- a/library/core/src/slice/iter.rs +++ b/library/core/src/slice/iter.rs @@ -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 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 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 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 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 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 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 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 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 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 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 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; } diff --git a/library/core/src/str/iter.rs b/library/core/src/str/iter.rs index a5774764573be..8db9edc61472f 100644 --- a/library/core/src/str/iter.rs +++ b/library/core/src/str/iter.rs @@ -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; }