Skip to content

Commit b330fba

Browse files
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 72f2715 commit b330fba

File tree

11 files changed

+36
-92
lines changed

11 files changed

+36
-92
lines changed

src/librustc/mir/repr.rs

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

369+
/// Indicates a terminator that can never be reached.
370+
Unreachable,
371+
369372
/// Drop the Lvalue
370373
Drop {
371374
location: Lvalue<'tcx>,
@@ -424,6 +427,7 @@ impl<'tcx> TerminatorKind<'tcx> {
424427
SwitchInt { targets: ref b, .. } => b[..].into_cow(),
425428
Resume => (&[]).into_cow(),
426429
Return => (&[]).into_cow(),
430+
Unreachable => (&[]).into_cow(),
427431
Call { destination: Some((_, t)), cleanup: Some(c), .. } => vec![t, c].into_cow(),
428432
Call { destination: Some((_, ref t)), cleanup: None, .. } =>
429433
slice::ref_slice(t).into_cow(),
@@ -453,6 +457,7 @@ impl<'tcx> TerminatorKind<'tcx> {
453457
SwitchInt { targets: ref mut b, .. } => b.iter_mut().collect(),
454458
Resume => Vec::new(),
455459
Return => Vec::new(),
460+
Unreachable => Vec::new(),
456461
Call { destination: Some((_, ref mut t)), cleanup: Some(ref mut c), .. } => vec![t, c],
457462
Call { destination: Some((_, ref mut t)), cleanup: None, .. } => vec![t],
458463
Call { destination: None, cleanup: Some(ref mut c), .. } => vec![c],
@@ -531,6 +536,7 @@ impl<'tcx> TerminatorKind<'tcx> {
531536
SwitchInt { discr: ref lv, .. } => write!(fmt, "switchInt({:?})", lv),
532537
Return => write!(fmt, "return"),
533538
Resume => write!(fmt, "resume"),
539+
Unreachable => write!(fmt, "unreachable"),
534540
Drop { ref location, .. } => write!(fmt, "drop({:?})", location),
535541
DropAndReplace { ref location, ref value, .. } =>
536542
write!(fmt, "replace({:?} <- {:?})", location, value),
@@ -574,7 +580,7 @@ impl<'tcx> TerminatorKind<'tcx> {
574580
pub fn fmt_successor_labels(&self) -> Vec<Cow<'static, str>> {
575581
use self::TerminatorKind::*;
576582
match *self {
577-
Return | Resume => vec![],
583+
Return | Resume | Unreachable => vec![],
578584
Goto { .. } => vec!["".into()],
579585
If { .. } => vec!["true".into(), "false".into()],
580586
Switch { ref adt_def, .. } => {

src/librustc/mir/visit.rs

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

387387
TerminatorKind::Resume |
388-
TerminatorKind::Return => {
388+
TerminatorKind::Return |
389+
TerminatorKind::Unreachable => {
389390
}
390391

391392
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
@@ -637,7 +637,9 @@ fn gather_moves<'a, 'tcx>(mir: &Mir<'tcx>, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> MoveD
637637

638638
debug!("gather_moves({:?})", bb_data.terminator());
639639
match bb_data.terminator().kind {
640-
TerminatorKind::Goto { target: _ } | TerminatorKind::Resume => { }
640+
TerminatorKind::Goto { target: _ } |
641+
TerminatorKind::Resume |
642+
TerminatorKind::Unreachable => { }
641643

642644
TerminatorKind::Return => {
643645
let source = Location { block: bb,

src/librustc_mir/build/matches/mod.rs

+11-6
Original file line numberDiff line numberDiff line change
@@ -89,18 +89,23 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
8989
// branch to the appropriate arm block
9090
let otherwise = self.match_candidates(span, &mut arm_blocks, candidates, block);
9191

92-
// because all matches are exhaustive, in principle we expect
93-
// an empty vector to be returned here, but the algorithm is
94-
// not entirely precise
92+
let scope_id = self.innermost_scope_id();
93+
9594
if !otherwise.is_empty() {
96-
let join_block = self.join_otherwise_blocks(span, otherwise);
97-
self.panic(join_block, "something about matches algorithm not being precise", span);
95+
// All matches are exhaustive. However, because some matches
96+
// only have exponentially-large exhaustive decision trees, we
97+
// sometimes generate an inexhaustive decision tree.
98+
//
99+
// In that case, the inexhaustive tips of the decision tree
100+
// can't be reached - terminate them with an `unreachable`.
101+
for block in otherwise {
102+
self.cfg.terminate(block, scope_id, span, TerminatorKind::Unreachable);
103+
}
98104
}
99105

100106
// all the arm blocks will rejoin here
101107
let end_block = self.cfg.start_new_block();
102108

103-
let scope_id = self.innermost_scope_id();
104109
for (arm_index, (extent, scope, body)) in arm_bodies.into_iter().enumerate() {
105110
let mut arm_block = arm_blocks.blocks[arm_index];
106111
// Re-enter the scope we created the bindings in.

src/librustc_mir/build/scope.rs

+1-81
Original file line numberDiff line numberDiff line change
@@ -90,12 +90,9 @@ use build::{BlockAnd, BlockAndExtension, Builder, CFG, ScopeAuxiliary};
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::*;
9595
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;
9996

10097
pub struct Scope<'tcx> {
10198
/// the scope-id within the scope_datas
@@ -536,50 +533,6 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
536533
next_target.unit()
537534
}
538535

539-
/// Create diverge cleanup and branch to it from `block`.
540-
// FIXME: Remove this (used only for unreachable cases in match).
541-
pub fn panic(&mut self, block: BasicBlock, msg: &'static str, span: Span) {
542-
// fn(&(msg: &'static str filename: &'static str, line: u32)) -> !
543-
let region = ty::ReStatic; // FIXME(mir-borrowck): use a better region?
544-
let func = self.lang_function(lang_items::PanicFnLangItem);
545-
let args = self.hir.tcx().replace_late_bound_regions(&func.ty.fn_args(), |_| region).0;
546-
547-
let ref_ty = args[0];
548-
let tup_ty = if let ty::TyRef(_, tyandmut) = ref_ty.sty {
549-
tyandmut.ty
550-
} else {
551-
span_bug!(span, "unexpected panic type: {:?}", func.ty);
552-
};
553-
554-
let (tuple, tuple_ref) = (self.temp(tup_ty), self.temp(ref_ty));
555-
let (file, line) = self.span_to_fileline_args(span);
556-
let message = Constant {
557-
span: span,
558-
ty: self.hir.tcx().mk_static_str(),
559-
literal: self.hir.str_literal(intern_and_get_ident(msg))
560-
};
561-
let elems = vec![Operand::Constant(message),
562-
Operand::Constant(file),
563-
Operand::Constant(line)];
564-
let scope_id = self.innermost_scope_id();
565-
// FIXME: We should have this as a constant, rather than a stack variable (to not pollute
566-
// icache with cold branch code), however to achieve that we either have to rely on rvalue
567-
// promotion or have some way, in MIR, to create constants.
568-
self.cfg.push_assign(block, scope_id, span, &tuple, // [1]
569-
Rvalue::Aggregate(AggregateKind::Tuple, elems));
570-
// [1] tuple = (message_arg, file_arg, line_arg);
571-
// FIXME: is this region really correct here?
572-
self.cfg.push_assign(block, scope_id, span, &tuple_ref, // tuple_ref = &tuple;
573-
Rvalue::Ref(region, BorrowKind::Shared, tuple));
574-
let cleanup = self.diverge_cleanup();
575-
self.cfg.terminate(block, scope_id, span, TerminatorKind::Call {
576-
func: Operand::Constant(func),
577-
args: vec![Operand::Consume(tuple_ref)],
578-
cleanup: cleanup,
579-
destination: None,
580-
});
581-
}
582-
583536
/// Create an Assert terminator and return the success block.
584537
/// If the boolean condition operand is not the expected value,
585538
/// a runtime panic will be caused with the given message.
@@ -605,39 +558,6 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
605558

606559
success_block
607560
}
608-
609-
fn lang_function(&mut self, lang_item: lang_items::LangItem) -> Constant<'tcx> {
610-
let funcdid = match self.hir.tcx().lang_items.require(lang_item) {
611-
Ok(d) => d,
612-
Err(m) => {
613-
self.hir.tcx().sess.fatal(&m)
614-
}
615-
};
616-
Constant {
617-
span: DUMMY_SP,
618-
ty: self.hir.tcx().lookup_item_type(funcdid).ty,
619-
literal: Literal::Item {
620-
def_id: funcdid,
621-
substs: self.hir.tcx().mk_substs(Substs::empty())
622-
}
623-
}
624-
}
625-
626-
fn span_to_fileline_args(&mut self, span: Span) -> (Constant<'tcx>, Constant<'tcx>) {
627-
let span_lines = self.hir.tcx().sess.codemap().lookup_char_pos(span.lo);
628-
(Constant {
629-
span: span,
630-
ty: self.hir.tcx().mk_static_str(),
631-
literal: self.hir.str_literal(intern_and_get_ident(&span_lines.file.name))
632-
}, Constant {
633-
span: span,
634-
ty: self.hir.tcx().types.u32,
635-
literal: Literal::Value {
636-
value: ConstVal::Integral(ConstInt::U32(span_lines.line as u32)),
637-
},
638-
})
639-
}
640-
641561
}
642562

643563
/// 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
@@ -359,6 +359,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
359359
TerminatorKind::Goto { .. } |
360360
TerminatorKind::Resume |
361361
TerminatorKind::Return |
362+
TerminatorKind::Unreachable |
362363
TerminatorKind::Drop { .. } => {
363364
// no checks needed for these
364365
}
@@ -575,6 +576,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
575576
span_mirbug!(self, block, "return on cleanup block")
576577
}
577578
}
579+
TerminatorKind::Unreachable => {}
578580
TerminatorKind::Drop { target, unwind, .. } |
579581
TerminatorKind::DropAndReplace { target, unwind, .. } |
580582
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
@@ -193,6 +193,10 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
193193
})
194194
}
195195

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

0 commit comments

Comments
 (0)