From 2dd9d6194d16cc8c8fd0137ea1372437fae58bc0 Mon Sep 17 00:00:00 2001 From: Conrad Ludgate Date: Sat, 15 Jan 2022 19:16:38 +0000 Subject: [PATCH 01/17] initial array binops --- library/core/src/array/binops.rs | 97 ++++++++++++++++++++++++++++++++ library/core/src/array/mod.rs | 1 + library/core/tests/array.rs | 18 ++++++ 3 files changed, 116 insertions(+) create mode 100644 library/core/src/array/binops.rs diff --git a/library/core/src/array/binops.rs b/library/core/src/array/binops.rs new file mode 100644 index 0000000000000..8e1933b3240bf --- /dev/null +++ b/library/core/src/array/binops.rs @@ -0,0 +1,97 @@ +use core::ops::{Add, AddAssign, Mul, MulAssign, Div, DivAssign, Sub, SubAssign}; +use core::mem::{MaybeUninit, ManuallyDrop}; +use core::ptr::{read, drop_in_place}; + +fn md_array(a: [T; N]) -> [ManuallyDrop; N] { + // SAFETY: + // This is safe since `ManuallyDrop` guarantees the same layout as `T`. + unsafe { read(&a as *const [T; N] as *const [ManuallyDrop; N]) } +} + +macro_rules! binop { + ($trait:ident, $method:ident) => { + #[stable(feature = "array_bin_ops", since = "1.59.0")] + impl $trait<[U; N]> for [T; N] where T: $trait { + type Output = [T::Output; N]; + + fn $method(self, rhs: [U; N]) -> Self::Output { + /// Drop checked to handle dropping the values properly in case of a panic + struct DropCheck, U, const N: usize> { + lhs: [ManuallyDrop; N], + rhs: [ManuallyDrop; N], + output: [MaybeUninit; N], + i: usize, + } + impl, U, const N: usize> Drop for DropCheck { + fn drop(&mut self) { + let i = self.i; + // SAFETY: + // `i` defines how many elements are valid at this point. + // The only possible panic point is the element-wise `$method` op, + // so it means that `i+1..` elements are currently live in lhs/rhs + // and `..i` elements are live in the output. + unsafe { + drop_in_place((&mut self.lhs[i+1..]) as *mut [_] as *mut [T]); + drop_in_place((&mut self.rhs[i+1..]) as *mut [_] as *mut [U]); + drop_in_place(&mut self.output[..i] as *mut [_] as *mut [T::Output]); + } + } + } + let mut dc = DropCheck { + lhs: md_array(self), + rhs: md_array(rhs), + output: MaybeUninit::uninit_array(), + i: 0, + }; + + while dc.i < N { + // SAFETY: + // Since `dc.i` is stricty-monotonic, we will only + // take each element only once from each of lhs/rhs + unsafe { + let lhs = ManuallyDrop::take(&mut dc.lhs[dc.i]); + let rhs = ManuallyDrop::take(&mut dc.rhs[dc.i]); + dc.output[dc.i].write(T::$method(lhs, rhs)); + dc.i += 1; + } + } + + // SAFETY: + // By this point, we are certain we have initialised all N elements + let output = unsafe { read(&dc.output as *const [_; N] as *const [T::Output; N]) }; + + // No more panics can occur after this point, so we 'forget' + // the dc value to skip dropping the contents + core::mem::forget(dc); + + output + } + } + } +} + +macro_rules! binop_assign { + ($trait:ident, $method:ident) => { + #[stable(feature = "array_bin_ops", since = "1.59.0")] + impl $trait<[U; N]> for [T; N] where T: $trait { + fn $method(&mut self, rhs: [U; N]) { + let mut rhs = IntoIterator::into_iter(rhs); + for i in 0..N { + // SAFETY: + // Since this is a constant size array, we know there's spare elements + self[i].$method(unsafe { rhs.next().unwrap_unchecked() }) + } + } + } + } +} + +binop!(Add, add); +binop!(Mul, mul); +binop!(Div, div); +binop!(Sub, sub); + +binop_assign!(AddAssign, add_assign); +binop_assign!(MulAssign, mul_assign); +binop_assign!(DivAssign, div_assign); +binop_assign!(SubAssign, sub_assign); diff --git a/library/core/src/array/mod.rs b/library/core/src/array/mod.rs index ee79021ed536e..705e6281e45d1 100644 --- a/library/core/src/array/mod.rs +++ b/library/core/src/array/mod.rs @@ -18,6 +18,7 @@ use crate::slice::{Iter, IterMut}; mod equality; mod iter; +mod binops; #[stable(feature = "array_value_iter", since = "1.51.0")] pub use iter::IntoIter; diff --git a/library/core/tests/array.rs b/library/core/tests/array.rs index a778779c0fd88..b34c01a3d0b9a 100644 --- a/library/core/tests/array.rs +++ b/library/core/tests/array.rs @@ -668,3 +668,21 @@ fn array_mixed_equality_nans() { assert!(!(mut3 == array3)); assert!(mut3 != array3); } + +#[test] +fn add() { + let a: [i32; 4] = [0, 1, 2, 3]; + let b: [i32; 4] = [4, 5, 6, 7]; + + assert_eq!(a + b, [4, 6, 8, 10]); +} + +#[test] +fn add_assign() { + let mut a: [i32; 4] = [0, 1, 2, 3]; + let b: [i32; 4] = [4, 5, 6, 7]; + + a += b; + + assert_eq!(a, [4, 6, 8, 10]); +} From 4c4ce71467bc9fe60a051db1f22bd2dc48f9dfd9 Mon Sep 17 00:00:00 2001 From: Conrad Ludgate Date: Sat, 15 Jan 2022 20:30:01 +0000 Subject: [PATCH 02/17] update op_assign --- library/core/src/array/binops.rs | 176 +++++++++++++++++++++---------- library/core/src/array/mod.rs | 2 +- 2 files changed, 123 insertions(+), 55 deletions(-) diff --git a/library/core/src/array/binops.rs b/library/core/src/array/binops.rs index 8e1933b3240bf..a5f7c56e055f6 100644 --- a/library/core/src/array/binops.rs +++ b/library/core/src/array/binops.rs @@ -1,6 +1,6 @@ -use core::ops::{Add, AddAssign, Mul, MulAssign, Div, DivAssign, Sub, SubAssign}; -use core::mem::{MaybeUninit, ManuallyDrop}; -use core::ptr::{read, drop_in_place}; +use core::mem::{ManuallyDrop, MaybeUninit}; +use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign}; +use core::ptr::{drop_in_place, read}; fn md_array(a: [T; N]) -> [ManuallyDrop; N] { // SAFETY: @@ -8,64 +8,128 @@ fn md_array(a: [T; N]) -> [ManuallyDrop; N] { unsafe { read(&a as *const [T; N] as *const [ManuallyDrop; N]) } } +/// Drop checked to handle dropping the values properly in case of a panic +struct DropCheck { + lhs: [ManuallyDrop; N], + rhs: [ManuallyDrop; N], + output: [MaybeUninit; N], + i: usize, +} + +impl Drop for DropCheck { + fn drop(&mut self) { + let i = self.i; + // SAFETY: + // `i` defines how many elements are valid at this point. + // The only possible panic point is the element-wise `$method` op, + // so it means that `i+1..` elements are currently live in lhs/rhs + // and `..i` elements are live in the output. + unsafe { + drop_in_place((&mut self.lhs[i + 1..]) as *mut [_] as *mut [T]); + drop_in_place((&mut self.rhs[i + 1..]) as *mut [_] as *mut [U]); + drop_in_place(&mut self.output[..i] as *mut [_] as *mut [O]); + } + } +} + +impl DropCheck { + fn new(lhs: [T; N], rhs: [U; N]) -> Self { + Self { + lhs: md_array(lhs), + rhs: md_array(rhs), + output: MaybeUninit::uninit_array(), + i: 0, + } + } + + /// # Safety + /// All values of output must be initialised, and all values in the inputs must be consumed + unsafe fn output(self) -> [O; N] { + let md = ManuallyDrop::new(self); + // SAFETY: + // Since it's wrapped in a ManuallyDrop, the contents will not be dropped/accessed again + // so it's safe to perform the copy + unsafe { read(&md.output as *const [MaybeUninit; N] as *const [O; N]) } + } + + /// # Safety + /// Must be called no more than `N` times. + unsafe fn inc(&mut self, f: impl FnOnce(T, U) -> O) { + // SAFETY: + // Since `dc.i` is stricty-monotonic, we will only + // take each element only once from each of lhs/rhs + unsafe { + let lhs = ManuallyDrop::take(&mut self.lhs[self.i]); + let rhs = ManuallyDrop::take(&mut self.rhs[self.i]); + self.output[self.i].write(f(lhs, rhs)); + self.i += 1; + } + } +} + macro_rules! binop { ($trait:ident, $method:ident) => { #[stable(feature = "array_bin_ops", since = "1.59.0")] - impl $trait<[U; N]> for [T; N] where T: $trait { + impl $trait<[U; N]> for [T; N] + where + T: $trait, + { type Output = [T::Output; N]; fn $method(self, rhs: [U; N]) -> Self::Output { - /// Drop checked to handle dropping the values properly in case of a panic - struct DropCheck, U, const N: usize> { - lhs: [ManuallyDrop; N], - rhs: [ManuallyDrop; N], - output: [MaybeUninit; N], - i: usize, - } - impl, U, const N: usize> Drop for DropCheck { - fn drop(&mut self) { - let i = self.i; - // SAFETY: - // `i` defines how many elements are valid at this point. - // The only possible panic point is the element-wise `$method` op, - // so it means that `i+1..` elements are currently live in lhs/rhs - // and `..i` elements are live in the output. - unsafe { - drop_in_place((&mut self.lhs[i+1..]) as *mut [_] as *mut [T]); - drop_in_place((&mut self.rhs[i+1..]) as *mut [_] as *mut [U]); - drop_in_place(&mut self.output[..i] as *mut [_] as *mut [T::Output]); - } - } - } - let mut dc = DropCheck { - lhs: md_array(self), - rhs: md_array(rhs), - output: MaybeUninit::uninit_array(), - i: 0, - }; - - while dc.i < N { + let mut dc = DropCheck::new(self, rhs); + + for _ in 0..N { // SAFETY: - // Since `dc.i` is stricty-monotonic, we will only - // take each element only once from each of lhs/rhs - unsafe { - let lhs = ManuallyDrop::take(&mut dc.lhs[dc.i]); - let rhs = ManuallyDrop::take(&mut dc.rhs[dc.i]); - dc.output[dc.i].write(T::$method(lhs, rhs)); - dc.i += 1; - } + // Will only be called a maximum of N times + unsafe { dc.inc(T::$method) } } // SAFETY: // By this point, we are certain we have initialised all N elements - let output = unsafe { read(&dc.output as *const [_; N] as *const [T::Output; N]) }; + unsafe { dc.output() } + } + } + }; +} - // No more panics can occur after this point, so we 'forget' - // the dc value to skip dropping the contents - core::mem::forget(dc); +/// Drop checked to handle dropping the values properly in case of a panic +struct DropCheckAssign { + rhs: [ManuallyDrop; N], + i: usize, +} - output - } +impl Drop for DropCheckAssign { + fn drop(&mut self) { + let i = self.i; + // SAFETY: + // `i` defines how many elements are valid at this point. + // The only possible panic point is the element-wise `$method` op, + // so it means that `i+1..` elements are currently live in rhs + unsafe { + drop_in_place((&mut self.rhs[i + 1..]) as *mut [_] as *mut [U]); + } + } +} + +impl DropCheckAssign { + fn new(rhs: [U; N]) -> Self { + Self { + rhs: md_array(rhs), + i: 0, + } + } + + /// # Safety + /// Must be called no more than `N` times. + unsafe fn inc(&mut self, lhs: &mut T, f: impl FnOnce(&mut T, U)) { + // SAFETY: + // Since `dc.i` is stricty-monotonic, we will only + // take each element only once from each of lhs/rhs + unsafe { + let rhs = ManuallyDrop::take(&mut self.rhs[self.i]); + f(lhs, rhs); + self.i += 1; } } } @@ -73,17 +137,21 @@ macro_rules! binop { macro_rules! binop_assign { ($trait:ident, $method:ident) => { #[stable(feature = "array_bin_ops", since = "1.59.0")] - impl $trait<[U; N]> for [T; N] where T: $trait { + impl $trait<[U; N]> for [T; N] + where + T: $trait, + { fn $method(&mut self, rhs: [U; N]) { - let mut rhs = IntoIterator::into_iter(rhs); - for i in 0..N { + let mut dc = DropCheckAssign::new(rhs); + + for _ in 0..N { // SAFETY: - // Since this is a constant size array, we know there's spare elements - self[i].$method(unsafe { rhs.next().unwrap_unchecked() }) + // Will only be called a maximum of N times + unsafe { dc.inc(&mut self[dc.i], T::$method) } } } } - } + }; } binop!(Add, add); diff --git a/library/core/src/array/mod.rs b/library/core/src/array/mod.rs index 705e6281e45d1..701ed9ce5a727 100644 --- a/library/core/src/array/mod.rs +++ b/library/core/src/array/mod.rs @@ -16,9 +16,9 @@ use crate::ops::{ }; use crate::slice::{Iter, IterMut}; +mod binops; mod equality; mod iter; -mod binops; #[stable(feature = "array_value_iter", since = "1.51.0")] pub use iter::IntoIter; From d74f1c7cf3ac23c66074735999690663776457ba Mon Sep 17 00:00:00 2001 From: Conrad Ludgate Date: Sat, 15 Jan 2022 20:46:35 +0000 Subject: [PATCH 03/17] code reuse --- library/core/src/array/binops.rs | 41 ++++++++++++++------------------ 1 file changed, 18 insertions(+), 23 deletions(-) diff --git a/library/core/src/array/binops.rs b/library/core/src/array/binops.rs index a5f7c56e055f6..ee495e78a2d9a 100644 --- a/library/core/src/array/binops.rs +++ b/library/core/src/array/binops.rs @@ -11,23 +11,21 @@ fn md_array(a: [T; N]) -> [ManuallyDrop; N] { /// Drop checked to handle dropping the values properly in case of a panic struct DropCheck { lhs: [ManuallyDrop; N], - rhs: [ManuallyDrop; N], + assign: DropCheckAssign, output: [MaybeUninit; N], - i: usize, } impl Drop for DropCheck { fn drop(&mut self) { - let i = self.i; + let i = self.assign.i; // SAFETY: // `i` defines how many elements are valid at this point. // The only possible panic point is the element-wise `$method` op, - // so it means that `i+1..` elements are currently live in lhs/rhs + // so it means that `i+1..` elements are currently live in lhs // and `..i` elements are live in the output. unsafe { - drop_in_place((&mut self.lhs[i + 1..]) as *mut [_] as *mut [T]); - drop_in_place((&mut self.rhs[i + 1..]) as *mut [_] as *mut [U]); - drop_in_place(&mut self.output[..i] as *mut [_] as *mut [O]); + drop_in_place((&mut self.lhs[i..]) as *mut [_] as *mut [T]); + drop_in_place(&mut self.output[..i - 1] as *mut [_] as *mut [O]); } } } @@ -36,9 +34,8 @@ impl DropCheck { fn new(lhs: [T; N], rhs: [U; N]) -> Self { Self { lhs: md_array(lhs), - rhs: md_array(rhs), + assign: DropCheckAssign::new(rhs), output: MaybeUninit::uninit_array(), - i: 0, } } @@ -54,15 +51,15 @@ impl DropCheck { /// # Safety /// Must be called no more than `N` times. - unsafe fn inc(&mut self, f: impl FnOnce(T, U) -> O) { + #[inline(always)] + unsafe fn step(&mut self, f: impl FnOnce(T, U) -> O) { // SAFETY: // Since `dc.i` is stricty-monotonic, we will only // take each element only once from each of lhs/rhs unsafe { - let lhs = ManuallyDrop::take(&mut self.lhs[self.i]); - let rhs = ManuallyDrop::take(&mut self.rhs[self.i]); - self.output[self.i].write(f(lhs, rhs)); - self.i += 1; + let lhs = ManuallyDrop::take(&mut self.lhs[self.assign.i]); + let out = &mut self.output[self.assign.i]; + out.write(f(lhs, self.assign.next_unchecked())); } } } @@ -82,7 +79,7 @@ macro_rules! binop { for _ in 0..N { // SAFETY: // Will only be called a maximum of N times - unsafe { dc.inc(T::$method) } + unsafe { dc.step(T::$method) } } // SAFETY: @@ -107,29 +104,27 @@ impl Drop for DropCheckAssign { // The only possible panic point is the element-wise `$method` op, // so it means that `i+1..` elements are currently live in rhs unsafe { - drop_in_place((&mut self.rhs[i + 1..]) as *mut [_] as *mut [U]); + drop_in_place((&mut self.rhs[i..]) as *mut [_] as *mut [U]); } } } impl DropCheckAssign { fn new(rhs: [U; N]) -> Self { - Self { - rhs: md_array(rhs), - i: 0, - } + Self { rhs: md_array(rhs), i: 0 } } /// # Safety /// Must be called no more than `N` times. - unsafe fn inc(&mut self, lhs: &mut T, f: impl FnOnce(&mut T, U)) { + #[inline(always)] + unsafe fn next_unchecked(&mut self) -> U { // SAFETY: // Since `dc.i` is stricty-monotonic, we will only // take each element only once from each of lhs/rhs unsafe { let rhs = ManuallyDrop::take(&mut self.rhs[self.i]); - f(lhs, rhs); self.i += 1; + rhs } } } @@ -147,7 +142,7 @@ macro_rules! binop_assign { for _ in 0..N { // SAFETY: // Will only be called a maximum of N times - unsafe { dc.inc(&mut self[dc.i], T::$method) } + unsafe { self[dc.i].$method(dc.next_unchecked()) } } } } From 8c880953a47ceac34a4454769d2ceb20b839f96f Mon Sep 17 00:00:00 2001 From: Conrad Ludgate Date: Sat, 15 Jan 2022 20:55:42 +0000 Subject: [PATCH 04/17] remove bounds checks --- library/core/src/array/binops.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/library/core/src/array/binops.rs b/library/core/src/array/binops.rs index ee495e78a2d9a..29b2f9109dad3 100644 --- a/library/core/src/array/binops.rs +++ b/library/core/src/array/binops.rs @@ -57,8 +57,8 @@ impl DropCheck { // Since `dc.i` is stricty-monotonic, we will only // take each element only once from each of lhs/rhs unsafe { - let lhs = ManuallyDrop::take(&mut self.lhs[self.assign.i]); - let out = &mut self.output[self.assign.i]; + let lhs = ManuallyDrop::take(self.lhs.get_unchecked_mut(self.assign.i)); + let out = self.output.get_unchecked_mut(self.assign.i); out.write(f(lhs, self.assign.next_unchecked())); } } @@ -122,7 +122,7 @@ impl DropCheckAssign { // Since `dc.i` is stricty-monotonic, we will only // take each element only once from each of lhs/rhs unsafe { - let rhs = ManuallyDrop::take(&mut self.rhs[self.i]); + let rhs = ManuallyDrop::take(self.rhs.get_unchecked_mut(self.i)); self.i += 1; rhs } @@ -142,7 +142,7 @@ macro_rules! binop_assign { for _ in 0..N { // SAFETY: // Will only be called a maximum of N times - unsafe { self[dc.i].$method(dc.next_unchecked()) } + unsafe { self.get_unchecked_mut(dc.i).$method(dc.next_unchecked()) } } } } From b079002f92290180f00b3c4ba48f12663791c627 Mon Sep 17 00:00:00 2001 From: Conrad Ludgate Date: Sat, 15 Jan 2022 21:17:00 +0000 Subject: [PATCH 05/17] shuffle --- library/core/src/array/binops.rs | 173 +++++++++++++++++-------------- 1 file changed, 93 insertions(+), 80 deletions(-) diff --git a/library/core/src/array/binops.rs b/library/core/src/array/binops.rs index 29b2f9109dad3..7b2b0d3eaf7b4 100644 --- a/library/core/src/array/binops.rs +++ b/library/core/src/array/binops.rs @@ -2,27 +2,78 @@ use core::mem::{ManuallyDrop, MaybeUninit}; use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign}; use core::ptr::{drop_in_place, read}; -fn md_array(a: [T; N]) -> [ManuallyDrop; N] { - // SAFETY: - // This is safe since `ManuallyDrop` guarantees the same layout as `T`. - unsafe { read(&a as *const [T; N] as *const [ManuallyDrop; N]) } + +macro_rules! binop { + ($trait:ident, $method:ident) => { + #[stable(feature = "array_bin_ops", since = "1.59.0")] + impl $trait<[U; N]> for [T; N] + where + T: $trait, + { + type Output = [T::Output; N]; + + fn $method(self, rhs: [U; N]) -> Self::Output { + let mut dc = Iter3::new(self, rhs); + + for _ in 0..N { + // SAFETY: + // Will only be called a maximum of N times + unsafe { dc.step(T::$method) } + } + + // SAFETY: + // By this point, we are certain we have initialised all N elements + unsafe { dc.output() } + } + } + }; } -/// Drop checked to handle dropping the values properly in case of a panic -struct DropCheck { +macro_rules! binop_assign { + ($trait:ident, $method:ident) => { + #[stable(feature = "array_bin_ops", since = "1.59.0")] + impl $trait<[U; N]> for [T; N] + where + T: $trait, + { + fn $method(&mut self, rhs: [U; N]) { + let mut dc = Iter::new(rhs); + + for _ in 0..N { + // SAFETY: + // Will only be called a maximum of N times + unsafe { self.get_unchecked_mut(dc.i).$method(dc.next_unchecked()) } + } + } + } + }; +} + +binop!(Add, add); +binop!(Mul, mul); +binop!(Div, div); +binop!(Sub, sub); + +binop_assign!(AddAssign, add_assign); +binop_assign!(MulAssign, mul_assign); +binop_assign!(DivAssign, div_assign); +binop_assign!(SubAssign, sub_assign); + +/// Like [`Iter`], but traverses 3 arrays at once +struct Iter3 { + rhs: Iter, lhs: [ManuallyDrop; N], - assign: DropCheckAssign, output: [MaybeUninit; N], } -impl Drop for DropCheck { +impl Drop for Iter3 { fn drop(&mut self) { - let i = self.assign.i; + let i = self.rhs.i; // SAFETY: // `i` defines how many elements are valid at this point. - // The only possible panic point is the element-wise `$method` op, - // so it means that `i+1..` elements are currently live in lhs - // and `..i` elements are live in the output. + // The only possible panic point is in the `f` passed to `step`, + // so it means that `i..` elements are currently live in lhs + // and `..i-1` elements are live in the output. unsafe { drop_in_place((&mut self.lhs[i..]) as *mut [_] as *mut [T]); drop_in_place(&mut self.output[..i - 1] as *mut [_] as *mut [O]); @@ -30,11 +81,11 @@ impl Drop for DropCheck { } } -impl DropCheck { +impl Iter3 { fn new(lhs: [T; N], rhs: [U; N]) -> Self { Self { + rhs: Iter::new(rhs), lhs: md_array(lhs), - assign: DropCheckAssign::new(rhs), output: MaybeUninit::uninit_array(), } } @@ -42,6 +93,8 @@ impl DropCheck { /// # Safety /// All values of output must be initialised, and all values in the inputs must be consumed unsafe fn output(self) -> [O; N] { + debug_assert_eq!(self.rhs.i, N); + let md = ManuallyDrop::new(self); // SAFETY: // Since it's wrapped in a ManuallyDrop, the contents will not be dropped/accessed again @@ -57,46 +110,23 @@ impl DropCheck { // Since `dc.i` is stricty-monotonic, we will only // take each element only once from each of lhs/rhs unsafe { - let lhs = ManuallyDrop::take(self.lhs.get_unchecked_mut(self.assign.i)); - let out = self.output.get_unchecked_mut(self.assign.i); - out.write(f(lhs, self.assign.next_unchecked())); + let i = self.rhs.i; + let rhs = self.rhs.next_unchecked(); + let lhs = ManuallyDrop::take(self.lhs.get_unchecked_mut(i)); + let out = self.output.get_unchecked_mut(i); + out.write(f(lhs, rhs)); } } } -macro_rules! binop { - ($trait:ident, $method:ident) => { - #[stable(feature = "array_bin_ops", since = "1.59.0")] - impl $trait<[U; N]> for [T; N] - where - T: $trait, - { - type Output = [T::Output; N]; - - fn $method(self, rhs: [U; N]) -> Self::Output { - let mut dc = DropCheck::new(self, rhs); - - for _ in 0..N { - // SAFETY: - // Will only be called a maximum of N times - unsafe { dc.step(T::$method) } - } - - // SAFETY: - // By this point, we are certain we have initialised all N elements - unsafe { dc.output() } - } - } - }; -} - -/// Drop checked to handle dropping the values properly in case of a panic -struct DropCheckAssign { +/// For sake of optimisation, it's a simplified version of [`array::IntoIter`] +/// that can only go forward, and can only be accessed through unsafe (to avoid bounds checks) +struct Iter { rhs: [ManuallyDrop; N], i: usize, } -impl Drop for DropCheckAssign { +impl Drop for Iter { fn drop(&mut self) { let i = self.i; // SAFETY: @@ -109,7 +139,7 @@ impl Drop for DropCheckAssign { } } -impl DropCheckAssign { +impl Iter { fn new(rhs: [U; N]) -> Self { Self { rhs: md_array(rhs), i: 0 } } @@ -118,43 +148,26 @@ impl DropCheckAssign { /// Must be called no more than `N` times. #[inline(always)] unsafe fn next_unchecked(&mut self) -> U { + debug_assert!(self.i < N); + + // SAFETY: + // Caller ensures that next is not called more than `N` times, so self.i must be + // smaller than N at this point + let rhs = unsafe { self.rhs.get_unchecked_mut(self.i) }; + // SAFETY: // Since `dc.i` is stricty-monotonic, we will only // take each element only once from each of lhs/rhs - unsafe { - let rhs = ManuallyDrop::take(self.rhs.get_unchecked_mut(self.i)); - self.i += 1; - rhs - } + let rhs = unsafe { ManuallyDrop::take(rhs) }; + + self.i += 1; + rhs } } -macro_rules! binop_assign { - ($trait:ident, $method:ident) => { - #[stable(feature = "array_bin_ops", since = "1.59.0")] - impl $trait<[U; N]> for [T; N] - where - T: $trait, - { - fn $method(&mut self, rhs: [U; N]) { - let mut dc = DropCheckAssign::new(rhs); - - for _ in 0..N { - // SAFETY: - // Will only be called a maximum of N times - unsafe { self.get_unchecked_mut(dc.i).$method(dc.next_unchecked()) } - } - } - } - }; +/// Create a new `[ManuallyDrop; N]` from the initialised array +fn md_array(a: [T; N]) -> [ManuallyDrop; N] { + // SAFETY: + // This is safe since `ManuallyDrop` guarantees the same layout as `T`. + unsafe { read(&a as *const [T; N] as *const [ManuallyDrop; N]) } } - -binop!(Add, add); -binop!(Mul, mul); -binop!(Div, div); -binop!(Sub, sub); - -binop_assign!(AddAssign, add_assign); -binop_assign!(MulAssign, mul_assign); -binop_assign!(DivAssign, div_assign); -binop_assign!(SubAssign, sub_assign); From e11486e3855dece098c81b03c4851156e5ff26ce Mon Sep 17 00:00:00 2001 From: Conrad Ludgate Date: Sat, 15 Jan 2022 21:21:51 +0000 Subject: [PATCH 06/17] update safety docs --- library/core/src/array/binops.rs | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/library/core/src/array/binops.rs b/library/core/src/array/binops.rs index 7b2b0d3eaf7b4..2e409d6e3337e 100644 --- a/library/core/src/array/binops.rs +++ b/library/core/src/array/binops.rs @@ -2,7 +2,6 @@ use core::mem::{ManuallyDrop, MaybeUninit}; use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign}; use core::ptr::{drop_in_place, read}; - macro_rules! binop { ($trait:ident, $method:ident) => { #[stable(feature = "array_bin_ops", since = "1.59.0")] @@ -70,10 +69,9 @@ impl Drop for Iter3 { fn drop(&mut self) { let i = self.rhs.i; // SAFETY: - // `i` defines how many elements are valid at this point. - // The only possible panic point is in the `f` passed to `step`, - // so it means that `i..` elements are currently live in lhs - // and `..i-1` elements are live in the output. + // `i` defines how many elements have been processed from the arrays. + // Caveat, the only potential panic would happen *before* the wrote to the output, + // so the `i`th output is not initialised as one would assume. unsafe { drop_in_place((&mut self.lhs[i..]) as *mut [_] as *mut [T]); drop_in_place(&mut self.output[..i - 1] as *mut [_] as *mut [O]); @@ -83,11 +81,7 @@ impl Drop for Iter3 { impl Iter3 { fn new(lhs: [T; N], rhs: [U; N]) -> Self { - Self { - rhs: Iter::new(rhs), - lhs: md_array(lhs), - output: MaybeUninit::uninit_array(), - } + Self { rhs: Iter::new(rhs), lhs: md_array(lhs), output: MaybeUninit::uninit_array() } } /// # Safety @@ -130,9 +124,8 @@ impl Drop for Iter { fn drop(&mut self) { let i = self.i; // SAFETY: - // `i` defines how many elements are valid at this point. - // The only possible panic point is the element-wise `$method` op, - // so it means that `i+1..` elements are currently live in rhs + // `i` defines how many elements have been processed from the array, + // meaning that theres `i..` elements left to process (and therefore, drop) unsafe { drop_in_place((&mut self.rhs[i..]) as *mut [_] as *mut [U]); } From 3b07dbdc00663918807b1ef101a48baaf4415d97 Mon Sep 17 00:00:00 2001 From: Conrad Ludgate Date: Sun, 16 Jan 2022 10:39:59 +0000 Subject: [PATCH 07/17] impl array bin ops --- library/core/src/array/binops.rs | 136 ++--------------------- library/core/src/array/binops/imp.rs | 158 +++++++++++++++++++++++++++ library/core/tests/array.rs | 23 ++++ 3 files changed, 188 insertions(+), 129 deletions(-) create mode 100644 library/core/src/array/binops/imp.rs diff --git a/library/core/src/array/binops.rs b/library/core/src/array/binops.rs index 2e409d6e3337e..1cba6141c1831 100644 --- a/library/core/src/array/binops.rs +++ b/library/core/src/array/binops.rs @@ -1,10 +1,11 @@ -use core::mem::{ManuallyDrop, MaybeUninit}; use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign}; -use core::ptr::{drop_in_place, read}; + +mod imp; +use imp::{binop_assign_impl, binop_impl}; macro_rules! binop { ($trait:ident, $method:ident) => { - #[stable(feature = "array_bin_ops", since = "1.59.0")] + #[stable(feature = "array_bin_ops", since = "1.60.0")] impl $trait<[U; N]> for [T; N] where T: $trait, @@ -12,17 +13,7 @@ macro_rules! binop { type Output = [T::Output; N]; fn $method(self, rhs: [U; N]) -> Self::Output { - let mut dc = Iter3::new(self, rhs); - - for _ in 0..N { - // SAFETY: - // Will only be called a maximum of N times - unsafe { dc.step(T::$method) } - } - - // SAFETY: - // By this point, we are certain we have initialised all N elements - unsafe { dc.output() } + binop_impl(self, rhs, T::$method) } } }; @@ -30,19 +21,13 @@ macro_rules! binop { macro_rules! binop_assign { ($trait:ident, $method:ident) => { - #[stable(feature = "array_bin_ops", since = "1.59.0")] + #[stable(feature = "array_bin_ops", since = "1.60.0")] impl $trait<[U; N]> for [T; N] where T: $trait, { fn $method(&mut self, rhs: [U; N]) { - let mut dc = Iter::new(rhs); - - for _ in 0..N { - // SAFETY: - // Will only be called a maximum of N times - unsafe { self.get_unchecked_mut(dc.i).$method(dc.next_unchecked()) } - } + binop_assign_impl(self, rhs, T::$method) } } }; @@ -57,110 +42,3 @@ binop_assign!(AddAssign, add_assign); binop_assign!(MulAssign, mul_assign); binop_assign!(DivAssign, div_assign); binop_assign!(SubAssign, sub_assign); - -/// Like [`Iter`], but traverses 3 arrays at once -struct Iter3 { - rhs: Iter, - lhs: [ManuallyDrop; N], - output: [MaybeUninit; N], -} - -impl Drop for Iter3 { - fn drop(&mut self) { - let i = self.rhs.i; - // SAFETY: - // `i` defines how many elements have been processed from the arrays. - // Caveat, the only potential panic would happen *before* the wrote to the output, - // so the `i`th output is not initialised as one would assume. - unsafe { - drop_in_place((&mut self.lhs[i..]) as *mut [_] as *mut [T]); - drop_in_place(&mut self.output[..i - 1] as *mut [_] as *mut [O]); - } - } -} - -impl Iter3 { - fn new(lhs: [T; N], rhs: [U; N]) -> Self { - Self { rhs: Iter::new(rhs), lhs: md_array(lhs), output: MaybeUninit::uninit_array() } - } - - /// # Safety - /// All values of output must be initialised, and all values in the inputs must be consumed - unsafe fn output(self) -> [O; N] { - debug_assert_eq!(self.rhs.i, N); - - let md = ManuallyDrop::new(self); - // SAFETY: - // Since it's wrapped in a ManuallyDrop, the contents will not be dropped/accessed again - // so it's safe to perform the copy - unsafe { read(&md.output as *const [MaybeUninit; N] as *const [O; N]) } - } - - /// # Safety - /// Must be called no more than `N` times. - #[inline(always)] - unsafe fn step(&mut self, f: impl FnOnce(T, U) -> O) { - // SAFETY: - // Since `dc.i` is stricty-monotonic, we will only - // take each element only once from each of lhs/rhs - unsafe { - let i = self.rhs.i; - let rhs = self.rhs.next_unchecked(); - let lhs = ManuallyDrop::take(self.lhs.get_unchecked_mut(i)); - let out = self.output.get_unchecked_mut(i); - out.write(f(lhs, rhs)); - } - } -} - -/// For sake of optimisation, it's a simplified version of [`array::IntoIter`] -/// that can only go forward, and can only be accessed through unsafe (to avoid bounds checks) -struct Iter { - rhs: [ManuallyDrop; N], - i: usize, -} - -impl Drop for Iter { - fn drop(&mut self) { - let i = self.i; - // SAFETY: - // `i` defines how many elements have been processed from the array, - // meaning that theres `i..` elements left to process (and therefore, drop) - unsafe { - drop_in_place((&mut self.rhs[i..]) as *mut [_] as *mut [U]); - } - } -} - -impl Iter { - fn new(rhs: [U; N]) -> Self { - Self { rhs: md_array(rhs), i: 0 } - } - - /// # Safety - /// Must be called no more than `N` times. - #[inline(always)] - unsafe fn next_unchecked(&mut self) -> U { - debug_assert!(self.i < N); - - // SAFETY: - // Caller ensures that next is not called more than `N` times, so self.i must be - // smaller than N at this point - let rhs = unsafe { self.rhs.get_unchecked_mut(self.i) }; - - // SAFETY: - // Since `dc.i` is stricty-monotonic, we will only - // take each element only once from each of lhs/rhs - let rhs = unsafe { ManuallyDrop::take(rhs) }; - - self.i += 1; - rhs - } -} - -/// Create a new `[ManuallyDrop; N]` from the initialised array -fn md_array(a: [T; N]) -> [ManuallyDrop; N] { - // SAFETY: - // This is safe since `ManuallyDrop` guarantees the same layout as `T`. - unsafe { read(&a as *const [T; N] as *const [ManuallyDrop; N]) } -} diff --git a/library/core/src/array/binops/imp.rs b/library/core/src/array/binops/imp.rs new file mode 100644 index 0000000000000..11f818282ebfb --- /dev/null +++ b/library/core/src/array/binops/imp.rs @@ -0,0 +1,158 @@ +use core::mem::{transmute_copy, ManuallyDrop, MaybeUninit}; +use core::ptr::drop_in_place; + +pub fn binop_impl( + lhs: [T; N], + rhs: [U; N], + op: impl Fn(T, U) -> O + Copy, +) -> [O; N] { + let mut dc = BinOpsIter::new(lhs, rhs); + + for _ in 0..N { + // SAFETY: + // Will only be called a maximum of N times + unsafe { dc.step(op) } + } + + // SAFETY: + // By this point, we are certain we have initialised all N elements + unsafe { dc.output() } +} + +pub fn binop_assign_impl( + lhs: &mut [T; N], + rhs: [U; N], + op: impl Fn(&mut T, U) + Copy, +) { + let mut dc = Iter::new(rhs); + + for _ in 0..N { + // SAFETY: + // Will only be called a maximum of N times + unsafe { op(lhs.get_unchecked_mut(dc.index()), dc.next_unchecked()) } + } +} + +/// Like [`Iter`], but traverses 3 arrays at once +pub struct BinOpsIter { + lhs: [MaybeUninit; N], + rhs: [MaybeUninit; N], + output: [MaybeUninit; N], + i: usize, +} + +impl Drop for BinOpsIter { + fn drop(&mut self) { + let i = self.i; + // SAFETY: + // `i` defines how many elements have been processed from the arrays. + // Caveat, the only potential panic would happen *before* the write to the output, + // so the `i`th output is not initialised as one would assume. + unsafe { + drop_in_place((&mut self.lhs[i..]) as *mut [_] as *mut [T]); + drop_in_place((&mut self.rhs[i..]) as *mut [_] as *mut [T]); + drop_in_place(&mut self.output[..i - 1] as *mut [_] as *mut [O]); + } + } +} + +impl BinOpsIter { + pub fn new(lhs: [T; N], rhs: [U; N]) -> Self { + Self { lhs: mu_array(lhs), rhs: mu_array(rhs), output: MaybeUninit::uninit_array(), i: 0 } + } + + /// # Safety + /// All values of output must be initialised, and all values in the inputs must be consumed + pub unsafe fn output(self) -> [O; N] { + debug_assert_eq!(self.i, N); + + let md = ManuallyDrop::new(self); + // SAFETY: + // caller is responsible for ensuring the output is fully initialised + unsafe { assume_array_init_copy(&md.output) } + } + + /// # Safety + /// Must be called no more than `N` times. + pub unsafe fn step(&mut self, f: impl FnOnce(T, U) -> O) { + debug_assert!(self.i < N); + + // SAFETY: + // Since `self.i` is stricty-monotonic, we will only + // take each element only once from each of lhs/rhs/out + unsafe { + let lhs = take(self.lhs.get_unchecked_mut(self.i)); + let rhs = take(self.rhs.get_unchecked_mut(self.i)); + let out = self.output.get_unchecked_mut(self.i); + self.i += 1; + out.write(f(lhs, rhs)); + } + } +} + +/// For sake of optimisation, it's a simplified version of [`array::IntoIter`] +/// that can only go forward, and can only be accessed through unsafe (to avoid bounds checks) +pub struct Iter { + rhs: [MaybeUninit; N], + i: usize, +} + +impl Drop for Iter { + fn drop(&mut self) { + let i = self.i; + // SAFETY: + // `i` defines how many elements have been processed from the array, + // meaning that theres `i..` elements left to process (and therefore, drop) + unsafe { + drop_in_place((&mut self.rhs[i..]) as *mut [_] as *mut [U]); + } + } +} + +impl Iter { + pub fn index(&self) -> usize { + self.i + } + + pub fn new(rhs: [U; N]) -> Self { + Self { rhs: mu_array(rhs), i: 0 } + } + + /// # Safety + /// Must be called no more than `N` times. + #[inline(always)] + pub unsafe fn next_unchecked(&mut self) -> U { + debug_assert!(self.i < N); + + // SAFETY: + // Caller ensures that next is not called more than `N` times, so self.i must be + // smaller than N at this point + let rhs = unsafe { self.rhs.get_unchecked_mut(self.i) }; + + // SAFETY: + // Since `dc.i` is stricty-monotonic, we will only + // take each element only once from each of lhs/rhs + let rhs = unsafe { take(rhs) }; + + self.i += 1; + rhs + } +} + +pub unsafe fn take(slot: &mut MaybeUninit) -> T { + // SAFETY: we are reading from a reference, which is guaranteed + // to be valid for reads. + unsafe { core::ptr::read(slot.assume_init_mut()) } +} + +/// Create a new `[ManuallyDrop; N]` from the initialised array +fn mu_array(a: [T; N]) -> [MaybeUninit; N] { + a.map(MaybeUninit::new) +} + +/// # Safety +/// the array must be fully initialised, and it must not be used after this call. +pub unsafe fn assume_array_init_copy(a: &[MaybeUninit; N]) -> [T; N] { + // SAFETY: MaybeUninit is guaranteed to have the same layout + unsafe { transmute_copy(a) } +} diff --git a/library/core/tests/array.rs b/library/core/tests/array.rs index b34c01a3d0b9a..0571cf5f9357b 100644 --- a/library/core/tests/array.rs +++ b/library/core/tests/array.rs @@ -1,5 +1,6 @@ use core::array; use core::convert::TryFrom; +use core::iter::Iterator; use core::sync::atomic::{AtomicUsize, Ordering}; #[test] @@ -686,3 +687,25 @@ fn add_assign() { assert_eq!(a, [4, 6, 8, 10]); } + +#[test] +fn add_many() { + let a: [i32; 128] = (0..128).collect(); + let b: [i32; 128] = (128..256).collect(); + + let exp = (128..384).step_by(2).collect(); + + assert_eq!(a + b, exp); +} + +#[test] +fn add_assign_many() { + let mut a: [i32; 128] = (0..128).collect(); + let b: [i32; 128] = (128..256).collect(); + + let exp = (128..384).step_by(2).collect(); + + a += b; + + assert_eq!(a, exp); +} From a6d17919c6bb41e1b614db3adc491f897e734f56 Mon Sep 17 00:00:00 2001 From: Conrad Ludgate Date: Sun, 16 Jan 2022 11:49:23 +0000 Subject: [PATCH 08/17] expose zip_map --- library/core/src/array/binops.rs | 7 +- library/core/src/array/mod.rs | 76 ++++++++++++++++++- .../src/array/{binops/imp.rs => zip_map.rs} | 44 ++--------- 3 files changed, 80 insertions(+), 47 deletions(-) rename library/core/src/array/{binops/imp.rs => zip_map.rs} (79%) diff --git a/library/core/src/array/binops.rs b/library/core/src/array/binops.rs index 1cba6141c1831..06d7435ec24a4 100644 --- a/library/core/src/array/binops.rs +++ b/library/core/src/array/binops.rs @@ -1,8 +1,5 @@ use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign}; -mod imp; -use imp::{binop_assign_impl, binop_impl}; - macro_rules! binop { ($trait:ident, $method:ident) => { #[stable(feature = "array_bin_ops", since = "1.60.0")] @@ -13,7 +10,7 @@ macro_rules! binop { type Output = [T::Output; N]; fn $method(self, rhs: [U; N]) -> Self::Output { - binop_impl(self, rhs, T::$method) + self.zip_map(rhs, T::$method) } } }; @@ -27,7 +24,7 @@ macro_rules! binop_assign { T: $trait, { fn $method(&mut self, rhs: [U; N]) { - binop_assign_impl(self, rhs, T::$method) + self.zip_map_assign(rhs, T::$method) } } }; diff --git a/library/core/src/array/mod.rs b/library/core/src/array/mod.rs index 701ed9ce5a727..23445224afa79 100644 --- a/library/core/src/array/mod.rs +++ b/library/core/src/array/mod.rs @@ -19,10 +19,13 @@ use crate::slice::{Iter, IterMut}; mod binops; mod equality; mod iter; +mod zip_map; #[stable(feature = "array_value_iter", since = "1.51.0")] pub use iter::IntoIter; +use zip_map::{ZipMapIter, ForwardIter}; + /// Creates an array `[T; N]` where each array element `T` is returned by the `cb` call. /// /// # Arguments @@ -504,11 +507,76 @@ impl [T; N] { /// ``` #[unstable(feature = "array_zip", issue = "80094")] pub fn zip(self, rhs: [U; N]) -> [(T, U); N] { - let mut iter = IntoIterator::into_iter(self).zip(rhs); + self.zip_map(rhs, |a, b| (a, b)) + } - // SAFETY: we know for certain that this iterator will yield exactly `N` - // items. - unsafe { collect_into_array_unchecked(&mut iter) } + /// 'Zips up' two arrays into a single array, applying the `op` over the pairs + /// + /// This is equivalent but faster than doing manual zip + map + /// + /// # Examples + /// + /// ```rust + /// # use std::ops::Add; + /// #![feature(array_zip_map)] + /// let op = i32::add; + /// + /// let x = [1, 2, 3]; + /// let y = [4, 5, 6]; + /// + /// let output1 = x.zip(y).map(|(a, b)| op(a, b)); + /// let output2 = x.zip_map(y, op); + /// + /// assert_eq!(output1, output2); + /// ``` + #[unstable(feature = "array_zip_map", issue = "none")] + pub fn zip_map(self, rhs: [U; N], mut op: F) -> [R; N] + where + F: FnMut(T, U) -> R, + { + let mut iter = ZipMapIter::new(self, rhs); + + for _ in 0..N { + // SAFETY: + // Will only be called a maximum of N times + unsafe { iter.step(&mut op) } + } + + // SAFETY: + // By this point, we are certain we have initialised all N elements + unsafe { iter.output() } + } + + /// Applies the op over pairs of elements + /// + /// # Examples + /// + /// ```rust + /// # use std::ops::Add; + /// #![feature(array_zip_map)] + /// let op = i32::add; + /// + /// let x = [1, 2, 3]; + /// let y = [4, 5, 6]; + /// + /// let output1 = x.zip(y).map(|(a, b)| op(a, b)); + /// let output2 = x.zip_map(y, op); + /// + /// assert_eq!(output1, output2); + /// ``` + #[unstable(feature = "array_zip_map", issue = "none")] + pub fn zip_map_assign(&mut self, rhs: [U; N], mut op: F) + where + F: FnMut(&mut T, U), + { + let mut iter = ForwardIter::new(rhs); + let op = &mut op; + + for _ in 0..N { + // SAFETY: + // Will only be called a maximum of N times + unsafe { op(self.get_unchecked_mut(iter.index()), iter.next_unchecked()) } + } } /// Returns a slice containing the entire array. Equivalent to `&s[..]`. diff --git a/library/core/src/array/binops/imp.rs b/library/core/src/array/zip_map.rs similarity index 79% rename from library/core/src/array/binops/imp.rs rename to library/core/src/array/zip_map.rs index 11f818282ebfb..e523c82589101 100644 --- a/library/core/src/array/binops/imp.rs +++ b/library/core/src/array/zip_map.rs @@ -1,47 +1,15 @@ use core::mem::{transmute_copy, ManuallyDrop, MaybeUninit}; use core::ptr::drop_in_place; -pub fn binop_impl( - lhs: [T; N], - rhs: [U; N], - op: impl Fn(T, U) -> O + Copy, -) -> [O; N] { - let mut dc = BinOpsIter::new(lhs, rhs); - - for _ in 0..N { - // SAFETY: - // Will only be called a maximum of N times - unsafe { dc.step(op) } - } - - // SAFETY: - // By this point, we are certain we have initialised all N elements - unsafe { dc.output() } -} - -pub fn binop_assign_impl( - lhs: &mut [T; N], - rhs: [U; N], - op: impl Fn(&mut T, U) + Copy, -) { - let mut dc = Iter::new(rhs); - - for _ in 0..N { - // SAFETY: - // Will only be called a maximum of N times - unsafe { op(lhs.get_unchecked_mut(dc.index()), dc.next_unchecked()) } - } -} - /// Like [`Iter`], but traverses 3 arrays at once -pub struct BinOpsIter { +pub struct ZipMapIter { lhs: [MaybeUninit; N], rhs: [MaybeUninit; N], output: [MaybeUninit; N], i: usize, } -impl Drop for BinOpsIter { +impl Drop for ZipMapIter { fn drop(&mut self) { let i = self.i; // SAFETY: @@ -56,7 +24,7 @@ impl Drop for BinOpsIter { } } -impl BinOpsIter { +impl ZipMapIter { pub fn new(lhs: [T; N], rhs: [U; N]) -> Self { Self { lhs: mu_array(lhs), rhs: mu_array(rhs), output: MaybeUninit::uninit_array(), i: 0 } } @@ -92,12 +60,12 @@ impl BinOpsIter { /// For sake of optimisation, it's a simplified version of [`array::IntoIter`] /// that can only go forward, and can only be accessed through unsafe (to avoid bounds checks) -pub struct Iter { +pub struct ForwardIter { rhs: [MaybeUninit; N], i: usize, } -impl Drop for Iter { +impl Drop for ForwardIter { fn drop(&mut self) { let i = self.i; // SAFETY: @@ -109,7 +77,7 @@ impl Drop for Iter { } } -impl Iter { +impl ForwardIter { pub fn index(&self) -> usize { self.i } From 6a5abcbed87ba12ba0af2008726b4092cb31cc4b Mon Sep 17 00:00:00 2001 From: Conrad Ludgate Date: Sun, 16 Jan 2022 14:47:08 +0000 Subject: [PATCH 09/17] fix tests --- library/core/src/array/mod.rs | 2 +- library/core/tests/array.rs | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/library/core/src/array/mod.rs b/library/core/src/array/mod.rs index 23445224afa79..f9c9bd86794ac 100644 --- a/library/core/src/array/mod.rs +++ b/library/core/src/array/mod.rs @@ -24,7 +24,7 @@ mod zip_map; #[stable(feature = "array_value_iter", since = "1.51.0")] pub use iter::IntoIter; -use zip_map::{ZipMapIter, ForwardIter}; +use zip_map::{ForwardIter, ZipMapIter}; /// Creates an array `[T; N]` where each array element `T` is returned by the `cb` call. /// diff --git a/library/core/tests/array.rs b/library/core/tests/array.rs index 0571cf5f9357b..bb1ed2440f547 100644 --- a/library/core/tests/array.rs +++ b/library/core/tests/array.rs @@ -690,20 +690,20 @@ fn add_assign() { #[test] fn add_many() { - let a: [i32; 128] = (0..128).collect(); - let b: [i32; 128] = (128..256).collect(); + let a: [i32; 128] = (0..128).collect::>().try_into().unwrap(); + let b: [i32; 128] = (128..256).collect::>().try_into().unwrap(); - let exp = (128..384).step_by(2).collect(); + let exp: [i32; 128] = (128..384).step_by(2).collect::>().try_into().unwrap(); assert_eq!(a + b, exp); } #[test] fn add_assign_many() { - let mut a: [i32; 128] = (0..128).collect(); - let b: [i32; 128] = (128..256).collect(); + let mut a: [i32; 128] = (0..128).collect::>().try_into().unwrap(); + let b: [i32; 128] = (128..256).collect::>().try_into().unwrap(); - let exp = (128..384).step_by(2).collect(); + let exp: [i32; 128] = (128..384).step_by(2).collect::>().try_into().unwrap(); a += b; From acfec4f800b7dc0d97b2485a90f8c875d30c9248 Mon Sep 17 00:00:00 2001 From: Conrad Ludgate Date: Sun, 23 Jan 2022 10:05:06 +0000 Subject: [PATCH 10/17] stash --- library/core/src/array/iter.rs | 20 +++++ library/core/src/array/mod.rs | 62 +++++++++++---- library/core/src/array/zip_map.rs | 126 ------------------------------ 3 files changed, 65 insertions(+), 143 deletions(-) delete mode 100644 library/core/src/array/zip_map.rs diff --git a/library/core/src/array/iter.rs b/library/core/src/array/iter.rs index e5024c215be9c..561ab30edd2f5 100644 --- a/library/core/src/array/iter.rs +++ b/library/core/src/array/iter.rs @@ -233,6 +233,26 @@ impl IntoIter { MaybeUninit::slice_assume_init_mut(slice) } } + + unsafe fn pop_front_unchecked(&mut self) -> T { + debug_assert!(!self.alive.is_empty()); + debug_assert!(self.alive.start < N); + + unsafe { + let front = take(self.data.get_unchecked_mut(self.alive.start)); + self.alive.start += 1; + front + } + } + + unsafe fn push_unchecked(&mut self, value: T) { + debug_assert!(self.alive.end < N); + + unsafe { + self.array.get_unchecked_mut(self.alive.end).write(value); + self.alive.end += 1; + } + } } #[stable(feature = "array_value_iter_impls", since = "1.40.0")] diff --git a/library/core/src/array/mod.rs b/library/core/src/array/mod.rs index f9c9bd86794ac..d24a40154ae19 100644 --- a/library/core/src/array/mod.rs +++ b/library/core/src/array/mod.rs @@ -19,13 +19,10 @@ use crate::slice::{Iter, IterMut}; mod binops; mod equality; mod iter; -mod zip_map; #[stable(feature = "array_value_iter", since = "1.51.0")] pub use iter::IntoIter; -use zip_map::{ForwardIter, ZipMapIter}; - /// Creates an array `[T; N]` where each array element `T` is returned by the `cb` call. /// /// # Arguments @@ -534,17 +531,38 @@ impl [T; N] { where F: FnMut(T, U) -> R, { - let mut iter = ZipMapIter::new(self, rhs); + if core::mem::needs_drop::() + || core::mem::needs_drop::() + || core::mem::needs_drop::() + { + let mut lhs = IntoIterator::into_iter(self); + let mut rhs = IntoIterator::into_iter(rhs); + let mut output = IntoIter::empty(); + + for _ in 0..N { + unsafe { + let lhs = lhs.pop_front_unchecked(); + let rhs = rhs.pop_front_unchecked(); + output.push_unchecked(op(lhs, rhs)); + } + } - for _ in 0..N { + unsafe { output.output() } + } else { // SAFETY: - // Will only be called a maximum of N times - unsafe { iter.step(&mut op) } - } + // we will not read from output, and caller ensures that O is non-drop + let mut output: [MaybeUninit; N] = uninit_array(); + + for i in 0..N { + unsafe { + let lhs = core::ptr::read(&self[i]); + let rhs = core::ptr::read(&rhs[i]); + output[i].write(op(lhs, rhs)); + } + } - // SAFETY: - // By this point, we are certain we have initialised all N elements - unsafe { iter.output() } + unsafe { core::ptr::read(&output as *const [MaybeUninit; N] as *const [O; N]) } + } } /// Applies the op over pairs of elements @@ -569,13 +587,23 @@ impl [T; N] { where F: FnMut(&mut T, U), { - let mut iter = ForwardIter::new(rhs); - let op = &mut op; + if needs_drop::() { + let mut rhs = IntoIterator::into_iter(rhs); - for _ in 0..N { - // SAFETY: - // Will only be called a maximum of N times - unsafe { op(self.get_unchecked_mut(iter.index()), iter.next_unchecked()) } + for i in 0..N { + // SAFETY: + // Will only be called a maximum of N times + unsafe { op(self.0.get_unchecked_mut(i), rhs.pop_front_unchecked()) } + } + } else { + for i in 0..N { + // SAFETY: + // Will only be called a maximum of N times + unsafe { + let rhs = core::ptr::read(&rhs[i]); + op(self.get_unchecked_mut(i), rhs) + } + } } } diff --git a/library/core/src/array/zip_map.rs b/library/core/src/array/zip_map.rs deleted file mode 100644 index e523c82589101..0000000000000 --- a/library/core/src/array/zip_map.rs +++ /dev/null @@ -1,126 +0,0 @@ -use core::mem::{transmute_copy, ManuallyDrop, MaybeUninit}; -use core::ptr::drop_in_place; - -/// Like [`Iter`], but traverses 3 arrays at once -pub struct ZipMapIter { - lhs: [MaybeUninit; N], - rhs: [MaybeUninit; N], - output: [MaybeUninit; N], - i: usize, -} - -impl Drop for ZipMapIter { - fn drop(&mut self) { - let i = self.i; - // SAFETY: - // `i` defines how many elements have been processed from the arrays. - // Caveat, the only potential panic would happen *before* the write to the output, - // so the `i`th output is not initialised as one would assume. - unsafe { - drop_in_place((&mut self.lhs[i..]) as *mut [_] as *mut [T]); - drop_in_place((&mut self.rhs[i..]) as *mut [_] as *mut [T]); - drop_in_place(&mut self.output[..i - 1] as *mut [_] as *mut [O]); - } - } -} - -impl ZipMapIter { - pub fn new(lhs: [T; N], rhs: [U; N]) -> Self { - Self { lhs: mu_array(lhs), rhs: mu_array(rhs), output: MaybeUninit::uninit_array(), i: 0 } - } - - /// # Safety - /// All values of output must be initialised, and all values in the inputs must be consumed - pub unsafe fn output(self) -> [O; N] { - debug_assert_eq!(self.i, N); - - let md = ManuallyDrop::new(self); - // SAFETY: - // caller is responsible for ensuring the output is fully initialised - unsafe { assume_array_init_copy(&md.output) } - } - - /// # Safety - /// Must be called no more than `N` times. - pub unsafe fn step(&mut self, f: impl FnOnce(T, U) -> O) { - debug_assert!(self.i < N); - - // SAFETY: - // Since `self.i` is stricty-monotonic, we will only - // take each element only once from each of lhs/rhs/out - unsafe { - let lhs = take(self.lhs.get_unchecked_mut(self.i)); - let rhs = take(self.rhs.get_unchecked_mut(self.i)); - let out = self.output.get_unchecked_mut(self.i); - self.i += 1; - out.write(f(lhs, rhs)); - } - } -} - -/// For sake of optimisation, it's a simplified version of [`array::IntoIter`] -/// that can only go forward, and can only be accessed through unsafe (to avoid bounds checks) -pub struct ForwardIter { - rhs: [MaybeUninit; N], - i: usize, -} - -impl Drop for ForwardIter { - fn drop(&mut self) { - let i = self.i; - // SAFETY: - // `i` defines how many elements have been processed from the array, - // meaning that theres `i..` elements left to process (and therefore, drop) - unsafe { - drop_in_place((&mut self.rhs[i..]) as *mut [_] as *mut [U]); - } - } -} - -impl ForwardIter { - pub fn index(&self) -> usize { - self.i - } - - pub fn new(rhs: [U; N]) -> Self { - Self { rhs: mu_array(rhs), i: 0 } - } - - /// # Safety - /// Must be called no more than `N` times. - #[inline(always)] - pub unsafe fn next_unchecked(&mut self) -> U { - debug_assert!(self.i < N); - - // SAFETY: - // Caller ensures that next is not called more than `N` times, so self.i must be - // smaller than N at this point - let rhs = unsafe { self.rhs.get_unchecked_mut(self.i) }; - - // SAFETY: - // Since `dc.i` is stricty-monotonic, we will only - // take each element only once from each of lhs/rhs - let rhs = unsafe { take(rhs) }; - - self.i += 1; - rhs - } -} - -pub unsafe fn take(slot: &mut MaybeUninit) -> T { - // SAFETY: we are reading from a reference, which is guaranteed - // to be valid for reads. - unsafe { core::ptr::read(slot.assume_init_mut()) } -} - -/// Create a new `[ManuallyDrop; N]` from the initialised array -fn mu_array(a: [T; N]) -> [MaybeUninit; N] { - a.map(MaybeUninit::new) -} - -/// # Safety -/// the array must be fully initialised, and it must not be used after this call. -pub unsafe fn assume_array_init_copy(a: &[MaybeUninit; N]) -> [T; N] { - // SAFETY: MaybeUninit is guaranteed to have the same layout - unsafe { transmute_copy(a) } -} From 8e384e8b726bb5f44530781919f2d4b8bc853b97 Mon Sep 17 00:00:00 2001 From: Conrad Ludgate Date: Sun, 27 Feb 2022 08:29:34 +0000 Subject: [PATCH 11/17] clean up --- library/core/src/array/iter.rs | 23 ++++++++++++++++++----- library/core/src/array/mod.rs | 25 ++++++++++++++++--------- 2 files changed, 34 insertions(+), 14 deletions(-) diff --git a/library/core/src/array/iter.rs b/library/core/src/array/iter.rs index 561ab30edd2f5..c02083a3f85e4 100644 --- a/library/core/src/array/iter.rs +++ b/library/core/src/array/iter.rs @@ -3,7 +3,7 @@ use crate::{ cmp, fmt, iter::{self, ExactSizeIterator, FusedIterator, TrustedLen}, - mem::{self, MaybeUninit}, + mem::{self, ManuallyDrop, MaybeUninit}, ops::Range, ptr, }; @@ -234,25 +234,38 @@ impl IntoIter { } } - unsafe fn pop_front_unchecked(&mut self) -> T { + pub(super) unsafe fn pop_front_unchecked(&mut self) -> T { debug_assert!(!self.alive.is_empty()); debug_assert!(self.alive.start < N); + // SAFETY: The caller should ensure that these values are still valid unsafe { - let front = take(self.data.get_unchecked_mut(self.alive.start)); + let front = MaybeUninit::assume_init_read(self.data.get_unchecked(self.alive.start)); self.alive.start += 1; front } } - unsafe fn push_unchecked(&mut self, value: T) { + pub(super) unsafe fn push_unchecked(&mut self, value: T) { debug_assert!(self.alive.end < N); + // SAFETY: The caller should ensure that there is still free space unsafe { - self.array.get_unchecked_mut(self.alive.end).write(value); + self.data.get_unchecked_mut(self.alive.end).write(value); self.alive.end += 1; } } + + pub(super) unsafe fn assume_init(self) -> [T; N] { + debug_assert!(self.alive == (0..N)); + let this = ManuallyDrop::new(self); + + // SAFETY: The caller should ensure that the array is full + unsafe { + let data = core::ptr::read(&this.data); + MaybeUninit::array_assume_init(data) + } + } } #[stable(feature = "array_value_iter_impls", since = "1.40.0")] diff --git a/library/core/src/array/mod.rs b/library/core/src/array/mod.rs index d24a40154ae19..6f56cd93a3dad 100644 --- a/library/core/src/array/mod.rs +++ b/library/core/src/array/mod.rs @@ -533,13 +533,14 @@ impl [T; N] { { if core::mem::needs_drop::() || core::mem::needs_drop::() - || core::mem::needs_drop::() + || core::mem::needs_drop::() { let mut lhs = IntoIterator::into_iter(self); let mut rhs = IntoIterator::into_iter(rhs); let mut output = IntoIter::empty(); for _ in 0..N { + // SAFETY: Will only be called a maximum of N times unsafe { let lhs = lhs.pop_front_unchecked(); let rhs = rhs.pop_front_unchecked(); @@ -547,13 +548,13 @@ impl [T; N] { } } - unsafe { output.output() } + // SAFETY: We have confirmed it has N elements + unsafe { output.assume_init() } } else { - // SAFETY: - // we will not read from output, and caller ensures that O is non-drop - let mut output: [MaybeUninit; N] = uninit_array(); + let mut output: [MaybeUninit; N] = MaybeUninit::uninit_array(); for i in 0..N { + // SAFETY: Will only be called a maximum of N times unsafe { let lhs = core::ptr::read(&self[i]); let rhs = core::ptr::read(&rhs[i]); @@ -561,7 +562,8 @@ impl [T; N] { } } - unsafe { core::ptr::read(&output as *const [MaybeUninit; N] as *const [O; N]) } + // SAFETY: We have confirmed it has N elements + unsafe { MaybeUninit::array_assume_init(output) } } } @@ -587,21 +589,26 @@ impl [T; N] { where F: FnMut(&mut T, U), { - if needs_drop::() { + if core::mem::needs_drop::() { let mut rhs = IntoIterator::into_iter(rhs); for i in 0..N { // SAFETY: // Will only be called a maximum of N times - unsafe { op(self.0.get_unchecked_mut(i), rhs.pop_front_unchecked()) } + unsafe { + let lhs = self.get_unchecked_mut(i); + let rhs = rhs.pop_front_unchecked(); + op(lhs, rhs) + } } } else { for i in 0..N { // SAFETY: // Will only be called a maximum of N times unsafe { + let lhs = self.get_unchecked_mut(i); let rhs = core::ptr::read(&rhs[i]); - op(self.get_unchecked_mut(i), rhs) + op(lhs, rhs) } } } From e1d06ac1465d14b990764d42c4abbb6878912ad2 Mon Sep 17 00:00:00 2001 From: Conrad Ludgate Date: Sun, 27 Feb 2022 08:36:17 +0000 Subject: [PATCH 12/17] disable traits for now --- library/core/src/array/binops.rs | 72 ++++++++++++++++---------------- library/core/tests/array.rs | 9 ++-- library/core/tests/lib.rs | 1 + 3 files changed, 42 insertions(+), 40 deletions(-) diff --git a/library/core/src/array/binops.rs b/library/core/src/array/binops.rs index 06d7435ec24a4..11fe136354cdb 100644 --- a/library/core/src/array/binops.rs +++ b/library/core/src/array/binops.rs @@ -1,41 +1,41 @@ -use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign}; +// use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign}; -macro_rules! binop { - ($trait:ident, $method:ident) => { - #[stable(feature = "array_bin_ops", since = "1.60.0")] - impl $trait<[U; N]> for [T; N] - where - T: $trait, - { - type Output = [T::Output; N]; +// macro_rules! binop { +// ($trait:ident, $method:ident) => { +// #[stable(feature = "array_bin_ops", since = "1.60.0")] +// impl $trait<[U; N]> for [T; N] +// where +// T: $trait, +// { +// type Output = [T::Output; N]; - fn $method(self, rhs: [U; N]) -> Self::Output { - self.zip_map(rhs, T::$method) - } - } - }; -} +// fn $method(self, rhs: [U; N]) -> Self::Output { +// self.zip_map(rhs, T::$method) +// } +// } +// }; +// } -macro_rules! binop_assign { - ($trait:ident, $method:ident) => { - #[stable(feature = "array_bin_ops", since = "1.60.0")] - impl $trait<[U; N]> for [T; N] - where - T: $trait, - { - fn $method(&mut self, rhs: [U; N]) { - self.zip_map_assign(rhs, T::$method) - } - } - }; -} +// macro_rules! binop_assign { +// ($trait:ident, $method:ident) => { +// #[stable(feature = "array_bin_ops", since = "1.60.0")] +// impl $trait<[U; N]> for [T; N] +// where +// T: $trait, +// { +// fn $method(&mut self, rhs: [U; N]) { +// self.zip_map_assign(rhs, T::$method) +// } +// } +// }; +// } -binop!(Add, add); -binop!(Mul, mul); -binop!(Div, div); -binop!(Sub, sub); +// binop!(Add, add); +// binop!(Mul, mul); +// binop!(Div, div); +// binop!(Sub, sub); -binop_assign!(AddAssign, add_assign); -binop_assign!(MulAssign, mul_assign); -binop_assign!(DivAssign, div_assign); -binop_assign!(SubAssign, sub_assign); +// binop_assign!(AddAssign, add_assign); +// binop_assign!(MulAssign, mul_assign); +// binop_assign!(DivAssign, div_assign); +// binop_assign!(SubAssign, sub_assign); diff --git a/library/core/tests/array.rs b/library/core/tests/array.rs index bb1ed2440f547..f60d28a9a4a5f 100644 --- a/library/core/tests/array.rs +++ b/library/core/tests/array.rs @@ -1,6 +1,7 @@ use core::array; use core::convert::TryFrom; use core::iter::Iterator; +use core::ops::{Add, AddAssign}; use core::sync::atomic::{AtomicUsize, Ordering}; #[test] @@ -675,7 +676,7 @@ fn add() { let a: [i32; 4] = [0, 1, 2, 3]; let b: [i32; 4] = [4, 5, 6, 7]; - assert_eq!(a + b, [4, 6, 8, 10]); + assert_eq!(a.zip_map(b, Add::add), [4, 6, 8, 10]); } #[test] @@ -683,7 +684,7 @@ fn add_assign() { let mut a: [i32; 4] = [0, 1, 2, 3]; let b: [i32; 4] = [4, 5, 6, 7]; - a += b; + a.zip_map_assign(b, AddAssign::add_assign); assert_eq!(a, [4, 6, 8, 10]); } @@ -695,7 +696,7 @@ fn add_many() { let exp: [i32; 128] = (128..384).step_by(2).collect::>().try_into().unwrap(); - assert_eq!(a + b, exp); + assert_eq!(a.zip_map(b, Add::add), exp); } #[test] @@ -705,7 +706,7 @@ fn add_assign_many() { let exp: [i32; 128] = (128..384).step_by(2).collect::>().try_into().unwrap(); - a += b; + a.zip_map_assign(b, AddAssign::add_assign); assert_eq!(a, exp); } diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs index 28830222c1a94..b15078634def5 100644 --- a/library/core/tests/lib.rs +++ b/library/core/tests/lib.rs @@ -2,6 +2,7 @@ #![feature(array_chunks)] #![feature(array_methods)] #![feature(array_windows)] +#![feature(array_zip_map)] #![feature(bench_black_box)] #![feature(bool_to_option)] #![feature(box_syntax)] From 314aa2fbe139ce01c0529cb5026097bdfdbfef18 Mon Sep 17 00:00:00 2001 From: Conrad Ludgate Date: Sun, 27 Feb 2022 08:37:28 +0000 Subject: [PATCH 13/17] disable traits for now --- library/core/src/array/binops.rs | 41 -------------------------------- 1 file changed, 41 deletions(-) delete mode 100644 library/core/src/array/binops.rs diff --git a/library/core/src/array/binops.rs b/library/core/src/array/binops.rs deleted file mode 100644 index 11fe136354cdb..0000000000000 --- a/library/core/src/array/binops.rs +++ /dev/null @@ -1,41 +0,0 @@ -// use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign}; - -// macro_rules! binop { -// ($trait:ident, $method:ident) => { -// #[stable(feature = "array_bin_ops", since = "1.60.0")] -// impl $trait<[U; N]> for [T; N] -// where -// T: $trait, -// { -// type Output = [T::Output; N]; - -// fn $method(self, rhs: [U; N]) -> Self::Output { -// self.zip_map(rhs, T::$method) -// } -// } -// }; -// } - -// macro_rules! binop_assign { -// ($trait:ident, $method:ident) => { -// #[stable(feature = "array_bin_ops", since = "1.60.0")] -// impl $trait<[U; N]> for [T; N] -// where -// T: $trait, -// { -// fn $method(&mut self, rhs: [U; N]) { -// self.zip_map_assign(rhs, T::$method) -// } -// } -// }; -// } - -// binop!(Add, add); -// binop!(Mul, mul); -// binop!(Div, div); -// binop!(Sub, sub); - -// binop_assign!(AddAssign, add_assign); -// binop_assign!(MulAssign, mul_assign); -// binop_assign!(DivAssign, div_assign); -// binop_assign!(SubAssign, sub_assign); From 27a9cc346e9be7cf3b1465b91efec09c33632b63 Mon Sep 17 00:00:00 2001 From: Conrad Ludgate Date: Sun, 27 Feb 2022 08:49:52 +0000 Subject: [PATCH 14/17] add extra comments --- library/core/src/array/mod.rs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/library/core/src/array/mod.rs b/library/core/src/array/mod.rs index 6f56cd93a3dad..ed21b68c61ca3 100644 --- a/library/core/src/array/mod.rs +++ b/library/core/src/array/mod.rs @@ -531,6 +531,11 @@ impl [T; N] { where F: FnMut(T, U) -> R, { + // If any of the items need drop, take the 'slow' path that ensures all drop + // handlers are called in case of panic. + // + // In my profiling, I found that the drop code can interfere with the codegen just enough + // to cause some noticable regressions in speed if core::mem::needs_drop::() || core::mem::needs_drop::() || core::mem::needs_drop::() @@ -589,12 +594,16 @@ impl [T; N] { where F: FnMut(&mut T, U), { + // If the rhs need drop, take the 'slow' path that ensures all drop + // handlers are called in case of panic. + // + // In my profiling, I found that the drop code can interfere with the codegen just enough + // to cause some noticable regressions in speed if core::mem::needs_drop::() { let mut rhs = IntoIterator::into_iter(rhs); for i in 0..N { - // SAFETY: - // Will only be called a maximum of N times + // SAFETY: Will only be called a maximum of N times unsafe { let lhs = self.get_unchecked_mut(i); let rhs = rhs.pop_front_unchecked(); @@ -603,8 +612,7 @@ impl [T; N] { } } else { for i in 0..N { - // SAFETY: - // Will only be called a maximum of N times + // SAFETY: Will only be called a maximum of N times unsafe { let lhs = self.get_unchecked_mut(i); let rhs = core::ptr::read(&rhs[i]); From 4dda5dfd457f0883a5dbdde2312168fbeabd2a3f Mon Sep 17 00:00:00 2001 From: Conrad Ludgate Date: Sun, 27 Feb 2022 08:50:40 +0000 Subject: [PATCH 15/17] remove dead module --- library/core/src/array/mod.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/library/core/src/array/mod.rs b/library/core/src/array/mod.rs index ed21b68c61ca3..62882b2257c95 100644 --- a/library/core/src/array/mod.rs +++ b/library/core/src/array/mod.rs @@ -16,7 +16,6 @@ use crate::ops::{ }; use crate::slice::{Iter, IterMut}; -mod binops; mod equality; mod iter; From 473469d8fcd5d75ff6bbd9163bec619e1724b3e7 Mon Sep 17 00:00:00 2001 From: Conrad Ludgate Date: Sun, 27 Feb 2022 09:22:13 +0000 Subject: [PATCH 16/17] fix doctests --- library/core/src/array/mod.rs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/library/core/src/array/mod.rs b/library/core/src/array/mod.rs index 62882b2257c95..f3a2e0be896e2 100644 --- a/library/core/src/array/mod.rs +++ b/library/core/src/array/mod.rs @@ -514,7 +514,7 @@ impl [T; N] { /// /// ```rust /// # use std::ops::Add; - /// #![feature(array_zip_map)] + /// #![feature(array_zip, array_zip_map)] /// let op = i32::add; /// /// let x = [1, 2, 3]; @@ -571,22 +571,21 @@ impl [T; N] { } } - /// Applies the op over pairs of elements + /// Applying the `op` over pairs of elements and assigning the value + /// back into `self` /// /// # Examples /// /// ```rust /// # use std::ops::Add; /// #![feature(array_zip_map)] - /// let op = i32::add; /// - /// let x = [1, 2, 3]; + /// let mut x = [1, 2, 3]; /// let y = [4, 5, 6]; /// - /// let output1 = x.zip(y).map(|(a, b)| op(a, b)); - /// let output2 = x.zip_map(y, op); + /// x.zip_map_assign(y, i32::add_assign); /// - /// assert_eq!(output1, output2); + /// assert_eq!(x, [5, 7, 9]); /// ``` #[unstable(feature = "array_zip_map", issue = "none")] pub fn zip_map_assign(&mut self, rhs: [U; N], mut op: F) From 7b95e897542c672d23cf6bc0e5131e3c1fca3cb3 Mon Sep 17 00:00:00 2001 From: Conrad Ludgate Date: Sun, 27 Feb 2022 10:31:22 +0000 Subject: [PATCH 17/17] fix doctests --- library/core/src/array/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/core/src/array/mod.rs b/library/core/src/array/mod.rs index f3a2e0be896e2..607a12be5e663 100644 --- a/library/core/src/array/mod.rs +++ b/library/core/src/array/mod.rs @@ -513,8 +513,8 @@ impl [T; N] { /// # Examples /// /// ```rust - /// # use std::ops::Add; /// #![feature(array_zip, array_zip_map)] + /// # use std::ops::Add; /// let op = i32::add; /// /// let x = [1, 2, 3]; @@ -577,8 +577,8 @@ impl [T; N] { /// # Examples /// /// ```rust - /// # use std::ops::Add; /// #![feature(array_zip_map)] + /// # use std::ops::AddAssign; /// /// let mut x = [1, 2, 3]; /// let y = [4, 5, 6];