auto merge of #13468 : alexcrichton/rust/issue-13467, r=thestinger

Previously, all slices derived from a vector whose values were of size 0 had a
null pointer as the 'data' pointer on the slice. This caused first pointer to be
yielded during iteration to always be the null pointer. Due to the null pointer
optimization, this meant that the first return value was None, instead of
Some(&T).

This commit changes slice construction from a Vec instance to use a base pointer
of 1 if the values have zero size. This means that the iterator will never
return null, and the iteration will proceed appropriately.

Closes #13467
This commit is contained in:
bors 2014-04-13 04:06:53 -07:00
commit 770b2fea06

View File

@ -598,7 +598,12 @@ impl<T> Vec<T> {
/// ```
#[inline]
pub fn as_mut_slice<'a>(&'a mut self) -> &'a mut [T] {
let slice = Slice { data: self.ptr as *T, len: self.len };
// See the comment in as_slice() for what's going on here.
let slice = if mem::size_of::<T>() == 0 {
Slice { data: 1 as *T, len: self.len }
} else {
Slice { data: self.ptr as *T, len: self.len }
};
unsafe { transmute(slice) }
}
@ -1335,7 +1340,15 @@ impl<T> Vector<T> for Vec<T> {
/// ```
#[inline]
fn as_slice<'a>(&'a self) -> &'a [T] {
let slice = Slice { data: self.ptr as *T, len: self.len };
// If we have a 0-sized vector, then the base pointer should not be NULL
// because an iterator over the slice will attempt to yield the base
// pointer as the first element in the vector, but this will end up
// being Some(NULL) which is optimized to None.
let slice = if mem::size_of::<T>() == 0 {
Slice { data: 1 as *T, len: self.len }
} else {
Slice { data: self.ptr as *T, len: self.len }
};
unsafe { transmute(slice) }
}
}
@ -1588,4 +1601,35 @@ mod tests {
vec.retain(|x| x%2 == 0);
assert!(vec == Vec::from_slice([2u, 4]));
}
#[test]
fn zero_sized_values() {
let mut v = Vec::new();
assert_eq!(v.len(), 0);
v.push(());
assert_eq!(v.len(), 1);
v.push(());
assert_eq!(v.len(), 2);
assert_eq!(v.pop(), Some(()));
assert_eq!(v.pop(), Some(()));
assert_eq!(v.pop(), None);
assert_eq!(v.iter().len(), 0);
v.push(());
assert_eq!(v.iter().len(), 1);
v.push(());
assert_eq!(v.iter().len(), 2);
for &() in v.iter() {}
assert_eq!(v.mut_iter().len(), 2);
v.push(());
assert_eq!(v.mut_iter().len(), 3);
v.push(());
assert_eq!(v.mut_iter().len(), 4);
for &() in v.mut_iter() {}
unsafe { v.set_len(0); }
assert_eq!(v.mut_iter().len(), 0);
}
}