Skip to content

Commit 7463e4a

Browse files
committed
Auto merge of #47291 - varkor:unreachable-never-patterns, r=<try>
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
2 parents 75af15e + 741c8ab commit 7463e4a

File tree

8 files changed

+197
-21
lines changed

8 files changed

+197
-21
lines changed

src/librustc/ty/sty.rs

+10
Original file line numberDiff line numberDiff line change
@@ -1283,6 +1283,16 @@ impl<'a, 'gcx, 'tcx> TyS<'tcx> {
12831283
}
12841284
}
12851285

1286+
pub fn conservative_is_uninhabited(&self) -> bool {
1287+
// "rustc-1.0-style" uncontentious uninhabitableness check
1288+
match self.sty {
1289+
ty::TyNever => true,
1290+
ty::TyAdt(def, _) => def.variants.is_empty(),
1291+
ty::TyTuple(tys) => tys.iter().any(|ty| ty.conservative_is_uninhabited()),
1292+
_ => false
1293+
}
1294+
}
1295+
12861296
pub fn is_primitive(&self) -> bool {
12871297
match self.sty {
12881298
TyBool | TyChar | TyInt(_) | TyUint(_) | TyFloat(_) => true,

src/librustc_mir/build/expr/into.rs

+14-4
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
2727
expr: Expr<'tcx>)
2828
-> BlockAnd<()>
2929
{
30-
debug!("into_expr(destination={:?}, block={:?}, expr={:?})",
31-
destination, block, expr);
30+
debug!("into_expr(destination={:?}, block={:?}, expr_kind={:?}, expr={:?})",
31+
destination, block, expr.kind, expr);
3232

3333
// since we frequently have to reference `self` from within a
3434
// closure, where `self` would be shadowed, it's easier to
@@ -317,9 +317,19 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
317317
_ => true,
318318
});
319319

320+
let ty = expr.ty;
320321
let rvalue = unpack!(block = this.as_local_rvalue(block, expr));
321-
this.cfg.push_assign(block, source_info, destination, rvalue);
322-
block.unit()
322+
323+
if ty.conservative_is_uninhabited() {
324+
// It's impossible to have an rvalue of type `!`, so if we encounter one,
325+
// we can terminate the block as unreachable immediately.
326+
this.cfg.terminate(block, source_info, TerminatorKind::Unreachable);
327+
let end_block = this.cfg.start_new_block();
328+
end_block.unit()
329+
} else {
330+
this.cfg.push_assign(block, source_info, destination, rvalue);
331+
block.unit()
332+
}
323333
}
324334
}
325335
}

src/librustc_mir/build/matches/mod.rs

+6
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,12 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
5454
let dummy_temp = self.temp(dummy_ty, dummy_source_info.span);
5555
self.cfg.push_assign(block, dummy_source_info, &dummy_temp, dummy_access);
5656

57+
let arms: Vec<Arm<'tcx>> = arms.into_iter().filter_map(|mut arm| {
58+
arm.patterns =
59+
arm.patterns.iter().filter(|pat| !pat.is_unreachable()).cloned().collect();
60+
if !arm.patterns.is_empty() { Some(arm) } else { None }
61+
}).collect();
62+
5763
let mut arm_blocks = ArmBlocks {
5864
blocks: arms.iter()
5965
.map(|_| self.cfg.start_new_block())

src/librustc_mir/build/mod.rs

+12-1
Original file line numberDiff line numberDiff line change
@@ -623,6 +623,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
623623
}
624624

