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/collections/hash/map.rs71
-rw-r--r--library/std/src/collections/hash/map/tests.rs8
-rw-r--r--library/std/src/collections/hash/set.rs3
-rw-r--r--library/std/src/collections/mod.rs5
-rw-r--r--library/std/src/error.rs18
-rw-r--r--library/std/src/ffi/os_str.rs86
-rw-r--r--library/std/src/io/buffered/bufreader.rs17
-rw-r--r--library/std/src/io/impls.rs3
-rw-r--r--library/std/src/io/mod.rs37
-rw-r--r--library/std/src/io/tests.rs49
-rw-r--r--library/std/src/io/util.rs10
-rw-r--r--library/std/src/lib.rs17
-rw-r--r--library/std/src/net/parser.rs32
-rw-r--r--library/std/src/path.rs30
-rw-r--r--library/std/src/prelude/mod.rs34
-rw-r--r--library/std/src/prelude/v1.rs13
-rw-r--r--library/std/src/process.rs50
-rw-r--r--library/std/src/sys/unix/ext/process.rs25
-rw-r--r--library/std/src/sys/unix/kernel_copy.rs18
-rw-r--r--library/std/src/sys/unix/kernel_copy/tests.rs18
-rw-r--r--library/std/src/sys/unix/process/process_unix.rs32
-rw-r--r--library/std/src/sys/unix/process/process_unix/tests.rs30
-rw-r--r--library/std/src/sys/unix/stack_overflow.rs5
-rw-r--r--library/std/src/sys/unix/thread.rs5
-rw-r--r--library/std/src/sys/wasi/fs.rs6
-rw-r--r--library/std/src/sys_common/thread_local_dtor.rs2
26 files changed, 544 insertions, 80 deletions
diff --git a/library/std/src/collections/hash/map.rs b/library/std/src/collections/hash/map.rs
index 27f7191831d..ed32668456d 100644
--- a/library/std/src/collections/hash/map.rs
+++ b/library/std/src/collections/hash/map.rs
@@ -1,3 +1,5 @@
+// ignore-tidy-filelength
+
 #[cfg(test)]
 mod tests;
 
@@ -15,7 +17,7 @@ use crate::iter::{FromIterator, FusedIterator};
 use crate::ops::Index;
 use crate::sys;
 
-/// A hash map implemented with quadratic probing and SIMD lookup.
+/// A [hash map] implemented with quadratic probing and SIMD lookup.
 ///
 /// By default, `HashMap` uses a hashing algorithm selected to provide
 /// resistance against HashDoS attacks. The algorithm is randomly seeded, and a
@@ -60,6 +62,7 @@ use crate::sys;
 /// The original C++ version of SwissTable can be found [here], and this
 /// [CppCon talk] gives an overview of how the algorithm works.
 ///
+/// [hash map]: crate::collections#use-a-hashmap-when
 /// [hashing algorithms available on crates.io]: https://crates.io/keywords/hasher
 /// [SwissTable]: https://abseil.io/blog/20180927-swisstables
 /// [here]: https://github.com/abseil/abseil-cpp/blob/master/absl/container/internal/raw_hash_set.h
@@ -842,6 +845,37 @@ where
         self.base.insert(k, v)
     }
 
+    /// Tries to insert a key-value pair into the map, and returns
+    /// a mutable reference to the value in the entry.
+    ///
+    /// If the map already had this key present, nothing is updated, and
+    /// an error containing the occupied entry and the value is returned.
+    ///
+    /// # Examples
+    ///
+    /// Basic usage:
+    ///
+    /// ```
+    /// #![feature(map_try_insert)]
+    ///
+    /// use std::collections::HashMap;
+    ///
+    /// let mut map = HashMap::new();
+    /// assert_eq!(map.try_insert(37, "a").unwrap(), &"a");
+    ///
+    /// let err = map.try_insert(37, "b").unwrap_err();
+    /// assert_eq!(err.entry.key(), &37);
+    /// assert_eq!(err.entry.get(), &"a");
+    /// assert_eq!(err.value, "b");
+    /// ```
+    #[unstable(feature = "map_try_insert", issue = "82766")]
+    pub fn try_insert(&mut self, key: K, value: V) -> Result<&mut V, OccupiedError<'_, K, V>> {
+        match self.entry(key) {
+            Occupied(entry) => Err(OccupiedError { entry, value }),
+            Vacant(entry) => Ok(entry.insert(value)),
+        }
+    }
+
     /// Removes a key from the map, returning the value at the key if the key
     /// was previously in the map.
     ///
@@ -1851,6 +1885,41 @@ impl<K: Debug, V> Debug for VacantEntry<'_, K, V> {
     }
 }
 
