Skip to content
Closed
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
13 changes: 4 additions & 9 deletions compiler/rustc_hir_typeck/src/_match.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::coercion::{AsCoercionSite, CoerceMany};
use crate::{Diverges, Expectation, FnCtxt, Needs};
use crate::{DivergeReason, Diverges, Expectation, FnCtxt, Needs};
use rustc_errors::{Applicability, MultiSpan};
use rustc_hir::{self as hir, ExprKind};
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
@@ -29,7 +29,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {

// If there are no arms, that is a diverging match; a special case.
if arms.is_empty() {
self.diverges.set(self.diverges.get() | Diverges::always(expr.span));
self.diverges.set(self.diverges.get() | Diverges::Always(DivergeReason::Other, expr));
return tcx.types.never;
}

@@ -204,13 +204,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// we can emit a better note. Rather than pointing
// at a diverging expression in an arbitrary arm,
// we can point at the entire `match` expression
if let (Diverges::Always { .. }, hir::MatchSource::Normal) = (all_arms_diverge, match_src) {
all_arms_diverge = Diverges::Always {
span: expr.span,
custom_note: Some(
"any code following this `match` expression is unreachable, as all arms diverge",
),
};
if let (Diverges::Always(..), hir::MatchSource::Normal) = (all_arms_diverge, match_src) {
all_arms_diverge = Diverges::Always(DivergeReason::AllArmsDiverge, expr);
}

// We won't diverge unless the scrutinee or all arms diverge.
64 changes: 31 additions & 33 deletions compiler/rustc_hir_typeck/src/diverges.rs
Original file line number Diff line number Diff line change
@@ -1,34 +1,21 @@
use rustc_span::source_map::DUMMY_SP;
use rustc_span::{self, Span};
use rustc_hir as hir;

use std::{cmp, ops};

/// Tracks whether executing a node may exit normally (versus
/// return/break/panic, which "diverge", leaving dead code in their
/// wake). Tracked semi-automatically (through type variables marked
/// as diverging), with some manual adjustments for control-flow
/// primitives (approximating a CFG).
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum Diverges {
#[derive(Copy, Clone, Debug)]
pub enum Diverges<'a> {
/// Potentially unknown, some cases converge,
/// others require a CFG to determine them.
Maybe,

/// Definitely known to diverge and therefore
/// not reach the next sibling or its parent.
Always {
/// The `Span` points to the expression
/// that caused us to diverge
/// (e.g. `return`, `break`, etc).
span: Span,
/// In some cases (e.g. a `match` expression
/// where all arms diverge), we may be
/// able to provide a more informative
/// message to the user.
/// If this is `None`, a default message
/// will be generated, which is suitable
/// for most cases.
custom_note: Option<&'static str>,
},
Always(DivergeReason, &'a hir::Expr<'a>),

/// Same as `Always` but with a reachability
/// warning already emitted.
@@ -37,42 +24,53 @@ pub enum Diverges {

// Convenience impls for combining `Diverges`.

impl ops::BitAnd for Diverges {
impl ops::BitAnd for Diverges<'_> {
type Output = Self;
fn bitand(self, other: Self) -> Self {
cmp::min(self, other)
cmp::min_by_key(self, other, Self::ordinal)
}
}

impl ops::BitOr for Diverges {
impl ops::BitOr for Diverges<'_> {
type Output = Self;
fn bitor(self, other: Self) -> Self {
cmp::max(self, other)
// argument order is to prefer `self` if ordinal is equal
cmp::max_by_key(other, self, Self::ordinal)
}
}

impl ops::BitAndAssign for Diverges {
impl ops::BitAndAssign for Diverges<'_> {
fn bitand_assign(&mut self, other: Self) {
*self = *self & other;
}
}

impl ops::BitOrAssign for Diverges {
impl ops::BitOrAssign for Diverges<'_> {
fn bitor_assign(&mut self, other: Self) {
*self = *self | other;
}
}

impl Diverges {
/// Creates a `Diverges::Always` with the provided `span` and the default note message.
pub(super) fn always(span: Span) -> Diverges {
Diverges::Always { span, custom_note: None }
impl Diverges<'_> {
pub(super) fn is_always(self) -> bool {
match self {
Self::Maybe => false,
Self::Always(..) | Self::WarnedAlways => true,
}
}

pub(super) fn is_always(self) -> bool {
// Enum comparison ignores the
// contents of fields, so we just
// fill them in with garbage here.
self >= Diverges::Always { span: DUMMY_SP, custom_note: None }
fn ordinal(&self) -> u8 {
match self {
Self::Maybe => 0,
Self::Always { .. } => 1,
Self::WarnedAlways => 2,
}
}
}

#[derive(Clone, Copy, Debug)]
pub enum DivergeReason {
AllArmsDiverge,
Uninhabited,
Other,
}
66 changes: 62 additions & 4 deletions compiler/rustc_hir_typeck/src/expr.rs
Original file line number Diff line number Diff line change
@@ -15,7 +15,7 @@ use crate::method::SelfSource;
use crate::type_error_struct;
use crate::Expectation::{self, ExpectCastableToType, ExpectHasType, NoExpectation};
use crate::{
report_unexpected_variant_res, BreakableCtxt, Diverges, FnCtxt, Needs,
report_unexpected_variant_res, BreakableCtxt, DivergeReason, Diverges, FnCtxt, Needs,
TupleArgumentsFlag::DontTupleArguments,
};
use rustc_ast as ast;
@@ -244,12 +244,32 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
ExprKind::MethodCall(segment, ..) => {
self.warn_if_unreachable(expr.hir_id, segment.ident.span, "call")
}
// allow field access when the struct and the field are both uninhabited
ExprKind::Field(..)
if matches!(
self.diverges.get(),
Diverges::Always(DivergeReason::Uninhabited, _)
) && self.tcx.is_ty_uninhabited_from(
self.parent_module,
self.freshen(ty),
self.param_env,
) => {}
_ => self.warn_if_unreachable(expr.hir_id, expr.span, "expression"),
}

// Any expression that produces a value of type `!` must have diverged
if ty.is_never() {
self.diverges.set(self.diverges.get() | Diverges::always(expr.span));
if !self.diverges.get().is_always() {
if ty.is_never() {
self.diverges.set(Diverges::Always(DivergeReason::Other, expr));
} else if expr_may_be_uninhabited(expr)
&& self.tcx.is_ty_uninhabited_from(
self.parent_module,
self.freshen(ty),
self.param_env,
)
{
self.diverges.set(Diverges::Always(DivergeReason::Uninhabited, expr));
}
}

// Record the type, which applies it effects.
@@ -1306,7 +1326,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
})
});
let mut coerce = CoerceMany::with_coercion_sites(coerce_to, args);
assert_eq!(self.diverges.get(), Diverges::Maybe);
assert!(matches!(self.diverges.get(), Diverges::Maybe));
for e in args {
let e_ty = self.check_expr_with_hint(e, coerce_to);
let cause = self.misc(e.span);
@@ -2886,3 +2906,41 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}
}

fn expr_may_be_uninhabited(expr: &hir::Expr<'_>) -> bool {
match expr.kind {
ExprKind::Call(..)
| ExprKind::MethodCall(..)
| ExprKind::Cast(..)
| ExprKind::Unary(hir::UnOp::Deref, _)
| ExprKind::Field(..)
| ExprKind::Path(..)
| ExprKind::Struct(..) => true,
ExprKind::Box(..)
| ExprKind::ConstBlock(..)
| ExprKind::Array(..)
| ExprKind::Tup(..)
| ExprKind::Binary(..)
| ExprKind::Unary(hir::UnOp::Neg | hir::UnOp::Not, _)
| ExprKind::Lit(..)
| ExprKind::Type(..)
| ExprKind::DropTemps(..)
| ExprKind::Let(..)
| ExprKind::If(..)
| ExprKind::Loop(..)
| ExprKind::Match(..)
| ExprKind::Closure(..)
| ExprKind::Block(..)
| ExprKind::Assign(..)
| ExprKind::AssignOp(..)
| ExprKind::Index(..)
| ExprKind::AddrOf(..)
| ExprKind::Break(..)
| ExprKind::Continue(..)
| ExprKind::Ret(..)
| ExprKind::InlineAsm(..)
| ExprKind::Repeat(..)
| ExprKind::Yield(..)
| ExprKind::Err => false,
}
}
66 changes: 39 additions & 27 deletions compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::callee::{self, DeferredCallResolution};
use crate::method::{self, MethodCallee, SelfSource};
use crate::rvalue_scopes;
use crate::{BreakableCtxt, Diverges, Expectation, FnCtxt, LocalTy};
use crate::{BreakableCtxt, DivergeReason, Diverges, Expectation, FnCtxt, LocalTy};
use rustc_data_structures::captures::Captures;
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::{Applicability, Diagnostic, ErrorGuaranteed, MultiSpan};
@@ -36,43 +36,55 @@ use rustc_trait_selection::traits::{
self, ObligationCause, ObligationCauseCode, TraitEngine, TraitEngineExt,
};

use std::borrow::Cow;
use std::collections::hash_map::Entry;
use std::slice;

impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
/// Produces warning on the given node, if the current point in the
/// function is unreachable, and there hasn't been another warning.
pub(in super::super) fn warn_if_unreachable(&self, id: hir::HirId, span: Span, kind: &str) {
// FIXME: Combine these two 'if' expressions into one once
// let chains are implemented
if let Diverges::Always { span: orig_span, custom_note } = self.diverges.get() {
// If span arose from a desugaring of `if` or `while`, then it is the condition itself,
// which diverges, that we are about to lint on. This gives suboptimal diagnostics.
// Instead, stop here so that the `if`- or `while`-expression's block is linted instead.
if !span.is_desugaring(DesugaringKind::CondTemporary)
&& !span.is_desugaring(DesugaringKind::Async)
&& !orig_span.is_desugaring(DesugaringKind::Await)
{
self.diverges.set(Diverges::WarnedAlways);
let Diverges::Always(reason, diverging_expr) = self.diverges.get() else {
return;
};
// If span arose from a desugaring of `if` or `while`, then it is the condition itself,
// which diverges, that we are about to lint on. This gives suboptimal diagnostics.
// Instead, stop here so that the `if`- or `while`-expression's block is linted instead.
if matches!(
span.desugaring_kind(),
Some(DesugaringKind::Async | DesugaringKind::Await | DesugaringKind::CondTemporary)
) {
return;
}

debug!("warn_if_unreachable: id={:?} span={:?} kind={}", id, span, kind);
self.diverges.set(Diverges::WarnedAlways);

let msg = format!("unreachable {}", kind);
self.tcx().struct_span_lint_hir(
lint::builtin::UNREACHABLE_CODE,
id,
span,
&msg,
|lint| {
lint.span_label(span, &msg).span_label(
orig_span,
custom_note
.unwrap_or("any code following this expression is unreachable"),
)
},
)
if matches!(reason, DivergeReason::Uninhabited) {
let def_id = self.tcx.hir().body_owner_def_id(hir::BodyId { hir_id: self.body_id });
if let Some(impl_of) = self.tcx.impl_of_method(def_id.to_def_id()) {
if self.tcx.has_attr(impl_of, sym::automatically_derived) {
// Built-in derives are generated before typeck,
// so they may contain unreachable code if there are uninhabited types
return;
}
}
}

debug!("warn_if_unreachable: id={:?} span={:?} kind={}", id, span, kind);

let msg = format!("unreachable {}", kind);
self.tcx().struct_span_lint_hir(lint::builtin::UNREACHABLE_CODE, id, span, &msg, |lint| {
let label = match reason {
DivergeReason::AllArmsDiverge =>
Cow::Borrowed("any code following this `match` expression is unreachable, as all arms diverge"),
DivergeReason::Uninhabited => format!(
"this expression has type `{}`, which is uninhabited",
self.typeck_results.borrow().node_type(diverging_expr.hir_id)
).into(),
DivergeReason::Other => Cow::Borrowed("any code following this expression is unreachable"),
};
lint.span_label(span, &msg).span_label(diverging_expr.span, label)
});
}

/// Resolves type and const variables in `ty` if possible. Unlike the infcx
5 changes: 4 additions & 1 deletion compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs
Original file line number Diff line number Diff line change
@@ -48,6 +48,8 @@ pub struct FnCtxt<'a, 'tcx> {
/// eventually).
pub(super) param_env: ty::ParamEnv<'tcx>,

pub(super) parent_module: DefId,

/// Number of errors that had been reported when we started
/// checking this function. On exit, if we find that *more* errors
/// have been reported, we will skip regionck and other work that
@@ -110,7 +112,7 @@ pub struct FnCtxt<'a, 'tcx> {
///
/// An expression represents dead code if, after checking it,
/// the diverges flag is set to something other than `Maybe`.
pub(super) diverges: Cell<Diverges>,
pub(super) diverges: Cell<Diverges<'tcx>>,

/// Whether any child nodes have any type errors.
pub(super) has_errors: Cell<bool>,
@@ -138,6 +140,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
FnCtxt {
body_id,
param_env,
parent_module: inh.tcx.parent_module(body_id).to_def_id(),
err_count_on_creation: inh.tcx.sess.err_count(),
ret_coercion: None,
in_tail_expr: false,
2 changes: 1 addition & 1 deletion compiler/rustc_hir_typeck/src/lib.rs
Original file line number Diff line number Diff line change
@@ -44,7 +44,7 @@ mod rvalue_scopes;
mod upvar;
mod writeback;

pub use diverges::Diverges;
pub use diverges::{DivergeReason, Diverges};
pub use expectation::Expectation;
pub use fn_ctxt::*;
pub use inherited::{Inherited, InheritedBuilder};
77 changes: 12 additions & 65 deletions compiler/rustc_passes/src/liveness.rs
Original file line number Diff line number Diff line change
@@ -94,7 +94,7 @@ use rustc_hir::intravisit::{self, Visitor};
use rustc_hir::{Expr, HirId, HirIdMap, HirIdSet};
use rustc_index::vec::IndexVec;
use rustc_middle::ty::query::Providers;
use rustc_middle::ty::{self, DefIdTree, RootVariableMinCaptureList, Ty, TyCtxt};
use rustc_middle::ty::{self, DefIdTree, RootVariableMinCaptureList, TyCtxt};
use rustc_session::lint;
use rustc_span::symbol::{kw, sym, Symbol};
use rustc_span::{BytePos, Span};
@@ -121,8 +121,8 @@ rustc_index::newtype_index! {
#[derive(Copy, Clone, PartialEq, Debug)]
enum LiveNodeKind {
UpvarNode(Span),
ExprNode(Span, HirId),
VarDefNode(Span, HirId),
ExprNode(Span),
VarDefNode(Span),
ClosureNode,
ExitNode,
}
@@ -131,8 +131,8 @@ fn live_node_kind_to_string(lnk: LiveNodeKind, tcx: TyCtxt<'_>) -> String {
let sm = tcx.sess.source_map();
match lnk {
UpvarNode(s) => format!("Upvar node [{}]", sm.span_to_diagnostic_string(s)),
ExprNode(s, _) => format!("Expr node [{}]", sm.span_to_diagnostic_string(s)),
VarDefNode(s, _) => format!("Var def node [{}]", sm.span_to_diagnostic_string(s)),
ExprNode(s) => format!("Expr node [{}]", sm.span_to_diagnostic_string(s)),
VarDefNode(s) => format!("Var def node [{}]", sm.span_to_diagnostic_string(s)),
ClosureNode => "Closure node".to_owned(),
ExitNode => "Exit node".to_owned(),
}
@@ -359,7 +359,7 @@ impl<'tcx> IrMaps<'tcx> {
let shorthand_field_ids = self.collect_shorthand_field_ids(pat);

pat.each_binding(|_, hir_id, _, ident| {
self.add_live_node_for_node(hir_id, VarDefNode(ident.span, hir_id));
self.add_live_node_for_node(hir_id, VarDefNode(ident.span));
self.add_variable(Local(LocalInfo {
id: hir_id,
name: ident.name,
@@ -373,7 +373,7 @@ impl<'tcx> Visitor<'tcx> for IrMaps<'tcx> {
fn visit_local(&mut self, local: &'tcx hir::Local<'tcx>) {
self.add_from_pat(&local.pat);
if local.els.is_some() {
self.add_live_node_for_node(local.hir_id, ExprNode(local.span, local.hir_id));
self.add_live_node_for_node(local.hir_id, ExprNode(local.span));
}
intravisit::walk_local(self, local);
}
@@ -408,14 +408,14 @@ impl<'tcx> Visitor<'tcx> for IrMaps<'tcx> {
hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) => {
debug!("expr {}: path that leads to {:?}", expr.hir_id, path.res);
if let Res::Local(_var_hir_id) = path.res {
self.add_live_node_for_node(expr.hir_id, ExprNode(expr.span, expr.hir_id));
self.add_live_node_for_node(expr.hir_id, ExprNode(expr.span));
}
intravisit::walk_expr(self, expr);
}
hir::ExprKind::Closure { .. } => {
// Interesting control flow (for loops can contain labeled
// breaks or continues)
self.add_live_node_for_node(expr.hir_id, ExprNode(expr.span, expr.hir_id));
self.add_live_node_for_node(expr.hir_id, ExprNode(expr.span));

// Make a live_node for each mentioned variable, with the span
// being the location that the variable is used. This results
@@ -444,11 +444,11 @@ impl<'tcx> Visitor<'tcx> for IrMaps<'tcx> {
| hir::ExprKind::Match(..)
| hir::ExprKind::Loop(..)
| hir::ExprKind::Yield(..) => {
self.add_live_node_for_node(expr.hir_id, ExprNode(expr.span, expr.hir_id));
self.add_live_node_for_node(expr.hir_id, ExprNode(expr.span));
intravisit::walk_expr(self, expr);
}
hir::ExprKind::Binary(op, ..) if op.node.is_lazy() => {
self.add_live_node_for_node(expr.hir_id, ExprNode(expr.span, expr.hir_id));
self.add_live_node_for_node(expr.hir_id, ExprNode(expr.span));
intravisit::walk_expr(self, expr);
}

@@ -1284,60 +1284,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
fn check_is_ty_uninhabited(&mut self, expr: &Expr<'_>, succ: LiveNode) -> LiveNode {
let ty = self.typeck_results.expr_ty(expr);
let m = self.ir.tcx.parent_module(expr.hir_id).to_def_id();
if self.ir.tcx.is_ty_uninhabited_from(m, ty, self.param_env) {
match self.ir.lnks[succ] {
LiveNodeKind::ExprNode(succ_span, succ_id) => {
self.warn_about_unreachable(expr.span, ty, succ_span, succ_id, "expression");
}
LiveNodeKind::VarDefNode(succ_span, succ_id) => {
self.warn_about_unreachable(expr.span, ty, succ_span, succ_id, "definition");
}
_ => {}
};
self.exit_ln
} else {
succ
}
}

fn warn_about_unreachable(
&mut self,
orig_span: Span,
orig_ty: Ty<'tcx>,
expr_span: Span,
expr_id: HirId,
descr: &str,
) {
if !orig_ty.is_never() {
// Unreachable code warnings are already emitted during type checking.
// However, during type checking, full type information is being
// calculated but not yet available, so the check for diverging
// expressions due to uninhabited result types is pretty crude and
// only checks whether ty.is_never(). Here, we have full type
// information available and can issue warnings for less obviously
// uninhabited types (e.g. empty enums). The check above is used so
// that we do not emit the same warning twice if the uninhabited type
// is indeed `!`.

let msg = format!("unreachable {}", descr);
self.ir.tcx.struct_span_lint_hir(
lint::builtin::UNREACHABLE_CODE,
expr_id,
expr_span,
&msg,
|diag| {
diag.span_label(expr_span, &msg)
.span_label(orig_span, "any code following this expression is unreachable")
.span_note(
orig_span,
&format!(
"this expression has type `{}`, which is uninhabited",
orig_ty
),
)
},
);
}
if self.ir.tcx.is_ty_uninhabited_from(m, ty, self.param_env) { self.exit_ln } else { succ }
}
}

1 change: 1 addition & 0 deletions compiler/rustc_target/src/asm/mod.rs
Original file line number Diff line number Diff line change
@@ -300,6 +300,7 @@ impl InlineAsmReg {
}
}

#[allow(unreachable_code)]
pub fn parse(arch: InlineAsmArch, name: Symbol) -> Result<Self, &'static str> {
// FIXME: use direct symbol comparison for register names
// Use `Symbol::as_str` instead of `Symbol::with` here because `has_feature` may access `Symbol`.
68 changes: 15 additions & 53 deletions src/test/mir-opt/const_prop/invalid_constant.main.ConstProp.diff
Original file line number Diff line number Diff line change
@@ -3,23 +3,24 @@

fn main() -> () {
let mut _0: (); // return place in scope 0 at $DIR/invalid_constant.rs:+0:11: +0:11
let _1: char; // in scope 0 at $DIR/invalid_constant.rs:+6:9: +6:22
let mut _2: main::InvalidChar; // in scope 0 at $DIR/invalid_constant.rs:+6:34: +6:63
let mut _4: E; // in scope 0 at $DIR/invalid_constant.rs:+13:25: +13:59
let mut _5: main::InvalidTag; // in scope 0 at $DIR/invalid_constant.rs:+13:34: +13:55
let mut _7: Empty; // in scope 0 at $DIR/invalid_constant.rs:+20:35: +20:73
let mut _8: main::NoVariants; // in scope 0 at $DIR/invalid_constant.rs:+20:44: +20:65
let mut _1: !; // in scope 0 at $DIR/invalid_constant.rs:+0:11: +27:2
let _2: char; // in scope 0 at $DIR/invalid_constant.rs:+6:9: +6:22
let mut _3: main::InvalidChar; // in scope 0 at $DIR/invalid_constant.rs:+6:34: +6:63
let mut _5: E; // in scope 0 at $DIR/invalid_constant.rs:+13:25: +13:59
let mut _6: main::InvalidTag; // in scope 0 at $DIR/invalid_constant.rs:+13:34: +13:55
let mut _8: Empty; // in scope 0 at $DIR/invalid_constant.rs:+20:35: +20:73
let mut _9: main::NoVariants; // in scope 0 at $DIR/invalid_constant.rs:+20:44: +20:65
scope 1 {
debug _invalid_char => _1; // in scope 1 at $DIR/invalid_constant.rs:+6:9: +6:22
let _3: [E; 1]; // in scope 1 at $DIR/invalid_constant.rs:+13:9: +13:21
debug _invalid_char => _2; // in scope 1 at $DIR/invalid_constant.rs:+6:9: +6:22
let _4: [E; 1]; // in scope 1 at $DIR/invalid_constant.rs:+13:9: +13:21
scope 3 {
debug _invalid_tag => _3; // in scope 3 at $DIR/invalid_constant.rs:+13:9: +13:21
let _6: [Empty; 1]; // in scope 3 at $DIR/invalid_constant.rs:+20:9: +20:31
debug _invalid_tag => _4; // in scope 3 at $DIR/invalid_constant.rs:+13:9: +13:21
let _7: [Empty; 1]; // in scope 3 at $DIR/invalid_constant.rs:+20:9: +20:31
scope 5 {
debug _enum_without_variants => _6; // in scope 5 at $DIR/invalid_constant.rs:+20:9: +20:31
let _9: main::Str<"���">; // in scope 5 at $DIR/invalid_constant.rs:+24:9: +24:22
debug _enum_without_variants => _7; // in scope 5 at $DIR/invalid_constant.rs:+20:9: +20:31
let _10: main::Str<"���">; // in scope 5 at $DIR/invalid_constant.rs:+24:9: +24:22
scope 7 {
debug _non_utf8_str => _9; // in scope 7 at $DIR/invalid_constant.rs:+24:9: +24:22
debug _non_utf8_str => _10; // in scope 7 at $DIR/invalid_constant.rs:+24:9: +24:22
}
}
scope 6 {
@@ -32,46 +33,7 @@
}

bb0: {
StorageLive(_1); // scope 0 at $DIR/invalid_constant.rs:+6:9: +6:22
StorageLive(_2); // scope 2 at $DIR/invalid_constant.rs:+6:34: +6:63
Deinit(_2); // scope 2 at $DIR/invalid_constant.rs:+6:34: +6:63
(_2.0: u32) = const 1114113_u32; // scope 2 at $DIR/invalid_constant.rs:+6:34: +6:63
- _1 = (_2.1: char); // scope 2 at $DIR/invalid_constant.rs:+6:34: +6:67
+ _1 = const {transmute(0x00110001): char}; // scope 2 at $DIR/invalid_constant.rs:+6:34: +6:67
StorageDead(_2); // scope 0 at $DIR/invalid_constant.rs:+6:69: +6:70
StorageLive(_3); // scope 1 at $DIR/invalid_constant.rs:+13:9: +13:21
StorageLive(_4); // scope 1 at $DIR/invalid_constant.rs:+13:25: +13:59
StorageLive(_5); // scope 4 at $DIR/invalid_constant.rs:+13:34: +13:55
Deinit(_5); // scope 4 at $DIR/invalid_constant.rs:+13:34: +13:55
(_5.0: u32) = const 4_u32; // scope 4 at $DIR/invalid_constant.rs:+13:34: +13:55
- _4 = (_5.1: E); // scope 4 at $DIR/invalid_constant.rs:+13:34: +13:57
- _3 = [move _4]; // scope 1 at $DIR/invalid_constant.rs:+13:24: +13:60
+ _4 = const Scalar(0x00000004): E; // scope 4 at $DIR/invalid_constant.rs:+13:34: +13:57
+ // mir::Constant
+ // + span: $DIR/invalid_constant.rs:28:34: 28:57
+ // + literal: Const { ty: E, val: Value(Scalar(0x00000004)) }
+ _3 = [const Scalar(0x00000004): E]; // scope 1 at $DIR/invalid_constant.rs:+13:24: +13:60
+ // mir::Constant
+ // + span: $DIR/invalid_constant.rs:28:24: 28:60
+ // + literal: Const { ty: E, val: Value(Scalar(0x00000004)) }
StorageDead(_4); // scope 1 at $DIR/invalid_constant.rs:+13:59: +13:60
StorageDead(_5); // scope 1 at $DIR/invalid_constant.rs:+13:60: +13:61
StorageLive(_6); // scope 3 at $DIR/invalid_constant.rs:+20:9: +20:31
StorageLive(_7); // scope 3 at $DIR/invalid_constant.rs:+20:35: +20:73
StorageLive(_8); // scope 6 at $DIR/invalid_constant.rs:+20:44: +20:65
Deinit(_8); // scope 6 at $DIR/invalid_constant.rs:+20:44: +20:65
(_8.0: u32) = const 0_u32; // scope 6 at $DIR/invalid_constant.rs:+20:44: +20:65
nop; // scope 6 at $DIR/invalid_constant.rs:+20:44: +20:71
nop; // scope 3 at $DIR/invalid_constant.rs:+20:34: +20:74
StorageDead(_7); // scope 3 at $DIR/invalid_constant.rs:+20:73: +20:74
StorageDead(_8); // scope 3 at $DIR/invalid_constant.rs:+20:74: +20:75
StorageLive(_9); // scope 5 at $DIR/invalid_constant.rs:+24:9: +24:22
nop; // scope 0 at $DIR/invalid_constant.rs:+0:11: +27:2
StorageDead(_9); // scope 5 at $DIR/invalid_constant.rs:+27:1: +27:2
StorageDead(_6); // scope 3 at $DIR/invalid_constant.rs:+27:1: +27:2
StorageDead(_3); // scope 1 at $DIR/invalid_constant.rs:+27:1: +27:2
StorageDead(_1); // scope 0 at $DIR/invalid_constant.rs:+27:1: +27:2
return; // scope 0 at $DIR/invalid_constant.rs:+27:2: +27:2
unreachable; // scope 0 at $DIR/invalid_constant.rs:+0:11: +27:2
}
}

Original file line number Diff line number Diff line change
@@ -11,8 +11,6 @@ fn process_void(_1: *const Void) -> () {
}

bb0: {
StorageLive(_2); // scope 0 at $DIR/uninhabited-enum.rs:+1:8: +1:14
StorageDead(_2); // scope 0 at $DIR/uninhabited-enum.rs:+4:1: +4:2
return; // scope 0 at $DIR/uninhabited-enum.rs:+4:2: +4:2
unreachable; // scope 0 at $DIR/uninhabited-enum.rs:+0:41: +4:2
}
}
4 changes: 2 additions & 2 deletions src/test/ui/break-diverging-value.rs
Original file line number Diff line number Diff line change
@@ -22,8 +22,8 @@ fn get_void() -> Void {
panic!()
}

fn loop_break_void() -> i32 { //~ ERROR mismatched types
let loop_value = loop { break get_void() };
fn loop_break_void() -> i32 {
let loop_value = loop { break get_void() }; // ok
}
Comment on lines -25 to 27
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cc @rust-lang/types a small bugfix makes some code pass that didn't pass before, because it didn't realize that get_void()'s result means the code is unreachable.


fn get_never() -> ! {
10 changes: 1 addition & 9 deletions src/test/ui/break-diverging-value.stderr
Original file line number Diff line number Diff line change
@@ -6,14 +6,6 @@ LL | fn loop_break_break() -> i32 {
| |
| implicitly returns `()` as its body has no tail or `return` expression

error[E0308]: mismatched types
--> $DIR/break-diverging-value.rs:25:25
|
LL | fn loop_break_void() -> i32 {
| --------------- ^^^ expected `i32`, found `()`
| |
| implicitly returns `()` as its body has no tail or `return` expression

error: aborting due to 2 previous errors
error: aborting due to previous error

For more information about this error, try `rustc --explain E0308`.
Original file line number Diff line number Diff line change
@@ -19,10 +19,10 @@ fn bar() -> Option<Empty> {

fn main() {
if let Some(x) = bar() {
Test1::B(x);
Test1::B(x); //~ unreachable call
}

if let Some(x) = bar() {
Test2::B(x);
Test2::B(x); //~ unreachable call
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
warning: unreachable call
--> $DIR/write-to-uninhabited-enum-variant.rs:22:9
|
LL | Test1::B(x);
| ^^^^^^^^ - this expression has type `Empty`, which is uninhabited
| |
| unreachable call
|
= note: `#[warn(unreachable_code)]` on by default

warning: unreachable call
--> $DIR/write-to-uninhabited-enum-variant.rs:26:9
|
LL | Test2::B(x);
| ^^^^^^^^ - this expression has type `Empty`, which is uninhabited
| |
| unreachable call

warning: 2 warnings emitted

7 changes: 6 additions & 1 deletion src/test/ui/consts/const_discriminant.rs
Original file line number Diff line number Diff line change
@@ -15,7 +15,12 @@ const TEST_A: Discriminant<Test> = discriminant(&Test::A(5));
const TEST_A_OTHER: Discriminant<Test> = discriminant(&Test::A(17));
const TEST_B: Discriminant<Test> = discriminant(&Test::B);

enum Void {}
mod private {
enum PrivateVoid {}
pub struct VoidS(PrivateVoid);
pub enum Void { X(VoidS) }
}
use private::Void;

enum SingleVariant {
V,
9 changes: 8 additions & 1 deletion src/test/ui/consts/issue-64506.rs
Original file line number Diff line number Diff line change
@@ -6,7 +6,14 @@ pub struct ChildStdin {
}

#[derive(Copy, Clone)]
enum AnonPipe {}
struct AnonPipe(private::Void);

mod private {
#[derive(Copy, Clone)]
pub struct Void(PrivateVoid);
#[derive(Copy, Clone)]
enum PrivateVoid {}
}

const FOO: () = {
union Foo {
47 changes: 32 additions & 15 deletions src/test/ui/deriving/deriving-all-codegen.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// check-pass
// compile-flags: -Zunpretty=expanded
// edition:2021
// revisions: check unpretty
// [unpretty] compile-flags: -Zunpretty=expanded
//
// This test checks the code generated for all[*] the builtin derivable traits
// on a variety of structs and enums. It protects against accidental changes to
@@ -14,34 +15,33 @@
// also require the `rustc_serialize` crate.

#![crate_type = "lib"]
#![allow(dead_code)]
#![allow(deprecated)]
#![warn(unused)] // test that lints are not triggerred in derived code

// Empty struct.
#[derive(Clone, Copy, Debug, Default, Hash, PartialEq, Eq, PartialOrd, Ord)]
struct Empty;
pub struct Empty;

// A basic struct.
#[derive(Clone, Copy, Debug, Default, Hash, PartialEq, Eq, PartialOrd, Ord)]
struct Point {
pub struct Point {
x: u32,
y: u32,
}

// A large struct.
#[derive(Clone, Debug, Default, Hash, PartialEq, Eq, PartialOrd, Ord)]
struct Big {
pub struct Big {
b1: u32, b2: u32, b3: u32, b4: u32, b5: u32, b6: u32, b7: u32, b8: u32,
}

// A struct with an unsized field. Some derives are not usable in this case.
#[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
struct Unsized([u32]);
pub struct Unsized([u32]);

// A packed tuple struct that impls `Copy`.
#[derive(Clone, Copy, Debug, Default, Hash, PartialEq, Eq, PartialOrd, Ord)]
#[repr(packed)]
struct PackedCopy(u32);
pub struct PackedCopy(u32);

// A packed tuple struct that does not impl `Copy`. Note that the alignment of
// the field must be 1 for this code to be valid. Otherwise it triggers an
@@ -50,28 +50,28 @@ struct PackedCopy(u32);
// it's possible that this struct is not supposed to work, but for now it does.
#[derive(Clone, Debug, Default, Hash, PartialEq, Eq, PartialOrd, Ord)]
#[repr(packed)]
struct PackedNonCopy(u8);
pub struct PackedNonCopy(u8);

// An empty enum.
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
enum Enum0 {}
pub enum Enum0 {}

// A single-variant enum.
#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
enum Enum1 {
pub enum Enum1 {
Single { x: u32 }
}

// A C-like, fieldless enum with a single variant.
#[derive(Clone, Debug, Default, Hash, PartialEq, Eq, PartialOrd, Ord)]
enum Fieldless1 {
pub enum Fieldless1 {
#[default]
A,
}

// A C-like, fieldless enum.
#[derive(Clone, Copy, Debug, Default, Hash, PartialEq, Eq, PartialOrd, Ord)]
enum Fieldless {
pub enum Fieldless {
#[default]
A,
B,
@@ -80,7 +80,7 @@ enum Fieldless {

// An enum with multiple fieldless and fielded variants.
#[derive(Clone, Copy, Debug, Default, Hash, PartialEq, Eq, PartialOrd, Ord)]
enum Mixed {
pub enum Mixed {
#[default]
P,
Q,
@@ -91,7 +91,7 @@ enum Mixed {
// An enum with no fieldless variants. Note that `Default` cannot be derived
// for this enum.
#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
enum Fielded {
pub enum Fielded {
X(u32),
Y(bool),
Z(Option<i32>),
@@ -104,3 +104,20 @@ pub union Union {
pub u: u32,
pub i: i32,
}

#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub enum Void {}

#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct WithUninhabited {
x: u32,
v: Void,
y: u32,
}

#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub enum EnumWithUninhabited {
A(u32),
B(Void),
C(u16),
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
#![feature(prelude_import)]
// check-pass
// compile-flags: -Zunpretty=expanded
// edition:2021
// revisions: check unpretty
// [unpretty] compile-flags: -Zunpretty=expanded
//
// This test checks the code generated for all[*] the builtin derivable traits
// on a variety of structs and enums. It protects against accidental changes to
@@ -15,15 +16,15 @@
// also require the `rustc_serialize` crate.

#![crate_type = "lib"]
#![allow(dead_code)]
#![allow(deprecated)]
#![warn(unused)]
#[prelude_import]
use std::prelude::rust_2021::*;
#[macro_use]
extern crate std;
// test that lints are not triggerred in derived code

// Empty struct.
struct Empty;
pub struct Empty;
#[automatically_derived]
impl ::core::clone::Clone for Empty {
#[inline]
@@ -79,7 +80,7 @@ impl ::core::cmp::Ord for Empty {
}

// A basic struct.
struct Point {
pub struct Point {
x: u32,
y: u32,
}
@@ -162,7 +163,7 @@ impl ::core::cmp::Ord for Point {
}

// A large struct.
struct Big {
pub struct Big {
b1: u32,
b2: u32,
b3: u32,
@@ -337,7 +338,7 @@ impl ::core::cmp::Ord for Big {
}

// A struct with an unsized field. Some derives are not usable in this case.
struct Unsized([u32]);
pub struct Unsized([u32]);
#[automatically_derived]
impl ::core::fmt::Debug for Unsized {
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
@@ -387,7 +388,7 @@ impl ::core::cmp::Ord for Unsized {

// A packed tuple struct that impls `Copy`.
#[repr(packed)]
struct PackedCopy(u32);
pub struct PackedCopy(u32);
#[automatically_derived]
impl ::core::clone::Clone for PackedCopy {
#[inline]
@@ -458,7 +459,7 @@ impl ::core::cmp::Ord for PackedCopy {
// derive Copy (error E0133)" at MIR building time. This is a weird case and
// it's possible that this struct is not supposed to work, but for now it does.
#[repr(packed)]
struct PackedNonCopy(u8);
pub struct PackedNonCopy(u8);
#[automatically_derived]
impl ::core::clone::Clone for PackedNonCopy {
#[inline]
@@ -532,7 +533,7 @@ impl ::core::cmp::Ord for PackedNonCopy {
}

// An empty enum.
enum Enum0 {}
pub enum Enum0 {}
#[automatically_derived]
impl ::core::clone::Clone for Enum0 {
#[inline]
@@ -587,7 +588,7 @@ impl ::core::cmp::Ord for Enum0 {
}

// A single-variant enum.
enum Enum1 {
pub enum Enum1 {
Single {
x: u32,
},
@@ -667,7 +668,7 @@ impl ::core::cmp::Ord for Enum1 {
}

// A C-like, fieldless enum with a single variant.
enum Fieldless1 {
pub enum Fieldless1 {

#[default]
A,
@@ -725,7 +726,7 @@ impl ::core::cmp::Ord for Fieldless1 {
}

// A C-like, fieldless enum.
enum Fieldless {
pub enum Fieldless {

#[default]
A,
@@ -802,7 +803,7 @@ impl ::core::cmp::Ord for Fieldless {
}

// An enum with multiple fieldless and fielded variants.
enum Mixed {
pub enum Mixed {

#[default]
P,
@@ -946,7 +947,7 @@ impl ::core::cmp::Ord for Mixed {

// An enum with no fieldless variants. Note that `Default` cannot be derived
// for this enum.
enum Fielded { X(u32), Y(bool), Z(Option<i32>), }
pub enum Fielded { X(u32), Y(bool), Z(Option<i32>), }
#[automatically_derived]
impl ::core::clone::Clone for Fielded {
#[inline]
@@ -1082,3 +1083,263 @@ impl ::core::clone::Clone for Union {
}
#[automatically_derived]
impl ::core::marker::Copy for Union { }

pub enum Void {}
#[automatically_derived]
impl ::core::clone::Clone for Void {
#[inline]
fn clone(&self) -> Void { *self }
}
#[automatically_derived]
impl ::core::marker::Copy for Void { }
#[automatically_derived]
impl ::core::fmt::Debug for Void {
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
unsafe { ::core::intrinsics::unreachable() }
}
}
#[automatically_derived]
impl ::core::hash::Hash for Void {
fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) -> () {
unsafe { ::core::intrinsics::unreachable() }
}
}
impl ::core::marker::StructuralPartialEq for Void {}
#[automatically_derived]
impl ::core::cmp::PartialEq for Void {
#[inline]
fn eq(&self, other: &Void) -> bool {
unsafe { ::core::intrinsics::unreachable() }
}
}
impl ::core::marker::StructuralEq for Void {}
#[automatically_derived]
impl ::core::cmp::Eq for Void {
#[inline]
#[doc(hidden)]
#[no_coverage]
fn assert_receiver_is_total_eq(&self) -> () {}
}
#[automatically_derived]
impl ::core::cmp::PartialOrd for Void {
#[inline]
fn partial_cmp(&self, other: &Void)
-> ::core::option::Option<::core::cmp::Ordering> {
unsafe { ::core::intrinsics::unreachable() }
}
}
#[automatically_derived]
impl ::core::cmp::Ord for Void {
#[inline]
fn cmp(&self, other: &Void) -> ::core::cmp::Ordering {
unsafe { ::core::intrinsics::unreachable() }
}
}

pub struct WithUninhabited {
x: u32,
v: Void,
y: u32,
}
#[automatically_derived]
impl ::core::clone::Clone for WithUninhabited {
#[inline]
fn clone(&self) -> WithUninhabited {
let _: ::core::clone::AssertParamIsClone<u32>;
let _: ::core::clone::AssertParamIsClone<Void>;
*self
}
}
#[automatically_derived]
impl ::core::marker::Copy for WithUninhabited { }
#[automatically_derived]
impl ::core::fmt::Debug for WithUninhabited {
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::debug_struct_field3_finish(f,
"WithUninhabited", "x", &&self.x, "v", &&self.v, "y", &&self.y)
}
}
#[automatically_derived]
impl ::core::hash::Hash for WithUninhabited {
fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) -> () {
::core::hash::Hash::hash(&self.x, state);
::core::hash::Hash::hash(&self.v, state);
::core::hash::Hash::hash(&self.y, state)
}
}
impl ::core::marker::StructuralPartialEq for WithUninhabited {}
#[automatically_derived]
impl ::core::cmp::PartialEq for WithUninhabited {
#[inline]
fn eq(&self, other: &WithUninhabited) -> bool {
self.x == other.x && self.v == other.v && self.y == other.y
}
}
impl ::core::marker::StructuralEq for WithUninhabited {}
#[automatically_derived]
impl ::core::cmp::Eq for WithUninhabited {
#[inline]
#[doc(hidden)]
#[no_coverage]
fn assert_receiver_is_total_eq(&self) -> () {
let _: ::core::cmp::AssertParamIsEq<u32>;
let _: ::core::cmp::AssertParamIsEq<Void>;
}
}
#[automatically_derived]
impl ::core::cmp::PartialOrd for WithUninhabited {
#[inline]
fn partial_cmp(&self, other: &WithUninhabited)
-> ::core::option::Option<::core::cmp::Ordering> {
match ::core::cmp::PartialOrd::partial_cmp(&self.x, &other.x) {
::core::option::Option::Some(::core::cmp::Ordering::Equal) =>
match ::core::cmp::PartialOrd::partial_cmp(&self.v, &other.v)
{
::core::option::Option::Some(::core::cmp::Ordering::Equal)
=> ::core::cmp::PartialOrd::partial_cmp(&self.y, &other.y),
cmp => cmp,
},
cmp => cmp,
}
}
}
#[automatically_derived]
impl ::core::cmp::Ord for WithUninhabited {
#[inline]
fn cmp(&self, other: &WithUninhabited) -> ::core::cmp::Ordering {
match ::core::cmp::Ord::cmp(&self.x, &other.x) {
::core::cmp::Ordering::Equal =>
match ::core::cmp::Ord::cmp(&self.v, &other.v) {
::core::cmp::Ordering::Equal =>
::core::cmp::Ord::cmp(&self.y, &other.y),
cmp => cmp,
},
cmp => cmp,
}
}
}

pub enum EnumWithUninhabited { A(u32), B(Void), C(u16), }
#[automatically_derived]
impl ::core::clone::Clone for EnumWithUninhabited {
#[inline]
fn clone(&self) -> EnumWithUninhabited {
let _: ::core::clone::AssertParamIsClone<u32>;
let _: ::core::clone::AssertParamIsClone<Void>;
let _: ::core::clone::AssertParamIsClone<u16>;
*self
}
}
#[automatically_derived]
impl ::core::marker::Copy for EnumWithUninhabited { }
#[automatically_derived]
impl ::core::fmt::Debug for EnumWithUninhabited {
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
match self {
EnumWithUninhabited::A(__self_0) =>
::core::fmt::Formatter::debug_tuple_field1_finish(f, "A",
&__self_0),
EnumWithUninhabited::B(__self_0) =>
::core::fmt::Formatter::debug_tuple_field1_finish(f, "B",
&__self_0),
EnumWithUninhabited::C(__self_0) =>
::core::fmt::Formatter::debug_tuple_field1_finish(f, "C",
&__self_0),
}
}
}
#[automatically_derived]
impl ::core::hash::Hash for EnumWithUninhabited {
fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) -> () {
let __self_tag = ::core::intrinsics::discriminant_value(self);
::core::hash::Hash::hash(&__self_tag, state);
match self {
EnumWithUninhabited::A(__self_0) =>
::core::hash::Hash::hash(__self_0, state),
EnumWithUninhabited::B(__self_0) =>
::core::hash::Hash::hash(__self_0, state),
EnumWithUninhabited::C(__self_0) =>
::core::hash::Hash::hash(__self_0, state),
}
}
}
impl ::core::marker::StructuralPartialEq for EnumWithUninhabited {}
#[automatically_derived]
impl ::core::cmp::PartialEq for EnumWithUninhabited {
#[inline]
fn eq(&self, other: &EnumWithUninhabited) -> bool {
let __self_tag = ::core::intrinsics::discriminant_value(self);
let __arg1_tag = ::core::intrinsics::discriminant_value(other);
__self_tag == __arg1_tag &&
match (self, other) {
(EnumWithUninhabited::A(__self_0),
EnumWithUninhabited::A(__arg1_0)) => *__self_0 == *__arg1_0,
(EnumWithUninhabited::B(__self_0),
EnumWithUninhabited::B(__arg1_0)) => *__self_0 == *__arg1_0,
(EnumWithUninhabited::C(__self_0),
EnumWithUninhabited::C(__arg1_0)) => *__self_0 == *__arg1_0,
_ => unsafe { ::core::intrinsics::unreachable() }
}
}
}
impl ::core::marker::StructuralEq for EnumWithUninhabited {}
#[automatically_derived]
impl ::core::cmp::Eq for EnumWithUninhabited {
#[inline]
#[doc(hidden)]
#[no_coverage]
fn assert_receiver_is_total_eq(&self) -> () {
let _: ::core::cmp::AssertParamIsEq<u32>;
let _: ::core::cmp::AssertParamIsEq<Void>;
let _: ::core::cmp::AssertParamIsEq<u16>;
}
}
#[automatically_derived]
impl ::core::cmp::PartialOrd for EnumWithUninhabited {
#[inline]
fn partial_cmp(&self, other: &EnumWithUninhabited)
-> ::core::option::Option<::core::cmp::Ordering> {
let __self_tag = ::core::intrinsics::discriminant_value(self);
let __arg1_tag = ::core::intrinsics::discriminant_value(other);
match ::core::cmp::PartialOrd::partial_cmp(&__self_tag, &__arg1_tag) {
::core::option::Option::Some(::core::cmp::Ordering::Equal) =>
match (self, other) {
(EnumWithUninhabited::A(__self_0),
EnumWithUninhabited::A(__arg1_0)) =>
::core::cmp::PartialOrd::partial_cmp(__self_0, __arg1_0),
(EnumWithUninhabited::B(__self_0),
EnumWithUninhabited::B(__arg1_0)) =>
::core::cmp::PartialOrd::partial_cmp(__self_0, __arg1_0),
(EnumWithUninhabited::C(__self_0),
EnumWithUninhabited::C(__arg1_0)) =>
::core::cmp::PartialOrd::partial_cmp(__self_0, __arg1_0),
_ => unsafe { ::core::intrinsics::unreachable() }
},
cmp => cmp,
}
}
}
#[automatically_derived]
impl ::core::cmp::Ord for EnumWithUninhabited {
#[inline]
fn cmp(&self, other: &EnumWithUninhabited) -> ::core::cmp::Ordering {
let __self_tag = ::core::intrinsics::discriminant_value(self);
let __arg1_tag = ::core::intrinsics::discriminant_value(other);
match ::core::cmp::Ord::cmp(&__self_tag, &__arg1_tag) {
::core::cmp::Ordering::Equal =>
match (self, other) {
(EnumWithUninhabited::A(__self_0),
EnumWithUninhabited::A(__arg1_0)) =>
::core::cmp::Ord::cmp(__self_0, __arg1_0),
(EnumWithUninhabited::B(__self_0),
EnumWithUninhabited::B(__arg1_0)) =>
::core::cmp::Ord::cmp(__self_0, __arg1_0),
(EnumWithUninhabited::C(__self_0),
EnumWithUninhabited::C(__arg1_0)) =>
::core::cmp::Ord::cmp(__self_0, __arg1_0),
_ => unsafe { ::core::intrinsics::unreachable() }
},
cmp => cmp,
}
}
}
4 changes: 2 additions & 2 deletions src/test/ui/generator/issue-93161.rs
Original file line number Diff line number Diff line change
@@ -39,8 +39,8 @@ async fn includes_never(crash: bool, x: u32) -> u32 {
}
#[allow(unused)]
let bad = never();
result *= async { x + x }.await;
drop(bad);
result *= async { x + x }.await; //~ unreachable statement
drop(bad); //~ unreachable call
result
}

20 changes: 20 additions & 0 deletions src/test/ui/generator/issue-93161.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
warning: unreachable statement
--> $DIR/issue-93161.rs:42:5
|
LL | let bad = never();
| ------- this expression has type `Never`, which is uninhabited
LL | result *= async { x + x }.await;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unreachable statement
|
= note: `#[warn(unreachable_code)]` on by default

warning: unreachable call
--> $DIR/issue-93161.rs:43:5
|
LL | drop(bad);
| ^^^^ --- this expression has type `Never`, which is uninhabited
| |
| unreachable call

warning: 2 warnings emitted

4 changes: 2 additions & 2 deletions src/test/ui/issues/issue-46519.rs
Original file line number Diff line number Diff line change
@@ -6,7 +6,7 @@
#[test]
#[should_panic(expected = "creating inhabited type")]
fn test() {
FontLanguageOverride::system_font(SystemFont::new());
FontLanguageOverride::system_font(SystemFont::new()); //~ unreachable call
}

pub enum FontLanguageOverride {
@@ -19,7 +19,7 @@ pub enum SystemFont {}

impl FontLanguageOverride {
fn system_font(f: SystemFont) -> Self {
FontLanguageOverride::System(f)
FontLanguageOverride::System(f) //~ unreachable call
}
}

20 changes: 20 additions & 0 deletions src/test/ui/issues/issue-46519.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
warning: unreachable call
--> $DIR/issue-46519.rs:9:5
|
LL | FontLanguageOverride::system_font(SystemFont::new());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ----------------- this expression has type `SystemFont`, which is uninhabited
| |
| unreachable call
|
= note: `#[warn(unreachable_code)]` on by default

warning: unreachable call
--> $DIR/issue-46519.rs:22:9
|
LL | FontLanguageOverride::System(f)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - this expression has type `SystemFont`, which is uninhabited
| |
| unreachable call

warning: 2 warnings emitted

2 changes: 1 addition & 1 deletion src/test/ui/issues/issue-46855.rs
Original file line number Diff line number Diff line change
@@ -12,7 +12,7 @@ union Foo {
b: Never
}

fn foo(xs: [(Never, u32); 1]) -> u32 { xs[0].1 }
fn foo(xs: [(Never, u32); 1]) -> u32 { xs[0].1 } //~ unreachable expression

fn bar([(_, x)]: [(Never, u32); 1]) -> u32 { x }

12 changes: 12 additions & 0 deletions src/test/ui/issues/issue-46855.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
warning: unreachable expression
--> $DIR/issue-46855.rs:15:43
|
LL | fn foo(xs: [(Never, u32); 1]) -> u32 { xs[0].1 }
| -- ^ unreachable expression
| |
| this expression has type `[(Never, u32); 1]`, which is uninhabited
|
= note: `#[warn(unreachable_code)]` on by default

warning: 1 warning emitted

2 changes: 1 addition & 1 deletion src/test/ui/lint/dead-code/issue-85071-2.rs
Original file line number Diff line number Diff line change
@@ -18,5 +18,5 @@ fn main() {
let x = s.f();
//~^ WARNING: unused variable: `x`
let _y = x;
//~^ WARNING: unreachable definition
//~^ WARNING: unreachable statement
}
13 changes: 4 additions & 9 deletions src/test/ui/lint/dead-code/issue-85071-2.stderr
Original file line number Diff line number Diff line change
@@ -1,17 +1,12 @@
warning: unreachable definition
--> $DIR/issue-85071-2.rs:20:9
warning: unreachable statement
--> $DIR/issue-85071-2.rs:20:5
|
LL | let x = s.f();
| ----- any code following this expression is unreachable
| ----- this expression has type `Foo`, which is uninhabited
LL |
LL | let _y = x;
| ^^ unreachable definition
| ^^^^^^^^^^^ unreachable statement
|
note: this expression has type `Foo`, which is uninhabited
--> $DIR/issue-85071-2.rs:18:13
|
LL | let x = s.f();
| ^^^^^
note: the lint level is defined here
--> $DIR/issue-85071-2.rs:7:26
|
2 changes: 1 addition & 1 deletion src/test/ui/lint/dead-code/issue-85071.rs
Original file line number Diff line number Diff line change
@@ -15,5 +15,5 @@ fn main() {
let x = f();
//~^ WARNING: unused variable: `x`
let _ = x;
//~^ WARNING: unreachable expression
//~^ WARNING: unreachable statement
}
13 changes: 4 additions & 9 deletions src/test/ui/lint/dead-code/issue-85071.stderr
Original file line number Diff line number Diff line change
@@ -1,17 +1,12 @@
warning: unreachable expression
--> $DIR/issue-85071.rs:17:13
warning: unreachable statement
--> $DIR/issue-85071.rs:17:5
|
LL | let x = f();
| --- any code following this expression is unreachable
| --- this expression has type `Foo`, which is uninhabited
LL |
LL | let _ = x;
| ^ unreachable expression
| ^^^^^^^^^^ unreachable statement
|
note: this expression has type `Foo`, which is uninhabited
--> $DIR/issue-85071.rs:15:13
|
LL | let x = f();
| ^^^
note: the lint level is defined here
--> $DIR/issue-85071.rs:9:26
|
2 changes: 1 addition & 1 deletion src/test/ui/match/match-no-arms-unreachable-after.stderr
Original file line number Diff line number Diff line change
@@ -2,7 +2,7 @@ error: unreachable statement
--> $DIR/match-no-arms-unreachable-after.rs:8:5
|
LL | match v { }
| ----------- any code following this expression is unreachable
| - this expression has type `Void`, which is uninhabited
LL | let x = 2;
| ^^^^^^^^^^ unreachable statement
|
7 changes: 5 additions & 2 deletions src/test/ui/reachable/unreachable-try-pattern.rs
Original file line number Diff line number Diff line change
@@ -27,8 +27,11 @@ fn qux(x: Result<u32, Void>) -> Result<u32, i32> {
}

fn vom(x: Result<u32, Void>) -> Result<u32, i32> {
let y = (match x { Ok(n) => Ok(n), Err(e) => Err(e) })?;
//~^ WARN unreachable pattern
let y = (match x {
Ok(n) => Ok(n),
Err(e) => Err(e) //~ WARN unreachable call
//~| WARN unreachable pattern
})?;
Ok(y)
}

16 changes: 12 additions & 4 deletions src/test/ui/reachable/unreachable-try-pattern.stderr
Original file line number Diff line number Diff line change
@@ -13,6 +13,14 @@ note: the lint level is defined here
LL | #![warn(unreachable_code)]
| ^^^^^^^^^^^^^^^^

warning: unreachable call
--> $DIR/unreachable-try-pattern.rs:32:19
|
LL | Err(e) => Err(e)
| ^^^ - this expression has type `Void`, which is uninhabited
| |
| unreachable call

warning: unreachable pattern
--> $DIR/unreachable-try-pattern.rs:19:24
|
@@ -26,10 +34,10 @@ LL | #![warn(unreachable_patterns)]
| ^^^^^^^^^^^^^^^^^^^^

warning: unreachable pattern
--> $DIR/unreachable-try-pattern.rs:30:40
--> $DIR/unreachable-try-pattern.rs:32:9
|
LL | let y = (match x { Ok(n) => Ok(n), Err(e) => Err(e) })?;
| ^^^^^^
LL | Err(e) => Err(e)
| ^^^^^^

warning: 3 warnings emitted
warning: 4 warnings emitted

24 changes: 24 additions & 0 deletions src/test/ui/uninhabited/uninhabited-struct-match.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#![crate_type = "lib"]

#![warn(unused)]

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Void {}

pub struct UnStruct {
x: u32,
v: Void
}

pub fn match_struct(x: UnStruct) {
match x {} //~ non-exhaustive patterns: type `UnStruct` is non-empty
}

pub fn match_inhabited_field(x: UnStruct) {
match x.x {} //~ non-exhaustive patterns: type `u32` is non-empty
//~| unreachable expression
}

pub fn match_uninhabited_field(x: UnStruct) {
match x.v {} // ok
}
52 changes: 52 additions & 0 deletions src/test/ui/uninhabited/uninhabited-struct-match.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
warning: unreachable expression
--> $DIR/uninhabited-struct-match.rs:18:11
|
LL | match x.x {}
| -^^
| |
| unreachable expression
| this expression has type `UnStruct`, which is uninhabited
|
note: the lint level is defined here
--> $DIR/uninhabited-struct-match.rs:3:9
|
LL | #![warn(unused)]
| ^^^^^^
= note: `#[warn(unreachable_code)]` implied by `#[warn(unused)]`

error[E0004]: non-exhaustive patterns: type `UnStruct` is non-empty
--> $DIR/uninhabited-struct-match.rs:14:11
|
LL | match x {}
| ^
|
note: `UnStruct` defined here
--> $DIR/uninhabited-struct-match.rs:8:12
|
LL | pub struct UnStruct {
| ^^^^^^^^
= note: the matched value is of type `UnStruct`
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown
|
LL ~ match x {
LL + _ => todo!(),
LL ~ }
|

error[E0004]: non-exhaustive patterns: type `u32` is non-empty
--> $DIR/uninhabited-struct-match.rs:18:11
|
LL | match x.x {}
| ^^^
|
= note: the matched value is of type `u32`
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown
|
LL ~ match x.x {
LL + _ => todo!(),
LL ~ }
|

error: aborting due to 2 previous errors; 1 warning emitted

For more information about this error, try `rustc --explain E0004`.
Original file line number Diff line number Diff line change
@@ -348,6 +348,7 @@ impl Constructor {

fn as_slice(&self) -> Option<Slice> {
match self {
#[allow(unreachable_code)]
Slice(slice) => Some(*slice),
_ => None,
}
@@ -470,6 +471,7 @@ impl Constructor {
(IntRange(self_range), IntRange(other_range)) => self_range.is_covered_by(other_range),
(FloatRange(void), FloatRange(..)) => match *void {},
(Str(void), Str(..)) => match *void {},
#[allow(unreachable_code)]
(Slice(self_slice), Slice(other_slice)) => self_slice.is_covered_by(*other_slice),

// We are trying to inspect an opaque constant. Thus we skip the row.
@@ -502,6 +504,7 @@ impl Constructor {
.iter()
.filter_map(|c| c.as_int_range())
.any(|other| range.is_covered_by(other)),
#[allow(unreachable_code)]
Slice(slice) => used_ctors
.iter()
.filter_map(|c| c.as_slice())
2 changes: 1 addition & 1 deletion src/tools/tidy/src/ui_tests.rs
Original file line number Diff line number Diff line change
@@ -8,7 +8,7 @@ use std::path::Path;
const ENTRY_LIMIT: usize = 1000;
// FIXME: The following limits should be reduced eventually.
const ROOT_ENTRY_LIMIT: usize = 948;
const ISSUES_ENTRY_LIMIT: usize = 2117;
const ISSUES_ENTRY_LIMIT: usize = 2118;

fn check_entries(path: &Path, bad: &mut bool) {
let dirs = walkdir::WalkDir::new(&path.join("test/ui"))