Use switches rather than chained conditionals to compile alt matches
This also moves the alt-related trans code into its own file. Closes #467
This commit is contained in:
parent
1ea449e2f6
commit
12d5532166
@ -32,7 +32,6 @@ import back::link;
|
||||
import back::x86;
|
||||
import back::abi;
|
||||
import back::upcall;
|
||||
import middle::ty::pat_ty;
|
||||
import syntax::visit;
|
||||
import visit::vt;
|
||||
import util::common;
|
||||
@ -3370,7 +3369,7 @@ fn move_val_if_temp(@block_ctxt cx, copy_action action, ValueRef dst,
|
||||
}
|
||||
}
|
||||
|
||||
fn trans_lit(&@crate_ctxt cx, &ast::lit lit, ast::node_id id) -> ValueRef {
|
||||
fn trans_lit(&@crate_ctxt cx, &ast::lit lit) -> ValueRef {
|
||||
alt (lit.node) {
|
||||
case (ast::lit_int(?i)) { ret C_int(i); }
|
||||
case (ast::lit_uint(?u)) { ret C_int(u as int); }
|
||||
@ -4742,219 +4741,6 @@ fn trans_do_while(&@block_ctxt cx, &ast::block body, &@ast::expr cond) ->
|
||||
ret rslt(next_cx, body_res.val);
|
||||
}
|
||||
|
||||
|
||||
// Pattern matching translation
|
||||
fn trans_pat_match(&@block_ctxt cx, &@ast::pat pat, ValueRef llval,
|
||||
&@block_ctxt next_cx) -> result {
|
||||
alt (pat.node) {
|
||||
case (ast::pat_wild) { ret rslt(cx, llval); }
|
||||
case (ast::pat_bind(_)) { ret rslt(cx, llval); }
|
||||
case (ast::pat_lit(?lt)) {
|
||||
auto lllit = trans_lit(cx.fcx.lcx.ccx, *lt, pat.id);
|
||||
auto lltype = ty::node_id_to_type(cx.fcx.lcx.ccx.tcx, pat.id);
|
||||
auto lleq = trans_compare(cx, ast::eq, lltype, llval, lllit);
|
||||
auto matched_cx = new_sub_block_ctxt(lleq.bcx, "matched_cx");
|
||||
lleq.bcx.build.CondBr(lleq.val, matched_cx.llbb, next_cx.llbb);
|
||||
ret rslt(matched_cx, llval);
|
||||
}
|
||||
case (ast::pat_tag(?ident, ?subpats)) {
|
||||
auto vdef;
|
||||
alt (cx.fcx.lcx.ccx.tcx.def_map.find(pat.id)) {
|
||||
case (some(?x)) { vdef = ast::variant_def_ids(x); }
|
||||
case (_) {
|
||||
cx.fcx.lcx.ccx.sess.span_fatal(pat.span,
|
||||
"trans_pat_match: unbound var");
|
||||
}
|
||||
}
|
||||
|
||||
auto variants = ty::tag_variants(cx.fcx.lcx.ccx.tcx, vdef._0);
|
||||
auto matched_cx = new_sub_block_ctxt(cx, "matched_cx");
|
||||
auto llblobptr = llval;
|
||||
|
||||
if (std::ivec::len(variants) == 1u) {
|
||||
cx.build.Br(matched_cx.llbb);
|
||||
} else {
|
||||
auto lltagptr =
|
||||
cx.build.PointerCast(llval,
|
||||
T_opaque_tag_ptr(cx.fcx.lcx.ccx.tn));
|
||||
auto lldiscrimptr = cx.build.GEP(lltagptr,
|
||||
~[C_int(0), C_int(0)]);
|
||||
auto lldiscrim = cx.build.Load(lldiscrimptr);
|
||||
auto variant_tag = 0;
|
||||
auto i = 0;
|
||||
for (ty::variant_info v in variants) {
|
||||
auto this_variant_id = v.id;
|
||||
if (vdef._1._0 == this_variant_id._0 &&
|
||||
vdef._1._1 == this_variant_id._1) {
|
||||
variant_tag = i;
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
auto lleq =
|
||||
cx.build.ICmp(lib::llvm::LLVMIntEQ, lldiscrim,
|
||||
C_int(variant_tag));
|
||||
cx.build.CondBr(lleq, matched_cx.llbb, next_cx.llbb);
|
||||
if (std::ivec::len(subpats) > 0u) {
|
||||
llblobptr =
|
||||
matched_cx.build.GEP(lltagptr, ~[C_int(0), C_int(1)]);
|
||||
}
|
||||
}
|
||||
|
||||
auto ty_params = ty::node_id_to_type_params
|
||||
(cx.fcx.lcx.ccx.tcx, pat.id);
|
||||
if (std::ivec::len(subpats) > 0u) {
|
||||
auto i = 0;
|
||||
for (@ast::pat subpat in subpats) {
|
||||
auto rslt =
|
||||
GEP_tag(matched_cx, llblobptr, vdef._0, vdef._1,
|
||||
ty_params, i);
|
||||
auto llsubvalptr = rslt.val;
|
||||
matched_cx = rslt.bcx;
|
||||
auto llsubval =
|
||||
load_if_immediate(matched_cx, llsubvalptr,
|
||||
pat_ty(cx.fcx.lcx.ccx.tcx, subpat));
|
||||
auto subpat_res =
|
||||
trans_pat_match(matched_cx, subpat, llsubval,
|
||||
next_cx);
|
||||
matched_cx = subpat_res.bcx;
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
ret rslt(matched_cx, llval);
|
||||
}
|
||||
case (ast::pat_rec(?field_pats, _)) {
|
||||
auto bcx = cx;
|
||||
auto ccx = cx.fcx.lcx.ccx;
|
||||
auto rec_ty = pat_ty(ccx.tcx, pat);
|
||||
auto fields = alt (ty::struct(ccx.tcx, rec_ty)) {
|
||||
ty::ty_rec(?fields) { fields }
|
||||
};
|
||||
for (ast::field_pat f in field_pats) {
|
||||
let uint ix = ty::field_idx(ccx.sess, f.pat.span,
|
||||
f.ident, fields);
|
||||
auto r = GEP_tup_like(bcx, rec_ty, llval, ~[0, ix as int]);
|
||||
auto v = load_if_immediate(r.bcx, r.val,
|
||||
pat_ty(ccx.tcx, f.pat));
|
||||
bcx = trans_pat_match(r.bcx, f.pat, v, next_cx).bcx;
|
||||
}
|
||||
ret rslt(bcx, llval);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type bind_map = hashmap[ast::ident, result];
|
||||
|
||||
fn trans_pat_binding(&@block_ctxt cx, &@ast::pat pat, ValueRef llval,
|
||||
bool is_mem, &bind_map bound) -> result {
|
||||
alt (pat.node) {
|
||||
case (ast::pat_wild) { ret rslt(cx, llval); }
|
||||
case (ast::pat_lit(_)) { ret rslt(cx, llval); }
|
||||
case (ast::pat_bind(?name)) {
|
||||
auto val = llval;
|
||||
if (!is_mem) {
|
||||
val = spill_if_immediate
|
||||
(cx, llval, node_id_type(cx.fcx.lcx.ccx, pat.id));
|
||||
}
|
||||
auto r = rslt(cx, val);
|
||||
bound.insert(name, r);
|
||||
ret r;
|
||||
}
|
||||
case (ast::pat_tag(_, ?subpats)) {
|
||||
if (std::ivec::len[@ast::pat](subpats) == 0u) {
|
||||
ret rslt(cx, llval);
|
||||
}
|
||||
|
||||
// Get the appropriate variant for this tag.
|
||||
auto vdef;
|
||||
alt (cx.fcx.lcx.ccx.tcx.def_map.find(pat.id)) {
|
||||
case (some(?x)) { vdef = ast::variant_def_ids(x); }
|
||||
case (_) { cx.fcx.lcx.ccx.sess.span_fatal(pat.span,
|
||||
"trans_pat_binding: internal error, unbound var"); }
|
||||
}
|
||||
auto llblobptr = llval;
|
||||
if (std::ivec::len(ty::tag_variants(cx.fcx.lcx.ccx.tcx, vdef._0))
|
||||
!= 1u) {
|
||||
auto lltagptr = cx.build.PointerCast
|
||||
(llval, T_opaque_tag_ptr(cx.fcx.lcx.ccx.tn));
|
||||
llblobptr = cx.build.GEP(lltagptr, ~[C_int(0), C_int(1)]);
|
||||
}
|
||||
|
||||
auto ty_param_substs =
|
||||
ty::node_id_to_type_params(cx.fcx.lcx.ccx.tcx, pat.id);
|
||||
|
||||
auto this_cx = cx;
|
||||
auto i = 0;
|
||||
for (@ast::pat subpat in subpats) {
|
||||
auto rslt =
|
||||
GEP_tag(this_cx, llblobptr, vdef._0, vdef._1,
|
||||
ty_param_substs, i);
|
||||
this_cx = rslt.bcx;
|
||||
auto subpat_res =
|
||||
trans_pat_binding(this_cx, subpat, rslt.val, true, bound);
|
||||
this_cx = subpat_res.bcx;
|
||||
i += 1;
|
||||
}
|
||||
ret rslt(this_cx, llval);
|
||||
}
|
||||
case (ast::pat_rec(?field_pats, _)) {
|
||||
auto bcx = cx;
|
||||
auto ccx = cx.fcx.lcx.ccx;
|
||||
auto rec_ty = pat_ty(ccx.tcx, pat);
|
||||
auto fields = alt (ty::struct(ccx.tcx, rec_ty)) {
|
||||
ty::ty_rec(?fields) { fields }
|
||||
};
|
||||
for (ast::field_pat f in field_pats) {
|
||||
let uint ix = ty::field_idx(ccx.sess, f.pat.span,
|
||||
f.ident, fields);
|
||||
auto r = GEP_tup_like(bcx, rec_ty, llval, ~[0, ix as int]);
|
||||
bcx = trans_pat_binding(bcx, f.pat, r.val, true, bound).bcx;
|
||||
}
|
||||
ret rslt(bcx, llval);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn trans_alt(&@block_ctxt cx, &@ast::expr expr, &ast::arm[] arms,
|
||||
ast::node_id id, &out_method output) -> result {
|
||||
auto expr_res = trans_expr(cx, expr);
|
||||
auto this_cx = expr_res.bcx;
|
||||
let result[] arm_results = ~[];
|
||||
for (ast::arm arm in arms) {
|
||||
auto bind_maps = ~[];
|
||||
auto block_cx = new_scope_block_ctxt(expr_res.bcx, "case block");
|
||||
for (@ast::pat pat in arm.pats) {
|
||||
auto next_cx = new_sub_block_ctxt(expr_res.bcx, "next");
|
||||
auto match_res =
|
||||
trans_pat_match(this_cx, pat, expr_res.val, next_cx);
|
||||
auto bind_map = new_str_hash[result]();
|
||||
auto binding_res = trans_pat_binding
|
||||
(match_res.bcx, pat, expr_res.val, false, bind_map);
|
||||
bind_maps += ~[bind_map];
|
||||
binding_res.bcx.build.Br(block_cx.llbb);
|
||||
this_cx = next_cx;
|
||||
}
|
||||
// Go over the names and node_ids of the bound variables, add a Phi
|
||||
// node for each and register the bindings.
|
||||
for each (@tup(ast::ident, ast::node_id) item in
|
||||
ast::pat_id_map(arm.pats.(0)).items()) {
|
||||
auto vals = ~[]; auto llbbs = ~[];
|
||||
for (bind_map map in bind_maps) {
|
||||
auto rslt = map.get(item._0);
|
||||
vals += ~[rslt.val];
|
||||
llbbs += ~[rslt.bcx.llbb];
|
||||
}
|
||||
auto phi = block_cx.build.Phi(val_ty(vals.(0)), vals, llbbs);
|
||||
block_cx.fcx.lllocals.insert(item._1, phi);
|
||||
}
|
||||
auto block_res = trans_block(block_cx, arm.block, output);
|
||||
arm_results += ~[block_res];
|
||||
}
|
||||
auto default_cx = this_cx;
|
||||
trans_fail(default_cx, some[span](expr.span),
|
||||
"non-exhaustive match failure");
|
||||
ret rslt(join_branches(cx, arm_results), C_nil());
|
||||
}
|
||||
|
||||
type generic_info =
|
||||
rec(ty::t item_type,
|
||||
(option::t[@tydesc_info])[] static_tis,
|
||||
@ -6224,10 +6010,9 @@ fn trans_expr(&@block_ctxt cx, &@ast::expr e) -> result {
|
||||
fn trans_expr_out(&@block_ctxt cx, &@ast::expr e, out_method output) ->
|
||||
result {
|
||||
// FIXME Fill in cx.sp
|
||||
|
||||
alt (e.node) {
|
||||
case (ast::expr_lit(?lit)) {
|
||||
ret rslt(cx, trans_lit(cx.fcx.lcx.ccx, *lit, e.id));
|
||||
ret rslt(cx, trans_lit(cx.fcx.lcx.ccx, *lit));
|
||||
}
|
||||
case (ast::expr_unary(?op, ?x)) {
|
||||
if (op != ast::deref) { ret trans_unary(cx, op, x, e.id); }
|
||||
@ -6259,7 +6044,8 @@ fn trans_expr_out(&@block_ctxt cx, &@ast::expr e, out_method output) ->
|
||||
ret trans_do_while(cx, body, cond);
|
||||
}
|
||||
case (ast::expr_alt(?expr, ?arms)) {
|
||||
ret with_out_method(bind trans_alt(cx, expr, arms, e.id, _),
|
||||
ret with_out_method(bind trans_alt::trans_alt(cx, expr,
|
||||
arms, e.id, _),
|
||||
cx, e.id, output);
|
||||
}
|
||||
case (ast::expr_fn(?f)) {
|
||||
@ -8692,7 +8478,7 @@ fn trans_tag_variant(@local_ctxt cx, ast::node_id tag_id,
|
||||
// that does so later on?
|
||||
fn trans_const_expr(&@crate_ctxt cx, @ast::expr e) -> ValueRef {
|
||||
alt (e.node) {
|
||||
case (ast::expr_lit(?lit)) { ret trans_lit(cx, *lit, e.id); }
|
||||
case (ast::expr_lit(?lit)) { ret trans_lit(cx, *lit); }
|
||||
case (_) {
|
||||
cx.sess.span_unimpl(e.span, "consts that's not a plain literal");
|
||||
}
|
||||
|
426
src/comp/middle/trans_alt.rs
Normal file
426
src/comp/middle/trans_alt.rs
Normal file
@ -0,0 +1,426 @@
|
||||
import std::str;
|
||||
import std::ivec;
|
||||
import std::option;
|
||||
import option::some;
|
||||
import option::none;
|
||||
import std::map::hashmap;
|
||||
|
||||
import lib::llvm::llvm;
|
||||
import lib::llvm::llvm::ValueRef;
|
||||
import lib::llvm::llvm::TypeRef;
|
||||
import lib::llvm::llvm::BasicBlockRef;
|
||||
import trans::result;
|
||||
import trans::rslt;
|
||||
import trans::crate_ctxt;
|
||||
import trans::block_ctxt;
|
||||
import trans::new_sub_block_ctxt;
|
||||
import trans::new_scope_block_ctxt;
|
||||
import trans::load_if_immediate;
|
||||
import trans::C_int;
|
||||
import trans::C_uint;
|
||||
import trans::C_nil;
|
||||
import trans::val_ty;
|
||||
import ty::pat_ty;
|
||||
import syntax::ast;
|
||||
import syntax::ast::def_id;
|
||||
import syntax::codemap::span;
|
||||
import util::common::lit_eq;
|
||||
|
||||
// An option identifying a branch (either a literal or a tag variant)
|
||||
tag opt {
|
||||
lit(@ast::lit);
|
||||
var(uint /* variant id */, tup(def_id, def_id) /* variant def ids */);
|
||||
}
|
||||
fn opt_eq(&opt a, &opt b) -> bool {
|
||||
alt (a) {
|
||||
lit(?la) {
|
||||
ret alt (b) { lit(?lb) { lit_eq(la, lb) } var(_, _) { false } };
|
||||
}
|
||||
var(?ida, _) {
|
||||
ret alt (b) { lit(_) { false } var(?idb, _) { ida == idb } };
|
||||
}
|
||||
}
|
||||
}
|
||||
fn trans_opt(&@crate_ctxt ccx, &opt o) -> ValueRef {
|
||||
alt (o) {
|
||||
lit(?l) { ret trans::trans_lit(ccx, *l); }
|
||||
var(?id, _) { ret C_int(id as int); }
|
||||
}
|
||||
}
|
||||
|
||||
fn variant_opt(&@crate_ctxt ccx, ast::node_id pat_id) -> opt {
|
||||
auto vdef = ast::variant_def_ids(ccx.tcx.def_map.get(pat_id));
|
||||
auto variants = ty::tag_variants(ccx.tcx, vdef._0);
|
||||
auto i = 0u;
|
||||
for (ty::variant_info v in variants) {
|
||||
if (vdef._1 == v.id) { ret var(i, vdef); }
|
||||
i += 1u;
|
||||
}
|
||||
fail;
|
||||
}
|
||||
|
||||
type bind_map = tup(ast::ident, ValueRef)[];
|
||||
type match_branch = @rec((@ast::pat)[] pats,
|
||||
BasicBlockRef body,
|
||||
mutable bind_map bound);
|
||||
type match = match_branch[];
|
||||
|
||||
fn matches_always(&@ast::pat p) -> bool {
|
||||
ret alt p.node {
|
||||
ast::pat_wild { true }
|
||||
ast::pat_bind(_) { true }
|
||||
ast::pat_rec(_, _) { true }
|
||||
_ { false }
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
fn bind_for_pat(&@ast::pat p, &match_branch br, ValueRef val) {
|
||||
alt p.node {
|
||||
ast::pat_bind(?name) {
|
||||
br.bound += ~[tup(name, val)];
|
||||
}
|
||||
_ {}
|
||||
}
|
||||
}
|
||||
|
||||
fn enter_default(&match m, uint col, ValueRef val) -> match {
|
||||
auto result = ~[];
|
||||
for (match_branch br in m) {
|
||||
if (matches_always(br.pats.(col))) {
|
||||
auto pats = ivec::slice(br.pats, 0u, col) +
|
||||
ivec::slice(br.pats, col + 1u, ivec::len(br.pats));
|
||||
auto new_br = @rec(pats=pats with *br);
|
||||
result += ~[new_br];
|
||||
bind_for_pat(br.pats.(col), new_br, val);
|
||||
}
|
||||
}
|
||||
ret result;
|
||||
}
|
||||
|
||||
fn enter_opt(&@crate_ctxt ccx, &match m, &opt opt,
|
||||
uint col, uint tag_size, ValueRef val) -> match {
|
||||
auto result = ~[];
|
||||
auto dummy = @rec(id=0, node=ast::pat_wild, span=rec(lo=0u, hi=0u));
|
||||
for (match_branch br in m) {
|
||||
auto pats = ivec::slice(br.pats, 0u, col);
|
||||
auto include = true;
|
||||
alt (br.pats.(col).node) {
|
||||
ast::pat_tag(?ctor, _) {
|
||||
include = opt_eq(variant_opt(ccx, br.pats.(col).id), opt);
|
||||
}
|
||||
ast::pat_lit(?l) {
|
||||
include = opt_eq(lit(l), opt);
|
||||
}
|
||||
_ {}
|
||||
}
|
||||
if (include) {
|
||||
alt (br.pats.(col).node) {
|
||||
ast::pat_tag(_, ?subpats) {
|
||||
assert ivec::len(subpats) == tag_size;
|
||||
pats += subpats;
|
||||
}
|
||||
_ {
|
||||
pats += ivec::init_elt(dummy, tag_size);
|
||||
}
|
||||
}
|
||||
pats += ivec::slice(br.pats, col + 1u, ivec::len(br.pats));
|
||||
auto new_br = @rec(pats=pats with *br);
|
||||
result += ~[new_br];
|
||||
bind_for_pat(br.pats.(col), new_br, val);
|
||||
}
|
||||
}
|
||||
ret result;
|
||||
}
|
||||
|
||||
fn enter_rec(&@crate_ctxt ccx, &match m, uint col, &ast::ident[] fields,
|
||||
ValueRef val) -> match {
|
||||
auto result = ~[];
|
||||
auto dummy = @rec(id=0, node=ast::pat_wild, span=rec(lo=0u, hi=0u));
|
||||
for (match_branch br in m) {
|
||||
auto pats = ivec::slice(br.pats, 0u, col);
|
||||
alt (br.pats.(col).node) {
|
||||
ast::pat_rec(?fpats, _) {
|
||||
for (ast::ident fname in fields) {
|
||||
auto pat = dummy;
|
||||
for (ast::field_pat fpat in fpats) {
|
||||
if (str::eq(fpat.ident, fname)) {
|
||||
pat = fpat.pat;
|
||||
break;
|
||||
}
|
||||
}
|
||||
pats += ~[pat];
|
||||
}
|
||||
}
|
||||
_ {
|
||||
pats += ivec::init_elt(dummy, ivec::len(fields));
|
||||
}
|
||||
}
|
||||
pats += ivec::slice(br.pats, col + 1u, ivec::len(br.pats));
|
||||
auto new_br = @rec(pats=pats with *br);
|
||||
result += ~[new_br];
|
||||
bind_for_pat(br.pats.(col), new_br, val);
|
||||
}
|
||||
ret result;
|
||||
}
|
||||
|
||||
fn get_options(&@crate_ctxt ccx, &match m, uint col) -> opt[] {
|
||||
fn add_to_set(&mutable opt[] set, &opt val) {
|
||||
for (opt l in set) {
|
||||
if (opt_eq(l, val)) { ret; }
|
||||
}
|
||||
set += ~[val];
|
||||
}
|
||||
|
||||
auto found = ~[];
|
||||
for (match_branch br in m) {
|
||||
alt (br.pats.(col).node) {
|
||||
ast::pat_lit(?l) { add_to_set(found, lit(l)); }
|
||||
ast::pat_tag(_, _) {
|
||||
add_to_set(found, variant_opt(ccx, br.pats.(col).id));
|
||||
}
|
||||
_ {}
|
||||
}
|
||||
}
|
||||
ret found;
|
||||
}
|
||||
|
||||
fn extract_variant_args(@block_ctxt bcx, ast::node_id pat_id,
|
||||
&tup(def_id, def_id) vdefs, ValueRef val)
|
||||
-> tup(ValueRef[], @block_ctxt) {
|
||||
auto ccx = bcx.fcx.lcx.ccx;
|
||||
auto ty_param_substs = ty::node_id_to_type_params(ccx.tcx, pat_id);
|
||||
auto blobptr = val;
|
||||
auto variants = ty::tag_variants(ccx.tcx, vdefs._0);
|
||||
auto args = ~[];
|
||||
auto size = ivec::len(ty::tag_variant_with_id
|
||||
(ccx.tcx, vdefs._0, vdefs._1).args);
|
||||
if (size > 0u && ivec::len(variants) != 1u) {
|
||||
auto tagptr = bcx.build.PointerCast
|
||||
(val, trans::T_opaque_tag_ptr(ccx.tn));
|
||||
blobptr = bcx.build.GEP(tagptr, ~[C_int(0), C_int(1)]);
|
||||
}
|
||||
auto i = 0u;
|
||||
while (i < size) {
|
||||
auto r = trans::GEP_tag(bcx, blobptr, vdefs._0, vdefs._1,
|
||||
ty_param_substs, i as int);
|
||||
bcx = r.bcx;
|
||||
args += ~[r.val];
|
||||
i += 1u;
|
||||
}
|
||||
ret tup(args, bcx);
|
||||
}
|
||||
|
||||
type exit_node = rec(bind_map bound,
|
||||
BasicBlockRef from,
|
||||
BasicBlockRef to);
|
||||
type mk_fail = fn() -> BasicBlockRef;
|
||||
|
||||
fn compile_submatch(@block_ctxt bcx, &match m, ValueRef[] vals, &mk_fail f,
|
||||
&mutable exit_node[] exits) {
|
||||
if (ivec::len(m) == 0u) {
|
||||
bcx.build.Br(f());
|
||||
ret;
|
||||
}
|
||||
if (ivec::len(m.(0).pats) == 0u) {
|
||||
exits += ~[rec(bound=m.(0).bound,
|
||||
from=bcx.llbb,
|
||||
to=m.(0).body)];
|
||||
bcx.build.Br(m.(0).body);
|
||||
ret;
|
||||
}
|
||||
|
||||
// FIXME maybe be clever about picking a column.
|
||||
auto col = 0u;
|
||||
auto val = vals.(col);
|
||||
auto vals_left = ivec::slice(vals, 1u, ivec::len(vals));
|
||||
auto ccx = bcx.fcx.lcx.ccx;
|
||||
auto pat_id = 0;
|
||||
|
||||
auto rec_fields = ~[];
|
||||
for (match_branch br in m) {
|
||||
// Find a real id (we're adding placeholder wildcard patterns, but
|
||||
// each column is guaranteed to have at least one real pattern)
|
||||
if (pat_id == 0) { pat_id = br.pats.(col).id; }
|
||||
// Gather field names
|
||||
alt (br.pats.(col).node) {
|
||||
ast::pat_rec(?fs, _) {
|
||||
for (ast::field_pat f in fs) {
|
||||
if (!ivec::any(bind str::eq(f.ident, _), rec_fields)) {
|
||||
rec_fields += ~[f.ident];
|
||||
}
|
||||
}
|
||||
}
|
||||
_ {}
|
||||
}
|
||||
}
|
||||
// Separate path for extracting and binding record fields
|
||||
if (ivec::len(rec_fields) > 0u) {
|
||||
auto rec_ty = ty::node_id_to_monotype(ccx.tcx, pat_id);
|
||||
auto fields = alt (ty::struct(ccx.tcx, rec_ty)) {
|
||||
ty::ty_rec(?fields) { fields }
|
||||
};
|
||||
auto rec_vals = ~[];
|
||||
for (ast::ident field_name in rec_fields) {
|
||||
let uint ix = ty::field_idx(ccx.sess, rec(lo=0u, hi=0u),
|
||||
field_name, fields);
|
||||
auto r = trans::GEP_tup_like(bcx, rec_ty, val, ~[0, ix as int]);
|
||||
rec_vals += ~[r.val];
|
||||
bcx = r.bcx;
|
||||
}
|
||||
compile_submatch(bcx, enter_rec(ccx, m, col, rec_fields, val),
|
||||
rec_vals + vals_left, f, exits);
|
||||
ret;
|
||||
}
|
||||
|
||||
// Decide what kind of branch we need
|
||||
auto opts = get_options(ccx, m, col);
|
||||
tag branch_kind { no_branch; single; switch; compare; }
|
||||
auto kind = no_branch;
|
||||
auto test_val = val;
|
||||
if (ivec::len(opts) > 0u) {
|
||||
alt (opts.(0)) {
|
||||
var(_, ?vdef) {
|
||||
if (ivec::len(ty::tag_variants(ccx.tcx, vdef._0)) == 1u) {
|
||||
kind = single;
|
||||
} else {
|
||||
auto tagptr = bcx.build.PointerCast
|
||||
(val, trans::T_opaque_tag_ptr(ccx.tn));
|
||||
auto discrimptr = bcx.build.GEP
|
||||
(tagptr, ~[C_int(0), C_int(0)]);
|
||||
test_val = bcx.build.Load(discrimptr);
|
||||
kind = switch;
|
||||
}
|
||||
}
|
||||
lit(?l) {
|
||||
test_val = bcx.build.Load(val);
|
||||
kind = alt (l.node) {
|
||||
ast::lit_str(_, _) { compare }
|
||||
_ { switch }
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
auto else_cx = alt (kind) {
|
||||
no_branch | single { bcx }
|
||||
_ { new_sub_block_ctxt(bcx, "match_else") }
|
||||
};
|
||||
auto sw = if (kind == switch) {
|
||||
bcx.build.Switch(test_val, else_cx.llbb, ivec::len(opts))
|
||||
} else { C_int(0) }; // Placeholder for when not using a switch
|
||||
|
||||
// Compile subtrees for each option
|
||||
for (opt opt in opts) {
|
||||
auto opt_cx = new_sub_block_ctxt(bcx, "match_case");
|
||||
alt (kind) {
|
||||
single { bcx.build.Br(opt_cx.llbb); }
|
||||
switch { llvm::LLVMAddCase(sw, trans_opt(ccx, opt), opt_cx.llbb);}
|
||||
compare {
|
||||
auto t = ty::node_id_to_type(ccx.tcx, pat_id);
|
||||
auto eq = trans::trans_compare(bcx, ast::eq, t, test_val,
|
||||
trans_opt(ccx, opt));
|
||||
bcx = new_sub_block_ctxt(bcx, "next");
|
||||
eq.bcx.build.CondBr(eq.val, opt_cx.llbb, bcx.llbb);
|
||||
}
|
||||
_ {}
|
||||
}
|
||||
auto size = 0u;
|
||||
auto unpacked = ~[];
|
||||
alt opt {
|
||||
var(_, ?vdef) {
|
||||
auto args = extract_variant_args(opt_cx, pat_id, vdef, val);
|
||||
size = ivec::len(args._0);
|
||||
unpacked = args._0;
|
||||
opt_cx = args._1;
|
||||
}
|
||||
lit(_) { }
|
||||
}
|
||||
compile_submatch(opt_cx, enter_opt(ccx, m, opt, col, size, val),
|
||||
unpacked + vals_left, f, exits);
|
||||
}
|
||||
|
||||
// Compile the fall-through case
|
||||
if (kind == compare) { bcx.build.Br(else_cx.llbb); }
|
||||
if (kind != single) {
|
||||
compile_submatch(else_cx, enter_default(m, col, val), vals_left,
|
||||
f, exits);
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME breaks on unreacheable cases
|
||||
fn make_phi_bindings(&@block_ctxt bcx, &exit_node[] map,
|
||||
&ast::pat_id_map ids) {
|
||||
fn assoc(str key, &tup(str, ValueRef)[] list) -> ValueRef {
|
||||
for (tup(str, ValueRef) elt in list) {
|
||||
if (str::eq(elt._0, key)) { ret elt._1; }
|
||||
}
|
||||
fail;
|
||||
}
|
||||
|
||||
auto our_block = bcx.llbb as uint;
|
||||
for each (@tup(ast::ident, ast::node_id) item in ids.items()) {
|
||||
auto llbbs = ~[];
|
||||
auto vals = ~[];
|
||||
for (exit_node ex in map) {
|
||||
if (ex.to as uint == our_block) {
|
||||
llbbs += ~[ex.from];
|
||||
vals += ~[assoc(item._0, ex.bound)];
|
||||
}
|
||||
}
|
||||
auto phi = bcx.build.Phi(val_ty(vals.(0)), vals, llbbs);
|
||||
bcx.fcx.lllocals.insert(item._1, phi);
|
||||
}
|
||||
}
|
||||
|
||||
fn trans_alt(&@block_ctxt cx, &@ast::expr expr, &ast::arm[] arms,
|
||||
ast::node_id id, &trans::out_method output) -> result {
|
||||
auto bodies = ~[];
|
||||
let match match = ~[];
|
||||
for (ast::arm a in arms) {
|
||||
auto body = new_scope_block_ctxt(cx, "case_body");
|
||||
bodies += ~[body];
|
||||
for (@ast::pat p in a.pats) {
|
||||
match += ~[@rec(pats=~[p], body=body.llbb, mutable bound=~[])];
|
||||
}
|
||||
}
|
||||
|
||||
// Cached fail-on-fallthrough block
|
||||
auto fail_cx = @mutable none;
|
||||
fn mk_fail(&@block_ctxt cx, &span sp,
|
||||
@mutable option::t[BasicBlockRef] done) -> BasicBlockRef {
|
||||
alt (*done) {
|
||||
some(?bb) { ret bb; }
|
||||
_ {}
|
||||
}
|
||||
auto fail_cx = new_sub_block_ctxt(cx, "case_fallthrough");
|
||||
trans::trans_fail(fail_cx, some(sp), "non-exhaustive match failure");
|
||||
*done = some(fail_cx.llbb);
|
||||
ret fail_cx.llbb;
|
||||
}
|
||||
|
||||
auto exit_map = ~[];
|
||||
auto er = trans::trans_expr(cx, expr);
|
||||
auto t = trans::node_id_type(cx.fcx.lcx.ccx, expr.id);
|
||||
auto v = trans::spill_if_immediate(er.bcx, er.val, t);
|
||||
compile_submatch(er.bcx, match, ~[v],
|
||||
bind mk_fail(cx, expr.span, fail_cx), exit_map);
|
||||
|
||||
auto i = 0u;
|
||||
auto arm_results = ~[];
|
||||
for (ast::arm a in arms) {
|
||||
auto body_cx = bodies.(i);
|
||||
make_phi_bindings(body_cx, exit_map, ast::pat_id_map(a.pats.(0)));
|
||||
auto block_res = trans::trans_block(body_cx, a.block, output);
|
||||
arm_results += ~[block_res];
|
||||
i += 1u;
|
||||
}
|
||||
ret rslt(trans::join_branches(cx, arm_results), C_nil());
|
||||
}
|
||||
|
||||
// Local Variables:
|
||||
// fill-column: 78;
|
||||
// indent-tabs-mode: nil
|
||||
// c-basic-offset: 4
|
||||
// buffer-file-coding-system: utf-8-unix
|
||||
// compile-command: "make -k -C $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
|
||||
// End:
|
@ -16,6 +16,7 @@ use std (name = "std",
|
||||
|
||||
mod middle {
|
||||
mod trans;
|
||||
mod trans_alt;
|
||||
mod ty;
|
||||
mod ast_map;
|
||||
mod resolve;
|
||||
|
@ -195,6 +195,7 @@ fn call_kind_str(call_kind c) -> str {
|
||||
case (kind_bind) { "Bind" }
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Local Variables:
|
||||
// mode: rust
|
||||
|
Loading…
x
Reference in New Issue
Block a user