+/// The error returned by [`try_insert`](HashMap::try_insert) when the key already exists.
+///
+/// Contains the occupied entry, and the value that was not inserted.
+#[unstable(feature = "map_try_insert", issue = "82766")]
+pub struct OccupiedError<'a, K: 'a, V: 'a> {
+    /// The entry in the map that was already occupied.
+    pub entry: OccupiedEntry<'a, K, V>,
+    /// The value which was not inserted, because the entry was already occupied.
+    pub value: V,
+}
+
+#[unstable(feature = "map_try_insert", issue = "82766")]
+impl<K: Debug, V: Debug> Debug for OccupiedError<'_, K, V> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.debug_struct("OccupiedError")
+            .field("key", self.entry.key())
+            .field("old_value", self.entry.get())
+            .field("new_value", &self.value)
+            .finish()
+    }
+}
+
+#[unstable(feature = "map_try_insert", issue = "82766")]
+impl<'a, K: Debug, V: Debug> fmt::Display for OccupiedError<'a, K, V> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        write!(
+            f,
+            "failed to insert {:?}, key {:?} already exists with value {:?}",
+            self.value,
+            self.entry.key(),
+            self.entry.get(),
+        )
+    }
+}
+
 #[stable(feature = "rust1", since = "1.0.0")]
 impl<'a, K, V, S> IntoIterator for &'a HashMap<K, V, S> {
     type Item = (&'a K, &'a V);
diff --git a/library/std/src/collections/hash/map/tests.rs b/library/std/src/collections/hash/map/tests.rs
index 467968354e2..819be142227 100644
--- a/library/std/src/collections/hash/map/tests.rs
+++ b/library/std/src/collections/hash/map/tests.rs
@@ -774,11 +774,11 @@ fn test_occupied_entry_key() {
     let key = "hello there";
     let value = "value goes here";
     assert!(a.is_empty());
-    a.insert(key.clone(), value.clone());
+    a.insert(key, value);
     assert_eq!(a.len(), 1);
     assert_eq!(a[key], value);
 
-    match a.entry(key.clone()) {
+    match a.entry(key) {
         Vacant(_) => panic!(),
         Occupied(e) => assert_eq!(key, *e.key()),
     }
@@ -793,11 +793,11 @@ fn test_vacant_entry_key() {
     let value = "value goes here";
 
     assert!(a.is_empty());
-    match a.entry(key.clone()) {
+    match a.entry(key) {
         Occupied(_) => panic!(),
         Vacant(e) => {
             assert_eq!(key, *e.key());
-            e.insert(value.clone());
+            e.insert(value);
         }
     }
     assert_eq!(a.len(), 1);
diff --git a/library/std/src/collections/hash/set.rs b/library/std/src/collections/hash/set.rs
index 912e975aa0a..8c801b9f128 100644
--- a/library/std/src/collections/hash/set.rs
+++ b/library/std/src/collections/hash/set.rs
@@ -19,7 +19,7 @@ use super::map::{map_try_reserve_error, RandomState};
 // for `bucket.val` in the case of HashSet. I suppose we would need HKT
 // to get rid of it properly.
 
-/// A hash set implemented as a `HashMap` where the value is `()`.
+/// A [hash set] implemented as a `HashMap` where the value is `()`.
 ///
 /// As with the [`HashMap`] type, a `HashSet` requires that the elements
 /// implement the [`Eq`] and [`Hash`] traits. This can frequently be achieved by
@@ -105,6 +105,7 @@ use super::map::{map_try_reserve_error, RandomState};
 /// // use the values stored in the set
 /// ```
 ///
+/// [hash set]: crate::collections#use-the-set-variant-of-any-of-these-maps-when
 /// [`HashMap`]: crate::collections::HashMap
 /// [`RefCell`]: crate::cell::RefCell
 /// [`Cell`]: crate::cell::Cell
diff --git a/library/std/src/collections/mod.rs b/library/std/src/collections/mod.rs
index 80a13d52a2a..7f8f9c991fe 100644
--- a/library/std/src/collections/mod.rs
+++ b/library/std/src/collections/mod.rs
@@ -401,9 +401,10 @@
 #![stable(feature = "rust1", since = "1.0.0")]
 
 #[stable(feature = "rust1", since = "1.0.0")]
-#[rustc_deprecated(reason = "moved to `std::ops::Bound`", since = "1.26.0")]
+#[rustc_deprecated(reason = "moved to `std::ops::Bound`", since = "1.52.0")]
 #[doc(hidden)]
-pub use crate::ops::Bound;
+pub type Bound<T> = crate::ops::Bound<T>;
+
 #[stable(feature = "rust1", since = "1.0.0")]
 pub use alloc_crate::collections::{binary_heap, btree_map, btree_set};
 #[stable(feature = "rust1", since = "1.0.0")]
diff --git a/library/std/src/error.rs b/library/std/src/error.rs
index 94338c7b04d..80c35307d52 100644
--- a/library/std/src/error.rs
+++ b/library/std/src/error.rs
@@ -470,6 +470,24 @@ impl Error for char::DecodeUtf16Error {
     }
 }
 
+#[unstable(feature = "map_try_insert", issue = "82766")]
+impl<'a, K: Debug + Ord, V: Debug> Error
+    for crate::collections::btree_map::OccupiedError<'a, K, V>
+{
+    #[allow(deprecated)]
+    fn description(&self) -> &str {
+        "key already exists"
+    }
+}
+
+#[unstable(feature = "map_try_insert", issue = "82766")]
+impl<'a, K: Debug, V: Debug> Error for crate::collections::hash_map::OccupiedError<'a, K, V> {
+    #[allow(deprecated)]
+    fn description(&self) -> &str {
+        "key already exists"
+    }
+}
+
 #[stable(feature = "box_error", since = "1.8.0")]
 impl<T: Error> Error for Box<T> {
     #[allow(deprecated, deprecated_in_future)]
diff --git a/library/std/src/ffi/os_str.rs b/library/std/src/ffi/os_str.rs
index 272eccda894..ce52ffc0241 100644
--- a/library/std/src/ffi/os_str.rs
+++ b/library/std/src/ffi/os_str.rs
@@ -5,6 +5,7 @@ use crate::borrow::{Borrow, Cow};
 use crate::cmp;
 use crate::fmt;
 use crate::hash::{Hash, Hasher};
+use crate::iter::{Extend, FromIterator};
 use crate::ops;
 use crate::rc::Rc;
 use crate::str::FromStr;
@@ -1192,3 +1193,88 @@ impl FromStr for OsString {
         Ok(OsString::from(s))
     }
 }
+
+#[stable(feature = "osstring_extend", since = "1.52.0")]
+impl Extend<OsString> for OsString {
+    #[inline]
+    fn extend<T: IntoIterator<Item = OsString>>(&mut self, iter: T) {
+        for s in iter {
+            self.push(&s);
+        }
+    }
+}
+
+#[stable(feature = "osstring_extend", since = "1.52.0")]
+impl<'a> Extend<&'a OsStr> for OsString {
+    #[inline]
+    fn extend<T: IntoIterator<Item = &'a OsStr>>(&mut self, iter: T) {
+        for s in iter {
+            self.push(s);
+        }
+    }
+}
+
+#[stable(feature = "osstring_extend", since = "1.52.0")]
+impl<'a> Extend<Cow<'a, OsStr>> for OsString {
+    #[inline]
+    fn extend<T: IntoIterator<Item = Cow<'a, OsStr>>>(&mut self, iter: T) {
+        for s in iter {
+            self.push(&s);
+        }
+    }
+}
+
+#[stable(feature = "osstring_extend", since = "1.52.0")]
+impl FromIterator<OsString> for OsString {
+    #[inline]
+    fn from_iter<I: IntoIterator<Item = OsString>>(iter: I) -> Self {
+        let mut iterator = iter.into_iter();
+
+        // Because we're iterating over `OsString`s, we can avoid at least
+        // one allocation by getting the first string from the iterator
+        // and appending to it all the subsequent strings.
+        match iterator.next() {
+            None => OsString::new(),
+            Some(mut buf) => {
+                buf.extend(iterator);
+                buf
+            }
+        }
+    }
+}
+
+#[stable(feature = "osstring_extend", since = "1.52.0")]
+impl<'a> FromIterator<&'a OsStr> for OsString {
+    #[inline]
+    fn from_iter<I: IntoIterator<Item = &'a OsStr>>(iter: I) -> Self {
+        let mut buf = Self::new();
+        for s in iter {
+            buf.push(s);
+        }
+        buf
+    }
+}
+
+#[stable(feature = "osstring_extend", since = "1.52.0")]
+impl<'a> FromIterator<Cow<'a, OsStr>> for OsString {
+    #[inline]
+    fn from_iter<I: IntoIterator<Item = Cow<'a, OsStr>>>(iter: I) -> Self {
+        let mut iterator = iter.into_iter();
+
+        // Because we're iterating over `OsString`s, we can avoid at least
+        // one allocation by getting the first owned string from the iterator
+        // and appending to it all the subsequent strings.
+        match iterator.next() {
+            None => OsString::new(),
+            Some(Cow::Owned(mut buf)) => {
+                buf.extend(iterator);
+                buf
+            }
+            Some(Cow::Borrowed(buf)) => {
+                let mut buf = OsString::from(buf);
+                buf.extend(iterator);
+                buf
+            }
+        }
+    }
+}
diff --git a/library/std/src/io/buffered/bufreader.rs b/library/std/src/io/buffered/bufreader.rs
index 987371f50ec..02b0fc0c57d 100644
--- a/library/std/src/io/buffered/bufreader.rs
+++ b/library/std/src/io/buffered/bufreader.rs
@@ -1,6 +1,8 @@
 use crate::cmp;
 use crate::fmt;
