summary refs log tree commit diff
path: root/src/libstd
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2014-04-13 04:06:53 -0700
committerbors <bors@rust-lang.org>2014-04-13 04:06:53 -0700
commit770b2fea06f96714b4602d54c0681dce8ad315b1 (patch)
tree9faa52b8edee9f56f88425d3ed2a5d6ba6a98566 /src/libstd
parent2f790546504f869788f3c0e9585b51470773332f (diff)
parent7a82d478a3c9301eda4453ec222842f750e13630 (diff)
downloadrust-770b2fea06f96714b4602d54c0681dce8ad315b1.tar.gz
rust-770b2fea06f96714b4602d54c0681dce8ad315b1.zip
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
Diffstat (limited to 'src/libstd')
-rw-r--r--src/libstd/vec.rs48
1 files changed, 46 insertions, 2 deletions
diff --git a/src/libstd/vec.rs b/src/libstd/vec.rs
index da0e0d73fed..a69120de00f 100644
--- a/src/libstd/vec.rs
+++ b/src/libstd/vec.rs
@@ -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);
+    }
 }