diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs index bb5c7b5fd2a5e..99bc665fcc8f2 100644 --- a/src/librustc/ty/sty.rs +++ b/src/librustc/ty/sty.rs @@ -1283,6 +1283,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/build/expr/into.rs b/src/librustc_mir/build/expr/into.rs index 28dc329e4fe7c..c4f8fca741151 100644 --- a/src/librustc_mir/build/expr/into.rs +++ b/src/librustc_mir/build/expr/into.rs @@ -27,8 +27,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { expr: Expr<'tcx>) -> BlockAnd<()> { - debug!("into_expr(destination={:?}, block={:?}, expr={:?})", - destination, block, expr); + debug!("into_expr(destination={:?}, block={:?}, expr_kind={:?}, expr={:?})", + destination, block, expr.kind, expr); // since we frequently have to reference `self` from within a // closure, where `self` would be shadowed, it's easier to @@ -317,9 +317,19 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { _ => true, }); + let ty = expr.ty; let rvalue = unpack!(block = this.as_local_rvalue(block, expr)); - this.cfg.push_assign(block, source_info, destination, rvalue); - block.unit() + + if ty.conservative_is_uninhabited() { + // It's impossible to have an rvalue of type `!`, so if we encounter one, + // we can terminate the block as unreachable immediately. + this.cfg.terminate(block, source_info, TerminatorKind::Unreachable); + let end_block = this.cfg.start_new_block(); + end_block.unit() + } else { + this.cfg.push_assign(block, source_info, destination, rvalue); + block.unit() + } } } } diff --git a/src/librustc_mir/build/matches/mod.rs b/src/librustc_mir/build/matches/mod.rs index 229e33dcd7862..55487745460bc 100644 --- a/src/librustc_mir/build/matches/mod.rs +++ b/src/librustc_mir/build/matches/mod.rs @@ -54,6 +54,12 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { let dummy_temp = self.temp(dummy_ty, dummy_source_info.span); self.cfg.push_assign(block, dummy_source_info, &dummy_temp, dummy_access); + let arms: Vec> = arms.into_iter().filter_map(|mut arm| { + arm.patterns = + arm.patterns.iter().filter(|pat| !pat.is_unreachable()).cloned().collect(); + if !arm.patterns.is_empty() { Some(arm) } else { None } + }).collect(); + let mut arm_blocks = ArmBlocks { blocks: arms.iter() .map(|_| self.cfg.start_new_block()) diff --git a/src/librustc_mir/build/mod.rs b/src/librustc_mir/build/mod.rs index 8494c043f90fc..c6bfcaa000adc 100644 --- a/src/librustc_mir/build/mod.rs +++ b/src/librustc_mir/build/mod.rs @@ -623,6 +623,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { } let mut scope = None; + let mut unreachable_arguments = false; // Bind the argument patterns for (index, &(ty, pattern)) in arguments.iter().enumerate() { // Function arguments always get the first Local indices after the return place @@ -632,6 +633,11 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { if let Some(pattern) = pattern { let pattern = self.hir.pattern_from_hir(pattern); + if pattern.is_unreachable() { + unreachable_arguments = true; + break; + } + match *pattern.kind { // Don't introduce extra copies for simple bindings PatternKind::Binding { mutability, var, mode: BindingMode::ByValue, .. } => { @@ -649,7 +655,6 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { // Make sure we drop (parts of) the argument even when not matched on. self.schedule_drop(pattern.as_ref().map_or(ast_body.span, |pat| pat.span), argument_scope, &place, ty); - } // Enter the argument pattern bindings visibility scope, if it exists. @@ -658,6 +663,12 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { } let body = self.hir.mirror(ast_body); + if unreachable_arguments { + let source_info = self.source_info(body.span); + self.cfg.terminate(block, source_info, TerminatorKind::Unreachable); + let end_block = self.cfg.start_new_block(); + return end_block.unit(); + } self.into(&Place::Local(RETURN_PLACE), block, body) } diff --git a/src/librustc_mir/hair/pattern/check_match.rs b/src/librustc_mir/hair/pattern/check_match.rs index d924baaf00521..055561008a124 100644 --- a/src/librustc_mir/hair/pattern/check_match.rs +++ b/src/librustc_mir/hair/pattern/check_match.rs @@ -225,7 +225,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 @@ -253,15 +253,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_mir/hair/pattern/mod.rs b/src/librustc_mir/hair/pattern/mod.rs index eb87d5b044b45..24eafc2b62538 100644 --- a/src/librustc_mir/hair/pattern/mod.rs +++ b/src/librustc_mir/hair/pattern/mod.rs @@ -65,6 +65,7 @@ pub struct Pattern<'tcx> { #[derive(Clone, Debug)] pub enum PatternKind<'tcx> { + /// Wildcard `_` Wild, /// x, ref x, x @ P, etc @@ -306,6 +307,39 @@ impl<'a, 'tcx> Pattern<'tcx> { debug!("Pattern::from_hir({:?}) = {:?}", pat, result); result } + + // Returns true if the pattern cannot bind, as it would require a value of type `!` to have + // been constructed. This check is conservative. + pub fn is_unreachable(&self) -> bool { + if self.ty.conservative_is_uninhabited() { + return true; + } + match *self.kind { + PatternKind::Binding { ty, ref subpattern, .. } => { + if ty.conservative_is_uninhabited() { + return true; + } + if let &Some(ref subpattern) = subpattern { + subpattern.is_unreachable() + } else { false } + }, + PatternKind::Variant { ref subpatterns, .. } | + PatternKind::Leaf { ref subpatterns } => { + subpatterns.iter().any(|field_pattern| + field_pattern.pattern.is_unreachable()) + }, + PatternKind::Deref { ref subpattern } => { + subpattern.is_unreachable() + }, + PatternKind::Slice { ref prefix, ref slice, ref suffix } | + PatternKind::Array { ref prefix, ref slice, ref suffix } => { + prefix.iter().any(|pat| pat.is_unreachable()) || + slice.iter().any(|pat| pat.is_unreachable()) || + suffix.iter().any(|pat| pat.is_unreachable()) + }, + _ => false + } + } } impl<'a, 'tcx> PatternContext<'a, 'tcx> { diff --git a/src/test/compile-fail/uninhabited-matches-feature-gated.rs b/src/test/compile-fail/uninhabited-matches-feature-gated.rs index 1d3f8ff12d865..f0cc8ed6333fd 100644 --- a/src/test/compile-fail/uninhabited-matches-feature-gated.rs +++ b/src/test/compile-fail/uninhabited-matches-feature-gated.rs @@ -19,9 +19,6 @@ fn main() { let x: &Void = unsafe { std::mem::uninitialized() }; let _ = match x {}; //~ ERROR non-exhaustive - 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 @@ -30,9 +27,6 @@ fn main() { &[] => (), }; - 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, diff --git a/src/test/mir-opt/never_type_unreachable.rs b/src/test/mir-opt/never_type_unreachable.rs new file mode 100644 index 0000000000000..eec5db9ca9b4c --- /dev/null +++ b/src/test/mir-opt/never_type_unreachable.rs @@ -0,0 +1,120 @@ +// Copyright 2017 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(never_type)] +#![allow(dead_code)] +#![allow(path_statements)] +#![allow(unreachable_patterns)] + +fn never_direct(x: !) { + x; +} + +fn never_ref_pat(ref x: !) { + *x; +} + +fn never_ref(x: &!) { + let &y = x; + y; +} + +fn never_slice(x: &[!]) { + x[0]; +} + +fn never_match(x: Result<(), !>) { + match x { + Ok(_) => {}, + Err(_) => {}, + } +} + +fn never_match_disj_patterns() { + let x: Option = None; + match x { + Some(_) | None => {} + } +} + +pub fn main() { } + +// END RUST SOURCE + +// START rustc.never_direct.SimplifyCfg-initial.after.mir +// bb0: { +// unreachable; +// } +// END rustc.never_direct.SimplifyCfg-initial.after.mir + +// START rustc.never_ref_pat.SimplifyCfg-initial.after.mir +// bb0: { +// unreachable; +// } +// END rustc.never_ref_pat.SimplifyCfg-initial.after.mir + +// START rustc.never_ref.SimplifyCfg-initial.after.mir +// bb0: { +// ... +// unreachable; +// } +// END rustc.never_ref.SimplifyCfg-initial.after.mir + +// START rustc.never_slice.SimplifyCfg-initial.after.mir +// bb1: { +// ... +// unreachable; +// } +// END rustc.never_slice.SimplifyCfg-initial.after.mir + +// START rustc.never_match.SimplifyCfg-initial.after.mir +// fn never_match(_1: std::result::Result<(), !>) -> () { +// ... +// bb0: { +// ... +// } +// bb1: { +// _0 = (); +// return; +// } +// bb2: { +// ... +// } +// bb3: { +// unreachable; +// } +// bb4: { +// unreachable; +// } +// } +// END rustc.never_match.SimplifyCfg-initial.after.mir + +// START rustc.never_match_disj_patterns.SimplifyCfg-initial.after.mir +// fn never_match_disj_patterns() -> () { +// ... +// bb0: { +// ... +// } +// bb1: { +// _0 = (); +// StorageDead(_1); +// return; +// } +// bb2: { +// ... +// } +// bb3: { +// unreachable; +// } +// bb4: { +// unreachable; +// } +// } +// END rustc.never_match_disj_patterns.SimplifyCfg-initial.after.mir