From e64afc8c473d43e375ab42bd33db2d0d4ac4e41b Mon Sep 17 00:00:00 2001 From: Thom Chiovoloni Date: Sun, 8 Sep 2019 01:24:45 -0700 Subject: [PATCH 1/3] Use MaybeUninit for storage of inline items. This is a backport of #162 to the smallvec 0.6 branch. To avoid bumping the minimum Rust version, the `maybe-uninit` crate is used in place of `std::mem::MaybeUninit`. To avoid breaking changes, the `Array::ptr` and `ptr_mut` methods are retained but are no longer used, and the API to `from_buf_and_len_unchecked` is unchanged. --- Cargo.toml | 3 +- lib.rs | 91 +++++++++++++++++++++++++++++++----------------------- 2 files changed, 54 insertions(+), 40 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 8622f80..299a646 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "smallvec" -version = "0.6.12" +version = "0.6.13" authors = ["Simon Sapin "] license = "MIT/Apache-2.0" repository = "https://github.com/servo/rust-smallvec" @@ -23,6 +23,7 @@ path = "lib.rs" [dependencies] serde = { version = "1", optional = true } +maybe-uninit = "2.0" [dev_dependencies] bincode = "1.0.1" diff --git a/lib.rs b/lib.rs index cedf2bd..ffb8766 100644 --- a/lib.rs +++ b/lib.rs @@ -45,18 +45,21 @@ use alloc::vec::Vec; #[cfg(feature = "serde")] extern crate serde; +extern crate maybe_uninit; + #[cfg(not(feature = "std"))] mod std { pub use core::*; } +use maybe_uninit::MaybeUninit; + use std::borrow::{Borrow, BorrowMut}; use std::cmp; use std::fmt; use std::hash::{Hash, Hasher}; use std::iter::{IntoIterator, FromIterator, repeat}; use std::mem; -use std::mem::ManuallyDrop; use std::ops; use std::ptr; use std::slice; @@ -275,26 +278,28 @@ impl<'a, T: 'a> Drop for Drain<'a,T> { #[cfg(feature = "union")] union SmallVecData { - inline: ManuallyDrop, + inline: MaybeUninit, heap: (*mut A::Item, usize), } #[cfg(feature = "union")] impl SmallVecData { #[inline] - unsafe fn inline(&self) -> &A { - &self.inline + unsafe fn inline(&self) -> *const A::Item { + self.inline.as_ptr() as *const A::Item } #[inline] - unsafe fn inline_mut(&mut self) -> &mut A { - &mut self.inline + unsafe fn inline_mut(&mut self) -> *mut A::Item { + self.inline.as_mut_ptr() as *mut A::Item } #[inline] - fn from_inline(inline: A) -> SmallVecData { - SmallVecData { inline: ManuallyDrop::new(inline) } + fn from_inline(inline: MaybeUninit) -> SmallVecData { + SmallVecData { inline } } #[inline] - unsafe fn into_inline(self) -> A { ManuallyDrop::into_inner(self.inline) } + unsafe fn into_inline(self) -> MaybeUninit { + self.inline + } #[inline] unsafe fn heap(&self) -> (*mut A::Item, usize) { self.heap @@ -311,34 +316,34 @@ impl SmallVecData { #[cfg(not(feature = "union"))] enum SmallVecData { - Inline(ManuallyDrop), + Inline(MaybeUninit), Heap((*mut A::Item, usize)), } #[cfg(not(feature = "union"))] impl SmallVecData { #[inline] - unsafe fn inline(&self) -> &A { + unsafe fn inline(&self) -> *const A::Item { match *self { - SmallVecData::Inline(ref a) => a, + SmallVecData::Inline(ref a) => a.as_ptr() as *const A::Item, _ => debug_unreachable!(), } } #[inline] - unsafe fn inline_mut(&mut self) -> &mut A { + unsafe fn inline_mut(&mut self) -> *mut A::Item { match *self { - SmallVecData::Inline(ref mut a) => a, + SmallVecData::Inline(ref mut a) => a.as_mut_ptr() as *mut A::Item, _ => debug_unreachable!(), } } #[inline] - fn from_inline(inline: A) -> SmallVecData { - SmallVecData::Inline(ManuallyDrop::new(inline)) + fn from_inline(inline: MaybeUninit) -> SmallVecData { + SmallVecData::Inline(inline) } #[inline] - unsafe fn into_inline(self) -> A { + unsafe fn into_inline(self) -> MaybeUninit { match self { - SmallVecData::Inline(a) => ManuallyDrop::into_inner(a), + SmallVecData::Inline(a) => a, _ => debug_unreachable!(), } } @@ -403,11 +408,15 @@ impl SmallVec { /// Construct an empty vector #[inline] pub fn new() -> SmallVec { - unsafe { - SmallVec { - capacity: 0, - data: SmallVecData::from_inline(mem::uninitialized()), - } + // Try to detect invalid custom implementations of `Array`. Hopefuly, + // this check should be optimized away entirely for valid ones. + assert!( + mem::size_of::() == A::size() * mem::size_of::() + && mem::align_of::() >= mem::align_of::() + ); + SmallVec { + capacity: 0, + data: SmallVecData::from_inline(MaybeUninit::uninit()), } } @@ -447,10 +456,10 @@ impl SmallVec { pub fn from_vec(mut vec: Vec) -> SmallVec { if vec.capacity() <= A::size() { unsafe { - let mut data = SmallVecData::::from_inline(mem::uninitialized()); + let mut data = SmallVecData::::from_inline(MaybeUninit::uninit()); let len = vec.len(); vec.set_len(0); - ptr::copy_nonoverlapping(vec.as_ptr(), data.inline_mut().ptr_mut(), len); + ptr::copy_nonoverlapping(vec.as_ptr(), data.inline_mut(), len); SmallVec { capacity: len, @@ -483,7 +492,7 @@ impl SmallVec { pub fn from_buf(buf: A) -> SmallVec { SmallVec { capacity: A::size(), - data: SmallVecData::from_inline(buf), + data: SmallVecData::from_inline(MaybeUninit::new(buf)), } } @@ -523,7 +532,7 @@ impl SmallVec { pub unsafe fn from_buf_and_len_unchecked(buf: A, len: usize) -> SmallVec { SmallVec { capacity: len, - data: SmallVecData::from_inline(buf), + data: SmallVecData::from_inline(MaybeUninit::new(buf)), } } @@ -571,7 +580,7 @@ impl SmallVec { let (ptr, len) = self.data.heap(); (ptr, len, self.capacity) } else { - (self.data.inline().ptr(), self.capacity, A::size()) + (self.data.inline(), self.capacity, A::size()) } } } @@ -584,7 +593,7 @@ impl SmallVec { let &mut (ptr, ref mut len_ptr) = self.data.heap_mut(); (ptr, len_ptr, self.capacity) } else { - (self.data.inline_mut().ptr_mut(), &mut self.capacity, A::size()) + (self.data.inline_mut(), &mut self.capacity, A::size()) } } } @@ -651,8 +660,8 @@ impl SmallVec { if unspilled { return; } - self.data = SmallVecData::from_inline(mem::uninitialized()); - ptr::copy_nonoverlapping(ptr, self.data.inline_mut().ptr_mut(), len); + self.data = SmallVecData::from_inline(MaybeUninit::uninit()); + ptr::copy_nonoverlapping(ptr, self.data.inline_mut(), len); self.capacity = len; } else if new_cap != cap { let mut vec = Vec::with_capacity(new_cap); @@ -717,8 +726,8 @@ impl SmallVec { if self.inline_size() >= len { unsafe { let (ptr, len) = self.data.heap(); - self.data = SmallVecData::from_inline(mem::uninitialized()); - ptr::copy_nonoverlapping(ptr, self.data.inline_mut().ptr_mut(), len); + self.data = SmallVecData::from_inline(MaybeUninit::uninit()); + ptr::copy_nonoverlapping(ptr, self.data.inline_mut(), len); deallocate(ptr, self.capacity); self.capacity = len; } @@ -883,7 +892,7 @@ impl SmallVec { unsafe { let data = ptr::read(&self.data); mem::forget(self); - Ok(data.into_inline()) + Ok(data.into_inline().assume_init()) } } } @@ -1041,8 +1050,12 @@ impl SmallVec where A::Item: Copy { SmallVec { capacity: len, data: SmallVecData::from_inline(unsafe { - let mut data: A = mem::uninitialized(); - ptr::copy_nonoverlapping(slice.as_ptr(), data.ptr_mut(), len); + let mut data: MaybeUninit = MaybeUninit::uninit(); + ptr::copy_nonoverlapping( + slice.as_ptr(), + data.as_mut_ptr() as *mut A::Item, + len, + ); data }) } @@ -1587,8 +1600,8 @@ macro_rules! impl_array( unsafe impl Array for [T; $size] { type Item = T; fn size() -> usize { $size } - fn ptr(&self) -> *const T { self.as_ptr() } - fn ptr_mut(&mut self) -> *mut T { self.as_mut_ptr() } + fn ptr(&self) -> *const T { unimplemented!() } + fn ptr_mut(&mut self) -> *mut T { unimplemented!() } } )+ } @@ -1889,7 +1902,7 @@ mod tests { assert_eq!(&v.iter().map(|v| *v).collect::>(), &[0, 5, 6, 1, 2, 3]); } - #[cfg(feature = "std")] + #[cfg(all(feature = "std", not(miri)))] // Miri currently does not support unwinding #[test] // https://github.com/servo/rust-smallvec/issues/96 fn test_insert_many_panic() { From 26ea2caa43eabe5e3fe43b2ef4f3c60c757745c9 Mon Sep 17 00:00:00 2001 From: Thom Chiovoloni Date: Sun, 8 Sep 2019 01:47:13 -0700 Subject: [PATCH 2/3] Run miri in CI --- .travis.yml | 3 ++- scripts/run_miri.sh | 21 +++++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 scripts/run_miri.sh diff --git a/.travis.yml b/.travis.yml index ee744e1..a5e7388 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,4 +11,5 @@ script: | ([ $TRAVIS_RUST_VERSION != nightly ] || cargo check --verbose --no-default-features) && ([ $TRAVIS_RUST_VERSION != nightly ] || cargo test --verbose --features union) && ([ $TRAVIS_RUST_VERSION != nightly ] || cargo test --verbose --all-features) && - ([ $TRAVIS_RUST_VERSION != nightly ] || cargo bench --verbose bench) + ([ $TRAVIS_RUST_VERSION != nightly ] || cargo bench --verbose bench) && + ([ $TRAVIS_RUST_VERSION != nightly ] || bash ./scripts/run_miri.sh) diff --git a/scripts/run_miri.sh b/scripts/run_miri.sh new file mode 100644 index 0000000..42f2884 --- /dev/null +++ b/scripts/run_miri.sh @@ -0,0 +1,21 @@ +#!/usr/bin/bash + +set -ex + +# Clean out our target dir, which may have artifacts compiled by a version of +# rust different from the one we're about to download. +cargo clean + +# Install and run the latest version of nightly where miri built successfully. +# Taken from: https://github.com/rust-lang/miri#running-miri-on-ci + +MIRI_NIGHTLY=nightly-$(curl -s https://rust-lang.github.io/rustup-components-history/x86_64-unknown-linux-gnu/miri) +echo "Installing latest nightly with Miri: $MIRI_NIGHTLY" +rustup default "$MIRI_NIGHTLY" + +rustup component add miri +cargo miri setup + +cargo miri test --verbose -- -- -Zunstable-options --exclude-should-panic +cargo miri test --verbose --features union -- -- -Zunstable-options --exclude-should-panic +cargo miri test --verbose --all-features -- -- -Zunstable-options --exclude-should-panic From 58d5416fab7cae3d1f87f89a0afd91ffa1ccb7ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20Cobos=20=C3=81lvarez?= Date: Sat, 19 Oct 2019 05:02:11 +0200 Subject: [PATCH 3/3] Add a test for #139. Works now on trunk. Closes #139 --- lib.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib.rs b/lib.rs index ffb8766..55278ad 100644 --- a/lib.rs +++ b/lib.rs @@ -2351,6 +2351,13 @@ mod tests { assert_eq!(v[..], ['a']); } + // #139 + #[test] + fn uninhabited() { + enum Void {} + let _sv = SmallVec::<[Void; 8]>::new(); + } + #[test] fn grow_spilled_same_size() { let mut v: SmallVec<[u8; 2]> = SmallVec::new();