about summary refs log tree commit diff
path: root/library/std/src
diff options
context:
space:
mode:
Diffstat (limited to 'library/std/src')
-rw-r--r--library/std/src/ascii.rs3
-rw-r--r--library/std/src/fs/tests.rs6
-rw-r--r--library/std/src/primitive_docs.rs20
-rw-r--r--library/std/src/sys/common/thread_local/mod.rs5
-rw-r--r--library/std/src/sys/common/thread_local/os_local.rs2
-rw-r--r--library/std/src/sys/hermit/time.rs12
-rw-r--r--library/std/src/sys/solid/time.rs4
-rw-r--r--library/std/src/sys/unix/time.rs16
-rw-r--r--library/std/src/sys/unsupported/mod.rs1
-rw-r--r--library/std/src/sys/unsupported/thread_parking.rs11
-rw-r--r--library/std/src/sys/wasi/mod.rs2
-rw-r--r--library/std/src/sys/wasm/mod.rs2
-rw-r--r--library/std/src/sys/windows/args.rs5
-rw-r--r--library/std/src/sys/windows/fs.rs29
-rw-r--r--library/std/src/sys_common/thread_parking/generic.rs125
-rw-r--r--library/std/src/sys_common/thread_parking/mod.rs5
-rw-r--r--library/std/src/thread/mod.rs32
-rw-r--r--library/std/src/time/tests.rs27
18 files changed, 123 insertions, 184 deletions
diff --git a/library/std/src/ascii.rs b/library/std/src/ascii.rs
index c29f015777f..b18ab50de12 100644
--- a/library/std/src/ascii.rs
+++ b/library/std/src/ascii.rs
@@ -16,6 +16,9 @@
 #[stable(feature = "rust1", since = "1.0.0")]
 pub use core::ascii::{escape_default, EscapeDefault};
 
+#[unstable(feature = "ascii_char", issue = "110998")]
+pub use core::ascii::Char;
+
 /// Extension methods for ASCII-subset only operations.
 ///
 /// Be aware that operations on seemingly non-ASCII characters can sometimes
