diff --git a/compiler/rustc_hir_typeck/src/_match.rs b/compiler/rustc_hir_typeck/src/_match.rs index 2b15d4dcd0848..fb8e02da43cd0 100644 --- a/compiler/rustc_hir_typeck/src/_match.rs +++ b/compiler/rustc_hir_typeck/src/_match.rs @@ -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. diff --git a/compiler/rustc_hir_typeck/src/diverges.rs b/compiler/rustc_hir_typeck/src/diverges.rs index 963a93a95c2bb..477f7d9d1b511 100644 --- a/compiler/rustc_hir_typeck/src/diverges.rs +++ b/compiler/rustc_hir_typeck/src/diverges.rs @@ -1,5 +1,5 @@ -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 @@ -7,28 +7,15 @@ use std::{cmp, ops}; /// 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, +} diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 41b00fda03ebf..bb9e6d62ca451 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -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, + } +} diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index 6a1cffe3e6025..4ce2f14a58dd8 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -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,6 +36,7 @@ use rustc_trait_selection::traits::{ self, ObligationCause, ObligationCauseCode, TraitEngine, TraitEngineExt, }; +use std::borrow::Cow; use std::collections::hash_map::Entry; use std::slice; @@ -43,36 +44,47 @@ 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 diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs index 0c600daf4459e..ebdb58ca97901 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs @@ -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, + pub(super) diverges: Cell>, /// Whether any child nodes have any type errors. pub(super) has_errors: Cell, @@ -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, diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs index e862d577573b4..a3d8841dcf959 100644 --- a/compiler/rustc_hir_typeck/src/lib.rs +++ b/compiler/rustc_hir_typeck/src/lib.rs @@ -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}; diff --git a/compiler/rustc_passes/src/liveness.rs b/compiler/rustc_passes/src/liveness.rs index c6fe40f72fc63..74a9e05e6a596 100644 --- a/compiler/rustc_passes/src/liveness.rs +++ b/compiler/rustc_passes/src/liveness.rs @@ -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 } } } diff --git a/compiler/rustc_target/src/asm/mod.rs b/compiler/rustc_target/src/asm/mod.rs index 65d2cd64bf693..0abebff55394d 100644 --- a/compiler/rustc_target/src/asm/mod.rs +++ b/compiler/rustc_target/src/asm/mod.rs @@ -300,6 +300,7 @@ impl InlineAsmReg { } } + #[allow(unreachable_code)] pub fn parse(arch: InlineAsmArch, name: Symbol) -> Result { // FIXME: use direct symbol comparison for register names // Use `Symbol::as_str` instead of `Symbol::with` here because `has_feature` may access `Symbol`. diff --git a/src/test/mir-opt/const_prop/invalid_constant.main.ConstProp.diff b/src/test/mir-opt/const_prop/invalid_constant.main.ConstProp.diff index 67a4dc3c09269..eb95595efb86c 100644 --- a/src/test/mir-opt/const_prop/invalid_constant.main.ConstProp.diff +++ b/src/test/mir-opt/const_prop/invalid_constant.main.ConstProp.diff @@ -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 } } diff --git a/src/test/mir-opt/uninhabited_enum.process_void.SimplifyLocals.after.mir b/src/test/mir-opt/uninhabited_enum.process_void.SimplifyLocals.after.mir index bbb81724ccfd8..4818e254f041f 100644 --- a/src/test/mir-opt/uninhabited_enum.process_void.SimplifyLocals.after.mir +++ b/src/test/mir-opt/uninhabited_enum.process_void.SimplifyLocals.after.mir @@ -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 } } diff --git a/src/test/ui/break-diverging-value.rs b/src/test/ui/break-diverging-value.rs index d070fddaffc19..cb530faa5be54 100644 --- a/src/test/ui/break-diverging-value.rs +++ b/src/test/ui/break-diverging-value.rs @@ -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 } fn get_never() -> ! { diff --git a/src/test/ui/break-diverging-value.stderr b/src/test/ui/break-diverging-value.stderr index 69edcd2408002..899f03ee1e6f9 100644 --- a/src/test/ui/break-diverging-value.stderr +++ b/src/test/ui/break-diverging-value.stderr @@ -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`. diff --git a/src/test/ui/consts/const-eval/write-to-uninhabited-enum-variant.rs b/src/test/ui/consts/const-eval/write-to-uninhabited-enum-variant.rs index cccb7879fc0fb..32202b378c174 100644 --- a/src/test/ui/consts/const-eval/write-to-uninhabited-enum-variant.rs +++ b/src/test/ui/consts/const-eval/write-to-uninhabited-enum-variant.rs @@ -19,10 +19,10 @@ fn bar() -> Option { 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 } } diff --git a/src/test/ui/consts/const-eval/write-to-uninhabited-enum-variant.stderr b/src/test/ui/consts/const-eval/write-to-uninhabited-enum-variant.stderr new file mode 100644 index 0000000000000..dc177cc197a00 --- /dev/null +++ b/src/test/ui/consts/const-eval/write-to-uninhabited-enum-variant.stderr @@ -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 + diff --git a/src/test/ui/consts/const_discriminant.rs b/src/test/ui/consts/const_discriminant.rs index 79e68590e85d4..776b2936f29a6 100644 --- a/src/test/ui/consts/const_discriminant.rs +++ b/src/test/ui/consts/const_discriminant.rs @@ -15,7 +15,12 @@ const TEST_A: Discriminant = discriminant(&Test::A(5)); const TEST_A_OTHER: Discriminant = discriminant(&Test::A(17)); const TEST_B: Discriminant = discriminant(&Test::B); -enum Void {} +mod private { + enum PrivateVoid {} + pub struct VoidS(PrivateVoid); + pub enum Void { X(VoidS) } +} +use private::Void; enum SingleVariant { V, diff --git a/src/test/ui/consts/issue-64506.rs b/src/test/ui/consts/issue-64506.rs index db3e85a7bdfd1..534ffe81e5d9e 100644 --- a/src/test/ui/consts/issue-64506.rs +++ b/src/test/ui/consts/issue-64506.rs @@ -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 { diff --git a/src/test/ui/deriving/deriving-all-codegen.rs b/src/test/ui/deriving/deriving-all-codegen.rs index ba7809413bd80..d29879bc0645f 100644 --- a/src/test/ui/deriving/deriving-all-codegen.rs +++ b/src/test/ui/deriving/deriving-all-codegen.rs @@ -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), @@ -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), +} diff --git a/src/test/ui/deriving/deriving-all-codegen.stdout b/src/test/ui/deriving/deriving-all-codegen.unpretty.stdout similarity index 78% rename from src/test/ui/deriving/deriving-all-codegen.stdout rename to src/test/ui/deriving/deriving-all-codegen.unpretty.stdout index 92fce6888c089..52775ecfb3c4a 100644 --- a/src/test/ui/deriving/deriving-all-codegen.stdout +++ b/src/test/ui/deriving/deriving-all-codegen.unpretty.stdout @@ -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), } +pub enum Fielded { X(u32), Y(bool), Z(Option), } #[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; + let _: ::core::clone::AssertParamIsClone; + *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; + let _: ::core::cmp::AssertParamIsEq; + } +} +#[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; + let _: ::core::clone::AssertParamIsClone; + let _: ::core::clone::AssertParamIsClone; + *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; + let _: ::core::cmp::AssertParamIsEq; + let _: ::core::cmp::AssertParamIsEq; + } +} +#[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, + } + } +} diff --git a/src/test/ui/generator/issue-93161.rs b/src/test/ui/generator/issue-93161.rs index 92305609c835c..695eab610e9cb 100644 --- a/src/test/ui/generator/issue-93161.rs +++ b/src/test/ui/generator/issue-93161.rs @@ -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 } diff --git a/src/test/ui/generator/issue-93161.stderr b/src/test/ui/generator/issue-93161.stderr new file mode 100644 index 0000000000000..b3bd4e7faefad --- /dev/null +++ b/src/test/ui/generator/issue-93161.stderr @@ -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 + diff --git a/src/test/ui/issues/issue-46519.rs b/src/test/ui/issues/issue-46519.rs index 0567923b7fc69..7c93385a0264f 100644 --- a/src/test/ui/issues/issue-46519.rs +++ b/src/test/ui/issues/issue-46519.rs @@ -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 } } diff --git a/src/test/ui/issues/issue-46519.stderr b/src/test/ui/issues/issue-46519.stderr new file mode 100644 index 0000000000000..06285d334cd33 --- /dev/null +++ b/src/test/ui/issues/issue-46519.stderr @@ -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 + diff --git a/src/test/ui/issues/issue-46855.rs b/src/test/ui/issues/issue-46855.rs index aa6378f8594bc..b7c27a01fc852 100644 --- a/src/test/ui/issues/issue-46855.rs +++ b/src/test/ui/issues/issue-46855.rs @@ -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 } diff --git a/src/test/ui/issues/issue-46855.stderr b/src/test/ui/issues/issue-46855.stderr new file mode 100644 index 0000000000000..bc0ae7802fff0 --- /dev/null +++ b/src/test/ui/issues/issue-46855.stderr @@ -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 + diff --git a/src/test/ui/lint/dead-code/issue-85071-2.rs b/src/test/ui/lint/dead-code/issue-85071-2.rs index f0639931c84f3..542549576e835 100644 --- a/src/test/ui/lint/dead-code/issue-85071-2.rs +++ b/src/test/ui/lint/dead-code/issue-85071-2.rs @@ -18,5 +18,5 @@ fn main() { let x = s.f(); //~^ WARNING: unused variable: `x` let _y = x; - //~^ WARNING: unreachable definition + //~^ WARNING: unreachable statement } diff --git a/src/test/ui/lint/dead-code/issue-85071-2.stderr b/src/test/ui/lint/dead-code/issue-85071-2.stderr index 5e963183d094b..442648431a619 100644 --- a/src/test/ui/lint/dead-code/issue-85071-2.stderr +++ b/src/test/ui/lint/dead-code/issue-85071-2.stderr @@ -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 | diff --git a/src/test/ui/lint/dead-code/issue-85071.rs b/src/test/ui/lint/dead-code/issue-85071.rs index d6969321cad4b..eeb0f117f81e6 100644 --- a/src/test/ui/lint/dead-code/issue-85071.rs +++ b/src/test/ui/lint/dead-code/issue-85071.rs @@ -15,5 +15,5 @@ fn main() { let x = f(); //~^ WARNING: unused variable: `x` let _ = x; - //~^ WARNING: unreachable expression + //~^ WARNING: unreachable statement } diff --git a/src/test/ui/lint/dead-code/issue-85071.stderr b/src/test/ui/lint/dead-code/issue-85071.stderr index 721fb8148d96b..eda5d78c50c3d 100644 --- a/src/test/ui/lint/dead-code/issue-85071.stderr +++ b/src/test/ui/lint/dead-code/issue-85071.stderr @@ -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 | diff --git a/src/test/ui/match/match-no-arms-unreachable-after.stderr b/src/test/ui/match/match-no-arms-unreachable-after.stderr index a0a3697266d4e..70e1013567113 100644 --- a/src/test/ui/match/match-no-arms-unreachable-after.stderr +++ b/src/test/ui/match/match-no-arms-unreachable-after.stderr @@ -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 | diff --git a/src/test/ui/reachable/unreachable-try-pattern.rs b/src/test/ui/reachable/unreachable-try-pattern.rs index 23360e73f4a3a..1dba50395d2a5 100644 --- a/src/test/ui/reachable/unreachable-try-pattern.rs +++ b/src/test/ui/reachable/unreachable-try-pattern.rs @@ -27,8 +27,11 @@ fn qux(x: Result) -> Result { } fn vom(x: Result) -> Result { - 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) } diff --git a/src/test/ui/reachable/unreachable-try-pattern.stderr b/src/test/ui/reachable/unreachable-try-pattern.stderr index 8f3e23119fb9e..3cd4ff19921d5 100644 --- a/src/test/ui/reachable/unreachable-try-pattern.stderr +++ b/src/test/ui/reachable/unreachable-try-pattern.stderr @@ -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 diff --git a/src/test/ui/uninhabited/uninhabited-struct-match.rs b/src/test/ui/uninhabited/uninhabited-struct-match.rs new file mode 100644 index 0000000000000..641831abbfe21 --- /dev/null +++ b/src/test/ui/uninhabited/uninhabited-struct-match.rs @@ -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 +} diff --git a/src/test/ui/uninhabited/uninhabited-struct-match.stderr b/src/test/ui/uninhabited/uninhabited-struct-match.stderr new file mode 100644 index 0000000000000..2239db2f67e54 --- /dev/null +++ b/src/test/ui/uninhabited/uninhabited-struct-match.stderr @@ -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`. diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/deconstruct_pat.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/deconstruct_pat.rs index 47d60fc41e700..3e6bcb61bac4d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/deconstruct_pat.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/deconstruct_pat.rs @@ -348,6 +348,7 @@ impl Constructor { fn as_slice(&self) -> Option { 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()) diff --git a/src/tools/tidy/src/ui_tests.rs b/src/tools/tidy/src/ui_tests.rs index c600f99c2c4bf..ab0de7e553590 100644 --- a/src/tools/tidy/src/ui_tests.rs +++ b/src/tools/tidy/src/ui_tests.rs @@ -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"))