Skip to content

Commit fab4409

Browse files
committed
Use const generics in impl Default for arrays
1 parent d9312a8 commit fab4409

File tree

2 files changed

+82
-23
lines changed

2 files changed

+82
-23
lines changed

src/libcore/array/mod.rs

+49-23
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,55 @@ where
385385
Ord::cmp(&&self[..], &&other[..])
386386
}
387387
}
388+
#[stable(since = "1.4.0", feature = "array_default")]
389+
impl<T: Default, const N: usize> Default for [T; N]
390+
where
391+
[T; N]: LengthAtMost32,
392+
{
393+
#[inline]
394+
fn default() -> [T; N] {
395+
use crate::mem::MaybeUninit;
396+
// invariant: first `init` items are initialized
397+
struct Wrapper<T, const N: usize> {
398+
data: MaybeUninit<[T; N]>,
399+
init: usize,
400+
}
401+
402+
impl<T, const N: usize> Drop for Wrapper<T, N> {
403+
fn drop(&mut self) {
404+
debug_assert!(self.init <= N);
405+
let ptr = self.data.as_mut_ptr() as *mut T;
406+
for i in 0..self.init {
407+
// SAFETY: we iterate over only initialized values.
408+
// Each value is dropped once.
409+
unsafe {
410+
crate::ptr::drop_in_place(ptr.add(i));
411+
}
412+
}
413+
}
414+
}
415+
416+
let mut w = Wrapper::<T, N> { data: MaybeUninit::uninit(), init: 0 };
417+
let array_pointer = w.data.as_mut_ptr() as *mut T;
418+
for i in 0..N {
419+
// FIXME: this does not work for big N.
420+
// Currently it is acceptable, because N <= 32.
421+
assert!(N <= isize::MAX as usize);
422+
// SAFETY: i < N <= isize::MAX, so add() is correct
423+
unsafe {
424+
let elem_ptr = array_pointer.add(i);
425+
elem_ptr.write(T::default());
426+
}
427+
w.init += 1;
428+
}
429+
430+
// Prevent double-read in callee and Wrepper::drop
431+
w.init = 0;
432+
433+
// SAFETY: all arraty is initialized now.
434+
unsafe { w.data.as_ptr().read() }
435+
}
436+
}
388437

389438
/// Implemented for lengths where trait impls are allowed on arrays in core/std
390439
#[rustc_on_unimplemented(message = "arrays only have std trait implementations for lengths 0..=32")]
@@ -410,26 +459,3 @@ array_impls! {
410459
20 21 22 23 24 25 26 27 28 29
411460
30 31 32
412461
}
413-
414-
// The Default impls cannot be generated using the array_impls! macro because
415-
// they require array literals.
416-
417-
macro_rules! array_impl_default {
418-
{$n:expr, $t:ident $($ts:ident)*} => {
419-
#[stable(since = "1.4.0", feature = "array_default")]
420-
impl<T> Default for [T; $n] where T: Default {
421-
fn default() -> [T; $n] {
422-
[$t::default(), $($ts::default()),*]
423-
}
424-
}
425-
array_impl_default!{($n - 1), $($ts)*}
426-
};
427-
{$n:expr,} => {
428-
#[stable(since = "1.4.0", feature = "array_default")]
429-
impl<T> Default for [T; $n] {
430-
fn default() -> [T; $n] { [] }
431-
}
432-
};
433-
}
434-
435-
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}

src/libcore/tests/array.rs

+33
Original file line numberDiff line numberDiff line change
@@ -241,3 +241,36 @@ fn iterator_drops() {
241241
}
242242
assert_eq!(i.get(), 5);
243243
}
244+
245+
#[test]
246+
fn array_default_impl_avoids_leaks_on_panic() {
247+
use core::sync::atomic::{AtomicUsize, Ordering::Relaxed};
248+
static COUNTER: AtomicUsize = AtomicUsize::new(0);
249+
struct Bomb;
250+
251+
impl Default for Bomb {
252+
fn default() -> Bomb {
253+
if COUNTER.load(Relaxed) == 3 {
254+
panic!("bomb limit exceeded");
255+
}
256+
COUNTER.fetch_add(1, Relaxed);
257+
258+
Bomb
259+
}
260+
}
261+
262+
impl Drop for Bomb {
263+
fn drop(&mut self) {
264+
COUNTER.fetch_sub(1, Relaxed);
265+
}
266+
}
267+
268+
let res = std::panic::catch_unwind(|| <[Bomb; 5]>::default());
269+
let panic_msg = match res {
270+
Ok(_) => unreachable!(),
271+
Err(p) => p.to_string()
272+
}
273+
assert_eq!(panic_msg, "bomb limit exceeded");
274+
// check that all bombs are successfully dropped
275+
assert_eq!(COUNTER.load(Relaxed), 0);
276+
}

0 commit comments

Comments
 (0)