-use crate::io::{self, BufRead, Initializer, IoSliceMut, Read, Seek, SeekFrom, DEFAULT_BUF_SIZE};
+use crate::io::{
+    self, BufRead, Initializer, IoSliceMut, Read, Seek, SeekFrom, SizeHint, DEFAULT_BUF_SIZE,
+};
 
 /// The `BufReader<R>` struct adds buffering to any reader.
 ///
@@ -90,10 +92,9 @@ impl<R: Read> BufReader<R> {
     #[stable(feature = "rust1", since = "1.0.0")]
     pub fn with_capacity(capacity: usize, inner: R) -> BufReader<R> {
         unsafe {
-            let mut buffer = Vec::with_capacity(capacity);
-            buffer.set_len(capacity);
-            inner.initializer().initialize(&mut buffer);
-            BufReader { inner, buf: buffer.into_boxed_slice(), pos: 0, cap: 0 }
+            let mut buf = Box::new_uninit_slice(capacity).assume_init();
+            inner.initializer().initialize(&mut buf);
+            BufReader { inner, buf, pos: 0, cap: 0 }
         }
     }
 }
@@ -435,3 +436,9 @@ impl<R: Seek> Seek for BufReader<R> {
         })
     }
 }
+
+impl<T> SizeHint for BufReader<T> {
+    fn lower_bound(&self) -> usize {
+        self.buffer().len()
+    }
+}
diff --git a/library/std/src/io/impls.rs b/library/std/src/io/impls.rs
index 00bf8b9af73..9870cfc4c95 100644
--- a/library/std/src/io/impls.rs
+++ b/library/std/src/io/impls.rs
@@ -1,6 +1,7 @@
 #[cfg(test)]
 mod tests;
 
+use crate::alloc::Allocator;
 use crate::cmp;
 use crate::fmt;
 use crate::io::{
@@ -357,7 +358,7 @@ impl Write for &mut [u8] {
 /// Write is implemented for `Vec<u8>` by appending to the vector.
 /// The vector will grow as needed.
 #[stable(feature = "rust1", since = "1.0.0")]
-impl Write for Vec<u8> {
+impl<A: Allocator> Write for Vec<u8, A> {
     #[inline]
     fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
         self.extend_from_slice(buf);
diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs
index 22914987405..17002e3b860 100644
--- a/library/std/src/io/mod.rs
+++ b/library/std/src/io/mod.rs
@@ -2238,6 +2238,19 @@ impl<T: BufRead, U: BufRead> BufRead for Chain<T, U> {
     }
 }
 
+impl<T, U> SizeHint for Chain<T, U> {
+    fn lower_bound(&self) -> usize {
+        SizeHint::lower_bound(&self.first) + SizeHint::lower_bound(&self.second)
+    }
+
+    fn upper_bound(&self) -> Option<usize> {
+        match (SizeHint::upper_bound(&self.first), SizeHint::upper_bound(&self.second)) {
+            (Some(first), Some(second)) => Some(first + second),
+            _ => None,
+        }
+    }
+}
+
 /// Reader adaptor which limits the bytes read from an underlying reader.
 ///
 /// This struct is generally created by calling [`take`] on a reader.
@@ -2464,6 +2477,30 @@ impl<R: Read> Iterator for Bytes<R> {
             };
         }
     }
+
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        SizeHint::size_hint(&self.inner)
+    }
+}
+
+trait SizeHint {
+    fn lower_bound(&self) -> usize;
+
+    fn upper_bound(&self) -> Option<usize>;
+
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        (self.lower_bound(), self.upper_bound())
+    }
+}
+
+impl<T> SizeHint for T {
+    default fn lower_bound(&self) -> usize {
+        0
+    }
+
+    default fn upper_bound(&self) -> Option<usize> {
+        None
+    }
 }
 
 /// An iterator over the contents of an instance of `BufRead` split on a
diff --git a/library/std/src/io/tests.rs b/library/std/src/io/tests.rs
index f176c2f088c..a85dd0d9827 100644
--- a/library/std/src/io/tests.rs
+++ b/library/std/src/io/tests.rs
@@ -1,7 +1,7 @@
 use super::{repeat, Cursor, SeekFrom};
 use crate::cmp::{self, min};
 use crate::io::{self, IoSlice, IoSliceMut};
-use crate::io::{BufRead, Read, Seek, Write};
+use crate::io::{BufRead, BufReader, Read, Seek, Write};
 use crate::ops::Deref;
 
 #[test]
@@ -199,6 +199,53 @@ fn chain_bufread() {
 }
 
 #[test]
+fn bufreader_size_hint() {
+    let testdata = b"ABCDEFGHIJKL";
+    let mut buf_reader = BufReader::new(&testdata[..]);
+    assert_eq!(buf_reader.buffer().len(), 0);
+
+    let buffer_length = testdata.len();
+    buf_reader.fill_buf().unwrap();
+
+    // Check that size hint matches buffer contents
+    let mut buffered_bytes = buf_reader.bytes();
+    let (lower_bound, _upper_bound) = buffered_bytes.size_hint();
+    assert_eq!(lower_bound, buffer_length);
+
+    // Check that size hint matches buffer contents after advancing
+    buffered_bytes.next().unwrap().unwrap();
+    let (lower_bound, _upper_bound) = buffered_bytes.size_hint();
+    assert_eq!(lower_bound, buffer_length - 1);
+}
+
+#[test]
+fn empty_size_hint() {
+    let size_hint = io::empty().bytes().size_hint();
+    assert_eq!(size_hint, (0, Some(0)));
+}
+
+#[test]
+fn chain_empty_size_hint() {
+    let chain = io::empty().chain(io::empty());
+    let size_hint = chain.bytes().size_hint();
+    assert_eq!(size_hint, (0, Some(0)));
+}
+
+#[test]
+fn chain_size_hint() {
+    let testdata = b"ABCDEFGHIJKL";
+    let mut buf_reader_1 = BufReader::new(&testdata[..6]);
+    let mut buf_reader_2 = BufReader::new(&testdata[6..]);
+
+    buf_reader_1.fill_buf().unwrap();
+    buf_reader_2.fill_buf().unwrap();
+
+    let chain = buf_reader_1.chain(buf_reader_2);
+    let size_hint = chain.bytes().size_hint();
+    assert_eq!(size_hint, (testdata.len(), None));
+}
+
+#[test]
 fn chain_zero_length_read_is_not_eof() {
     let a = b"A";
     let b = b"B";
diff --git a/library/std/src/io/util.rs b/library/std/src/io/util.rs
index e43ce4cdb4b..f472361f916 100644
--- a/library/std/src/io/util.rs
+++ b/library/std/src/io/util.rs
@@ -4,7 +4,9 @@
 mod tests;
 
 use crate::fmt;
-use crate::io::{self, BufRead, Initializer, IoSlice, IoSliceMut, Read, Seek, SeekFrom, Write};
+use crate::io::{
+    self, BufRead, Initializer, IoSlice, IoSliceMut, Read, Seek, SeekFrom, SizeHint, Write,
+};
 
 /// A reader which is always at EOF.
 ///
@@ -80,6 +82,12 @@ impl fmt::Debug for Empty {
     }
 }
 
