rustc: Translate by-value pattern bindings

This commit is contained in:
Patrick Walton 2012-08-01 15:23:21 -07:00
parent 7f60c56c3e
commit 4991cc5d19

View file

@ -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);