diff --git a/src/libcore/array/mod.rs b/src/libcore/array/mod.rs index 937451274cfc2..d0c36a12582e3 100644 --- a/src/libcore/array/mod.rs +++ b/src/libcore/array/mod.rs @@ -385,6 +385,55 @@ where Ord::cmp(&&self[..], &&other[..]) } } +#[stable(since = "1.4.0", feature = "array_default")] +impl<T: Default, const N: usize> Default for [T; N] +where + [T; N]: LengthAtMost32, +{ + #[inline] + fn default() -> [T; N] { + use crate::mem::MaybeUninit; + // invariant: first `init` items are initialized + struct Wrapper<T, const N: usize> { + data: MaybeUninit<[T; N]>, + init: usize, + } + + impl<T, const N: usize> Drop for Wrapper<T, N> { + fn drop(&mut self) { + debug_assert!(self.init <= N); + let ptr = self.data.as_mut_ptr() as *mut T; + for i in 0..self.init { + // SAFETY: we iterate over only initialized values. + // Each value is dropped once. + unsafe { + crate::ptr::drop_in_place(ptr.add(i)); + } + } + } + } + + let mut w = Wrapper::<T, N> { data: MaybeUninit::uninit(), init: 0 }; + let array_pointer = w.data.as_mut_ptr() as *mut T; + for i in 0..N { + // FIXME: this does not work for big N. + // Currently it is acceptable, because N <= 32. + assert!(N <= isize::MAX as usize); + // SAFETY: i < N <= isize::MAX, so add() is correct + unsafe { + let elem_ptr = array_pointer.add(i); + elem_ptr.write(T::default()); + } + w.init += 1; + } + + // Prevent double-read in callee and Wrepper::drop + w.init = 0; + + // SAFETY: all arraty is initialized now. + unsafe { w.data.as_ptr().read() } + } +} /// Implemented for lengths where trait impls are allowed on arrays in core/std #[rustc_on_unimplemented(message = "arrays only have std trait implementations for lengths 0..=32")] @@ -410,26 +459,3 @@ array_impls! { 20 21 22 23 24 25 26 27 28 29 30 31 32 } - -// The Default impls cannot be generated using the array_impls! macro because -// they require array literals. - -macro_rules! array_impl_default { - {$n:expr, $t:ident $($ts:ident)*} => { - #[stable(since = "1.4.0", feature = "array_default")] - impl<T> Default for [T; $n] where T: Default { - fn default() -> [T; $n] { - [$t::default(), $($ts::default()),*] - } - } - array_impl_default!{($n - 1), $($ts)*} - }; - {$n:expr,} => { - #[stable(since = "1.4.0", feature = "array_default")] - impl<T> Default for [T; $n] { - fn default() -> [T; $n] { [] } - } - }; -} - -array_impl_default! {32, T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T T} diff --git a/src/libcore/tests/array.rs b/src/libcore/tests/array.rs index c2a816f0a7d90..40b312331b96f 100644 --- a/src/libcore/tests/array.rs +++ b/src/libcore/tests/array.rs @@ -241,3 +241,36 @@ fn iterator_drops() { } assert_eq!(i.get(), 5); } + +#[test] +fn array_default_impl_avoids_leaks_on_panic() { + use core::sync::atomic::{AtomicUsize, Ordering::Relaxed}; + static COUNTER: AtomicUsize = AtomicUsize::new(0); + struct Bomb; + + impl Default for Bomb { + fn default() -> Bomb { + if COUNTER.load(Relaxed) == 3 { + panic!("bomb limit exceeded"); + } + COUNTER.fetch_add(1, Relaxed); + + Bomb + } + } + + impl Drop for Bomb { + fn drop(&mut self) { + COUNTER.fetch_sub(1, Relaxed); + } + } + + let res = std::panic::catch_unwind(|| <[Bomb; 5]>::default()); + let panic_msg = match res { + Ok(_) => unreachable!(), + Err(p) => p.to_string() + } + assert_eq!(panic_msg, "bomb limit exceeded"); + // check that all bombs are successfully dropped + assert_eq!(COUNTER.load(Relaxed), 0); +}