From 0eba4c206a3c47c375cb9ead20d37cf00f6351cf Mon Sep 17 00:00:00 2001 From: Trevor Spiteri Date: Mon, 8 Jan 2018 19:22:37 +0100 Subject: [PATCH 01/22] doc: show that `f32::log` and `f64::log` are not correctly rounded --- src/libstd/f32.rs | 19 +++++++++---------- src/libstd/f64.rs | 19 +++++++++---------- 2 files changed, 18 insertions(+), 20 deletions(-) diff --git a/src/libstd/f32.rs b/src/libstd/f32.rs index 6d76c7e722c..8a15f19e440 100644 --- a/src/libstd/f32.rs +++ b/src/libstd/f32.rs @@ -470,22 +470,21 @@ impl f32 { return unsafe { intrinsics::logf32(self) }; } - /// Returns the logarithm of the number with respect to an arbitrary base. + /// Returns the logarithm of the number with respect to an arbitrary base, + /// calculated as `self.ln() / base.ln()`. + /// + /// `self.log2()` can produce more accurate results for base 2, and + /// `self.log10()` can produce more accurate results for base 10. /// /// ``` /// use std::f32; /// - /// let ten = 10.0f32; - /// let two = 2.0f32; + /// let five = 5.0f32; /// - /// // log10(10) - 1 == 0 - /// let abs_difference_10 = (ten.log(10.0) - 1.0).abs(); + /// // log5(5) - 1 == 0 + /// let abs_difference = (five.log(5.0) - 1.0).abs(); /// - /// // log2(2) - 1 == 0 - /// let abs_difference_2 = (two.log(2.0) - 1.0).abs(); - /// - /// assert!(abs_difference_10 <= f32::EPSILON); - /// assert!(abs_difference_2 <= f32::EPSILON); + /// assert!(abs_difference <= f32::EPSILON); /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[inline] diff --git a/src/libstd/f64.rs b/src/libstd/f64.rs index dee9566f1fc..33f447b794c 100644 --- a/src/libstd/f64.rs +++ b/src/libstd/f64.rs @@ -430,20 +430,19 @@ impl f64 { self.log_wrapper(|n| { unsafe { intrinsics::logf64(n) } }) } - /// Returns the logarithm of the number with respect to an arbitrary base. + /// Returns the logarithm of the number with respect to an arbitrary base, + /// calculated as `self.ln() / base.ln()`. + /// + /// `self.log2()` can produce more accurate results for base 2, and + /// `self.log10()` can produce more accurate results for base 10. /// /// ``` - /// let ten = 10.0_f64; - /// let two = 2.0_f64; + /// let five = 5.0_f64; /// - /// // log10(10) - 1 == 0 - /// let abs_difference_10 = (ten.log(10.0) - 1.0).abs(); + /// // log5(5) - 1 == 0 + /// let abs_difference = (five.log(5.0) - 1.0).abs(); /// - /// // log2(2) - 1 == 0 - /// let abs_difference_2 = (two.log(2.0) - 1.0).abs(); - /// - /// assert!(abs_difference_10 < 1e-10); - /// assert!(abs_difference_2 < 1e-10); + /// assert!(abs_difference < 1e-10); /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[inline] From 6d82e7814f1a387c0510ee525c6e0ac8fa890c40 Mon Sep 17 00:00:00 2001 From: Trevor Spiteri Date: Tue, 9 Jan 2018 12:26:00 +0100 Subject: [PATCH 02/22] remove implementation detail from doc --- src/libstd/f32.rs | 4 ++-- src/libstd/f64.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libstd/f32.rs b/src/libstd/f32.rs index 8a15f19e440..5e5695f15ac 100644 --- a/src/libstd/f32.rs +++ b/src/libstd/f32.rs @@ -470,9 +470,9 @@ impl f32 { return unsafe { intrinsics::logf32(self) }; } - /// Returns the logarithm of the number with respect to an arbitrary base, - /// calculated as `self.ln() / base.ln()`. + /// Returns the logarithm of the number with respect to an arbitrary base. /// + /// The result may not be correctly rounded owing to implementation details; /// `self.log2()` can produce more accurate results for base 2, and /// `self.log10()` can produce more accurate results for base 10. /// diff --git a/src/libstd/f64.rs b/src/libstd/f64.rs index 33f447b794c..e4eea745bb7 100644 --- a/src/libstd/f64.rs +++ b/src/libstd/f64.rs @@ -430,9 +430,9 @@ impl f64 { self.log_wrapper(|n| { unsafe { intrinsics::logf64(n) } }) } - /// Returns the logarithm of the number with respect to an arbitrary base, - /// calculated as `self.ln() / base.ln()`. + /// Returns the logarithm of the number with respect to an arbitrary base. /// + /// The result may not be correctly rounded owing to implementation details; /// `self.log2()` can produce more accurate results for base 2, and /// `self.log10()` can produce more accurate results for base 10. /// From 76cf279504e794208ee49f2debaea33a2d37b207 Mon Sep 17 00:00:00 2001 From: Chris Vittal Date: Thu, 11 Jan 2018 18:08:28 -0500 Subject: [PATCH 03/22] Add NLL tests for #46557 and #38899 Closes #47366 Adapt the sample code from the issues into mir-borrowck/nll test cases. --- .../ui/nll/borrowed-referent-issue-38899.rs | 32 +++++++++++++++++++ .../nll/borrowed-referent-issue-38899.stderr | 11 +++++++ src/test/ui/nll/return-ref-mut-issue-46557.rs | 23 +++++++++++++ .../ui/nll/return-ref-mut-issue-46557.stderr | 13 ++++++++ 4 files changed, 79 insertions(+) create mode 100644 src/test/ui/nll/borrowed-referent-issue-38899.rs create mode 100644 src/test/ui/nll/borrowed-referent-issue-38899.stderr create mode 100644 src/test/ui/nll/return-ref-mut-issue-46557.rs create mode 100644 src/test/ui/nll/return-ref-mut-issue-46557.stderr diff --git a/src/test/ui/nll/borrowed-referent-issue-38899.rs b/src/test/ui/nll/borrowed-referent-issue-38899.rs new file mode 100644 index 00000000000..1057e4ef6f3 --- /dev/null +++ b/src/test/ui/nll/borrowed-referent-issue-38899.rs @@ -0,0 +1,32 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Regression test for issue #38899 + +// compile-flags:-Znll -Zborrowck=mir -Zverbose + +#![feature(nll)] +#![allow(dead_code)] + +pub struct Block<'a> { + current: &'a u8, + unrelated: &'a u8, +} + +fn bump<'a>(mut block: &mut Block<'a>) { + let x = &mut block; + println!("{}", x.current); + let p: &'a u8 = &*block.current; + //~^ ERROR cannot borrow `*block.current` as immutable because it is also borrowed as mutable + drop(x); + drop(p); +} + +fn main() {} diff --git a/src/test/ui/nll/borrowed-referent-issue-38899.stderr b/src/test/ui/nll/borrowed-referent-issue-38899.stderr new file mode 100644 index 00000000000..ff23880e709 --- /dev/null +++ b/src/test/ui/nll/borrowed-referent-issue-38899.stderr @@ -0,0 +1,11 @@ +error[E0502]: cannot borrow `*block.current` as immutable because it is also borrowed as mutable + --> $DIR/borrowed-referent-issue-38899.rs:26:21 + | +24 | let x = &mut block; + | ---------- mutable borrow occurs here +25 | println!("{}", x.current); +26 | let p: &'a u8 = &*block.current; + | ^^^^^^^^^^^^^^^ immutable borrow occurs here + +error: aborting due to previous error + diff --git a/src/test/ui/nll/return-ref-mut-issue-46557.rs b/src/test/ui/nll/return-ref-mut-issue-46557.rs new file mode 100644 index 00000000000..ee00e703f05 --- /dev/null +++ b/src/test/ui/nll/return-ref-mut-issue-46557.rs @@ -0,0 +1,23 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Regression test for issue #46557 + +// compile-flags:-Znll -Zborrowck=mir -Zverbose + +#![feature(nll)] +#![allow(dead_code)] + +fn gimme_static_mut() -> &'static mut u32 { + let ref mut x = 1234543; //~ ERROR borrowed value does not live long enough [E0597] + x +} + +fn main() {} diff --git a/src/test/ui/nll/return-ref-mut-issue-46557.stderr b/src/test/ui/nll/return-ref-mut-issue-46557.stderr new file mode 100644 index 00000000000..c5b5cf75813 --- /dev/null +++ b/src/test/ui/nll/return-ref-mut-issue-46557.stderr @@ -0,0 +1,13 @@ +error[E0597]: borrowed value does not live long enough + --> $DIR/return-ref-mut-issue-46557.rs:19:21 + | +19 | let ref mut x = 1234543; //~ ERROR borrowed value does not live long enough [E0597] + | ^^^^^^^ temporary value does not live long enough +20 | x +21 | } + | - temporary value only lives until here + | + = note: borrowed value must be valid for lifetime '_#2r... + +error: aborting due to previous error + From d088b25d7e3a317e98b59d49c262a314fb2bbd26 Mon Sep 17 00:00:00 2001 From: Seiichi Uchida Date: Fri, 12 Jan 2018 11:41:33 +0900 Subject: [PATCH 04/22] Avoid panicking when invalid argument is passed to cfg(..) Closes #43925. Closes #43926. --- src/librustc_metadata/native_libs.rs | 16 +++++++++++++--- src/test/compile-fail/issue-43925.rs | 16 ++++++++++++++++ src/test/compile-fail/issue-43926.rs | 14 ++++++++++++++ 3 files changed, 43 insertions(+), 3 deletions(-) create mode 100644 src/test/compile-fail/issue-43925.rs create mode 100644 src/test/compile-fail/issue-43926.rs diff --git a/src/librustc_metadata/native_libs.rs b/src/librustc_metadata/native_libs.rs index cc332acb5b0..c0ce32cc970 100644 --- a/src/librustc_metadata/native_libs.rs +++ b/src/librustc_metadata/native_libs.rs @@ -92,9 +92,19 @@ impl<'a, 'tcx> ItemLikeVisitor<'tcx> for Collector<'a, 'tcx> { let cfg = items.iter().find(|k| { k.check_name("cfg") }).and_then(|a| a.meta_item_list()); - let cfg = cfg.map(|list| { - list[0].meta_item().unwrap().clone() - }); + let cfg = if let Some(list) = cfg { + if list.is_empty() { + self.tcx.sess.span_err(m.span(), "`cfg()` must have an argument"); + return; + } else if let cfg @ Some(..) = list[0].meta_item() { + cfg.cloned() + } else { + self.tcx.sess.span_err(list[0].span(), "invalid argument for `cfg(..)`"); + return; + } + } else { + None + }; let foreign_items = fm.items.iter() .map(|it| self.tcx.hir.local_def_id(it.id)) .collect(); diff --git a/src/test/compile-fail/issue-43925.rs b/src/test/compile-fail/issue-43925.rs new file mode 100644 index 00000000000..8ad57647290 --- /dev/null +++ b/src/test/compile-fail/issue-43925.rs @@ -0,0 +1,16 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(attr_literals)] + +#[link(name="foo", cfg("rlib"))] //~ ERROR invalid argument for `cfg(..)` +extern {} + +fn main() {} diff --git a/src/test/compile-fail/issue-43926.rs b/src/test/compile-fail/issue-43926.rs new file mode 100644 index 00000000000..5d510b643a3 --- /dev/null +++ b/src/test/compile-fail/issue-43926.rs @@ -0,0 +1,14 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[link(name="foo", cfg())] //~ ERROR `cfg()` must have an argument +extern {} + +fn main() {} From e14720bc7b9f9be8bb072cfdda2b41bdabd9c6e1 Mon Sep 17 00:00:00 2001 From: Christopher Vittal Date: Fri, 12 Jan 2018 10:37:55 -0500 Subject: [PATCH 05/22] remove unnecessary compile-flags comments --- src/test/ui/nll/borrowed-referent-issue-38899.rs | 2 -- src/test/ui/nll/borrowed-referent-issue-38899.stderr | 8 ++++---- src/test/ui/nll/return-ref-mut-issue-46557.rs | 2 -- src/test/ui/nll/return-ref-mut-issue-46557.stderr | 8 ++++---- 4 files changed, 8 insertions(+), 12 deletions(-) diff --git a/src/test/ui/nll/borrowed-referent-issue-38899.rs b/src/test/ui/nll/borrowed-referent-issue-38899.rs index 1057e4ef6f3..d7c15851418 100644 --- a/src/test/ui/nll/borrowed-referent-issue-38899.rs +++ b/src/test/ui/nll/borrowed-referent-issue-38899.rs @@ -10,8 +10,6 @@ // Regression test for issue #38899 -// compile-flags:-Znll -Zborrowck=mir -Zverbose - #![feature(nll)] #![allow(dead_code)] diff --git a/src/test/ui/nll/borrowed-referent-issue-38899.stderr b/src/test/ui/nll/borrowed-referent-issue-38899.stderr index ff23880e709..3031fec2d0b 100644 --- a/src/test/ui/nll/borrowed-referent-issue-38899.stderr +++ b/src/test/ui/nll/borrowed-referent-issue-38899.stderr @@ -1,10 +1,10 @@ error[E0502]: cannot borrow `*block.current` as immutable because it is also borrowed as mutable - --> $DIR/borrowed-referent-issue-38899.rs:26:21 + --> $DIR/borrowed-referent-issue-38899.rs:24:21 | -24 | let x = &mut block; +22 | let x = &mut block; | ---------- mutable borrow occurs here -25 | println!("{}", x.current); -26 | let p: &'a u8 = &*block.current; +23 | println!("{}", x.current); +24 | let p: &'a u8 = &*block.current; | ^^^^^^^^^^^^^^^ immutable borrow occurs here error: aborting due to previous error diff --git a/src/test/ui/nll/return-ref-mut-issue-46557.rs b/src/test/ui/nll/return-ref-mut-issue-46557.rs index ee00e703f05..79150f340ca 100644 --- a/src/test/ui/nll/return-ref-mut-issue-46557.rs +++ b/src/test/ui/nll/return-ref-mut-issue-46557.rs @@ -10,8 +10,6 @@ // Regression test for issue #46557 -// compile-flags:-Znll -Zborrowck=mir -Zverbose - #![feature(nll)] #![allow(dead_code)] diff --git a/src/test/ui/nll/return-ref-mut-issue-46557.stderr b/src/test/ui/nll/return-ref-mut-issue-46557.stderr index c5b5cf75813..763e2bfd892 100644 --- a/src/test/ui/nll/return-ref-mut-issue-46557.stderr +++ b/src/test/ui/nll/return-ref-mut-issue-46557.stderr @@ -1,10 +1,10 @@ error[E0597]: borrowed value does not live long enough - --> $DIR/return-ref-mut-issue-46557.rs:19:21 + --> $DIR/return-ref-mut-issue-46557.rs:17:21 | -19 | let ref mut x = 1234543; //~ ERROR borrowed value does not live long enough [E0597] +17 | let ref mut x = 1234543; //~ ERROR borrowed value does not live long enough [E0597] | ^^^^^^^ temporary value does not live long enough -20 | x -21 | } +18 | x +19 | } | - temporary value only lives until here | = note: borrowed value must be valid for lifetime '_#2r... From cee295e8af297bfeb5a53512ff0cb02d192378e2 Mon Sep 17 00:00:00 2001 From: Bulat Musin <9249387+bmusin@users.noreply.github.com> Date: Wed, 10 Jan 2018 20:23:01 +0300 Subject: [PATCH 06/22] fix off-by-one error --- src/libstd/io/buffered.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libstd/io/buffered.rs b/src/libstd/io/buffered.rs index 7001d8e0421..bc1f3bb9a99 100644 --- a/src/libstd/io/buffered.rs +++ b/src/libstd/io/buffered.rs @@ -316,8 +316,8 @@ impl Seek for BufReader { /// /// let mut stream = TcpStream::connect("127.0.0.1:34254").unwrap(); /// -/// for i in 1..10 { -/// stream.write(&[i]).unwrap(); +/// for i in 0..10 { +/// stream.write(&[i+1]).unwrap(); /// } /// ``` /// @@ -332,8 +332,8 @@ impl Seek for BufReader { /// /// let mut stream = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap()); /// -/// for i in 1..10 { -/// stream.write(&[i]).unwrap(); +/// for i in 0..10 { +/// stream.write(&[i+1]).unwrap(); /// } /// ``` /// From 51d546f4aa8a94b81d2a580518d95d1ab12a3655 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Tue, 2 Jan 2018 02:13:20 +0200 Subject: [PATCH 07/22] Add slice::ExactChunks and ::ExactChunksMut iterators These guarantee that always the requested slice size will be returned and any leftoever elements at the end will be ignored. It allows llvm to get rid of bounds checks in the code using the iterator. This is inspired by the same iterators provided by ndarray. See https://github.com/rust-lang/rust/issues/47115 --- src/liballoc/lib.rs | 1 + src/liballoc/slice.rs | 56 ++++++++++ src/libcore/slice/mod.rs | 230 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 287 insertions(+) diff --git a/src/liballoc/lib.rs b/src/liballoc/lib.rs index 3cc3ea46796..d8ce28695ab 100644 --- a/src/liballoc/lib.rs +++ b/src/liballoc/lib.rs @@ -124,6 +124,7 @@ #![feature(unsize)] #![feature(allocator_internals)] #![feature(on_unimplemented)] +#![feature(exact_chunks)] #![cfg_attr(not(test), feature(fused, fn_traits, placement_new_protocol, swap_with_slice, i128))] #![cfg_attr(test, feature(test, box_heap))] diff --git a/src/liballoc/slice.rs b/src/liballoc/slice.rs index fa73197885b..bae36673637 100644 --- a/src/liballoc/slice.rs +++ b/src/liballoc/slice.rs @@ -123,6 +123,8 @@ pub use core::slice::{from_raw_parts, from_raw_parts_mut}; pub use core::slice::{from_ref, from_ref_mut}; #[unstable(feature = "slice_get_slice", issue = "35729")] pub use core::slice::SliceIndex; +#[unstable(feature = "exact_chunks", issue = "47115")] +pub use core::slice::{ExactChunks, ExactChunksMut}; //////////////////////////////////////////////////////////////////////////////// // Basic slice extension methods @@ -631,6 +633,31 @@ impl [T] { core_slice::SliceExt::chunks(self, chunk_size) } + /// Returns an iterator over `chunk_size` elements of the slice at a + /// time. The chunks are slices and do not overlap. If `chunk_size` does + /// not divide the length of the slice, then the last up to `chunk_size-1` + /// elements will be omitted. + /// + /// # Panics + /// + /// Panics if `chunk_size` is 0. + /// + /// # Examples + /// + /// ``` + /// let slice = ['l', 'o', 'r', 'e', 'm']; + /// let mut iter = slice.chunks(2); + /// assert_eq!(iter.next().unwrap(), &['l', 'o']); + /// assert_eq!(iter.next().unwrap(), &['r', 'e']); + /// assert_eq!(iter.next().unwrap(), &['m']); + /// assert!(iter.next().is_none()); + /// ``` + #[unstable(feature = "exact_chunks", issue = "47115")] + #[inline] + pub fn exact_chunks(&self, chunk_size: usize) -> ExactChunks { + core_slice::SliceExt::exact_chunks(self, chunk_size) + } + /// Returns an iterator over `chunk_size` elements of the slice at a time. /// The chunks are mutable slices, and do not overlap. If `chunk_size` does /// not divide the length of the slice, then the last chunk will not @@ -660,6 +687,35 @@ impl [T] { core_slice::SliceExt::chunks_mut(self, chunk_size) } + /// Returns an iterator over `chunk_size` elements of the slice at a time. + /// The chunks are mutable slices, and do not overlap. If `chunk_size` does + /// not divide the length of the slice, then the last up to `chunk_size-1` + /// elements will be omitted. + /// + /// # Panics + /// + /// Panics if `chunk_size` is 0. + /// + /// # Examples + /// + /// ``` + /// let v = &mut [0, 0, 0, 0, 0]; + /// let mut count = 1; + /// + /// for chunk in v.exact_chunks_mut(2) { + /// for elem in chunk.iter_mut() { + /// *elem += count; + /// } + /// count += 1; + /// } + /// assert_eq!(v, &[1, 1, 2, 2, 3]); + /// ``` + #[unstable(feature = "exact_chunks", issue = "47115")] + #[inline] + pub fn exact_chunks_mut(&mut self, chunk_size: usize) -> ExactChunksMut { + core_slice::SliceExt::exact_chunks_mut(self, chunk_size) + } + /// Divides one slice into two at an index. /// /// The first will contain all indices from `[0, mid)` (excluding diff --git a/src/libcore/slice/mod.rs b/src/libcore/slice/mod.rs index 72036d6d3a2..5791b1d6e79 100644 --- a/src/libcore/slice/mod.rs +++ b/src/libcore/slice/mod.rs @@ -104,6 +104,9 @@ pub trait SliceExt { #[stable(feature = "core", since = "1.6.0")] fn chunks(&self, size: usize) -> Chunks; + #[unstable(feature = "exact_chunks", issue = "47115")] + fn exact_chunks(&self, size: usize) -> ExactChunks; + #[stable(feature = "core", since = "1.6.0")] fn get(&self, index: I) -> Option<&I::Output> where I: SliceIndex; @@ -181,6 +184,9 @@ pub trait SliceExt { #[stable(feature = "core", since = "1.6.0")] fn chunks_mut(&mut self, chunk_size: usize) -> ChunksMut; + #[unstable(feature = "exact_chunks", issue = "47115")] + fn exact_chunks_mut(&mut self, size: usize) -> ExactChunksMut; + #[stable(feature = "core", since = "1.6.0")] fn swap(&mut self, a: usize, b: usize); @@ -353,6 +359,14 @@ impl SliceExt for [T] { Chunks { v: self, chunk_size: chunk_size } } + #[inline] + fn exact_chunks(&self, chunk_size: usize) -> ExactChunks { + assert!(chunk_size != 0); + let rem = self.len() % chunk_size; + let len = self.len() - rem; + ExactChunks { v: &self[..len], chunk_size: chunk_size} + } + #[inline] fn get(&self, index: I) -> Option<&I::Output> where I: SliceIndex<[T]> @@ -536,6 +550,14 @@ impl SliceExt for [T] { ChunksMut { v: self, chunk_size: chunk_size } } + #[inline] + fn exact_chunks_mut(&mut self, chunk_size: usize) -> ExactChunksMut { + assert!(chunk_size != 0); + let rem = self.len() % chunk_size; + let len = self.len() - rem; + ExactChunksMut { v: &mut self[..len], chunk_size: chunk_size} + } + #[inline] fn swap(&mut self, a: usize, b: usize) { unsafe { @@ -2365,6 +2387,214 @@ unsafe impl<'a, T> TrustedRandomAccess for ChunksMut<'a, T> { fn may_have_side_effect() -> bool { false } } +/// An iterator over a slice in (non-overlapping) chunks (`chunk_size` elements at a +/// time). +/// +/// When the slice len is not evenly divided by the chunk size, the last +/// up to `chunk_size-1` elements will be omitted. +/// +/// This struct is created by the [`exact_chunks`] method on [slices]. +/// +/// [`exact_chunks`]: ../../std/primitive.slice.html#method.exact_chunks +/// [slices]: ../../std/primitive.slice.html +#[derive(Debug)] +#[unstable(feature = "exact_chunks", issue = "47115")] +pub struct ExactChunks<'a, T:'a> { + v: &'a [T], + chunk_size: usize +} + +// FIXME(#26925) Remove in favor of `#[derive(Clone)]` +#[unstable(feature = "exact_chunks", issue = "47115")] +impl<'a, T> Clone for ExactChunks<'a, T> { + fn clone(&self) -> ExactChunks<'a, T> { + ExactChunks { + v: self.v, + chunk_size: self.chunk_size, + } + } +} + +#[unstable(feature = "exact_chunks", issue = "47115")] +impl<'a, T> Iterator for ExactChunks<'a, T> { + type Item = &'a [T]; + + #[inline] + fn next(&mut self) -> Option<&'a [T]> { + if self.v.len() < self.chunk_size { + None + } else { + let (fst, snd) = self.v.split_at(self.chunk_size); + self.v = snd; + Some(fst) + } + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + let n = self.v.len() / self.chunk_size; + (n, Some(n)) + } + + #[inline] + fn count(self) -> usize { + self.len() + } + + #[inline] + fn nth(&mut self, n: usize) -> Option { + let (start, overflow) = n.overflowing_mul(self.chunk_size); + if start >= self.v.len() || overflow { + self.v = &[]; + None + } else { + let end = match start.checked_add(self.chunk_size) { + Some(sum) => cmp::min(self.v.len(), sum), + None => self.v.len(), + }; + + if end - start != self.chunk_size { + self.v = &[]; + None + } else { + let nth = &self.v[start..end]; + self.v = &self.v[end..]; + Some(nth) + } + } + } + + #[inline] + fn last(self) -> Option { + if self.v.len() < self.chunk_size { + None + } else { + let start = self.v.len() - self.chunk_size; + Some(&self.v[start..]) + } + } +} + +#[unstable(feature = "exact_chunks", issue = "47115")] +impl<'a, T> DoubleEndedIterator for ExactChunks<'a, T> { + #[inline] + fn next_back(&mut self) -> Option<&'a [T]> { + if self.v.len() < self.chunk_size { + None + } else { + let (fst, snd) = self.v.split_at(self.v.len() - self.chunk_size); + self.v = fst; + Some(snd) + } + } +} + +#[unstable(feature = "exact_chunks", issue = "47115")] +impl<'a, T> ExactSizeIterator for ExactChunks<'a, T> {} + +#[unstable(feature = "fused", issue = "35602")] +impl<'a, T> FusedIterator for ExactChunks<'a, T> {} + +/// An iterator over a slice in (non-overlapping) mutable chunks (`chunk_size` +/// elements at a time). When the slice len is not evenly divided by the chunk +/// size, the last up to `chunk_size-1` elements will be omitted. +/// +/// This struct is created by the [`exact_chunks_mut`] method on [slices]. +/// +/// [`exact_chunks_mut`]: ../../std/primitive.slice.html#method.exact_chunks_mut +/// [slices]: ../../std/primitive.slice.html +#[derive(Debug)] +#[unstable(feature = "exact_chunks", issue = "47115")] +pub struct ExactChunksMut<'a, T:'a> { + v: &'a mut [T], + chunk_size: usize +} + +#[unstable(feature = "exact_chunks", issue = "47115")] +impl<'a, T> Iterator for ExactChunksMut<'a, T> { + type Item = &'a mut [T]; + + #[inline] + fn next(&mut self) -> Option<&'a mut [T]> { + if self.v.len() < self.chunk_size { + None + } else { + let tmp = mem::replace(&mut self.v, &mut []); + let (head, tail) = tmp.split_at_mut(self.chunk_size); + self.v = tail; + Some(head) + } + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + let n = self.v.len() / self.chunk_size; + (n, Some(n)) + } + + #[inline] + fn count(self) -> usize { + self.len() + } + + #[inline] + fn nth(&mut self, n: usize) -> Option<&'a mut [T]> { + let (start, overflow) = n.overflowing_mul(self.chunk_size); + if start >= self.v.len() || overflow { + self.v = &mut []; + None + } else { + let end = match start.checked_add(self.chunk_size) { + Some(sum) => cmp::min(self.v.len(), sum), + None => self.v.len(), + }; + + if end - start != self.chunk_size { + self.v = &mut []; + None + } else { + let tmp = mem::replace(&mut self.v, &mut []); + let (head, tail) = tmp.split_at_mut(end); + let (_, nth) = head.split_at_mut(start); + self.v = tail; + Some(nth) + } + } + } + + #[inline] + fn last(self) -> Option { + if self.v.len() < self.chunk_size { + None + } else { + let start = (self.v.len() - self.chunk_size) / self.chunk_size * self.chunk_size; + Some(&mut self.v[start..]) + } + } +} + +#[unstable(feature = "exact_chunks", issue = "47115")] +impl<'a, T> DoubleEndedIterator for ExactChunksMut<'a, T> { + #[inline] + fn next_back(&mut self) -> Option<&'a mut [T]> { + if self.v.len() < self.chunk_size { + None + } else { + let tmp = mem::replace(&mut self.v, &mut []); + let tmp_len = tmp.len(); + let (head, tail) = tmp.split_at_mut(tmp_len - self.chunk_size); + self.v = head; + Some(tail) + } + } +} + +#[unstable(feature = "exact_chunks", issue = "47115")] +impl<'a, T> ExactSizeIterator for ExactChunksMut<'a, T> {} + +#[unstable(feature = "fused", issue = "35602")] +impl<'a, T> FusedIterator for ExactChunksMut<'a, T> {} + // // Free functions // From 83396fc712aefd24531f7925d377b148da8ed04f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Tue, 2 Jan 2018 14:54:18 +0200 Subject: [PATCH 08/22] Add #![feature(exact_chunks)] to the documentation examples to fix the doc tests --- src/liballoc/slice.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/liballoc/slice.rs b/src/liballoc/slice.rs index bae36673637..6a3970f3728 100644 --- a/src/liballoc/slice.rs +++ b/src/liballoc/slice.rs @@ -645,6 +645,8 @@ impl [T] { /// # Examples /// /// ``` + /// #![feature(exact_chunks)] + /// /// let slice = ['l', 'o', 'r', 'e', 'm']; /// let mut iter = slice.chunks(2); /// assert_eq!(iter.next().unwrap(), &['l', 'o']); @@ -699,6 +701,8 @@ impl [T] { /// # Examples /// /// ``` + /// #![feature(exact_chunks)] + /// /// let v = &mut [0, 0, 0, 0, 0]; /// let mut count = 1; /// From 802ba9ea5b432d36444d8da8646bd2a1624b9dfa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Tue, 2 Jan 2018 14:55:25 +0200 Subject: [PATCH 09/22] Fix assertions in examples of the exact_chunk() documentation --- src/liballoc/slice.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/liballoc/slice.rs b/src/liballoc/slice.rs index 6a3970f3728..9ff39363fd7 100644 --- a/src/liballoc/slice.rs +++ b/src/liballoc/slice.rs @@ -651,7 +651,6 @@ impl [T] { /// let mut iter = slice.chunks(2); /// assert_eq!(iter.next().unwrap(), &['l', 'o']); /// assert_eq!(iter.next().unwrap(), &['r', 'e']); - /// assert_eq!(iter.next().unwrap(), &['m']); /// assert!(iter.next().is_none()); /// ``` #[unstable(feature = "exact_chunks", issue = "47115")] @@ -712,7 +711,7 @@ impl [T] { /// } /// count += 1; /// } - /// assert_eq!(v, &[1, 1, 2, 2, 3]); + /// assert_eq!(v, &[1, 1, 2, 2]); /// ``` #[unstable(feature = "exact_chunks", issue = "47115")] #[inline] From e51a89a0ad35f42af9c39d4ada25a5ad9746cd62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Tue, 2 Jan 2018 19:21:39 +0200 Subject: [PATCH 10/22] Fix doctests for slice::exact_chunks() for real --- src/liballoc/slice.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/liballoc/slice.rs b/src/liballoc/slice.rs index 9ff39363fd7..182e9b0a00e 100644 --- a/src/liballoc/slice.rs +++ b/src/liballoc/slice.rs @@ -648,7 +648,7 @@ impl [T] { /// #![feature(exact_chunks)] /// /// let slice = ['l', 'o', 'r', 'e', 'm']; - /// let mut iter = slice.chunks(2); + /// let mut iter = slice.exact_chunks(2); /// assert_eq!(iter.next().unwrap(), &['l', 'o']); /// assert_eq!(iter.next().unwrap(), &['r', 'e']); /// assert!(iter.next().is_none()); @@ -711,7 +711,7 @@ impl [T] { /// } /// count += 1; /// } - /// assert_eq!(v, &[1, 1, 2, 2]); + /// assert_eq!(v, &[1, 1, 2, 2, 0]); /// ``` #[unstable(feature = "exact_chunks", issue = "47115")] #[inline] From aa0c08a22c32da5cc1db16fba2e5e6b762a47de5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Tue, 2 Jan 2018 23:54:42 +0200 Subject: [PATCH 11/22] Apply review comments from @bluss - Simplify nth() by making use of the fact that the slice is evenly divisible by the chunk size, and calling next() instead of duplicating it - Call next_back() in last(), they are equivalent - Implement ExactSizeIterator::is_empty() --- src/libcore/slice/mod.rs | 67 ++++++++++++++-------------------------- 1 file changed, 23 insertions(+), 44 deletions(-) diff --git a/src/libcore/slice/mod.rs b/src/libcore/slice/mod.rs index 5791b1d6e79..2d606f43084 100644 --- a/src/libcore/slice/mod.rs +++ b/src/libcore/slice/mod.rs @@ -2448,30 +2448,16 @@ impl<'a, T> Iterator for ExactChunks<'a, T> { self.v = &[]; None } else { - let end = match start.checked_add(self.chunk_size) { - Some(sum) => cmp::min(self.v.len(), sum), - None => self.v.len(), - }; - - if end - start != self.chunk_size { - self.v = &[]; - None - } else { - let nth = &self.v[start..end]; - self.v = &self.v[end..]; - Some(nth) - } + let (_, snd) = self.v.split_at(start); + self.v = snd; + assert!(self.v.len() == self.chunk_size); + self.next() } } #[inline] - fn last(self) -> Option { - if self.v.len() < self.chunk_size { - None - } else { - let start = self.v.len() - self.chunk_size; - Some(&self.v[start..]) - } + fn last(mut self) -> Option { + self.next_back() } } @@ -2490,7 +2476,11 @@ impl<'a, T> DoubleEndedIterator for ExactChunks<'a, T> { } #[unstable(feature = "exact_chunks", issue = "47115")] -impl<'a, T> ExactSizeIterator for ExactChunks<'a, T> {} +impl<'a, T> ExactSizeIterator for ExactChunks<'a, T> { + fn is_empty(&self) -> bool { + self.v.is_empty() + } +} #[unstable(feature = "fused", issue = "35602")] impl<'a, T> FusedIterator for ExactChunks<'a, T> {} @@ -2544,32 +2534,17 @@ impl<'a, T> Iterator for ExactChunksMut<'a, T> { self.v = &mut []; None } else { - let end = match start.checked_add(self.chunk_size) { - Some(sum) => cmp::min(self.v.len(), sum), - None => self.v.len(), - }; - - if end - start != self.chunk_size { - self.v = &mut []; - None - } else { - let tmp = mem::replace(&mut self.v, &mut []); - let (head, tail) = tmp.split_at_mut(end); - let (_, nth) = head.split_at_mut(start); - self.v = tail; - Some(nth) - } + let tmp = mem::replace(&mut self.v, &mut []); + let (_, snd) = tmp.split_at_mut(start); + self.v = snd; + assert!(self.v.len() == self.chunk_size); + self.next() } } #[inline] - fn last(self) -> Option { - if self.v.len() < self.chunk_size { - None - } else { - let start = (self.v.len() - self.chunk_size) / self.chunk_size * self.chunk_size; - Some(&mut self.v[start..]) - } + fn last(mut self) -> Option { + self.next_back() } } @@ -2590,7 +2565,11 @@ impl<'a, T> DoubleEndedIterator for ExactChunksMut<'a, T> { } #[unstable(feature = "exact_chunks", issue = "47115")] -impl<'a, T> ExactSizeIterator for ExactChunksMut<'a, T> {} +impl<'a, T> ExactSizeIterator for ExactChunksMut<'a, T> { + fn is_empty(&self) -> bool { + self.v.is_empty() + } +} #[unstable(feature = "fused", issue = "35602")] impl<'a, T> FusedIterator for ExactChunksMut<'a, T> {} From cea36f447ea746397003f1ee98ee41e858b1951b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Wed, 3 Jan 2018 00:05:13 +0200 Subject: [PATCH 12/22] Remove useless assertion --- src/libcore/slice/mod.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/libcore/slice/mod.rs b/src/libcore/slice/mod.rs index 2d606f43084..c61d17290c8 100644 --- a/src/libcore/slice/mod.rs +++ b/src/libcore/slice/mod.rs @@ -2450,7 +2450,6 @@ impl<'a, T> Iterator for ExactChunks<'a, T> { } else { let (_, snd) = self.v.split_at(start); self.v = snd; - assert!(self.v.len() == self.chunk_size); self.next() } } @@ -2537,7 +2536,6 @@ impl<'a, T> Iterator for ExactChunksMut<'a, T> { let tmp = mem::replace(&mut self.v, &mut []); let (_, snd) = tmp.split_at_mut(start); self.v = snd; - assert!(self.v.len() == self.chunk_size); self.next() } } From 6bf1dfdd76625ba753de0d1b87a2825606cf6f2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Wed, 3 Jan 2018 01:01:40 +0200 Subject: [PATCH 13/22] Implement TrustedRandomAccess for slice::{ExactChunks, ExactChunksMut} --- src/libcore/slice/mod.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/libcore/slice/mod.rs b/src/libcore/slice/mod.rs index c61d17290c8..9379a064a6c 100644 --- a/src/libcore/slice/mod.rs +++ b/src/libcore/slice/mod.rs @@ -2484,6 +2484,15 @@ impl<'a, T> ExactSizeIterator for ExactChunks<'a, T> { #[unstable(feature = "fused", issue = "35602")] impl<'a, T> FusedIterator for ExactChunks<'a, T> {} +#[doc(hidden)] +unsafe impl<'a, T> TrustedRandomAccess for ExactChunks<'a, T> { + unsafe fn get_unchecked(&mut self, i: usize) -> &'a [T] { + let start = i * self.chunk_size; + from_raw_parts(self.v.as_ptr().offset(start as isize), self.chunk_size) + } + fn may_have_side_effect() -> bool { false } +} + /// An iterator over a slice in (non-overlapping) mutable chunks (`chunk_size` /// elements at a time). When the slice len is not evenly divided by the chunk /// size, the last up to `chunk_size-1` elements will be omitted. @@ -2572,6 +2581,15 @@ impl<'a, T> ExactSizeIterator for ExactChunksMut<'a, T> { #[unstable(feature = "fused", issue = "35602")] impl<'a, T> FusedIterator for ExactChunksMut<'a, T> {} +#[doc(hidden)] +unsafe impl<'a, T> TrustedRandomAccess for ExactChunksMut<'a, T> { + unsafe fn get_unchecked(&mut self, i: usize) -> &'a mut [T] { + let start = i * self.chunk_size; + from_raw_parts_mut(self.v.as_mut_ptr().offset(start as isize), self.chunk_size) + } + fn may_have_side_effect() -> bool { false } +} + // // Free functions // From 8a82e8e89f6eec3744f12d45c12ce05d48233d8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Tue, 9 Jan 2018 22:58:41 +0200 Subject: [PATCH 14/22] Mention in the exact_chunks docs that this can often be optimized better by the compiler And also link from the normal chunks iterator to the exact_chunks one. --- src/liballoc/slice.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/liballoc/slice.rs b/src/liballoc/slice.rs index 182e9b0a00e..f8980b8777e 100644 --- a/src/liballoc/slice.rs +++ b/src/liballoc/slice.rs @@ -613,6 +613,9 @@ impl [T] { /// not divide the length of the slice, then the last chunk will /// not have length `chunk_size`. /// + /// See [`exact_chunks`] for a variant of this iterator that returns chunks + /// of always exactly `chunk_size` elements. + /// /// # Panics /// /// Panics if `chunk_size` is 0. @@ -638,6 +641,10 @@ impl [T] { /// not divide the length of the slice, then the last up to `chunk_size-1` /// elements will be omitted. /// + /// Due to each chunk having exactly `chunk_size` elements, the compiler + /// can often optimize the resulting code better than in the case of + /// [`chunks`]. + /// /// # Panics /// /// Panics if `chunk_size` is 0. @@ -664,6 +671,9 @@ impl [T] { /// not divide the length of the slice, then the last chunk will not /// have length `chunk_size`. /// + /// See [`exact_chunks_mut`] for a variant of this iterator that returns chunks + /// of always exactly `chunk_size` elements. + /// /// # Panics /// /// Panics if `chunk_size` is 0. @@ -693,6 +703,11 @@ impl [T] { /// not divide the length of the slice, then the last up to `chunk_size-1` /// elements will be omitted. /// + /// + /// Due to each chunk having exactly `chunk_size` elements, the compiler + /// can often optimize the resulting code better than in the case of + /// [`chunks_mut`]. + /// /// # Panics /// /// Panics if `chunk_size` is 0. From baa81dc77f0a46b35c6b103cba79334d87622f2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Thu, 11 Jan 2018 12:11:32 +0200 Subject: [PATCH 15/22] Use assert_eq!() instead of assert!(a == b) in slice chunks_mut() unit test This way more useful information is printed if the test ever fails. --- src/liballoc/tests/slice.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/liballoc/tests/slice.rs b/src/liballoc/tests/slice.rs index 85d5ce304b8..c2f7522c5f0 100644 --- a/src/liballoc/tests/slice.rs +++ b/src/liballoc/tests/slice.rs @@ -1124,7 +1124,7 @@ fn test_mut_chunks() { } } let result = [0, 0, 0, 1, 1, 1, 2]; - assert!(v == result); + assert_eq!(v, result); } #[test] @@ -1136,7 +1136,7 @@ fn test_mut_chunks_rev() { } } let result = [2, 2, 2, 1, 1, 1, 0]; - assert!(v == result); + assert_eq!(v, result); } #[test] From ed774838b372bf532a3fb221601bdcd1c8856277 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Thu, 11 Jan 2018 12:12:55 +0200 Subject: [PATCH 16/22] Test the whole chunks instead of just an element in the chunks/chunks_mut tests Easy enough to do and ensures that the whole chunk is as expected instead of just the element that was looked at before. --- src/libcore/tests/slice.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/libcore/tests/slice.rs b/src/libcore/tests/slice.rs index d6230e93f99..4f9fc30785c 100644 --- a/src/libcore/tests/slice.rs +++ b/src/libcore/tests/slice.rs @@ -117,12 +117,12 @@ fn test_chunks_count() { fn test_chunks_nth() { let v: &[i32] = &[0, 1, 2, 3, 4, 5]; let mut c = v.chunks(2); - assert_eq!(c.nth(1).unwrap()[1], 3); - assert_eq!(c.next().unwrap()[0], 4); + assert_eq!(c.nth(1).unwrap(), &[2, 3]); + assert_eq!(c.next().unwrap(), &[4, 5]); let v2: &[i32] = &[0, 1, 2, 3, 4]; let mut c2 = v2.chunks(3); - assert_eq!(c2.nth(1).unwrap()[1], 4); + assert_eq!(c2.nth(1).unwrap(), &[3, 4]); assert_eq!(c2.next(), None); } @@ -168,12 +168,12 @@ fn test_chunks_mut_count() { fn test_chunks_mut_nth() { let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5]; let mut c = v.chunks_mut(2); - assert_eq!(c.nth(1).unwrap()[1], 3); - assert_eq!(c.next().unwrap()[0], 4); + assert_eq!(c.nth(1).unwrap(), &[2, 3]); + assert_eq!(c.next().unwrap(), &[4, 5]); let v2: &mut [i32] = &mut [0, 1, 2, 3, 4]; let mut c2 = v2.chunks_mut(3); - assert_eq!(c2.nth(1).unwrap()[1], 4); + assert_eq!(c2.nth(1).unwrap(), &[3, 4]); assert_eq!(c2.next(), None); } @@ -181,11 +181,11 @@ fn test_chunks_mut_nth() { fn test_chunks_mut_last() { let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5]; let c = v.chunks_mut(2); - assert_eq!(c.last().unwrap()[1], 5); + assert_eq!(c.last().unwrap(), &[4, 5]); let v2: &mut [i32] = &mut [0, 1, 2, 3, 4]; let c2 = v2.chunks_mut(2); - assert_eq!(c2.last().unwrap()[0], 4); + assert_eq!(c2.last().unwrap(), &[4]); } #[test] From 5f4fc8214279f2ffadbf2cc3a4c22b748c17bd15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Thu, 11 Jan 2018 12:13:45 +0200 Subject: [PATCH 17/22] Add unit tests for exact_chunks/exact_chunks_mut These are basically modified copies of the chunks/chunks_mut tests. --- src/liballoc/tests/lib.rs | 1 + src/liballoc/tests/slice.rs | 56 +++++++++++++++++++ src/libcore/tests/lib.rs | 1 + src/libcore/tests/slice.rs | 104 ++++++++++++++++++++++++++++++++++++ 4 files changed, 162 insertions(+) diff --git a/src/liballoc/tests/lib.rs b/src/liballoc/tests/lib.rs index f1e95883b38..eee229bc6fd 100644 --- a/src/liballoc/tests/lib.rs +++ b/src/liballoc/tests/lib.rs @@ -30,6 +30,7 @@ #![feature(string_retain)] #![feature(unboxed_closures)] #![feature(unicode)] +#![feature(exact_chunks)] extern crate alloc_system; extern crate std_unicode; diff --git a/src/liballoc/tests/slice.rs b/src/liballoc/tests/slice.rs index c2f7522c5f0..5a3e5e6cb78 100644 --- a/src/liballoc/tests/slice.rs +++ b/src/liballoc/tests/slice.rs @@ -910,6 +910,30 @@ fn test_chunksator_0() { let _it = v.chunks(0); } +#[test] +fn test_exact_chunksator() { + let v = &[1, 2, 3, 4, 5]; + + assert_eq!(v.exact_chunks(2).len(), 2); + + let chunks: &[&[_]] = &[&[1, 2], &[3, 4]]; + assert_eq!(v.exact_chunks(2).collect::>(), chunks); + let chunks: &[&[_]] = &[&[1, 2, 3]]; + assert_eq!(v.exact_chunks(3).collect::>(), chunks); + let chunks: &[&[_]] = &[]; + assert_eq!(v.exact_chunks(6).collect::>(), chunks); + + let chunks: &[&[_]] = &[&[3, 4], &[1, 2]]; + assert_eq!(v.exact_chunks(2).rev().collect::>(), chunks); +} + +#[test] +#[should_panic] +fn test_exact_chunksator_0() { + let v = &[1, 2, 3, 4]; + let _it = v.exact_chunks(0); +} + #[test] fn test_reverse_part() { let mut values = [1, 2, 3, 4, 5]; @@ -1146,6 +1170,38 @@ fn test_mut_chunks_0() { let _it = v.chunks_mut(0); } +#[test] +fn test_mut_exact_chunks() { + let mut v = [0, 1, 2, 3, 4, 5, 6]; + assert_eq!(v.exact_chunks_mut(2).len(), 3); + for (i, chunk) in v.exact_chunks_mut(3).enumerate() { + for x in chunk { + *x = i as u8; + } + } + let result = [0, 0, 0, 1, 1, 1, 6]; + assert_eq!(v, result); +} + +#[test] +fn test_mut_exact_chunks_rev() { + let mut v = [0, 1, 2, 3, 4, 5, 6]; + for (i, chunk) in v.exact_chunks_mut(3).rev().enumerate() { + for x in chunk { + *x = i as u8; + } + } + let result = [1, 1, 1, 0, 0, 0, 6]; + assert_eq!(v, result); +} + +#[test] +#[should_panic] +fn test_mut_exact_chunks_0() { + let mut v = [1, 2, 3, 4]; + let _it = v.exact_chunks_mut(0); +} + #[test] fn test_mut_last() { let mut x = [1, 2, 3, 4, 5]; diff --git a/src/libcore/tests/lib.rs b/src/libcore/tests/lib.rs index c4b85b82981..2c0009569d7 100644 --- a/src/libcore/tests/lib.rs +++ b/src/libcore/tests/lib.rs @@ -42,6 +42,7 @@ #![feature(try_from)] #![feature(try_trait)] #![feature(unique)] +#![feature(exact_chunks)] extern crate core; extern crate test; diff --git a/src/libcore/tests/slice.rs b/src/libcore/tests/slice.rs index 4f9fc30785c..a89a88ac0af 100644 --- a/src/libcore/tests/slice.rs +++ b/src/libcore/tests/slice.rs @@ -202,6 +202,110 @@ fn test_chunks_mut_zip() { assert_eq!(v1, [13, 14, 19, 20, 14]); } +#[test] +fn test_exact_chunks_count() { + let v: &[i32] = &[0, 1, 2, 3, 4, 5]; + let c = v.exact_chunks(3); + assert_eq!(c.count(), 2); + + let v2: &[i32] = &[0, 1, 2, 3, 4]; + let c2 = v2.exact_chunks(2); + assert_eq!(c2.count(), 2); + + let v3: &[i32] = &[]; + let c3 = v3.exact_chunks(2); + assert_eq!(c3.count(), 0); +} + +#[test] +fn test_exact_chunks_nth() { + let v: &[i32] = &[0, 1, 2, 3, 4, 5]; + let mut c = v.exact_chunks(2); + assert_eq!(c.nth(1).unwrap(), &[2, 3]); + assert_eq!(c.next().unwrap(), &[4, 5]); + + let v2: &[i32] = &[0, 1, 2, 3, 4, 5, 6]; + let mut c2 = v2.exact_chunks(3); + assert_eq!(c2.nth(1).unwrap(), &[3, 4, 5]); + assert_eq!(c2.next(), None); +} + +#[test] +fn test_exact_chunks_last() { + let v: &[i32] = &[0, 1, 2, 3, 4, 5]; + let c = v.exact_chunks(2); + assert_eq!(c.last().unwrap(), &[4, 5]); + + let v2: &[i32] = &[0, 1, 2, 3, 4]; + let c2 = v2.exact_chunks(2); + assert_eq!(c2.last().unwrap(), &[2, 3]); +} + +#[test] +fn test_exact_chunks_zip() { + let v1: &[i32] = &[0, 1, 2, 3, 4]; + let v2: &[i32] = &[6, 7, 8, 9, 10]; + + let res = v1.exact_chunks(2) + .zip(v2.exact_chunks(2)) + .map(|(a, b)| a.iter().sum::() + b.iter().sum::()) + .collect::>(); + assert_eq!(res, vec![14, 22]); +} + +#[test] +fn test_exact_chunks_mut_count() { + let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5]; + let c = v.exact_chunks_mut(3); + assert_eq!(c.count(), 2); + + let v2: &mut [i32] = &mut [0, 1, 2, 3, 4]; + let c2 = v2.exact_chunks_mut(2); + assert_eq!(c2.count(), 2); + + let v3: &mut [i32] = &mut []; + let c3 = v3.exact_chunks_mut(2); + assert_eq!(c3.count(), 0); +} + +#[test] +fn test_exact_chunks_mut_nth() { + let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5]; + let mut c = v.exact_chunks_mut(2); + assert_eq!(c.nth(1).unwrap(), &[2, 3]); + assert_eq!(c.next().unwrap(), &[4, 5]); + + let v2: &mut [i32] = &mut [0, 1, 2, 3, 4, 5, 6]; + let mut c2 = v2.exact_chunks_mut(3); + assert_eq!(c2.nth(1).unwrap(), &[3, 4, 5]); + assert_eq!(c2.next(), None); +} + +#[test] +fn test_exact_chunks_mut_last() { + let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5]; + let c = v.exact_chunks_mut(2); + assert_eq!(c.last().unwrap(), &[4, 5]); + + let v2: &mut [i32] = &mut [0, 1, 2, 3, 4]; + let c2 = v2.exact_chunks_mut(2); + assert_eq!(c2.last().unwrap(), &[2, 3]); +} + +#[test] +fn test_exact_chunks_mut_zip() { + let v1: &mut [i32] = &mut [0, 1, 2, 3, 4]; + let v2: &[i32] = &[6, 7, 8, 9, 10]; + + for (a, b) in v1.exact_chunks_mut(2).zip(v2.exact_chunks(2)) { + let sum = b.iter().sum::(); + for v in a { + *v += sum; + } + } + assert_eq!(v1, [13, 14, 19, 20, 4]); +} + #[test] fn test_windows_count() { let v: &[i32] = &[0, 1, 2, 3, 4, 5]; From 38e2667584505a81fad8380f93d7de33578914fb Mon Sep 17 00:00:00 2001 From: est31 Date: Sat, 13 Jan 2018 14:45:58 +0100 Subject: [PATCH 18/22] Enforce dashes in the unstable book file names Also rename the existing underscore using files to use dashes. Fixes #47394. --- .../{crate_in_paths.md => crate-in-paths.md} | 0 ...absolute_paths.md => extern-absolute-paths.md} | 0 .../{extern_in_paths.md => extern-in-paths.md} | 0 ...ault_bindings.md => match-default-bindings.md} | 0 src/tools/tidy/src/unstable_book.rs | 4 ++-- src/tools/unstable-book-gen/src/main.rs | 15 +++++++++------ 6 files changed, 11 insertions(+), 8 deletions(-) rename src/doc/unstable-book/src/language-features/{crate_in_paths.md => crate-in-paths.md} (100%) rename src/doc/unstable-book/src/language-features/{extern_absolute_paths.md => extern-absolute-paths.md} (100%) rename src/doc/unstable-book/src/language-features/{extern_in_paths.md => extern-in-paths.md} (100%) rename src/doc/unstable-book/src/language-features/{match_default_bindings.md => match-default-bindings.md} (100%) diff --git a/src/doc/unstable-book/src/language-features/crate_in_paths.md b/src/doc/unstable-book/src/language-features/crate-in-paths.md similarity index 100% rename from src/doc/unstable-book/src/language-features/crate_in_paths.md rename to src/doc/unstable-book/src/language-features/crate-in-paths.md diff --git a/src/doc/unstable-book/src/language-features/extern_absolute_paths.md b/src/doc/unstable-book/src/language-features/extern-absolute-paths.md similarity index 100% rename from src/doc/unstable-book/src/language-features/extern_absolute_paths.md rename to src/doc/unstable-book/src/language-features/extern-absolute-paths.md diff --git a/src/doc/unstable-book/src/language-features/extern_in_paths.md b/src/doc/unstable-book/src/language-features/extern-in-paths.md similarity index 100% rename from src/doc/unstable-book/src/language-features/extern_in_paths.md rename to src/doc/unstable-book/src/language-features/extern-in-paths.md diff --git a/src/doc/unstable-book/src/language-features/match_default_bindings.md b/src/doc/unstable-book/src/language-features/match-default-bindings.md similarity index 100% rename from src/doc/unstable-book/src/language-features/match_default_bindings.md rename to src/doc/unstable-book/src/language-features/match-default-bindings.md diff --git a/src/tools/tidy/src/unstable_book.rs b/src/tools/tidy/src/unstable_book.rs index a4a35a706fd..6ffe78eab41 100644 --- a/src/tools/tidy/src/unstable_book.rs +++ b/src/tools/tidy/src/unstable_book.rs @@ -49,7 +49,7 @@ pub fn collect_unstable_feature_names(features: &Features) -> BTreeSet { features .iter() .filter(|&(_, ref f)| f.level == Status::Unstable) - .map(|(name, _)| name.to_owned()) + .map(|(name, _)| name.replace('_', "-")) .collect() } @@ -60,7 +60,7 @@ pub fn collect_unstable_book_section_file_names(dir: &path::Path) -> BTreeSet, dir: &str set .iter() .map(|ref n| format!(" - [{}]({}/{}.md)", - n, + n.replace('-', "_"), dir, - n.replace('_', "-"))) + n)) .fold("".to_owned(), |s, a| s + &a + "\n") } @@ -96,14 +96,17 @@ fn generate_unstable_book_files(src :&Path, out: &Path, features :&Features) { let unstable_section_file_names = collect_unstable_book_section_file_names(src); t!(fs::create_dir_all(&out)); for feature_name in &unstable_features - &unstable_section_file_names { - let file_name = format!("{}.md", feature_name.replace('_', "-")); + let feature_name_underscore = feature_name.replace('-', "_"); + let file_name = format!("{}.md", feature_name); let out_file_path = out.join(&file_name); - let feature = &features[&feature_name]; + let feature = &features[&feature_name_underscore]; if has_valid_tracking_issue(&feature) { - generate_stub_issue(&out_file_path, &feature_name, feature.tracking_issue.unwrap()); + generate_stub_issue(&out_file_path, + &feature_name_underscore, + feature.tracking_issue.unwrap()); } else { - generate_stub_no_issue(&out_file_path, &feature_name); + generate_stub_no_issue(&out_file_path, &feature_name_underscore); } } } From 52e074e40ee3eaf04415b2044c93bcfddad52f35 Mon Sep 17 00:00:00 2001 From: Clar Charr Date: Mon, 1 Jan 2018 21:56:22 -0500 Subject: [PATCH 19/22] Better Debug impl for io::Error. --- src/libstd/io/error.rs | 43 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 35 insertions(+), 8 deletions(-) diff --git a/src/libstd/io/error.rs b/src/libstd/io/error.rs index bb9383d3d6e..f0b41f30251 100644 --- a/src/libstd/io/error.rs +++ b/src/libstd/io/error.rs @@ -62,12 +62,18 @@ pub type Result = result::Result; /// [`Write`]: ../io/trait.Write.html /// [`Seek`]: ../io/trait.Seek.html /// [`ErrorKind`]: enum.ErrorKind.html -#[derive(Debug)] #[stable(feature = "rust1", since = "1.0.0")] pub struct Error { repr: Repr, } +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Debug for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Debug::fmt(&self.repr, f) + } +} + enum Repr { Os(i32), Simple(ErrorKind), @@ -511,10 +517,12 @@ impl Error { impl fmt::Debug for Repr { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { match *self { - Repr::Os(ref code) => - fmt.debug_struct("Os").field("code", code) - .field("message", &sys::os::error_string(*code)).finish(), - Repr::Custom(ref c) => fmt.debug_tuple("Custom").field(c).finish(), + Repr::Os(code) => + fmt.debug_struct("Os") + .field("code", &code) + .field("kind", &sys::decode_error_kind(code)) + .field("message", &sys::os::error_string(code)).finish(), + Repr::Custom(ref c) => fmt::Debug::fmt(&c, fmt), Repr::Simple(kind) => fmt.debug_tuple("Kind").field(&kind).finish(), } } @@ -559,17 +567,36 @@ fn _assert_error_is_sync_send() { #[cfg(test)] mod test { - use super::{Error, ErrorKind}; + use super::{Error, ErrorKind, Repr, Custom}; use error; use fmt; use sys::os::error_string; + use sys::decode_error_kind; #[test] fn test_debug_error() { let code = 6; let msg = error_string(code); - let err = Error { repr: super::Repr::Os(code) }; - let expected = format!("Error {{ repr: Os {{ code: {:?}, message: {:?} }} }}", code, msg); + let kind = decode_error_kind(code); + let err = Error { + repr: Repr::Custom(box Custom { + kind: ErrorKind::InvalidInput, + error: box Error { + repr: super::Repr::Os(code) + }, + }) + }; + let expected = format!( + "Custom {{ \ + kind: InvalidInput, \ + error: Os {{ \ + code: {:?}, \ + kind: {:?}, \ + message: {:?} \ + }} \ + }}", + code, kind, msg + ); assert_eq!(format!("{:?}", err), expected); } From 318cf22bd1955ba870a0a3189cb5dfdbce31b53e Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Sat, 13 Jan 2018 23:13:49 +0300 Subject: [PATCH 20/22] Move "no asm" check into AST validation --- src/librustc_driver/driver.rs | 6 +--- src/librustc_passes/ast_validation.rs | 3 ++ src/librustc_passes/lib.rs | 1 - src/librustc_passes/no_asm.rs | 47 --------------------------- 4 files changed, 4 insertions(+), 53 deletions(-) delete mode 100644 src/librustc_passes/no_asm.rs diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index b7265762208..75a7f990841 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -37,7 +37,7 @@ use rustc_typeck as typeck; use rustc_privacy; use rustc_plugin::registry::Registry; use rustc_plugin as plugin; -use rustc_passes::{self, ast_validation, no_asm, loops, consts, static_recursion, hir_stats}; +use rustc_passes::{self, ast_validation, loops, consts, static_recursion, hir_stats}; use rustc_const_eval::{self, check_match}; use super::Compilation; use ::DefaultTransCrate; @@ -856,10 +856,6 @@ pub fn phase_2_configure_and_expand(sess: &Session, println!("{}", json::as_json(&krate)); } - time(time_passes, - "checking for inline asm in case the target doesn't support it", - || no_asm::check_crate(sess, &krate)); - time(time_passes, "AST validation", || ast_validation::check_crate(sess, &krate)); diff --git a/src/librustc_passes/ast_validation.rs b/src/librustc_passes/ast_validation.rs index 61f54774163..dbad82ed977 100644 --- a/src/librustc_passes/ast_validation.rs +++ b/src/librustc_passes/ast_validation.rs @@ -149,6 +149,9 @@ impl<'a> Visitor<'a> for AstValidator<'a> { ExprKind::Continue(Some(ident)) => { self.check_label(ident.node, ident.span); } + ExprKind::InlineAsm(..) if !self.session.target.target.options.allow_asm => { + span_err!(self.session, expr.span, E0472, "asm! is unsupported on this target"); + } _ => {} } diff --git a/src/librustc_passes/lib.rs b/src/librustc_passes/lib.rs index 754c3bbd074..73c71ec0b2f 100644 --- a/src/librustc_passes/lib.rs +++ b/src/librustc_passes/lib.rs @@ -42,7 +42,6 @@ pub mod consts; pub mod hir_stats; pub mod loops; mod mir_stats; -pub mod no_asm; pub mod static_recursion; #[cfg(not(stage0))] // remove after the next snapshot diff --git a/src/librustc_passes/no_asm.rs b/src/librustc_passes/no_asm.rs deleted file mode 100644 index 4dbf57a99bc..00000000000 --- a/src/librustc_passes/no_asm.rs +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright 2015 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -/// Run over the whole crate and check for ExprInlineAsm. -/// Inline asm isn't allowed on virtual ISA based targets, so we reject it -/// here. - -use rustc::session::Session; - -use syntax::ast; -use syntax::visit::Visitor; -use syntax::visit; - -pub fn check_crate(sess: &Session, krate: &ast::Crate) { - if sess.target.target.options.allow_asm { - return; - } - - visit::walk_crate(&mut CheckNoAsm { sess: sess }, krate); -} - -#[derive(Copy, Clone)] -struct CheckNoAsm<'a> { - sess: &'a Session, -} - -impl<'a> Visitor<'a> for CheckNoAsm<'a> { - fn visit_expr(&mut self, e: &'a ast::Expr) { - match e.node { - ast::ExprKind::InlineAsm(_) => { - span_err!(self.sess, - e.span, - E0472, - "asm! is unsupported on this target") - } - _ => {} - } - visit::walk_expr(self, e) - } -} From 46c59aef6250cafdb5e845e03ab1664d1553f92f Mon Sep 17 00:00:00 2001 From: Ryan Cumming Date: Mon, 15 Jan 2018 08:09:55 +1100 Subject: [PATCH 21/22] Make ui-fulldeps/update-references executable When a ui-fulldeps comparison fails it suggests running update-references.sh: ``` src/test/ui-fulldeps/update-references.sh 'rust/build/x86_64-apple-darwin/test/ui-fulldeps' 'resolve-error.rs' ``` This does not work as update-references.sh isn't executable. The other update-references.sh in the ui directory is already executable so this looks like an oversight. --- src/test/ui-fulldeps/update-references.sh | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 src/test/ui-fulldeps/update-references.sh diff --git a/src/test/ui-fulldeps/update-references.sh b/src/test/ui-fulldeps/update-references.sh old mode 100644 new mode 100755 From 1482afbbedfbbd6920b8cf05bd23ba30a36630ce Mon Sep 17 00:00:00 2001 From: Fenrir Date: Sun, 14 Jan 2018 23:07:51 -0700 Subject: [PATCH 22/22] Remove leftover Rand stuff --- src/libstd/rand/reader.rs | 108 -------------------------------------- 1 file changed, 108 deletions(-) delete mode 100644 src/libstd/rand/reader.rs diff --git a/src/libstd/rand/reader.rs b/src/libstd/rand/reader.rs deleted file mode 100644 index 08bc809ed4d..00000000000 --- a/src/libstd/rand/reader.rs +++ /dev/null @@ -1,108 +0,0 @@ -// Copyright 2013 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! A wrapper around any Read to treat it as an RNG. - -#![allow(dead_code)] - -use io::prelude::*; -use rand::Rng; - -/// An RNG that reads random bytes straight from a `Read`. This will -/// work best with an infinite reader, but this is not required. -/// -/// # Panics -/// -/// It will panic if it there is insufficient data to fulfill a request. -pub struct ReaderRng { - reader: R -} - -impl ReaderRng { - /// Create a new `ReaderRng` from a `Read`. - pub fn new(r: R) -> ReaderRng { - ReaderRng { - reader: r - } - } -} - -impl Rng for ReaderRng { - fn next_u32(&mut self) -> u32 { - // This is designed for speed: reading a LE integer on a LE - // platform just involves blitting the bytes into the memory - // of the u32, similarly for BE on BE; avoiding byteswapping. - let mut bytes = [0; 4]; - self.fill_bytes(&mut bytes); - unsafe { *(bytes.as_ptr() as *const u32) } - } - fn next_u64(&mut self) -> u64 { - // see above for explanation. - let mut bytes = [0; 8]; - self.fill_bytes(&mut bytes); - unsafe { *(bytes.as_ptr() as *const u64) } - } - fn fill_bytes(&mut self, mut v: &mut [u8]) { - while !v.is_empty() { - let t = v; - match self.reader.read(t) { - Ok(0) => panic!("ReaderRng.fill_bytes: EOF reached"), - Ok(n) => v = t.split_at_mut(n).1, - Err(e) => panic!("ReaderRng.fill_bytes: {}", e), - } - } - } -} - -#[cfg(test)] -mod tests { - use super::ReaderRng; - use rand::Rng; - - #[test] - fn test_reader_rng_u64() { - // transmute from the target to avoid endianness concerns. - let v = &[0, 0, 0, 0, 0, 0, 0, 1, - 0, 0, 0, 0, 0, 0, 0, 2, - 0, 0, 0, 0, 0, 0, 0, 3][..]; - let mut rng = ReaderRng::new(v); - - assert_eq!(rng.next_u64(), 1u64.to_be()); - assert_eq!(rng.next_u64(), 2u64.to_be()); - assert_eq!(rng.next_u64(), 3u64.to_be()); - } - #[test] - fn test_reader_rng_u32() { - let v = &[0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 3][..]; - let mut rng = ReaderRng::new(v); - - assert_eq!(rng.next_u32(), 1u32.to_be()); - assert_eq!(rng.next_u32(), 2u32.to_be()); - assert_eq!(rng.next_u32(), 3u32.to_be()); - } - #[test] - fn test_reader_rng_fill_bytes() { - let v = [1, 2, 3, 4, 5, 6, 7, 8]; - let mut w = [0; 8]; - - let mut rng = ReaderRng::new(&v[..]); - rng.fill_bytes(&mut w); - - assert!(v == w); - } - - #[test] - #[should_panic] - fn test_reader_rng_insufficient_bytes() { - let mut rng = ReaderRng::new(&[][..]); - let mut v = [0; 3]; - rng.fill_bytes(&mut v); - } -}