librustc: Make the fall-through case in match not use garbage collected functions

This commit is contained in:
Patrick Walton 2013-08-29 15:50:53 -07:00
parent 15ce791ff5
commit 16e87cb527
1 changed files with 76 additions and 26 deletions

View File

@ -533,7 +533,7 @@ fn enter_default<'r>(bcx: @mut Block,
m: &[Match<'r>],
col: uint,
val: ValueRef,
chk: Option<mk_fail>)
chk: FailureHandler)
-> ~[Match<'r>] {
debug!("enter_default(bcx=%s, m=%s, col=%u, val=%s)",
bcx.to_str(),
@ -567,7 +567,7 @@ fn enter_default<'r>(bcx: @mut Block,
// we don't need any default cases. If the check *isn't* nonexhaustive
// (because chk is Some), then we need the defaults anyways.
let is_exhaustive = match matches.last_opt() {
Some(m) if m.data.arm.guard.is_some() && chk.is_none() => true,
Some(m) if m.data.arm.guard.is_some() && chk.is_infallible() => true,
_ => false
};
@ -1185,7 +1185,62 @@ fn any_tuple_struct_pat(bcx: @mut Block, m: &[Match], col: uint) -> bool {
}
}
type mk_fail = @fn() -> BasicBlockRef;
trait CustomFailureHandler {
fn handle_fail(&self) -> BasicBlockRef;
}
struct DynamicFailureHandler {
bcx: @mut Block,
sp: span,
msg: @str,
finished: @mut Option<BasicBlockRef>,
}
impl CustomFailureHandler for DynamicFailureHandler {
fn handle_fail(&self) -> BasicBlockRef {
match *self.finished {
Some(bb) => return bb,
_ => (),
}
let fail_cx = sub_block(self.bcx, "case_fallthrough");
controlflow::trans_fail(fail_cx, Some(self.sp), self.msg);
*self.finished = Some(fail_cx.llbb);
fail_cx.llbb
}
}
/// What to do when the pattern match fails.
enum FailureHandler {
Infallible,
JumpToBasicBlock(BasicBlockRef),
CustomFailureHandlerClass(@CustomFailureHandler),
}
impl FailureHandler {
fn is_infallible(&self) -> bool {
match *self {
Infallible => true,
_ => false,
}
}
fn is_fallible(&self) -> bool {
!self.is_infallible()
}
fn handle_fail(&self) -> BasicBlockRef {
match *self {
Infallible => {
fail!("attempted to fail in infallible failure handler!")
}
JumpToBasicBlock(basic_block) => basic_block,
CustomFailureHandlerClass(custom_failure_handler) => {
custom_failure_handler.handle_fail()
}
}
}
}
fn pick_col(m: &[Match]) -> uint {
fn score(p: &ast::Pat) -> uint {
@ -1347,7 +1402,7 @@ fn compile_guard(bcx: @mut Block,
data: &ArmData,
m: &[Match],
vals: &[ValueRef],
chk: Option<mk_fail>)
chk: FailureHandler)
-> @mut Block {
debug!("compile_guard(bcx=%s, guard_expr=%s, m=%s, vals=%s)",
bcx.to_str(),
@ -1400,9 +1455,9 @@ fn compile_guard(bcx: @mut Block,
}
fn compile_submatch(bcx: @mut Block,
m: &[Match],
vals: &[ValueRef],
chk: Option<mk_fail>) {
m: &[Match],
vals: &[ValueRef],
chk: FailureHandler) {
debug!("compile_submatch(bcx=%s, m=%s, vals=%s)",
bcx.to_str(),
m.repr(bcx.tcx()),
@ -1412,11 +1467,11 @@ fn compile_submatch(bcx: @mut Block,
/*
For an empty match, a fall-through case must exist
*/
assert!((m.len() > 0u || chk.is_some()));
assert!((m.len() > 0u || chk.is_fallible()));
let _icx = push_ctxt("match::compile_submatch");
let mut bcx = bcx;
if m.len() == 0u {
Br(bcx, chk.unwrap()());
Br(bcx, chk.handle_fail());
return;
}
if m[0].pats.len() == 0u {
@ -1454,7 +1509,7 @@ fn compile_submatch(bcx: @mut Block,
fn compile_submatch_continue(mut bcx: @mut Block,
m: &[Match],
vals: &[ValueRef],
chk: Option<mk_fail>,
chk: FailureHandler,
col: uint,
val: ValueRef) {
let tcx = bcx.tcx();
@ -1617,7 +1672,7 @@ fn compile_submatch_continue(mut bcx: @mut Block,
};
let defaults = enter_default(else_cx, dm, m, col, val, chk);
let exhaustive = chk.is_none() && defaults.len() == 0u;
let exhaustive = chk.is_infallible() && defaults.len() == 0u;
let len = opts.len();
// Compile subtrees for each option
@ -1721,7 +1776,7 @@ fn compile_submatch_continue(mut bcx: @mut Block,
// If none of these subcases match, move on to the
// next condition.
branch_chk = Some::<mk_fail>(|| bcx.llbb);
branch_chk = JumpToBasicBlock(bcx.llbb);
CondBr(after_cx, matches, opt_cx.llbb, bcx.llbb);
}
_ => ()
@ -1860,11 +1915,15 @@ fn trans_match_inner(scope_cx: @mut Block,
if ty::type_is_empty(tcx, t) {
// Special case for empty types
let fail_cx = @mut None;
let f: mk_fail = || mk_fail(scope_cx, discr_expr.span,
@"scrutinizing value that can't exist", fail_cx);
Some(f)
let fail_handler = @DynamicFailureHandler {
bcx: scope_cx,
sp: discr_expr.span,
msg: @"scrutinizing value that can't exist",
finished: fail_cx,
} as @CustomFailureHandler;
CustomFailureHandlerClass(fail_handler)
} else {
None
Infallible
}
};
let lldiscr = discr_datum.to_zeroable_ref_llval(bcx);
@ -1892,15 +1951,6 @@ fn trans_match_inner(scope_cx: @mut Block,
bcx = controlflow::join_blocks(scope_cx, arm_cxs);
return bcx;
fn mk_fail(bcx: @mut Block, sp: Span, msg: @str,
finished: @mut Option<BasicBlockRef>) -> BasicBlockRef {
match *finished { Some(bb) => return bb, _ => () }
let fail_cx = sub_block(bcx, "case_fallthrough");
controlflow::trans_fail(fail_cx, Some(sp), msg);
*finished = Some(fail_cx.llbb);
return fail_cx.llbb;
}
}
enum IrrefutablePatternBindingMode {
@ -1913,7 +1963,7 @@ enum IrrefutablePatternBindingMode {
pub fn store_local(bcx: @mut Block,
pat: @ast::Pat,
opt_init_expr: Option<@ast::Expr>)
-> @mut Block {
-> @mut Block {
/*!
* Generates code for a local variable declaration like
* `let <pat>;` or `let <pat> = <opt_init_expr>`.