Skip to content

Commit

Permalink
Auto merge of #95448 - Dylan-DPC:rollup-wpj5yto, r=Dylan-DPC
Browse files Browse the repository at this point in the history
Rollup of 4 pull requests

Successful merges:

 - #93840 (Stabilize Termination and ExitCode)
 - #95256 (Ensure io::Error's bitpacked repr doesn't accidentally impl UnwindSafe)
 - #95386 (Suggest wrapping patterns in enum variants)
 - #95437 (diagnostics: regression test for derive bounds)

Failed merges:

r? `@ghost`
`@rustbot` modify labels: rollup
  • Loading branch information
bors committed Mar 29, 2022
2 parents 5e1d19d + 2471502 commit 9c06e1b
Show file tree
Hide file tree
Showing 17 changed files with 388 additions and 39 deletions.
69 changes: 69 additions & 0 deletions compiler/rustc_infer/src/infer/error_reporting/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ use rustc_hir::def_id::DefId;
use rustc_hir::lang_items::LangItem;
use rustc_hir::{Item, ItemKind, Node};
use rustc_middle::dep_graph::DepContext;
use rustc_middle::ty::print::with_no_trimmed_paths;
use rustc_middle::ty::{
self,
error::TypeError,
Expand Down Expand Up @@ -1736,6 +1737,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
};

if should_suggest_fixes {
self.suggest_tuple_pattern(cause, &exp_found, diag);
self.suggest_as_ref_where_appropriate(span, &exp_found, diag);
self.suggest_accessing_field_where_appropriate(cause, &exp_found, diag);
self.suggest_await_on_expect_found(cause, span, &exp_found, diag);
Expand Down Expand Up @@ -1766,6 +1768,73 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
self.note_error_origin(diag, cause, exp_found, terr);
}

fn suggest_tuple_pattern(
&self,
cause: &ObligationCause<'tcx>,
exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
diag: &mut Diagnostic,
) {
// Heavily inspired by `FnCtxt::suggest_compatible_variants`, with
// some modifications due to that being in typeck and this being in infer.
if let ObligationCauseCode::Pattern { .. } = cause.code() {
if let ty::Adt(expected_adt, substs) = exp_found.expected.kind() {
let compatible_variants: Vec<_> = expected_adt
.variants()
.iter()
.filter(|variant| {
variant.fields.len() == 1 && variant.ctor_kind == hir::def::CtorKind::Fn
})
.filter_map(|variant| {
let sole_field = &variant.fields[0];
let sole_field_ty = sole_field.ty(self.tcx, substs);
if same_type_modulo_infer(sole_field_ty, exp_found.found) {
let variant_path =
with_no_trimmed_paths!(self.tcx.def_path_str(variant.def_id));
// FIXME #56861: DRYer prelude filtering
if let Some(path) = variant_path.strip_prefix("std::prelude::") {
if let Some((_, path)) = path.split_once("::") {
return Some(path.to_string());
}
}
Some(variant_path)
} else {
None
}
})
.collect();
match &compatible_variants[..] {
[] => {}
[variant] => {
diag.multipart_suggestion_verbose(
&format!("try wrapping the pattern in `{}`", variant),
vec![
(cause.span.shrink_to_lo(), format!("{}(", variant)),
(cause.span.shrink_to_hi(), ")".to_string()),
],
Applicability::MaybeIncorrect,
);
}
_ => {
// More than one matching variant.
diag.multipart_suggestions(
&format!(
"try wrapping the pattern in a variant of `{}`",
self.tcx.def_path_str(expected_adt.did())
),
compatible_variants.into_iter().map(|variant| {
vec![
(cause.span.shrink_to_lo(), format!("{}(", variant)),
(cause.span.shrink_to_hi(), ")".to_string()),
]
}),
Applicability::MaybeIncorrect,
);
}
}
}
}
}

