about summary refs log tree commit diff
diff options
context:
space:
mode:
authorPatrick Walton <pcwalton@mimiga.net>2013-08-29 15:50:53 -0700
committerPatrick Walton <pcwalton@mimiga.net>2013-09-23 18:23:18 -0700
commit16e87cb5279cd85ac11eb31de8a0087b08d831eb (patch)
tree438935c0c7be203d6051be6af5fef8671a92beeb
parent15ce791ff5b5756b1455c65708782bc8028de56f (diff)
downloadrust-16e87cb5279cd85ac11eb31de8a0087b08d831eb.tar.gz
rust-16e87cb5279cd85ac11eb31de8a0087b08d831eb.zip
librustc: Make the fall-through case in match not use garbage collected functions
-rw-r--r--src/librustc/middle/trans/_match.rs102
1 files 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<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>`.