about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_error_codes/src/error_codes.rs2
-rw-r--r--compiler/rustc_error_codes/src/error_codes/E0755.md28
-rw-r--r--compiler/rustc_lexer/src/lib.rs2
-rw-r--r--compiler/rustc_typeck/src/check/demand.rs25
-rw-r--r--library/alloc/benches/vec.rs28
-rw-r--r--library/alloc/src/collections/btree/map/tests.rs19
-rw-r--r--library/alloc/src/collections/btree/node.rs12
-rw-r--r--library/core/src/sync/atomic.rs90
-rw-r--r--library/std/src/sys/unix/thread.rs10
-rw-r--r--library/test/src/lib.rs1
-rw-r--r--library/test/src/stats.rs18
-rw-r--r--src/test/ui/ffi_pure.stderr1
-rw-r--r--src/test/ui/mir/issue-76740-copy-propagation.rs30
m---------src/tools/miri16
14 files changed, 127 insertions, 155 deletions
diff --git a/compiler/rustc_error_codes/src/error_codes.rs b/compiler/rustc_error_codes/src/error_codes.rs
index b0be1bf7e72..23a7b08016e 100644
--- a/compiler/rustc_error_codes/src/error_codes.rs
+++ b/compiler/rustc_error_codes/src/error_codes.rs
@@ -440,6 +440,7 @@ E0751: include_str!("./error_codes/E0751.md"),
 E0752: include_str!("./error_codes/E0752.md"),
 E0753: include_str!("./error_codes/E0753.md"),
 E0754: include_str!("./error_codes/E0754.md"),
+E0755: include_str!("./error_codes/E0755.md"),
 E0758: include_str!("./error_codes/E0758.md"),
 E0759: include_str!("./error_codes/E0759.md"),
 E0760: include_str!("./error_codes/E0760.md"),
@@ -632,7 +633,6 @@ E0774: include_str!("./error_codes/E0774.md"),
     E0722, // Malformed `#[optimize]` attribute
     E0726, // non-explicit (not `'_`) elided lifetime in unsupported position
 //  E0738, // Removed; errored on `#[track_caller] fn`s in `extern "Rust" { ... }`.
-    E0755, // `#[ffi_pure]` is only allowed on foreign functions
     E0756, // `#[ffi_const]` is only allowed on foreign functions
     E0757, // `#[ffi_const]` functions cannot be `#[ffi_pure]`
     E0772, // `'static' obligation coming from `impl dyn Trait {}` or `impl Foo for dyn Bar {}`.
diff --git a/compiler/rustc_error_codes/src/error_codes/E0755.md b/compiler/rustc_error_codes/src/error_codes/E0755.md
new file mode 100644
index 00000000000..88b7f484969
--- /dev/null
+++ b/compiler/rustc_error_codes/src/error_codes/E0755.md
@@ -0,0 +1,28 @@
+The `ffi_pure` attribute was used on a non-foreign function.
+
+Erroneous code example:
+
+```compile_fail,E0755
+#![feature(ffi_pure)]
+
+#[ffi_pure] // error!
+pub fn foo() {}
+# fn main() {}
+```
+
+The `ffi_pure` attribute can only be used on foreign functions which do not have
+side effects or infinite loops:
+
+```
+#![feature(ffi_pure)]
+
+extern "C" {
+    #[ffi_pure] // ok!
+    pub fn strlen(s: *const i8) -> isize;
+}
+# fn main() {}
+```
+
+You can find more information about it in the [unstable Rust Book].
+
+[unstable Rust Book]: https://doc.rust-lang.org/unstable-book/language-features/ffi-pure.html
diff --git a/compiler/rustc_lexer/src/lib.rs b/compiler/rustc_lexer/src/lib.rs
index 44999bbe857..d784a86f14c 100644
--- a/compiler/rustc_lexer/src/lib.rs
+++ b/compiler/rustc_lexer/src/lib.rs
@@ -2,7 +2,7 @@
 //!
 //! The idea with `librustc_lexer` is to make a reusable library,
 //! by separating out pure lexing and rustc-specific concerns, like spans,
-//! error reporting an interning.  So, rustc_lexer operates directly on `&str`,
+//! error reporting, and interning.  So, rustc_lexer operates directly on `&str`,
 //! produces simple tokens which are a pair of type-tag and a bit of original text,
 //! and does not report errors, instead storing them as flags on the token.
 //!
