diff --git a/.travis.yml b/.travis.yml index 3730670d..c02af700 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,6 +15,7 @@ matrix: - rust: stable env: - FEATURES='array-sizes-33-128 array-sizes-129-255' + - ARRAYVECTEST_ENSURE_MAYBEUNINIT=1 - rust: beta - rust: nightly env: @@ -23,12 +24,12 @@ matrix: - rust: nightly env: - NODROP_FEATURES='use_needs_drop' - - ARRAYVECTEST_ENSURE_UNION=1 + - ARRAYVECTEST_ENSURE_MAYBEUNINIT=1 - rust: nightly env: - FEATURES='serde use_union' - NODROP_FEATURES='use_union' - - ARRAYVECTEST_ENSURE_UNION=1 + - ARRAYVECTEST_ENSURE_MAYBEUNINIT=1 branches: only: - master diff --git a/build.rs b/build.rs index 1a4ac7d3..a91c5f4d 100644 --- a/build.rs +++ b/build.rs @@ -11,17 +11,28 @@ fn main() { } fn detect_maybe_uninit() { + let has_stable_maybe_uninit = probe(&stable_maybe_uninit()); + if has_stable_maybe_uninit { + println!("cargo:rustc-cfg=has_stable_maybe_uninit"); + return; + } let has_unstable_union_with_md = probe(&maybe_uninit_code(true)); if has_unstable_union_with_md { println!("cargo:rustc-cfg=has_manually_drop_in_union"); println!("cargo:rustc-cfg=has_union_feature"); - return; } +} - let has_stable_union_with_md = probe(&maybe_uninit_code(false)); - if has_stable_union_with_md { - println!("cargo:rustc-cfg=has_manually_drop_in_union"); - } +// To guard against changes in this currently unstable feature, use +// a detection tests instead of a Rustc version and/or date test. +fn stable_maybe_uninit() -> String { + let code = " + #![allow(warnings)] + use std::mem::MaybeUninit; + + fn main() { } + "; + code.to_string() } // To guard against changes in this currently unstable feature, use diff --git a/src/lib.rs b/src/lib.rs index cd8f0b9e..1e05e59a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -50,9 +50,12 @@ use std::fmt; use std::io; -#[cfg(has_manually_drop_in_union)] +#[cfg(has_stable_maybe_uninit)] +#[path="maybe_uninit_stable.rs"] mod maybe_uninit; -#[cfg(not(has_manually_drop_in_union))] +#[cfg(all(not(has_stable_maybe_uninit), has_manually_drop_in_union))] +mod maybe_uninit; +#[cfg(all(not(has_stable_maybe_uninit), not(has_manually_drop_in_union)))] #[path="maybe_uninit_nodrop.rs"] mod maybe_uninit; diff --git a/src/maybe_uninit_stable.rs b/src/maybe_uninit_stable.rs new file mode 100644 index 00000000..cb631a9d --- /dev/null +++ b/src/maybe_uninit_stable.rs @@ -0,0 +1,40 @@ + + +use array::Array; +use std::mem::MaybeUninit as StdMaybeUninit; + +pub struct MaybeUninit { + inner: StdMaybeUninit, +} + +impl MaybeUninit { + /// Create a new MaybeUninit with uninitialized interior + pub unsafe fn uninitialized() -> Self { + MaybeUninit { inner: StdMaybeUninit::uninit() } + } + + /// Create a new MaybeUninit from the value `v`. + pub fn from(v: T) -> Self { + MaybeUninit { inner: StdMaybeUninit::new(v) } + } + + // Raw pointer casts written so that we don't reference or access the + // uninitialized interior value + + /// Return a raw pointer to the start of the interior array + pub fn ptr(&self) -> *const T::Item + where T: Array + { + // std MaybeUninit creates a &self.value reference here which is + // not guaranteed to be sound in our case - we will partially + // initialize the value, not always wholly. + self.inner.as_ptr() as *const T::Item + } + + /// Return a mut raw pointer to the start of the interior array + pub fn ptr_mut(&mut self) -> *mut T::Item + where T: Array + { + self.inner.as_mut_ptr() as *mut T::Item + } +} diff --git a/tests/tests.rs b/tests/tests.rs index 0e694672..b8dbadb2 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -510,10 +510,8 @@ fn test_sizes_129_255() { #[test] -fn test_nightly_uses_maybe_uninit() { - if option_env!("ARRAYVECTEST_ENSURE_UNION").map(|s| !s.is_empty()).unwrap_or(false) { - assert!(cfg!(has_manually_drop_in_union)); - type ByteArray = ArrayVec<[u8; 4]>; - assert!(mem::size_of::() == 5); +fn test_newish_stable_uses_maybe_uninit() { + if option_env!("ARRAYVECTEST_ENSURE_MAYBEUNINIT").map(|s| !s.is_empty()).unwrap_or(false) { + assert!(cfg!(has_stable_maybe_uninit)); } }