diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs index 6128c119b6b76..0f5ef37092412 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs @@ -202,6 +202,13 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { flags.push((sym::_Self, Some("{integral}".to_owned()))); } + if let ty::RawPtr(ptr) = self_ty.kind() { + let string = match ptr.mutbl { + hir::Mutability::Mut => "*mut T", + hir::Mutability::Not => "*const T", + }; + flags.push((sym::_Self, Some(string.to_owned()))); + } if let ty::Array(aty, len) = self_ty.kind() { flags.push((sym::_Self, Some("[]".to_owned()))); flags.push((sym::_Self, Some(format!("[{}]", aty)))); diff --git a/library/core/src/iter/range.rs b/library/core/src/iter/range.rs index 06733a1b50b91..35f371edfa813 100644 --- a/library/core/src/iter/range.rs +++ b/library/core/src/iter/range.rs @@ -21,6 +21,10 @@ unsafe_impl_trusted_step![char i8 i16 i32 i64 i128 isize u8 u16 u32 u64 u128 usi /// The *successor* operation moves towards values that compare greater. /// The *predecessor* operation moves towards values that compare lesser. #[unstable(feature = "step_trait", reason = "recently redesigned", issue = "42168")] +#[rustc_on_unimplemented( + on(_Self = "*const T", label = "use core::ptr::IterConst to iterate a range of pointers"), + on(_Self = "*mut T", label = "use core::ptr::IterMut to iterate a range of pointers") +)] pub trait Step: Clone + PartialOrd + Sized { /// Returns the number of *successor* steps required to get from `start` to `end`. /// diff --git a/library/core/src/ptr/iter.rs b/library/core/src/ptr/iter.rs new file mode 100644 index 0000000000000..354b584503b2c --- /dev/null +++ b/library/core/src/ptr/iter.rs @@ -0,0 +1,513 @@ +use crate::fmt::{self, Debug}; +use crate::hash::{Hash, Hasher}; +use crate::iter::{FusedIterator, TrustedLen, TrustedRandomAccess, TrustedRandomAccessNoCoerce}; +use crate::mem; + +macro_rules! ptr_iter { + ($ty:ident, *$mutability:tt T) => { + /// Iterator with items generated by incrementing a raw pointer. + /// + /// # Safety + /// + /// Constructing an iterator, as well as dereferencing any pointers + /// obtained from traversing that iterator, require several invariants + /// to be upheld by the programmer or else the result is Undefined + /// Behavior. + #[doc = concat!("See [`", stringify!($ty), "::new`].")] + /// + /// Most importantly, remember that 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 traversal of this iterator are + /// only dereferenceable if the `start` and `end` with which the + /// iterator was constructed 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 iterator are at offsets of multiples of + /// `size_of::()` bytes from the iterator's start, so if the + /// iterator's starting pointer is misaligned, then all the pointers in + /// the iterator are misaligned as well. + /// + /// # Example + /// + #[doc = example!($mutability, + const { + /// ``` + /// #![feature(ptr_iter)] + /// + /// use core::ptr; + /// + /// // Designed to be called from C++ or C. + /// #[no_mangle] + /// unsafe extern "C" fn demo(start: *const u16, end: *const u16) { + /// for ptr in ptr::IterConst::new(start, end) { + /// println!("{}", *ptr); + /// } + /// } + /// + /// fn main() { + /// let slice = &[1u16, 2, 3]; + /// let range = slice.as_ptr_range(); + /// unsafe { demo(range.start, range.end); } + /// } + /// ``` + } + mut { + /// ``` + /// #![feature(ptr_iter)] + /// #![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 ptr::IterMut::new(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); + /// } + /// } + /// ``` + })] + #[unstable(feature = "ptr_iter", issue = "none")] + pub struct $ty { + start: *$mutability T, + end: *$mutability T, + } + + impl $ty { + /// Creates an iterator with functionality analogous to: + /// + /// ``` + /// # let start = core::ptr::null::<()>(); + /// # let end = start; + /// # + /// let mut item = start; + /// while item < end { + /// // ... do thing with item + /// + /// item = unsafe { item.offset(1) }; + /// } + /// ``` + /// + /// # Safety + /// + /// If any of the following conditions are violated, the result is + /// Undefined Behavior: + /// + /// * Both the `start` and `end` pointer must be either in bounds or + /// one byte past the end of the same [allocated object]. + /// + /// * Both pointers must be *derived from* a pointer to the same + /// object. (See [pointer::offset_from] for an example.) + /// + /// * `null::() < start <= end`. + /// + /// * If `T`'s size is positive, then the distance between the + /// pointers, in bytes, must be an exact multiple of the size of + /// `T`. + /// + /// * The distance between the pointers, **in bytes**, cannot + /// overflow an `isize`. + /// + /// Rust types are never larger than `isize::MAX`, so two pointers + /// within some value of any Rust type will always satisfy the last + /// condition. The standard library also generally ensures that + /// allocations never reach a size where an offset is a concern. For + /// instance, `Vec` and `Box` ensure they never allocate more than + /// `isize::MAX` bytes, so the pointers from `vec.as_ptr_range()` + /// always satisfies the last condition. + /// + /// Most platforms fundamentally can't even construct such a large + /// allocation. For instance, no known 64-bit platform can ever + /// serve a request for 263 bytes due to page-table + /// limitations or splitting the address space. However, some 32-bit + /// and 16-bit platforms may successfully serve a request for more + /// than `isize::MAX` bytes with things like Physical Address + /// Extension. As such, memory acquired directly from allocators or + /// memory mapped files *may* be too large to handle with this + /// iterator. (Note that [`offset`] and [`add`] also have a similar + /// limitation and hence cannot be used on such large allocations + /// either.) + /// + /// While not a requirement of this constructor call, dereferencing + /// any of the pointers obtained through iteration of this iterator + /// requires that `start` have been constructed with provenance over + /// that memory location. All the pointers produced by the iterator + /// have the same provenance as `start`. + /// + #[doc = example!($mutability, + const { + /// ```no_run + /// #![feature(ptr_iter)] + /// + /// use core::ptr; + /// + /// let slice = &[0i32, 1, 2, 3]; + /// let start: *const i32 = &slice[0]; // provenance over slice[0] only + /// let end = unsafe { start.offset(4) }; + /// + /// for p in unsafe { ptr::IterConst::new(start, end) } { // UB 💥 + /// println!("{}", unsafe { *p }); + /// } + /// ``` + } + mut { + /// ```no_run + /// #![feature(ptr_iter)] + /// + /// use core::ptr; + /// + /// let slice = &mut [0i32, 1, 2, 3]; + /// let start: *mut i32 = &mut slice[0]; // provenance over slice[0] only + /// let end = unsafe { start.offset(4) }; + /// + /// for p in unsafe { ptr::IterMut::new(start, end) } { + /// unsafe { *p = 99; } + /// } + /// ``` + })] + /// + /// Correct code would instead have used: + /// + #[doc = example!($mutability, + const { + /// ``` + /// # let slice = &[0i32, 1, 2, 3]; + /// let start = slice.as_ptr(); + /// ``` + } + mut { + /// ``` + /// # let slice = &mut [0i32, 1, 2, 3]; + /// let start = slice.as_mut_ptr(); + /// ``` + })] + /// + /// or: + /// + #[doc = example!($mutability, + const { + /// ``` + /// use core::ops::Range; + /// + /// # let slice = &[0i32, 1, 2, 3]; + /// let Range { start, end } = slice.as_ptr_range(); + /// ``` + } + mut { + /// ``` + /// use core::ops::Range; + /// + /// # let slice = &mut [0i32, 1, 2, 3]; + /// let Range { start, end } = slice.as_mut_ptr_range(); + /// ``` + })] + /// + /// [allocated object]: crate::ptr#allocated-object + /// [`offset`]: pointer::offset + /// [`add`]: pointer::add + #[unstable(feature = "ptr_iter", issue = "none")] + pub unsafe fn new(start: *$mutability T, end: *$mutability T) -> Self { + $ty { start, end } + } + + /// Returns true if the iterator has no pointers remaining in it. + /// + /// # Example + /// + #[doc = example!($mutability, + const { + /// ``` + /// #![feature(ptr_iter)] + /// + /// use core::ptr; + /// + /// let slice = &[0i32, 1, 2, 3]; + /// let start = slice.as_ptr(); + /// let end = unsafe { start.offset(2) }; + /// + /// let mut iter = unsafe { ptr::IterConst::new(start, end) }; + /// assert!(!iter.is_empty()); + /// assert_eq!(unsafe { *iter.next().unwrap() }, 0); + /// assert!(!iter.is_empty()); + /// assert_eq!(unsafe { *iter.next().unwrap() }, 1); + /// assert!(iter.is_empty()); + /// assert_eq!(iter.next(), None); + /// ``` + } + mut { + /// ``` + /// #![feature(ptr_iter)] + /// + /// use core::ptr; + /// + /// let slice = &mut [0i32, 1, 2, 3]; + /// let start = slice.as_mut_ptr(); + /// let end = unsafe { start.offset(2) }; + /// + /// let mut iter = unsafe { ptr::IterMut::new(start, end) }; + /// assert!(!iter.is_empty()); + /// unsafe { *iter.next().unwrap() = 99; } + /// assert!(!iter.is_empty()); + /// unsafe { *iter.next().unwrap() = 99; } + /// assert!(iter.is_empty()); + /// assert_eq!(iter.next(), None); + /// assert_eq!(slice, &[99, 99, 2, 3]); + /// ``` + })] + #[unstable(feature = "ptr_iter", issue = "none")] + pub fn is_empty(&self) -> bool { + !(self.start < self.end) + } + } + + #[unstable(feature = "ptr_iter", issue = "none")] + impl Iterator for $ty { + type Item = *$mutability T; + + fn next(&mut self) -> Option { + if self.is_empty() { + None + } else { + let curr = self.start; + // SAFETY: `new`'s extensive precondition allows us to do + // this until the iterator becomes exhausted. + self.start = unsafe { curr.offset(1) }; + 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 { + // SAFETY: `new`'s extensive precondition allows us to do this. + let hint = unsafe { self.end.offset_from(self.start) } as usize; + (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) => { + if n <= len { + // SAFETY: `start` remains at most equal to `end` + // which is in bounds or one byte past the end of + // the same allocated object according to the + // preconditions of `new`. + self.start = unsafe { self.start.add(n) }; + Ok(()) + } else { + self.start = self.end; + Err(len) + } + } + } + } + + #[doc(hidden)] + unsafe fn __iterator_get_unchecked(&mut self, idx: usize) -> Self::Item { + // SAFETY: TrustedRandomAccess guarantees that there are enough + // remaining items in the iterator, so this offset lands + // somewhere in between self.start and self.end. + unsafe { self.start.add(idx) } + } + } + + #[unstable(feature = "ptr_iter", issue = "none")] + impl DoubleEndedIterator for $ty { + 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) => { + // Advance leaving `len - 1` elements in the iterator. + // + // SAFETY: this pointer remains in between `start` and + // `end` which is in bounds of the same allocated object + // according to the preconditions of `new`. + self.end = unsafe { self.start.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 { + // Advance leaving `len - n - 1` elements in the + // iterator. + // + // SAFETY: this pointer remains in between `start` + // and `end` which is in bounds of the same + // allocated object according to the preconditions + // of `new`. + self.end = unsafe { self.start.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) => { + if n <= len { + // Advance leaving `len - n` elements in the + // iterator. + // + // SAFETY: this pointer remains in between `start` + // and `end` which is in bounds of the same + // allocated object according to the preconditions + // of `new`. + self.end = unsafe { self.start.add(len - n) }; + Ok(()) + } else { + // Advance too far. + self.end = self.start; + Err(len) + } + } + } + } + } + + #[unstable(feature = "ptr_iter", issue = "none")] + impl FusedIterator for $ty {} + + #[unstable(feature = "trusted_len", issue = "37572")] + unsafe impl TrustedLen for $ty {} + + #[doc(hidden)] + #[unstable(feature = "trusted_random_access", issue = "none")] + unsafe impl TrustedRandomAccess for $ty {} + + #[doc(hidden)] + #[unstable(feature = "trusted_random_access", issue = "none")] + unsafe impl TrustedRandomAccessNoCoerce for $ty { + const MAY_HAVE_SIDE_EFFECT: bool = false; + } + + // not Copy -- see #27186 + #[unstable(feature = "ptr_iter", issue = "none")] + impl Clone for $ty { + fn clone(&self) -> Self { + $ty { + start: self.start, + end: self.end, + } + } + } + + #[unstable(feature = "ptr_iter", issue = "none")] + impl PartialEq for $ty { + fn eq(&self, other: &Self) -> bool { + self.start == other.start && self.end == other.end + } + } + + #[unstable(feature = "ptr_iter", issue = "none")] + impl Eq for $ty {} + + #[unstable(feature = "ptr_iter", issue = "none")] + impl Hash for $ty { + fn hash(&self, state: &mut H) { + self.start.hash(state); + self.end.hash(state); + } + } + + #[unstable(feature = "ptr_iter", issue = "none")] + impl Debug for $ty { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter + .debug_struct(stringify!($ty)) + .field("start", &self.start) + .field("end", &self.end) + .finish() + } + } + }; +} + +macro_rules! example { + (const, const { $($doc:tt)* } mut $unused:tt) => { + doc_comment_to_literal!($($doc)*) + }; + + (mut, const $unused:tt mut { $($doc:tt)* }) => { + doc_comment_to_literal!($($doc)*) + }; +} + +macro_rules! doc_comment_to_literal { + ($(#[doc = $example:literal])*) => { + concat!($($example, '\n'),*) + }; +} + +ptr_iter!(IterConst, *const T); +ptr_iter!(IterMut, *mut T); diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index 8ab72e6aeeafa..f64300d61da87 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -90,6 +90,10 @@ pub use crate::intrinsics::copy; #[doc(inline)] pub use crate::intrinsics::write_bytes; +mod iter; +#[unstable(feature = "ptr_iter", issue = "none")] +pub use iter::{IterConst, IterMut}; + mod metadata; pub(crate) use metadata::PtrRepr; #[unstable(feature = "ptr_metadata", issue = "81513")] diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs index 00d0259321d13..36cdea6717366 100644 --- a/library/core/tests/lib.rs +++ b/library/core/tests/lib.rs @@ -63,6 +63,7 @@ #![feature(unwrap_infallible)] #![feature(result_into_ok_or_err)] #![cfg_attr(not(bootstrap), feature(portable_simd))] +#![feature(ptr_iter)] #![feature(ptr_metadata)] #![feature(once_cell)] #![feature(unsized_tuple_coercion)] diff --git a/library/core/tests/ptr.rs b/library/core/tests/ptr.rs index 11af8090c3a4f..7d258cb63b8b2 100644 --- a/library/core/tests/ptr.rs +++ b/library/core/tests/ptr.rs @@ -1,3 +1,5 @@ +mod iter; + use core::cell::RefCell; use core::ptr; use core::ptr::*; diff --git a/library/core/tests/ptr/iter.rs b/library/core/tests/ptr/iter.rs new file mode 100644 index 0000000000000..31dacc68592ea --- /dev/null +++ b/library/core/tests/ptr/iter.rs @@ -0,0 +1,97 @@ +use core::ops::Range; +use core::ptr::IterConst; + +#[test] +fn test_ptr_iter_empty() { + let start = &() as *const () as *const [u8; 100]; + let mut iter = unsafe { IterConst::new(start, start) }; + assert_eq!(iter.size_hint(), (0, Some(0))); + assert_eq!(iter.next(), None); + assert_eq!(iter.nth(0), None); + assert_eq!(iter.nth(1), None); + assert_eq!(iter.advance_by(0), Ok(())); + assert_eq!(iter.advance_by(1), Err(0)); + assert_eq!(iter.next_back(), None); + assert_eq!(iter.nth_back(0), None); + assert_eq!(iter.nth_back(1), None); + assert_eq!(iter.advance_back_by(0), Ok(())); + assert_eq!(iter.advance_back_by(1), Err(0)); + assert_eq!(iter, unsafe { IterConst::new(start, start) }); +} + +#[test] +fn test_ptr_iter() { + let data = [[0u8; 10]; 12]; + let Range { start, end } = data.as_ptr_range(); + let mut iter = unsafe { IterConst::new(start, end) }; + assert_eq!(iter.size_hint(), (12, Some(12))); + assert_eq!(iter.next(), Some(&data[0] as _)); + assert_eq!(iter, unsafe { IterConst::new(&data[1], end) }); + assert_eq!(iter.nth(0), Some(&data[1] as _)); + assert_eq!(iter, unsafe { IterConst::new(&data[2], end) }); + assert_eq!(iter.nth(1), Some(&data[3] as _)); + assert_eq!(iter, unsafe { IterConst::new(&data[4], end) }); + assert_eq!(iter.advance_by(0), Ok(())); + assert_eq!(iter, unsafe { IterConst::new(&data[4], end) }); + assert_eq!(iter.advance_by(1), Ok(())); + assert_eq!(iter, unsafe { IterConst::new(&data[5], end) }); + assert_eq!(iter.next_back(), Some(&data[11] as _)); + assert_eq!(iter, unsafe { IterConst::new(&data[5], &data[11]) }); + assert_eq!(iter.nth_back(0), Some(&data[10] as _)); + assert_eq!(iter, unsafe { IterConst::new(&data[5], &data[10]) }); + assert_eq!(iter.nth_back(1), Some(&data[8] as _)); + assert_eq!(iter, unsafe { IterConst::new(&data[5], &data[8]) }); + assert_eq!(iter.advance_back_by(0), Ok(())); + assert_eq!(iter, unsafe { IterConst::new(&data[5], &data[8]) }); + assert_eq!(iter.advance_back_by(1), Ok(())); + assert_eq!(iter, unsafe { IterConst::new(&data[5], &data[7]) }); + + let mut iter = unsafe { IterConst::new(start, end) }; + assert_eq!(iter.nth(20), None); + assert_eq!(iter, unsafe { IterConst::new(end, end) }); + + let mut iter = unsafe { IterConst::new(start, end) }; + assert_eq!(iter.advance_by(20), Err(12)); + assert_eq!(iter, unsafe { IterConst::new(end, end) }); + + let mut iter = unsafe { IterConst::new(start, end) }; + assert_eq!(iter.nth_back(20), None); + assert_eq!(iter, unsafe { IterConst::new(start, start) }); + + let mut iter = unsafe { IterConst::new(start, end) }; + assert_eq!(iter.advance_back_by(20), Err(12)); + assert_eq!(iter, unsafe { IterConst::new(start, start) }); +} + +#[test] +fn test_ptr_iter_zero_sized() { + let start = &0i32 as *const i32 as *const (); + let mut iter = unsafe { IterConst::new(start, start) }; + assert_eq!(iter.size_hint(), (0, Some(0))); + assert_eq!(iter.next(), None); + assert_eq!(iter.nth(0), None); + assert_eq!(iter.nth(1), None); + assert_eq!(iter.advance_by(0), Ok(())); + assert_eq!(iter.advance_by(1), Err(0)); + assert_eq!(iter.next_back(), None); + assert_eq!(iter.nth_back(0), None); + assert_eq!(iter.nth_back(1), None); + assert_eq!(iter.advance_back_by(0), Ok(())); + assert_eq!(iter.advance_back_by(1), Err(0)); + assert_eq!(iter, unsafe { IterConst::new(start, start) }); + + let end = unsafe { start.cast::().add(1).cast::<()>() }; + let mut iter = unsafe { IterConst::new(start, end) }; + assert_eq!(iter.size_hint(), (usize::MAX, None)); + assert_eq!(iter.next(), Some(start)); + assert_eq!(iter.nth(0), Some(start)); + assert_eq!(iter.nth(1), Some(start)); + assert_eq!(iter.advance_by(0), Ok(())); + assert_eq!(iter.advance_by(1), Ok(())); + assert_eq!(iter.next_back(), Some(start)); + assert_eq!(iter.nth_back(0), Some(start)); + assert_eq!(iter.nth_back(1), Some(start)); + assert_eq!(iter.advance_back_by(0), Ok(())); + assert_eq!(iter.advance_back_by(1), Ok(())); + assert_eq!(iter, unsafe { IterConst::new(start, end) }); +} diff --git a/src/test/ui/iterators/ptr-range.rs b/src/test/ui/iterators/ptr-range.rs new file mode 100644 index 0000000000000..95f7acdc81804 --- /dev/null +++ b/src/test/ui/iterators/ptr-range.rs @@ -0,0 +1,5 @@ +fn main() { + let range = [0i32].as_ptr_range(); + for _ in range {} + //~^ ERROR: the trait bound `*const i32: Step` is not satisfied +} diff --git a/src/test/ui/iterators/ptr-range.stderr b/src/test/ui/iterators/ptr-range.stderr new file mode 100644 index 0000000000000..40e0e51cb13fd --- /dev/null +++ b/src/test/ui/iterators/ptr-range.stderr @@ -0,0 +1,13 @@ +error[E0277]: the trait bound `*const i32: Step` is not satisfied + --> $DIR/ptr-range.rs:3:14 + | +LL | for _ in range {} + | ^^^^^ use core::ptr::IterConst to iterate a range of pointers + | + = help: the trait `Step` is not implemented for `*const i32` + = note: required because of the requirements on the impl of `Iterator` for `std::ops::Range<*const i32>` + = note: required because of the requirements on the impl of `IntoIterator` for `std::ops::Range<*const i32>` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`.