From 5cebe34268f4cd27b8350482c76929f6f1490a0b Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 10 Jul 2018 09:01:10 +0200 Subject: [PATCH] add some tests for ZST slices, and fix failures due to unaligned references --- src/libcore/slice/mod.rs | 74 +++++++++++++++++++------------------- src/libcore/tests/slice.rs | 69 +++++++++++++++++++++++++++++++++++ 2 files changed, 107 insertions(+), 36 deletions(-) diff --git a/src/libcore/slice/mod.rs b/src/libcore/slice/mod.rs index 69bdb15ee4b8f..4acaf4054840d 100644 --- a/src/libcore/slice/mod.rs +++ b/src/libcore/slice/mod.rs @@ -45,7 +45,7 @@ use option::Option; use option::Option::{None, Some}; use result::Result; use result::Result::{Ok, Err}; -use ptr; +use ptr::{self, NonNull}; use mem; use marker::{Copy, Send, Sync, Sized, self}; use iter_private::TrustedRandomAccess; @@ -80,7 +80,7 @@ macro_rules! slice_offset { ($ptr:expr, $by:expr) => {{ let ptr = $ptr; if size_from_ptr(ptr) == 0 { - (ptr as *mut i8).wrapping_offset($by) as _ + (ptr as *mut i8).wrapping_offset($by.wrapping_mul(align_from_ptr(ptr) as isize)) as _ } else { ptr.offset($by) } @@ -93,7 +93,7 @@ macro_rules! make_ref { let ptr = $ptr; if size_from_ptr(ptr) == 0 { // Use a non-null pointer value - &*(1 as *mut _) + &*(NonNull::dangling().as_ptr() as *const _) } else { &*ptr } @@ -106,13 +106,39 @@ macro_rules! make_ref_mut { let ptr = $ptr; if size_from_ptr(ptr) == 0 { // Use a non-null pointer value - &mut *(1 as *mut _) + &mut *(NonNull::dangling().as_ptr()) } else { &mut *ptr } }}; } +macro_rules! make_slice { + ($start: expr, $end: expr) => {{ + let start = $start; + let len = unsafe { ptrdistance($start, $end) }; + if size_from_ptr(start) == 0 { + // use a non-null pointer value + unsafe { from_raw_parts(NonNull::dangling().as_ptr() as *const _, len) } + } else { + unsafe { from_raw_parts(start, len) } + } + }} +} + +macro_rules! make_mut_slice { + ($start: expr, $end: expr) => {{ + let start = $start; + let len = unsafe { ptrdistance($start, $end) }; + if size_from_ptr(start) == 0 { + // use a non-null pointer value + unsafe { from_raw_parts_mut(NonNull::dangling().as_ptr(), len) } + } else { + unsafe { from_raw_parts_mut(start, len) } + } + }} +} + #[lang = "slice"] #[cfg(not(test))] impl [T] { @@ -581,7 +607,7 @@ impl [T] { pub fn iter(&self) -> Iter { unsafe { let p = if mem::size_of::() == 0 { - 1 as *const _ + NonNull::dangling().as_ptr() as *const _ } else { let p = self.as_ptr(); assume(!p.is_null()); @@ -612,7 +638,7 @@ impl [T] { pub fn iter_mut(&mut self) -> IterMut { unsafe { let p = if mem::size_of::() == 0 { - 1 as *mut _ + NonNull::dangling().as_ptr() } else { let p = self.as_mut_ptr(); assume(!p.is_null()); @@ -2369,6 +2395,11 @@ fn size_from_ptr(_: *const T) -> usize { mem::size_of::() } +#[inline] +fn align_from_ptr(_: *const T) -> usize { + mem::align_of::() +} + // The shared definition of the `Iter` and `IterMut` iterators macro_rules! iterator { (struct $name:ident -> $ptr:ty, $elem:ty, $mkref:ident) => { @@ -2547,34 +2578,6 @@ macro_rules! iterator { } } -macro_rules! make_slice { - ($start: expr, $end: expr) => {{ - let start = $start; - let diff = ($end as usize).wrapping_sub(start as usize); - if size_from_ptr(start) == 0 { - // use a non-null pointer value - unsafe { from_raw_parts(1 as *const _, diff) } - } else { - let len = diff / size_from_ptr(start); - unsafe { from_raw_parts(start, len) } - } - }} -} - -macro_rules! make_mut_slice { - ($start: expr, $end: expr) => {{ - let start = $start; - let diff = ($end as usize).wrapping_sub(start as usize); - if size_from_ptr(start) == 0 { - // use a non-null pointer value - unsafe { from_raw_parts_mut(1 as *mut _, diff) } - } else { - let len = diff / size_from_ptr(start); - unsafe { from_raw_parts_mut(start, len) } - } - }} -} - /// Immutable slice iterator /// /// This struct is created by the [`iter`] method on [slices]. @@ -2791,11 +2794,10 @@ impl<'a, T> ExactSizeIterator for IterMut<'a, T> { } // Return the number of elements of `T` from `start` to `end`. -// Return the arithmetic difference if `T` is zero size. #[inline(always)] unsafe fn ptrdistance(start: *const T, end: *const T) -> usize { if mem::size_of::() == 0 { - (end as usize).wrapping_sub(start as usize) + (end as usize).wrapping_sub(start as usize) / mem::align_of::() } else { end.offset_from(start) as usize } diff --git a/src/libcore/tests/slice.rs b/src/libcore/tests/slice.rs index 7981567067dad..feb6e52981021 100644 --- a/src/libcore/tests/slice.rs +++ b/src/libcore/tests/slice.rs @@ -376,6 +376,75 @@ fn test_windows_zip() { assert_eq!(res, [14, 18, 22, 26]); } +#[test] +#[allow(const_err)] +fn test_iter_consistency() { + use std::fmt::Debug; + use std::mem; + + fn helper(x : T) { + let v : &[T] = &[x, x, x]; + let len = v.len(); + + // TODO: Once #42789 is resolved, also compare the locations with each other + // and with slice patterns. + + for i in 0..len { + let nth = v.iter().nth(i).unwrap(); + assert!(nth as *const _ as usize % mem::align_of::() == 0); + assert_eq!(nth, &x); + } + assert_eq!(v.iter().nth(len), None, "nth(len) should return None"); + + let mut it = v.iter(); + for i in 0..len{ + let remaining = len - i; + assert_eq!(it.size_hint(), (remaining, Some(remaining))); + + let next = it.next().unwrap(); + assert!(next as *const _ as usize % mem::align_of::() == 0); + assert_eq!(next, &x); + } + assert_eq!(it.size_hint(), (0, Some(0))); + assert_eq!(it.next(), None, "The final call to next() should return None"); + } + + fn helper_mut(mut x : T) { + let v : &mut [T] = &mut [x, x, x]; + let len = v.len(); + + // TODO: Once #42789 is resolved, also compare the locations with each other + // and with slice patterns. + + for i in 0..len { + let nth = v.iter_mut().nth(i).unwrap(); + assert!(nth as *mut _ as usize % mem::align_of::() == 0); + assert_eq!(nth, &mut x); + } + assert_eq!(v.iter().nth(len), None, "nth(len) should return None"); + + let mut it = v.iter_mut(); + for i in 0..len { + let remaining = len - i; + assert_eq!(it.size_hint(), (remaining, Some(remaining))); + + let next = it.next().unwrap(); + assert!(next as *mut _ as usize % mem::align_of::() == 0); + assert_eq!(next, &mut x); + } + assert_eq!(it.size_hint(), (0, Some(0))); + assert_eq!(it.next(), None, "The final call to next() should return None"); + } + + // Make sure this works consistently for various types, including ZSTs. + helper(0u32); + helper(()); + helper([0u32; 0]); // ZST with alignment > 0 + helper_mut(0u32); + helper_mut(()); + helper_mut([0u32; 0]); // ZST with alignment > 0 +} + // The current implementation of SliceIndex fails to handle methods // orthogonally from range types; therefore, it is worth testing // all of the indexing operations on each input.