From 16e87cb5279cd85ac11eb31de8a0087b08d831eb Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Thu, 29 Aug 2013 15:50:53 -0700 Subject: [PATCH] librustc: Make the fall-through case in match not use garbage collected functions --- src/librustc/middle/trans/_match.rs | 102 +++++++++++++++++++++------- 1 file changed, 76 insertions(+), 26 deletions(-) diff --git a/src/librustc/middle/trans/_match.rs b/src/librustc/middle/trans/_match.rs index c9a113aeb96..464bcfee48e 100644 --- a/src/librustc/middle/trans/_match.rs +++ b/src/librustc/middle/trans/_match.rs @@ -533,7 +533,7 @@ fn enter_default<'r>(bcx: @mut Block, m: &[Match<'r>], col: uint, val: ValueRef, - chk: Option) + 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, +} + +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) + 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) { + 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, + 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::(|| 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 { - 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 ;` or `let = `.