From 88150bd68276b28b67e1db25e3261742ddedd6df Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Wed, 17 Nov 2021 16:13:24 -0800 Subject: [PATCH 1/7] Make ptr range iterable --- library/core/src/iter/range.rs | 2 + library/core/src/iter/range/ptr.rs | 274 +++++++++++++++++++++++++++++ library/core/tests/iter/range.rs | 165 +++++++++++++++++ 3 files changed, 441 insertions(+) create mode 100644 library/core/src/iter/range/ptr.rs 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..6ab35ba6bb051 --- /dev/null +++ b/library/core/src/iter/range/ptr.rs @@ -0,0 +1,274 @@ +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 + /// + /// # 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 next = curr.wrapping_add(1); + self.start = if (curr..self.end).contains(&next) { + next + } else { + // Saturate to self.end if the wrapping_add wrapped or + // landed 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..103b1b63a37b7 100644 --- a/library/core/tests/iter/range.rs +++ b/library/core/tests/iter/range.rs @@ -467,3 +467,168 @@ 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); +} From 7ac5ca3943b0aa1c1eea54431b11ad62c56438f2 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Thu, 18 Nov 2021 09:46:33 -0800 Subject: [PATCH 2/7] More ptr range test cases --- library/core/tests/iter/range.rs | 87 ++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) diff --git a/library/core/tests/iter/range.rs b/library/core/tests/iter/range.rs index 103b1b63a37b7..a3ebb104b87a9 100644 --- a/library/core/tests/iter/range.rs +++ b/library/core/tests/iter/range.rs @@ -632,3 +632,90 @@ fn test_ptr_range_zero_sized() { 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(())); // Err(0) would be equally fine + 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(())); // Err(0) would be equally fine + 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); +} From 1f15e7332c0a21c6b2beb7c3d9b7031e54d36d6d Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Thu, 18 Nov 2021 09:59:59 -0800 Subject: [PATCH 3/7] Document alignedness of ptr range elements --- library/core/src/iter/range/ptr.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/library/core/src/iter/range/ptr.rs b/library/core/src/iter/range/ptr.rs index 6ab35ba6bb051..638aeeaeb3bbc 100644 --- a/library/core/src/iter/range/ptr.rs +++ b/library/core/src/iter/range/ptr.rs @@ -25,6 +25,13 @@ macro_rules! impl_iterator_for_ptr_range { /// /// [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. + /// /// # Example /// #[doc = example!($mutability)] From f6db19044f53a2065dec622b259d54ce898e85c6 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Thu, 18 Nov 2021 10:01:57 -0800 Subject: [PATCH 4/7] Add ui test of ptr range coercion --- src/test/ui/iterators/ranges.rs | 6 ++++++ src/test/ui/iterators/ranges.stderr | 18 +++++++++++++++++- 2 files changed, 23 insertions(+), 1 deletion(-) 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`. From 47c86469fc2f8542a0a561e24285aaa562522bc3 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Thu, 18 Nov 2021 11:01:21 -0800 Subject: [PATCH 5/7] Document add/get_unchecked alternatives --- library/core/src/iter/range/ptr.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/library/core/src/iter/range/ptr.rs b/library/core/src/iter/range/ptr.rs index 638aeeaeb3bbc..7d654114d6a40 100644 --- a/library/core/src/iter/range/ptr.rs +++ b/library/core/src/iter/range/ptr.rs @@ -32,6 +32,20 @@ macro_rules! impl_iterator_for_ptr_range { /// 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)] From 4cb61510e1bdbf289e939eb3d75b2e876b133b4b Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Thu, 18 Nov 2021 11:02:14 -0800 Subject: [PATCH 6/7] Err(0) is no longer going to be okay following PR 89916 --- library/core/tests/iter/range.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/core/tests/iter/range.rs b/library/core/tests/iter/range.rs index a3ebb104b87a9..9ff3d3b23614d 100644 --- a/library/core/tests/iter/range.rs +++ b/library/core/tests/iter/range.rs @@ -643,12 +643,12 @@ fn test_ptr_range_reversed() { 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(())); // Err(0) would be equally fine + 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(())); // Err(0) would be equally fine + assert_eq!(range.clone().advance_back_by(0), Ok(())); assert_eq!(range.clone().advance_back_by(1), Err(0)); let start = 2222 as *const (); From 81d29d4643a13b1210e6067813a2313431109457 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Thu, 18 Nov 2021 11:53:07 -0800 Subject: [PATCH 7/7] Produce better machine code for a loop over ptr range Test code: extern "C" { fn foreign(ptr: *const i32); } pub fn forloop(start: *const i32, end: *const i32) { for ptr in start..end { unsafe { foreign(ptr) } } } Before: asm::forloop: push r15 push r14 push rbx cmp rdi, rsi jae .LBB0_3 mov r15, rsi mov r14, qword, ptr, [rip, +, foreign@GOTPCREL] .LBB0_2: lea rax, [rdi, +, 4] cmp rax, r15 mov rbx, r15 cmovb rbx, rax cmp rdi, rax cmova rbx, r15 call r14 mov rdi, rbx cmp rbx, r15 jb .LBB0_2 .LBB0_3: pop rbx pop r14 pop r15 ret After: asm::forloop: push r15 push r14 push rbx cmp rdi, rsi jae .LBB0_3 mov r15, rsi mov r14, qword, ptr, [rip, +, foreign@GOTPCREL] .LBB0_2: mov rax, r15 sub rax, rdi lea rbx, [rdi, +, 4] cmp rax, 4 cmovb rbx, r15 call r14 mov rdi, rbx cmp rbx, r15 jb .LBB0_2 .LBB0_3: pop rbx pop r14 pop r15 ret --- library/core/src/iter/range/ptr.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/library/core/src/iter/range/ptr.rs b/library/core/src/iter/range/ptr.rs index 7d654114d6a40..e3027ff604f3d 100644 --- a/library/core/src/iter/range/ptr.rs +++ b/library/core/src/iter/range/ptr.rs @@ -58,12 +58,12 @@ macro_rules! impl_iterator_for_ptr_range { None } else { let curr = self.start; - let next = curr.wrapping_add(1); - self.start = if (curr..self.end).contains(&next) { - next + 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 wrapped or - // landed beyond end. + // Saturate to self.end if the wrapping_add would wrap + // or land beyond end. self.end }; Some(curr)