about summary refs log tree commit diff
path: root/src/rustc
diff options
context:
space:
mode:
authorPatrick Walton <pcwalton@mimiga.net>2012-08-01 15:23:21 -0700
committerPatrick Walton <pcwalton@mimiga.net>2012-08-01 15:24:11 -0700
commit4991cc5d19a61da91345e9a324c0ea4b8d1b923d (patch)
tree3788c62c410de31677a22e12deda3e6ef912fb0c /src/rustc
parent7f60c56c3e1abfc9cae313ba8d0ed26ff455a9c1 (diff)
downloadrust-4991cc5d19a61da91345e9a324c0ea4b8d1b923d.tar.gz
rust-4991cc5d19a61da91345e9a324c0ea4b8d1b923d.zip
rustc: Translate by-value pattern bindings
Diffstat (limited to 'src/rustc')
-rw-r--r--src/rustc/middle/trans/alt.rs155
1 files changed, 120 insertions, 35 deletions
diff --git a/src/rustc/middle/trans/alt.rs b/src/rustc/middle/trans/alt.rs
index b25aa26066f..62b7c247784 100644
--- a/src/rustc/middle/trans/alt.rs
+++ b/src/rustc/middle/trans/alt.rs
@@ -79,17 +79,24 @@ fn variant_opt(tcx: ty::ctxt, pat_id: ast::node_id) -> opt {
     core::unreachable();
 }
 
+struct binding {
+    val: ValueRef;
+    mode: ast::binding_mode;
+    ty: ty::t;
+}
+
 type bind_map = ~[{
     ident: ast::ident,
-    val: ValueRef,
-    mode: ast::binding_mode
+    binding: binding
 }];
 
