Skip to content

Commit

Permalink
Auto merge of #47291 - varkor:unreachable-never-patterns, r=<try>
Browse files Browse the repository at this point in the history
Eager unreachable blocks for `!` type

This change optimises the generation of MIR in the presence of `!`. Blocks are now immediately marked as unreachable if:
- A function argument is derived from the value of type `!`.
- A match arm binds a value whose type is derived from `!`.
- An rvalue is created whose type is derived from `!`.

This avoids unnecessary MIR generation and also fixes #43061 and #41446.

r? @nikomatsakis
  • Loading branch information
bors committed Mar 20, 2018
2 parents 75af15e + 741c8ab commit 7463e4a
Show file tree
Hide file tree
Showing 8 changed files with 197 additions and 21 deletions.
10 changes: 10 additions & 0 deletions src/librustc/ty/sty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
18 changes: 14 additions & 4 deletions src/librustc_mir/build/expr/into.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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()
}
}
}
}
Expand Down
6 changes: 6 additions & 0 deletions src/librustc_mir/build/matches/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Arm<'tcx>> = 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())
Expand Down
13 changes: 12 additions & 1 deletion src/librustc_mir/build/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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, .. } => {
Expand All @@ -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.
Expand All @@ -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)
}

Expand Down
11 changes: 1 addition & 10 deletions src/librustc_mir/hair/pattern/check_match.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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| {
Expand Down
34 changes: 34 additions & 0 deletions src/librustc_mir/hair/pattern/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ pub struct Pattern<'tcx> {

#[derive(Clone, Debug)]
pub enum PatternKind<'tcx> {
/// Wildcard `_`
Wild,

/// x, ref x, x @ P, etc
Expand Down Expand Up @@ -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> {
Expand Down
6 changes: 0 additions & 6 deletions src/test/compile-fail/uninhabited-matches-feature-gated.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -30,9 +27,6 @@ fn main() {
&[] => (),
};

let x: Void = unsafe { std::mem::uninitialized() };
let _ = match x {}; // okay

let x: Result<u32, Void> = Ok(23);
let _ = match x { //~ ERROR non-exhaustive
Ok(x) => x,
Expand Down
120 changes: 120 additions & 0 deletions src/test/mir-opt/never_type_unreachable.rs
Original file line number Diff line number Diff line change
@@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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

0 comments on commit 7463e4a

Please sign in to comment.