about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2021-06-10 03:11:24 +0000
committerbors <bors@rust-lang.org>2021-06-10 03:11:24 +0000
commitc5fbcd35a8217a17f6b63a22217ace06cf8f5f02 (patch)
tree32d947c1d7b0e634a699956cdae8c2fd664ba58f
parent1639a16ebfaad2aa74fd535c778fd1614475b53d (diff)
parenta6b7e1c121152a222e732fb71c2eeb1bfb9c1d3d (diff)
downloadrust-c5fbcd35a8217a17f6b63a22217ace06cf8f5f02.tar.gz
rust-c5fbcd35a8217a17f6b63a22217ace06cf8f5f02.zip
Auto merge of #86186 - JohnTitor:rollup-upaw6wx, r=JohnTitor
Rollup of 7 pull requests

Successful merges:

 - #82037 (Make symbols stripping work on MacOS X)
 - #84687 (Multiple improvements to RwLocks)
 - #85997 (rustdoc: Print a warning if the diff when comparing to old nightlies is empty)
 - #86051 (Updated code examples and wording in move keyword documentation )
 - #86111 (fix off by one in `std::iter::Iterator` documentation)
 - #86113 (build doctests with lld if use-lld = true)
 - #86175 (update Miri)

Failed merges:

r? `@ghost`
`@rustbot` modify labels: rollup
-rw-r--r--compiler/rustc_codegen_ssa/src/back/link.rs51
-rw-r--r--compiler/rustc_codegen_ssa/src/back/linker.rs11
-rw-r--r--library/core/src/iter/traits/iterator.rs2
-rw-r--r--library/std/src/keyword_docs.rs20
-rw-r--r--library/std/src/panicking.rs15
-rw-r--r--library/std/src/sync/rwlock.rs34
-rw-r--r--library/std/src/sys/hermit/rwlock.rs2
-rw-r--r--library/std/src/sys/sgx/rwlock.rs2
-rw-r--r--library/std/src/sys/unix/os.rs11
-rw-r--r--library/std/src/sys/unix/rwlock.rs54
-rw-r--r--library/std/src/sys/unsupported/rwlock.rs2
-rw-r--r--library/std/src/sys/wasm/atomics/rwlock.rs2
-rw-r--r--library/std/src/sys/windows/rwlock.rs2
-rw-r--r--library/std/src/sys_common/rwlock.rs120
-rw-r--r--src/bootstrap/bin/rustdoc.rs7
-rw-r--r--src/bootstrap/builder.rs4
-rw-r--r--src/bootstrap/lib.rs15
-rw-r--r--src/bootstrap/test.rs17
-rw-r--r--src/tools/compiletest/src/runtest.rs30
m---------src/tools/miri24
20 files changed, 233 insertions, 192 deletions
diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs
index 6a2005b4d23..cb56e3f9e8a 100644
--- a/compiler/rustc_codegen_ssa/src/back/link.rs
+++ b/compiler/rustc_codegen_ssa/src/back/link.rs
@@ -5,7 +5,7 @@ use rustc_fs_util::fix_windows_verbatim_for_gcc;
 use rustc_hir::def_id::CrateNum;
 use rustc_middle::middle::cstore::{DllImport, LibSource};
 use rustc_middle::middle::dependency_format::Linkage;
-use rustc_session::config::{self, CFGuard, CrateType, DebugInfo};
+use rustc_session::config::{self, CFGuard, CrateType, DebugInfo, Strip};
 use rustc_session::config::{OutputFilenames, OutputType, PrintRequest};
 use rustc_session::output::{check_file_is_writeable, invalid_output_for_target, out_filename};
 use rustc_session::search_paths::PathKind;
@@ -907,14 +907,6 @@ fn link_natively<'a, B: ArchiveBuilder<'a>>(
         }
     }
 
