diff --git a/src/doc/unstable-book/src/language-features/compile-error.md b/src/doc/unstable-book/src/language-features/compile-error.md
index 1b25eeda3f662..4b24c0a6a0d39 100644
--- a/src/doc/unstable-book/src/language-features/compile-error.md
+++ b/src/doc/unstable-book/src/language-features/compile-error.md
@@ -2,7 +2,7 @@
 
 The tracking issue for this feature is: [#40872]
 
-[#29599]: https://github.com/rust-lang/rust/issues/40872
+[#40872]: https://github.com/rust-lang/rust/issues/40872
 
 ------------------------
 
diff --git a/src/liballoc/allocator.rs b/src/liballoc/allocator.rs
index 9bddce29957e1..bf38629ed38a7 100644
--- a/src/liballoc/allocator.rs
+++ b/src/liballoc/allocator.rs
@@ -873,7 +873,7 @@ pub unsafe trait Alloc {
     {
         let k = Layout::new::<T>();
         if k.size() > 0 {
-            unsafe { self.alloc(k).map(|p|Unique::new(*p as *mut T)) }
+            unsafe { self.alloc(k).map(|p| Unique::new(p as *mut T)) }
         } else {
             Err(AllocErr::invalid_input("zero-sized type invalid for alloc_one"))
         }
diff --git a/src/liballoc/benches/lib.rs b/src/liballoc/benches/lib.rs
index 958020d0b0e0c..5f274eec87d47 100644
--- a/src/liballoc/benches/lib.rs
+++ b/src/liballoc/benches/lib.rs
@@ -17,6 +17,7 @@
 #![feature(sort_unstable)]
 #![feature(test)]
 
+extern crate rand;
 extern crate test;
 
 mod btree;
diff --git a/src/liballoc/benches/slice.rs b/src/liballoc/benches/slice.rs
index aa5a438b35e62..d99270e7f311e 100644
--- a/src/liballoc/benches/slice.rs
+++ b/src/liballoc/benches/slice.rs
@@ -8,9 +8,11 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-use std::{mem, ptr};
-use std::__rand::{Rng, thread_rng};
+use std::__rand::{thread_rng};
+use std::mem;
+use std::ptr;
 
+use rand::{Rng, SeedableRng, XorShiftRng};
 use test::{Bencher, black_box};
 
 #[bench]
@@ -191,17 +193,17 @@ fn gen_descending(len: usize) -> Vec<u64> {
 }
 
 fn gen_random(len: usize) -> Vec<u64> {
-    let mut rng = thread_rng();
+    let mut rng = XorShiftRng::from_seed([0, 1, 2, 3]);
     rng.gen_iter::<u64>().take(len).collect()
 }
 
 fn gen_random_bytes(len: usize) -> Vec<u8> {
-    let mut rng = thread_rng();
+    let mut rng = XorShiftRng::from_seed([0, 1, 2, 3]);
     rng.gen_iter::<u8>().take(len).collect()
 }
 
 fn gen_mostly_ascending(len: usize) -> Vec<u64> {
-    let mut rng = thread_rng();
+    let mut rng = XorShiftRng::from_seed([0, 1, 2, 3]);
     let mut v = gen_ascending(len);
     for _ in (0usize..).take_while(|x| x * x <= len) {
         let x = rng.gen::<usize>() % len;
@@ -212,7 +214,7 @@ fn gen_mostly_ascending(len: usize) -> Vec<u64> {
 }
 
 fn gen_mostly_descending(len: usize) -> Vec<u64> {
-    let mut rng = thread_rng();
+    let mut rng = XorShiftRng::from_seed([0, 1, 2, 3]);
     let mut v = gen_descending(len);
     for _ in (0usize..).take_while(|x| x * x <= len) {
         let x = rng.gen::<usize>() % len;
@@ -223,7 +225,7 @@ fn gen_mostly_descending(len: usize) -> Vec<u64> {
 }
 
 fn gen_strings(len: usize) -> Vec<String> {
-    let mut rng = thread_rng();
+    let mut rng = XorShiftRng::from_seed([0, 1, 2, 3]);
     let mut v = vec![];
     for _ in 0..len {
         let n = rng.gen::<usize>() % 20 + 1;
@@ -233,7 +235,7 @@ fn gen_strings(len: usize) -> Vec<String> {
 }
 
 fn gen_big_random(len: usize) -> Vec<[u64; 16]> {
-    let mut rng = thread_rng();
+    let mut rng = XorShiftRng::from_seed([0, 1, 2, 3]);
     rng.gen_iter().map(|x| [x; 16]).take(len).collect()
 }
 
@@ -241,18 +243,32 @@ macro_rules! sort {
     ($f:ident, $name:ident, $gen:expr, $len:expr) => {
         #[bench]
         fn $name(b: &mut Bencher) {
-            b.iter(|| $gen($len).$f());
+            let v = $gen($len);
+            b.iter(|| v.clone().$f());
             b.bytes = $len * mem::size_of_val(&$gen(1)[0]) as u64;
         }
     }
 }
 
+macro_rules! sort_strings {
+    ($f:ident, $name:ident, $gen:expr, $len:expr) => {
+        #[bench]
+        fn $name(b: &mut Bencher) {
+            let v = $gen($len);
+            let v = v.iter().map(|s| &**s).collect::<Vec<&str>>();
+            b.iter(|| v.clone().$f());
+            b.bytes = $len * mem::size_of::<&str>() as u64;
+        }
+    }
+}
+
 macro_rules! sort_expensive {
     ($f:ident, $name:ident, $gen:expr, $len:expr) => {
         #[bench]
         fn $name(b: &mut Bencher) {
+            let v = $gen($len);
             b.iter(|| {
-                let mut v = $gen($len);
+                let mut v = v.clone();
                 let mut count = 0;
                 v.$f(|a: &u64, b: &u64| {
                     count += 1;
@@ -263,7 +279,7 @@ macro_rules! sort_expensive {
                 });
                 black_box(count);
             });
-            b.bytes = $len as u64 * mem::size_of::<u64>() as u64;
+            b.bytes = $len * mem::size_of_val(&$gen(1)[0]) as u64;
         }
     }
 }
@@ -271,30 +287,30 @@ macro_rules! sort_expensive {
 sort!(sort, sort_small_ascending, gen_ascending, 10);
 sort!(sort, sort_small_descending, gen_descending, 10);
 sort!(sort, sort_small_random, gen_random, 10);
-sort!(sort, sort_small_big_random, gen_big_random, 10);
+sort!(sort, sort_small_big, gen_big_random, 10);
 sort!(sort, sort_medium_random, gen_random, 100);
 sort!(sort, sort_large_ascending, gen_ascending, 10000);
 sort!(sort, sort_large_descending, gen_descending, 10000);
 sort!(sort, sort_large_mostly_ascending, gen_mostly_ascending, 10000);
 sort!(sort, sort_large_mostly_descending, gen_mostly_descending, 10000);
 sort!(sort, sort_large_random, gen_random, 10000);
-sort!(sort, sort_large_big_random, gen_big_random, 10000);
-sort!(sort, sort_large_strings, gen_strings, 10000);
-sort_expensive!(sort_by, sort_large_random_expensive, gen_random, 10000);
+sort!(sort, sort_large_big, gen_big_random, 10000);
+sort_strings!(sort, sort_large_strings, gen_strings, 10000);
+sort_expensive!(sort_by, sort_large_expensive, gen_random, 10000);
 
 sort!(sort_unstable, sort_unstable_small_ascending, gen_ascending, 10);
 sort!(sort_unstable, sort_unstable_small_descending, gen_descending, 10);
 sort!(sort_unstable, sort_unstable_small_random, gen_random, 10);
-sort!(sort_unstable, sort_unstable_small_big_random, gen_big_random, 10);
+sort!(sort_unstable, sort_unstable_small_big, gen_big_random, 10);
 sort!(sort_unstable, sort_unstable_medium_random, gen_random, 100);
 sort!(sort_unstable, sort_unstable_large_ascending, gen_ascending, 10000);
 sort!(sort_unstable, sort_unstable_large_descending, gen_descending, 10000);
 sort!(sort_unstable, sort_unstable_large_mostly_ascending, gen_mostly_ascending, 10000);
 sort!(sort_unstable, sort_unstable_large_mostly_descending, gen_mostly_descending, 10000);
 sort!(sort_unstable, sort_unstable_large_random, gen_random, 10000);
-sort!(sort_unstable, sort_unstable_large_big_random, gen_big_random, 10000);
-sort!(sort_unstable, sort_unstable_large_strings, gen_strings, 10000);
-sort_expensive!(sort_unstable_by, sort_unstable_large_random_expensive, gen_random, 10000);
+sort!(sort_unstable, sort_unstable_large_big, gen_big_random, 10000);
+sort_strings!(sort_unstable, sort_unstable_large_strings, gen_strings, 10000);
+sort_expensive!(sort_unstable_by, sort_unstable_large_expensive, gen_random, 10000);
 
 macro_rules! reverse {
     ($name:ident, $ty:ty, $f:expr) => {
diff --git a/src/liballoc/fmt.rs b/src/liballoc/fmt.rs
index 1bd95fb82aa4d..4847b21c0b3b5 100644
--- a/src/liballoc/fmt.rs
+++ b/src/liballoc/fmt.rs
@@ -498,12 +498,10 @@ pub use core::fmt::{DebugList, DebugMap, DebugSet, DebugStruct, DebugTuple};
 
 use string;
 
-/// The format function takes a precompiled format string and a list of
-/// arguments, to return the resulting formatted string.
+/// The `format` function takes an `Arguments` struct and returns the resulting
+/// formatted string.
 ///
-/// # Arguments
-///
-///   * args - a structure of arguments generated via the `format_args!` macro.
+/// The `Arguments` instance can be created with the `format_args!` macro.
 ///
 /// # Examples
 ///
diff --git a/src/liballoc/slice.rs b/src/liballoc/slice.rs
index 88876999d765a..fe8893904eba1 100644
--- a/src/liballoc/slice.rs
+++ b/src/liballoc/slice.rs
@@ -1794,7 +1794,7 @@ unsafe fn merge<T, F>(v: &mut [T], mid: usize, buf: *mut T, is_less: &mut F)
 
     impl<T> Drop for MergeHole<T> {
         fn drop(&mut self) {
-            // `T` is not a zero-sized type, so it's okay to divide by it's size.
+            // `T` is not a zero-sized type, so it's okay to divide by its size.
             let len = (self.end as usize - self.start as usize) / mem::size_of::<T>();
             unsafe { ptr::copy_nonoverlapping(self.start, self.dest, len); }
         }
@@ -1908,7 +1908,7 @@ fn merge_sort<T, F>(v: &mut [T], mut is_less: F)
     // if `Some(r)` is returned, that means `runs[r]` and `runs[r + 1]` must be merged next. If the
     // algorithm should continue building a new run instead, `None` is returned.
     //
-    // TimSort is infamous for it's buggy implementations, as described here:
+    // TimSort is infamous for its buggy implementations, as described here:
     // http://envisage-project.eu/timsort-specification-and-verification/
     //
     // The gist of the story is: we must enforce the invariants on the top four runs on the stack.
diff --git a/src/liballoc/tests/slice.rs b/src/liballoc/tests/slice.rs
index 7fa65a2144e9b..c53bf15f1bfb6 100644
--- a/src/liballoc/tests/slice.rs
+++ b/src/liballoc/tests/slice.rs
@@ -396,18 +396,44 @@ fn test_sort() {
     let mut rng = thread_rng();
 
     for len in (2..25).chain(500..510) {
-        for _ in 0..100 {
-            let mut v: Vec<_> = rng.gen_iter::<i32>().take(len).collect();
-            let mut v1 = v.clone();
-
-            v.sort();
-            assert!(v.windows(2).all(|w| w[0] <= w[1]));
-
-            v1.sort_by(|a, b| a.cmp(b));
-            assert!(v1.windows(2).all(|w| w[0] <= w[1]));
-
-            v1.sort_by(|a, b| b.cmp(a));
-            assert!(v1.windows(2).all(|w| w[0] >= w[1]));
+        for &modulus in &[5, 10, 100, 1000] {
+            for _ in 0..10 {
+                let orig: Vec<_> = rng.gen_iter::<i32>()
+                    .map(|x| x % modulus)
+                    .take(len)
+                    .collect();
+
+                // Sort in default order.
+                let mut v = orig.clone();
+                v.sort();
+                assert!(v.windows(2).all(|w| w[0] <= w[1]));
+
+                // Sort in ascending order.
+                let mut v = orig.clone();
+                v.sort_by(|a, b| a.cmp(b));
+                assert!(v.windows(2).all(|w| w[0] <= w[1]));
+
+                // Sort in descending order.
+                let mut v = orig.clone();
+                v.sort_by(|a, b| b.cmp(a));
+                assert!(v.windows(2).all(|w| w[0] >= w[1]));
+
+                // Sort with many pre-sorted runs.
+                let mut v = orig.clone();
+                v.sort();
+                v.reverse();
+                for _ in 0..5 {
+                    let a = rng.gen::<usize>() % len;
+                    let b = rng.gen::<usize>() % len;
+                    if a < b {
+                        v[a..b].reverse();
+                    } else {
+                        v.swap(a, b);
+                    }
+                }
+                v.sort();
+                assert!(v.windows(2).all(|w| w[0] <= w[1]));
+            }
         }
     }
 
diff --git a/src/libcore/fmt/mod.rs b/src/libcore/fmt/mod.rs
index 8c3d3ce7d886b..bcc6d53c81d3b 100644
--- a/src/libcore/fmt/mod.rs
+++ b/src/libcore/fmt/mod.rs
@@ -897,14 +897,11 @@ pub trait UpperExp {
     fn fmt(&self, f: &mut Formatter) -> Result;
 }
 
-/// The `write` function takes an output stream, a precompiled format string,
-/// and a list of arguments. The arguments will be formatted according to the
-/// specified format string into the output stream provided.
+/// The `write` function takes an output stream, and an `Arguments` struct
+/// that can be precompiled with the `format_args!` macro.
 ///
-/// # Arguments
-///
-///   * output - the buffer to write output to
-///   * args - the precompiled arguments generated by `format_args!`
+/// The arguments will be formatted according to the specified format string
+/// into the output stream provided.
 ///
 /// # Examples
 ///
diff --git a/src/librustc/infer/error_reporting/mod.rs b/src/librustc/infer/error_reporting/mod.rs
index 11bac21bc429e..2265c0c0a8c70 100644
--- a/src/librustc/infer/error_reporting/mod.rs
+++ b/src/librustc/infer/error_reporting/mod.rs
@@ -337,9 +337,12 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
             if !(did1.is_local() || did2.is_local()) && did1.krate != did2.krate {
                 let exp_path = self.tcx.item_path_str(did1);
                 let found_path = self.tcx.item_path_str(did2);
+                let exp_abs_path = self.tcx.absolute_item_path_str(did1);
+                let found_abs_path = self.tcx.absolute_item_path_str(did2);
                 // We compare strings because DefPath can be different
                 // for imported and non-imported crates
-                if exp_path == found_path {
+                if exp_path == found_path
+                || exp_abs_path == found_abs_path {
                     let crate_name = self.tcx.sess.cstore.crate_name(did1.krate);
                     err.span_note(sp, &format!("Perhaps two different versions \
                                                 of crate `{}` are being used?",
diff --git a/src/libstd/fs.rs b/src/libstd/fs.rs
index 5b8c0c339900d..88994b284c90d 100644
--- a/src/libstd/fs.rs
+++ b/src/libstd/fs.rs
@@ -653,15 +653,29 @@ impl OpenOptions {
     /// # Errors
     ///
     /// This function will return an error under a number of different
-    /// circumstances, to include but not limited to:
-    ///
-    /// * Opening a file that does not exist without setting `create` or
-    ///   `create_new`.
-    /// * Attempting to open a file with access that the user lacks
-    ///   permissions for
-    /// * Filesystem-level errors (full disk, etc)
-    /// * Invalid combinations of open options (truncate without write access,
-    ///   no access mode set, etc)
+    /// circumstances. Some of these error conditions are listed here, together
+    /// with their [`ErrorKind`]. The mapping to [`ErrorKind`]s is not part of
+    /// the compatiblity contract of the function, especially the `Other` kind
+    /// might change to more specific kinds in the future.
+    ///
+    /// * [`NotFound`]: The specified file does not exist and neither `create`
+    ///   or `create_new` is set.
+    /// * [`NotFound`]: One of the directory components of the file path does
+    ///   not exist.
+    /// * [`PermissionDenied`]: The user lacks permission to get the specified
+    ///   access rights for the file.
+    /// * [`PermissionDenied`]: The user lacks permission to open one of the
+    ///   directory components of the specified path.
+    /// * [`AlreadyExists`]: `create_new` was specified and the file already
+    ///   exists.
+    /// * [`InvalidInput`]: Invalid combinations of open options (truncate
+    ///   without write access, no access mode set, etc.).
+    /// * [`Other`]: One of the directory components of the specified file path
+    ///   was not, in fact, a directory.
+    /// * [`Other`]: Filesystem-level errors: full disk, write permission
+    ///   requested on a read-only file system, exceeded disk quota, too many
+    ///   open files, too long filename, too many symbolic links in the
+    ///   specified path (Unix-like systems only), etc.
     ///
     /// # Examples
     ///
@@ -670,6 +684,13 @@ impl OpenOptions {
     ///
     /// let file = OpenOptions::new().open("foo.txt");
     /// ```
+    ///
+    /// [`ErrorKind`]: ../io/enum.ErrorKind.html
+    /// [`AlreadyExists`]: ../io/enum.ErrorKind.html#variant.AlreadyExists
+    /// [`InvalidInput`]: ../io/enum.ErrorKind.html#variant.InvalidInput
+    /// [`NotFound`]: ../io/enum.ErrorKind.html#variant.NotFound
+    /// [`Other`]: ../io/enum.ErrorKind.html#variant.Other
+    /// [`PermissionDenied`]: ../io/enum.ErrorKind.html#variant.PermissionDenied
     #[stable(feature = "rust1", since = "1.0.0")]
     pub fn open<P: AsRef<Path>>(&self, path: P) -> io::Result<File> {
         self._open(path.as_ref())
diff --git a/src/test/run-make/type-mismatch-same-crate-name/Makefile b/src/test/run-make/type-mismatch-same-crate-name/Makefile
new file mode 100644
index 0000000000000..24ef203278a74
--- /dev/null
+++ b/src/test/run-make/type-mismatch-same-crate-name/Makefile
@@ -0,0 +1,19 @@
+-include ../tools.mk
+
+all:
+	# compile two different versions of crateA
+	$(RUSTC) --crate-type=rlib crateA.rs -C metadata=-1 -C extra-filename=-1
+	$(RUSTC) --crate-type=rlib crateA.rs -C metadata=-2 -C extra-filename=-2
+	# make crateB depend on version 1 of crateA
+	$(RUSTC) --crate-type=rlib crateB.rs --extern crateA=$(TMPDIR)/libcrateA-1.rlib
+	# make crateC depend on version 2 of crateA
+	$(RUSTC) crateC.rs --extern crateA=$(TMPDIR)/libcrateA-2.rlib 2>&1 | \
+		grep -z \
+	"mismatched types.*\
+	crateB::try_foo(foo2);.*\
+	expected struct \`crateA::foo::Foo\`, found struct \`crateA::Foo\`.*\
+	different versions of crate \`crateA\`.*\
+	mismatched types.*\
+	crateB::try_bar(bar2);.*\
+	expected trait \`crateA::bar::Bar\`, found trait \`crateA::Bar\`.*\
+	different versions of crate \`crateA\`"
diff --git a/src/test/run-make/type-mismatch-same-crate-name/crateA.rs b/src/test/run-make/type-mismatch-same-crate-name/crateA.rs
new file mode 100644
index 0000000000000..e40266bb4cdaa
--- /dev/null
+++ b/src/test/run-make/type-mismatch-same-crate-name/crateA.rs
@@ -0,0 +1,26 @@
+// Copyright 2017 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+mod foo {
+    pub struct Foo;
+}
+
+mod bar {
+    pub trait Bar{}
+
+    pub fn bar() -> Box<Bar> {
+        unimplemented!()
+    }
+}
+
+// This makes the publicly accessible path
+// differ from the internal one.
+pub use foo::Foo;
+pub use bar::{Bar, bar};
diff --git a/src/test/run-make/type-mismatch-same-crate-name/crateB.rs b/src/test/run-make/type-mismatch-same-crate-name/crateB.rs
new file mode 100644
index 0000000000000..da4ea1c9387e9
--- /dev/null
+++ b/src/test/run-make/type-mismatch-same-crate-name/crateB.rs
@@ -0,0 +1,14 @@
+// Copyright 2017 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+extern crate crateA;
+
+pub fn try_foo(x: crateA::Foo){}
+pub fn try_bar(x: Box<crateA::Bar>){}
diff --git a/src/test/run-make/type-mismatch-same-crate-name/crateC.rs b/src/test/run-make/type-mismatch-same-crate-name/crateC.rs
new file mode 100644
index 0000000000000..da869d2145fe1
--- /dev/null
+++ b/src/test/run-make/type-mismatch-same-crate-name/crateC.rs
@@ -0,0 +1,35 @@
+// Copyright 2017 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// This tests the extra note reported when a type error deals with
+// seemingly identical types.
+// The main use case of this error is when there are two crates
+// (generally different versions of the same crate) with the same name
+// causing a type mismatch.
+
+// The test is nearly the same as the one in
+// compile-fail/type-mismatch-same-crate-name.rs
+// but deals with the case where one of the crates
+// is only introduced as an indirect dependency.
+// and the type is accessed via a reexport.
+// This is similar to how the error can be introduced
+// when using cargo's automatic dependency resolution.
+
+extern crate crateA;
+
+fn main() {
+    let foo2 = crateA::Foo;
+    let bar2 = crateA::bar();
+    {
+        extern crate crateB;
+        crateB::try_foo(foo2);
+        crateB::try_bar(bar2);
+    }
+}
diff --git a/src/test/run-pass/allocator-alloc-one.rs b/src/test/run-pass/allocator-alloc-one.rs
new file mode 100644
index 0000000000000..7cc547dcc04e2
--- /dev/null
+++ b/src/test/run-pass/allocator-alloc-one.rs
@@ -0,0 +1,27 @@
+// Copyright 2017 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(alloc, allocator_api, heap_api, unique)]
+
+extern crate alloc;
+
+use alloc::heap::HeapAlloc;
+use alloc::allocator::Alloc;
+
+fn main() {
+    unsafe {
+        let ptr = HeapAlloc.alloc_one::<i32>().unwrap_or_else(|e| {
+            HeapAlloc.oom(e)
+        });
+        *ptr.as_ptr() = 4;
+        assert_eq!(*ptr.as_ptr(), 4);
+        HeapAlloc.dealloc_one(ptr);
+    }
+}
diff --git a/src/test/run-pass/vector-sort-panic-safe.rs b/src/test/run-pass/vector-sort-panic-safe.rs
index 8ad6ca0abb027..4387a43f03b6a 100644
--- a/src/test/run-pass/vector-sort-panic-safe.rs
+++ b/src/test/run-pass/vector-sort-panic-safe.rs
@@ -10,14 +10,17 @@
 
 // ignore-emscripten no threads support
 
-#![feature(rand)]
 #![feature(const_fn)]
+#![feature(rand)]
+#![feature(sort_unstable)]
 
 use std::__rand::{thread_rng, Rng};
+use std::cell::Cell;
+use std::cmp::Ordering;
 use std::panic;
-use std::sync::atomic::{AtomicUsize, Ordering};
+use std::sync::atomic::{ATOMIC_USIZE_INIT, AtomicUsize};
+use std::sync::atomic::Ordering::Relaxed;
 use std::thread;
-use std::cell::Cell;
 
 const MAX_LEN: usize = 80;
 
@@ -45,54 +48,85 @@ static DROP_COUNTS: [AtomicUsize; MAX_LEN] = [
     AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0),
 ];
 
-#[derive(Clone, PartialEq, PartialOrd, Eq, Ord)]
+static VERSIONS: AtomicUsize = ATOMIC_USIZE_INIT;
+
+#[derive(Clone, Eq)]
 struct DropCounter {
     x: u32,
     id: usize,
+    version: Cell<usize>,
+}
+
+impl PartialEq for DropCounter {
+    fn eq(&self, other: &Self) -> bool {
+        self.partial_cmp(other) == Some(Ordering::Equal)
+    }
+}
+
+impl PartialOrd for DropCounter {
+    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+        self.version.set(self.version.get() + 1);
+        other.version.set(other.version.get() + 1);
+        VERSIONS.fetch_add(2, Relaxed);
+        self.x.partial_cmp(&other.x)
+    }
+}
+
+impl Ord for DropCounter {
+    fn cmp(&self, other: &Self) -> Ordering {
+        self.partial_cmp(other).unwrap()
+    }
 }
 
 impl Drop for DropCounter {
     fn drop(&mut self) {
-        DROP_COUNTS[self.id].fetch_add(1, Ordering::Relaxed);
+        DROP_COUNTS[self.id].fetch_add(1, Relaxed);
+        VERSIONS.fetch_sub(self.version.get(), Relaxed);
     }
 }
 
-fn test(input: &[DropCounter]) {
-    let len = input.len();
-
-    // Work out the total number of comparisons required to sort
-    // this array...
-    let mut count = 0usize;
-    input.to_owned().sort_by(|a, b| { count += 1; a.cmp(b) });
+macro_rules! test {
+    ($input:ident, $func:ident) => {
+        let len = $input.len();
+
+        // Work out the total number of comparisons required to sort
+        // this array...
+        let mut count = 0usize;
+        $input.to_owned().$func(|a, b| { count += 1; a.cmp(b) });
+
+        // ... and then panic on each and every single one.
+        for panic_countdown in 0..count {
+            // Refresh the counters.
+            VERSIONS.store(0, Relaxed);
+            for i in 0..len {
+                DROP_COUNTS[i].store(0, Relaxed);
+            }
 
-    // ... and then panic on each and every single one.
-    for panic_countdown in 0..count {
-        // Refresh the counters.
-        for i in 0..len {
-            DROP_COUNTS[i].store(0, Ordering::Relaxed);
-        }
+            let v = $input.to_owned();
+            let _ = thread::spawn(move || {
+                let mut v = v;
+                let mut panic_countdown = panic_countdown;
+                v.$func(|a, b| {
+                    if panic_countdown == 0 {
+                        SILENCE_PANIC.with(|s| s.set(true));
+                        panic!();
+                    }
+                    panic_countdown -= 1;
+                    a.cmp(b)
+                })
+            }).join();
+
+            // Check that the number of things dropped is exactly
+            // what we expect (i.e. the contents of `v`).
+            for (i, c) in DROP_COUNTS.iter().enumerate().take(len) {
+                let count = c.load(Relaxed);
+                assert!(count == 1,
+                        "found drop count == {} for i == {}, len == {}",
+                        count, i, len);
+            }
 
-        let v = input.to_owned();
-        let _ = thread::spawn(move || {
-            let mut v = v;
-            let mut panic_countdown = panic_countdown;
-            v.sort_by(|a, b| {
-                if panic_countdown == 0 {
-                    SILENCE_PANIC.with(|s| s.set(true));
-                    panic!();
-                }
-                panic_countdown -= 1;
-                a.cmp(b)
-            })
-        }).join();
-
-        // Check that the number of things dropped is exactly
-        // what we expect (i.e. the contents of `v`).
-        for (i, c) in DROP_COUNTS.iter().enumerate().take(len) {
-            let count = c.load(Ordering::Relaxed);
-            assert!(count == 1,
-                    "found drop count == {} for i == {}, len == {}",
-                    count, i, len);
+            // Check that the most recent versions of values were dropped.
+            assert_eq!(VERSIONS.load(Relaxed), 0);
         }
     }
 }
@@ -106,33 +140,41 @@ fn main() {
             prev(info);
         }
     }));
+
+    let mut rng = thread_rng();
+
     for len in (1..20).chain(70..MAX_LEN) {
-        // Test on a random array.
-        let mut rng = thread_rng();
-        let input = (0..len).map(|id| {
-            DropCounter {
-                x: rng.next_u32(),
-                id: id,
-            }
-        }).collect::<Vec<_>>();
-        test(&input);
-
-        // Test on a sorted array with two elements randomly swapped, creating several natural
-        // runs of random lengths. Such arrays have very high chances of hitting all code paths in
-        // the merge procedure.
-        for _ in 0..5 {
-            let mut input = (0..len).map(|i|
-                DropCounter {
-                    x: i as u32,
-                    id: i,
+        for &modulus in &[5, 20, 50] {
+            for &has_runs in &[false, true] {
+                let mut input = (0..len)
+                    .map(|id| {
+                        DropCounter {
+                            x: rng.next_u32() % modulus,
+                            id: id,
+                            version: Cell::new(0),
+                        }
+                    })
+                    .collect::<Vec<_>>();
+
+                if has_runs {
+                    for c in &mut input {
+                        c.x = c.id as u32;
+                    }
+
+                    for _ in 0..5 {
+                        let a = rng.gen::<usize>() % len;
+                        let b = rng.gen::<usize>() % len;
+                        if a < b {
+                            input[a..b].reverse();
+                        } else {
+                            input.swap(a, b);
+                        }
+                    }
                 }
-            ).collect::<Vec<_>>();
-
-            let a = rng.gen::<usize>() % len;
-            let b = rng.gen::<usize>() % len;
-            input.swap(a, b);
 
-            test(&input);
+                test!(input, sort_by);
+                test!(input, sort_unstable_by);
+            }
         }
     }
 }