625625
let mut scope = None;
626+
let mut unreachable_arguments = false;
626627
// Bind the argument patterns
627628
for (index, &(ty, pattern)) in arguments.iter().enumerate() {
628629
// Function arguments always get the first Local indices after the return place
@@ -632,6 +633,11 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
632633
if let Some(pattern) = pattern {
633634
let pattern = self.hir.pattern_from_hir(pattern);
634635

636+
if pattern.is_unreachable() {
637+
unreachable_arguments = true;
638+
break;
639+
}
640+
635641
match *pattern.kind {
636642
// Don't introduce extra copies for simple bindings
637643
PatternKind::Binding { mutability, var, mode: BindingMode::ByValue, .. } => {
@@ -649,7 +655,6 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
649655
// Make sure we drop (parts of) the argument even when not matched on.
650656
self.schedule_drop(pattern.as_ref().map_or(ast_body.span, |pat| pat.span),
651657
argument_scope, &place, ty);
652-
653658
}
654659

655660
// Enter the argument pattern bindings visibility scope, if it exists.
@@ -658,6 +663,12 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
658663
}
659664

660665
let body = self.hir.mirror(ast_body);
666+
if unreachable_arguments {
667+
let source_info = self.source_info(body.span);
668+
self.cfg.terminate(block, source_info, TerminatorKind::Unreachable);
669+
let end_block = self.cfg.start_new_block();
670+
return end_block.unit();
671+
}
661672
self.into(&Place::Local(RETURN_PLACE), block, body)
662673
}
663674

src/librustc_mir/hair/pattern/check_match.rs

+1-10
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,7 @@ impl<'a, 'tcx> MatchVisitor<'a, 'tcx> {
225225
let scrutinee_is_uninhabited = if self.tcx.features().exhaustive_patterns {
226226
self.tcx.is_ty_uninhabited_from(module, pat_ty)
227227
} else {
228-
self.conservative_is_uninhabited(pat_ty)
228+
pat_ty.conservative_is_uninhabited()
229229
};
230230
if !scrutinee_is_uninhabited {
231231
// We know the type is inhabited, so this must be wrong
@@ -253,15 +253,6 @@ impl<'a, 'tcx> MatchVisitor<'a, 'tcx> {
253253
})
254254
}
255255

256-
fn conservative_is_uninhabited(&self, scrutinee_ty: Ty<'tcx>) -> bool {
257-
// "rustc-1.0-style" uncontentious uninhabitableness check
258-
match scrutinee_ty.sty {
259-
ty::TyNever => true,
260-
ty::TyAdt(def, _) => def.variants.is_empty(),
261-
_ => false
262-
}
263-
}
264-
265256
fn check_irrefutable(&self, pat: &'tcx Pat, origin: &str) {
266257
let module = self.tcx.hir.get_module_parent(pat.id);
267258
MatchCheckCtxt::create_and_enter(self.tcx, module, |ref mut cx| {

src/librustc_mir/hair/pattern/mod.rs

+34
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ pub struct Pattern<'tcx> {
6565

6666
#[derive(Clone, Debug)]
6767
pub enum PatternKind<'tcx> {
68+
/// Wildcard `_`
6869
Wild,
6970

7071
/// x, ref x, x @ P, etc
@@ -306,6 +307,39 @@ impl<'a, 'tcx> Pattern<'tcx> {
306307
debug!("Pattern::from_hir({:?}) = {:?}", pat, result);
307308
result
308309
}
310+
311+
// Returns true if the pattern cannot bind, as it would require a value of type `!` to have
312+
// been constructed. This check is conservative.
313+
pub fn is_unreachable(&self) -> bool {
314+
if self.ty.conservative_is_uninhabited() {
315+
return true;
316+
}
317+
match *self.kind {
318+
PatternKind::Binding { ty, ref subpattern, .. } => {
319+
if ty.conservative_is_uninhabited() {
320+
return true;
321+
}
322+
if let &Some(ref subpattern) = subpattern {
323+
subpattern.is_unreachable()
324+
} else { false }
325+
},
326+
PatternKind::Variant { ref subpatterns, .. } |
327+
PatternKind::Leaf { ref subpatterns } => {
328+
subpatterns.iter().any(|field_pattern|
329+
field_pattern.pattern.is_unreachable())
330+
},
331+
PatternKind::Deref { ref subpattern } => {
332+
subpattern.is_unreachable()
333+
},
334+
PatternKind::Slice { ref prefix, ref slice, ref suffix } |
335+
PatternKind::Array { ref prefix, ref slice, ref suffix } => {
336+
prefix.iter().any(|pat| pat.is_unreachable()) ||
337+
slice.iter().any(|pat| pat.is_unreachable()) ||
338+
suffix.iter().any(|pat| pat.is_unreachable())
339+
},
340+
_ => false
341+
}
342+
}
309343
}
310344

311345
impl<'a, 'tcx> PatternContext<'a, 'tcx> {

src/test/compile-fail/uninhabited-matches-feature-gated.rs

-6
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,6 @@ fn main() {
1919
let x: &Void = unsafe { std::mem::uninitialized() };
2020
let _ = match x {}; //~ ERROR non-exhaustive
2121

22-
let x: (Void,) = unsafe { std::mem::uninitialized() };
23-
let _ = match x {}; //~ ERROR non-exhaustive
24-
2522
let x: [Void; 1] = unsafe { std::mem::uninitialized() };
2623
let _ = match x {}; //~ ERROR non-exhaustive
2724

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

33-
let x: Void = unsafe { std::mem::uninitialized() };
34-
let _ = match x {}; // okay
35-
3630
let x: Result<u32, Void> = Ok(23);
3731
let _ = match x { //~ ERROR non-exhaustive
3832
Ok(x) => x,
+120
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
#![feature(never_type)]
12+
#![allow(dead_code)]
13+
#![allow(path_statements)]
14+
#![allow(unreachable_patterns)]
15+
16+
fn never_direct(x: !) {
17+
x;
18+
}
19+
20+
fn never_ref_pat(ref x: !) {
21+
*x;
22+
}
23+
24+
fn never_ref(x: &!) {
25+
let &y = x;
26+
y;
27+
}
28+
29+
fn never_slice(x: &[!]) {
30+
x[0];
31+
}
32+
33+
fn never_match(x: Result<(), !>) {
34+
match x {
35+
Ok(_) => {},
36+
Err(_) => {},
37+
}
38+
}
39+
40+
fn never_match_disj_patterns() {
41+
let x: Option<!> = None;
42+
match x {
43+
Some(_) | None => {}
44+
}
45+
}
46+
47+
pub fn main() { }
48+
49+
// END RUST SOURCE
50+
51+
// START rustc.never_direct.SimplifyCfg-initial.after.mir
52+
// bb0: {
53+
// unreachable;
54+
// }
55+
// END rustc.never_direct.SimplifyCfg-initial.after.mir
56+
57+
// START rustc.never_ref_pat.SimplifyCfg-initial.after.mir
58+
// bb0: {
59+
// unreachable;
60+
// }
61+
// END rustc.never_ref_pat.SimplifyCfg-initial.after.mir
62+
63+
// START rustc.never_ref.SimplifyCfg-initial.after.mir
64+
// bb0: {
65+
// ...
66+
// unreachable;
67+
// }
68+
// END rustc.never_ref.SimplifyCfg-initial.after.mir
69+
70+
// START rustc.never_slice.SimplifyCfg-initial.after.mir
71+
// bb1: {
72+
// ...
73+
// unreachable;
74+
// }
75+
// END rustc.never_slice.SimplifyCfg-initial.after.mir
76+
77+
// START rustc.never_match.SimplifyCfg-initial.after.mir
78+
// fn never_match(_1: std::result::Result<(), !>) -> () {
79+
// ...
80+
// bb0: {
81+
// ...
82+
// }
83+
// bb1: {
84+
// _0 = ();
85+
// return;
86+
// }
87+
// bb2: {
88+
// ...
89+
// }
90+
// bb3: {
91+
// unreachable;
92+
// }
93+
// bb4: {
94+
// unreachable;
95+
// }
96+
// }
97+
// END rustc.never_match.SimplifyCfg-initial.after.mir
98+
99+
// START rustc.never_match_disj_patterns.SimplifyCfg-initial.after.mir
100+
// fn never_match_disj_patterns() -> () {
101+
// ...
102+
// bb0: {
103+
// ...
104+
// }
105+
// bb1: {
106+
// _0 = ();
107+
// StorageDead(_1);
108+
// return;
109+
// }
110+
// bb2: {
111+
// ...
112+
// }
113+
// bb3: {
114+
// unreachable;
115+
// }
116+
// bb4: {
117+
// unreachable;
118+
// }
119+
// }
120+
// END rustc.never_match_disj_patterns.SimplifyCfg-initial.after.mir

0 commit comments

Comments
 (0)