diff --git a/library/core/src/iter/range.rs b/library/core/src/iter/range.rs index 06733a1b50b91..9f1c584f5c6df 100644 --- a/library/core/src/iter/range.rs +++ b/library/core/src/iter/range.rs @@ -1,3 +1,5 @@ +mod ptr; + use crate::char; use crate::convert::TryFrom; use crate::mem; diff --git a/library/core/src/iter/range/ptr.rs b/library/core/src/iter/range/ptr.rs new file mode 100644 index 0000000000000..e3027ff604f3d --- /dev/null +++ b/library/core/src/iter/range/ptr.rs @@ -0,0 +1,295 @@ +use crate::cmp::Ordering; +use crate::iter::{FusedIterator, TrustedLen, TrustedRandomAccess, TrustedRandomAccessNoCoerce}; +use crate::mem; +use crate::ops::Range; + +macro_rules! impl_iterator_for_ptr_range { + ($mutability:ident /* const or mut */) => { + /// Iteration of a pointer range, as is common in code that interfaces + /// with C++ iterators. + /// + /// # Safety + /// + /// Traversing a pointer range is always safe, but **using the resulting + /// pointers** is not! + /// + /// The pointers between the start and end of a range "remember" the + /// [allocated object] that they refer into. Pointers resulting from + /// pointer arithmetic must not be used to read or write to any other + /// allocated object. + /// + /// As a consequence, pointers from a range traversal are only + /// dereferenceable if start and end of the original range both point + /// into the same allocated object. Dereferencing a pointer obtained via + /// iteration when this is not the case is Undefined Behavior. + /// + /// [allocated object]: crate::ptr#allocated-object + /// + /// # Alignment + /// + /// All the pointers in the range are at offsets of multiples of + /// `size_of::()` bytes from the range's start, so if the range's + /// start is misaligned, then the pointers in the range are misaligned + /// as well. The alignedness of the range's end is not relevant. + /// + /// # Optimizability + /// + /// Iteration being a safe operation imposes some restrictions on the + /// kinds of pointer arithmetic operations available to this + /// implementation. In code that meets the unsafe requirements of + /// [\<\*const T\>::add][add] or + /// [\<\*const \[T\]\>::get_unchecked][get_unchecked], it is possible + /// that performing the iteration unsafely in terms of those functions + /// could yield more optimized code than safely iterating a pointer + /// range. + /// + /// [add]: pointer::add + /// [get_unchecked]: pointer::get_unchecked + /// + /// # Example + /// + #[doc = example!($mutability)] + #[stable(feature = "iterate_ptr_range", since = "1.58.0")] + impl Iterator for Range<*$mutability T> { + type Item = *$mutability T; + + fn next(&mut self) -> Option { + if self.is_empty() { + None + } else { + let curr = self.start; + let byte_offset = self.end as usize - curr as usize; + self.start = if byte_offset >= mem::size_of::() { + curr.wrapping_add(1) + } else { + // Saturate to self.end if the wrapping_add would wrap + // or land beyond end. + self.end + }; + Some(curr) + } + } + + fn size_hint(&self) -> (usize, Option) { + if self.is_empty() { + (0, Some(0)) + } else if mem::size_of::() == 0 { + // T is zero sized so there are infinity of them in the + // nonempty range. + (usize::MAX, None) + } else { + // In between self.start and self.end there are some number + // of whole elements of type T, followed by possibly a + // remainder element if T's size doesn't evenly divide the + // byte distance between the endpoints. The remainder + // element still counts as being part of this range, since + // the pointer to it does lie between self.start and + // self.end. + let byte_offset = self.end as usize - self.start as usize; + let number_of_whole_t = byte_offset / mem::size_of::(); + let remainder_bytes = byte_offset % mem::size_of::(); + let maybe_remainder_t = (remainder_bytes > 0) as usize; + let hint = number_of_whole_t + maybe_remainder_t; + (hint, Some(hint)) + } + } + + fn nth(&mut self, n: usize) -> Option { + let _ = self.advance_by(n); + self.next() + } + + fn last(mut self) -> Option { + self.next_back() + } + + fn min(mut self) -> Option { + self.next() + } + + fn max(mut self) -> Option { + self.next_back() + } + + fn is_sorted(self) -> bool { + true + } + + fn advance_by(&mut self, n: usize) -> Result<(), usize> { + match self.size_hint().1 { + None => { + // T is zero sized. Advancing does nothing. + Ok(()) + } + Some(len) => match n.cmp(&len) { + Ordering::Less => { + // Advance past n number of whole elements. + self.start = self.start.wrapping_add(n); + Ok(()) + } + Ordering::Equal => { + // Advance past every single element in the + // iterator, including perhaps the remainder + // element, leaving an empty iterator. + self.start = self.end; + Ok(()) + } + Ordering::Greater => { + // Advance too far. + self.start = self.end; + Err(len) + } + } + } + } + + #[doc(hidden)] + unsafe fn __iterator_get_unchecked(&mut self, idx: usize) -> Self::Item { + self.start.wrapping_add(idx) + } + } + + #[stable(feature = "iterate_ptr_range", since = "1.58.0")] + impl DoubleEndedIterator for Range<*$mutability T> { + fn next_back(&mut self) -> Option { + match self.size_hint().1 { + None => { + // T is zero sized so the iterator never progresses past + // start, even if going backwards. + Some(self.start) + } + Some(0) => { + None + } + Some(len) => { + self.end = self.start.wrapping_add(len - 1); + Some(self.end) + } + } + } + + fn nth_back(&mut self, n: usize) -> Option { + match self.size_hint().1 { + None => { + // T is zero sized. + Some(self.start) + } + Some(len) => { + if n < len { + self.end = self.start.wrapping_add(len - n - 1); + Some(self.end) + } else { + self.end = self.start; + None + } + } + } + } + + fn advance_back_by(&mut self, n: usize) -> Result<(), usize> { + match self.size_hint().1 { + None => { + // T is zero sized. Advancing does nothing. + Ok(()) + } + Some(len) => match n.cmp(&len) { + Ordering::Less => { + // Advance leaving `len - n` elements in the + // iterator. Careful to preserve the remainder + // element if told to advance by 0. + if n > 0 { + self.end = self.start.wrapping_add(len - n); + } + Ok(()) + } + Ordering::Equal => { + // Advance past every single element in the + // iterator, leaving an empty iterator. + self.end = self.start; + Ok(()) + } + Ordering::Greater => { + // Advance too far. + self.end = self.start; + Err(len) + } + } + } + } + } + + #[stable(feature = "iterate_ptr_range", since = "1.58.0")] + impl FusedIterator for Range<*$mutability T> {} + + #[unstable(feature = "trusted_len", issue = "37572")] + unsafe impl TrustedLen for Range<*$mutability T> {} + + #[doc(hidden)] + #[unstable(feature = "trusted_random_access", issue = "none")] + unsafe impl TrustedRandomAccess for Range<*$mutability T> {} + + #[doc(hidden)] + #[unstable(feature = "trusted_random_access", issue = "none")] + unsafe impl TrustedRandomAccessNoCoerce for Range<*$mutability T> { + const MAY_HAVE_SIDE_EFFECT: bool = false; + } + }; +} + +macro_rules! example { + (const) => { + doc_comment_to_literal! { + /// ``` + /// // Designed to be called from C++ or C. + /// #[no_mangle] + /// unsafe extern "C" fn demo(start: *const u16, end: *const u16) { + /// for ptr in start..end { + /// println!("{}", *ptr); + /// } + /// } + /// + /// fn main() { + /// let slice = &[1u16, 2, 3]; + /// let range = slice.as_ptr_range(); + /// unsafe { demo(range.start, range.end); } + /// } + /// ``` + } + }; + + (mut) => { + doc_comment_to_literal! { + /// ``` + /// #![feature(vec_spare_capacity)] + /// + /// use core::ptr; + /// + /// // Designed to be called from C++ or C. + /// #[no_mangle] + /// unsafe extern "C" fn demo(start: *mut u16, end: *mut u16) { + /// for (i, ptr) in (start..end).enumerate() { + /// ptr::write(ptr, i as u16); + /// } + /// } + /// + /// fn main() { + /// let mut vec: Vec = Vec::with_capacity(100); + /// let range = vec.spare_capacity_mut().as_mut_ptr_range(); + /// unsafe { + /// demo(range.start.cast::(), range.end.cast::()); + /// vec.set_len(100); + /// } + /// } + /// ``` + } + }; +} + +macro_rules! doc_comment_to_literal { + ($(#[doc = $example:literal])*) => { + concat!($($example, '\n'),*) + }; +} + +impl_iterator_for_ptr_range!(const); +impl_iterator_for_ptr_range!(mut); diff --git a/library/core/tests/iter/range.rs b/library/core/tests/iter/range.rs index 6b4cf33efe1ff..9ff3d3b23614d 100644 --- a/library/core/tests/iter/range.rs +++ b/library/core/tests/iter/range.rs @@ -467,3 +467,255 @@ fn test_double_ended_range() { panic!("unreachable"); } } + +#[test] +fn test_ptr_range_empty() { + let start = 4 as *const [u8; 100]; + let mut range = start..start; + assert_eq!(range.size_hint(), (0, Some(0))); + assert_eq!(range.next(), None); + assert_eq!(range.nth(0), None); + assert_eq!(range.nth(1), None); + assert_eq!(range.advance_by(0), Ok(())); + assert_eq!(range.advance_by(1), Err(0)); + assert_eq!(range.next_back(), None); + assert_eq!(range.nth_back(0), None); + assert_eq!(range.nth_back(1), None); + assert_eq!(range.advance_back_by(0), Ok(())); + assert_eq!(range.advance_back_by(1), Err(0)); + assert_eq!(range, start..start); +} + +#[test] +fn test_ptr_range() { + let start = 0_04 as *const [u8; 100]; + let end = 12_04 as *const [u8; 100]; + let mut range = start..end; + assert_eq!(range.size_hint(), (12, Some(12))); + assert_eq!(range.next(), Some(0_04 as _)); + assert_eq!(range, 1_04 as _..12_04 as _); + assert_eq!(range.nth(0), Some(1_04 as _)); + assert_eq!(range, 2_04 as _..12_04 as _); + assert_eq!(range.nth(1), Some(3_04 as _)); + assert_eq!(range, 4_04 as _..12_04 as _); + assert_eq!(range.advance_by(0), Ok(())); + assert_eq!(range, 4_04 as _..12_04 as _); + assert_eq!(range.advance_by(1), Ok(())); + assert_eq!(range, 5_04 as _..12_04 as _); + assert_eq!(range.next_back(), Some(11_04 as _)); + assert_eq!(range, 5_04 as _..11_04 as _); + assert_eq!(range.nth_back(0), Some(10_04 as _)); + assert_eq!(range, 5_04 as _..10_04 as _); + assert_eq!(range.nth_back(1), Some(8_04 as _)); + assert_eq!(range, 5_04 as _..8_04 as _); + assert_eq!(range.advance_back_by(0), Ok(())); + assert_eq!(range, 5_04 as _..8_04 as _); + assert_eq!(range.advance_back_by(1), Ok(())); + assert_eq!(range, 5_04 as _..7_04 as _); + + let mut range = start..end; + assert_eq!(range.nth(20), None); + assert_eq!(range, end..end); + + let mut range = start..end; + assert_eq!(range.advance_by(20), Err(12)); + assert_eq!(range, end..end); + + let mut range = start..end; + assert_eq!(range.nth_back(20), None); + assert_eq!(range, start..start); + + let mut range = start..end; + assert_eq!(range.advance_back_by(20), Err(12)); + assert_eq!(range, start..start); +} + +#[test] +fn test_ptr_range_with_remainder() { + let start = 0_04 as *const [u8; 100]; + let end = 12_09 as *const [u8; 100]; + let mut range = start..end; + assert_eq!(range.size_hint(), (13, Some(13))); + assert_eq!(range.next(), Some(4 as _)); + assert_eq!(range, 1_04 as _..12_09 as _); + assert_eq!(range.nth(0), Some(1_04 as _)); + assert_eq!(range, 2_04 as _..12_09 as _); + assert_eq!(range.nth(1), Some(3_04 as _)); + assert_eq!(range, 4_04 as _..12_09 as _); + assert_eq!(range.advance_by(0), Ok(())); + assert_eq!(range, 4_04 as _..12_09 as _); + assert_eq!(range.advance_by(1), Ok(())); + assert_eq!(range, 5_04 as _..12_09 as _); + + let mut range = start..end; + assert_eq!(range.nth(12), Some(12_04 as _)); + assert_eq!(range, end..end); + + let mut range = start..end; + assert_eq!(range.advance_by(12), Ok(())); + assert_eq!(range, 12_04 as _..end); + + let mut range = start..end; + assert_eq!(range.advance_by(13), Ok(())); + assert_eq!(range, end..end); + + let mut range = start..end; + assert_eq!(range.advance_by(14), Err(13)); + assert_eq!(range, end..end); + + let mut range = start..end; + assert_eq!(range.next_back(), Some(12_04 as _)); + assert_eq!(range, start..12_04 as _); + + let mut range = start..end; + assert_eq!(range.nth_back(0), Some(12_04 as _)); + assert_eq!(range, start..12_04 as _); + + let mut range = start..end; + assert_eq!(range.nth_back(1), Some(11_04 as _)); + assert_eq!(range, start..11_04 as _); + + let mut range = start..end; + assert_eq!(range.nth_back(12), Some(start)); + assert_eq!(range, start..start); + + let mut range = start..end; + assert_eq!(range.advance_back_by(0), Ok(())); + assert_eq!(range, start..end); + + let mut range = start..end; + assert_eq!(range.advance_back_by(1), Ok(())); + assert_eq!(range, start..12_04 as _); + + let mut range = start..end; + assert_eq!(range.advance_back_by(12), Ok(())); + assert_eq!(range, start..1_04 as _); + + let mut range = start..end; + assert_eq!(range.advance_back_by(13), Ok(())); + assert_eq!(range, start..start); + + let mut range = start..end; + assert_eq!(range.advance_back_by(14), Err(13)); + assert_eq!(range, start..start); +} + +#[test] +fn test_ptr_range_zero_sized() { + let start = 8 as *const (); + let mut range = start..start; + assert_eq!(range.size_hint(), (0, Some(0))); + assert_eq!(range.next(), None); + assert_eq!(range.nth(0), None); + assert_eq!(range.nth(1), None); + assert_eq!(range.advance_by(0), Ok(())); + assert_eq!(range.advance_by(1), Err(0)); + assert_eq!(range.next_back(), None); + assert_eq!(range.nth_back(0), None); + assert_eq!(range.nth_back(1), None); + assert_eq!(range.advance_back_by(0), Ok(())); + assert_eq!(range.advance_back_by(1), Err(0)); + assert_eq!(range, start..start); + + let end = 11 as *const (); + let mut range = start..end; + assert_eq!(range.size_hint(), (usize::MAX, None)); + assert_eq!(range.next(), Some(start)); + assert_eq!(range.nth(0), Some(start)); + assert_eq!(range.nth(1), Some(start)); + assert_eq!(range.advance_by(0), Ok(())); + assert_eq!(range.advance_by(1), Ok(())); + assert_eq!(range.next_back(), Some(start)); + assert_eq!(range.nth_back(0), Some(start)); + assert_eq!(range.nth_back(1), Some(start)); + assert_eq!(range.advance_back_by(0), Ok(())); + assert_eq!(range.advance_back_by(1), Ok(())); + assert_eq!(range, start..end); +} + +#[test] +fn test_ptr_range_reversed() { + let start = 2222 as *const i32; + let end = 1111 as *const i32; + let range = start..end; + assert!(range.is_empty()); + assert_eq!(range.size_hint(), (0, Some(0))); + assert_eq!(range.clone().next(), None); + assert_eq!(range.clone().nth(0), None); + assert_eq!(range.clone().nth(1), None); + assert_eq!(range.clone().advance_by(0), Ok(())); + assert_eq!(range.clone().advance_by(1), Err(0)); + assert_eq!(range.clone().next_back(), None); + assert_eq!(range.clone().nth_back(0), None); + assert_eq!(range.clone().nth_back(1), None); + assert_eq!(range.clone().advance_back_by(0), Ok(())); + assert_eq!(range.clone().advance_back_by(1), Err(0)); + + let start = 2222 as *const (); + let end = 1111 as *const (); + let range = start..end; + assert!(range.is_empty()); + assert_eq!(range.size_hint(), (0, Some(0))); + assert_eq!(range.clone().next(), None); + assert_eq!(range.clone().nth(0), None); + assert_eq!(range.clone().nth(1), None); + assert_eq!(range.clone().advance_by(0), Ok(())); + assert_eq!(range.clone().advance_by(1), Err(0)); + assert_eq!(range.clone().next_back(), None); + assert_eq!(range.clone().nth_back(0), None); + assert_eq!(range.clone().nth_back(1), None); + assert_eq!(range.clone().advance_back_by(0), Ok(())); + assert_eq!(range.clone().advance_back_by(1), Err(0)); +} + +#[test] +fn test_ptr_range_underflow() { + let start = 1 as *const [u8; 100]; + let end = 2 as *const [u8; 100]; + + let mut range = start..end; + assert_eq!(range.next_back(), Some(start)); + assert_eq!(range, start..start); + + let mut range = start..end; + assert_eq!(range.nth_back(0), Some(start)); + assert_eq!(range, start..start); + + let mut range = start..end; + assert_eq!(range.nth_back(1), None); + assert_eq!(range, start..start); + + let mut range = start..end; + assert_eq!(range.advance_back_by(0), Ok(())); + assert_eq!(range, start..end); + + let mut range = start..end; + assert_eq!(range.advance_back_by(1), Ok(())); + assert_eq!(range, start..start); +} + +#[test] +fn test_ptr_range_overflow() { + let start = (usize::MAX - 2) as *const [u8; 100]; + let end = usize::MAX as *const [u8; 100]; + + let mut range = start..end; + assert_eq!(range.next(), Some(start)); + assert_eq!(range, end..end); + + let mut range = start..end; + assert_eq!(range.nth(0), Some(start)); + assert_eq!(range, end..end); + + let mut range = start..end; + assert_eq!(range.nth(1), None); + assert_eq!(range, end..end); + + let mut range = start..end; + assert_eq!(range.advance_by(0), Ok(())); + assert_eq!(range, start..end); + + let mut range = start..end; + assert_eq!(range.advance_by(1), Ok(())); + assert_eq!(range, end..end); +} diff --git a/src/test/ui/iterators/ranges.rs b/src/test/ui/iterators/ranges.rs index 925d2d61a1247..14163f894c5fe 100644 --- a/src/test/ui/iterators/ranges.rs +++ b/src/test/ui/iterators/ranges.rs @@ -7,3 +7,9 @@ fn main() { for _ in 0..=10 {} for _ in 0.. {} } + +fn references_do_not_coerce_to_ptr_range(start: &i32, end: &i32) { + // Better not turn into Range<*const i32>. + for _ in start..end {} + //~^ ERROR E0277 +} diff --git a/src/test/ui/iterators/ranges.stderr b/src/test/ui/iterators/ranges.stderr index fdc33862c0aba..2754cccb43e23 100644 --- a/src/test/ui/iterators/ranges.stderr +++ b/src/test/ui/iterators/ranges.stderr @@ -28,6 +28,22 @@ note: required by `into_iter` LL | fn into_iter(self) -> Self::IntoIter; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 2 previous errors +error[E0277]: the trait bound `&i32: Step` is not satisfied + --> $DIR/ranges.rs:13:14 + | +LL | for _ in start..end {} + | ^^^^^^^^^^ the trait `Step` is not implemented for `&i32` + | + = help: the following implementations were found: + + = note: required because of the requirements on the impl of `Iterator` for `std::ops::Range<&i32>` + = note: required because of the requirements on the impl of `IntoIterator` for `std::ops::Range<&i32>` +note: required by `into_iter` + --> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL + | +LL | fn into_iter(self) -> Self::IntoIter; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0277`.