diff --git a/library/std/src/fs/tests.rs b/library/std/src/fs/tests.rs
index 401def18458..a8a0b9f122d 100644
--- a/library/std/src/fs/tests.rs
+++ b/library/std/src/fs/tests.rs
@@ -919,6 +919,7 @@ fn symlink_noexist() {
 
 #[test]
 fn read_link() {
+    let tmpdir = tmpdir();
     if cfg!(windows) {
         // directory symlink
         assert_eq!(check!(fs::read_link(r"C:\Users\All Users")), Path::new(r"C:\ProgramData"));
@@ -933,8 +934,11 @@ fn read_link() {
                 Path::new(r"C:\Users")
             );
         }
+        // Check that readlink works with non-drive paths on Windows.
+        let link = tmpdir.join("link_unc");
+        check!(symlink_dir(r"\\localhost\c$\", &link));
+        assert_eq!(check!(fs::read_link(&link)), Path::new(r"\\localhost\c$\"));
     }
-    let tmpdir = tmpdir();
     let link = tmpdir.join("link");
     if !got_symlink_permission(&tmpdir) {
         return;
diff --git a/library/std/src/primitive_docs.rs b/library/std/src/primitive_docs.rs
index 08ffc407ead..e06ccb5b287 100644
--- a/library/std/src/primitive_docs.rs
+++ b/library/std/src/primitive_docs.rs
@@ -610,6 +610,9 @@ mod prim_pointer {}
 /// if the element type allows it. As a stopgap, trait implementations are
 /// statically generated up to size 32.
 ///
+/// Arrays of sizes from 1 to 12 (inclusive) implement [`From<Tuple>`], where `Tuple`
+/// is a homogenous [prim@tuple] of appropriate length.
+///
 /// Arrays coerce to [slices (`[T]`)][slice], so a slice method may be called on
 /// an array. Indeed, this provides most of the API for working with arrays.
 ///
@@ -672,6 +675,13 @@ mod prim_pointer {}
 /// move_away(roa);
 /// ```
 ///
+/// Arrays can be created from homogenous tuples of appropriate length:
+///
+/// ```
+/// let tuple: (u32, u32, u32) = (1, 2, 3);
+/// let array: [u32; 3] = tuple.into();
+/// ```
+///
 /// # Editions
 ///
 /// Prior to Rust 1.53, arrays did not implement [`IntoIterator`] by value, so the method call
@@ -774,6 +784,7 @@ mod prim_pointer {}
 /// [`Borrow`]: borrow::Borrow
 /// [`BorrowMut`]: borrow::BorrowMut
 /// [slice pattern]: ../reference/patterns.html#slice-patterns
+/// [`From<Tuple>`]: convert::From
 #[stable(feature = "rust1", since = "1.0.0")]
 mod prim_array {}
 
@@ -1000,7 +1011,9 @@ mod prim_str {}
 /// * [`Debug`]
 /// * [`Default`]
 /// * [`Hash`]
+/// * [`From<[T; N]>`][from]
 ///
+/// [from]: convert::From
 /// [`Debug`]: fmt::Debug
 /// [`Hash`]: hash::Hash
 ///
@@ -1051,6 +1064,13 @@ mod prim_str {}
 /// assert_eq!(y, 5);
 /// ```
 ///
+/// Homogenous tuples can be created from arrays of appropriate length:
+///
+/// ```
+/// let array: [u32; 3] = [1, 2, 3];
+/// let tuple: (u32, u32, u32) = array.into();
+/// ```
+///
 #[stable(feature = "rust1", since = "1.0.0")]
 mod prim_tuple {}
 
diff --git a/library/std/src/sys/common/thread_local/mod.rs b/library/std/src/sys/common/thread_local/mod.rs
index 951d509ec95..77f64588310 100644
--- a/library/std/src/sys/common/thread_local/mod.rs
+++ b/library/std/src/sys/common/thread_local/mod.rs
@@ -1,5 +1,10 @@
 #![unstable(feature = "thread_local_internals", reason = "should not be necessary", issue = "none")]
 
+// There are three thread-local implementations: "static", "fast", "OS".
+// The "OS" thread local key type is accessed via platform-specific API calls and is slow, while the
+// "fast" key type is accessed via code generated via LLVM, where TLS keys are set up by the linker.
+// "static" is for single-threaded platforms where a global static is sufficient.
+
 cfg_if::cfg_if! {
     if #[cfg(all(target_family = "wasm", not(target_feature = "atomics")))] {
         #[doc(hidden)]
diff --git a/library/std/src/sys/common/thread_local/os_local.rs b/library/std/src/sys/common/thread_local/os_local.rs
index d004897df28..5d48ce1e03b 100644
--- a/library/std/src/sys/common/thread_local/os_local.rs
+++ b/library/std/src/sys/common/thread_local/os_local.rs
@@ -18,7 +18,7 @@ pub macro thread_local_inner {
         ) -> $crate::option::Option<&'static $t> {
             const INIT_EXPR: $t = $init;
 
-                        // On platforms without `#[thread_local]` we fall back to the
+            // On platforms without `#[thread_local]` we fall back to the
             // same implementation as below for os thread locals.
             #[inline]
             const fn __init() -> $t { INIT_EXPR }
diff --git a/library/std/src/sys/hermit/time.rs b/library/std/src/sys/hermit/time.rs
index 32ddc4346ee..5440d85df4a 100644
--- a/library/std/src/sys/hermit/time.rs
+++ b/library/std/src/sys/hermit/time.rs
@@ -40,11 +40,7 @@ impl Timespec {
     }
 
     fn checked_add_duration(&self, other: &Duration) -> Option<Timespec> {
-        let mut secs = other
-            .as_secs()
-            .try_into() // <- target type would be `libc::time_t`
-            .ok()
-            .and_then(|secs| self.t.tv_sec.checked_add(secs))?;
+        let mut secs = self.tv_sec.checked_add_unsigned(other.as_secs())?;
 
         // Nano calculations can't overflow because nanos are <1B which fit
         // in a u32.
@@ -57,11 +53,7 @@ impl Timespec {
     }
 
     fn checked_sub_duration(&self, other: &Duration) -> Option<Timespec> {
-        let mut secs = other
-            .as_secs()
-            .try_into() // <- target type would be `libc::time_t`
-            .ok()
-            .and_then(|secs| self.t.tv_sec.checked_sub(secs))?;
+        let mut secs = self.tv_sec.checked_sub_unsigned(other.as_secs())?;
 
         // Similar to above, nanos can't overflow.
         let mut nsec = self.t.tv_nsec as i32 - other.subsec_nanos() as i32;
diff --git a/library/std/src/sys/solid/time.rs b/library/std/src/sys/solid/time.rs
index ce31cb45a69..f83f1644fe8 100644
--- a/library/std/src/sys/solid/time.rs
+++ b/library/std/src/sys/solid/time.rs
@@ -47,10 +47,10 @@ impl SystemTime {
     }
 
     pub fn checked_add_duration(&self, other: &Duration) -> Option<SystemTime> {
-        Some(SystemTime(self.0.checked_add(other.as_secs().try_into().ok()?)?))
+        Some(SystemTime(self.0.checked_add_unsigned(other.as_secs())?))
     }
 
     pub fn checked_sub_duration(&self, other: &Duration) -> Option<SystemTime> {
-        Some(SystemTime(self.0.checked_sub(other.as_secs().try_into().ok()?)?))
+        Some(SystemTime(self.0.checked_sub_unsigned(other.as_secs())?))
     }
 }
diff --git a/library/std/src/sys/unix/time.rs b/library/std/src/sys/unix/time.rs
index a61d926ca8b..a9fbc7ab108 100644
--- a/library/std/src/sys/unix/time.rs
+++ b/library/std/src/sys/unix/time.rs
@@ -113,11 +113,7 @@ impl Timespec {
     }
 
     pub fn checked_add_duration(&self, other: &Duration) -> Option<Timespec> {
-        let mut secs = other
-            .as_secs()
-            .try_into() // <- target type would be `i64`
-            .ok()
-            .and_then(|secs| self.tv_sec.checked_add(secs))?;
+        let mut secs = self.tv_sec.checked_add_unsigned(other.as_secs())?;
 
         // Nano calculations can't overflow because nanos are <1B which fit
         // in a u32.
@@ -126,15 +122,11 @@ impl Timespec {
             nsec -= NSEC_PER_SEC as u32;
             secs = secs.checked_add(1)?;
         }
-        Some(Timespec::new(secs, nsec as i64))
+        Some(Timespec::new(secs, nsec.into()))
     }
 
     pub fn checked_sub_duration(&self, other: &Duration) -> Option<Timespec> {
-        let mut secs = other
-            .as_secs()
-            .try_into() // <- target type would be `i64`
-            .ok()
-            .and_then(|secs| self.tv_sec.checked_sub(secs))?;
+        let mut secs = self.tv_sec.checked_sub_unsigned(other.as_secs())?;
 
         // Similar to above, nanos can't overflow.
         let mut nsec = self.tv_nsec.0 as i32 - other.subsec_nanos() as i32;
@@ -142,7 +134,7 @@ impl Timespec {
             nsec += NSEC_PER_SEC as i32;
             secs = secs.checked_sub(1)?;
         }
-        Some(Timespec::new(secs, nsec as i64))
+        Some(Timespec::new(secs, nsec.into()))
     }
 
     #[allow(dead_code)]
diff --git a/library/std/src/sys/unsupported/mod.rs b/library/std/src/sys/unsupported/mod.rs
index 15b22c620d5..e1a38de6471 100644
--- a/library/std/src/sys/unsupported/mod.rs
+++ b/library/std/src/sys/unsupported/mod.rs
@@ -22,6 +22,7 @@ pub mod thread;
 #[cfg(target_thread_local)]
 pub mod thread_local_dtor;
 pub mod thread_local_key;
+pub mod thread_parking;
 pub mod time;
 
 mod common;
diff --git a/library/std/src/sys/unsupported/thread_parking.rs b/library/std/src/sys/unsupported/thread_parking.rs
new file mode 100644
index 00000000000..197078bb186
--- /dev/null
+++ b/library/std/src/sys/unsupported/thread_parking.rs
@@ -0,0 +1,11 @@
+use crate::pin::Pin;
+use crate::time::Duration;
+
+pub struct Parker {}
+
+impl Parker {
+    pub unsafe fn new_in_place(_parker: *mut Parker) {}
+    pub unsafe fn park(self: Pin<&Self>) {}
+    pub unsafe fn park_timeout(self: Pin<&Self>, _dur: Duration) {}
+    pub fn unpark(self: Pin<&Self>) {}
+}
diff --git a/library/std/src/sys/wasi/mod.rs b/library/std/src/sys/wasi/mod.rs
index c468ae395fc..a2223708024 100644
--- a/library/std/src/sys/wasi/mod.rs
+++ b/library/std/src/sys/wasi/mod.rs
@@ -47,6 +47,8 @@ pub mod thread;
 pub mod thread_local_dtor;
 #[path = "../unsupported/thread_local_key.rs"]
 pub mod thread_local_key;
+#[path = "../unsupported/thread_parking.rs"]
+pub mod thread_parking;
 pub mod time;
 
 cfg_if::cfg_if! {
diff --git a/library/std/src/sys/wasm/mod.rs b/library/std/src/sys/wasm/mod.rs
index 77ebe3c4ac6..6c05b56e1bf 100644
--- a/library/std/src/sys/wasm/mod.rs
+++ b/library/std/src/sys/wasm/mod.rs
@@ -70,6 +70,8 @@ cfg_if::cfg_if! {
         pub mod once;
         #[path = "../unsupported/thread.rs"]
         pub mod thread;
+        #[path = "../unsupported/thread_parking.rs"]
+        pub mod thread_parking;
     }
 }
 
diff --git a/library/std/src/sys/windows/args.rs b/library/std/src/sys/windows/args.rs
index 43c0cdb657e..5bfd8b52ed0 100644
--- a/library/std/src/sys/windows/args.rs
+++ b/library/std/src/sys/windows/args.rs
@@ -313,6 +313,9 @@ pub(crate) fn make_bat_command_line(
 ///
 /// This is necessary because cmd.exe does not support verbatim paths.
 pub(crate) fn to_user_path(path: &Path) -> io::Result<Vec<u16>> {
+    from_wide_to_user_path(to_u16s(path)?)
+}
+pub(crate) fn from_wide_to_user_path(mut path: Vec<u16>) -> io::Result<Vec<u16>> {
     use crate::ptr;
     use crate::sys::windows::fill_utf16_buf;
 
@@ -325,8 +328,6 @@ pub(crate) fn to_user_path(path: &Path) -> io::Result<Vec<u16>> {
     const N: u16 = b'N' as _;
     const C: u16 = b'C' as _;
 
-    let mut path = to_u16s(path)?;
-
     // Early return if the path is too long to remove the verbatim prefix.
     const LEGACY_MAX_PATH: usize = 260;
     if path.len() > LEGACY_MAX_PATH {
diff --git a/library/std/src/sys/windows/fs.rs b/library/std/src/sys/windows/fs.rs
index f99cdfbecfb..fe052c8281b 100644
--- a/library/std/src/sys/windows/fs.rs
+++ b/library/std/src/sys/windows/fs.rs
@@ -477,7 +477,7 @@ impl File {
     fn reparse_point(
         &self,
         space: &mut Align8<[MaybeUninit<u8>]>,
-    ) -> io::Result<(c::DWORD, *const c::REPARSE_DATA_BUFFER)> {
+    ) -> io::Result<(c::DWORD, *mut c::REPARSE_DATA_BUFFER)> {
         unsafe {
             let mut bytes = 0;
             cvt({
@@ -496,7 +496,7 @@ impl File {
                 )
             })?;
             const _: () = assert!(core::mem::align_of::<c::REPARSE_DATA_BUFFER>() <= 8);
-            Ok((bytes, space.0.as_ptr().cast::<c::REPARSE_DATA_BUFFER>()))
+            Ok((bytes, space.0.as_mut_ptr().cast::<c::REPARSE_DATA_BUFFER>()))
         }
     }
 
@@ -506,22 +506,22 @@ impl File {
         unsafe {
             let (path_buffer, subst_off, subst_len, relative) = match (*buf).ReparseTag {
                 c::IO_REPARSE_TAG_SYMLINK => {
-                    let info: *const c::SYMBOLIC_LINK_REPARSE_BUFFER =
-                        ptr::addr_of!((*buf).rest).cast();
+                    let info: *mut c::SYMBOLIC_LINK_REPARSE_BUFFER =
+                        ptr::addr_of_mut!((*buf).rest).cast();
                     assert!(info.is_aligned());
                     (
-                        ptr::addr_of!((*info).PathBuffer).cast::<u16>(),
+                        ptr::addr_of_mut!((*info).PathBuffer).cast::<u16>(),
                         (*info).SubstituteNameOffset / 2,
                         (*info).SubstituteNameLength / 2,
                         (*info).Flags & c::SYMLINK_FLAG_RELATIVE != 0,
                     )
                 }
                 c::IO_REPARSE_TAG_MOUNT_POINT => {
-                    let info: *const c::MOUNT_POINT_REPARSE_BUFFER =
-                        ptr::addr_of!((*buf).rest).cast();
+                    let info: *mut c::MOUNT_POINT_REPARSE_BUFFER =
+                        ptr::addr_of_mut!((*buf).rest).cast();
                     assert!(info.is_aligned());
                     (
-                        ptr::addr_of!((*info).PathBuffer).cast::<u16>(),
+                        ptr::addr_of_mut!((*info).PathBuffer).cast::<u16>(),
                         (*info).SubstituteNameOffset / 2,
                         (*info).SubstituteNameLength / 2,
                         false,
@@ -535,13 +535,20 @@ impl File {
                 }
             };
             let subst_ptr = path_buffer.add(subst_off.into());
-            let mut subst = slice::from_raw_parts(subst_ptr, subst_len as usize);
+            let subst = slice::from_raw_parts_mut(subst_ptr, subst_len as usize);
             // Absolute paths start with an NT internal namespace prefix `\??\`
             // We should not let it leak through.
             if !relative && subst.starts_with(&[92u16, 63u16, 63u16, 92u16]) {
-                subst = &subst[4..];
+                // Turn `\??\` into `\\?\` (a verbatim path).
+                subst[1] = b'\\' as u16;
+                // Attempt to convert to a more user-friendly path.
+                let user = super::args::from_wide_to_user_path(
+                    subst.iter().copied().chain([0]).collect(),
+                )?;
+                Ok(PathBuf::from(OsString::from_wide(&user.strip_suffix(&[0]).unwrap_or(&user))))
+            } else {
+                Ok(PathBuf::from(OsString::from_wide(subst)))
             }
-            Ok(PathBuf::from(OsString::from_wide(subst)))
         }
     }
 
diff --git a/library/std/src/sys_common/thread_parking/generic.rs b/library/std/src/sys_common/thread_parking/generic.rs
deleted file mode 100644
index 3209bffe353..00000000000
--- a/library/std/src/sys_common/thread_parking/generic.rs
+++ /dev/null
@@ -1,125 +0,0 @@
-//! Parker implementation based on a Mutex and Condvar.
-
-use crate::pin::Pin;
-use crate::sync::atomic::AtomicUsize;
-use crate::sync::atomic::Ordering::SeqCst;
-use crate::sync::{Condvar, Mutex};
-use crate::time::Duration;
-
-const EMPTY: usize = 0;
-const PARKED: usize = 1;
-const NOTIFIED: usize = 2;
-
-pub struct Parker {
-    state: AtomicUsize,
-    lock: Mutex<()>,
-    cvar: Condvar,
-}
-
-impl Parker {
-    /// Construct the generic parker. The UNIX parker implementation
-    /// requires this to happen in-place.
-    pub unsafe fn new_in_place(parker: *mut Parker) {
-        parker.write(Parker {
-            state: AtomicUsize::new(EMPTY),
-            lock: Mutex::new(()),
-            cvar: Condvar::new(),
-        });
-    }
-
-    // This implementation doesn't require `unsafe` and `Pin`, but other implementations do.
-    pub unsafe fn park(self: Pin<&Self>) {
-        // If we were previously notified then we consume this notification and
-        // return quickly.
-        if self.state.compare_exchange(NOTIFIED, EMPTY, SeqCst, SeqCst).is_ok() {
-            return;
-        }
-
-        // Otherwise we need to coordinate going to sleep
-        let mut m = self.lock.lock().unwrap();
-        match self.state.compare_exchange(EMPTY, PARKED, SeqCst, SeqCst) {
-            Ok(_) => {}
-            Err(NOTIFIED) => {
-                // We must read here, even though we know it will be `NOTIFIED`.
-                // This is because `unpark` may have been called again since we read
-                // `NOTIFIED` in the `compare_exchange` above. We must perform an
-                // acquire operation that synchronizes with that `unpark` to observe
-                // any writes it made before the call to unpark. To do that we must
-                // read from the write it made to `state`.
-                let old = self.state.swap(EMPTY, SeqCst);
-                assert_eq!(old, NOTIFIED, "park state changed unexpectedly");
-                return;
-            } // should consume this notification, so prohibit spurious wakeups in next park.
-            Err(_) => panic!("inconsistent park state"),
-        }
-        loop {
-            m = self.cvar.wait(m).unwrap();
-            match self.state.compare_exchange(NOTIFIED, EMPTY, SeqCst, SeqCst) {
-                Ok(_) => return, // got a notification
-                Err(_) => {}     // spurious wakeup, go back to sleep
-            }
-        }
-    }
-
-    // This implementation doesn't require `unsafe` and `Pin`, but other implementations do.
-    pub unsafe fn park_timeout(self: Pin<&Self>, dur: Duration) {
-        // Like `park` above we have a fast path for an already-notified thread, and
-        // afterwards we start coordinating for a sleep.
-        // return quickly.
-        if self.state.compare_exchange(NOTIFIED, EMPTY, SeqCst, SeqCst).is_ok() {
-            return;
-        }
-        let m = self.lock.lock().unwrap();
-        match self.state.compare_exchange(EMPTY, PARKED, SeqCst, SeqCst) {
-            Ok(_) => {}
-            Err(NOTIFIED) => {
-                // We must read again here, see `park`.
-                let old = self.state.swap(EMPTY, SeqCst);
-                assert_eq!(old, NOTIFIED, "park state changed unexpectedly");
-                return;
-            } // should consume this notification, so prohibit spurious wakeups in next park.
-            Err(_) => panic!("inconsistent park_timeout state"),
-        }
-
-        // Wait with a timeout, and if we spuriously wake up or otherwise wake up
-        // from a notification we just want to unconditionally set the state back to
-        // empty, either consuming a notification or un-flagging ourselves as
-        // parked.
-        let (_m, _result) = self.cvar.wait_timeout(m, dur).unwrap();
-        match self.state.swap(EMPTY, SeqCst) {
-            NOTIFIED => {} // got a notification, hurray!
-            PARKED => {}   // no notification, alas
-            n => panic!("inconsistent park_timeout state: {n}"),
-        }
-    }
-
-    // This implementation doesn't require `Pin`, but other implementations do.
-    pub fn unpark(self: Pin<&Self>) {
-        // To ensure the unparked thread will observe any writes we made
-        // before this call, we must perform a release operation that `park`
-        // can synchronize with. To do that we must write `NOTIFIED` even if
-        // `state` is already `NOTIFIED`. That is why this must be a swap
-        // rather than a compare-and-swap that returns if it reads `NOTIFIED`
-        // on failure.
-        match self.state.swap(NOTIFIED, SeqCst) {
-            EMPTY => return,    // no one was waiting
-            NOTIFIED => return, // already unparked
-            PARKED => {}        // gotta go wake someone up
-            _ => panic!("inconsistent state in unpark"),
-        }
-
-        // There is a period between when the parked thread sets `state` to
-        // `PARKED` (or last checked `state` in the case of a spurious wake
-        // up) and when it actually waits on `cvar`. If we were to notify
-        // during this period it would be ignored and then when the parked
-        // thread went to sleep it would never wake up. Fortunately, it has
-        // `lock` locked at this stage so we can acquire `lock` to wait until
-        // it is ready to receive the notification.
-        //
-        // Releasing `lock` before the call to `notify_one` means that when the
-        // parked thread wakes it doesn't get woken only to have to wait for us
-        // to release `lock`.
-        drop(self.lock.lock().unwrap());
-        self.cvar.notify_one()
-    }
-}
diff --git a/library/std/src/sys_common/thread_parking/mod.rs b/library/std/src/sys_common/thread_parking/mod.rs
index e8e028bb330..c4d3f9ea2f4 100644
--- a/library/std/src/sys_common/thread_parking/mod.rs
+++ b/library/std/src/sys_common/thread_parking/mod.rs
@@ -18,10 +18,7 @@ cfg_if::cfg_if! {
     ))] {
         mod id;
         pub use id::Parker;
-    } else if #[cfg(any(windows, target_family = "unix"))] {
-        pub use crate::sys::thread_parking::Parker;
     } else {
-        mod generic;
-        pub use generic::Parker;
+        pub use crate::sys::thread_parking::Parker;
     }
 }
diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs
index 9cdc17a287c..f712c872708 100644
--- a/library/std/src/thread/mod.rs
+++ b/library/std/src/thread/mod.rs
@@ -193,22 +193,22 @@ pub use scoped::{scope, Scope, ScopedJoinHandle};
 #[macro_use]
 mod local;
 
-#[stable(feature = "rust1", since = "1.0.0")]
-pub use self::local::{AccessError, LocalKey};
-
-// Provide the type used by the thread_local! macro to access TLS keys. This
-// needs to be kept in sync with the macro itself (in `local.rs`).
-// There are three types: "static", "fast", "OS". The "OS" thread local key
-// type is accessed via platform-specific API calls and is slow, while the "fast"
-// key type is accessed via code generated via LLVM, where TLS keys are set up
-// by the elf linker. "static" is for single-threaded platforms where a global
-// static is sufficient.
-
-// Implementation details used by the thread_local!{} macro.
-#[doc(hidden)]
-#[unstable(feature = "thread_local_internals", issue = "none")]
-pub mod local_impl {
-    pub use crate::sys::common::thread_local::{thread_local_inner, Key};
+cfg_if::cfg_if! {
+    if #[cfg(test)] {
+        // Avoid duplicating the global state assoicated with thread-locals between this crate and
+        // realstd. Miri relies on this.
+        pub use realstd::thread::{local_impl, AccessError, LocalKey};
+    } else {
+        #[stable(feature = "rust1", since = "1.0.0")]
+        pub use self::local::{AccessError, LocalKey};
+
+        // Implementation details used by the thread_local!{} macro.
+        #[doc(hidden)]
+        #[unstable(feature = "thread_local_internals", issue = "none")]
+        pub mod local_impl {
+            pub use crate::sys::common::thread_local::{thread_local_inner, Key};
+        }
+    }
 }
 
 ////////////////////////////////////////////////////////////////////////////////
diff --git a/library/std/src/time/tests.rs b/library/std/src/time/tests.rs
index 2e64ae59aff..6ed84806e6d 100644
--- a/library/std/src/time/tests.rs
+++ b/library/std/src/time/tests.rs
@@ -1,4 +1,5 @@
 use super::{Duration, Instant, SystemTime, UNIX_EPOCH};
+use core::fmt::Debug;
 #[cfg(not(target_arch = "wasm32"))]
 use test::{black_box, Bencher};
 
@@ -201,6 +202,32 @@ fn since_epoch() {
     assert!(a < hundred_twenty_years);
 }
 
+#[test]
+fn big_math() {
+    // Check that the same result occurs when adding/subtracting each duration one at a time as when
+    // adding/subtracting them all at once.
+    #[track_caller]
+    fn check<T: Eq + Copy + Debug>(start: Option<T>, op: impl Fn(&T, Duration) -> Option<T>) {
+        const DURATIONS: [Duration; 2] =
+            [Duration::from_secs(i64::MAX as _), Duration::from_secs(50)];
+        if let Some(start) = start {
+            assert_eq!(
+                op(&start, DURATIONS.into_iter().sum()),
+                DURATIONS.into_iter().try_fold(start, |t, d| op(&t, d))
+            )
+        }
+    }
+
+    check(SystemTime::UNIX_EPOCH.checked_sub(Duration::from_secs(100)), SystemTime::checked_add);
+    check(SystemTime::UNIX_EPOCH.checked_add(Duration::from_secs(100)), SystemTime::checked_sub);
+
+    let instant = Instant::now();
+    check(instant.checked_sub(Duration::from_secs(100)), Instant::checked_add);
+    check(instant.checked_sub(Duration::from_secs(i64::MAX as _)), Instant::checked_add);
+    check(instant.checked_add(Duration::from_secs(100)), Instant::checked_sub);
+    check(instant.checked_add(Duration::from_secs(i64::MAX as _)), Instant::checked_sub);
+}
+
 macro_rules! bench_instant_threaded {
     ($bench_name:ident, $thread_count:expr) => {
         #[bench]