about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2018-09-25 22:52:16 +0000
committerbors <bors@rust-lang.org>2018-09-25 22:52:16 +0000
commit92aff72a0503baa6cf0448555f649957de802b03 (patch)
tree7d88f60555c884da79b470c76d1afa299b195f74
parent4141a4079e3e6b2c4ac104fed042a9b7241467eb (diff)
parentcc9dea43be5ab201421b41dbb027f6ba36973cac (diff)
downloadrust-92aff72a0503baa6cf0448555f649957de802b03.tar.gz
rust-92aff72a0503baa6cf0448555f649957de802b03.zip
Auto merge of #54575 - pietroalbini:rollup, r=pietroalbini
Rollup of 12 pull requests

Successful merges:

 - #53518 (Add doc for impl From in char_convert)
 - #54058 (Introduce the partition_dedup/by/by_key methods for slices)
 - #54281 (Search box)
 - #54368 (Reduce code block sides padding)
 - #54498 (The project moved under the Mozilla umbrella)
 - #54518 (resolve: Do not block derive helper resolutions on single import resolutions)
 - #54522 (Fixed three small typos.)
 - #54529 (aarch64-pc-windows-msvc: Don't link libpanic_unwind to libtest.)
 - #54537 (Rename slice::exact_chunks() to slice::chunks_exact())
 - #54539 (Fix js error)
 - #54557 (incr.comp.: Don't automatically enable -Zshare-generics for incr. comp. builds.)
 - #54558 (Improvements to finding LLVM's FileCheck)

Failed merges:

r? @ghost
-rw-r--r--config.toml.example5
-rw-r--r--src/bootstrap/config.rs6
-rwxr-xr-xsrc/bootstrap/configure.py6
-rw-r--r--src/bootstrap/lib.rs23
-rw-r--r--src/doc/index.md22
-rw-r--r--src/doc/unstable-book/src/compiler-flags/profile.md2
-rw-r--r--src/liballoc/lib.rs3
-rw-r--r--src/liballoc/slice.rs4
-rw-r--r--src/liballoc/tests/lib.rs2
-rw-r--r--src/liballoc/tests/slice.rs30
-rw-r--r--src/liballoc/vec.rs100
-rw-r--r--src/libcore/char/convert.rs26
-rw-r--r--src/libcore/slice/mod.rs276
-rw-r--r--src/libcore/tests/lib.rs3
-rw-r--r--src/libcore/tests/slice.rs117
-rw-r--r--src/librustc/session/config.rs1
-rw-r--r--src/librustc_resolve/macros.rs20
-rw-r--r--src/librustdoc/html/static/main.js7
-rw-r--r--src/librustdoc/html/static/rustdoc.css2
-rw-r--r--src/libstd/primitive_docs.rs6
-rw-r--r--src/libtest/lib.rs10
-rw-r--r--src/test/compile-fail-fulldeps/proc-macro/proc-macro-attributes.rs6
-rw-r--r--src/test/ui-fulldeps/custom-derive/auxiliary/plugin.rs10
-rw-r--r--src/test/ui-fulldeps/custom-derive/helper-attr-blocked-by-import-ambig.rs13
-rw-r--r--src/test/ui-fulldeps/custom-derive/helper-attr-blocked-by-import-ambig.stderr20
-rw-r--r--src/test/ui-fulldeps/custom-derive/helper-attr-blocked-by-import.rs29
-rw-r--r--src/test/ui-fulldeps/proc-macro/derive-helper-shadowing.stderr13
27 files changed, 538 insertions, 224 deletions
diff --git a/config.toml.example b/config.toml.example
index 3159c19528a..66eaab236f7 100644
--- a/config.toml.example
+++ b/config.toml.example
@@ -322,6 +322,7 @@
 
 # Flag indicating whether codegen tests will be run or not. If you get an error
 # saying that the FileCheck executable is missing, you may want to disable this.
+# Also see the target's llvm-filecheck option.
 #codegen-tests = true
 
 # Flag indicating whether git info will be retrieved from .git automatically.
@@ -416,6 +417,10 @@
 # target.
 #llvm-config = "../path/to/llvm/root/bin/llvm-config"
 
+# Normally the build system can find LLVM's FileCheck utility, but if
+# not, you can specify an explicit file name for it.
+#llvm-filecheck = "/path/to/FileCheck"
+
 # Path to the custom jemalloc static library to link into the standard library
 # by default. This is only used if jemalloc is still enabled above
 #jemalloc = "/path/to/jemalloc/libjemalloc_pic.a"
diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs
index cc6d76c76f2..3a4bc526d03 100644
--- a/src/bootstrap/config.rs
+++ b/src/bootstrap/config.rs
@@ -162,6 +162,8 @@ pub struct Config {
 pub struct Target {
     /// Some(path to llvm-config) if using an external LLVM.
     pub llvm_config: Option<PathBuf>,
+    /// Some(path to FileCheck) if one was specified.
+    pub llvm_filecheck: Option<PathBuf>,
     pub jemalloc: Option<PathBuf>,
     pub cc: Option<PathBuf>,
     pub cxx: Option<PathBuf>,
@@ -330,6 +332,7 @@ struct Rust {
 #[serde(deny_unknown_fields, rename_all = "kebab-case")]
 struct TomlTarget {
     llvm_config: Option<String>,
+    llvm_filecheck: Option<String>,
     jemalloc: Option<String>,
     cc: Option<String>,
     cxx: Option<String>,
@@ -583,6 +586,9 @@ impl Config {
                 if let Some(ref s) = cfg.llvm_config {
                     target.llvm_config = Some(config.src.join(s));
                 }
+                if let Some(ref s) = cfg.llvm_filecheck {
+                    target.llvm_filecheck = Some(config.src.join(s));
+                }
                 if let Some(ref s) = cfg.jemalloc {
                     target.jemalloc = Some(config.src.join(s));
                 }
diff --git a/src/bootstrap/configure.py b/src/bootstrap/configure.py
index cf7f78eeba0..75831dbe262 100755
--- a/src/bootstrap/configure.py
+++ b/src/bootstrap/configure.py
@@ -95,6 +95,8 @@ v("docdir", "install.docdir", "install documentation in PATH")
 v("bindir", "install.bindir", "install binaries")
 
 v("llvm-root", None, "set LLVM root")
+v("llvm-config", None, "set path to llvm-config")
+v("llvm-filecheck", None, "set path to LLVM's FileCheck utility")
 v("python", "build.python", "set path to python")
 v("jemalloc-root", None, "set directory where libjemalloc_pic.a is located")
 v("android-cross-path", "target.arm-linux-androideabi.android-ndk",
@@ -323,6 +325,10 @@ for key in known_args:
         set('build.cargo', value + '/bin/cargo')
     elif option.name == 'llvm-root':
         set('target.{}.llvm-config'.format(build()), value + '/bin/llvm-config')
+    elif option.name == 'llvm-config':
+        set('target.{}.llvm-config'.format(build()), value)
+    elif option.name == 'llvm-filecheck':
+        set('target.{}.llvm-filecheck'.format(build()), value)
     elif option.name == 'jemalloc-root':
         set('target.{}.jemalloc'.format(build()), value + '/libjemalloc_pic.a')
     elif option.name == 'tools':
diff --git a/src/bootstrap/lib.rs b/src/bootstrap/lib.rs
index 6b88516bacd..75c18cd2dd4 100644
--- a/src/bootstrap/lib.rs
+++ b/src/bootstrap/lib.rs
@@ -641,9 +641,28 @@ impl Build {
     /// Returns the path to `FileCheck` binary for the specified target
     fn llvm_filecheck(&self, target: Interned<String>) -> PathBuf {
         let target_config = self.config.target_config.get(&target);
-        if let Some(s) = target_config.and_then(|c| c.llvm_config.as_ref()) {
+        if let Some(s) = target_config.and_then(|c| c.llvm_filecheck.as_ref()) {
+            s.to_path_buf()
+        } else if let Some(s) = target_config.and_then(|c| c.llvm_config.as_ref()) {
             let llvm_bindir = output(Command::new(s).arg("--bindir"));
-            Path::new(llvm_bindir.trim()).join(exe("FileCheck", &*target))
+            let filecheck = Path::new(llvm_bindir.trim()).join(exe("FileCheck", &*target));
+            if filecheck.exists() {
+                filecheck
+            } else {
+                // On Fedora the system LLVM installs FileCheck in the
+                // llvm subdirectory of the libdir.
+                let llvm_libdir = output(Command::new(s).arg("--libdir"));
+                let lib_filecheck = Path::new(llvm_libdir.trim())
+                    .join("llvm").join(exe("FileCheck", &*target));
+                if lib_filecheck.exists() {
+                    lib_filecheck
+                } else {
+                    // Return the most normal file name, even though
+                    // it doesn't exist, so that any error message
+                    // refers to that.
+                    filecheck
+                }
+            }
         } else {
             let base = self.llvm_out(self.config.build).join("build");
             let base = if !self.config.ninja && self.config.build.contains("msvc") {
diff --git a/src/doc/index.md b/src/doc/index.md
index b1788d8c32f..33ee76739c5 100644
--- a/src/doc/index.md
+++ b/src/doc/index.md
@@ -4,6 +4,23 @@
 nav {
     display: none;
 }
+#search-input {
+    width: calc(100% - 58px);
+}
+#search-but {
+    cursor: pointer;
+}
+#search-but, #search-input {
+    padding: 4px;
+    border: 1px solid #ccc;
+    border-radius: 3px;
+    outline: none;
+    font-size: 0.7em;
+    background-color: #fff;
+}
+#search-but:hover, #search-input:focus {
+    border-color: #55a9ff;
+}
 </style>
 
 Welcome to an overview of the documentation provided by the Rust project.
@@ -45,8 +62,9 @@ accomplishing various tasks.
 
 <div>
   <form action="std/index.html" method="get">
-    <input type="search" name="search"/>
-    <button>Search</button>
+    <input id="search-input" type="search" name="search"
+           placeholder="Search through the standard library"/>
+    <button id="search-but">Search</button>
   </form>
 </div>
 
diff --git a/src/doc/unstable-book/src/compiler-flags/profile.md b/src/doc/unstable-book/src/compiler-flags/profile.md
index 66d14fd0997..452aca51532 100644
--- a/src/doc/unstable-book/src/compiler-flags/profile.md
+++ b/src/doc/unstable-book/src/compiler-flags/profile.md
@@ -18,4 +18,4 @@ cargo run
 ```
 
 Once you've built and run your program, files with the `gcno` (after build) and `gcda` (after execution) extensions will be created.
-You can parse them with [llvm-cov gcov](http://llvm.org/docs/CommandGuide/llvm-cov.html#llvm-cov-gcov) or [grcov](https://github.com/marco-c/grcov).
+You can parse them with [llvm-cov gcov](https://llvm.org/docs/CommandGuide/llvm-cov.html#llvm-cov-gcov) or [grcov](https://github.com/mozilla/grcov).
diff --git a/src/liballoc/lib.rs b/src/liballoc/lib.rs
index 089480c06d2..7960936dad6 100644
--- a/src/liballoc/lib.rs
+++ b/src/liballoc/lib.rs
@@ -116,9 +116,10 @@
 #![feature(unsize)]
 #![feature(allocator_internals)]
 #![feature(on_unimplemented)]
-#![feature(exact_chunks)]
+#![feature(chunks_exact)]
 #![feature(rustc_const_unstable)]
 #![feature(const_vec_new)]
+#![feature(slice_partition_dedup)]
 #![feature(maybe_uninit)]
 
 // Allow testing this library
diff --git a/src/liballoc/slice.rs b/src/liballoc/slice.rs
index 6c0b1c33a1f..33d28bef2d7 100644
--- a/src/liballoc/slice.rs
+++ b/src/liballoc/slice.rs
@@ -123,8 +123,8 @@ pub use core::slice::{from_raw_parts, from_raw_parts_mut};
 pub use core::slice::{from_ref, from_mut};
 #[stable(feature = "slice_get_slice", since = "1.28.0")]
 pub use core::slice::SliceIndex;
-#[unstable(feature = "exact_chunks", issue = "47115")]
-pub use core::slice::{ExactChunks, ExactChunksMut};
+#[unstable(feature = "chunks_exact", issue = "47115")]
+pub use core::slice::{ChunksExact, ChunksExactMut};
 
 ////////////////////////////////////////////////////////////////////////////////
 // Basic slice extension methods
diff --git a/src/liballoc/tests/lib.rs b/src/liballoc/tests/lib.rs
index 710c659ac53..6ff39227555 100644
--- a/src/liballoc/tests/lib.rs
+++ b/src/liballoc/tests/lib.rs
@@ -20,7 +20,7 @@
 #![feature(str_escape)]
 #![feature(try_reserve)]
 #![feature(unboxed_closures)]
-#![feature(exact_chunks)]
+#![feature(chunks_exact)]
 #![feature(repeat_generic_slice)]
 
 extern crate alloc_system;
diff --git a/src/liballoc/tests/slice.rs b/src/liballoc/tests/slice.rs
index f33bf64d40b..c214c59618d 100644
--- a/src/liballoc/tests/slice.rs
+++ b/src/liballoc/tests/slice.rs
@@ -975,27 +975,27 @@ fn test_chunksator_0() {
 }
 
 #[test]
-fn test_exact_chunksator() {
+fn test_chunks_exactator() {
     let v = &[1, 2, 3, 4, 5];
 
-    assert_eq!(v.exact_chunks(2).len(), 2);
+    assert_eq!(v.chunks_exact(2).len(), 2);
 
     let chunks: &[&[_]] = &[&[1, 2], &[3, 4]];
-    assert_eq!(v.exact_chunks(2).collect::<Vec<_>>(), chunks);
+    assert_eq!(v.chunks_exact(2).collect::<Vec<_>>(), chunks);
     let chunks: &[&[_]] = &[&[1, 2, 3]];
-    assert_eq!(v.exact_chunks(3).collect::<Vec<_>>(), chunks);
+    assert_eq!(v.chunks_exact(3).collect::<Vec<_>>(), chunks);
     let chunks: &[&[_]] = &[];
-    assert_eq!(v.exact_chunks(6).collect::<Vec<_>>(), chunks);
+    assert_eq!(v.chunks_exact(6).collect::<Vec<_>>(), chunks);
 
     let chunks: &[&[_]] = &[&[3, 4], &[1, 2]];
-    assert_eq!(v.exact_chunks(2).rev().collect::<Vec<_>>(), chunks);
+    assert_eq!(v.chunks_exact(2).rev().collect::<Vec<_>>(), chunks);
 }
 
 #[test]
 #[should_panic]
-fn test_exact_chunksator_0() {
+fn test_chunks_exactator_0() {
     let v = &[1, 2, 3, 4];
-    let _it = v.exact_chunks(0);
+    let _it = v.chunks_exact(0);
 }
 
 #[test]
@@ -1235,10 +1235,10 @@ fn test_mut_chunks_0() {
 }
 
 #[test]
-fn test_mut_exact_chunks() {
+fn test_mut_chunks_exact() {
     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() {
+    assert_eq!(v.chunks_exact_mut(2).len(), 3);
+    for (i, chunk) in v.chunks_exact_mut(3).enumerate() {
         for x in chunk {
             *x = i as u8;
         }
@@ -1248,9 +1248,9 @@ fn test_mut_exact_chunks() {
 }
 
 #[test]
-fn test_mut_exact_chunks_rev() {
+fn test_mut_chunks_exact_rev() {
     let mut v = [0, 1, 2, 3, 4, 5, 6];
-    for (i, chunk) in v.exact_chunks_mut(3).rev().enumerate() {
+    for (i, chunk) in v.chunks_exact_mut(3).rev().enumerate() {
         for x in chunk {
             *x = i as u8;
         }
@@ -1261,9 +1261,9 @@ fn test_mut_exact_chunks_rev() {
 
 #[test]
 #[should_panic]
-fn test_mut_exact_chunks_0() {
+fn test_mut_chunks_exact_0() {
     let mut v = [1, 2, 3, 4];
-    let _it = v.exact_chunks_mut(0);
+    let _it = v.chunks_exact_mut(0);
 }
 
 #[test]
diff --git a/src/liballoc/vec.rs b/src/liballoc/vec.rs
index 7fc4453fec5..e845438c0a8 100644
--- a/src/liballoc/vec.rs
+++ b/src/liballoc/vec.rs
@@ -947,10 +947,9 @@ impl<T> Vec<T> {
     /// Removes all but the first of consecutive elements in the vector satisfying a given equality
     /// relation.
     ///
-    /// The `same_bucket` function is passed references to two elements from the vector, and
-    /// returns `true` if the elements compare equal, or `false` if they do not. The elements are
-    /// passed in opposite order from their order in the vector, so if `same_bucket(a, b)` returns
-    /// `true`, `a` is removed.
+    /// The `same_bucket` function is passed references to two elements from the vector and
+    /// must determine if the elements compare equal. The elements are passed in opposite order
+    /// from their order in the slice, so if `same_bucket(a, b)` returns `true`, `a` is removed.
     ///
     /// If the vector is sorted, this removes all duplicates.
     ///
@@ -964,90 +963,12 @@ impl<T> Vec<T> {
     /// assert_eq!(vec, ["foo", "bar", "baz", "bar"]);
     /// ```
     #[stable(feature = "dedup_by", since = "1.16.0")]
-    pub fn dedup_by<F>(&mut self, mut same_bucket: F) where F: FnMut(&mut T, &mut T) -> bool {
-        unsafe {
-            // Although we have a mutable reference to `self`, we cannot make
-            // *arbitrary* changes. The `same_bucket` calls could panic, so we
-            // must ensure that the vector is in a valid state at all time.
-            //
-            // The way that we handle this is by using swaps; we iterate
-            // over all the elements, swapping as we go so that at the end
-            // the elements we wish to keep are in the front, and those we
-            // wish to reject are at the back. We can then truncate the
-            // vector. This operation is still O(n).
-            //
-            // Example: We start in this state, where `r` represents "next
-            // read" and `w` represents "next_write`.
-            //
-            //           r
-            //     +---+---+---+---+---+---+
-            //     | 0 | 1 | 1 | 2 | 3 | 3 |
-            //     +---+---+---+---+---+---+
-            //           w
-            //
-            // Comparing self[r] against self[w-1], this is not a duplicate, so
-            // we swap self[r] and self[w] (no effect as r==w) and then increment both
-            // r and w, leaving us with:
-            //
-            //               r
-            //     +---+---+---+---+---+---+
-            //     | 0 | 1 | 1 | 2 | 3 | 3 |
-            //     +---+---+---+---+---+---+
-            //               w
-            //
-            // Comparing self[r] against self[w-1], this value is a duplicate,
-            // so we increment `r` but leave everything else unchanged:
-            //
-            //                   r
-            //     +---+---+---+---+---+---+
-            //     | 0 | 1 | 1 | 2 | 3 | 3 |
-            //     +---+---+---+---+---+---+
-            //               w
-            //
-            // Comparing self[r] against self[w-1], this is not a duplicate,
-            // so swap self[r] and self[w] and advance r and w:
-            //
-            //                       r
-            //     +---+---+---+---+---+---+
-            //     | 0 | 1 | 2 | 1 | 3 | 3 |
-            //     +---+---+---+---+---+---+
-            //                   w
-            //
-            // Not a duplicate, repeat:
-            //
-            //                           r
-            //     +---+---+---+---+---+---+
-            //     | 0 | 1 | 2 | 3 | 1 | 3 |
-            //     +---+---+---+---+---+---+
-            //                       w
-            //
-            // Duplicate, advance r. End of vec. Truncate to w.
-
-            let ln = self.len();
-            if ln <= 1 {
-                return;
-            }
-
-            // Avoid bounds checks by using raw pointers.
-            let p = self.as_mut_ptr();
-            let mut r: usize = 1;
-            let mut w: usize = 1;
-
-            while r < ln {
-                let p_r = p.add(r);
-                let p_wm1 = p.add(w - 1);
-                if !same_bucket(&mut *p_r, &mut *p_wm1) {
-                    if r != w {
-                        let p_w = p_wm1.offset(1);
-                        mem::swap(&mut *p_r, &mut *p_w);
-                    }
-                    w += 1;
-                }
-                r += 1;
-            }
-
-            self.truncate(w);
-        }
+    pub fn dedup_by<F>(&mut self, same_bucket: F) where F: FnMut(&mut T, &mut T) -> bool {
+        let len = {
+            let (dedup, _) = self.as_mut_slice().partition_dedup_by(same_bucket);
+            dedup.len()
+        };
+        self.truncate(len);
     }
 
     /// Appends an element to the back of a collection.
@@ -1533,7 +1454,8 @@ impl<'a> Drop for SetLenOnDrop<'a> {
 }
 
 impl<T: PartialEq> Vec<T> {
-    /// Removes consecutive repeated elements in the vector.
+    /// Removes consecutive repeated elements in the vector according to the
+    /// [`PartialEq`] trait implementation.
     ///
     /// If the vector is sorted, this removes all duplicates.
     ///
diff --git a/src/libcore/char/convert.rs b/src/libcore/char/convert.rs
index 803a924eb3a..e9ccdd0ea3c 100644
--- a/src/libcore/char/convert.rs
+++ b/src/libcore/char/convert.rs
@@ -115,6 +115,19 @@ pub unsafe fn from_u32_unchecked(i: u32) -> char {
 
 #[stable(feature = "char_convert", since = "1.13.0")]
 impl From<char> for u32 {
+    /// Converts a [`char`] into a [`u32`].
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// use std::mem;
+    ///
+    /// fn main() {
+    ///     let c = 'c';
+    ///     let u = u32::from(c);
+    ///     assert!(4 == mem::size_of_val(&u))
+    /// }
+    /// ```
     #[inline]
     fn from(c: char) -> Self {
         c as u32
@@ -141,6 +154,19 @@ impl From<char> for u32 {
 /// C0 and C1 control codes.
 #[stable(feature = "char_convert", since = "1.13.0")]
 impl From<u8> for char {
+    /// Converts a [`u8`] into a [`char`].
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// use std::mem;
+    ///
+    /// fn main() {
+    ///     let u = 32 as u8;
+    ///     let c = char::from(u);
+    ///     assert!(4 == mem::size_of_val(&c))
+    /// }
+    /// ```
     #[inline]
     fn from(i: u8) -> Self {
         i as char
diff --git a/src/libcore/slice/mod.rs b/src/libcore/slice/mod.rs
index aed9020d9d1..05027bbe898 100644
--- a/src/libcore/slice/mod.rs
+++ b/src/libcore/slice/mod.rs
@@ -624,7 +624,7 @@ impl<T> [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
+    /// See [`chunks_exact`] for a variant of this iterator that returns chunks
     /// of always exactly `chunk_size` elements.
     ///
     /// # Panics
@@ -642,7 +642,7 @@ impl<T> [T] {
     /// assert!(iter.next().is_none());
     /// ```
     ///
-    /// [`exact_chunks`]: #method.exact_chunks
+    /// [`chunks_exact`]: #method.chunks_exact
     #[stable(feature = "rust1", since = "1.0.0")]
     #[inline]
     pub fn chunks(&self, chunk_size: usize) -> Chunks<T> {
@@ -655,7 +655,7 @@ impl<T> [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
+    /// See [`chunks_exact_mut`] for a variant of this iterator that returns chunks
     /// of always exactly `chunk_size` elements.
     ///
     /// # Panics
@@ -677,7 +677,7 @@ impl<T> [T] {
     /// assert_eq!(v, &[1, 1, 2, 2, 3]);
     /// ```
     ///
-    /// [`exact_chunks_mut`]: #method.exact_chunks_mut
+    /// [`chunks_exact_mut`]: #method.chunks_exact_mut
     #[stable(feature = "rust1", since = "1.0.0")]
     #[inline]
     pub fn chunks_mut(&mut self, chunk_size: usize) -> ChunksMut<T> {
@@ -702,24 +702,24 @@ impl<T> [T] {
     /// # Examples
     ///
     /// ```
-    /// #![feature(exact_chunks)]
+    /// #![feature(chunks_exact)]
     ///
     /// let slice = ['l', 'o', 'r', 'e', 'm'];
-    /// let mut iter = slice.exact_chunks(2);
+    /// let mut iter = slice.chunks_exact(2);
     /// assert_eq!(iter.next().unwrap(), &['l', 'o']);
     /// assert_eq!(iter.next().unwrap(), &['r', 'e']);
     /// assert!(iter.next().is_none());
     /// ```
     ///
     /// [`chunks`]: #method.chunks
-    #[unstable(feature = "exact_chunks", issue = "47115")]
+    #[unstable(feature = "chunks_exact", issue = "47115")]
     #[inline]
-    pub fn exact_chunks(&self, chunk_size: usize) -> ExactChunks<T> {
+    pub fn chunks_exact(&self, chunk_size: usize) -> ChunksExact<T> {
         assert!(chunk_size != 0);
         let rem = self.len() % chunk_size;
         let len = self.len() - rem;
         let (fst, snd) = self.split_at(len);
-        ExactChunks { v: fst, rem: snd, chunk_size }
+        ChunksExact { v: fst, rem: snd, chunk_size }
     }
 
     /// Returns an iterator over `chunk_size` elements of the slice at a time.
@@ -739,12 +739,12 @@ impl<T> [T] {
     /// # Examples
     ///
     /// ```
-    /// #![feature(exact_chunks)]
+    /// #![feature(chunks_exact)]
     ///
     /// let v = &mut [0, 0, 0, 0, 0];
     /// let mut count = 1;
     ///
-    /// for chunk in v.exact_chunks_mut(2) {
+    /// for chunk in v.chunks_exact_mut(2) {
     ///     for elem in chunk.iter_mut() {
     ///         *elem += count;
     ///     }
@@ -754,14 +754,14 @@ impl<T> [T] {
     /// ```
     ///
     /// [`chunks_mut`]: #method.chunks_mut
-    #[unstable(feature = "exact_chunks", issue = "47115")]
+    #[unstable(feature = "chunks_exact", issue = "47115")]
     #[inline]
-    pub fn exact_chunks_mut(&mut self, chunk_size: usize) -> ExactChunksMut<T> {
+    pub fn chunks_exact_mut(&mut self, chunk_size: usize) -> ChunksExactMut<T> {
         assert!(chunk_size != 0);
         let rem = self.len() % chunk_size;
         let len = self.len() - rem;
         let (fst, snd) = self.split_at_mut(len);
-        ExactChunksMut { v: fst, rem: snd, chunk_size }
+        ChunksExactMut { v: fst, rem: snd, chunk_size }
     }
 
     /// Divides one slice into two at an index.
@@ -1402,6 +1402,178 @@ impl<T> [T] {
         sort::quicksort(self, |a, b| f(a).lt(&f(b)));
     }
 
+    /// Moves all consecutive repeated elements to the end of the slice according to the
+    /// [`PartialEq`] trait implementation.
+    ///
+    /// Returns two slices. The first contains no consecutive repeated elements.
+    /// The second contains all the duplicates in no specified order.
+    ///
+    /// If the slice is sorted, the first returned slice contains no duplicates.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(slice_partition_dedup)]
+    ///
+    /// let mut slice = [1, 2, 2, 3, 3, 2, 1, 1];
+    ///
+    /// let (dedup, duplicates) = slice.partition_dedup();
+    ///
+    /// assert_eq!(dedup, [1, 2, 3, 2, 1]);
+    /// assert_eq!(duplicates, [2, 3, 1]);
+    /// ```
+    #[unstable(feature = "slice_partition_dedup", issue = "54279")]
+    #[inline]
+    pub fn partition_dedup(&mut self) -> (&mut [T], &mut [T])
+        where T: PartialEq
+    {
+        self.partition_dedup_by(|a, b| a == b)
+    }
+
+    /// Moves all but the first of consecutive elements to the end of the slice satisfying
+    /// a given equality relation.
+    ///
+    /// Returns two slices. The first contains no consecutive repeated elements.
+    /// The second contains all the duplicates in no specified order.
+    ///
+    /// The `same_bucket` function is passed references to two elements from the slice and
+    /// must determine if the elements compare equal. The elements are passed in opposite order
+    /// from their order in the slice, so if `same_bucket(a, b)` returns `true`, `a` is moved
+    /// at the end of the slice.
+    ///
+    /// If the slice is sorted, the first returned slice contains no duplicates.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(slice_partition_dedup)]
+    ///
+    /// let mut slice = ["foo", "Foo", "BAZ", "Bar", "bar", "baz", "BAZ"];
+    ///
+    /// let (dedup, duplicates) = slice.partition_dedup_by(|a, b| a.eq_ignore_ascii_case(b));
+    ///
+    /// assert_eq!(dedup, ["foo", "BAZ", "Bar", "baz"]);
+    /// assert_eq!(duplicates, ["bar", "Foo", "BAZ"]);
+    /// ```
+    #[unstable(feature = "slice_partition_dedup", issue = "54279")]
+    #[inline]
+    pub fn partition_dedup_by<F>(&mut self, mut same_bucket: F) -> (&mut [T], &mut [T])
+        where F: FnMut(&mut T, &mut T) -> bool
+    {
+        // Although we have a mutable reference to `self`, we cannot make
+        // *arbitrary* changes. The `same_bucket` calls could panic, so we
+        // must ensure that the slice is in a valid state at all times.
+        //
+        // The way that we handle this is by using swaps; we iterate
+        // over all the elements, swapping as we go so that at the end
+        // the elements we wish to keep are in the front, and those we
+        // wish to reject are at the back. We can then split the slice.
+        // This operation is still O(n).
+        //
+        // Example: We start in this state, where `r` represents "next
+        // read" and `w` represents "next_write`.
+        //
+        //           r
+        //     +---+---+---+---+---+---+
+        //     | 0 | 1 | 1 | 2 | 3 | 3 |
+        //     +---+---+---+---+---+---+
+        //           w
+        //
+        // Comparing self[r] against self[w-1], this is not a duplicate, so
+        // we swap self[r] and self[w] (no effect as r==w) and then increment both
+        // r and w, leaving us with:
+        //
+        //               r
+        //     +---+---+---+---+---+---+
+        //     | 0 | 1 | 1 | 2 | 3 | 3 |
+        //     +---+---+---+---+---+---+
+        //               w
+        //
+        // Comparing self[r] against self[w-1], this value is a duplicate,
+        // so we increment `r` but leave everything else unchanged:
+        //
+        //                   r
+        //     +---+---+---+---+---+---+
+        //     | 0 | 1 | 1 | 2 | 3 | 3 |
+        //     +---+---+---+---+---+---+
+        //               w
+        //
+        // Comparing self[r] against self[w-1], this is not a duplicate,
+        // so swap self[r] and self[w] and advance r and w:
+        //
+        //                       r
+        //     +---+---+---+---+---+---+
+        //     | 0 | 1 | 2 | 1 | 3 | 3 |
+        //     +---+---+---+---+---+---+
+        //                   w
+        //
+        // Not a duplicate, repeat:
+        //
+        //                           r
+        //     +---+---+---+---+---+---+
+        //     | 0 | 1 | 2 | 3 | 1 | 3 |
+        //     +---+---+---+---+---+---+
+        //                       w
+        //
+        // Duplicate, advance r. End of slice. Split at w.
+
+        let len = self.len();
+        if len <= 1 {
+            return (self, &mut [])
+        }
+
+        let ptr = self.as_mut_ptr();
+        let mut next_read: usize = 1;
+        let mut next_write: usize = 1;
+
+        unsafe {
+            // Avoid bounds checks by using raw pointers.
+            while next_read < len {
+                let ptr_read = ptr.add(next_read);
+                let prev_ptr_write = ptr.add(next_write - 1);
+                if !same_bucket(&mut *ptr_read, &mut *prev_ptr_write) {
+                    if next_read != next_write {
+                        let ptr_write = prev_ptr_write.offset(1);
+                        mem::swap(&mut *ptr_read, &mut *ptr_write);
+                    }
+                    next_write += 1;
+                }
+                next_read += 1;
+            }
+        }
+
+        self.split_at_mut(next_write)
+    }
+
+    /// Moves all but the first of consecutive elements to the end of the slice that resolve
+    /// to the same key.
+    ///
+    /// Returns two slices. The first contains no consecutive repeated elements.
+    /// The second contains all the duplicates in no specified order.
+    ///
+    /// If the slice is sorted, the first returned slice contains no duplicates.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(slice_partition_dedup)]
+    ///
+    /// let mut slice = [10, 20, 21, 30, 30, 20, 11, 13];
+    ///
+    /// let (dedup, duplicates) = slice.partition_dedup_by_key(|i| *i / 10);
+    ///
+    /// assert_eq!(dedup, [10, 20, 30, 20, 11]);
+    /// assert_eq!(duplicates, [21, 30, 13]);
+    /// ```
+    #[unstable(feature = "slice_partition_dedup", issue = "54279")]
+    #[inline]
+    pub fn partition_dedup_by_key<K, F>(&mut self, mut key: F) -> (&mut [T], &mut [T])
+        where F: FnMut(&mut T) -> K,
+              K: PartialEq,
+    {
+        self.partition_dedup_by(|a, b| key(a) == key(b))
+    }
+
     /// Rotates the slice in-place such that the first `mid` elements of the
     /// slice move to the end while the last `self.len() - mid` elements move to
     /// the front. After calling `rotate_left`, the element previously at index
@@ -3657,21 +3829,21 @@ unsafe impl<'a, T> TrustedRandomAccess for ChunksMut<'a, T> {
 /// up to `chunk_size-1` elements will be omitted but can be retrieved from
 /// the [`remainder`] function from the iterator.
 ///
-/// This struct is created by the [`exact_chunks`] method on [slices].
+/// This struct is created by the [`chunks_exact`] method on [slices].
 ///
-/// [`exact_chunks`]: ../../std/primitive.slice.html#method.exact_chunks
-/// [`remainder`]: ../../std/slice/struct.ExactChunks.html#method.remainder
+/// [`chunks_exact`]: ../../std/primitive.slice.html#method.chunks_exact
+/// [`remainder`]: ../../std/slice/struct.ChunksExact.html#method.remainder
 /// [slices]: ../../std/primitive.slice.html
 #[derive(Debug)]
-#[unstable(feature = "exact_chunks", issue = "47115")]
-pub struct ExactChunks<'a, T:'a> {
+#[unstable(feature = "chunks_exact", issue = "47115")]
+pub struct ChunksExact<'a, T:'a> {
     v: &'a [T],
     rem: &'a [T],
     chunk_size: usize
 }
 
-#[unstable(feature = "exact_chunks", issue = "47115")]
-impl<'a, T> ExactChunks<'a, T> {
+#[unstable(feature = "chunks_exact", issue = "47115")]
+impl<'a, T> ChunksExact<'a, T> {
     /// Return the remainder of the original slice that is not going to be
     /// returned by the iterator. The returned slice has at most `chunk_size-1`
     /// elements.
@@ -3681,10 +3853,10 @@ impl<'a, T> ExactChunks<'a, T> {
 }
 
 // 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 {
+#[unstable(feature = "chunks_exact", issue = "47115")]
+impl<'a, T> Clone for ChunksExact<'a, T> {
+    fn clone(&self) -> ChunksExact<'a, T> {
+        ChunksExact {
             v: self.v,
             rem: self.rem,
             chunk_size: self.chunk_size,
@@ -3692,8 +3864,8 @@ impl<'a, T> Clone for ExactChunks<'a, T> {
     }
 }
 
-#[unstable(feature = "exact_chunks", issue = "47115")]
-impl<'a, T> Iterator for ExactChunks<'a, T> {
+#[unstable(feature = "chunks_exact", issue = "47115")]
+impl<'a, T> Iterator for ChunksExact<'a, T> {
     type Item = &'a [T];
 
     #[inline]
@@ -3737,8 +3909,8 @@ impl<'a, T> Iterator for ExactChunks<'a, T> {
     }
 }
 
-#[unstable(feature = "exact_chunks", issue = "47115")]
-impl<'a, T> DoubleEndedIterator for ExactChunks<'a, T> {
+#[unstable(feature = "chunks_exact", issue = "47115")]
+impl<'a, T> DoubleEndedIterator for ChunksExact<'a, T> {
     #[inline]
     fn next_back(&mut self) -> Option<&'a [T]> {
         if self.v.len() < self.chunk_size {
@@ -3751,21 +3923,21 @@ impl<'a, T> DoubleEndedIterator for ExactChunks<'a, T> {
     }
 }
 
-#[unstable(feature = "exact_chunks", issue = "47115")]
-impl<'a, T> ExactSizeIterator for ExactChunks<'a, T> {
+#[unstable(feature = "chunks_exact", issue = "47115")]
+impl<'a, T> ExactSizeIterator for ChunksExact<'a, T> {
     fn is_empty(&self) -> bool {
         self.v.is_empty()
     }
 }
 
 #[unstable(feature = "trusted_len", issue = "37572")]
-unsafe impl<'a, T> TrustedLen for ExactChunks<'a, T> {}
+unsafe impl<'a, T> TrustedLen for ChunksExact<'a, T> {}
 
-#[unstable(feature = "exact_chunks", issue = "47115")]
-impl<'a, T> FusedIterator for ExactChunks<'a, T> {}
+#[unstable(feature = "chunks_exact", issue = "47115")]
+impl<'a, T> FusedIterator for ChunksExact<'a, T> {}
 
 #[doc(hidden)]
-unsafe impl<'a, T> TrustedRandomAccess for ExactChunks<'a, T> {
+unsafe impl<'a, T> TrustedRandomAccess for ChunksExact<'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().add(start), self.chunk_size)
@@ -3780,21 +3952,21 @@ unsafe impl<'a, T> TrustedRandomAccess for ExactChunks<'a, T> {
 /// `chunk_size-1` elements will be omitted but can be retrieved from the
 /// [`into_remainder`] function from the iterator.
 ///
-/// This struct is created by the [`exact_chunks_mut`] method on [slices].
+/// This struct is created by the [`chunks_exact_mut`] method on [slices].
 ///
-/// [`exact_chunks_mut`]: ../../std/primitive.slice.html#method.exact_chunks_mut
-/// [`into_remainder`]: ../../std/slice/struct.ExactChunksMut.html#method.into_remainder
+/// [`chunks_exact_mut`]: ../../std/primitive.slice.html#method.chunks_exact_mut
+/// [`into_remainder`]: ../../std/slice/struct.ChunksExactMut.html#method.into_remainder
 /// [slices]: ../../std/primitive.slice.html
 #[derive(Debug)]
-#[unstable(feature = "exact_chunks", issue = "47115")]
-pub struct ExactChunksMut<'a, T:'a> {
+#[unstable(feature = "chunks_exact", issue = "47115")]
+pub struct ChunksExactMut<'a, T:'a> {
     v: &'a mut [T],
     rem: &'a mut [T],
     chunk_size: usize
 }
 
-#[unstable(feature = "exact_chunks", issue = "47115")]
-impl<'a, T> ExactChunksMut<'a, T> {
+#[unstable(feature = "chunks_exact", issue = "47115")]
+impl<'a, T> ChunksExactMut<'a, T> {
     /// Return the remainder of the original slice that is not going to be
     /// returned by the iterator. The returned slice has at most `chunk_size-1`
     /// elements.
@@ -3803,8 +3975,8 @@ impl<'a, T> ExactChunksMut<'a, T> {
     }
 }
 
-#[unstable(feature = "exact_chunks", issue = "47115")]
-impl<'a, T> Iterator for ExactChunksMut<'a, T> {
+#[unstable(feature = "chunks_exact", issue = "47115")]
+impl<'a, T> Iterator for ChunksExactMut<'a, T> {
     type Item = &'a mut [T];
 
     #[inline]
@@ -3850,8 +4022,8 @@ impl<'a, T> Iterator for ExactChunksMut<'a, T> {
     }
 }
 
-#[unstable(feature = "exact_chunks", issue = "47115")]
-impl<'a, T> DoubleEndedIterator for ExactChunksMut<'a, T> {
+#[unstable(feature = "chunks_exact", issue = "47115")]
+impl<'a, T> DoubleEndedIterator for ChunksExactMut<'a, T> {
     #[inline]
     fn next_back(&mut self) -> Option<&'a mut [T]> {
         if self.v.len() < self.chunk_size {
@@ -3866,21 +4038,21 @@ impl<'a, T> DoubleEndedIterator for ExactChunksMut<'a, T> {
     }
 }
 
-#[unstable(feature = "exact_chunks", issue = "47115")]
-impl<'a, T> ExactSizeIterator for ExactChunksMut<'a, T> {
+#[unstable(feature = "chunks_exact", issue = "47115")]
+impl<'a, T> ExactSizeIterator for ChunksExactMut<'a, T> {
     fn is_empty(&self) -> bool {
         self.v.is_empty()
     }
 }
 
 #[unstable(feature = "trusted_len", issue = "37572")]
-unsafe impl<'a, T> TrustedLen for ExactChunksMut<'a, T> {}
+unsafe impl<'a, T> TrustedLen for ChunksExactMut<'a, T> {}
 
-#[unstable(feature = "exact_chunks", issue = "47115")]
-impl<'a, T> FusedIterator for ExactChunksMut<'a, T> {}
+#[unstable(feature = "chunks_exact", issue = "47115")]
+impl<'a, T> FusedIterator for ChunksExactMut<'a, T> {}
 
 #[doc(hidden)]
-unsafe impl<'a, T> TrustedRandomAccess for ExactChunksMut<'a, T> {
+unsafe impl<'a, T> TrustedRandomAccess for ChunksExactMut<'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().add(start), self.chunk_size)
diff --git a/src/libcore/tests/lib.rs b/src/libcore/tests/lib.rs
index 8fc32f40b99..e034ff80593 100644
--- a/src/libcore/tests/lib.rs
+++ b/src/libcore/tests/lib.rs
@@ -33,12 +33,13 @@
 #![feature(trusted_len)]
 #![feature(try_from)]
 #![feature(try_trait)]
-#![feature(exact_chunks)]
+#![feature(chunks_exact)]
 #![feature(align_offset)]
 #![feature(reverse_bits)]
 #![feature(inner_deref)]
 #![feature(slice_internals)]
 #![feature(option_replace)]
+#![feature(slice_partition_dedup)]
 #![feature(copy_within)]
 
 extern crate core;
diff --git a/src/libcore/tests/slice.rs b/src/libcore/tests/slice.rs
index d46a35ab82c..de49a7bed39 100644
--- a/src/libcore/tests/slice.rs
+++ b/src/libcore/tests/slice.rs
@@ -221,115 +221,115 @@ fn test_chunks_mut_zip() {
 }
 
 #[test]
-fn test_exact_chunks_count() {
+fn test_chunks_exact_count() {
     let v: &[i32] = &[0, 1, 2, 3, 4, 5];
-    let c = v.exact_chunks(3);
+    let c = v.chunks_exact(3);
     assert_eq!(c.count(), 2);
 
     let v2: &[i32] = &[0, 1, 2, 3, 4];
-    let c2 = v2.exact_chunks(2);
+    let c2 = v2.chunks_exact(2);
     assert_eq!(c2.count(), 2);
 
     let v3: &[i32] = &[];
-    let c3 = v3.exact_chunks(2);
+    let c3 = v3.chunks_exact(2);
     assert_eq!(c3.count(), 0);
 }
 
 #[test]
-fn test_exact_chunks_nth() {
+fn test_chunks_exact_nth() {
     let v: &[i32] = &[0, 1, 2, 3, 4, 5];
-    let mut c = v.exact_chunks(2);
+    let mut c = v.chunks_exact(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);
+    let mut c2 = v2.chunks_exact(3);
     assert_eq!(c2.nth(1).unwrap(), &[3, 4, 5]);
     assert_eq!(c2.next(), None);
 }
 
 #[test]
-fn test_exact_chunks_last() {
+fn test_chunks_exact_last() {
     let v: &[i32] = &[0, 1, 2, 3, 4, 5];
-    let c = v.exact_chunks(2);
+    let c = v.chunks_exact(2);
     assert_eq!(c.last().unwrap(), &[4, 5]);
 
     let v2: &[i32] = &[0, 1, 2, 3, 4];
-    let c2 = v2.exact_chunks(2);
+    let c2 = v2.chunks_exact(2);
     assert_eq!(c2.last().unwrap(), &[2, 3]);
 }
 
 #[test]
-fn test_exact_chunks_remainder() {
+fn test_chunks_exact_remainder() {
     let v: &[i32] = &[0, 1, 2, 3, 4];
-    let c = v.exact_chunks(2);
+    let c = v.chunks_exact(2);
     assert_eq!(c.remainder(), &[4]);
 }
 
 #[test]
-fn test_exact_chunks_zip() {
+fn test_chunks_exact_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))
+    let res = v1.chunks_exact(2)
+        .zip(v2.chunks_exact(2))
         .map(|(a, b)| a.iter().sum::<i32>() + b.iter().sum::<i32>())
         .collect::<Vec<_>>();
     assert_eq!(res, vec![14, 22]);
 }
 
 #[test]
-fn test_exact_chunks_mut_count() {
+fn test_chunks_exact_mut_count() {
     let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5];
-    let c = v.exact_chunks_mut(3);
+    let c = v.chunks_exact_mut(3);
     assert_eq!(c.count(), 2);
 
     let v2: &mut [i32] = &mut [0, 1, 2, 3, 4];
-    let c2 = v2.exact_chunks_mut(2);
+    let c2 = v2.chunks_exact_mut(2);
     assert_eq!(c2.count(), 2);
 
     let v3: &mut [i32] = &mut [];
-    let c3 = v3.exact_chunks_mut(2);
+    let c3 = v3.chunks_exact_mut(2);
     assert_eq!(c3.count(), 0);
 }
 
 #[test]
-fn test_exact_chunks_mut_nth() {
+fn test_chunks_exact_mut_nth() {
     let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5];
-    let mut c = v.exact_chunks_mut(2);
+    let mut c = v.chunks_exact_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);
+    let mut c2 = v2.chunks_exact_mut(3);
     assert_eq!(c2.nth(1).unwrap(), &[3, 4, 5]);
     assert_eq!(c2.next(), None);
 }
 
 #[test]
-fn test_exact_chunks_mut_last() {
+fn test_chunks_exact_mut_last() {
     let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5];
-    let c = v.exact_chunks_mut(2);
+    let c = v.chunks_exact_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);
+    let c2 = v2.chunks_exact_mut(2);
     assert_eq!(c2.last().unwrap(), &[2, 3]);
 }
 
 #[test]
-fn test_exact_chunks_mut_remainder() {
+fn test_chunks_exact_mut_remainder() {
     let v: &mut [i32] = &mut [0, 1, 2, 3, 4];
-    let c = v.exact_chunks_mut(2);
+    let c = v.chunks_exact_mut(2);
     assert_eq!(c.into_remainder(), &[4]);
 }
 
 #[test]
-fn test_exact_chunks_mut_zip() {
+fn test_chunks_exact_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)) {
+    for (a, b) in v1.chunks_exact_mut(2).zip(v2.chunks_exact(2)) {
         let sum = b.iter().sum::<i32>();
         for v in a {
             *v += sum;
@@ -1002,6 +1002,65 @@ fn test_align_to_empty_mid() {
 }
 
 #[test]
+fn test_slice_partition_dedup_by() {
+    let mut slice: [i32; 9] = [1, -1, 2, 3, 1, -5, 5, -2, 2];
+
+    let (dedup, duplicates) = slice.partition_dedup_by(|a, b| a.abs() == b.abs());
+
+    assert_eq!(dedup, [1, 2, 3, 1, -5, -2]);
+    assert_eq!(duplicates, [5, -1, 2]);
+}
+
+#[test]
+fn test_slice_partition_dedup_empty() {
+    let mut slice: [i32; 0] = [];
+
+    let (dedup, duplicates) = slice.partition_dedup();
+
+    assert_eq!(dedup, []);
+    assert_eq!(duplicates, []);
+}
+
+#[test]
+fn test_slice_partition_dedup_one() {
+    let mut slice = [12];
+
+    let (dedup, duplicates) = slice.partition_dedup();
+
+    assert_eq!(dedup, [12]);
+    assert_eq!(duplicates, []);
+}
+
+#[test]
+fn test_slice_partition_dedup_multiple_ident() {
+    let mut slice = [12, 12, 12, 12, 12, 11, 11, 11, 11, 11, 11];
+
+    let (dedup, duplicates) = slice.partition_dedup();
+
+    assert_eq!(dedup, [12, 11]);
+    assert_eq!(duplicates, [12, 12, 12, 12, 11, 11, 11, 11, 11]);
+}
+
+#[test]
+fn test_slice_partition_dedup_partialeq() {
+    #[derive(Debug)]
+    struct Foo(i32, i32);
+
+    impl PartialEq for Foo {
+        fn eq(&self, other: &Foo) -> bool {
+            self.0 == other.0
+        }
+    }
+
+    let mut slice = [Foo(0, 1), Foo(0, 5), Foo(1, 7), Foo(1, 9)];
+
+    let (dedup, duplicates) = slice.partition_dedup();
+
+    assert_eq!(dedup, [Foo(0, 1), Foo(1, 7)]);
+    assert_eq!(duplicates, [Foo(0, 5), Foo(1, 9)]);
+}
+
+#[test]
 fn test_copy_within() {
     // Start to end, with a RangeTo.
     let mut bytes = *b"Hello, World!";
diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs
index 1fdbf687be8..8fa15d48a5d 100644
--- a/src/librustc/session/config.rs
+++ b/src/librustc/session/config.rs
@@ -646,7 +646,6 @@ impl Options {
         match self.debugging_opts.share_generics {
             Some(setting) => setting,
             None => {
-                self.incremental.is_some() ||
                 match self.optimize {
                     OptLevel::No   |
                     OptLevel::Less |
diff --git a/src/librustc_resolve/macros.rs b/src/librustc_resolve/macros.rs
index 35d96b9302b..89bc2f2709a 100644
--- a/src/librustc_resolve/macros.rs
+++ b/src/librustc_resolve/macros.rs
@@ -575,6 +575,13 @@ impl<'a, 'cl> Resolver<'a, 'cl> {
         // 5. Standard library prelude (de-facto closed, controlled).
         // 6. Language prelude (closed, controlled).
         // (Macro NS)
+        // 0. Derive helpers (open, not controlled). All ambiguities with other names
+        //    are currently reported as errors. They should be higher in priority than preludes
+        //    and probably even names in modules according to the "general principles" above. They
+        //    also should be subject to restricted shadowing because are effectively produced by
+        //    derives (you need to resolve the derive first to add helpers into scope), but they
+        //    should be available before the derive is expanded for compatibility.
+        //    It's mess in general, so we are being conservative for now.
         // 1. Names in modules (both normal `mod`ules and blocks), loop through hygienic parents
         //    (open, not controlled).
         // 2. `macro_use` prelude (open, the open part is from macro expansions, not controlled).
@@ -583,13 +590,6 @@ impl<'a, 'cl> Resolver<'a, 'cl> {
         // 2b. Standard library prelude is currently implemented as `macro-use` (closed, controlled)
         // 3. Language prelude: builtin macros (closed, controlled, except for legacy plugins).
         // 4. Language prelude: builtin attributes (closed, controlled).
-        // N (unordered). Derive helpers (open, not controlled). All ambiguities with other names
-        //    are currently reported as errors. They should be higher in priority than preludes
-        //    and maybe even names in modules according to the "general principles" above. They
-        //    also should be subject to restricted shadowing because are effectively produced by
-        //    derives (you need to resolve the derive first to add helpers into scope), but they
-        //    should be available before the derive is expanded for compatibility.
-        //    It's mess in general, so we are being conservative for now.
 
         assert!(ns == TypeNS  || ns == MacroNS);
         assert!(force || !record_used); // `record_used` implies `force`
@@ -621,7 +621,7 @@ impl<'a, 'cl> Resolver<'a, 'cl> {
         }
 
         // Go through all the scopes and try to resolve the name.
-        let mut where_to_resolve = WhereToResolve::Module(parent_scope.module);
+        let mut where_to_resolve = WhereToResolve::DeriveHelpers;
         let mut use_prelude = !parent_scope.module.no_implicit_prelude;
         loop {
             let result = match where_to_resolve {
@@ -751,8 +751,8 @@ impl<'a, 'cl> Resolver<'a, 'cl> {
                     }
                     WhereToResolve::MacroUsePrelude => WhereToResolve::BuiltinMacros,
                     WhereToResolve::BuiltinMacros => WhereToResolve::BuiltinAttrs,
-                    WhereToResolve::BuiltinAttrs => WhereToResolve::DeriveHelpers,
-                    WhereToResolve::DeriveHelpers => break, // nowhere else to search
+                    WhereToResolve::BuiltinAttrs => break, // nowhere else to search
+                    WhereToResolve::DeriveHelpers => WhereToResolve::Module(parent_scope.module),
                     WhereToResolve::ExternPrelude => WhereToResolve::ToolPrelude,
                     WhereToResolve::ToolPrelude => WhereToResolve::StdLibPrelude,
                     WhereToResolve::StdLibPrelude => WhereToResolve::BuiltinTypes,
diff --git a/src/librustdoc/html/static/main.js b/src/librustdoc/html/static/main.js
index 3dbefabace1..0b56692bc2e 100644
--- a/src/librustdoc/html/static/main.js
+++ b/src/librustdoc/html/static/main.js
@@ -1991,8 +1991,11 @@
                 }
         };
         if (getCurrentValue('rustdoc-trait-implementations') !== "false") {
-            onEach(document.getElementById('implementations-list')
-                           .getElementsByClassName("collapse-toggle"), collapser);
+            var impl_list = document.getElementById('implementations-list');
+
+            if (impl_list !== null) {
+                onEach(impl_list.getElementsByClassName("collapse-toggle"), collapser);
+            }
         }
         if (getCurrentValue('rustdoc-method-docs') !== "false") {
             var implItems = document.getElementsByClassName('impl-items');
diff --git a/src/librustdoc/html/static/rustdoc.css b/src/librustdoc/html/static/rustdoc.css
index 923a486060a..ee811f33792 100644
--- a/src/librustdoc/html/static/rustdoc.css
+++ b/src/librustdoc/html/static/rustdoc.css
@@ -139,7 +139,7 @@ code, pre {
 }
 .docblock code, .docblock-short code {
 	border-radius: 3px;
-	padding: 0 0.2em;
+	padding: 0 0.1em;
 }
 .docblock pre code, .docblock-short pre code, .docblock code.spotlight {
 	padding: 0;
diff --git a/src/libstd/primitive_docs.rs b/src/libstd/primitive_docs.rs
index 4c1fdc4f895..8d54728a75f 100644
--- a/src/libstd/primitive_docs.rs
+++ b/src/libstd/primitive_docs.rs
@@ -208,7 +208,7 @@ mod prim_bool { }
 /// # `!` and traits
 ///
 /// When writing your own traits, `!` should have an `impl` whenever there is an obvious `impl`
-/// which doesn't `panic!`. As is turns out, most traits can have an `impl` for `!`. Take [`Debug`]
+/// which doesn't `panic!`. As it turns out, most traits can have an `impl` for `!`. Take [`Debug`]
 /// for example:
 ///
 /// ```
@@ -228,9 +228,9 @@ mod prim_bool { }
 /// [`fmt::Result`]. Since this method takes a `&!` as an argument we know that it can never be
 /// called (because there is no value of type `!` for it to be called with). Writing `*self`
 /// essentially tells the compiler "We know that this code can never be run, so just treat the
-/// entire function body has having type [`fmt::Result`]". This pattern can be used a lot when
+/// entire function body as having type [`fmt::Result`]". This pattern can be used a lot when
 /// implementing traits for `!`. Generally, any trait which only has methods which take a `self`
-/// parameter should have such as impl.
+/// parameter should have such an impl.
 ///
 /// On the other hand, one trait which would not be appropriate to implement is [`Default`]:
 ///
diff --git a/src/libtest/lib.rs b/src/libtest/lib.rs
index 6ffa6e9be93..f6383c19cb5 100644
--- a/src/libtest/lib.rs
+++ b/src/libtest/lib.rs
@@ -45,9 +45,17 @@
 extern crate getopts;
 #[cfg(any(unix, target_os = "cloudabi"))]
 extern crate libc;
-extern crate panic_unwind;
 extern crate term;
 
+// FIXME(#54291): rustc and/or LLVM don't yet support building with panic-unwind
+//                on aarch64-pc-windows-msvc, so we don't link libtest against
+//                libunwind (for the time being), even though it means that
+//                libtest won't be fully functional on this platform.
+//
+// See also: https://github.com/rust-lang/rust/issues/54190#issuecomment-422904437
+#[cfg(not(all(windows, target_arch = "aarch64")))]
+extern crate panic_unwind;
+
 pub use self::TestFn::*;
 pub use self::ColorConfig::*;
 pub use self::TestResult::*;
diff --git a/src/test/compile-fail-fulldeps/proc-macro/proc-macro-attributes.rs b/src/test/compile-fail-fulldeps/proc-macro/proc-macro-attributes.rs
index 83bbb7c13c4..d0aed8b1624 100644
--- a/src/test/compile-fail-fulldeps/proc-macro/proc-macro-attributes.rs
+++ b/src/test/compile-fail-fulldeps/proc-macro/proc-macro-attributes.rs
@@ -11,13 +11,11 @@
 // aux-build:derive-b.rs
 // ignore-stage1
 
-#![allow(warnings)]
-
 #[macro_use]
 extern crate derive_b;
 
-#[B] //~ ERROR `B` is a derive mode
-#[C]
+#[B]
+#[C] //~ ERROR attribute `C` is currently unknown to the compiler
 #[B(D)]
 #[B(E = "foo")]
 #[B(arbitrary tokens)]
diff --git a/src/test/ui-fulldeps/custom-derive/auxiliary/plugin.rs b/src/test/ui-fulldeps/custom-derive/auxiliary/plugin.rs
index 7be909c3c9e..124bc05b7a3 100644
--- a/src/test/ui-fulldeps/custom-derive/auxiliary/plugin.rs
+++ b/src/test/ui-fulldeps/custom-derive/auxiliary/plugin.rs
@@ -25,3 +25,13 @@ pub fn derive_foo(input: TokenStream) -> TokenStream {
 pub fn derive_bar(input: TokenStream) -> TokenStream {
     panic!("lolnope");
 }
+
+#[proc_macro_derive(WithHelper, attributes(helper))]
+pub fn with_helper(input: TokenStream) -> TokenStream {
+    TokenStream::new()
+}
+
+#[proc_macro_attribute]
+pub fn helper(_: TokenStream, input: TokenStream) -> TokenStream {
+    input
+}
diff --git a/src/test/ui-fulldeps/custom-derive/helper-attr-blocked-by-import-ambig.rs b/src/test/ui-fulldeps/custom-derive/helper-attr-blocked-by-import-ambig.rs
new file mode 100644
index 00000000000..b750a8bb0d9
--- /dev/null
+++ b/src/test/ui-fulldeps/custom-derive/helper-attr-blocked-by-import-ambig.rs
@@ -0,0 +1,13 @@
+// aux-build:plugin.rs
+// ignore-stage1
+
+#[macro_use(WithHelper)]
+extern crate plugin;
+
+use plugin::helper;
+
+#[derive(WithHelper)]
+#[helper] //~ ERROR `helper` is ambiguous
+struct S;
+
+fn main() {}
diff --git a/src/test/ui-fulldeps/custom-derive/helper-attr-blocked-by-import-ambig.stderr b/src/test/ui-fulldeps/custom-derive/helper-attr-blocked-by-import-ambig.stderr
new file mode 100644
index 00000000000..059629c0b62
--- /dev/null
+++ b/src/test/ui-fulldeps/custom-derive/helper-attr-blocked-by-import-ambig.stderr
@@ -0,0 +1,20 @@
+error[E0659]: `helper` is ambiguous
+  --> $DIR/helper-attr-blocked-by-import-ambig.rs:10:3
+   |
+LL | #[helper] //~ ERROR `helper` is ambiguous
+   |   ^^^^^^ ambiguous name
+   |
+note: `helper` could refer to the name defined here
+  --> $DIR/helper-attr-blocked-by-import-ambig.rs:9:10
+   |
+LL | #[derive(WithHelper)]
+   |          ^^^^^^^^^^
+note: `helper` could also refer to the name imported here
+  --> $DIR/helper-attr-blocked-by-import-ambig.rs:7:5
+   |
+LL | use plugin::helper;
+   |     ^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0659`.
diff --git a/src/test/ui-fulldeps/custom-derive/helper-attr-blocked-by-import.rs b/src/test/ui-fulldeps/custom-derive/helper-attr-blocked-by-import.rs
new file mode 100644
index 00000000000..03b774f6c64
--- /dev/null
+++ b/src/test/ui-fulldeps/custom-derive/helper-attr-blocked-by-import.rs
@@ -0,0 +1,29 @@
+// compile-pass
+// aux-build:plugin.rs
+// ignore-stage1
+
+#[macro_use(WithHelper)]
+extern crate plugin;
+
+use self::one::*;
+use self::two::*;
+
+mod helper {}
+
+mod one {
+    use helper;
+
+    #[derive(WithHelper)]
+    #[helper]
+    struct One;
+}
+
+mod two {
+    use helper;
+
+    #[derive(WithHelper)]
+    #[helper]
+    struct Two;
+}
+
+fn main() {}
diff --git a/src/test/ui-fulldeps/proc-macro/derive-helper-shadowing.stderr b/src/test/ui-fulldeps/proc-macro/derive-helper-shadowing.stderr
index cdfecb3d101..e0aeae4ba6c 100644
--- a/src/test/ui-fulldeps/proc-macro/derive-helper-shadowing.stderr
+++ b/src/test/ui-fulldeps/proc-macro/derive-helper-shadowing.stderr
@@ -4,17 +4,16 @@ error[E0659]: `my_attr` is ambiguous
 LL | #[my_attr] //~ ERROR `my_attr` is ambiguous
    |   ^^^^^^^ ambiguous name
    |
-note: `my_attr` could refer to the name imported here
-  --> $DIR/derive-helper-shadowing.rs:4:5
-   |
-LL | use derive_helper_shadowing::*;
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^
-note: `my_attr` could also refer to the name defined here
+note: `my_attr` could refer to the name defined here
   --> $DIR/derive-helper-shadowing.rs:7:10
    |
 LL | #[derive(MyTrait)]
    |          ^^^^^^^
-   = note: consider adding an explicit import of `my_attr` to disambiguate
+note: `my_attr` could also refer to the name imported here
+  --> $DIR/derive-helper-shadowing.rs:4:5
+   |
+LL | use derive_helper_shadowing::*;
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: aborting due to previous error