From 424fb3884e707a73c03bc9bb320944726275a4f1 Mon Sep 17 00:00:00 2001 From: Vincent Esche Date: Wed, 31 Mar 2021 11:39:47 +0200 Subject: [PATCH 1/2] impl: arbitrary for arrays using newly stabilized minimal const generics --- src/arbitrary.rs | 51 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/src/arbitrary.rs b/src/arbitrary.rs index 92f893b..0631240 100644 --- a/src/arbitrary.rs +++ b/src/arbitrary.rs @@ -6,6 +6,7 @@ use std::env; use std::ffi::{CString, OsString}; use std::hash::{BuildHasher, Hash}; use std::iter::{empty, once}; +use std::mem::MaybeUninit; use std::net::{ IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6, }; @@ -248,6 +249,33 @@ impl_arb_for_tuples! { (H, 7), } +impl Arbitrary for [T; N] { + #[allow(unused_variables)] // for [T; 0] + fn arbitrary(g: &mut Gen) -> [T; N] { + let mut maybe_uninit: [MaybeUninit; N] = + unsafe { MaybeUninit::uninit().assume_init() }; + + for elem in &mut maybe_uninit[..] { + *elem = MaybeUninit::new(T::arbitrary(g)); + } + + unsafe { std::mem::transmute_copy::<_, [T; N]>(&maybe_uninit) } + } + + fn shrink(&self) -> Box> { + let cloned = self.clone(); + let iter = (0..N).flat_map(move |index| { + let cloned = cloned.clone(); + cloned[index].shrink().map(move |shr_value| { + let mut result = cloned.clone(); + result[index] = shr_value.clone(); + result + }) + }); + Box::new(iter) + } +} + impl Arbitrary for Vec { fn arbitrary(g: &mut Gen) -> Vec { let size = { @@ -1410,6 +1438,29 @@ mod test { eq(Wrapping(0i32), vec![]); } + #[test] + fn arrays() { + eq([true], vec![[false]]); + + eq([false, false], vec![]); + eq([true, false], vec![[false, false]]); + eq([true, true], vec![[false, true], [true, false]]); + + eq([false, false, false], vec![]); + eq([true, false, false], vec![[false, false, false]]); + eq( + [true, true, false], + vec![[false, true, false], [true, false, false]], + ); + + eq([false, false, false, false], vec![]); + eq([true, false, false, false], vec![[false, false, false, false]]); + eq( + [true, true, false, false], + vec![[false, true, false, false], [true, false, false, false]], + ); + } + #[test] fn vecs() { eq( From a8ef8929076200a0a68fd82996449e0d1e8a67a1 Mon Sep 17 00:00:00 2001 From: Vincent Esche Date: Thu, 25 Mar 2021 14:56:45 +0100 Subject: [PATCH 2/2] doc: add inline-comments on safety of `unsafe` use --- src/arbitrary.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/arbitrary.rs b/src/arbitrary.rs index 0631240..340eb3f 100644 --- a/src/arbitrary.rs +++ b/src/arbitrary.rs @@ -252,13 +252,29 @@ impl_arb_for_tuples! { impl Arbitrary for [T; N] { #[allow(unused_variables)] // for [T; 0] fn arbitrary(g: &mut Gen) -> [T; N] { + // The following code was adopted from the corresponding example provided for `MaybeUninit`: + // https://doc.rust-lang.org/1.50.0/core/mem/union.MaybeUninit.html#initializing-an-array-element-by-element + + // SAFETY: Create an uninitialized array of `MaybeUninit`. The `assume_init` is + // safe because the type we are claiming to have initialized here is a + // bunch of `MaybeUninit`s, which do not require initialization. let mut maybe_uninit: [MaybeUninit; N] = unsafe { MaybeUninit::uninit().assume_init() }; + // SAFETY: Dropping a `MaybeUninit` does nothing. Thus using raw pointer + // assignment instead of `ptr::write` does not cause the old + // uninitialized value to be dropped. Also if there is a panic during + // this loop, we have a memory leak, but there is no memory safety + // issue. for elem in &mut maybe_uninit[..] { *elem = MaybeUninit::new(T::arbitrary(g)); } + // SAFETY: Everything is initialized (i.e. safe). + // Transmute the array to the initialized type. + // (We need to use `transmute_copy` here as `transmute` + // has some limitations around generic types: + // https://github.com/rust-lang/rust/issues/47966) unsafe { std::mem::transmute_copy::<_, [T; N]>(&maybe_uninit) } }