-    fn escape_string(s: &[u8]) -> String {
-        str::from_utf8(s).map(|s| s.to_owned()).unwrap_or_else(|_| {
-            let mut x = "Non-UTF-8 output: ".to_string();
-            x.extend(s.iter().flat_map(|&b| ascii::escape_default(b)).map(char::from));
-            x
-        })
-    }
-
     match prog {
         Ok(prog) => {
             if !prog.status.success() {
@@ -1056,6 +1048,47 @@ fn link_natively<'a, B: ArchiveBuilder<'a>>(
         // ... and otherwise we're processing a `*.dwp` packed dwarf file.
         SplitDebuginfo::Packed => link_dwarf_object(sess, &out_filename),
     }
+
+    if sess.target.is_like_osx {
+        if let Some(option) = osx_strip_opt(sess.opts.debugging_opts.strip) {
+            strip_symbols_in_osx(sess, &out_filename, option);
+        }
+    }
+}
+
+fn strip_symbols_in_osx<'a>(sess: &'a Session, out_filename: &Path, option: &str) {
+    let prog = Command::new("strip").arg(option).arg(out_filename).output();
+    match prog {
+        Ok(prog) => {
+            if !prog.status.success() {
+                let mut output = prog.stderr.clone();
+                output.extend_from_slice(&prog.stdout);
+                sess.struct_warn(&format!(
+                    "stripping debug info with `strip` failed: {}",
+                    prog.status
+                ))
+                .note(&escape_string(&output))
+                .emit();
+            }
+        }
+        Err(e) => sess.fatal(&format!("unable to run `strip`: {}", e)),
+    }
+}
+
+fn osx_strip_opt<'a>(strip: Strip) -> Option<&'a str> {
+    match strip {
+        Strip::Debuginfo => Some("-S"),
+        Strip::Symbols => Some("-x"),
+        Strip::None => None,
+    }
+}
+
+fn escape_string(s: &[u8]) -> String {
+    str::from_utf8(s).map(|s| s.to_owned()).unwrap_or_else(|_| {
+        let mut x = "Non-UTF-8 output: ".to_string();
+        x.extend(s.iter().flat_map(|&b| ascii::escape_default(b)).map(char::from));
+        x
+    })
 }
 
 fn add_sanitizer_libraries(sess: &Session, crate_type: CrateType, linker: &mut dyn Linker) {
diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs
index 1d53e9ad8b3..43ff664c3e6 100644
--- a/compiler/rustc_codegen_ssa/src/back/linker.rs
+++ b/compiler/rustc_codegen_ssa/src/back/linker.rs
@@ -526,15 +526,18 @@ impl<'a> Linker for GccLinker<'a> {
     fn control_flow_guard(&mut self) {}
 
     fn debuginfo(&mut self, strip: Strip) {
+        // MacOS linker doesn't support stripping symbols directly anymore.
+        if self.sess.target.is_like_osx {
+            return;
+        }
+
         match strip {
             Strip::None => {}
             Strip::Debuginfo => {
-                // MacOS linker does not support longhand argument --strip-debug
-                self.linker_arg("-S");
+                self.linker_arg("--strip-debug");
             }
             Strip::Symbols => {
-                // MacOS linker does not support longhand argument --strip-all
-                self.linker_arg("-s");
+                self.linker_arg("--strip-all");
             }
         }
     }
diff --git a/library/core/src/iter/traits/iterator.rs b/library/core/src/iter/traits/iterator.rs
index 96b924f6e2a..11dea400a46 100644
--- a/library/core/src/iter/traits/iterator.rs
+++ b/library/core/src/iter/traits/iterator.rs
@@ -138,7 +138,7 @@ pub trait Iterator {
     /// A more complex example:
     ///
     /// ```
-    /// // The even numbers from zero to ten.
+    /// // The even numbers in the range of zero to nine.
     /// let iter = (0..10).filter(|x| x % 2 == 0);
     ///
     /// // We might iterate from zero to ten times. Knowing that it's five
diff --git a/library/std/src/keyword_docs.rs b/library/std/src/keyword_docs.rs
index ba2b8b6955d..5b8e83766f0 100644
--- a/library/std/src/keyword_docs.rs
+++ b/library/std/src/keyword_docs.rs
@@ -987,13 +987,13 @@ mod mod_keyword {}
 /// Capture a [closure]'s environment by value.
 ///
 /// `move` converts any variables captured by reference or mutable reference
-/// to owned by value variables.
+/// to variables captured by value.
 ///
 /// ```rust
-/// let capture = "hello";
-/// let closure = move || {
-///     println!("rust says {}", capture);
-/// };
+/// let data = vec![1, 2, 3];
+/// let closure = move || println!("captured {:?} by value", data);
+///
+/// // data is no longer available, it is owned by the closure
 /// ```
 ///
 /// Note: `move` closures may still implement [`Fn`] or [`FnMut`], even though
@@ -1004,31 +1004,29 @@ mod mod_keyword {}
 /// ```rust
 /// fn create_fn() -> impl Fn() {
 ///     let text = "Fn".to_owned();
-///
 ///     move || println!("This is a: {}", text)
 /// }
 ///
 /// let fn_plain = create_fn();
-///
 /// fn_plain();
 /// ```
 ///
 /// `move` is often used when [threads] are involved.
 ///
 /// ```rust
-/// let x = 5;
+/// let data = vec![1, 2, 3];
 ///
 /// std::thread::spawn(move || {
-///     println!("captured {} by value", x)
+///     println!("captured {:?} by value", data)
 /// }).join().unwrap();
 ///
-/// // x is no longer available
+/// // data was moved to the spawned thread, so we cannot use it here
 /// ```
 ///
 /// `move` is also valid before an async block.
 ///
 /// ```rust
-/// let capture = "hello";
+/// let capture = "hello".to_owned();
 /// let block = async move {
 ///     println!("rust says {} from async block", capture);
 /// };
diff --git a/library/std/src/panicking.rs b/library/std/src/panicking.rs
index e591e073e7b..0b9c9fb479f 100644
--- a/library/std/src/panicking.rs
+++ b/library/std/src/panicking.rs
@@ -19,7 +19,7 @@ use crate::process;
 use crate::sync::atomic::{AtomicBool, Ordering};
 use crate::sys::stdio::panic_output;
 use crate::sys_common::backtrace::{self, RustBacktrace};
-use crate::sys_common::rwlock::RWLock;
+use crate::sys_common::rwlock::StaticRWLock;
 use crate::sys_common::thread_info;
 use crate::thread;
 
@@ -74,7 +74,7 @@ enum Hook {
     Custom(*mut (dyn Fn(&PanicInfo<'_>) + 'static + Sync + Send)),
 }
 
-static HOOK_LOCK: RWLock = RWLock::new();
+static HOOK_LOCK: StaticRWLock = StaticRWLock::new();
 static mut HOOK: Hook = Hook::Default;
 
 /// Registers a custom panic hook, replacing any that was previously registered.
@@ -117,10 +117,10 @@ pub fn set_hook(hook: Box<dyn Fn(&PanicInfo<'_>) + 'static + Sync + Send>) {
     }
 
     unsafe {
-        HOOK_LOCK.write();
+        let guard = HOOK_LOCK.write();
         let old_hook = HOOK;
         HOOK = Hook::Custom(Box::into_raw(hook));
-        HOOK_LOCK.write_unlock();
+        drop(guard);
 
         if let Hook::Custom(ptr) = old_hook {
             #[allow(unused_must_use)]
@@ -165,10 +165,10 @@ pub fn take_hook() -> Box<dyn Fn(&PanicInfo<'_>) + 'static + Sync + Send> {
     }
 
     unsafe {
-        HOOK_LOCK.write();
+        let guard = HOOK_LOCK.write();
         let hook = HOOK;
         HOOK = Hook::Default;
-        HOOK_LOCK.write_unlock();
+        drop(guard);
 
         match hook {
             Hook::Default => Box::new(default_hook),
@@ -608,7 +608,7 @@ fn rust_panic_with_hook(
 
     unsafe {
         let mut info = PanicInfo::internal_constructor(message, location);
-        HOOK_LOCK.read();
+        let _guard = HOOK_LOCK.read();
         match HOOK {
             // Some platforms (like wasm) know that printing to stderr won't ever actually
             // print anything, and if that's the case we can skip the default
@@ -626,7 +626,6 @@ fn rust_panic_with_hook(
                 (*ptr)(&info);
             }
         };
-        HOOK_LOCK.read_unlock();
     }
 
     if panics > 1 {
diff --git a/library/std/src/sync/rwlock.rs b/library/std/src/sync/rwlock.rs
index 9d521ab14cb..0d00f74eaa1 100644
--- a/library/std/src/sync/rwlock.rs
+++ b/library/std/src/sync/rwlock.rs
@@ -3,9 +3,7 @@ mod tests;
 
 use crate::cell::UnsafeCell;
 use crate::fmt;
-use crate::mem;
 use crate::ops::{Deref, DerefMut};
-use crate::ptr;
 use crate::sync::{poison, LockResult, TryLockError, TryLockResult};
 use crate::sys_common::rwlock as sys;
 
@@ -66,7 +64,7 @@ use crate::sys_common::rwlock as sys;
 /// [`Mutex`]: super::Mutex
 #[stable(feature = "rust1", since = "1.0.0")]
 pub struct RwLock<T: ?Sized> {
-    inner: Box<sys::RWLock>,
+    inner: sys::MovableRWLock,
     poison: poison::Flag,
     data: UnsafeCell<T>,
 }
@@ -130,7 +128,7 @@ impl<T> RwLock<T> {
     #[stable(feature = "rust1", since = "1.0.0")]
     pub fn new(t: T) -> RwLock<T> {
         RwLock {
-            inner: box sys::RWLock::new(),
+            inner: sys::MovableRWLock::new(),
             poison: poison::Flag::new(),
             data: UnsafeCell::new(t),
         }
@@ -376,24 +374,8 @@ impl<T: ?Sized> RwLock<T> {
     where
         T: Sized,
     {
-        // We know statically that there are no outstanding references to
-        // `self` so there's no need to lock the inner lock.
-        //
-        // To get the inner value, we'd like to call `data.into_inner()`,
-        // but because `RwLock` impl-s `Drop`, we can't move out of it, so
-        // we'll have to destructure it manually instead.
-        unsafe {
-            // Like `let RwLock { inner, poison, data } = self`.
-            let (inner, poison, data) = {
-                let RwLock { ref inner, ref poison, ref data } = self;
-                (ptr::read(inner), ptr::read(poison), ptr::read(data))
-            };
-            mem::forget(self);
-            inner.destroy(); // Keep in sync with the `Drop` impl.
-            drop(inner);
-
-            poison::map_result(poison.borrow(), |_| data.into_inner())
-        }
+        let data = self.data.into_inner();
+        poison::map_result(self.poison.borrow(), |_| data)
     }
 
     /// Returns a mutable reference to the underlying data.
@@ -425,14 +407,6 @@ impl<T: ?Sized> RwLock<T> {
 }
 
 #[stable(feature = "rust1", since = "1.0.0")]
-unsafe impl<#[may_dangle] T: ?Sized> Drop for RwLock<T> {
-    fn drop(&mut self) {
-        // IMPORTANT: This code needs to be kept in sync with `RwLock::into_inner`.
-        unsafe { self.inner.destroy() }
-    }
-}
-
-#[stable(feature = "rust1", since = "1.0.0")]
 impl<T: ?Sized + fmt::Debug> fmt::Debug for RwLock<T> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         let mut d = f.debug_struct("RwLock");
diff --git a/library/std/src/sys/hermit/rwlock.rs b/library/std/src/sys/hermit/rwlock.rs
index 06442e925f4..d2058180121 100644
--- a/library/std/src/sys/hermit/rwlock.rs
+++ b/library/std/src/sys/hermit/rwlock.rs
@@ -8,6 +8,8 @@ pub struct RWLock {
     state: UnsafeCell<State>,
 }
 
+pub type MovableRWLock = Box<RWLock>;
+
 enum State {
     Unlocked,
     Reading(usize),
diff --git a/library/std/src/sys/sgx/rwlock.rs b/library/std/src/sys/sgx/rwlock.rs
index 0c96e3fcddc..2d038b51896 100644
--- a/library/std/src/sys/sgx/rwlock.rs
+++ b/library/std/src/sys/sgx/rwlock.rs
@@ -13,6 +13,8 @@ pub struct RWLock {
     writer: SpinMutex<WaitVariable<bool>>,
 }
 
+pub type MovableRWLock = Box<RWLock>;
+
 // Check at compile time that RWLock size matches C definition (see test_c_rwlock_initializer below)
 //
 // # Safety
diff --git a/library/std/src/sys/unix/os.rs b/library/std/src/sys/unix/os.rs
index bbc4691d963..41ca9762390 100644
--- a/library/std/src/sys/unix/os.rs
+++ b/library/std/src/sys/unix/os.rs
@@ -20,8 +20,7 @@ use crate::str;
 use crate::sys::cvt;
 use crate::sys::fd;
 use crate::sys::memchr;
-use crate::sys::rwlock::{RWLockReadGuard, StaticRWLock};
-use crate::sys_common::mutex::{StaticMutex, StaticMutexGuard};
+use crate::sys_common::rwlock::{StaticRWLock, StaticRWLockReadGuard};
 use crate::vec;
 
 use libc::{c_char, c_int, c_void};
@@ -490,8 +489,8 @@ pub unsafe fn environ() -> *mut *const *const c_char {
 
 static ENV_LOCK: StaticRWLock = StaticRWLock::new();
 
-pub fn env_read_lock() -> RWLockReadGuard {
-    ENV_LOCK.read_with_guard()
+pub fn env_read_lock() -> StaticRWLockReadGuard {
+    ENV_LOCK.read()
 }
 
 /// Returns a vector of (variable, value) byte-vector pairs for all the
@@ -551,7 +550,7 @@ pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
     let v = CString::new(v.as_bytes())?;
 
     unsafe {
-        let _guard = ENV_LOCK.write_with_guard();
+        let _guard = ENV_LOCK.write();
         cvt(libc::setenv(k.as_ptr(), v.as_ptr(), 1)).map(drop)
     }
 }
@@ -560,7 +559,7 @@ pub fn unsetenv(n: &OsStr) -> io::Result<()> {
     let nbuf = CString::new(n.as_bytes())?;
 
     unsafe {
-        let _guard = ENV_LOCK.write_with_guard();
+        let _guard = ENV_LOCK.write();
         cvt(libc::unsetenv(nbuf.as_ptr())).map(drop)
     }
 }
diff --git a/library/std/src/sys/unix/rwlock.rs b/library/std/src/sys/unix/rwlock.rs
index d97d9d712fc..b1faf12c226 100644
--- a/library/std/src/sys/unix/rwlock.rs
+++ b/library/std/src/sys/unix/rwlock.rs
@@ -7,6 +7,8 @@ pub struct RWLock {
     num_readers: AtomicUsize,
 }
 
+pub type MovableRWLock = Box<RWLock>;
+
 unsafe impl Send for RWLock {}
 unsafe impl Sync for RWLock {}
 
@@ -139,55 +141,3 @@ impl RWLock {
         }
     }
 }
-
-pub struct StaticRWLock(RWLock);
-
-impl StaticRWLock {
-    pub const fn new() -> StaticRWLock {
-        StaticRWLock(RWLock::new())
-    }
-
-    /// Acquires shared access to the underlying lock, blocking the current
-    /// thread to do so.
-    ///
-    /// The lock is automatically unlocked when the returned guard is dropped.
-    #[inline]
-    pub fn read_with_guard(&'static self) -> RWLockReadGuard {
-        // SAFETY: All methods require static references, therefore self
-        // cannot be moved between invocations.
-        unsafe {
-            self.0.read();
-        }
-        RWLockReadGuard(&self.0)
-    }
-
-    /// Acquires write access to the underlying lock, blocking the current thread
-    /// to do so.
-    ///
-    /// The lock is automatically unlocked when the returned guard is dropped.
-    #[inline]
-    pub fn write_with_guard(&'static self) -> RWLockWriteGuard {
-        // SAFETY: All methods require static references, therefore self
-        // cannot be moved between invocations.
-        unsafe {
-            self.0.write();
-        }
-        RWLockWriteGuard(&self.0)
-    }
-}
-
-pub struct RWLockReadGuard(&'static RWLock);
-
-impl Drop for RWLockReadGuard {
-    fn drop(&mut self) {
-        unsafe { self.0.read_unlock() }
-    }
-}
-
-pub struct RWLockWriteGuard(&'static RWLock);
-
-impl Drop for RWLockWriteGuard {
-    fn drop(&mut self) {
-        unsafe { self.0.write_unlock() }
-    }
-}
diff --git a/library/std/src/sys/unsupported/rwlock.rs b/library/std/src/sys/unsupported/rwlock.rs
index 6982b2b155f..8438adeb5b5 100644
--- a/library/std/src/sys/unsupported/rwlock.rs
+++ b/library/std/src/sys/unsupported/rwlock.rs
@@ -5,6 +5,8 @@ pub struct RWLock {
     mode: Cell<isize>,
 }
 
+pub type MovableRWLock = RWLock;
+
 unsafe impl Send for RWLock {}
 unsafe impl Sync for RWLock {} // no threads on this platform
 
diff --git a/library/std/src/sys/wasm/atomics/rwlock.rs b/library/std/src/sys/wasm/atomics/rwlock.rs
index 06442e925f4..64eaa2fc482 100644
--- a/library/std/src/sys/wasm/atomics/rwlock.rs
+++ b/library/std/src/sys/wasm/atomics/rwlock.rs
@@ -8,6 +8,8 @@ pub struct RWLock {
     state: UnsafeCell<State>,
 }
 
+pub type MovableRWLock = RWLock;
+
 enum State {
     Unlocked,
     Reading(usize),
diff --git a/library/std/src/sys/windows/rwlock.rs b/library/std/src/sys/windows/rwlock.rs
index a769326352c..b7a5b1e7acc 100644
--- a/library/std/src/sys/windows/rwlock.rs
+++ b/library/std/src/sys/windows/rwlock.rs
@@ -5,6 +5,8 @@ pub struct RWLock {
     inner: UnsafeCell<c::SRWLOCK>,
 }
 
+pub type MovableRWLock = RWLock;
+
 unsafe impl Send for RWLock {}
 unsafe impl Sync for RWLock {}
 
diff --git a/library/std/src/sys_common/rwlock.rs b/library/std/src/sys_common/rwlock.rs
index 3705d641a1b..07ec20f4dc6 100644
--- a/library/std/src/sys_common/rwlock.rs
+++ b/library/std/src/sys_common/rwlock.rs
@@ -1,63 +1,112 @@
 use crate::sys::rwlock as imp;
 
+/// An OS-based reader-writer lock, meant for use in static variables.
+///
+/// This rwlock does not implement poisoning.
+///
+/// This rwlock has a const constructor ([`StaticRWLock::new`]), does not
+/// implement `Drop` to cleanup resources.
+pub struct StaticRWLock(imp::RWLock);
+
+impl StaticRWLock {
+    /// Creates a new rwlock for use.
+    pub const fn new() -> Self {
+        Self(imp::RWLock::new())
+    }
+
+    /// Acquires shared access to the underlying lock, blocking the current
+    /// thread to do so.
+    ///
+    /// The lock is automatically unlocked when the returned guard is dropped.
+    #[inline]
+    pub fn read(&'static self) -> StaticRWLockReadGuard {
+        unsafe { self.0.read() };
+        StaticRWLockReadGuard(&self.0)
+    }
+
+    /// Acquires write access to the underlying lock, blocking the current thread
+    /// to do so.
+    ///
+    /// The lock is automatically unlocked when the returned guard is dropped.
+    #[inline]
+    pub fn write(&'static self) -> StaticRWLockWriteGuard {
+        unsafe { self.0.write() };
+        StaticRWLockWriteGuard(&self.0)
+    }
+}
+
+#[must_use]
+pub struct StaticRWLockReadGuard(&'static imp::RWLock);
+
+impl Drop for StaticRWLockReadGuard {
+    #[inline]
+    fn drop(&mut self) {
+        unsafe {
+            self.0.read_unlock();
+        }
+    }
+}
+
+#[must_use]
+pub struct StaticRWLockWriteGuard(&'static imp::RWLock);
+
+impl Drop for StaticRWLockWriteGuard {
+    #[inline]
+    fn drop(&mut self) {
+        unsafe {
+            self.0.write_unlock();
+        }
+    }
+}
+
 /// An OS-based reader-writer lock.
 ///
-/// This structure is entirely unsafe and serves as the lowest layer of a
-/// cross-platform binding of system rwlocks. It is recommended to use the
-/// safer types at the top level of this crate instead of this type.
-pub struct RWLock(imp::RWLock);
+/// This rwlock does *not* have a const constructor, cleans up its resources in
+/// its `Drop` implementation and may safely be moved (when not borrowed).
+///
+/// This rwlock does not implement poisoning.
+///
+/// This is either a wrapper around `Box<imp::RWLock>` or `imp::RWLock`,
+/// depending on the platform. It is boxed on platforms where `imp::RWLock` may
+/// not be moved.
+pub struct MovableRWLock(imp::MovableRWLock);
 
-impl RWLock {
+impl MovableRWLock {
     /// Creates a new reader-writer lock for use.
-    ///
-    /// Behavior is undefined if the reader-writer lock is moved after it is
-    /// first used with any of the functions below.
-    pub const fn new() -> RWLock {
-        RWLock(imp::RWLock::new())
+    pub fn new() -> Self {
+        Self(imp::MovableRWLock::from(imp::RWLock::new()))
     }
 
     /// Acquires shared access to the underlying lock, blocking the current
     /// thread to do so.
-    ///
-    /// Behavior is undefined if the rwlock has been moved between this and any
-    /// previous method call.
     #[inline]
-    pub unsafe fn read(&self) {
-        self.0.read()
+    pub fn read(&self) {
+        unsafe { self.0.read() }
     }
 
     /// Attempts to acquire shared access to this lock, returning whether it
     /// succeeded or not.
     ///
     /// This function does not block the current thread.
-    ///
-    /// Behavior is undefined if the rwlock has been moved between this and any
-    /// previous method call.
     #[inline]
-    pub unsafe fn try_read(&self) -> bool {
-        self.0.try_read()
+    pub fn try_read(&self) -> bool {
+        unsafe { self.0.try_read() }
     }
 
     /// Acquires write access to the underlying lock, blocking the current thread
     /// to do so.
-    ///
-    /// Behavior is undefined if the rwlock has been moved between this and any
-    /// previous method call.
     #[inline]
-    pub unsafe fn write(&self) {
-        self.0.write()
+    pub fn write(&self) {
+        unsafe { self.0.write() }
     }
 
     /// Attempts to acquire exclusive access to this lock, returning whether it
     /// succeeded or not.
     ///
     /// This function does not block the current thread.
-    ///
-    /// Behavior is undefined if the rwlock has been moved between this and any
-    /// previous method call.
     #[inline]
-    pub unsafe fn try_write(&self) -> bool {
-        self.0.try_write()
+    pub fn try_write(&self) -> bool {
+        unsafe { self.0.try_write() }
     }
 
     /// Unlocks previously acquired shared access to this lock.
@@ -76,13 +125,10 @@ impl RWLock {
     pub unsafe fn write_unlock(&self) {
         self.0.write_unlock()
     }
+}
 
-    /// Destroys OS-related resources with this RWLock.
-    ///
-    /// Behavior is undefined if there are any currently active users of this
-    /// lock.
-    #[inline]
-    pub unsafe fn destroy(&self) {
-        self.0.destroy()
+impl Drop for MovableRWLock {
+    fn drop(&mut self) {
+        unsafe { self.0.destroy() };
     }
 }
diff --git a/src/bootstrap/bin/rustdoc.rs b/src/bootstrap/bin/rustdoc.rs
index cba17c8e608..e4396d53016 100644
--- a/src/bootstrap/bin/rustdoc.rs
+++ b/src/bootstrap/bin/rustdoc.rs
@@ -41,7 +41,12 @@ fn main() {
         cmd.arg(arg);
     }
     if env::var_os("RUSTDOC_FUSE_LD_LLD").is_some() {
-        cmd.arg("-Clink-args=-fuse-ld=lld");
+        cmd.arg("-Clink-arg=-fuse-ld=lld");
+        if cfg!(windows) {
+            cmd.arg("-Clink-arg=-Wl,/threads:1");
+        } else {
+            cmd.arg("-Clink-arg=-Wl,--threads=1");
+        }
     }
 
     // Needed to be able to run all rustdoc tests.
diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs
index 06f8bf89dae..bc499fdba59 100644
--- a/src/bootstrap/builder.rs
+++ b/src/bootstrap/builder.rs
@@ -1142,6 +1142,7 @@ impl<'a> Builder<'a> {
         }
         if self.is_fuse_ld_lld(compiler.host) {
             cargo.env("RUSTC_HOST_FUSE_LD_LLD", "1");
+            cargo.env("RUSTDOC_FUSE_LD_LLD", "1");
         }
 
         if let Some(target_linker) = self.linker(target) {
@@ -1151,6 +1152,9 @@ impl<'a> Builder<'a> {
         if self.is_fuse_ld_lld(target) {
             rustflags.arg("-Clink-args=-fuse-ld=lld");
         }
+        self.lld_flags(target).for_each(|flag| {
+            rustdocflags.arg(&flag);
+        });
 
         if !(["build", "check", "clippy", "fix", "rustc"].contains(&cmd)) && want_rustdoc {
             cargo.env("RUSTDOC_LIBDIR", self.rustc_libdir(compiler));
diff --git a/src/bootstrap/lib.rs b/src/bootstrap/lib.rs
index 21c7dd11d24..347236c655a 100644
--- a/src/bootstrap/lib.rs
+++ b/src/bootstrap/lib.rs
@@ -923,6 +923,21 @@ impl Build {
         self.config.use_lld && !target.contains("msvc")
     }
 
+    fn lld_flags(&self, target: TargetSelection) -> impl Iterator<Item = String> {
+        let mut options = [None, None];
+
+        if self.config.use_lld {
+            if self.is_fuse_ld_lld(target) {
+                options[0] = Some("-Clink-arg=-fuse-ld=lld".to_string());
+            }
+
+            let threads = if target.contains("windows") { "/threads:1" } else { "--threads=1" };
+            options[1] = Some(format!("-Clink-arg=-Wl,{}", threads));
+        }
+
+        std::array::IntoIter::new(options).flatten()
+    }
+
     /// Returns if this target should statically link the C runtime, if specified
     fn crt_static(&self, target: TargetSelection) -> Option<bool> {
         if target.contains("pc-windows-msvc") {
diff --git a/src/bootstrap/test.rs b/src/bootstrap/test.rs
index cc7c143d474..70cf1b105c0 100644
--- a/src/bootstrap/test.rs
+++ b/src/bootstrap/test.rs
@@ -1131,19 +1131,6 @@ struct Compiletest {
     compare_mode: Option<&'static str>,
 }
 
-impl Compiletest {
-    fn add_lld_flags(builder: &Builder<'_>, target: TargetSelection, flags: &mut Vec<String>) {
-        if builder.config.use_lld {
-            if builder.is_fuse_ld_lld(target) {
-                flags.push("-Clink-arg=-fuse-ld=lld".to_string());
-            }
-
-            let threads = if target.contains("windows") { "/threads:1" } else { "--threads=1" };
-            flags.push(format!("-Clink-arg=-Wl,{}", threads));
-        }
-    }
-}
-
 impl Step for Compiletest {
     type Output = ();
 
@@ -1289,12 +1276,12 @@ note: if you're sure you want to do this, please open an issue as to why. In the
 
         let mut hostflags = flags.clone();
         hostflags.push(format!("-Lnative={}", builder.test_helpers_out(compiler.host).display()));
-        Self::add_lld_flags(builder, compiler.host, &mut hostflags);
+        hostflags.extend(builder.lld_flags(compiler.host));
         cmd.arg("--host-rustcflags").arg(hostflags.join(" "));
 
         let mut targetflags = flags;
         targetflags.push(format!("-Lnative={}", builder.test_helpers_out(target).display()));
-        Self::add_lld_flags(builder, target, &mut targetflags);
+        targetflags.extend(builder.lld_flags(target));
         cmd.arg("--target-rustcflags").arg(targetflags.join(" "));
 
         cmd.arg("--docck-python").arg(builder.python());
diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs
index 931c822ffe2..1e7c3930246 100644
--- a/src/tools/compiletest/src/runtest.rs
+++ b/src/tools/compiletest/src/runtest.rs
@@ -2488,6 +2488,7 @@ impl<'test> TestCx<'test> {
 
         {
             let mut diff_output = File::create(&diff_filename).unwrap();
+            let mut wrote_data = false;
             for entry in walkdir::WalkDir::new(out_dir) {
                 let entry = entry.expect("failed to read file");
                 let extension = entry.path().extension().and_then(|p| p.to_str());
@@ -2500,17 +2501,28 @@ impl<'test> TestCx<'test> {
                         if let Ok(s) = std::fs::read(&expected_path) { s } else { continue };
                     let actual_path = entry.path();
                     let actual = std::fs::read(&actual_path).unwrap();
-                    diff_output
-                        .write_all(&unified_diff::diff(
-                            &expected,
-                            &expected_path.to_string_lossy(),
-                            &actual,
-                            &actual_path.to_string_lossy(),
-                            3,
-                        ))
-                        .unwrap();
+                    let diff = unified_diff::diff(
+                        &expected,
+                        &expected_path.to_string_lossy(),
+                        &actual,
+                        &actual_path.to_string_lossy(),
+                        3,
+                    );
+                    wrote_data |= !diff.is_empty();
+                    diff_output.write_all(&diff).unwrap();
                 }
             }
+
+            if !wrote_data {
+                println!("note: diff is identical to nightly rustdoc");
+                assert!(diff_output.metadata().unwrap().len() == 0);
+                return;
+            } else if self.config.verbose {
+                eprintln!("printing diff:");
+                let mut buf = Vec::new();
+                diff_output.read_to_end(&mut buf).unwrap();
+                std::io::stderr().lock().write_all(&mut buf).unwrap();
+            }
         }
 
         match self.config.color {
diff --git a/src/tools/miri b/src/tools/miri
-Subproject c8713c2f9fc1e28c90876b9ec9557d8c5729757
+Subproject e5c3af6f516311cc4b1fc017c58d83b7442cbc3