Skip to content

Commit

Permalink
add some tests for ZST slices, and fix failures due to unaligned refe…
Browse files Browse the repository at this point in the history
…rences
  • Loading branch information
RalfJung committed Jul 11, 2018
1 parent 77d8059 commit 5cebe34
Show file tree
Hide file tree
Showing 2 changed files with 107 additions and 36 deletions.
74 changes: 38 additions & 36 deletions src/libcore/slice/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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)
}
Expand All @@ -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
}
Expand All @@ -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> [T] {
Expand Down Expand Up @@ -581,7 +607,7 @@ impl<T> [T] {
pub fn iter(&self) -> Iter<T> {
unsafe {
let p = if mem::size_of::<T>() == 0 {
1 as *const _
NonNull::dangling().as_ptr() as *const _
} else {
let p = self.as_ptr();
assume(!p.is_null());
Expand Down Expand Up @@ -612,7 +638,7 @@ impl<T> [T] {
pub fn iter_mut(&mut self) -> IterMut<T> {
unsafe {
let p = if mem::size_of::<T>() == 0 {
1 as *mut _
NonNull::dangling().as_ptr()
} else {
let p = self.as_mut_ptr();
assume(!p.is_null());
Expand Down Expand Up @@ -2369,6 +2395,11 @@ fn size_from_ptr<T>(_: *const T) -> usize {
mem::size_of::<T>()
}

#[inline]
fn align_from_ptr<T>(_: *const T) -> usize {
mem::align_of::<T>()
}

// The shared definition of the `Iter` and `IterMut` iterators
macro_rules! iterator {
(struct $name:ident -> $ptr:ty, $elem:ty, $mkref:ident) => {
Expand Down Expand Up @@ -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].
Expand Down Expand Up @@ -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<T>(start: *const T, end: *const T) -> usize {
if mem::size_of::<T>() == 0 {
(end as usize).wrapping_sub(start as usize)
(end as usize).wrapping_sub(start as usize) / mem::align_of::<T>()
} else {
end.offset_from(start) as usize
}
Expand Down
69 changes: 69 additions & 0 deletions src/libcore/tests/slice.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<T : Copy + Debug + PartialEq>(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::<T>() == 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::<T>() == 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<T : Copy + Debug + PartialEq>(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::<T>() == 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::<T>() == 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.
Expand Down

0 comments on commit 5cebe34

Please sign in to comment.