Skip to content

Commit

Permalink
Rollup merge of rust-lang#93840 - yaahc:termination-stabilization-cel…
Browse files Browse the repository at this point in the history
…ebration-station, r=joshtriplett

Stabilize Termination and ExitCode

From rust-lang#43301

This PR stabilizes the Termination trait and associated ExitCode type. It also adjusts the ExitCode feature flag to replace the placeholder flag with a more permanent name, as well as splitting off the `to_i32` method behind its own permanently unstable feature flag.

This PR stabilizes the termination trait with the following signature:

```rust
pub trait Termination {
    fn report(self) -> ExitCode;
}
```

The existing impls of `Termination` are effectively already stable due to the prior stabilization of `?` in main.

This PR also stabilizes the following APIs on exit code

```rust
#[derive(Clone, Copy, Debug)]
pub struct ExitCode(_);

impl ExitCode {
    pub const SUCCESS: ExitCode;
    pub const FAILURE: ExitCode;
}

impl From<u8> for ExitCode { /* ... */ }
```

---

All of the previous blockers have been resolved. The main ones that were resolved recently are:

* The trait's name: We decided against changing this since none of the alternatives seemed particularly compelling. Instead we decided to end the bikeshedding and stick with the current name. ([link to the discussion](https://rust-lang.zulipchat.com/#narrow/stream/219381-t-libs/topic/Termination.2FExit.20Status.20Stabilization/near/269793887))
* Issues around platform specific representations: We resolved this issue by changing the return type of `report` from `i32` to the opaque type `ExitCode`. That way we can change the underlying representation without affecting the API, letting us offer full support for platform specific exit code APIs in the future.
* Custom exit codes: We resolved this by adding `From<u8> for ExitCode`. We choose to only support u8 initially because it is the least common denominator between the sets of exit codes supported by our current platforms. In the future we anticipate adding platform specific extension traits to ExitCode for constructors from larger or negative numbers, as needed.
  • Loading branch information
Dylan-DPC committed Mar 29, 2022
2 parents 5e1d19d + 97c58e8 commit bba2a64
Show file tree
Hide file tree
Showing 4 changed files with 69 additions and 28 deletions.
92 changes: 68 additions & 24 deletions library/std/src/process.rs
Expand Up @@ -1417,6 +1417,15 @@ impl From<fs::File> for Stdio {
/// For proper error reporting of failed processes, print the value of `ExitStatus` or
/// `ExitStatusError` using their implementations of [`Display`](crate::fmt::Display).
///
/// # Differences from `ExitStatus`
///
/// `ExitCode` is intended for terminating the currently running process, via
/// the `Termination` trait, in contrast to [`ExitStatus`], which represents the
/// termination of a child process. These APIs are separate due to platform
/// compatibility differences and their expected usage; it is not generally
/// possible to exactly reproduce an ExitStatus from a child for the current
/// process after the fact.
///
/// [`status`]: Command::status
/// [`wait`]: Child::wait
//
Expand Down Expand Up @@ -1649,8 +1658,16 @@ 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.
/// This type represents the status code the current process can return
/// to its parent under normal termination.
///
/// `ExitCode` is intended to be consumed only by the standard library (via
/// [`Termination::report()`]), and intentionally does not provide accessors like
/// `PartialEq`, `Eq`, or `Hash`. Instead the standard library provides the
/// canonical `SUCCESS` and `FAILURE` exit codes as well as `From<u8> for
/// ExitCode` for constructing other arbitrary exit codes.
///
/// # Portability
///
/// Numeric values used in this type don't have portable meanings, and
/// different platforms may mask different amounts of them.
Expand All @@ -1661,52 +1678,78 @@ impl crate::error::Error for ExitStatusError {}
/// [`SUCCESS`]: ExitCode::SUCCESS
/// [`FAILURE`]: ExitCode::FAILURE
///
/// **Warning**: While various forms of this were discussed in [RFC #1937],
/// it was ultimately cut from that RFC, and thus this type is more subject
/// to change even than the usual unstable item churn.
/// # Differences from `ExitStatus`
///
/// `ExitCode` is intended for terminating the currently running process, via
/// the `Termination` trait, in contrast to [`ExitStatus`], which represents the
/// termination of a child process. These APIs are separate due to platform
/// compatibility differences and their expected usage; it is not generally
/// possible to exactly reproduce an ExitStatus from a child for the current
/// process after the fact.
///
/// # Examples
///
/// `ExitCode` can be returned from the `main` function of a crate, as it implements
/// [`Termination`]:
///
/// ```
/// use std::process::ExitCode;
/// # fn check_foo() -> bool { true }
///
/// [RFC #1937]: https://github.com/rust-lang/rfcs/pull/1937
/// fn main() -> ExitCode {
/// if !check_foo() {
/// return ExitCode::from(42);
/// }
///
/// ExitCode::SUCCESS
/// }
/// ```
#[derive(Clone, Copy, Debug)]
#[unstable(feature = "process_exitcode_placeholder", issue = "48711")]
#[stable(feature = "process_exitcode", since = "1.60.0")]
pub struct ExitCode(imp::ExitCode);

#[unstable(feature = "process_exitcode_placeholder", issue = "48711")]
#[stable(feature = "process_exitcode", since = "1.60.0")]
impl ExitCode {
/// The canonical ExitCode for successful termination on this platform.
/// The canonical `ExitCode` for successful termination on this platform.
///
/// Note that a `()`-returning `main` implicitly results in a successful
/// termination, so there's no need to return this from `main` unless
/// you're also returning other possible codes.
#[unstable(feature = "process_exitcode_placeholder", issue = "48711")]
#[stable(feature = "process_exitcode", since = "1.60.0")]
pub const SUCCESS: ExitCode = ExitCode(imp::ExitCode::SUCCESS);

/// The canonical ExitCode for unsuccessful termination on this platform.
/// The canonical `ExitCode` for unsuccessful termination on this platform.
///
/// If you're only returning this and `SUCCESS` from `main`, consider
/// instead returning `Err(_)` and `Ok(())` respectively, which will
/// return the same codes (but will also `eprintln!` the error).
#[unstable(feature = "process_exitcode_placeholder", issue = "48711")]
#[stable(feature = "process_exitcode", since = "1.60.0")]
pub const FAILURE: ExitCode = ExitCode(imp::ExitCode::FAILURE);
}

impl ExitCode {
// This should not be stabilized when stabilizing ExitCode, we don't know that i32 will serve
// This is private/perma-unstable because ExitCode is opaque; we don't know that i32 will serve
// all usecases, for example windows seems to use u32, unix uses the 8-15th bits of an i32, we
// likely want to isolate users anything that could restrict the platform specific
// representation of an ExitCode
//
// More info: https://internals.rust-lang.org/t/mini-pre-rfc-redesigning-process-exitstatus/5426
/// Convert an ExitCode into an i32
#[unstable(feature = "process_exitcode_placeholder", issue = "48711")]
/// Convert an `ExitCode` into an i32
#[unstable(
feature = "process_exitcode_internals",
reason = "exposed only for libstd",
issue = "none"
)]
#[inline]
#[doc(hidden)]
pub fn to_i32(self) -> i32 {
self.0.as_i32()
}
}