+impl SizeHint for Empty {
+    fn upper_bound(&self) -> Option<usize> {
+        Some(0)
+    }
+}
+
 /// A reader which yields one byte over and over and over and over and over and...
 ///
 /// This struct is generally created by calling [`repeat()`]. Please
diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs
index ba49dee38e6..8149858e103 100644
--- a/library/std/src/lib.rs
+++ b/library/std/src/lib.rs
@@ -228,11 +228,13 @@
 #![feature(arbitrary_self_types)]
 #![feature(array_error_internals)]
 #![feature(asm)]
+#![feature(assert_matches)]
 #![feature(associated_type_bounds)]
 #![feature(atomic_mut_ptr)]
 #![feature(box_syntax)]
 #![feature(c_variadic)]
 #![feature(cfg_accessible)]
+#![cfg_attr(not(bootstrap), feature(cfg_eval))]
 #![feature(cfg_target_has_atomic)]
 #![feature(cfg_target_thread_local)]
 #![feature(char_error_internals)]
@@ -281,6 +283,7 @@
 #![feature(linkage)]
 #![feature(llvm_asm)]
 #![feature(log_syntax)]
+#![feature(map_try_insert)]
 #![feature(maybe_uninit_extra)]
 #![feature(maybe_uninit_ref)]
 #![feature(maybe_uninit_slice)]
@@ -289,6 +292,7 @@
 #![feature(needs_panic_runtime)]
 #![feature(negative_impls)]
 #![feature(never_type)]
+#![feature(new_uninit)]
 #![feature(nll)]
 #![feature(nonnull_slice_from_raw_parts)]
 #![feature(once_cell)]
@@ -298,6 +302,7 @@
 #![feature(panic_internals)]
 #![feature(panic_unwind)]
 #![feature(pin_static_ref)]
+#![feature(prelude_2021)]
 #![feature(prelude_import)]
 #![feature(ptr_internals)]
 #![feature(raw)]
@@ -323,7 +328,7 @@
 #![feature(try_blocks)]
 #![feature(try_reserve)]
 #![feature(unboxed_closures)]
-#![feature(unsafe_block_in_unsafe_fn)]
+#![cfg_attr(bootstrap, feature(unsafe_block_in_unsafe_fn))]
 #![feature(unsafe_cell_raw_get)]
 #![feature(unwind_attributes)]
 #![feature(vec_into_raw_parts)]
@@ -391,7 +396,11 @@ pub use alloc_crate::vec;
 #[stable(feature = "rust1", since = "1.0.0")]
 pub use core::any;
 #[stable(feature = "simd_arch", since = "1.27.0")]
-#[doc(no_inline)]
+// The `no_inline`-attribute is required to make the documentation of all
+// targets available.
+// See https://github.com/rust-lang/rust/pull/57808#issuecomment-457390549 for
+// more information.
+#[doc(no_inline)] // Note (#82861): required for correct documentation
 pub use core::arch;
 #[stable(feature = "core_array", since = "1.36.0")]
 pub use core::array;
@@ -550,8 +559,8 @@ pub use std_detect::detect;
 #[stable(feature = "rust1", since = "1.0.0")]
 #[allow(deprecated, deprecated_in_future)]
 pub use core::{
-    assert_eq, assert_ne, debug_assert, debug_assert_eq, debug_assert_ne, matches, r#try, todo,
-    unimplemented, unreachable, write, writeln,
+    assert_eq, assert_matches, assert_ne, debug_assert, debug_assert_eq, debug_assert_matches,
+    debug_assert_ne, matches, r#try, todo, unimplemented, unreachable, write, writeln,
 };
 
 // Re-export built-in macros defined through libcore.
diff --git a/library/std/src/net/parser.rs b/library/std/src/net/parser.rs
index e8b89626fbd..7064ed3ed23 100644
--- a/library/std/src/net/parser.rs
+++ b/library/std/src/net/parser.rs
@@ -35,7 +35,7 @@ macro_rules! impl_helper {
 impl_helper! { u8 u16 u32 }
 
 struct Parser<'a> {
-    // parsing as ASCII, so can use byte array
+    // Parsing as ASCII, so can use byte array.
     state: &'a [u8],
 }
 
@@ -44,7 +44,7 @@ impl<'a> Parser<'a> {
         Parser { state: input.as_bytes() }
     }
 
-    /// Run a parser, and restore the pre-parse state if it fails
+    /// Run a parser, and restore the pre-parse state if it fails.
     fn read_atomically<T, F>(&mut self, inner: F) -> Option<T>
     where
         F: FnOnce(&mut Parser<'_>) -> Option<T>,
@@ -126,7 +126,7 @@ impl<'a> Parser<'a> {
         })
     }
 
