Skip to content

Commit 798be90

Browse files
arielb1Ariel Ben-Yehuda
authored and
Ariel Ben-Yehuda
committed
introduce an unreachable terminator
Use it instead of a `panic` for inexhaustive matches and correct the comment. I think we trust our match-generation algorithm enough to generate these blocks, and not generating an `unreachable` means that LLVM won't optimize `match void() {}` to an `unreachable`.
1 parent 6405527 commit 798be90

File tree

11 files changed

+37
-92
lines changed

11 files changed

+37
-92
lines changed

src/librustc/mir/repr.rs

+7-1
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,9 @@ pub enum TerminatorKind<'tcx> {
374374
/// have been filled in by now. This should occur at most once.
375375
Return,
376376

377+
/// Indicates a terminator that can never be reached.
378+
Unreachable,
379+
377380
/// Drop the Lvalue
378381
Drop {
379382
location: Lvalue<'tcx>,
@@ -432,6 +435,7 @@ impl<'tcx> TerminatorKind<'tcx> {
432435
SwitchInt { targets: ref b, .. } => b[..].into_cow(),
433436
Resume => (&[]).into_cow(),
434437
Return => (&[]).into_cow(),
438+
Unreachable => (&[]).into_cow(),
435439
Call { destination: Some((_, t)), cleanup: Some(c), .. } => vec![t, c].into_cow(),
436440
Call { destination: Some((_, ref t)), cleanup: None, .. } =>
437441
slice::ref_slice(t).into_cow(),
@@ -461,6 +465,7 @@ impl<'tcx> TerminatorKind<'tcx> {
461465
SwitchInt { targets: ref mut b, .. } => b.iter_mut().collect(),
462466
Resume => Vec::new(),
463467
Return => Vec::new(),
468+
Unreachable => Vec::new(),
464469
Call { destination: Some((_, ref mut t)), cleanup: Some(ref mut c), .. } => vec![t, c],
465470
Call { destination: Some((_, ref mut t)), cleanup: None, .. } => vec![t],
466471
Call { destination: None, cleanup: Some(ref mut c), .. } => vec![c],
@@ -539,6 +544,7 @@ impl<'tcx> TerminatorKind<'tcx> {
539544
SwitchInt { discr: ref lv, .. } => write!(fmt, "switchInt({:?})", lv),
540545
Return => write!(fmt, "return"),
541546
Resume => write!(fmt, "resume"),
547+
Unreachable => write!(fmt, "unreachable"),
542548
Drop { ref location, .. } => write!(fmt, "drop({:?})", location),
543549
DropAndReplace { ref location, ref value, .. } =>
544550
write!(fmt, "replace({:?} <- {:?})", location, value),
@@ -582,7 +588,7 @@ impl<'tcx> TerminatorKind<'tcx> {
582588
pub fn fmt_successor_labels(&self) -> Vec<Cow<'static, str>> {
583589
use self::TerminatorKind::*;
584590
match *self {
585-
Return | Resume => vec![],
591+
Return | Resume | Unreachable => vec![],
586592
Goto { .. } => vec!["".into()],
587593
If { .. } => vec!["true".into(), "false".into()],
588594
Switch { ref adt_def, .. } => {

src/librustc/mir/visit.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -386,7 +386,8 @@ macro_rules! make_mir_visitor {
386386
}
387387

388388
TerminatorKind::Resume |
389-
TerminatorKind::Return => {
389+
TerminatorKind::Return |
390+
TerminatorKind::Unreachable => {
390391
}
391392

392393
TerminatorKind::Drop { ref $($mutability)* location,

src/librustc_borrowck/borrowck/mir/dataflow/mod.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -449,7 +449,8 @@ impl<'a, 'tcx: 'a, D> DataflowAnalysis<'a, 'tcx, D>
449449
{
450450
match bb_data.terminator().kind {
451451
repr::TerminatorKind::Return |
452-
repr::TerminatorKind::Resume => {}
452+
repr::TerminatorKind::Resume |
453+
repr::TerminatorKind::Unreachable => {}
453454
repr::TerminatorKind::Goto { ref target } |
454455
repr::TerminatorKind::Assert { ref target, cleanup: None, .. } |
455456
repr::TerminatorKind::Drop { ref target, location: _, unwind: None } |

src/librustc_borrowck/borrowck/mir/gather_moves.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -621,7 +621,9 @@ fn gather_moves<'a, 'tcx>(mir: &Mir<'tcx>, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> MoveD
621621

622622
debug!("gather_moves({:?})", bb_data.terminator());
623623
match bb_data.terminator().kind {
624-
TerminatorKind::Goto { target: _ } | TerminatorKind::Resume => { }
624+
TerminatorKind::Goto { target: _ } |
625+
TerminatorKind::Resume |
626+
TerminatorKind::Unreachable => { }
625627

626628
TerminatorKind::Return => {
627629
let source = Location { block: bb,

src/librustc_mir/build/matches/mod.rs

+11-5
Original file line numberDiff line numberDiff line change
@@ -78,12 +78,18 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
7878
// branch to the appropriate arm block
7979
let otherwise = self.match_candidates(span, &mut arm_blocks, candidates, block);
8080

81-
// because all matches are exhaustive, in principle we expect
82-
// an empty vector to be returned here, but the algorithm is
83-
// not entirely precise
8481
if !otherwise.is_empty() {
85-
let join_block = self.join_otherwise_blocks(span, otherwise);
86-
self.panic(join_block, "something about matches algorithm not being precise", span);
82+
// All matches are exhaustive. However, because some matches
83+
// only have exponentially-large exhaustive decision trees, we
84+
// sometimes generate an inexhaustive decision tree.
85+
//
86+
// In that case, the inexhaustive tips of the decision tree
87+
// can't be reached - terminate them with an `unreachable`.
88+
let source_info = self.source_info(span);
89+
90+
for block in otherwise {
91+
self.cfg.terminate(block, source_info, TerminatorKind::Unreachable);
92+
}
8793
}
8894

8995
// all the arm blocks will rejoin here

src/librustc_mir/build/scope.rs

+2-82
Original file line numberDiff line numberDiff line change
@@ -90,12 +90,9 @@ use build::{BlockAnd, BlockAndExtension, Builder, CFG, ScopeAuxiliary, ScopeId};
9090
use rustc::middle::region::{CodeExtent, CodeExtentData};
9191
use rustc::middle::lang_items;
9292
use rustc::ty::subst::{Substs, Subst, VecPerParamSpace};
93-
use rustc::ty::{self, Ty, TyCtxt};
93+
use rustc::ty::{Ty, TyCtxt};
9494
use rustc::mir::repr::*;
95-
use syntax::codemap::{Span, DUMMY_SP};
96-
use syntax::parse::token::intern_and_get_ident;
97-
use rustc::middle::const_val::ConstVal;
98-
use rustc_const_math::ConstInt;
95+
use syntax::codemap::Span;
9996
use rustc_data_structures::indexed_vec::Idx;
10097

10198
pub struct Scope<'tcx> {
@@ -556,50 +553,6 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
556553
next_target.unit()
557554
}
558555

559-
/// Create diverge cleanup and branch to it from `block`.
560-
// FIXME: Remove this (used only for unreachable cases in match).
561-
pub fn panic(&mut self, block: BasicBlock, msg: &'static str, span: Span) {
562-
// fn(&(msg: &'static str filename: &'static str, line: u32)) -> !
563-
let region = ty::ReStatic; // FIXME(mir-borrowck): use a better region?
564-
let func = self.lang_function(lang_items::PanicFnLangItem);
565-
let args = self.hir.tcx().replace_late_bound_regions(&func.ty.fn_args(), |_| region).0;
566-
567-
let ref_ty = args[0];
568-
let tup_ty = if let ty::TyRef(_, tyandmut) = ref_ty.sty {
569-
tyandmut.ty
570-
} else {
571-
span_bug!(span, "unexpected panic type: {:?}", func.ty);
572-
};
573-
574-
let (tuple, tuple_ref) = (self.temp(tup_ty), self.temp(ref_ty));
575-
let (file, line) = self.span_to_fileline_args(span);
576-
let message = Constant {
577-
span: span,
578-
ty: self.hir.tcx().mk_static_str(),
579-
literal: self.hir.str_literal(intern_and_get_ident(msg))
580-
};
581-
let elems = vec![Operand::Constant(message),
582-
Operand::Constant(file),
583-
Operand::Constant(line)];
584-
let source_info = self.source_info(span);
585-
// FIXME: We should have this as a constant, rather than a stack variable (to not pollute
586-
// icache with cold branch code), however to achieve that we either have to rely on rvalue
587-
// promotion or have some way, in MIR, to create constants.
588-
self.cfg.push_assign(block, source_info, &tuple, // [1]
589-
Rvalue::Aggregate(AggregateKind::Tuple, elems));
590-
// [1] tuple = (message_arg, file_arg, line_arg);
591-
// FIXME: is this region really correct here?
592-
self.cfg.push_assign(block, source_info, &tuple_ref, // tuple_ref = &tuple;
593-
Rvalue::Ref(region, BorrowKind::Shared, tuple));
594-
let cleanup = self.diverge_cleanup();
595-
self.cfg.terminate(block, source_info, TerminatorKind::Call {
596-
func: Operand::Constant(func),
597-
args: vec![Operand::Consume(tuple_ref)],
598-
cleanup: cleanup,
599-
destination: None,
600-
});
601-
}
602-
603556
/// Create an Assert terminator and return the success block.
604557
/// If the boolean condition operand is not the expected value,
605558
/// a runtime panic will be caused with the given message.
@@ -625,39 +578,6 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
625578

626579
success_block
627580
}
628-
629-
fn lang_function(&mut self, lang_item: lang_items::LangItem) -> Constant<'tcx> {
630-
let funcdid = match self.hir.tcx().lang_items.require(lang_item) {
631-
Ok(d) => d,
632-
Err(m) => {
633-
self.hir.tcx().sess.fatal(&m)
634-
}
635-
};
636-
Constant {
637-
span: DUMMY_SP,
638-
ty: self.hir.tcx().lookup_item_type(funcdid).ty,
639-
literal: Literal::Item {
640-
def_id: funcdid,
641-
substs: self.hir.tcx().mk_substs(Substs::empty())
642-
}
643-
}
644-
}
645-
646-
fn span_to_fileline_args(&mut self, span: Span) -> (Constant<'tcx>, Constant<'tcx>) {
647-
let span_lines = self.hir.tcx().sess.codemap().lookup_char_pos(span.lo);
648-
(Constant {
649-
span: span,
650-
ty: self.hir.tcx().mk_static_str(),
651-
literal: self.hir.str_literal(intern_and_get_ident(&span_lines.file.name))
652-
}, Constant {
653-
span: span,
654-
ty: self.hir.tcx().types.u32,
655-
literal: Literal::Value {
656-
value: ConstVal::Integral(ConstInt::U32(span_lines.line as u32)),
657-
},
658-
})
659-
}
660-
661581
}
662582

663583
/// Builds drops for pop_scope and exit_scope.

src/librustc_mir/transform/no_landing_pads.rs

+1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ impl<'tcx> MutVisitor<'tcx> for NoLandingPads {
2424
TerminatorKind::Goto { .. } |
2525
TerminatorKind::Resume |
2626
TerminatorKind::Return |
27+
TerminatorKind::Unreachable |
2728
TerminatorKind::If { .. } |
2829
TerminatorKind::Switch { .. } |
2930
TerminatorKind::SwitchInt { .. } => {

src/librustc_mir/transform/qualify_consts.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -362,7 +362,8 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> {
362362
TerminatorKind::Switch {..} |
363363
TerminatorKind::SwitchInt {..} |
364364
TerminatorKind::DropAndReplace { .. } |
365-
TerminatorKind::Resume => None,
365+
TerminatorKind::Resume |
366+
TerminatorKind::Unreachable => None,
366367

367368
TerminatorKind::Return => {
368369
// Check for unused values. This usually means

src/librustc_mir/transform/type_check.rs

+2
Original file line numberDiff line numberDiff line change
@@ -379,6 +379,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
379379
TerminatorKind::Goto { .. } |
380380
TerminatorKind::Resume |
381381
TerminatorKind::Return |
382+
TerminatorKind::Unreachable |
382383
TerminatorKind::Drop { .. } => {
383384
// no checks needed for these
384385
}
@@ -595,6 +596,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
595596
span_mirbug!(self, block, "return on cleanup block")
596597
}
597598
}
599+
TerminatorKind::Unreachable => {}
598600
TerminatorKind::Drop { target, unwind, .. } |
599601
TerminatorKind::DropAndReplace { target, unwind, .. } |
600602
TerminatorKind::Assert { target, cleanup: unwind, .. } => {

src/librustc_trans/mir/analyze.rs

+1
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,7 @@ pub fn cleanup_kinds<'bcx,'tcx>(_bcx: Block<'bcx,'tcx>,
173173
TerminatorKind::Goto { .. } |
174174
TerminatorKind::Resume |
175175
TerminatorKind::Return |
176+
TerminatorKind::Unreachable |
176177
TerminatorKind::If { .. } |
177178
TerminatorKind::Switch { .. } |
178179
TerminatorKind::SwitchInt { .. } => {

src/librustc_trans/mir/block.rs

+4
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,10 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
191191
})
192192
}
193193

194+
mir::TerminatorKind::Unreachable => {
195+
bcx.unreachable();
196+
}
197+
194198
mir::TerminatorKind::Drop { ref location, target, unwind } => {
195199
let lvalue = self.trans_lvalue(&bcx, location);
196200
let ty = lvalue.ty.to_ty(bcx.tcx());

0 commit comments

Comments
 (0)