about summary refs log tree commit diff
path: root/library/std
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2021-03-10 01:25:43 +0000
committerbors <bors@rust-lang.org>2021-03-10 01:25:43 +0000
commit861872bc453bde79b83ff99d443d035225f10e87 (patch)
treefd570f707b6a801d7f1e7fd6b72fc983184daa04 /library/std
parent3a5d45f68cadc8fff4fbb557780f92b403b19c19 (diff)
parent9dc82face341121e3464a59665bc7c5b394cacc0 (diff)
downloadrust-861872bc453bde79b83ff99d443d035225f10e87.tar.gz
rust-861872bc453bde79b83ff99d443d035225f10e87.zip
Auto merge of #82953 - JohnTitor:rollup-8rtk5g2, r=JohnTitor
Rollup of 10 pull requests

Successful merges:

 - #77511 (Add StatementKind::CopyNonOverlapping)
 - #79208 (Stabilize `unsafe_op_in_unsafe_fn` lint)
 - #82411 (Fixes to ExitStatus and its docs)
 - #82733 (Add powerpc-unknown-openbsd target)
 - #82802 (Build rustdoc for run-make tests, not just run-make-fulldeps)
 - #82849 (Add Option::get_or_default)
 - #82908 (:arrow_up: rust-analyzer)
 - #82937 (Update README.md to use the correct cmake version number)
 - #82938 (Bump tracing-tree dependency)
 - #82942 (Don't hardcode the `v1` prelude in diagnostics, to allow for new preludes.)

Failed merges:

r? `@ghost`
`@rustbot` modify labels: rollup
Diffstat (limited to 'library/std')
-rw-r--r--library/std/src/lib.rs2
-rw-r--r--library/std/src/process.rs25
-rw-r--r--library/std/src/sys/unix/ext/process.rs14
-rw-r--r--library/std/src/sys/unix/process/process_unix.rs17
-rw-r--r--library/std/src/sys/unix/process/process_unix/tests.rs30
5 files changed, 76 insertions, 12 deletions
diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs
index cebe05c39cb..247d39743be 100644
--- a/library/std/src/lib.rs
+++ b/library/std/src/lib.rs
@@ -327,7 +327,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)]
diff --git a/library/std/src/process.rs b/library/std/src/process.rs
index 6480e654c55..15ac9e402c5 100644
--- a/library/std/src/process.rs
+++ b/library/std/src/process.rs
@@ -885,7 +885,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 +899,7 @@ impl Command {
     ///                      .status()
     ///                      .expect("failed to execute process");
     ///
-    /// println!("process exited with: {}", status);
+    /// println!("process finished with: {}", status);
     ///
     /// assert!(status.success());
     /// ```
@@ -1368,11 +1368,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 +1406,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 +1416,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..4e170a8bb1c 100644
--- a/library/std/src/sys/unix/ext/process.rs
+++ b/library/std/src/sys/unix/ext/process.rs
@@ -188,12 +188,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 +230,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/process/process_unix.rs b/library/std/src/sys/unix/process/process_unix.rs
index 2746f87468d..2fdbabae277 100644
--- a/library/std/src/sys/unix/process/process_unix.rs
+++ b/library/std/src/sys/unix/process/process_unix.rs
@@ -527,9 +527,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");
+    }
+}