Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Stabilize Termination and ExitCode #93840

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 68 additions & 24 deletions library/std/src/process.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1404,6 +1404,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 @@ -1636,8 +1645,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.
yaahc marked this conversation as resolved.
Show resolved Hide resolved
///
/// `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 @@ -1648,52 +1665,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 @@ -2031,26 +2074,27 @@ pub fn id() -> u32 {
/// The default implementations are returning `libc::EXIT_SUCCESS` to indicate
/// a successful execution. In case of a failure, `libc::EXIT_FAILURE` is returned.
#[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 @@ -2060,14 +2104,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 @@ -2076,15 +2120,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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
// run-pass
#![feature(process_exitcode_placeholder)]

use std::process::ExitCode;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
// run-pass
#![feature(termination_trait_lib)]

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