-    /// Read an IPv4 address
+    /// Read an IPv4 address.
     fn read_ipv4_addr(&mut self) -> Option<Ipv4Addr> {
         self.read_atomically(|p| {
             let mut groups = [0; 4];
@@ -139,18 +139,18 @@ impl<'a> Parser<'a> {
         })
     }
 
-    /// Read an IPV6 Address
+    /// Read an IPv6 Address.
     fn read_ipv6_addr(&mut self) -> Option<Ipv6Addr> {
-        /// Read a chunk of an ipv6 address into `groups`. Returns the number
+        /// Read a chunk of an IPv6 address into `groups`. Returns the number
         /// of groups read, along with a bool indicating if an embedded
-        /// trailing ipv4 address was read. Specifically, read a series of
-        /// colon-separated ipv6 groups (0x0000 - 0xFFFF), with an optional
-        /// trailing embedded ipv4 address.
+        /// trailing IPv4 address was read. Specifically, read a series of
+        /// colon-separated IPv6 groups (0x0000 - 0xFFFF), with an optional
+        /// trailing embedded IPv4 address.
         fn read_groups(p: &mut Parser<'_>, groups: &mut [u16]) -> (usize, bool) {
             let limit = groups.len();
 
             for (i, slot) in groups.iter_mut().enumerate() {
-                // Try to read a trailing embedded ipv4 address. There must be
+                // Try to read a trailing embedded IPv4 address. There must be
                 // at least two groups left.
                 if i < limit - 1 {
                     let ipv4 = p.read_separator(':', i, |p| p.read_ipv4_addr());
@@ -188,8 +188,8 @@ impl<'a> Parser<'a> {
                 return None;
             }
 
-            // read `::` if previous code parsed less than 8 groups
-            // `::` indicates one or more groups of 16 bits of zeros
+            // Read `::` if previous code parsed less than 8 groups.
+            // `::` indicates one or more groups of 16 bits of zeros.
             p.read_given_char(':')?;
             p.read_given_char(':')?;
 
@@ -206,12 +206,12 @@ impl<'a> Parser<'a> {
         })
     }
 
-    /// Read an IP Address, either IPV4 or IPV6.
+    /// Read an IP Address, either IPv4 or IPv6.
     fn read_ip_addr(&mut self) -> Option<IpAddr> {
         self.read_ipv4_addr().map(IpAddr::V4).or_else(move || self.read_ipv6_addr().map(IpAddr::V6))
     }
 
-    /// Read a : followed by a port in base 10.
+    /// Read a `:` followed by a port in base 10.
     fn read_port(&mut self) -> Option<u16> {
         self.read_atomically(|p| {
             p.read_given_char(':')?;
@@ -219,7 +219,7 @@ impl<'a> Parser<'a> {
         })
     }
 
-    /// Read a % followed by a scope id in base 10.
+    /// Read a `%` followed by a scope ID in base 10.
     fn read_scope_id(&mut self) -> Option<u32> {
         self.read_atomically(|p| {
             p.read_given_char('%')?;
@@ -227,7 +227,7 @@ impl<'a> Parser<'a> {
         })
     }
 
-    /// Read an IPV4 address with a port
+    /// Read an IPv4 address with a port.
     fn read_socket_addr_v4(&mut self) -> Option<SocketAddrV4> {
         self.read_atomically(|p| {
             let ip = p.read_ipv4_addr()?;
@@ -236,7 +236,7 @@ impl<'a> Parser<'a> {
         })
     }
 
-    /// Read an IPV6 address with a port
+    /// Read an IPv6 address with a port.
     fn read_socket_addr_v6(&mut self) -> Option<SocketAddrV6> {
         self.read_atomically(|p| {
             p.read_given_char('[')?;
diff --git a/library/std/src/path.rs b/library/std/src/path.rs
index de3b57df44e..57c892f32b1 100644
--- a/library/std/src/path.rs
+++ b/library/std/src/path.rs
@@ -2472,6 +2472,36 @@ impl Path {
         fs::metadata(self).is_ok()
     }
 
+    /// Returns `Ok(true)` if the path points at an existing entity.
+    ///
+    /// This function will traverse symbolic links to query information about the
+    /// destination file. In case of broken symbolic links this will return `Ok(false)`.
+    ///
+    /// As opposed to the `exists()` method, this one doesn't silently ignore errors
+    /// unrelated to the path not existing. (E.g. it will return `Err(_)` in case of permission
+    /// denied on some of the parent directories.)
+    ///
+    /// # Examples
+    ///
+    /// ```no_run
+    /// #![feature(path_try_exists)]
+    ///
+    /// use std::path::Path;
+    /// assert!(!Path::new("does_not_exist.txt").try_exists().expect("Can't check existence of file does_not_exist.txt"));
+    /// assert!(Path::new("/root/secret_file.txt").try_exists().is_err());
+    /// ```
+    // FIXME: stabilization should modify documentation of `exists()` to recommend this method
+    // instead.
+    #[unstable(feature = "path_try_exists", issue = "83186")]
+    #[inline]
+    pub fn try_exists(&self) -> io::Result<bool> {
+        match fs::metadata(self) {
+            Ok(_) => Ok(true),
+            Err(error) if error.kind() == io::ErrorKind::NotFound => Ok(false),
+            Err(error) => Err(error),
+        }
+    }
+
     /// Returns `true` if the path exists on disk and is pointing at a regular file.
     ///
     /// This function will traverse symbolic links to query information about the
diff --git a/library/std/src/prelude/mod.rs b/library/std/src/prelude/mod.rs
index eb2095b8196..505b5f3013b 100644
--- a/library/std/src/prelude/mod.rs
+++ b/library/std/src/prelude/mod.rs
@@ -84,3 +84,37 @@
 #![stable(feature = "rust1", since = "1.0.0")]
 
 pub mod v1;
+
+/// The 2015 version of the prelude of The Rust Standard Library.
+///
+/// See the [module-level documentation](self) for more.
+#[unstable(feature = "prelude_2015", issue = "none")]
+pub mod rust_2015 {
+    #[unstable(feature = "prelude_2015", issue = "none")]
+    #[doc(no_inline)]
+    pub use super::v1::*;
+}
+
+/// The 2018 version of the prelude of The Rust Standard Library.
+///
+/// See the [module-level documentation](self) for more.
+#[unstable(feature = "prelude_2018", issue = "none")]
+pub mod rust_2018 {
+    #[unstable(feature = "prelude_2018", issue = "none")]
+    #[doc(no_inline)]
+    pub use super::v1::*;
+}
+
+/// The 2021 version of the prelude of The Rust Standard Library.
+///
+/// See the [module-level documentation](self) for more.
+#[unstable(feature = "prelude_2021", issue = "none")]
+pub mod rust_2021 {
+    #[unstable(feature = "prelude_2021", issue = "none")]
+    #[doc(no_inline)]
+    pub use super::v1::*;
+
+    #[unstable(feature = "prelude_2021", issue = "none")]
+    #[doc(no_inline)]
+    pub use core::prelude::rust_2021::*;
+}
diff --git a/library/std/src/prelude/v1.rs b/library/std/src/prelude/v1.rs
index ef9aec54a4c..c5b871edbf2 100644
--- a/library/std/src/prelude/v1.rs
+++ b/library/std/src/prelude/v1.rs
@@ -1,6 +1,6 @@
 //! The first version of the prelude of The Rust Standard Library.
 //!
-//! See the [module-level documentation](../index.html) for more.
+//! See the [module-level documentation](super) for more.
 
 #![stable(feature = "rust1", since = "1.0.0")]
 
@@ -48,7 +48,7 @@ pub use core::prelude::v1::{
 // FIXME: Attribute and internal derive macros are not documented because for them rustdoc generates
 // dead links which fail link checker testing.
 #[stable(feature = "builtin_macro_prelude", since = "1.38.0")]
-#[allow(deprecated)]
+#[allow(deprecated, deprecated_in_future)]
 #[doc(hidden)]
 pub use core::prelude::v1::{
     bench, global_allocator, test, test_case, RustcDecodable, RustcEncodable,
@@ -67,6 +67,15 @@ pub use core::prelude::v1::derive;
 #[doc(hidden)]
 pub use core::prelude::v1::cfg_accessible;
 
+#[cfg(not(bootstrap))]
+#[unstable(
+    feature = "cfg_eval",
+    issue = "82679",
+    reason = "`cfg_eval` is a recently implemented feature"
+)]
+#[doc(hidden)]
+pub use core::prelude::v1::cfg_eval;
+
 // The file so far is equivalent to src/libcore/prelude/v1.rs,
 // and below to src/liballoc/prelude.rs.
 // Those files are duplicated rather than using glob imports
diff --git a/library/std/src/process.rs b/library/std/src/process.rs
index 6480e654c55..f9cfd11e906 100644
--- a/library/std/src/process.rs
+++ b/library/std/src/process.rs
@@ -71,11 +71,15 @@
 //!     .spawn()
 //!     .expect("failed to execute child");
 //!
-//! {
-//!     // limited borrow of stdin
-//!     let stdin = child.stdin.as_mut().expect("failed to get stdin");
+//! // If the child process fills its stdout buffer, it may end up
+//! // waiting until the parent reads the stdout, and not be able to
+//! // read stdin in the meantime, causing a deadlock.
+//! // Writing from another thread ensures that stdout is being read
+//! // at the same time, avoiding the problem.
+//! let mut stdin = child.stdin.take().expect("failed to get stdin");
+//! std::thread::spawn(move || {
 //!     stdin.write_all(b"test").expect("failed to write to stdin");
-//! }
+//! });
 //!
 //! let output = child
 //!     .wait_with_output()
@@ -885,7 +889,7 @@ impl Command {
     }
 
     /// Executes a command as a child process, waiting for it to finish and
-    /// collecting its exit status.
+    /// collecting its status.
     ///
     /// By default, stdin, stdout and stderr are inherited from the parent.
     ///
@@ -899,7 +903,7 @@ impl Command {
     ///                      .status()
     ///                      .expect("failed to execute process");
     ///
-    /// println!("process exited with: {}", status);
+    /// println!("process finished with: {}", status);
     ///
     /// assert!(status.success());
     /// ```
@@ -1145,14 +1149,21 @@ impl Stdio {
     ///     .spawn()
     ///     .expect("Failed to spawn child process");
     ///
-    /// {
-    ///     let stdin = child.stdin.as_mut().expect("Failed to open stdin");
+    /// let mut stdin = child.stdin.take().expect("Failed to open stdin");
+    /// std::thread::spawn(move || {
     ///     stdin.write_all("Hello, world!".as_bytes()).expect("Failed to write to stdin");
-    /// }
+    /// });
     ///
     /// let output = child.wait_with_output().expect("Failed to read stdout");
     /// assert_eq!(String::from_utf8_lossy(&output.stdout), "!dlrow ,olleH");
     /// ```
+    ///
+    /// Writing more than a pipe buffer's worth of input to stdin without also reading
+    /// stdout and stderr at the same time may cause a deadlock.
+    /// This is an issue when running any program that doesn't guarantee that it reads
+    /// its entire stdin before writing more than a pipe buffer's worth of output.
+    /// The size of a pipe buffer varies on different targets.
+    ///
     #[stable(feature = "process", since = "1.0.0")]
     pub fn piped() -> Stdio {
         Stdio(imp::Stdio::MakePipe)
@@ -1368,11 +1379,17 @@ impl From<fs::File> for Stdio {
 
 /// Describes the result of a process after it has terminated.
 ///
-/// This `struct` is used to represent the exit status of a child process.
+/// This `struct` is used to represent the exit status or other termination of a child process.
 /// Child processes are created via the [`Command`] struct and their exit
 /// status is exposed through the [`status`] method, or the [`wait`] method
 /// of a [`Child`] process.
 ///
+/// An `ExitStatus` represents every possible disposition of a process.  On Unix this
+/// is the **wait status**.  It is *not* simply an *exit status* (a value passed to `exit`).
+///
+/// For proper error reporting of failed processes, print the value of `ExitStatus` using its
+/// implementation of [`Display`](crate::fmt::Display).
+///
 /// [`status`]: Command::status
 /// [`wait`]: Child::wait
 #[derive(PartialEq, Eq, Clone, Copy, Debug)]
@@ -1400,7 +1417,7 @@ impl ExitStatus {
     /// if status.success() {
     ///     println!("'projects/' directory created");
     /// } else {
-    ///     println!("failed to create 'projects/' directory");
+    ///     println!("failed to create 'projects/' directory: {}", status);
     /// }
     /// ```
     #[stable(feature = "process", since = "1.0.0")]
@@ -1410,9 +1427,14 @@ impl ExitStatus {
 
     /// Returns the exit code of the process, if any.
     ///
-    /// On Unix, this will return `None` if the process was terminated
-    /// by a signal; `std::os::unix` provides an extension trait for
-    /// extracting the signal and other details from the `ExitStatus`.
+    /// In Unix terms the return value is the **exit status**: the value passed to `exit`, if the
+    /// process finished by calling `exit`.  Note that on Unix the exit status is truncated to 8
+    /// bits, and that values that didn't come from a program's call to `exit` may be invented the
+    /// runtime system (often, for example, 255, 254, 127 or 126).
+    ///
+    /// On Unix, this will return `None` if the process was terminated by a signal.
+    /// [`ExitStatusExt`](crate::os::unix::process::ExitStatusExt) is an
+    /// extension trait for extracting any such signal, and other details, from the `ExitStatus`.
     ///
     /// # Examples
     ///
diff --git a/library/std/src/sys/unix/ext/process.rs b/library/std/src/sys/unix/ext/process.rs
index 88a27f27f66..1276edc6af6 100644
--- a/library/std/src/sys/unix/ext/process.rs
+++ b/library/std/src/sys/unix/ext/process.rs
@@ -62,9 +62,14 @@ pub trait CommandExt: Sealed {
     /// `fork`. This primarily means that any modifications made to memory on
     /// behalf of this closure will **not** be visible to the parent process.
     /// This is often a very constrained environment where normal operations
-    /// like `malloc` or acquiring a mutex are not guaranteed to work (due to
+    /// like `malloc`, accessing environment variables through [`std::env`]
+    /// or acquiring a mutex are not guaranteed to work (due to
     /// other threads perhaps still running when the `fork` was run).
     ///
+    /// For further details refer to the [POSIX fork() specification]
+    /// and the equivalent documentation for any targeted
+    /// platform, especially the requirements around *async-signal-safety*.
+    ///
     /// This also means that all resources such as file descriptors and
     /// memory-mapped regions got duplicated. It is your responsibility to make
     /// sure that the closure does not violate library invariants by making
@@ -73,6 +78,10 @@ pub trait CommandExt: Sealed {
     /// When this closure is run, aspects such as the stdio file descriptors and
     /// working directory have successfully been changed, so output to these
     /// locations may not appear where intended.
+    ///
+    /// [POSIX fork() specification]:
+    ///     https://pubs.opengroup.org/onlinepubs/9699919799/functions/fork.html
+    /// [`std::env`]: mod@crate::env
     #[stable(feature = "process_pre_exec", since = "1.34.0")]
     unsafe fn pre_exec<F>(&mut self, f: F) -> &mut process::Command
     where
@@ -188,12 +197,20 @@ impl CommandExt for process::Command {
 
 /// Unix-specific extensions to [`process::ExitStatus`].
 ///
+/// On Unix, `ExitStatus` **does not necessarily represent an exit status**, as passed to the
+/// `exit` system call or returned by [`ExitStatus::code()`](crate::process::ExitStatus::code).
+/// It represents **any wait status**, as returned by one of the `wait` family of system calls.
+///
+/// This is because a Unix wait status (a Rust `ExitStatus`) can represent a Unix exit status, but
+/// can also represent other kinds of process event.
+///
 /// This trait is sealed: it cannot be implemented outside the standard library.
 /// This is so that future additional methods are not breaking changes.
 #[stable(feature = "rust1", since = "1.0.0")]
 pub trait ExitStatusExt: Sealed {
-    /// Creates a new `ExitStatus` from the raw underlying `i32` return value of
-    /// a process.
+    /// Creates a new `ExitStatus` from the raw underlying integer status value from `wait`
+    ///
+    /// The value should be a **wait status, not an exit status**.
     #[stable(feature = "exit_status_from", since = "1.12.0")]
     fn from_raw(raw: i32) -> Self;
 
@@ -222,6 +239,8 @@ pub trait ExitStatusExt: Sealed {
     fn continued(&self) -> bool;
 
     /// Returns the underlying raw `wait` status.
+    ///
+    /// The returned integer is a **wait status, not an exit status**.
     #[unstable(feature = "unix_process_wait_more", issue = "80695")]
     fn into_raw(self) -> i32;
 }
diff --git a/library/std/src/sys/unix/kernel_copy.rs b/library/std/src/sys/unix/kernel_copy.rs
index 200dbf06ff8..9687576bb6a 100644
--- a/library/std/src/sys/unix/kernel_copy.rs
+++ b/library/std/src/sys/unix/kernel_copy.rs
@@ -61,6 +61,7 @@ use crate::process::{ChildStderr, ChildStdin, ChildStdout};
 use crate::ptr;
 use crate::sync::atomic::{AtomicBool, AtomicU8, Ordering};
 use crate::sys::cvt;
+use libc::{EBADF, EINVAL, ENOSYS, EOPNOTSUPP, EOVERFLOW, EPERM, EXDEV};
 
 #[cfg(test)]
 mod tests;
@@ -535,7 +536,7 @@ pub(super) fn copy_regular_files(reader: RawFd, writer: RawFd, max_len: u64) ->
                 cvt(copy_file_range(INVALID_FD, ptr::null_mut(), INVALID_FD, ptr::null_mut(), 1, 0))
             };
 
-            if matches!(result.map_err(|e| e.raw_os_error()), Err(Some(libc::EBADF))) {
+            if matches!(result.map_err(|e| e.raw_os_error()), Err(Some(EBADF))) {
                 HAS_COPY_FILE_RANGE.store(AVAILABLE, Ordering::Relaxed);
             } else {
                 HAS_COPY_FILE_RANGE.store(UNAVAILABLE, Ordering::Relaxed);
@@ -573,19 +574,20 @@ pub(super) fn copy_regular_files(reader: RawFd, writer: RawFd, max_len: u64) ->
             Err(err) => {
                 return match err.raw_os_error() {
                     // when file offset + max_length > u64::MAX
-                    Some(libc::EOVERFLOW) => CopyResult::Fallback(written),
-                    Some(
-                        libc::ENOSYS | libc::EXDEV | libc::EINVAL | libc::EPERM | libc::EOPNOTSUPP,
-                    ) => {
+                    Some(EOVERFLOW) => CopyResult::Fallback(written),
+                    Some(ENOSYS | EXDEV | EINVAL | EPERM | EOPNOTSUPP | EBADF) => {
                         // Try fallback io::copy if either:
                         // - Kernel version is < 4.5 (ENOSYS¹)
                         // - Files are mounted on different fs (EXDEV)
                         // - copy_file_range is broken in various ways on RHEL/CentOS 7 (EOPNOTSUPP)
                         // - copy_file_range file is immutable or syscall is blocked by seccomp¹ (EPERM)
                         // - copy_file_range cannot be used with pipes or device nodes (EINVAL)
+                        // - the writer fd was opened with O_APPEND (EBADF²)
                         //
                         // ¹ these cases should be detected by the initial probe but we handle them here
                         //   anyway in case syscall interception changes during runtime
+                        // ² actually invalid file descriptors would cause this too, but in that case
+                        //   the fallback code path is expected to encounter the same error again
                         assert_eq!(written, 0);
                         CopyResult::Fallback(0)
                     }
@@ -649,7 +651,7 @@ fn sendfile_splice(mode: SpliceMode, reader: RawFd, writer: RawFd, len: u64) ->
             Ok(ret) => written += ret as u64,
             Err(err) => {
                 return match err.raw_os_error() {
-                    Some(libc::ENOSYS | libc::EPERM) => {
+                    Some(ENOSYS | EPERM) => {
                         // syscall not supported (ENOSYS)
                         // syscall is disallowed, e.g. by seccomp (EPERM)
                         match mode {
@@ -659,12 +661,12 @@ fn sendfile_splice(mode: SpliceMode, reader: RawFd, writer: RawFd, len: u64) ->
                         assert_eq!(written, 0);
                         CopyResult::Fallback(0)
                     }
-                    Some(libc::EINVAL) => {
+                    Some(EINVAL) => {
                         // splice/sendfile do not support this particular file descriptor (EINVAL)
                         assert_eq!(written, 0);
                         CopyResult::Fallback(0)
                     }
-                    Some(os_err) if mode == SpliceMode::Sendfile && os_err == libc::EOVERFLOW => {
+                    Some(os_err) if mode == SpliceMode::Sendfile && os_err == EOVERFLOW => {
                         CopyResult::Fallback(written)
                     }
                     _ => CopyResult::Error(err, written),
diff --git a/library/std/src/sys/unix/kernel_copy/tests.rs b/library/std/src/sys/unix/kernel_copy/tests.rs
index 77369cdd35f..3fe849e23e2 100644
--- a/library/std/src/sys/unix/kernel_copy/tests.rs
+++ b/library/std/src/sys/unix/kernel_copy/tests.rs
@@ -65,6 +65,24 @@ fn copy_specialization() -> Result<()> {
     result.and(rm1).and(rm2)
 }
 
+#[test]
+fn copies_append_mode_sink() -> Result<()> {
+    let tmp_path = tmpdir();
+    let source_path = tmp_path.join("copies_append_mode.source");
+    let sink_path = tmp_path.join("copies_append_mode.sink");
+    let mut source =
+        OpenOptions::new().create(true).truncate(true).write(true).read(true).open(&source_path)?;
+    write!(source, "not empty")?;
+    source.seek(SeekFrom::Start(0))?;
+    let mut sink = OpenOptions::new().create(true).append(true).open(&sink_path)?;
+
+    let copied = crate::io::copy(&mut source, &mut sink)?;
+
+    assert_eq!(copied, 9);
+
+    Ok(())
+}
+
 #[bench]
 fn bench_file_to_file_copy(b: &mut test::Bencher) {
     const BYTES: usize = 128 * 1024;
diff --git a/library/std/src/sys/unix/process/process_unix.rs b/library/std/src/sys/unix/process/process_unix.rs
index 9e82df7755e..47aaca82af9 100644
--- a/library/std/src/sys/unix/process/process_unix.rs
+++ b/library/std/src/sys/unix/process/process_unix.rs
@@ -1,6 +1,7 @@
 use crate::convert::TryInto;
 use crate::fmt;
 use crate::io::{self, Error, ErrorKind};
+use crate::mem;
 use crate::ptr;
 use crate::sys;
 use crate::sys::cvt;
@@ -45,15 +46,14 @@ impl Command {
         //
         // Note that as soon as we're done with the fork there's no need to hold
         // a lock any more because the parent won't do anything and the child is
-        // in its own process.
-        let result = unsafe {
-            let _env_lock = sys::os::env_read_lock();
-            cvt(libc::fork())?
-        };
+        // in its own process. Thus the parent drops the lock guard while the child
+        // forgets it to avoid unlocking it on a new thread, which would be invalid.
+        let (env_lock, result) = unsafe { (sys::os::env_read_lock(), cvt(libc::fork())?) };
 
         let pid = unsafe {
             match result {
                 0 => {
+                    mem::forget(env_lock);
                     drop(input);
                     let Err(err) = self.do_exec(theirs, envp.as_ref());
                     let errno = err.raw_os_error().unwrap_or(libc::EINVAL) as u32;
@@ -74,7 +74,10 @@ impl Command {
                     rtassert!(output.write(&bytes).is_ok());
                     libc::_exit(1)
                 }
-                n => n,
+                n => {
+                    drop(env_lock);
+                    n
+                }
             }
         };
 
@@ -527,9 +530,22 @@ impl fmt::Display for ExitStatus {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         if let Some(code) = self.code() {
             write!(f, "exit code: {}", code)
+        } else if let Some(signal) = self.signal() {
+            if self.core_dumped() {
+                write!(f, "signal: {} (core dumped)", signal)
+            } else {
+                write!(f, "signal: {}", signal)
+            }
+        } else if let Some(signal) = self.stopped_signal() {
+            write!(f, "stopped (not terminated) by signal: {}", signal)
+        } else if self.continued() {
+            write!(f, "continued (WIFCONTINUED)")
         } else {
-            let signal = self.signal().unwrap();
-            write!(f, "signal: {}", signal)
+            write!(f, "unrecognised wait status: {} {:#x}", self.0, self.0)
         }
     }
 }
+
+#[cfg(test)]
+#[path = "process_unix/tests.rs"]
+mod tests;
diff --git a/library/std/src/sys/unix/process/process_unix/tests.rs b/library/std/src/sys/unix/process/process_unix/tests.rs
new file mode 100644
index 00000000000..5819d2c2a5a
--- /dev/null
+++ b/library/std/src/sys/unix/process/process_unix/tests.rs
@@ -0,0 +1,30 @@
+#[test]
+fn exitstatus_display_tests() {
+    // In practice this is the same on every Unix.
+    // If some weird platform turns out to be different, and this test fails, use #[cfg].
+    use crate::os::unix::process::ExitStatusExt;
+    use crate::process::ExitStatus;
+
+    let t = |v, s| assert_eq!(s, format!("{}", <ExitStatus as ExitStatusExt>::from_raw(v)));
+
+    t(0x0000f, "signal: 15");
+    t(0x0008b, "signal: 11 (core dumped)");
+    t(0x00000, "exit code: 0");
+    t(0x0ff00, "exit code: 255");
+
+    // On MacOS, 0x0137f is WIFCONTINUED, not WIFSTOPPED.  Probably *BSD is similar.
+    //   https://github.com/rust-lang/rust/pull/82749#issuecomment-790525956
+    // The purpose of this test is to test our string formatting, not our understanding of the wait
+    // status magic numbers.  So restrict these to Linux.
+    if cfg!(target_os = "linux") {
+        t(0x0137f, "stopped (not terminated) by signal: 19");
+        t(0x0ffff, "continued (WIFCONTINUED)");
+    }
+
+    // Testing "unrecognised wait status" is hard because the wait.h macros typically
+    // assume that the value came from wait and isn't mad.  With the glibc I have here
+    // this works:
+    if cfg!(all(target_os = "linux", target_env = "gnu")) {
+        t(0x000ff, "unrecognised wait status: 255 0xff");
+    }
+}
diff --git a/library/std/src/sys/unix/stack_overflow.rs b/library/std/src/sys/unix/stack_overflow.rs
index d7bba50c76a..2a487fff54a 100644
--- a/library/std/src/sys/unix/stack_overflow.rs
+++ b/library/std/src/sys/unix/stack_overflow.rs
@@ -39,6 +39,7 @@ impl Drop for Handler {
 ))]
 mod imp {
     use super::Handler;
+    use crate::io;
     use crate::mem;
     use crate::ptr;
 
@@ -149,11 +150,11 @@ mod imp {
             0,
         );
         if stackp == MAP_FAILED {
-            panic!("failed to allocate an alternative stack");
+            panic!("failed to allocate an alternative stack: {}", io::Error::last_os_error());
         }
         let guard_result = libc::mprotect(stackp, page_size(), PROT_NONE);
         if guard_result != 0 {
-            panic!("failed to set up alternative stack guard page");
+            panic!("failed to set up alternative stack guard page: {}", io::Error::last_os_error());
         }
         stackp.add(page_size())
     }
diff --git a/library/std/src/sys/unix/thread.rs b/library/std/src/sys/unix/thread.rs
index 40c96307514..01a12dcf5a2 100644
--- a/library/std/src/sys/unix/thread.rs
+++ b/library/std/src/sys/unix/thread.rs
@@ -231,6 +231,7 @@ pub mod guard {
     use libc::{mmap, mprotect};
     use libc::{MAP_ANON, MAP_FAILED, MAP_FIXED, MAP_PRIVATE, PROT_NONE, PROT_READ, PROT_WRITE};
 
+    use crate::io;
     use crate::ops::Range;
     use crate::sync::atomic::{AtomicUsize, Ordering};
     use crate::sys::os;
@@ -361,12 +362,12 @@ pub mod guard {
                 0,
             );
             if result != stackaddr || result == MAP_FAILED {
-                panic!("failed to allocate a guard page");
+                panic!("failed to allocate a guard page: {}", io::Error::last_os_error());
             }
 
             let result = mprotect(stackaddr, page_size, PROT_NONE);
             if result != 0 {
-                panic!("failed to protect the guard page");
+                panic!("failed to protect the guard page: {}", io::Error::last_os_error());
             }
 
             let guardaddr = stackaddr as usize;
diff --git a/library/std/src/sys/wasi/fs.rs b/library/std/src/sys/wasi/fs.rs
index bcf7da46b4b..63c22136273 100644
--- a/library/std/src/sys/wasi/fs.rs
+++ b/library/std/src/sys/wasi/fs.rs
@@ -650,13 +650,11 @@ fn open_parent(p: &Path) -> io::Result<(ManuallyDrop<WasiFd>, PathBuf)> {
                 );
                 return Err(io::Error::new(io::ErrorKind::Other, msg));
             }
-            let len = CStr::from_ptr(buf.as_ptr().cast()).to_bytes().len();
-            buf.set_len(len);
-            buf.shrink_to_fit();
+            let relative = CStr::from_ptr(relative_path).to_bytes().to_vec();
 
             return Ok((
                 ManuallyDrop::new(WasiFd::from_raw(fd as u32)),
-                PathBuf::from(OsString::from_vec(buf)),
+                PathBuf::from(OsString::from_vec(relative)),
             ));
         }
     }
diff --git a/library/std/src/sys_common/thread_local_dtor.rs b/library/std/src/sys_common/thread_local_dtor.rs
index 6f5ebf4a271..f9971fb6f21 100644
--- a/library/std/src/sys_common/thread_local_dtor.rs
+++ b/library/std/src/sys_common/thread_local_dtor.rs
@@ -1,6 +1,6 @@
 //! Thread-local destructor
 //!
-//! Besides thread-local "keys" (pointer-sized non-adressable thread-local store
+//! Besides thread-local "keys" (pointer-sized non-addressable thread-local store
 //! with an associated destructor), many platforms also provide thread-local
 //! destructors that are not associated with any particular data. These are
 //! often more efficient.