From b50c1bbb0e2b73aed1a3cfc8da5e1082ddb21040 Mon Sep 17 00:00:00 2001
From: Ian Jackson <ijackson@chiark.greenend.org.uk>
Date: Wed, 3 Mar 2021 11:36:07 +0000
Subject: [PATCH 1/7] windows: provide NonZeroDWORD

Signed-off-by: Ian Jackson <ijackson@chiark.greenend.org.uk>
---
 library/std/src/sys/windows/c.rs | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/library/std/src/sys/windows/c.rs b/library/std/src/sys/windows/c.rs
index 7ea6048e94a88..e91c489361ea6 100644
--- a/library/std/src/sys/windows/c.rs
+++ b/library/std/src/sys/windows/c.rs
@@ -4,6 +4,7 @@
 #![cfg_attr(test, allow(dead_code))]
 #![unstable(issue = "none", feature = "windows_c")]
 
+use crate::os::raw::NonZero_c_ulong;
 use crate::os::raw::{c_char, c_int, c_long, c_longlong, c_uint, c_ulong, c_ushort};
 use crate::ptr;
 
@@ -13,6 +14,7 @@ pub use self::EXCEPTION_DISPOSITION::*;
 pub use self::FILE_INFO_BY_HANDLE_CLASS::*;
 
 pub type DWORD = c_ulong;
+pub type NonZeroDWORD = NonZero_c_ulong;
 pub type HANDLE = LPVOID;
 pub type HINSTANCE = HANDLE;
 pub type HMODULE = HINSTANCE;

From e893089ea066ce2b339543ac8e59b4e0ca8c44d3 Mon Sep 17 00:00:00 2001
From: Ian Jackson <ijackson@chiark.greenend.org.uk>
Date: Wed, 3 Mar 2021 12:17:16 +0000
Subject: [PATCH 2/7] Provide ExitStatusError

Closes #73125

This is in pursuance of
  Issue #73127 Consider adding #[must_use] to std::process::ExitStatus

In
  MR #81452 Add #[must_use] to [...] process::ExitStatus
we concluded that the existing arrangements in are too awkward
so adding that #[must_use] is blocked on improving the ergonomics.

I wrote a mini-RFC-style discusion of the approach in
  https://github.com/rust-lang/rust/issues/73125#issuecomment-771092741

Signed-off-by: Ian Jackson <ijackson@chiark.greenend.org.uk>
---
 library/std/src/process.rs                    | 138 +++++++++++++++++-
 library/std/src/sys/unix/process/mod.rs       |   2 +-
 .../src/sys/unix/process/process_fuchsia.rs   |  26 +++-
 .../std/src/sys/unix/process/process_unix.rs  |  31 +++-
 library/std/src/sys/unsupported/process.rs    |  18 ++-
 library/std/src/sys/windows/process.rs        |  25 +++-
 6 files changed, 227 insertions(+), 13 deletions(-)

diff --git a/library/std/src/process.rs b/library/std/src/process.rs
index b45c620fd0b06..6020ecf685366 100644
--- a/library/std/src/process.rs
+++ b/library/std/src/process.rs
@@ -110,6 +110,7 @@ use crate::ffi::OsStr;
 use crate::fmt;
 use crate::fs;
 use crate::io::{self, Initializer, IoSlice, IoSliceMut};
+use crate::num::NonZeroI32;
 use crate::path::Path;
 use crate::str;
 use crate::sys::pipe::{read2, AnonPipe};