pub fn get_impl_future_output_ty(&self, ty: Ty<'tcx>) -> Option<Binder<'tcx, Ty<'tcx>>> {
if let ty::Opaque(def_id, substs) = ty.kind() {
let future_trait = self.tcx.require_lang_item(LangItem::Future, None);
Expand Down
8 changes: 3 additions & 5 deletions compiler/rustc_typeck/src/check/demand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -268,10 +268,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
expr_ty: Ty<'tcx>,
) {
if let ty::Adt(expected_adt, substs) = expected.kind() {
if !expected_adt.is_enum() {
return;
}

// If the expression is of type () and it's the return expression of a block,
// we suggest adding a separate return expression instead.
// (To avoid things like suggesting `Ok(while .. { .. })`.)
Expand Down Expand Up @@ -336,7 +332,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let compatible_variants: Vec<String> = expected_adt
.variants()
.iter()
.filter(|variant| variant.fields.len() == 1)
.filter(|variant| {
variant.fields.len() == 1 && variant.ctor_kind == hir::def::CtorKind::Fn
})
.filter_map(|variant| {
let sole_field = &variant.fields[0];
let sole_field_ty = sole_field.ty(self.tcx, substs);
Expand Down
20 changes: 15 additions & 5 deletions library/std/src/io/error/repr_bitpacked.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@

use super::{Custom, ErrorData, ErrorKind, SimpleMessage};
use alloc::boxed::Box;
use core::marker::PhantomData;
use core::mem::{align_of, size_of};
use core::ptr::NonNull;

Expand All @@ -114,8 +115,17 @@ const TAG_CUSTOM: usize = 0b01;
const TAG_OS: usize = 0b10;
const TAG_SIMPLE: usize = 0b11;

/// The internal representation.
///
/// See the module docs for more, this is just a way to hack in a check that we
/// indeed are not unwind-safe.
///
/// ```compile_fail,E0277
/// fn is_unwind_safe<T: core::panic::UnwindSafe>() {}
/// is_unwind_safe::<std::io::Error>();
/// ```
#[repr(transparent)]
pub(super) struct Repr(NonNull<()>);
pub(super) struct Repr(NonNull<()>, PhantomData<ErrorData<Box<Custom>>>);

// All the types `Repr` stores internally are Send + Sync, and so is it.
unsafe impl Send for Repr {}
Expand Down Expand Up @@ -145,7 +155,7 @@ impl Repr {
// box, and `TAG_CUSTOM` just... isn't zero -- it's `0b01`). Therefore,
// `TAG_CUSTOM + p` isn't zero and so `tagged` can't be, and the
// `new_unchecked` is safe.
let res = Self(unsafe { NonNull::new_unchecked(tagged) });
let res = Self(unsafe { NonNull::new_unchecked(tagged) }, PhantomData);
// quickly smoke-check we encoded the right thing (This generally will
// only run in libstd's tests, unless the user uses -Zbuild-std)
debug_assert!(matches!(res.data(), ErrorData::Custom(_)), "repr(custom) encoding failed");
Expand All @@ -156,7 +166,7 @@ impl Repr {
pub(super) fn new_os(code: i32) -> Self {
let utagged = ((code as usize) << 32) | TAG_OS;
// Safety: `TAG_OS` is not zero, so the result of the `|` is not 0.
let res = Self(unsafe { NonNull::new_unchecked(utagged as *mut ()) });
let res = Self(unsafe { NonNull::new_unchecked(utagged as *mut ()) }, PhantomData);
// quickly smoke-check we encoded the right thing (This generally will
// only run in libstd's tests, unless the user uses -Zbuild-std)
debug_assert!(
Expand All @@ -170,7 +180,7 @@ impl Repr {
pub(super) fn new_simple(kind: ErrorKind) -> Self {
let utagged = ((kind as usize) << 32) | TAG_SIMPLE;
// Safety: `TAG_SIMPLE` is not zero, so the result of the `|` is not 0.
let res = Self(unsafe { NonNull::new_unchecked(utagged as *mut ()) });
let res = Self(unsafe { NonNull::new_unchecked(utagged as *mut ()) }, PhantomData);
// quickly smoke-check we encoded the right thing (This generally will
// only run in libstd's tests, unless the user uses -Zbuild-std)
debug_assert!(
Expand All @@ -184,7 +194,7 @@ impl Repr {
#[inline]
pub(super) const fn new_simple_message(m: &'static SimpleMessage) -> Self {
// Safety: References are never null.
Self(unsafe { NonNull::new_unchecked(m as *const _ as *mut ()) })
Self(unsafe { NonNull::new_unchecked(m as *const _ as *mut ()) }, PhantomData)
}

#[inline]
Expand Down
92 changes: 68 additions & 24 deletions library/std/src/process.rs
Original file line number Diff line number Diff line change
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
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
Loading

0 comments on commit 9c06e1b

Please sign in to comment.