From 969542c76603f369575fdeed61360c1d1ee892b5 Mon Sep 17 00:00:00 2001 From: varkor Date: Fri, 27 Apr 2018 00:28:13 +0100 Subject: [PATCH 01/19] Add Diverges::UnwarnedAlways --- src/librustc_typeck/check/_match.rs | 8 +++++--- src/librustc_typeck/check/mod.rs | 13 ++++++++++--- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/src/librustc_typeck/check/_match.rs b/src/librustc_typeck/check/_match.rs index c9b5fd525dd82..619a844c743cc 100644 --- a/src/librustc_typeck/check/_match.rs +++ b/src/librustc_typeck/check/_match.rs @@ -633,18 +633,20 @@ https://doc.rust-lang.org/reference/types.html#trait-objects"); match all_pats_diverge { Diverges::Maybe => Diverges::Maybe, Diverges::Always | Diverges::WarnedAlways => Diverges::WarnedAlways, + Diverges::UnwarnedAlways => Diverges::UnwarnedAlways, } }).collect(); // Now typecheck the blocks. // // The result of the match is the common supertype of all the - // arms. Start out the value as bottom, since it's the, well, - // bottom the type lattice, and we'll be moving up the lattice as + // arms. We start the value as the logical bottom (skipping + // `UnwarnedAlways`, which is a special case), since it's the, well, + // bottom of the type lattice, and we'll be moving up the lattice as // we process each arm. (Note that any match with 0 arms is matching // on any empty type and is therefore unreachable; should the flow // of execution reach it, we will panic, so bottom is an appropriate - // type in that case) + // type in that case.) let mut all_arms_diverge = Diverges::WarnedAlways; let expected = expected.adjust_for_branches(self); diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 4af89d2148dfc..ef44dad586ae4 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -445,11 +445,18 @@ pub enum Diverges { Always, /// Same as `Always` but with a reachability - /// warning already emitted - WarnedAlways + /// warning already emitted. + WarnedAlways, + + /// Same as `Always` but without a reachability + /// warning emitted. Unlike `Always`, cannot be + /// converted to `WarnedAlways`. Used when + /// unreachable code is expected (e.g. in + /// function parameters as part of trait impls). + UnwarnedAlways, } -// Convenience impls for combinig `Diverges`. +// Convenience impls for combining `Diverges`. impl ops::BitAnd for Diverges { type Output = Self; From 2e9b296b4dfee3a2267b7a98f785ed23926210cb Mon Sep 17 00:00:00 2001 From: varkor Date: Fri, 27 Apr 2018 00:28:38 +0100 Subject: [PATCH 02/19] Functions with uninhabited parameters diverge --- src/librustc/ty/sty.rs | 10 ++++++++++ src/librustc_mir/hair/pattern/check_match.rs | 11 +---------- src/librustc_typeck/check/mod.rs | 7 +++++++ 3 files changed, 18 insertions(+), 10 deletions(-) diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs index dd38188824338..b62346b599884 100644 --- a/src/librustc/ty/sty.rs +++ b/src/librustc/ty/sty.rs @@ -1481,6 +1481,16 @@ impl<'a, 'gcx, 'tcx> TyS<'tcx> { } } + pub fn conservative_is_uninhabited(&self) -> bool { + // "rustc-1.0-style" uncontentious uninhabitableness check + match self.sty { + ty::TyNever => true, + ty::TyAdt(def, _) => def.variants.is_empty(), + ty::TyTuple(tys) => tys.iter().any(|ty| ty.conservative_is_uninhabited()), + _ => false + } + } + pub fn is_primitive(&self) -> bool { match self.sty { TyBool | TyChar | TyInt(_) | TyUint(_) | TyFloat(_) => true, diff --git a/src/librustc_mir/hair/pattern/check_match.rs b/src/librustc_mir/hair/pattern/check_match.rs index 35f9dcee99f82..63fb60df29688 100644 --- a/src/librustc_mir/hair/pattern/check_match.rs +++ b/src/librustc_mir/hair/pattern/check_match.rs @@ -227,7 +227,7 @@ impl<'a, 'tcx> MatchVisitor<'a, 'tcx> { let scrutinee_is_uninhabited = if self.tcx.features().exhaustive_patterns { self.tcx.is_ty_uninhabited_from(module, pat_ty) } else { - self.conservative_is_uninhabited(pat_ty) + pat_ty.conservative_is_uninhabited() }; if !scrutinee_is_uninhabited { // We know the type is inhabited, so this must be wrong @@ -255,15 +255,6 @@ impl<'a, 'tcx> MatchVisitor<'a, 'tcx> { }) } - fn conservative_is_uninhabited(&self, scrutinee_ty: Ty<'tcx>) -> bool { - // "rustc-1.0-style" uncontentious uninhabitableness check - match scrutinee_ty.sty { - ty::TyNever => true, - ty::TyAdt(def, _) => def.variants.is_empty(), - _ => false - } - } - fn check_irrefutable(&self, pat: &'tcx Pat, origin: &str) { let module = self.tcx.hir.get_module_parent(pat.id); MatchCheckCtxt::create_and_enter(self.tcx, module, |ref mut cx| { diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index ef44dad586ae4..c2fc78149eff0 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -1054,6 +1054,13 @@ fn check_fn<'a, 'gcx, 'tcx>(inherited: &'a Inherited<'a, 'gcx, 'tcx>, fcx.check_pat_walk(&arg.pat, arg_ty, ty::BindingMode::BindByValue(hir::Mutability::MutImmutable), true); + // If any of a function's parameters have a type that is uninhabited, then it + // may never be called (because its arguments cannot be constructed). Therefore, + // it must always diverge. + if arg_ty.conservative_is_uninhabited() { + fcx.diverges.set(fcx.diverges.get() | Diverges::UnwarnedAlways); + } + // Check that argument is Sized. // The check for a non-trivial pattern is a hack to avoid duplicate warnings // for simple cases like `fn foo(x: Trait)`, From 02f3b8f5aea8409d0656e0bb72b0ff2d6c54f35f Mon Sep 17 00:00:00 2001 From: varkor Date: Fri, 27 Apr 2018 00:50:50 +0100 Subject: [PATCH 03/19] Use conservative_is_uninhabited instead of is_never where possible --- src/librustc/cfg/construct.rs | 3 +-- src/librustc/middle/liveness.rs | 6 ++---- src/librustc/traits/error_reporting.rs | 4 ++-- src/librustc_codegen_llvm/debuginfo/mod.rs | 2 +- src/librustc_codegen_llvm/declare.rs | 2 +- src/librustc_mir/borrow_check/nll/type_check/mod.rs | 3 +-- src/librustc_mir/build/expr/into.rs | 3 +-- src/librustc_typeck/check/mod.rs | 13 ++++++++++--- 8 files changed, 19 insertions(+), 17 deletions(-) diff --git a/src/librustc/cfg/construct.rs b/src/librustc/cfg/construct.rs index f1e2794691556..0efecd658ea24 100644 --- a/src/librustc/cfg/construct.rs +++ b/src/librustc/cfg/construct.rs @@ -415,8 +415,7 @@ impl<'a, 'tcx> CFGBuilder<'a, 'tcx> { args: I) -> CFGIndex { let func_or_rcvr_exit = self.expr(func_or_rcvr, pred); let ret = self.straightline(call_expr, func_or_rcvr_exit, args); - // FIXME(canndrew): This is_never should probably be an is_uninhabited. - if self.tables.expr_ty(call_expr).is_never() { + if self.tables.expr_ty(call_expr).conservative_is_uninhabited() { self.add_unreachable_node() } else { ret diff --git a/src/librustc/middle/liveness.rs b/src/librustc/middle/liveness.rs index b828b1bd30a94..f897664f7353e 100644 --- a/src/librustc/middle/liveness.rs +++ b/src/librustc/middle/liveness.rs @@ -1115,8 +1115,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { } hir::ExprKind::Call(ref f, ref args) => { - // FIXME(canndrew): This is_never should really be an is_uninhabited - let succ = if self.tables.expr_ty(expr).is_never() { + let succ = if self.tables.expr_ty(expr).conservative_is_uninhabited() { self.s.exit_ln } else { succ @@ -1126,8 +1125,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { } hir::ExprKind::MethodCall(.., ref args) => { - // FIXME(canndrew): This is_never should really be an is_uninhabited - let succ = if self.tables.expr_ty(expr).is_never() { + let succ = if self.tables.expr_ty(expr).conservative_is_uninhabited() { self.s.exit_ln } else { succ diff --git a/src/librustc/traits/error_reporting.rs b/src/librustc/traits/error_reporting.rs index df26ac670601c..056ec6c694c5e 100644 --- a/src/librustc/traits/error_reporting.rs +++ b/src/librustc/traits/error_reporting.rs @@ -645,8 +645,8 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { // If this error is due to `!: Trait` not implemented but `(): Trait` is // implemented, and fallback has occured, then it could be due to a - // variable that used to fallback to `()` now falling back to `!`. Issue a - // note informing about the change in behaviour. + // variable that used to fallback to `()` now falling back to `!`. + // Issue a note informing about the change in behaviour. if trait_predicate.skip_binder().self_ty().is_never() && fallback_has_occurred { diff --git a/src/librustc_codegen_llvm/debuginfo/mod.rs b/src/librustc_codegen_llvm/debuginfo/mod.rs index 068dd9821ac97..39c6b85916189 100644 --- a/src/librustc_codegen_llvm/debuginfo/mod.rs +++ b/src/librustc_codegen_llvm/debuginfo/mod.rs @@ -270,7 +270,7 @@ pub fn create_function_debug_context<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, } None => {} }; - if cx.layout_of(sig.output()).abi == ty::layout::Abi::Uninhabited { + if sig.output().conservative_is_uninhabited() { flags = flags | DIFlags::FlagNoReturn; } diff --git a/src/librustc_codegen_llvm/declare.rs b/src/librustc_codegen_llvm/declare.rs index fdc84f914f502..d9ca8d55d91ee 100644 --- a/src/librustc_codegen_llvm/declare.rs +++ b/src/librustc_codegen_llvm/declare.rs @@ -134,7 +134,7 @@ pub fn declare_fn<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, name: &str, let fty = FnType::new(cx, sig, &[]); let llfn = declare_raw_fn(cx, name, fty.llvm_cconv(), fty.llvm_type(cx)); - if cx.layout_of(sig.output()).abi == layout::Abi::Uninhabited { + if sig.output().conservative_is_uninhabited() { llvm::Attribute::NoReturn.apply_llfn(Function, llfn); } diff --git a/src/librustc_mir/borrow_check/nll/type_check/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/mod.rs index e23f9b20a10f8..9a41fa570c205 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs @@ -1083,8 +1083,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { } } None => { - // FIXME(canndrew): This is_never should probably be an is_uninhabited - if !sig.output().is_never() { + if !sig.output().conservative_is_uninhabited() { span_mirbug!(self, term, "call to converging function {:?} w/o dest", sig); } } diff --git a/src/librustc_mir/build/expr/into.rs b/src/librustc_mir/build/expr/into.rs index 59a7f49af8074..481e805eabc20 100644 --- a/src/librustc_mir/build/expr/into.rs +++ b/src/librustc_mir/build/expr/into.rs @@ -213,8 +213,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { exit_block.unit() } ExprKind::Call { ty, fun, args } => { - // FIXME(canndrew): This is_never should probably be an is_uninhabited - let diverges = expr.ty.is_never(); + let diverges = expr.ty.conservative_is_uninhabited(); let intrinsic = match ty.sty { ty::TyFnDef(def_id, _) => { let f = ty.fn_sig(this.hir.tcx()); diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index c2fc78149eff0..eaf9a2952b2df 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -3666,9 +3666,16 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { _ => self.warn_if_unreachable(expr.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); + // Any expression that produces a value of an uninhabited type must have diverged. + if ty.conservative_is_uninhabited() { + let always = if ty.is_never() { + Diverges::Always + } else { + // We avoid linting in this case for + // consistency with previous versions. + Diverges::UnwarnedAlways + }; + self.diverges.set(self.diverges.get() | always); } // Record the type, which applies it effects. From 39313bbf13f035eb29ee0a00fb558d63e7efb1d4 Mon Sep 17 00:00:00 2001 From: varkor Date: Fri, 27 Apr 2018 11:00:07 +0100 Subject: [PATCH 04/19] Special-case no-arm matches --- src/librustc_typeck/check/_match.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/librustc_typeck/check/_match.rs b/src/librustc_typeck/check/_match.rs index 619a844c743cc..da9413f09b4b6 100644 --- a/src/librustc_typeck/check/_match.rs +++ b/src/librustc_typeck/check/_match.rs @@ -606,7 +606,12 @@ https://doc.rust-lang.org/reference/types.html#trait-objects"); // 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); + let diverges = match self.diverges.get() { + Diverges::Maybe | Diverges::Always | Diverges::UnwarnedAlways + => Diverges::Always, + Diverges::WarnedAlways => Diverges::WarnedAlways, + }; + self.diverges.set(diverges); return tcx.types.never; } From e61cccf4d552293df3ba78daeeec4da0e9b5b151 Mon Sep 17 00:00:00 2001 From: varkor Date: Fri, 27 Apr 2018 11:01:33 +0100 Subject: [PATCH 05/19] Update test to account for uninhabitedness of tuples --- src/test/compile-fail/uninhabited-matches-feature-gated.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/compile-fail/uninhabited-matches-feature-gated.rs b/src/test/compile-fail/uninhabited-matches-feature-gated.rs index 1d3f8ff12d865..492008a4f580a 100644 --- a/src/test/compile-fail/uninhabited-matches-feature-gated.rs +++ b/src/test/compile-fail/uninhabited-matches-feature-gated.rs @@ -20,7 +20,7 @@ fn main() { let _ = match x {}; //~ ERROR non-exhaustive let x: (Void,) = unsafe { std::mem::uninitialized() }; - let _ = match x {}; //~ ERROR non-exhaustive + let _ = match x {}; // okay let x: [Void; 1] = unsafe { std::mem::uninitialized() }; let _ = match x {}; //~ ERROR non-exhaustive From ca5afd86c16f521bddb5ae62b6009d7758305d16 Mon Sep 17 00:00:00 2001 From: varkor Date: Fri, 27 Apr 2018 13:24:29 +0100 Subject: [PATCH 06/19] Take account of TyArray uninhabitedness --- src/librustc/ty/sty.rs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs index b62346b599884..2883ea3929cca 100644 --- a/src/librustc/ty/sty.rs +++ b/src/librustc/ty/sty.rs @@ -1482,11 +1482,22 @@ impl<'a, 'gcx, 'tcx> TyS<'tcx> { } pub fn conservative_is_uninhabited(&self) -> bool { - // "rustc-1.0-style" uncontentious uninhabitableness check + // Uncontentious uninhabitableness check match self.sty { ty::TyNever => true, + // We could extend this to ADTs in which every variant is uninhabited + // (for enums, unions, etc.). ty::TyAdt(def, _) => def.variants.is_empty(), ty::TyTuple(tys) => tys.iter().any(|ty| ty.conservative_is_uninhabited()), + ty::TyArray(ty, len) => { + match len.val.to_raw_bits() { + // If the array is definitely non-empty, it's uninhabited if + // the type of its elements is uninhabited. + Some(n) if n != 0 => ty.conservative_is_uninhabited(), + _ => false + } + } + // `ty::TyRef` is a grey area at the moment. _ => false } } From 2e23d8d63b6d2a45ce3b6fb134fd09bec6310522 Mon Sep 17 00:00:00 2001 From: varkor Date: Sat, 28 Apr 2018 00:46:25 +0100 Subject: [PATCH 07/19] Improve the documentation of Diverges --- src/librustc_typeck/check/mod.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index eaf9a2952b2df..1465dd10db158 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -434,6 +434,15 @@ pub enum PlaceOp { /// wake). Tracked semi-automatically (through type variables marked /// as diverging), with some manual adjustments for control-flow /// primitives (approximating a CFG). +/// We know a node diverges in the following (conservative) situations: +/// - A function with a parameter whose type is uninhabited necessarily diverges. +/// - A match expression with no arms necessarily diverges. +/// - A match expression whose arms patterns all diverge necessarily diverges. +/// - A match expression whose arms all diverge necessarily diverges. +/// - An expression whose type is uninhabited necessarily diverges. +/// In the above, the node will be marked as diverging `Always`, `WarnedAlways` +/// or `UnwarnedAlways`. +/// In any other situation, it will be marked as `Maybe`. #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] pub enum Diverges { /// Potentially unknown, some cases converge, From d485cc4d7d509f0ef48d37e78d05c5e5ae6ec42d Mon Sep 17 00:00:00 2001 From: varkor Date: Sat, 28 Apr 2018 00:48:20 +0100 Subject: [PATCH 08/19] Update test to account for uninhabitedness of arrays --- src/test/compile-fail/uninhabited-matches-feature-gated.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/compile-fail/uninhabited-matches-feature-gated.rs b/src/test/compile-fail/uninhabited-matches-feature-gated.rs index 492008a4f580a..a3b99deac233b 100644 --- a/src/test/compile-fail/uninhabited-matches-feature-gated.rs +++ b/src/test/compile-fail/uninhabited-matches-feature-gated.rs @@ -23,7 +23,7 @@ fn main() { let _ = match x {}; // okay let x: [Void; 1] = unsafe { std::mem::uninitialized() }; - let _ = match x {}; //~ ERROR non-exhaustive + let _ = match x {}; // okay let x: &[Void] = unsafe { std::mem::uninitialized() }; let _ = match x { //~ ERROR non-exhaustive From 9d4b466bd7d61582d3d45e94b1a23897368980f5 Mon Sep 17 00:00:00 2001 From: varkor Date: Sat, 28 Apr 2018 21:20:18 +0100 Subject: [PATCH 09/19] Add a feature gate for the improved divergence behaviour --- src/librustc_mir/hair/pattern/check_match.rs | 10 +++- src/librustc_typeck/check/_match.rs | 11 ++--- src/librustc_typeck/check/mod.rs | 33 +++++-------- src/libsyntax/feature_gate.rs | 3 ++ ...ited-matches-better-divergence-checking.rs | 46 +++++++++++++++++++ .../uninhabited-matches-feature-gated.rs | 4 +- 6 files changed, 75 insertions(+), 32 deletions(-) create mode 100644 src/test/compile-fail/uninhabited-matches-better-divergence-checking.rs diff --git a/src/librustc_mir/hair/pattern/check_match.rs b/src/librustc_mir/hair/pattern/check_match.rs index 63fb60df29688..c53ee45c5d76d 100644 --- a/src/librustc_mir/hair/pattern/check_match.rs +++ b/src/librustc_mir/hair/pattern/check_match.rs @@ -227,7 +227,15 @@ impl<'a, 'tcx> MatchVisitor<'a, 'tcx> { let scrutinee_is_uninhabited = if self.tcx.features().exhaustive_patterns { self.tcx.is_ty_uninhabited_from(module, pat_ty) } else { - pat_ty.conservative_is_uninhabited() + if self.tcx.features().better_divergence_checking { + pat_ty.conservative_is_uninhabited() + } else { + match pat_ty.sty { + ty::TyNever => true, + ty::TyAdt(def, _) => def.variants.is_empty(), + _ => false + } + } }; if !scrutinee_is_uninhabited { // We know the type is inhabited, so this must be wrong diff --git a/src/librustc_typeck/check/_match.rs b/src/librustc_typeck/check/_match.rs index da9413f09b4b6..b542393ff65f8 100644 --- a/src/librustc_typeck/check/_match.rs +++ b/src/librustc_typeck/check/_match.rs @@ -607,8 +607,7 @@ https://doc.rust-lang.org/reference/types.html#trait-objects"); // If there are no arms, that is a diverging match; a special case. if arms.is_empty() { let diverges = match self.diverges.get() { - Diverges::Maybe | Diverges::Always | Diverges::UnwarnedAlways - => Diverges::Always, + Diverges::Maybe | Diverges::Always => Diverges::Always, Diverges::WarnedAlways => Diverges::WarnedAlways, }; self.diverges.set(diverges); @@ -638,17 +637,15 @@ https://doc.rust-lang.org/reference/types.html#trait-objects"); match all_pats_diverge { Diverges::Maybe => Diverges::Maybe, Diverges::Always | Diverges::WarnedAlways => Diverges::WarnedAlways, - Diverges::UnwarnedAlways => Diverges::UnwarnedAlways, } }).collect(); // Now typecheck the blocks. // // The result of the match is the common supertype of all the - // arms. We start the value as the logical bottom (skipping - // `UnwarnedAlways`, which is a special case), since it's the, well, - // bottom of the type lattice, and we'll be moving up the lattice as - // we process each arm. (Note that any match with 0 arms is matching + // arms. We start the value as the logical bottom , since it's the, + // well, bottom of the type lattice, and we'll be moving up the lattice + // as we process each arm. (Note that any match with 0 arms is matching // on any empty type and is therefore unreachable; should the flow // of execution reach it, we will panic, so bottom is an appropriate // type in that case.) diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 1465dd10db158..28e81800a7996 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -440,8 +440,7 @@ pub enum PlaceOp { /// - A match expression whose arms patterns all diverge necessarily diverges. /// - A match expression whose arms all diverge necessarily diverges. /// - An expression whose type is uninhabited necessarily diverges. -/// In the above, the node will be marked as diverging `Always`, `WarnedAlways` -/// or `UnwarnedAlways`. +/// In the above, the node will be marked as diverging `Always` or `WarnedAlways`. /// In any other situation, it will be marked as `Maybe`. #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] pub enum Diverges { @@ -456,13 +455,6 @@ pub enum Diverges { /// Same as `Always` but with a reachability /// warning already emitted. WarnedAlways, - - /// Same as `Always` but without a reachability - /// warning emitted. Unlike `Always`, cannot be - /// converted to `WarnedAlways`. Used when - /// unreachable code is expected (e.g. in - /// function parameters as part of trait impls). - UnwarnedAlways, } // Convenience impls for combining `Diverges`. @@ -1066,8 +1058,10 @@ fn check_fn<'a, 'gcx, 'tcx>(inherited: &'a Inherited<'a, 'gcx, 'tcx>, // If any of a function's parameters have a type that is uninhabited, then it // may never be called (because its arguments cannot be constructed). Therefore, // it must always diverge. - if arg_ty.conservative_is_uninhabited() { - fcx.diverges.set(fcx.diverges.get() | Diverges::UnwarnedAlways); + if fcx.tcx.features().better_divergence_checking { + if arg_ty.conservative_is_uninhabited() { + fcx.diverges.set(fcx.diverges.get() | Diverges::Always); + } } // Check that argument is Sized. @@ -3649,9 +3643,9 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { /// that there are actually multiple representations for `TyError`, so avoid /// that when err needs to be handled differently. fn check_expr_with_expectation_and_needs(&self, - expr: &'gcx hir::Expr, - expected: Expectation<'tcx>, - needs: Needs) -> Ty<'tcx> { + expr: &'gcx hir::Expr, + expected: Expectation<'tcx>, + needs: Needs) -> Ty<'tcx> { debug!(">> typechecking: expr={:?} expected={:?}", expr, expected); @@ -3677,14 +3671,9 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { // Any expression that produces a value of an uninhabited type must have diverged. if ty.conservative_is_uninhabited() { - let always = if ty.is_never() { - Diverges::Always - } else { - // We avoid linting in this case for - // consistency with previous versions. - Diverges::UnwarnedAlways - }; - self.diverges.set(self.diverges.get() | always); + if ty.is_never() || self.tcx.features().better_divergence_checking { + self.diverges.set(self.diverges.get() | Diverges::Always); + } } // Record the type, which applies it effects. diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index 30137439e7740..df580059b6d87 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -474,6 +474,9 @@ declare_features! ( (active, alloc_error_handler, "1.29.0", Some(51540), None), (active, abi_amdgpu_kernel, "1.29.0", Some(51575), None), + + // Enables improved divegence checking in functions/match arms. + (active, better_divergence_checking, "1.279.0", Some(50262), None), ); declare_features! ( diff --git a/src/test/compile-fail/uninhabited-matches-better-divergence-checking.rs b/src/test/compile-fail/uninhabited-matches-better-divergence-checking.rs new file mode 100644 index 0000000000000..f514675f11cb3 --- /dev/null +++ b/src/test/compile-fail/uninhabited-matches-better-divergence-checking.rs @@ -0,0 +1,46 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(better_divergence_checking)] + +enum Void {} + +fn main() { + let x: Result = Ok(23); + let _ = match x { //~ ERROR non-exhaustive + Ok(n) => n, + }; + + let x: &Void = unsafe { std::mem::uninitialized() }; + let _ = match x {}; //~ ERROR non-exhaustive + + let x: (Void,) = unsafe { std::mem::uninitialized() }; + let _ = match x {}; // okay + + let x: [Void; 1] = unsafe { std::mem::uninitialized() }; + let _ = match x {}; // okay + + let x: &[Void] = unsafe { std::mem::uninitialized() }; + let _ = match x { //~ ERROR non-exhaustive + &[] => (), + }; + + let x: Void = unsafe { std::mem::uninitialized() }; + let _ = match x {}; // okay + + let x: Result = Ok(23); + let _ = match x { //~ ERROR non-exhaustive + Ok(x) => x, + }; + + let x: Result = Ok(23); + let Ok(x) = x; + //~^ ERROR refutable +} diff --git a/src/test/compile-fail/uninhabited-matches-feature-gated.rs b/src/test/compile-fail/uninhabited-matches-feature-gated.rs index a3b99deac233b..1d3f8ff12d865 100644 --- a/src/test/compile-fail/uninhabited-matches-feature-gated.rs +++ b/src/test/compile-fail/uninhabited-matches-feature-gated.rs @@ -20,10 +20,10 @@ fn main() { let _ = match x {}; //~ ERROR non-exhaustive let x: (Void,) = unsafe { std::mem::uninitialized() }; - let _ = match x {}; // okay + let _ = match x {}; //~ ERROR non-exhaustive let x: [Void; 1] = unsafe { std::mem::uninitialized() }; - let _ = match x {}; // okay + let _ = match x {}; //~ ERROR non-exhaustive let x: &[Void] = unsafe { std::mem::uninitialized() }; let _ = match x { //~ ERROR non-exhaustive From 30b1942698c84eccaf1b3cc0500cc605c3699600 Mon Sep 17 00:00:00 2001 From: varkor Date: Sat, 28 Apr 2018 22:46:06 +0100 Subject: [PATCH 10/19] Add tests for better_divergence_checking feature gate --- src/test/ui/better_divergence_checking.rs | 20 ++++++++++++++ src/test/ui/better_divergence_checking.stderr | 17 ++++++++++++ ...eature-gate-better_divergence_checking.err | 11 ++++++++ ...feature-gate-better_divergence_checking.rs | 19 +++++++++++++ ...ure-gate-better_divergence_checking.stderr | 27 +++++++++++++++++++ 5 files changed, 94 insertions(+) create mode 100644 src/test/ui/better_divergence_checking.rs create mode 100644 src/test/ui/better_divergence_checking.stderr create mode 100644 src/test/ui/feature-gate-better_divergence_checking.err create mode 100644 src/test/ui/feature-gate-better_divergence_checking.rs create mode 100644 src/test/ui/feature-gate-better_divergence_checking.stderr diff --git a/src/test/ui/better_divergence_checking.rs b/src/test/ui/better_divergence_checking.rs new file mode 100644 index 0000000000000..d2fab4f0d07de --- /dev/null +++ b/src/test/ui/better_divergence_checking.rs @@ -0,0 +1,20 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(better_divergence_checking)] +#![deny(unreachable_code)] + +pub enum Void {} + +pub fn uninhabited_parameter(_v: Void) { + //~^ ERROR unreachable expression +} + +fn main() {} diff --git a/src/test/ui/better_divergence_checking.stderr b/src/test/ui/better_divergence_checking.stderr new file mode 100644 index 0000000000000..ff9a46719a017 --- /dev/null +++ b/src/test/ui/better_divergence_checking.stderr @@ -0,0 +1,17 @@ +error: unreachable expression + --> $DIR/better_divergence_checking.rs:16:40 + | +LL | pub fn uninhabited_parameter(_v: Void) { + | ________________________________________^ +LL | | //~^ ERROR unreachable expression +LL | | } + | |_^ + | +note: lint level defined here + --> $DIR/better_divergence_checking.rs:12:9 + | +LL | #![deny(unreachable_code)] + | ^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/feature-gate-better_divergence_checking.err b/src/test/ui/feature-gate-better_divergence_checking.err new file mode 100644 index 0000000000000..72f2f08b5b8e7 --- /dev/null +++ b/src/test/ui/feature-gate-better_divergence_checking.err @@ -0,0 +1,11 @@ +error[E0658]: box expression syntax is experimental; you can call `Box::new` instead. (see issue #27779) + --> $DIR/feature-gate-box-expr.rs:22:13 + | +LL | let x = box 'c'; //~ ERROR box expression syntax is experimental + | ^^^^^^^ + | + = help: add #![feature(box_syntax)] to the crate attributes to enable + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/feature-gate-better_divergence_checking.rs b/src/test/ui/feature-gate-better_divergence_checking.rs new file mode 100644 index 0000000000000..509dfdd3a36c9 --- /dev/null +++ b/src/test/ui/feature-gate-better_divergence_checking.rs @@ -0,0 +1,19 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +pub enum Void {} + +fn main() { + let x: (Void,) = unsafe { std::mem::uninitialized() }; + let _ = match x {}; //~ ERROR non-exhaustive + + let x: [Void; 1] = unsafe { std::mem::uninitialized() }; + let _ = match x {}; //~ ERROR non-exhaustive +} diff --git a/src/test/ui/feature-gate-better_divergence_checking.stderr b/src/test/ui/feature-gate-better_divergence_checking.stderr new file mode 100644 index 0000000000000..6466d41200cec --- /dev/null +++ b/src/test/ui/feature-gate-better_divergence_checking.stderr @@ -0,0 +1,27 @@ +error[E0004]: non-exhaustive patterns: type (Void,) is non-empty + --> $DIR/feature-gate-better_divergence_checking.rs:15:19 + | +LL | let _ = match x {}; //~ ERROR non-exhaustive + | ^ + | +help: Please ensure that all possible cases are being handled; possibly adding wildcards or more match arms. + --> $DIR/feature-gate-better_divergence_checking.rs:15:19 + | +LL | let _ = match x {}; //~ ERROR non-exhaustive + | ^ + +error[E0004]: non-exhaustive patterns: type [Void; 1] is non-empty + --> $DIR/feature-gate-better_divergence_checking.rs:18:19 + | +LL | let _ = match x {}; //~ ERROR non-exhaustive + | ^ + | +help: Please ensure that all possible cases are being handled; possibly adding wildcards or more match arms. + --> $DIR/feature-gate-better_divergence_checking.rs:18:19 + | +LL | let _ = match x {}; //~ ERROR non-exhaustive + | ^ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0004`. From a778a7349ef9647a5abc80c424a3ce317735a13a Mon Sep 17 00:00:00 2001 From: varkor Date: Sat, 28 Apr 2018 20:55:15 +0100 Subject: [PATCH 11/19] Fix the nil-enum test --- src/test/debuginfo/nil-enum.rs | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/src/test/debuginfo/nil-enum.rs b/src/test/debuginfo/nil-enum.rs index 94377421c0b0c..c6f1f570ddd1a 100644 --- a/src/test/debuginfo/nil-enum.rs +++ b/src/test/debuginfo/nil-enum.rs @@ -15,31 +15,20 @@ // compile-flags:-g // gdb-command:run -// gdb-command:print first +// gdb-command:print *first // gdbg-check:$1 = {} // gdbr-check:$1 = -// gdb-command:print second -// gdbg-check:$2 = {} -// gdbr-check:$2 = - #![allow(unused_variables)] #![feature(omit_gdb_pretty_printer_section)] #![omit_gdb_pretty_printer_section] -enum ANilEnum {} -enum AnotherNilEnum {} +enum Void {} -// This test relies on gdbg printing the string "{}" for empty -// structs (which may change some time) -// The error from gdbr is expected since nil enums are not supposed to exist. fn main() { - unsafe { - let first: ANilEnum = ::std::mem::zeroed(); - let second: AnotherNilEnum = ::std::mem::zeroed(); + let first: *const Void = 1 as *const _; - zzz(); // #break - } + zzz(); // #break } -fn zzz() {()} +fn zzz() {} From e7dc0759dc231213799ca0d11f87a80ad9fca00d Mon Sep 17 00:00:00 2001 From: varkor Date: Mon, 30 Apr 2018 22:18:46 +0100 Subject: [PATCH 12/19] Fix a typo in the feature gate --- src/libsyntax/feature_gate.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index df580059b6d87..30137439e7740 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -474,9 +474,6 @@ declare_features! ( (active, alloc_error_handler, "1.29.0", Some(51540), None), (active, abi_amdgpu_kernel, "1.29.0", Some(51575), None), - - // Enables improved divegence checking in functions/match arms. - (active, better_divergence_checking, "1.279.0", Some(50262), None), ); declare_features! ( From c07fcd8a9ed71e76a5857d9195f36c819e09b4ef Mon Sep 17 00:00:00 2001 From: varkor Date: Mon, 30 Apr 2018 22:51:14 +0100 Subject: [PATCH 13/19] Remove additions to conservative_is_uninhabited --- src/librustc/ty/sty.rs | 12 ------------ src/librustc_typeck/check/mod.rs | 2 ++ 2 files changed, 2 insertions(+), 12 deletions(-) diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs index 2883ea3929cca..ee2b11bf6bdee 100644 --- a/src/librustc/ty/sty.rs +++ b/src/librustc/ty/sty.rs @@ -1485,19 +1485,7 @@ impl<'a, 'gcx, 'tcx> TyS<'tcx> { // Uncontentious uninhabitableness check match self.sty { ty::TyNever => true, - // We could extend this to ADTs in which every variant is uninhabited - // (for enums, unions, etc.). ty::TyAdt(def, _) => def.variants.is_empty(), - ty::TyTuple(tys) => tys.iter().any(|ty| ty.conservative_is_uninhabited()), - ty::TyArray(ty, len) => { - match len.val.to_raw_bits() { - // If the array is definitely non-empty, it's uninhabited if - // the type of its elements is uninhabited. - Some(n) if n != 0 => ty.conservative_is_uninhabited(), - _ => false - } - } - // `ty::TyRef` is a grey area at the moment. _ => false } } diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 28e81800a7996..be6bf09a3759e 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -434,12 +434,14 @@ pub enum PlaceOp { /// wake). Tracked semi-automatically (through type variables marked /// as diverging), with some manual adjustments for control-flow /// primitives (approximating a CFG). +/// /// We know a node diverges in the following (conservative) situations: /// - A function with a parameter whose type is uninhabited necessarily diverges. /// - A match expression with no arms necessarily diverges. /// - A match expression whose arms patterns all diverge necessarily diverges. /// - A match expression whose arms all diverge necessarily diverges. /// - An expression whose type is uninhabited necessarily diverges. +/// /// In the above, the node will be marked as diverging `Always` or `WarnedAlways`. /// In any other situation, it will be marked as `Maybe`. #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] From 3daf17cb0307362bc44cbcea7924d52c0bb53440 Mon Sep 17 00:00:00 2001 From: varkor Date: Mon, 30 Apr 2018 22:53:54 +0100 Subject: [PATCH 14/19] Replace better_divergence_checking feature flag with exhaustive_patterns --- src/libcore/num/mod.rs | 1 + src/librustc_mir/hair/pattern/check_match.rs | 2 +- src/librustc_typeck/check/mod.rs | 4 +- src/libstd/error.rs | 1 + src/libstd/process.rs | 1 + ...ited-matches-better-divergence-checking.rs | 46 ------------------- src/test/ui/better_divergence_checking.rs | 2 +- 7 files changed, 7 insertions(+), 50 deletions(-) delete mode 100644 src/test/compile-fail/uninhabited-matches-better-divergence-checking.rs diff --git a/src/libcore/num/mod.rs b/src/libcore/num/mod.rs index 0b8f8f0703df7..ffd5437832288 100644 --- a/src/libcore/num/mod.rs +++ b/src/libcore/num/mod.rs @@ -4210,6 +4210,7 @@ impl fmt::Display for TryFromIntError { #[unstable(feature = "try_from", issue = "33417")] impl From for TryFromIntError { + #[allow(unreachable_code)] fn from(never: !) -> TryFromIntError { never } diff --git a/src/librustc_mir/hair/pattern/check_match.rs b/src/librustc_mir/hair/pattern/check_match.rs index c53ee45c5d76d..927193aa3631e 100644 --- a/src/librustc_mir/hair/pattern/check_match.rs +++ b/src/librustc_mir/hair/pattern/check_match.rs @@ -227,7 +227,7 @@ impl<'a, 'tcx> MatchVisitor<'a, 'tcx> { let scrutinee_is_uninhabited = if self.tcx.features().exhaustive_patterns { self.tcx.is_ty_uninhabited_from(module, pat_ty) } else { - if self.tcx.features().better_divergence_checking { + if self.tcx.features().exhaustive_patterns { pat_ty.conservative_is_uninhabited() } else { match pat_ty.sty { diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index be6bf09a3759e..ff70aeeb01164 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -1060,7 +1060,7 @@ fn check_fn<'a, 'gcx, 'tcx>(inherited: &'a Inherited<'a, 'gcx, 'tcx>, // If any of a function's parameters have a type that is uninhabited, then it // may never be called (because its arguments cannot be constructed). Therefore, // it must always diverge. - if fcx.tcx.features().better_divergence_checking { + if fcx.tcx.features().exhaustive_patterns { if arg_ty.conservative_is_uninhabited() { fcx.diverges.set(fcx.diverges.get() | Diverges::Always); } @@ -3673,7 +3673,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { // Any expression that produces a value of an uninhabited type must have diverged. if ty.conservative_is_uninhabited() { - if ty.is_never() || self.tcx.features().better_divergence_checking { + if ty.is_never() || self.tcx.features().exhaustive_patterns { self.diverges.set(self.diverges.get() | Diverges::Always); } } diff --git a/src/libstd/error.rs b/src/libstd/error.rs index 1958915602f83..3d6140125e2ce 100644 --- a/src/libstd/error.rs +++ b/src/libstd/error.rs @@ -309,6 +309,7 @@ impl Error for string::FromUtf16Error { #[stable(feature = "str_parse_error2", since = "1.8.0")] impl Error for string::ParseError { + #[allow(unreachable_code)] fn description(&self) -> &str { match *self {} } diff --git a/src/libstd/process.rs b/src/libstd/process.rs index 00051d4487a21..a0ac261a673f7 100644 --- a/src/libstd/process.rs +++ b/src/libstd/process.rs @@ -1477,6 +1477,7 @@ impl Termination for Result<(), E> { #[unstable(feature = "termination_trait_lib", issue = "43301")] impl Termination for ! { + #[allow(unreachable_code)] fn report(self) -> i32 { self } } diff --git a/src/test/compile-fail/uninhabited-matches-better-divergence-checking.rs b/src/test/compile-fail/uninhabited-matches-better-divergence-checking.rs deleted file mode 100644 index f514675f11cb3..0000000000000 --- a/src/test/compile-fail/uninhabited-matches-better-divergence-checking.rs +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright 2018 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -#![feature(better_divergence_checking)] - -enum Void {} - -fn main() { - let x: Result = Ok(23); - let _ = match x { //~ ERROR non-exhaustive - Ok(n) => n, - }; - - let x: &Void = unsafe { std::mem::uninitialized() }; - let _ = match x {}; //~ ERROR non-exhaustive - - let x: (Void,) = unsafe { std::mem::uninitialized() }; - let _ = match x {}; // okay - - let x: [Void; 1] = unsafe { std::mem::uninitialized() }; - let _ = match x {}; // okay - - let x: &[Void] = unsafe { std::mem::uninitialized() }; - let _ = match x { //~ ERROR non-exhaustive - &[] => (), - }; - - let x: Void = unsafe { std::mem::uninitialized() }; - let _ = match x {}; // okay - - let x: Result = Ok(23); - let _ = match x { //~ ERROR non-exhaustive - Ok(x) => x, - }; - - let x: Result = Ok(23); - let Ok(x) = x; - //~^ ERROR refutable -} diff --git a/src/test/ui/better_divergence_checking.rs b/src/test/ui/better_divergence_checking.rs index d2fab4f0d07de..c4e4f9c1f7da4 100644 --- a/src/test/ui/better_divergence_checking.rs +++ b/src/test/ui/better_divergence_checking.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(better_divergence_checking)] +#![feature(exhaustive_patterns)] #![deny(unreachable_code)] pub enum Void {} From 38c3e0d652dfb95bebd5171958f9ac9e24647d1b Mon Sep 17 00:00:00 2001 From: varkor Date: Tue, 1 May 2018 18:45:44 +0100 Subject: [PATCH 15/19] Fix unreachable-try-patterns --- src/librustc_codegen_llvm/declare.rs | 1 - src/test/compile-fail/unreachable-try-pattern.rs | 3 +++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/librustc_codegen_llvm/declare.rs b/src/librustc_codegen_llvm/declare.rs index d9ca8d55d91ee..6f774e82e6df9 100644 --- a/src/librustc_codegen_llvm/declare.rs +++ b/src/librustc_codegen_llvm/declare.rs @@ -23,7 +23,6 @@ use llvm::{self, ValueRef}; use llvm::AttributePlace::Function; use rustc::ty::{self, Ty}; -use rustc::ty::layout::{self, LayoutOf}; use rustc::session::config::Sanitizer; use rustc_target::spec::PanicStrategy; use abi::{Abi, FnType, FnTypeExt}; diff --git a/src/test/compile-fail/unreachable-try-pattern.rs b/src/test/compile-fail/unreachable-try-pattern.rs index df340095bb433..de6a38f25abc0 100644 --- a/src/test/compile-fail/unreachable-try-pattern.rs +++ b/src/test/compile-fail/unreachable-try-pattern.rs @@ -17,7 +17,9 @@ enum Void {} impl From for i32 { fn from(v: Void) -> i32 { + //~^ WARN unreachable expression match v {} + //~^ WARN unreachable expression } } @@ -39,6 +41,7 @@ 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 + //~| WARN unreachable expression Ok(y) } From b80a490be78e616ab50ab2aaccf4bab879dbe54d Mon Sep 17 00:00:00 2001 From: varkor Date: Sat, 5 May 2018 19:56:01 +0100 Subject: [PATCH 16/19] Empty diverging functions do not warn --- src/libcore/num/mod.rs | 1 - src/librustc_typeck/check/_match.rs | 9 ++++++--- src/librustc_typeck/check/mod.rs | 18 +++++++++++++++++- src/libstd/error.rs | 3 +-- src/libstd/process.rs | 1 - .../compile-fail/unreachable-try-pattern.rs | 1 - src/test/ui/better_divergence_checking.rs | 19 +++++++++++++++++-- src/test/ui/better_divergence_checking.stderr | 11 ++++++----- 8 files changed, 47 insertions(+), 16 deletions(-) diff --git a/src/libcore/num/mod.rs b/src/libcore/num/mod.rs index ffd5437832288..0b8f8f0703df7 100644 --- a/src/libcore/num/mod.rs +++ b/src/libcore/num/mod.rs @@ -4210,7 +4210,6 @@ impl fmt::Display for TryFromIntError { #[unstable(feature = "try_from", issue = "33417")] impl From for TryFromIntError { - #[allow(unreachable_code)] fn from(never: !) -> TryFromIntError { never } diff --git a/src/librustc_typeck/check/_match.rs b/src/librustc_typeck/check/_match.rs index b542393ff65f8..ffce81fc3b232 100644 --- a/src/librustc_typeck/check/_match.rs +++ b/src/librustc_typeck/check/_match.rs @@ -609,6 +609,7 @@ https://doc.rust-lang.org/reference/types.html#trait-objects"); let diverges = match self.diverges.get() { Diverges::Maybe | Diverges::Always => Diverges::Always, Diverges::WarnedAlways => Diverges::WarnedAlways, + Diverges::UnwarnedAlways => Diverges::UnwarnedAlways, }; self.diverges.set(diverges); return tcx.types.never; @@ -637,15 +638,17 @@ https://doc.rust-lang.org/reference/types.html#trait-objects"); match all_pats_diverge { Diverges::Maybe => Diverges::Maybe, Diverges::Always | Diverges::WarnedAlways => Diverges::WarnedAlways, + Diverges::UnwarnedAlways => Diverges::UnwarnedAlways, } }).collect(); // Now typecheck the blocks. // // The result of the match is the common supertype of all the - // arms. We start the value as the logical bottom , since it's the, - // well, bottom of the type lattice, and we'll be moving up the lattice - // as we process each arm. (Note that any match with 0 arms is matching + // arms. We start the value as the logical bottom (skipping + // `UnwarnedAlways`, which is a special case), since it's the, well, + // bottom of the type lattice, and we'll be moving up the lattice as + // we process each arm. (Note that any match with 0 arms is matching // on any empty type and is therefore unreachable; should the flow // of execution reach it, we will panic, so bottom is an appropriate // type in that case.) diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index ff70aeeb01164..18182fd7561db 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -457,6 +457,13 @@ pub enum Diverges { /// Same as `Always` but with a reachability /// warning already emitted. WarnedAlways, + + /// Same as `Always` but without a reachability + /// warning emitted. Unlike `Always`, cannot be + /// converted to `WarnedAlways`. Used when + /// unreachable code is expected (e.g. in + /// function parameters as part of trait impls). + UnwarnedAlways, } // Convenience impls for combining `Diverges`. @@ -1062,7 +1069,16 @@ fn check_fn<'a, 'gcx, 'tcx>(inherited: &'a Inherited<'a, 'gcx, 'tcx>, // it must always diverge. if fcx.tcx.features().exhaustive_patterns { if arg_ty.conservative_is_uninhabited() { - fcx.diverges.set(fcx.diverges.get() | Diverges::Always); + let mut diverges = Diverges::Always; + if let hir::Expr_::ExprBlock(ref block) = body.value.node { + // If the function is completely empty, or has a single trailing + // expression, then we do not issue a warning (as it was likely + // mandated by a trait, rather than being an oversight). + if block.stmts.is_empty() { + diverges = Diverges::UnwarnedAlways; + } + } + fcx.diverges.set(fcx.diverges.get() | diverges); } } diff --git a/src/libstd/error.rs b/src/libstd/error.rs index 3d6140125e2ce..99de84ea0bcff 100644 --- a/src/libstd/error.rs +++ b/src/libstd/error.rs @@ -309,9 +309,8 @@ impl Error for string::FromUtf16Error { #[stable(feature = "str_parse_error2", since = "1.8.0")] impl Error for string::ParseError { - #[allow(unreachable_code)] fn description(&self) -> &str { - match *self {} + match self {} } } diff --git a/src/libstd/process.rs b/src/libstd/process.rs index a0ac261a673f7..00051d4487a21 100644 --- a/src/libstd/process.rs +++ b/src/libstd/process.rs @@ -1477,7 +1477,6 @@ impl Termination for Result<(), E> { #[unstable(feature = "termination_trait_lib", issue = "43301")] impl Termination for ! { - #[allow(unreachable_code)] fn report(self) -> i32 { self } } diff --git a/src/test/compile-fail/unreachable-try-pattern.rs b/src/test/compile-fail/unreachable-try-pattern.rs index de6a38f25abc0..7b97a9b1a722f 100644 --- a/src/test/compile-fail/unreachable-try-pattern.rs +++ b/src/test/compile-fail/unreachable-try-pattern.rs @@ -17,7 +17,6 @@ enum Void {} impl From for i32 { fn from(v: Void) -> i32 { - //~^ WARN unreachable expression match v {} //~^ WARN unreachable expression } diff --git a/src/test/ui/better_divergence_checking.rs b/src/test/ui/better_divergence_checking.rs index c4e4f9c1f7da4..3a2cac1eccbf0 100644 --- a/src/test/ui/better_divergence_checking.rs +++ b/src/test/ui/better_divergence_checking.rs @@ -9,12 +9,27 @@ // except according to those terms. #![feature(exhaustive_patterns)] +#![feature(never_type)] #![deny(unreachable_code)] pub enum Void {} -pub fn uninhabited_parameter(_v: Void) { - //~^ ERROR unreachable expression +pub fn uninhabited_parameter_i(_v: Void) { + // A function with an uninhabited parameter + // is permitted if its body is empty. +} + +pub fn uninhabited_parameter_ii(v: !) -> i32 { + // A function with an uninhabited parameter + // is permitted if it simply returns a value + // as a trailing expression to satisfy the + // return type. + v +} + +pub fn uninhabited_parameter_iii(_v: Void, x: i32) -> i32 { + println!("Call me if you can!"); //~^ ERROR unreachable expression + x } fn main() {} diff --git a/src/test/ui/better_divergence_checking.stderr b/src/test/ui/better_divergence_checking.stderr index ff9a46719a017..5f21027e5479b 100644 --- a/src/test/ui/better_divergence_checking.stderr +++ b/src/test/ui/better_divergence_checking.stderr @@ -1,14 +1,15 @@ error: unreachable expression - --> $DIR/better_divergence_checking.rs:16:40 + --> $DIR/better_divergence_checking.rs:30:59 | -LL | pub fn uninhabited_parameter(_v: Void) { - | ________________________________________^ -LL | | //~^ ERROR unreachable expression +LL | pub fn uninhabited_parameter_iii(_v: Void, x: i32) -> i32 { + | ___________________________________________________________^ +LL | | println!("Call me if you can!"); //~^ ERROR unreachable expression +LL | | x LL | | } | |_^ | note: lint level defined here - --> $DIR/better_divergence_checking.rs:12:9 + --> $DIR/better_divergence_checking.rs:13:9 | LL | #![deny(unreachable_code)] | ^^^^^^^^^^^^^^^^ From 5efa7c93f5eded0c051fe4cbf75e9b8ed389cc5d Mon Sep 17 00:00:00 2001 From: varkor Date: Thu, 17 May 2018 20:49:00 +0100 Subject: [PATCH 17/19] ExprBlocks now have a label field --- src/librustc_typeck/check/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 18182fd7561db..445160271dfd1 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -1070,7 +1070,7 @@ fn check_fn<'a, 'gcx, 'tcx>(inherited: &'a Inherited<'a, 'gcx, 'tcx>, if fcx.tcx.features().exhaustive_patterns { if arg_ty.conservative_is_uninhabited() { let mut diverges = Diverges::Always; - if let hir::Expr_::ExprBlock(ref block) = body.value.node { + if let hir::Expr_::ExprBlock(ref block, _) = body.value.node { // If the function is completely empty, or has a single trailing // expression, then we do not issue a warning (as it was likely // mandated by a trait, rather than being an oversight). From b9ea7150dcfe107c3102784f5f259631b05caeff Mon Sep 17 00:00:00 2001 From: varkor Date: Mon, 23 Jul 2018 12:41:42 +0100 Subject: [PATCH 18/19] Fix rebase issues --- src/librustc_typeck/check/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 445160271dfd1..58abe30e6a32e 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -1070,7 +1070,7 @@ fn check_fn<'a, 'gcx, 'tcx>(inherited: &'a Inherited<'a, 'gcx, 'tcx>, if fcx.tcx.features().exhaustive_patterns { if arg_ty.conservative_is_uninhabited() { let mut diverges = Diverges::Always; - if let hir::Expr_::ExprBlock(ref block, _) = body.value.node { + if let hir::ExprKind::Block(ref block, _) = body.value.node { // If the function is completely empty, or has a single trailing // expression, then we do not issue a warning (as it was likely // mandated by a trait, rather than being an oversight). From 168d2a3cc078a3eed90a92e6534305b2386faeee Mon Sep 17 00:00:00 2001 From: varkor Date: Mon, 23 Jul 2018 12:47:13 +0100 Subject: [PATCH 19/19] Fix issues in comments --- src/librustc_mir/hair/pattern/check_match.rs | 12 ++++-------- src/librustc_typeck/check/mod.rs | 4 ++-- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/src/librustc_mir/hair/pattern/check_match.rs b/src/librustc_mir/hair/pattern/check_match.rs index 927193aa3631e..f3172ecafa8ba 100644 --- a/src/librustc_mir/hair/pattern/check_match.rs +++ b/src/librustc_mir/hair/pattern/check_match.rs @@ -227,14 +227,10 @@ impl<'a, 'tcx> MatchVisitor<'a, 'tcx> { let scrutinee_is_uninhabited = if self.tcx.features().exhaustive_patterns { self.tcx.is_ty_uninhabited_from(module, pat_ty) } else { - if self.tcx.features().exhaustive_patterns { - pat_ty.conservative_is_uninhabited() - } else { - match pat_ty.sty { - ty::TyNever => true, - ty::TyAdt(def, _) => def.variants.is_empty(), - _ => false - } + match pat_ty.sty { + ty::TyNever => true, + ty::TyAdt(def, _) => def.variants.is_empty(), + _ => false } }; if !scrutinee_is_uninhabited { diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 58abe30e6a32e..5fe562ef6d6f1 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -1065,8 +1065,8 @@ fn check_fn<'a, 'gcx, 'tcx>(inherited: &'a Inherited<'a, 'gcx, 'tcx>, ty::BindingMode::BindByValue(hir::Mutability::MutImmutable), true); // If any of a function's parameters have a type that is uninhabited, then it - // may never be called (because its arguments cannot be constructed). Therefore, - // it must always diverge. + // it is not possible to call that function (because its arguments cannot be constructed). + // Therefore, it must always diverge. if fcx.tcx.features().exhaustive_patterns { if arg_ty.conservative_is_uninhabited() { let mut diverges = Diverges::Always;