#[unstable(feature = "process_exitcode_placeholder", issue = "48711")]
#[stable(feature = "process_exitcode", since = "1.60.0")]
impl From<u8> for ExitCode {
/// Construct an exit code from an arbitrary u8 value.
/// Construct an `ExitCode` from an arbitrary u8 value.
fn from(code: u8) -> Self {
ExitCode(imp::ExitCode::from(code))
}
Expand Down Expand Up @@ -2049,26 +2092,27 @@ pub fn id() -> u32 {
/// standard library's runtime for convenience. Other runtimes are not required
/// to provide similar functionality.
#[cfg_attr(not(test), lang = "termination")]
#[unstable(feature = "termination_trait_lib", issue = "43301")]
#[stable(feature = "termination_trait_lib", since = "1.60.0")]
#[rustc_on_unimplemented(
message = "`main` has invalid return type `{Self}`",
label = "`main` can only return types that implement `{Termination}`"
)]
pub trait Termination {
/// Is called to get the representation of the value as status code.
/// This status code is returned to the operating system.
#[stable(feature = "termination_trait_lib", since = "1.60.0")]
fn report(self) -> ExitCode;
}

#[unstable(feature = "termination_trait_lib", issue = "43301")]
#[stable(feature = "termination_trait_lib", since = "1.60.0")]
impl Termination for () {
#[inline]
fn report(self) -> ExitCode {
ExitCode::SUCCESS.report()
}
}

#[unstable(feature = "termination_trait_lib", issue = "43301")]
#[stable(feature = "termination_trait_lib", since = "1.60.0")]
impl<E: fmt::Debug> Termination for Result<(), E> {
fn report(self) -> ExitCode {
match self {
Expand All @@ -2078,14 +2122,14 @@ impl<E: fmt::Debug> Termination for Result<(), E> {
}
}

#[unstable(feature = "termination_trait_lib", issue = "43301")]
#[stable(feature = "termination_trait_lib", since = "1.60.0")]
impl Termination for ! {
fn report(self) -> ExitCode {
self
}
}

#[unstable(feature = "termination_trait_lib", issue = "43301")]
#[stable(feature = "termination_trait_lib", since = "1.60.0")]
impl<E: fmt::Debug> Termination for Result<!, E> {
fn report(self) -> ExitCode {
let Err(err) = self;
Expand All @@ -2094,15 +2138,15 @@ impl<E: fmt::Debug> Termination for Result<!, E> {
}
}

#[unstable(feature = "termination_trait_lib", issue = "43301")]
#[stable(feature = "termination_trait_lib", since = "1.60.0")]
impl<E: fmt::Debug> Termination for Result<Infallible, E> {
fn report(self) -> ExitCode {
let Err(err) = self;
Err::<!, _>(err).report()
}
}

#[unstable(feature = "termination_trait_lib", issue = "43301")]
#[stable(feature = "termination_trait_lib", since = "1.60.0")]
impl Termination for ExitCode {
#[inline]
fn report(self) -> ExitCode {
Expand Down
3 changes: 1 addition & 2 deletions library/test/src/lib.rs
Expand Up @@ -19,8 +19,7 @@
#![feature(bench_black_box)]
#![feature(internal_output_capture)]
#![feature(staged_api)]
#![feature(termination_trait_lib)]
#![feature(process_exitcode_placeholder)]
#![feature(process_exitcode_internals)]
#![feature(test)]
#![feature(total_cmp)]

Expand Down
@@ -1,5 +1,4 @@
// run-pass
#![feature(process_exitcode_placeholder)]

use std::process::ExitCode;

Expand Down
@@ -1,4 +1,3 @@
// run-pass
#![feature(termination_trait_lib)]

fn main() -> impl std::process::Termination { }

0 comments on commit bba2a64

Please sign in to comment.