@@ -1387,8 +1388,8 @@ impl From<fs::File> for Stdio {
 /// 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).
+/// For proper error reporting of failed processes, print the value of `ExitStatus` or
+/// `ExitStatusError` using their implementations of [`Display`](crate::fmt::Display).
 ///
 /// [`status`]: Command::status
 /// [`wait`]: Child::wait
@@ -1401,6 +1402,29 @@ pub struct ExitStatus(imp::ExitStatus);
 impl crate::sealed::Sealed for ExitStatus {}
 
 impl ExitStatus {
+    /// Was termination successful?  Returns a `Result`.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(exit_status_error)]
+    /// # if cfg!(unix) {
+    /// use std::process::Command;
+    ///
+    /// let status = Command::new("ls")
+    ///                      .arg("/dev/nonexistent")
+    ///                      .status()
+    ///                      .expect("ls could not be executed");
+    ///
+    /// println!("ls: {}", status);
+    /// status.exit_ok().expect_err("/dev/nonexistent could be listed!");
+    /// # } // cfg!(unix)
+    /// ```
+    #[unstable(feature = "exit_status_error", issue = "84908")]
+    pub fn exit_ok(&self) -> Result<(), ExitStatusError> {
+        self.0.exit_ok().map_err(ExitStatusError)
+    }
+
     /// Was termination successful? Signal termination is not considered a
     /// success, and success is defined as a zero exit status.
     ///
@@ -1422,7 +1446,7 @@ impl ExitStatus {
     /// ```
     #[stable(feature = "process", since = "1.0.0")]
     pub fn success(&self) -> bool {
-        self.0.success()
+        self.0.exit_ok().is_ok()
     }
 
     /// Returns the exit code of the process, if any.
@@ -1476,6 +1500,114 @@ impl fmt::Display for ExitStatus {
     }
 }
 
+/// Describes the result of a process after it has failed
+///
+/// Produced by the [`.exit_ok`](ExitStatus::exit_ok) method on [`ExitStatus`].
+///
+/// # Examples
+///
+/// ```
+/// #![feature(exit_status_error)]
+/// # if cfg!(unix) {
+/// use std::process::{Command, ExitStatusError};
+///
+/// fn run(cmd: &str) -> Result<(),ExitStatusError> {
+///     Command::new(cmd).status().unwrap().exit_ok()?;
+///     Ok(())
+/// }
+///
+/// run("true").unwrap();
+/// run("false").unwrap_err();
+/// # } // cfg!(unix)
+/// ```
+#[derive(PartialEq, Eq, Clone, Copy, Debug)]
+#[unstable(feature = "exit_status_error", issue = "84908")]
+// The definition of imp::ExitStatusError should ideally be such that
+// Result<(), imp::ExitStatusError> has an identical representation to imp::ExitStatus.
+pub struct ExitStatusError(imp::ExitStatusError);
+
+#[unstable(feature = "exit_status_error", issue = "84908")]
+impl ExitStatusError {
+    /// Reports the exit code, if applicable, from an `ExitStatusError`.
+    ///
+    /// 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.  If you want to
+    /// handle such situations specially, consider using
+    /// [`ExitStatusExt`](crate::os::unix::process::ExitStatusExt) (possibly after getting the
+    /// general `ExitStatus` by using [`status()`](ExitStatusError::status).
+    ///
+    /// If the process finished by calling `exit` with a nonzero value, this will return
+    /// that exit status.
+    ///
+    /// If the error was something else, it will return `None`.
+    ///
+    /// If the process exited successfully (ie, by calling `exit(0)`), there is no
+    /// `ExitStatusError`.  So the return value from `ExitStatusError::code()` is always nonzero.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(exit_status_error)]
+    /// # #[cfg(unix)] {
+    /// use std::process::Command;
+    ///
+    /// let bad = Command::new("false").status().unwrap().exit_ok().unwrap_err();
+    /// assert_eq!(bad.code(), Some(1));
+    /// # } // #[cfg(unix)]
+    /// ```
+    pub fn code(&self) -> Option<i32> {
+        self.code_nonzero().map(Into::into)
+    }
+
+    /// Reports the exit code, if applicable, from an `ExitStatusError`, as a `NonZero`
+    ///
+    /// This is exaclty like [`code()`](Self::code), except that it returns a `NonZeroI32`.
+    ///
+    /// Plain `code`, returning a plain integer, is provided because is is often more convenient.
+    /// The returned value from `code()` is indeed also nonzero; use `code_nonzero()` when you want
+    /// a type-level guarantee of nonzeroness.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(exit_status_error)]
+    /// # if cfg!(unix) {
+    /// use std::convert::TryFrom;
+    /// use std::num::NonZeroI32;
+    /// use std::process::Command;
+    ///
+    /// let bad = Command::new("false").status().unwrap().exit_ok().unwrap_err();
+    /// assert_eq!(bad.code_nonzero().unwrap(), NonZeroI32::try_from(1).unwrap());
+    /// # } // cfg!(unix)
+    /// ```
+    pub fn code_nonzero(&self) -> Option<NonZeroI32> {
+        self.0.code()
+    }
+
+    /// Converts an `ExitStatusError` (back) to an `ExitStatus`.
+    pub fn into_status(&self) -> ExitStatus {
+        ExitStatus(self.0.into())
+    }
+}
+
+#[unstable(feature = "exit_status_error", issue = "84908")]
+impl Into<ExitStatus> for ExitStatusError {
+    fn into(self) -> ExitStatus {
+        ExitStatus(self.0.into())
+    }
+}
+
+#[unstable(feature = "exit_status_error", issue = "84908")]
+impl fmt::Display for ExitStatusError {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        self.into_status().fmt(f)
+    }
+}
+
 /// This type represents the status code a process can return to its
 /// parent under normal termination.
 ///
diff --git a/library/std/src/sys/unix/process/mod.rs b/library/std/src/sys/unix/process/mod.rs
index f67c70c01772f..b5a19ed54a2f2 100644
--- a/library/std/src/sys/unix/process/mod.rs
+++ b/library/std/src/sys/unix/process/mod.rs
@@ -1,5 +1,5 @@
 pub use self::process_common::{Command, CommandArgs, ExitCode, Stdio, StdioPipes};
-pub use self::process_inner::{ExitStatus, Process};
+pub use self::process_inner::{ExitStatus, ExitStatusError, Process};
 pub use crate::ffi::OsString as EnvKey;
 pub use crate::sys_common::process::CommandEnvs;
 
diff --git a/library/std/src/sys/unix/process/process_fuchsia.rs b/library/std/src/sys/unix/process/process_fuchsia.rs
index b19ad4ccdc777..507abb27871bf 100644
--- a/library/std/src/sys/unix/process/process_fuchsia.rs
+++ b/library/std/src/sys/unix/process/process_fuchsia.rs
@@ -1,7 +1,8 @@
-use crate::convert::TryInto;
+use crate::convert::{TryFrom, TryInto};
 use crate::fmt;
 use crate::io;
 use crate::mem;
+use crate::num::{NonZeroI32, NonZeroI64};
 use crate::ptr;
 
 use crate::sys::process::process_common::*;
@@ -236,8 +237,11 @@ impl Process {
 pub struct ExitStatus(i64);
 
 impl ExitStatus {
-    pub fn success(&self) -> bool {
-        self.code() == Some(0)
+    pub fn exit_ok(&self) -> Result<(), ExitStatusError> {
+        match NonZeroI64::try_from(self.0) {
+            /* was nonzero */ Ok(failure) => Err(ExitStatusError(failure)),
+            /* was zero, couldn't convert */ Err(_) => Ok(()),
+        }
     }
 
     pub fn code(&self) -> Option<i32> {
@@ -306,3 +310,19 @@ impl fmt::Display for ExitStatus {
         write!(f, "exit code: {}", self.0)
     }
 }
+
+#[derive(PartialEq, Eq, Clone, Copy, Debug)]
+pub struct ExitStatusError(NonZeroI64);
+
+impl Into<ExitStatus> for ExitStatusError {
+    fn into(self) -> ExitStatus {
+        ExitStatus(self.0.into())
+    }
+}
+
+impl ExitStatusError {
+    pub fn code(self) -> Option<NonZeroI32> {
+        // fixme: affected by the same bug as ExitStatus::code()
+        ExitStatus(self.0.into()).code().map(|st| st.try_into().unwrap())
+    }
+}
diff --git a/library/std/src/sys/unix/process/process_unix.rs b/library/std/src/sys/unix/process/process_unix.rs
index ed9044382a898..f711b4cc68950 100644
--- a/library/std/src/sys/unix/process/process_unix.rs
+++ b/library/std/src/sys/unix/process/process_unix.rs
@@ -1,7 +1,9 @@
-use crate::convert::TryInto;
+use crate::convert::{TryFrom, TryInto};
 use crate::fmt;
 use crate::io::{self, Error, ErrorKind};
 use crate::mem;
+use crate::num::NonZeroI32;
+use crate::os::raw::NonZero_c_int;
 use crate::ptr;
 use crate::sys;
 use crate::sys::cvt;
@@ -490,8 +492,16 @@ impl ExitStatus {
         libc::WIFEXITED(self.0)
     }
 
-    pub fn success(&self) -> bool {
-        self.code() == Some(0)
+    pub fn exit_ok(&self) -> Result<(), ExitStatusError> {
+        // This assumes that WIFEXITED(status) && WEXITSTATUS==0 corresponds to status==0.  This is
+        // true on all actual versios of Unix, is widely assumed, and is specified in SuS
+        // https://pubs.opengroup.org/onlinepubs/9699919799/functions/wait.html .  If it is not
+        // true for a platform pretending to be Unix, the tests (our doctests, and also
+        // procsss_unix/tests.rs) will spot it.  `ExitStatusError::code` assumes this too.
+        match NonZero_c_int::try_from(self.0) {
+            /* was nonzero */ Ok(failure) => Err(ExitStatusError(failure)),
+            /* was zero, couldn't convert */ Err(_) => Ok(()),
+        }
     }
 
     pub fn code(&self) -> Option<i32> {
@@ -546,6 +556,21 @@ impl fmt::Display for ExitStatus {
     }
 }
 
+#[derive(PartialEq, Eq, Clone, Copy, Debug)]
+pub struct ExitStatusError(NonZero_c_int);
+
+impl Into<ExitStatus> for ExitStatusError {
+    fn into(self) -> ExitStatus {
+        ExitStatus(self.0.into())
+    }
+}
+
+impl ExitStatusError {
+    pub fn code(self) -> Option<NonZeroI32> {
+        ExitStatus(self.0.into()).code().map(|st| st.try_into().unwrap())
+    }
+}
+
 #[cfg(test)]
 #[path = "process_unix/tests.rs"]
 mod tests;
diff --git a/library/std/src/sys/unsupported/process.rs b/library/std/src/sys/unsupported/process.rs
index 38ac0a1ddd5f9..7846e43cfb53e 100644
--- a/library/std/src/sys/unsupported/process.rs
+++ b/library/std/src/sys/unsupported/process.rs
@@ -2,6 +2,7 @@ use crate::ffi::OsStr;
 use crate::fmt;
 use crate::io;
 use crate::marker::PhantomData;
+use crate::num::NonZeroI32;
 use crate::path::Path;
 use crate::sys::fs::File;
 use crate::sys::pipe::AnonPipe;
@@ -97,7 +98,7 @@ impl fmt::Debug for Command {
 pub struct ExitStatus(!);
 
 impl ExitStatus {
-    pub fn success(&self) -> bool {
+    pub fn exit_ok(&self) -> Result<(), ExitStatusError> {
         self.0
     }
 
@@ -134,6 +135,21 @@ impl fmt::Display for ExitStatus {
     }
 }
 
+#[derive(PartialEq, Eq, Clone, Copy, Debug)]
+pub struct ExitStatusError(ExitStatus);
+
+impl Into<ExitStatus> for ExitStatusError {
+    fn into(self) -> ExitStatus {
+        self.0.0
+    }
+}
+
+impl ExitStatusError {
+    pub fn code(self) -> Option<NonZeroI32> {
+        self.0.0
+    }
+}
+
 #[derive(PartialEq, Eq, Clone, Copy, Debug)]
 pub struct ExitCode(bool);
 
diff --git a/library/std/src/sys/windows/process.rs b/library/std/src/sys/windows/process.rs
index a5799606142ec..81dbea4a06739 100644
--- a/library/std/src/sys/windows/process.rs
+++ b/library/std/src/sys/windows/process.rs
@@ -5,6 +5,7 @@ mod tests;
 
 use crate::borrow::Borrow;
 use crate::collections::BTreeMap;
+use crate::convert::{TryFrom, TryInto};
 use crate::env;
 use crate::env::split_paths;
 use crate::ffi::{OsStr, OsString};
@@ -12,10 +13,12 @@ use crate::fmt;
 use crate::fs;
 use crate::io::{self, Error, ErrorKind};
 use crate::mem;
+use crate::num::NonZeroI32;
 use crate::os::windows::ffi::OsStrExt;
 use crate::path::Path;
 use crate::ptr;
 use crate::sys::c;
+use crate::sys::c::NonZeroDWORD;
 use crate::sys::cvt;
 use crate::sys::fs::{File, OpenOptions};
 use crate::sys::handle::Handle;
@@ -376,8 +379,11 @@ impl Process {
 pub struct ExitStatus(c::DWORD);
 
 impl ExitStatus {
-    pub fn success(&self) -> bool {
-        self.0 == 0
+    pub fn exit_ok(&self) -> Result<(), ExitStatusError> {
+        match NonZeroDWORD::try_from(self.0) {
+            /* was nonzero */ Ok(failure) => Err(ExitStatusError(failure)),
+            /* was zero, couldn't convert */ Err(_) => Ok(()),
+        }
     }
     pub fn code(&self) -> Option<i32> {
         Some(self.0 as i32)
@@ -406,6 +412,21 @@ impl fmt::Display for ExitStatus {
     }
 }
 
+#[derive(PartialEq, Eq, Clone, Copy, Debug)]
+pub struct ExitStatusError(c::NonZeroDWORD);
+
+impl Into<ExitStatus> for ExitStatusError {
+    fn into(self) -> ExitStatus {
+        ExitStatus(self.0.into())
+    }
+}
+
+impl ExitStatusError {
+    pub fn code(self) -> Option<NonZeroI32> {
+        Some((u32::from(self.0) as i32).try_into().unwrap())
+    }
+}
+
 #[derive(PartialEq, Eq, Clone, Copy, Debug)]
 pub struct ExitCode(c::DWORD);
 

From 60a4d9612d5cd5dde600bbf7a3cb5431f55de670 Mon Sep 17 00:00:00 2001
From: Ian Jackson <ijackson@chiark.greenend.org.uk>
Date: Wed, 24 Feb 2021 14:52:16 +0000
Subject: [PATCH 3/7] unix: impl ExitStatusExt for ExitStatusError

It is unergnomic to have to say things like
   bad.into_status().signal()

Implementing `ExitStatusExt` for `ExitStatusError` fixes this.
Unfortunately it does mean making a previously-infallible method
capable of panicing, although of course the existing impl remains
infallible.

The alternative would be a whole new `ExitStatusErrorExt` trait.

`<ExitStatus as ExitStatusExt>::into_raw()` is not particularly
ergonomic to call because of the often-required type annotation.
See for example the code in the test case in
  library/std/src/sys/unix/process/process_unix/tests.rs

Perhaps we should provide equivalent free functions for `ExitStatus`
and `ExitStatusExt` in std::os::unix::process and maybe deprecate this
trait method.  But I think that is for the future.

Signed-off-by: Ian Jackson <ijackson@chiark.greenend.org.uk>
---
 library/std/src/os/unix/process.rs | 77 +++++++++++++++++++++++++++---
 library/std/src/process.rs         |  9 ++--
 2 files changed, 76 insertions(+), 10 deletions(-)

diff --git a/library/std/src/os/unix/process.rs b/library/std/src/os/unix/process.rs
index 355855bcd10e2..21da8ba15de01 100644
--- a/library/std/src/os/unix/process.rs
+++ b/library/std/src/os/unix/process.rs
@@ -195,28 +195,62 @@ impl CommandExt for process::Command {
     }
 }
 
-/// Unix-specific extensions to [`process::ExitStatus`].
+/// Unix-specific extensions to [`process::ExitStatus`] and
+/// [`ExitStatusError`](process::ExitStatusError).
 ///
-/// 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.
+/// On Unix, `ExitStatus` and `ExitStatusError` **do not necessarily represent an exit status**, as
+/// passed to the `exit` system call or returned by
+/// [`ExitStatus::code()`](crate::process::ExitStatus::code).  They represents **any wait status**
+/// (or any nonzero wait status, respectively), 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.
+/// 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 integer status value from `wait`
+    /// Creates a new `ExitStatus` or `ExitStatusError` from the raw underlying integer status
+    /// value from `wait`
     ///
     /// The value should be a **wait status, not an exit status**.
+    ///
+    /// # Panics
+    ///
+    /// Panics on an attempt to make an `ExitStatusError` from a wait status of `0`.
+    ///
+    /// Making an `ExitStatus` always succeds and never panics.
     #[stable(feature = "exit_status_from", since = "1.12.0")]
     fn from_raw(raw: i32) -> Self;
 
     /// If the process was terminated by a signal, returns that signal.
     ///
     /// In other words, if `WIFSIGNALED`, this returns `WTERMSIG`.
+    ///
+    /// # Examples
+    /// ```
+    /// #![feature(exit_status_error)]
+    /// use std::process::{Command, ExitStatusError};
+    /// use std::os::unix::process::ExitStatusExt;
+    ///
+    /// fn run(script: &str) -> Result<(), ExitStatusError> {
+    ///     Command::new("sh").args(&["-ec",script])
+    ///         .status().expect("failed to fork/exec sh")
+    ///         .exit_ok()
+    ///         .or_else(|bad| {
+    ///             if bad.signal() == Some(13) /*PIPE*/ {
+    ///                 Ok(())
+    ///             } else {
+    ///                 Err(bad)
+    ///             }
+    ///         })
+    /// }
+    ///
+    /// run("exit").unwrap();
+    /// run("kill -PIPE $$").unwrap();
+    /// run("exit 42").unwrap_err();
+    /// ```
     #[stable(feature = "rust1", since = "1.0.0")]
     fn signal(&self) -> Option<i32>;
 
@@ -272,6 +306,35 @@ impl ExitStatusExt for process::ExitStatus {
     }
 }
 
+#[unstable(feature = "exit_status_error", issue = "84908")]
+impl ExitStatusExt for process::ExitStatusError {
+    fn from_raw(raw: i32) -> Self {
+        process::ExitStatus::from_raw(raw)
+            .exit_ok()
+            .expect_err("<ExitStatusError as ExitStatusExt>::from_raw(0) but zero is not an error")
+    }
+
+    fn signal(&self) -> Option<i32> {
+        self.into_status().signal()
+    }
+
+    fn core_dumped(&self) -> bool {
+        self.into_status().core_dumped()
+    }
+
+    fn stopped_signal(&self) -> Option<i32> {
+        self.into_status().stopped_signal()
+    }
+
+    fn continued(&self) -> bool {
+        self.into_status().continued()
+    }
+
+    fn into_raw(self) -> i32 {
+        self.into_status().into_raw()
+    }
+}
+
 #[stable(feature = "process_extensions", since = "1.2.0")]
 impl FromRawFd for process::Stdio {
     #[inline]
diff --git a/library/std/src/process.rs b/library/std/src/process.rs
index 6020ecf685366..c55cc18b2cff5 100644
--- a/library/std/src/process.rs
+++ b/library/std/src/process.rs
@@ -1500,6 +1500,10 @@ impl fmt::Display for ExitStatus {
     }
 }
 
+/// Allows extension traits within `std`.
+#[unstable(feature = "sealed", issue = "none")]
+impl crate::sealed::Sealed for ExitStatusError {}
+
 /// Describes the result of a process after it has failed
 ///
 /// Produced by the [`.exit_ok`](ExitStatus::exit_ok) method on [`ExitStatus`].
@@ -1536,9 +1540,8 @@ impl ExitStatusError {
     /// runtime system (often, for example, 255, 254, 127 or 126).
     ///
     /// On Unix, this will return `None` if the process was terminated by a signal.  If you want to
-    /// handle such situations specially, consider using
-    /// [`ExitStatusExt`](crate::os::unix::process::ExitStatusExt) (possibly after getting the
-    /// general `ExitStatus` by using [`status()`](ExitStatusError::status).
+    /// handle such situations specially, consider using methods from
+    /// [`ExitStatusExt`](crate::os::unix::process::ExitStatusExt).
     ///
     /// If the process finished by calling `exit` with a nonzero value, this will return
     /// that exit status.

From 8832b0a81cd24aa66111a60453375f7833303987 Mon Sep 17 00:00:00 2001
From: Ian Jackson <ijackson+github@slimy.greenend.org.uk>
Date: Tue, 16 Mar 2021 15:17:27 +0000
Subject: [PATCH 4/7] Fix typo in doc

Co-authored-by: Josh Triplett <josh@joshtriplett.org>
---
 library/std/src/process.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/library/std/src/process.rs b/library/std/src/process.rs
index c55cc18b2cff5..e9d807134003e 100644
--- a/library/std/src/process.rs
+++ b/library/std/src/process.rs
@@ -1536,7 +1536,7 @@ impl ExitStatusError {
     ///
     /// 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
+    /// bits, and that values that didn't come from a program's call to `exit` may be invented by 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.  If you want to

From bb4ef68639a0a9bae88c96d9b40207f8cdbbe1ee Mon Sep 17 00:00:00 2001
From: Ian Jackson <ijackson@chiark.greenend.org.uk>
Date: Tue, 4 May 2021 14:31:48 +0100
Subject: [PATCH 5/7] ExitStatusError: Be more verbose in Display impl

Co-authored-by: Jane Lusby <jlusby@yaah.dev>
---
 library/std/src/process.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/library/std/src/process.rs b/library/std/src/process.rs
index e9d807134003e..9252aba903f51 100644
--- a/library/std/src/process.rs
+++ b/library/std/src/process.rs
@@ -1607,7 +1607,7 @@ impl Into<ExitStatus> for ExitStatusError {
 #[unstable(feature = "exit_status_error", issue = "84908")]
 impl fmt::Display for ExitStatusError {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        self.into_status().fmt(f)
+        write!(f, "process exited unsuccessfully: {}", self.into_status())
     }
 }
 

From 46871539e47f04a9c255213bffa909d7556d1448 Mon Sep 17 00:00:00 2001
From: Ian Jackson <ijackson@chiark.greenend.org.uk>
Date: Wed, 5 May 2021 01:29:52 +0100
Subject: [PATCH 6/7] impl crate::error::Error for ExitStatusError

Signed-off-by: Ian Jackson <ijackson@chiark.greenend.org.uk>
---
 library/std/src/process.rs | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/library/std/src/process.rs b/library/std/src/process.rs
index 9252aba903f51..6903ba9056089 100644
--- a/library/std/src/process.rs
+++ b/library/std/src/process.rs
@@ -1611,6 +1611,9 @@ impl fmt::Display for ExitStatusError {
     }
 }
 
+#[unstable(feature = "exit_status_error", issue = "84908")]
+impl crate::error::Error for ExitStatusError {}
+
 /// This type represents the status code a process can return to its
 /// parent under normal termination.
 ///

From 26c782b8e7f939a00f889dfb3f1e969fc8f8c79d Mon Sep 17 00:00:00 2001
From: Ian Jackson <ijackson@chiark.greenend.org.uk>
Date: Tue, 11 May 2021 18:09:03 +0100
Subject: [PATCH 7/7] ExitStatusError: Remove mentions in stable docs

We should revert this commit when this is stabilised.

Signed-off-by: Ian Jackson <ijackson@chiark.greenend.org.uk>
---
 library/std/src/os/unix/process.rs | 30 +++---------------------------
 1 file changed, 3 insertions(+), 27 deletions(-)

diff --git a/library/std/src/os/unix/process.rs b/library/std/src/os/unix/process.rs
index 21da8ba15de01..8b98a22f768e1 100644
--- a/library/std/src/os/unix/process.rs
+++ b/library/std/src/os/unix/process.rs
@@ -198,10 +198,10 @@ impl CommandExt for process::Command {
 /// Unix-specific extensions to [`process::ExitStatus`] and
 /// [`ExitStatusError`](process::ExitStatusError).
 ///
-/// On Unix, `ExitStatus` and `ExitStatusError` **do not necessarily represent an exit status**, as
+/// 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).  They represents **any wait status**
-/// (or any nonzero wait status, respectively), as returned by one of the `wait` family of system
+/// [`ExitStatus::code()`](crate::process::ExitStatus::code).  It represents **any wait status**
+/// as returned by one of the `wait` family of system
 /// calls.
 ///
 /// A Unix wait status (a Rust `ExitStatus`) can represent a Unix exit status, but can also
@@ -227,30 +227,6 @@ pub trait ExitStatusExt: Sealed {
     /// If the process was terminated by a signal, returns that signal.
     ///
     /// In other words, if `WIFSIGNALED`, this returns `WTERMSIG`.
-    ///
-    /// # Examples
-    /// ```
-    /// #![feature(exit_status_error)]
-    /// use std::process::{Command, ExitStatusError};
-    /// use std::os::unix::process::ExitStatusExt;
-    ///
-    /// fn run(script: &str) -> Result<(), ExitStatusError> {
-    ///     Command::new("sh").args(&["-ec",script])
-    ///         .status().expect("failed to fork/exec sh")
-    ///         .exit_ok()
-    ///         .or_else(|bad| {
-    ///             if bad.signal() == Some(13) /*PIPE*/ {
-    ///                 Ok(())
-    ///             } else {
-    ///                 Err(bad)
-    ///             }
-    ///         })
-    /// }
-    ///
-    /// run("exit").unwrap();
-    /// run("kill -PIPE $$").unwrap();
-    /// run("exit 42").unwrap_err();
-    /// ```
     #[stable(feature = "rust1", since = "1.0.0")]
     fn signal(&self) -> Option<i32>;