-fn assoc(key: ast::ident, list: bind_map) -> option<ValueRef> {
+fn assoc(key: ast::ident, list: bind_map) -> option<binding> {
     for vec::each(list) |elt| {
-        if str::eq(*elt.ident, *key) { ret some(elt.val); }
+        if str::eq(*elt.ident, *key) {
+            return some(elt.binding);
+        }
     }
-    ret none;
+    return none;
 }
 
 type match_branch =
@@ -110,7 +117,9 @@ fn has_nested_bindings(m: match_, col: uint) -> bool {
     ret false;
 }
 
-fn expand_nested_bindings(m: match_, col: uint, val: ValueRef) -> match_ {
+fn expand_nested_bindings(bcx: block, m: match_, col: uint, val: ValueRef)
+                       -> match_ {
+
     let mut result = ~[];
     for vec::each(m) |br| {
       alt br.pats[col].node {
@@ -123,8 +132,12 @@ fn expand_nested_bindings(m: match_, col: uint, val: ValueRef) -> match_ {
                       @{pats: pats,
                         bound: vec::append(
                             br.bound, ~[{ident: path_to_ident(name),
-                                        val: val,
-                                        mode: mode}])
+                                         binding: binding {
+                                            val: val,
+                                            mode: mode,
+                                            ty: node_id_type(bcx,
+                                                             br.pats[col].id)
+                                         }}])
                                 with *br});
           }
           _ { vec::push(result, br); }
@@ -135,7 +148,7 @@ fn expand_nested_bindings(m: match_, col: uint, val: ValueRef) -> match_ {
 
 type enter_pat = fn(@ast::pat) -> option<~[@ast::pat]>;
 
-fn enter_match(dm: DefMap, m: match_, col: uint, val: ValueRef,
+fn enter_match(bcx: block, dm: DefMap, m: match_, col: uint, val: ValueRef,
                e: enter_pat) -> match_ {
     let mut result = ~[];
     for vec::each(m) |br| {
@@ -149,8 +162,11 @@ fn enter_match(dm: DefMap, m: match_, col: uint, val: ValueRef,
               ast::pat_ident(mode, name, none) if !pat_is_variant(dm, self) {
                 vec::append(br.bound,
                             ~[{ident: path_to_ident(name),
-                               val: val,
-                               mode: mode}])
+                               binding: binding {
+                                   val: val,
+                                   mode: mode,
+                                   ty: node_id_type(bcx, br.pats[col].id)
+                               }}])
               }
               _ { br.bound }
             };
@@ -162,8 +178,10 @@ fn enter_match(dm: DefMap, m: match_, col: uint, val: ValueRef,
     ret result;
 }
 
-fn enter_default(dm: DefMap, m: match_, col: uint, val: ValueRef) -> match_ {
-    do enter_match(dm, m, col, val) |p| {
+fn enter_default(bcx: block, dm: DefMap, m: match_, col: uint, val: ValueRef)
+              -> match_ {
+
+    do enter_match(bcx, dm, m, col, val) |p| {
         alt p.node {
           ast::pat_wild | ast::pat_rec(_, _) | ast::pat_tup(_) { some(~[]) }
           ast::pat_ident(_, _, none) if !pat_is_variant(dm, p) {
@@ -174,10 +192,11 @@ fn enter_default(dm: DefMap, m: match_, col: uint, val: ValueRef) -> match_ {
     }
 }
 
-fn enter_opt(tcx: ty::ctxt, m: match_, opt: opt, col: uint,
+fn enter_opt(bcx: block, m: match_, opt: opt, col: uint,
              variant_size: uint, val: ValueRef) -> match_ {
+    let tcx = bcx.tcx();
     let dummy = @{id: 0, node: ast::pat_wild, span: dummy_sp()};
-    do enter_match(tcx.def_map, m, col, val) |p| {
+    do enter_match(bcx, tcx.def_map, m, col, val) |p| {
         alt p.node {
           ast::pat_enum(_, subpats) {
             if opt_eq(tcx, variant_opt(tcx, p.id), opt) {
@@ -200,10 +219,10 @@ fn enter_opt(tcx: ty::ctxt, m: match_, opt: opt, col: uint,
     }
 }
 
-fn enter_rec(dm: DefMap, m: match_, col: uint, fields: ~[ast::ident],
-             val: ValueRef) -> match_ {
+fn enter_rec(bcx: block, dm: DefMap, m: match_, col: uint,
+             fields: ~[ast::ident], val: ValueRef) -> match_ {
     let dummy = @{id: 0, node: ast::pat_wild, span: dummy_sp()};
-    do enter_match(dm, m, col, val) |p| {
+    do enter_match(bcx, dm, m, col, val) |p| {
         alt p.node {
           ast::pat_rec(fpats, _) {
             let mut pats = ~[];
@@ -221,10 +240,10 @@ fn enter_rec(dm: DefMap, m: match_, col: uint, fields: ~[ast::ident],
     }
 }
 
-fn enter_tup(dm: DefMap, m: match_, col: uint, val: ValueRef,
+fn enter_tup(bcx: block, dm: DefMap, m: match_, col: uint, val: ValueRef,
              n_elts: uint) -> match_ {
     let dummy = @{id: 0, node: ast::pat_wild, span: dummy_sp()};
-    do enter_match(dm, m, col, val) |p| {
+    do enter_match(bcx, dm, m, col, val) |p| {
         alt p.node {
           ast::pat_tup(elts) { some(elts) }
           _ { some(vec::from_elem(n_elts, dummy)) }
@@ -232,9 +251,10 @@ fn enter_tup(dm: DefMap, m: match_, col: uint, val: ValueRef,
     }
 }
 
-fn enter_box(dm: DefMap, m: match_, col: uint, val: ValueRef) -> match_ {
+fn enter_box(bcx: block, dm: DefMap, m: match_, col: uint, val: ValueRef)
+          -> match_ {
     let dummy = @{id: 0, node: ast::pat_wild, span: dummy_sp()};
-    do enter_match(dm, m, col, val) |p| {
+    do enter_match(bcx, dm, m, col, val) |p| {
         alt p.node {
           ast::pat_box(sub) { some(~[sub]) }
           _ { some(~[dummy]) }
@@ -242,9 +262,10 @@ fn enter_box(dm: DefMap, m: match_, col: uint, val: ValueRef) -> match_ {
     }
 }
 
-fn enter_uniq(dm: DefMap, m: match_, col: uint, val: ValueRef) -> match_ {
+fn enter_uniq(bcx: block, dm: DefMap, m: match_, col: uint, val: ValueRef)
+           -> match_ {
     let dummy = @{id: 0, node: ast::pat_wild, span: dummy_sp()};
-    do enter_match(dm, m, col, val) |p| {
+    do enter_match(bcx, dm, m, col, val) |p| {
         alt p.node {
           ast::pat_uniq(sub) { some(~[sub]) }
           _ { some(~[dummy]) }
@@ -407,9 +428,24 @@ fn compile_submatch(bcx: block, m: match_, vals: ~[ValueRef],
           some(e) {
             // Temporarily set bindings. They'll be rewritten to PHI nodes
             // for the actual arm block.
+            //
+            // Also, in the case of by-value, do the copy now.
+
             for data.id_map.each |key, val| {
-                let loc = local_mem(option::get(assoc(key, m[0].bound)));
-                bcx.fcx.lllocals.insert(val, loc);
+                let binding = assoc(key, m[0].bound).get();
+                let (llval, mode) = (binding.val, binding.mode);
+                let ty = binding.ty;
+
+                if mode == ast::bind_by_value {
+                    let llty = type_of::type_of(bcx.fcx.ccx, ty);
+                    let alloc = alloca(bcx, llty);
+                    bcx = copy_val(bcx, INIT, alloc,
+                                   load_if_immediate(bcx, llval, ty), ty);
+                    bcx.fcx.lllocals.insert(val, local_mem(alloc));
+                    add_clean(bcx, alloc, ty);
+                } else {
+                    bcx.fcx.lllocals.insert(val, local_mem(llval));
+                }
             };
             let {bcx: guard_cx, val} = {
                 do with_scope_result(bcx, e.info(), ~"guard") |bcx| {
@@ -434,7 +470,7 @@ fn compile_submatch(bcx: block, m: match_, vals: ~[ValueRef],
     let col = pick_col(m);
     let val = vals[col];
     let m = if has_nested_bindings(m, col) {
-                expand_nested_bindings(m, col, val)
+                expand_nested_bindings(bcx, m, col, val)
             } else { m };
 
     let vals_left = vec::append(vec::slice(vals, 0u, col),
@@ -458,7 +494,7 @@ fn compile_submatch(bcx: block, m: match_, vals: ~[ValueRef],
             let ix = option::get(ty::field_idx(field_name, fields));
             vec::push(rec_vals, GEPi(bcx, val, ~[0u, ix]));
         }
-        compile_submatch(bcx, enter_rec(dm, m, col, rec_fields, val),
+        compile_submatch(bcx, enter_rec(bcx, dm, m, col, rec_fields, val),
                          vec::append(rec_vals, vals_left), chk, exits);
         ret;
     }
@@ -474,7 +510,7 @@ fn compile_submatch(bcx: block, m: match_, vals: ~[ValueRef],
             vec::push(tup_vals, GEPi(bcx, val, ~[0u, i]));
             i += 1u;
         }
-        compile_submatch(bcx, enter_tup(dm, m, col, val, n_tup_elts),
+        compile_submatch(bcx, enter_tup(bcx, dm, m, col, val, n_tup_elts),
                          vec::append(tup_vals, vals_left), chk, exits);
         ret;
     }
@@ -485,7 +521,7 @@ fn compile_submatch(bcx: block, m: match_, vals: ~[ValueRef],
         let box_no_addrspace = non_gc_box_cast(bcx, llbox);
         let unboxed =
             GEPi(bcx, box_no_addrspace, ~[0u, abi::box_field_body]);
-        compile_submatch(bcx, enter_box(dm, m, col, val),
+        compile_submatch(bcx, enter_box(bcx, dm, m, col, val),
                          vec::append(~[unboxed], vals_left), chk, exits);
         ret;
     }
@@ -495,7 +531,7 @@ fn compile_submatch(bcx: block, m: match_, vals: ~[ValueRef],
         let box_no_addrspace = non_gc_box_cast(bcx, llbox);
         let unboxed =
             GEPi(bcx, box_no_addrspace, ~[0u, abi::box_field_body]);
-        compile_submatch(bcx, enter_uniq(dm, m, col, val),
+        compile_submatch(bcx, enter_uniq(bcx, dm, m, col, val),
                          vec::append(~[unboxed], vals_left), chk, exits);
         ret;
     }
@@ -544,7 +580,7 @@ fn compile_submatch(bcx: block, m: match_, vals: ~[ValueRef],
         Switch(bcx, test_val, else_cx.llbb, opts.len())
     } else { C_int(ccx, 0) }; // Placeholder for when not using a switch
 
-    let defaults = enter_default(dm, m, col, val);
+    let defaults = enter_default(bcx, dm, m, col, val);
     let exhaustive = option::is_none(chk) && defaults.len() == 0u;
     let len = opts.len();
     let mut i = 0u;
@@ -599,7 +635,7 @@ fn compile_submatch(bcx: block, m: match_, vals: ~[ValueRef],
           }
           lit(_) | range(_, _) { }
         }
-        compile_submatch(opt_cx, enter_opt(tcx, m, opt, col, size, val),
+        compile_submatch(opt_cx, enter_opt(bcx, m, opt, col, size, val),
                          vec::append(unpacked, vals_left), chk, exits);
     }
 
@@ -624,9 +660,9 @@ fn make_phi_bindings(bcx: block, map: ~[exit_node],
         for vec::each(map) |ex| {
             if ex.to as uint == our_block {
                 alt assoc(name, ex.bound) {
-                  some(val) {
+                  some(binding) {
                     vec::push(llbbs, ex.from);
-                    vec::push(vals, val);
+                    vec::push(vals, binding.val);
                   }
                   none { }
                 }
@@ -643,6 +679,54 @@ fn make_phi_bindings(bcx: block, map: ~[exit_node],
     ret success;
 }
 
+// Copies by-value bindings into their homes.
+fn copy_by_value_bindings(bcx: block,
+                          exit_node_map: &[exit_node],
+                          pat_ids: pat_util::pat_id_map)
+                       -> block {
+    let mut bcx = bcx;
+    let our_block = bcx.llbb as uint;
+    for pat_ids.each |name, node_id| {
+        let bindings = dvec::dvec();
+        for exit_node_map.each |exit_node| {
+            if exit_node.to as uint == our_block {
+                match assoc(name, exit_node.bound) {
+                    none => {}
+                    some(binding) => bindings.push(binding)
+                }
+            }
+        }
+
+        if bindings.len() == 0 {
+            again;
+        }
+
+        let binding = bindings[0];
+        match binding.mode {
+            ast::bind_by_ref => {}
+            ast::bind_by_value => {
+                let llvalue;
+                match bcx.fcx.lllocals.get(node_id) {
+                    local_mem(llval) =>
+                        llvalue = llval,
+                    local_imm(_) =>
+                        bcx.sess().bug(~"local_imm unexpected here")
+                }
+
+                let lltype = type_of::type_of(bcx.fcx.ccx, binding.ty);
+                let allocation = alloca(bcx, lltype);
+                let ty = binding.ty;
+                bcx = copy_val(bcx, INIT, allocation,
+                               load_if_immediate(bcx, llvalue, ty), ty);
+                bcx.fcx.lllocals.insert(node_id, local_mem(allocation));
+                add_clean(bcx, allocation, ty);
+            }
+        }
+    }
+
+    return bcx;
+}
+
 fn trans_alt(bcx: block,
              alt_expr: @ast::expr,
              expr: @ast::expr,
@@ -713,6 +797,7 @@ fn trans_alt_inner(scope_cx: block, expr: @ast::expr, arms: ~[ast::arm],
         let body_cx = bodies[i];
         let id_map = pat_util::pat_id_map(tcx.def_map, a.pats[0]);
         if make_phi_bindings(body_cx, exit_map, id_map) {
+            let body_cx = copy_by_value_bindings(body_cx, exit_map, id_map);
             let arm_dest = dup_for_join(dest);
             vec::push(arm_dests, arm_dest);
             let mut arm_cx = trans_block(body_cx, a.body, arm_dest);