diff --git a/compiler/rustc_typeck/src/check/demand.rs b/compiler/rustc_typeck/src/check/demand.rs
index c7ce5008c33..247bbf637ce 100644
--- a/compiler/rustc_typeck/src/check/demand.rs
+++ b/compiler/rustc_typeck/src/check/demand.rs
@@ -362,16 +362,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         false
     }
 
-    fn replace_prefix<A, B, C>(&self, s: A, old: B, new: C) -> Option<String>
-    where
-        A: AsRef<str>,
-        B: AsRef<str>,
-        C: AsRef<str>,
-    {
-        let s = s.as_ref();
-        let old = old.as_ref();
+    fn replace_prefix(&self, s: &str, old: &str, new: &str) -> Option<String> {
         if let Some(stripped) = s.strip_prefix(old) {
-            Some(new.as_ref().to_owned() + stripped)
+            Some(new.to_string() + stripped)
         } else {
             None
         }
@@ -422,7 +415,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 (&ty::Str, &ty::Array(arr, _) | &ty::Slice(arr)) if arr == self.tcx.types.u8 => {
                     if let hir::ExprKind::Lit(_) = expr.kind {
                         if let Ok(src) = sm.span_to_snippet(sp) {
-                            if let Some(src) = self.replace_prefix(src, "b\"", "\"") {
+                            if let Some(src) = self.replace_prefix(&src, "b\"", "\"") {
                                 return Some((
                                     sp,
                                     "consider removing the leading `b`",
@@ -436,7 +429,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 (&ty::Array(arr, _) | &ty::Slice(arr), &ty::Str) if arr == self.tcx.types.u8 => {
                     if let hir::ExprKind::Lit(_) = expr.kind {
                         if let Ok(src) = sm.span_to_snippet(sp) {
-                            if let Some(src) = self.replace_prefix(src, "\"", "b\"") {
+                            if let Some(src) = self.replace_prefix(&src, "\"", "b\"") {
                                 return Some((
                                     sp,
                                     "consider adding a leading `b`",
@@ -561,7 +554,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 // we may want to suggest removing a `&`.
                 if sm.is_imported(expr.span) {
                     if let Ok(src) = sm.span_to_snippet(sp) {
-                        if let Some(src) = self.replace_prefix(src, "&", "") {
+                        if let Some(src) = self.replace_prefix(&src, "&", "") {
                             return Some((
                                 sp,
                                 "consider removing the borrow",
@@ -598,7 +591,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                                     match mutbl_a {
                                         hir::Mutability::Mut => {
                                             if let Some(s) =
-                                                self.replace_prefix(src, "&mut ", new_prefix)
+                                                self.replace_prefix(&src, "&mut ", &new_prefix)
                                             {
                                                 Some((s, Applicability::MachineApplicable))
                                             } else {
@@ -607,7 +600,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                                         }
                                         hir::Mutability::Not => {
                                             if let Some(s) =
-                                                self.replace_prefix(src, "&", new_prefix)
+                                                self.replace_prefix(&src, "&", &new_prefix)
                                             {
                                                 Some((s, Applicability::Unspecified))
                                             } else {
@@ -621,7 +614,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                                     match mutbl_a {
                                         hir::Mutability::Mut => {
                                             if let Some(s) =
-                                                self.replace_prefix(src, "&mut ", new_prefix)
+                                                self.replace_prefix(&src, "&mut ", &new_prefix)
                                             {
                                                 Some((s, Applicability::MachineApplicable))
                                             } else {
@@ -630,7 +623,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                                         }
                                         hir::Mutability::Not => {
                                             if let Some(s) =
-                                                self.replace_prefix(src, "&", new_prefix)
+                                                self.replace_prefix(&src, "&", &new_prefix)
                                             {
                                                 Some((s, Applicability::MachineApplicable))
                                             } else {
diff --git a/library/alloc/benches/vec.rs b/library/alloc/benches/vec.rs
index 6703a99b151..b295342f361 100644
--- a/library/alloc/benches/vec.rs
+++ b/library/alloc/benches/vec.rs
@@ -496,7 +496,7 @@ bench_in_place![
 ];
 
 #[bench]
-fn bench_in_place_recycle(b: &mut test::Bencher) {
+fn bench_in_place_recycle(b: &mut Bencher) {
     let mut data = vec![0; 1000];
 
     b.iter(|| {
@@ -513,7 +513,7 @@ fn bench_in_place_recycle(b: &mut test::Bencher) {
 }
 
 #[bench]
-fn bench_in_place_zip_recycle(b: &mut test::Bencher) {
+fn bench_in_place_zip_recycle(b: &mut Bencher) {
     let mut data = vec![0u8; 1000];
     let mut rng = rand::thread_rng();
     let mut subst = vec![0u8; 1000];
@@ -533,7 +533,7 @@ fn bench_in_place_zip_recycle(b: &mut test::Bencher) {
 }
 
 #[bench]
-fn bench_in_place_zip_iter_mut(b: &mut test::Bencher) {
+fn bench_in_place_zip_iter_mut(b: &mut Bencher) {
     let mut data = vec![0u8; 256];
     let mut rng = rand::thread_rng();
     let mut subst = vec![0u8; 1000];
@@ -558,7 +558,7 @@ impl Drop for Droppable {
 }
 
 #[bench]
-fn bench_in_place_collect_droppable(b: &mut test::Bencher) {
+fn bench_in_place_collect_droppable(b: &mut Bencher) {
     let v: Vec<Droppable> = std::iter::repeat_with(|| Droppable(0)).take(1000).collect();
     b.iter(|| {
         v.clone()
@@ -571,13 +571,13 @@ fn bench_in_place_collect_droppable(b: &mut test::Bencher) {
 }
 
 #[bench]
-fn bench_chain_collect(b: &mut test::Bencher) {
+fn bench_chain_collect(b: &mut Bencher) {
     let data = black_box([0; LEN]);
     b.iter(|| data.iter().cloned().chain([1].iter().cloned()).collect::<Vec<_>>());
 }
 
 #[bench]
-fn bench_chain_chain_collect(b: &mut test::Bencher) {
+fn bench_chain_chain_collect(b: &mut Bencher) {
     let data = black_box([0; LEN]);
     b.iter(|| {
         data.iter()
@@ -589,7 +589,7 @@ fn bench_chain_chain_collect(b: &mut test::Bencher) {
 }
 
 #[bench]
-fn bench_nest_chain_chain_collect(b: &mut test::Bencher) {
+fn bench_nest_chain_chain_collect(b: &mut Bencher) {
     let data = black_box([0; LEN]);
     b.iter(|| {
         data.iter().cloned().chain([1].iter().chain([2].iter()).cloned()).collect::<Vec<_>>()
@@ -616,12 +616,12 @@ pub fn map_fast(l: &[(u32, u32)]) -> Vec<u32> {
 const LEN: usize = 16384;
 
 #[bench]
-fn bench_range_map_collect(b: &mut test::Bencher) {
+fn bench_range_map_collect(b: &mut Bencher) {
     b.iter(|| (0..LEN).map(|_| u32::default()).collect::<Vec<_>>());
 }
 
 #[bench]
-fn bench_chain_extend_ref(b: &mut test::Bencher) {
+fn bench_chain_extend_ref(b: &mut Bencher) {
     let data = black_box([0; LEN]);
     b.iter(|| {
         let mut v = Vec::<u32>::with_capacity(data.len() + 1);
@@ -631,7 +631,7 @@ fn bench_chain_extend_ref(b: &mut test::Bencher) {
 }
 
 #[bench]
-fn bench_chain_extend_value(b: &mut test::Bencher) {
+fn bench_chain_extend_value(b: &mut Bencher) {
     let data = black_box([0; LEN]);
     b.iter(|| {
         let mut v = Vec::<u32>::with_capacity(data.len() + 1);
@@ -641,7 +641,7 @@ fn bench_chain_extend_value(b: &mut test::Bencher) {
 }
 
 #[bench]
-fn bench_rev_1(b: &mut test::Bencher) {
+fn bench_rev_1(b: &mut Bencher) {
     let data = black_box([0; LEN]);
     b.iter(|| {
         let mut v = Vec::<u32>::new();
@@ -651,13 +651,13 @@ fn bench_rev_1(b: &mut test::Bencher) {
 }
 
 #[bench]
-fn bench_rev_2(b: &mut test::Bencher) {
+fn bench_rev_2(b: &mut Bencher) {
     let data = black_box([0; LEN]);
     b.iter(|| example_plain_slow(&data));
 }
 
 #[bench]
-fn bench_map_regular(b: &mut test::Bencher) {
+fn bench_map_regular(b: &mut Bencher) {
     let data = black_box([(0, 0); LEN]);
     b.iter(|| {
         let mut v = Vec::<u32>::new();
@@ -667,7 +667,7 @@ fn bench_map_regular(b: &mut test::Bencher) {
 }
 
 #[bench]
-fn bench_map_fast(b: &mut test::Bencher) {
+fn bench_map_fast(b: &mut Bencher) {
     let data = black_box([(0, 0); LEN]);
     b.iter(|| map_fast(&data));
 }
diff --git a/library/alloc/src/collections/btree/map/tests.rs b/library/alloc/src/collections/btree/map/tests.rs
index d2cd6b8e524..8018514fa17 100644
--- a/library/alloc/src/collections/btree/map/tests.rs
+++ b/library/alloc/src/collections/btree/map/tests.rs
@@ -88,6 +88,11 @@ impl<'a, K: 'a, V: 'a> BTreeMap<K, V> {
                     let min_len = if is_root { 1 } else { node::MIN_LEN };
                     assert!(node.len() >= min_len, "{} < {}", node.len(), min_len);
 
+                    for idx in 0..=node.len() {
+                        let edge = unsafe { node::Handle::new_edge(node, idx) };
+                        assert!(edge.descend().ascend().ok().unwrap() == edge);
+                    }
+
                     internal_length += node.len();
                 }
                 Position::InternalKV(kv) => {
@@ -1846,3 +1851,17 @@ fn test_into_values() {
     assert!(values.contains(&'b'));
     assert!(values.contains(&'c'));
 }
+
+#[test]
+fn test_insert_remove_intertwined() {
+    let loops = if cfg!(miri) { 100 } else { 1_000_000 };
+    let mut map = BTreeMap::new();
+    let mut i = 1;
+    for _ in 0..loops {
+        i = (i + 421) & 0xFF;
+        map.insert(i, i);
+        map.remove(&(0xFF - i));
+    }
+
+    map.check();
+}
diff --git a/library/alloc/src/collections/btree/node.rs b/library/alloc/src/collections/btree/node.rs
index f52d07c9b8c..f1d66e973cb 100644
--- a/library/alloc/src/collections/btree/node.rs
+++ b/library/alloc/src/collections/btree/node.rs
@@ -613,8 +613,8 @@ impl<'a, K, V> NodeRef<marker::Mut<'a>, K, V, marker::Internal> {
 }
 
 impl<'a, K: 'a, V: 'a> NodeRef<marker::Mut<'a>, K, V, marker::Internal> {
-    /// Adds a key/value pair and an edge to go to the right of that pair to
-    /// the end of the node.
+    /// Adds a key/value pair, and an edge to go to the right of that pair,
+    /// to the end of the node.
     pub fn push(&mut self, key: K, val: V, edge: Root<K, V>) {
         assert!(edge.height == self.height - 1);
 
@@ -630,8 +630,8 @@ impl<'a, K: 'a, V: 'a> NodeRef<marker::Mut<'a>, K, V, marker::Internal> {
         }
     }
 
-    /// Adds a key/value pair and an edge to go to the left of that pair to
-    /// the beginning of the node.
+    /// Adds a key/value pair, and an edge to go to the left of that pair,
+    /// to the beginning of the node.
     pub fn push_front(&mut self, key: K, val: V, edge: Root<K, V>) {
         assert!(edge.height == self.height - 1);
         assert!(self.len() < CAPACITY);
@@ -1152,7 +1152,7 @@ impl<'a, K: 'a, V: 'a> Handle<NodeRef<marker::Mut<'a>, K, V, marker::Leaf>, mark
     ///
     /// - The node is truncated to only contain the key/value pairs to the right of
     ///   this handle.
-    /// - The key and value pointed to by this handle and extracted.
+    /// - The key and value pointed to by this handle are extracted.
     /// - All the key/value pairs to the right of this handle are put into a newly
     ///   allocated node.
     pub fn split(mut self) -> (NodeRef<marker::Mut<'a>, K, V, marker::Leaf>, K, V, Root<K, V>) {
@@ -1196,7 +1196,7 @@ impl<'a, K: 'a, V: 'a> Handle<NodeRef<marker::Mut<'a>, K, V, marker::Internal>,
     ///
     /// - The node is truncated to only contain the edges and key/value pairs to the
     ///   right of this handle.
-    /// - The key and value pointed to by this handle and extracted.
+    /// - The key and value pointed to by this handle are extracted.
     /// - All the edges and key/value pairs to the right of this handle are put into
     ///   a newly allocated node.
     pub fn split(mut self) -> (NodeRef<marker::Mut<'a>, K, V, marker::Internal>, K, V, Root<K, V>) {
diff --git a/library/core/src/sync/atomic.rs b/library/core/src/sync/atomic.rs
index c67d6422c01..7eec2c487fe 100644
--- a/library/core/src/sync/atomic.rs
+++ b/library/core/src/sync/atomic.rs
@@ -110,7 +110,6 @@ use self::Ordering::*;
 use crate::cell::UnsafeCell;
 use crate::fmt;
 use crate::intrinsics;
-use crate::mem::align_of;
 
 use crate::hint::spin_loop;
 
@@ -328,27 +327,6 @@ impl AtomicBool {
         unsafe { &mut *(self.v.get() as *mut bool) }
     }
 
-    /// Get atomic access to a `&mut bool`.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// #![feature(atomic_from_mut)]
-    /// use std::sync::atomic::{AtomicBool, Ordering};
-    ///
-    /// let mut some_bool = true;
-    /// let a = AtomicBool::from_mut(&mut some_bool);
-    /// a.store(false, Ordering::Relaxed);
-    /// assert_eq!(some_bool, false);
-    /// ```
-    #[inline]
-    #[unstable(feature = "atomic_from_mut", issue = "76314")]
-    pub fn from_mut(v: &mut bool) -> &Self {
-        // SAFETY: the mutable reference guarantees unique ownership, and
-        // alignment of both `bool` and `Self` is 1.
-        unsafe { &*(v as *mut bool as *mut Self) }
-    }
-
     /// Consumes the atomic and returns the contained value.
     ///
     /// This is safe because passing `self` by value guarantees that no other threads are
@@ -841,30 +819,6 @@ impl<T> AtomicPtr<T> {
         self.p.get_mut()
     }
 
-    /// Get atomic access to a pointer.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// #![feature(atomic_from_mut)]
-    /// use std::sync::atomic::{AtomicPtr, Ordering};
-    ///
-    /// let mut some_ptr = &mut 123 as *mut i32;
-    /// let a = AtomicPtr::from_mut(&mut some_ptr);
-    /// a.store(&mut 456, Ordering::Relaxed);
-    /// assert_eq!(unsafe { *some_ptr }, 456);
-    /// ```
-    #[inline]
-    #[unstable(feature = "atomic_from_mut", issue = "76314")]
-    pub fn from_mut(v: &mut *mut T) -> &Self {
-        let [] = [(); align_of::<AtomicPtr<()>>() - align_of::<*mut ()>()];
-        // SAFETY:
-        //  - the mutable reference guarantees unique ownership.
-        //  - the alignment of `*mut T` and `Self` is the same on all platforms
-        //    supported by rust, as verified above.
-        unsafe { &*(v as *mut *mut T as *mut Self) }
-    }
-
     /// Consumes the atomic and returns the contained value.
     ///
     /// This is safe because passing `self` by value guarantees that no other threads are
@@ -1167,7 +1121,6 @@ macro_rules! atomic_int {
      $stable_nand:meta,
      $const_stable:meta,
      $stable_init_const:meta,
-     $(from_mut: cfg($from_mut_cfg:meta),)?
      $s_int_type:literal, $int_ref:expr,
      $extra_feature:expr,
      $min_fn:ident, $max_fn:ident,
@@ -1279,45 +1232,6 @@ assert_eq!(some_var.load(Ordering::SeqCst), 5);
             }
 
             doc_comment! {
-                concat!("Get atomic access to a `&mut ", stringify!($int_type), "`.
-
-",
-if_not_8_bit! {
-    $int_type,
-    concat!(
-        "**Note:** This function is only available on targets where `",
-        stringify!($int_type), "` has an alignment of ", $align, " bytes."
-    )
-},
-"
-
-# Examples
-
-```
-#![feature(atomic_from_mut)]
-", $extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering};
-
-let mut some_int = 123;
-let a = ", stringify!($atomic_type), "::from_mut(&mut some_int);
-a.store(100, Ordering::Relaxed);
-assert_eq!(some_int, 100);
-```
-                "),
-                #[inline]
-                $(#[cfg($from_mut_cfg)])?
-                #[unstable(feature = "atomic_from_mut", issue = "76314")]
-                pub fn from_mut(v: &mut $int_type) -> &Self {
-                    let [] = [(); align_of::<Self>() - align_of::<$int_type>()];
-                    // SAFETY:
-                    //  - the mutable reference guarantees unique ownership.
-                    //  - the alignment of `$int_type` and `Self` is the
-                    //    same on all platforms enabled by `$from_mut_cfg`
-                    //    as verified above.
-                    unsafe { &*(v as *mut $int_type as *mut Self) }
-                }
-            }
-
-            doc_comment! {
                 concat!("Consumes the atomic and returns the contained value.
 
 This is safe because passing `self` by value guarantees that no other threads are
@@ -2075,7 +1989,6 @@ atomic_int! {
     stable(feature = "integer_atomics_stable", since = "1.34.0"),
     rustc_const_stable(feature = "const_integer_atomics", since = "1.34.0"),
     unstable(feature = "integer_atomics", issue = "32976"),
-    from_mut: cfg(not(target_arch = "x86")),
     "i64", "../../../std/primitive.i64.html",
     "",
     atomic_min, atomic_max,
@@ -2094,7 +2007,6 @@ atomic_int! {
     stable(feature = "integer_atomics_stable", since = "1.34.0"),
     rustc_const_stable(feature = "const_integer_atomics", since = "1.34.0"),
     unstable(feature = "integer_atomics", issue = "32976"),
-    from_mut: cfg(not(target_arch = "x86")),
     "u64", "../../../std/primitive.u64.html",
     "",
     atomic_umin, atomic_umax,
@@ -2113,7 +2025,6 @@ atomic_int! {
     unstable(feature = "integer_atomics", issue = "32976"),
     rustc_const_stable(feature = "const_integer_atomics", since = "1.34.0"),
     unstable(feature = "integer_atomics", issue = "32976"),
-    from_mut: cfg(not(target_arch = "x86_64")),
     "i128", "../../../std/primitive.i128.html",
     "#![feature(integer_atomics)]\n\n",
     atomic_min, atomic_max,
@@ -2132,7 +2043,6 @@ atomic_int! {
     unstable(feature = "integer_atomics", issue = "32976"),
     rustc_const_stable(feature = "const_integer_atomics", since = "1.34.0"),
     unstable(feature = "integer_atomics", issue = "32976"),
-    from_mut: cfg(not(target_arch = "x86_64")),
     "u128", "../../../std/primitive.u128.html",
     "#![feature(integer_atomics)]\n\n",
     atomic_umin, atomic_umax,
diff --git a/library/std/src/sys/unix/thread.rs b/library/std/src/sys/unix/thread.rs
index 04da9812ddc..652219e28f6 100644
--- a/library/std/src/sys/unix/thread.rs
+++ b/library/std/src/sys/unix/thread.rs
@@ -294,6 +294,7 @@ pub mod guard {
     unsafe fn get_stack_start() -> Option<*mut libc::c_void> {
         let mut ret = None;
         let mut attr: libc::pthread_attr_t = crate::mem::zeroed();
+        #[cfg(target_os = "freebsd")]
         assert_eq!(libc::pthread_attr_init(&mut attr), 0);
         #[cfg(target_os = "freebsd")]
         let e = libc::pthread_attr_get_np(libc::pthread_self(), &mut attr);
@@ -305,7 +306,9 @@ pub mod guard {
             assert_eq!(libc::pthread_attr_getstack(&attr, &mut stackaddr, &mut stacksize), 0);
             ret = Some(stackaddr);
         }
-        assert_eq!(libc::pthread_attr_destroy(&mut attr), 0);
+        if e == 0 || cfg!(target_os = "freebsd") {
+            assert_eq!(libc::pthread_attr_destroy(&mut attr), 0);
+        }
         ret
     }
 
@@ -403,6 +406,7 @@ pub mod guard {
     pub unsafe fn current() -> Option<Guard> {
         let mut ret = None;
         let mut attr: libc::pthread_attr_t = crate::mem::zeroed();
+        #[cfg(target_os = "freebsd")]
         assert_eq!(libc::pthread_attr_init(&mut attr), 0);
         #[cfg(target_os = "freebsd")]
         let e = libc::pthread_attr_get_np(libc::pthread_self(), &mut attr);
@@ -446,7 +450,9 @@ pub mod guard {
                 Some(stackaddr..stackaddr + guardsize)
             };
         }
-        assert_eq!(libc::pthread_attr_destroy(&mut attr), 0);
+        if e == 0 || cfg!(target_os = "freebsd") {
+            assert_eq!(libc::pthread_attr_destroy(&mut attr), 0);
+        }
         ret
     }
 }
diff --git a/library/test/src/lib.rs b/library/test/src/lib.rs
index 6bd708ef487..caea4b1e309 100644
--- a/library/test/src/lib.rs
+++ b/library/test/src/lib.rs
@@ -29,6 +29,7 @@
 #![feature(staged_api)]
 #![feature(termination_trait_lib)]
 #![feature(test)]
+#![feature(total_cmp)]
 
 // Public reexports
 pub use self::bench::{black_box, Bencher};
diff --git a/library/test/src/stats.rs b/library/test/src/stats.rs
index c02f93bf9d4..1a2cb893a8a 100644
--- a/library/test/src/stats.rs
+++ b/library/test/src/stats.rs
@@ -1,29 +1,13 @@
 #![allow(missing_docs)]
 #![allow(deprecated)] // Float
 
-use std::cmp::Ordering::{self, Equal, Greater, Less};
 use std::mem;
 
 #[cfg(test)]
 mod tests;
 
-fn local_cmp(x: f64, y: f64) -> Ordering {
-    // arbitrarily decide that NaNs are larger than everything.
-    if y.is_nan() {
-        Less
-    } else if x.is_nan() {
-        Greater
-    } else if x < y {
-        Less
-    } else if x == y {
-        Equal
-    } else {
-        Greater
-    }
-}
-
 fn local_sort(v: &mut [f64]) {
-    v.sort_by(|x: &f64, y: &f64| local_cmp(*x, *y));
+    v.sort_by(|x: &f64, y: &f64| x.total_cmp(y));
 }
 
 /// Trait that provides simple descriptive statistics on a univariate set of numeric samples.
diff --git a/src/test/ui/ffi_pure.stderr b/src/test/ui/ffi_pure.stderr
index 3a849c0bca7..bc911c85ddb 100644
--- a/src/test/ui/ffi_pure.stderr
+++ b/src/test/ui/ffi_pure.stderr
@@ -6,3 +6,4 @@ LL | #[ffi_pure]
 
 error: aborting due to previous error
 
+For more information about this error, try `rustc --explain E0755`.
diff --git a/src/test/ui/mir/issue-76740-copy-propagation.rs b/src/test/ui/mir/issue-76740-copy-propagation.rs
new file mode 100644
index 00000000000..e3283949b26
--- /dev/null
+++ b/src/test/ui/mir/issue-76740-copy-propagation.rs
@@ -0,0 +1,30 @@
+// Regression test for issue #76740.
+// run-fail FIXME: change to run-pass once #76899 lands
+// compile-flags: -Zmir-opt-level=3
+
+#[derive(Copy, Clone)]
+pub struct V([usize; 4]);
+
+impl V {
+    fn new() -> Self {
+        V([0; 4])
+    }
+
+    #[inline(never)]
+    fn check(mut self) {
+        assert_eq!(self.0[0], 0);
+        self.0[0] = 1;
+    }
+}
+
+fn main() {
+    let v = V::new();
+    let mut i = 0;
+    while i != 10 {
+        // Copy propagation incorrectly assumed that Operand::Move does not
+        // mutate the local, and used the same v for each V::check call,
+        // rather than a copy.
+        v.check();
+        i += 1;
+    }
+}
diff --git a/src/tools/miri b/src/tools/miri
-Subproject cbc7560ae2d44669ef6ba0f43014e10ce881180
+Subproject 02a33d411d8e385942776760a99535d69826349