rustc: Teach trans::alt.rs about exhaustive alts

Closes #1971
This commit is contained in:
Marijn Haverbeke 2012-03-13 13:42:29 +01:00
parent 15985277b9
commit 463cf837eb
2 changed files with 82 additions and 72 deletions

View file

@ -354,10 +354,10 @@ fn pick_col(m: match) -> uint {
ret best_col; ret best_col;
} }
fn compile_submatch(bcx: block, m: match, vals: [ValueRef], f: mk_fail, fn compile_submatch(bcx: block, m: match, vals: [ValueRef],
&exits: [exit_node]) { chk: option<mk_fail>, &exits: [exit_node]) {
let bcx = bcx, tcx = bcx.tcx(), dm = tcx.def_map; let bcx = bcx, tcx = bcx.tcx(), dm = tcx.def_map;
if m.len() == 0u { Br(bcx, f()); ret; } if m.len() == 0u { Br(bcx, option::get(chk)()); ret; }
if m[0].pats.len() == 0u { if m[0].pats.len() == 0u {
let data = m[0].data; let data = m[0].data;
alt data.guard { alt data.guard {
@ -372,7 +372,7 @@ fn compile_submatch(bcx: block, m: match, vals: [ValueRef], f: mk_fail,
trans_temp_expr(bcx, e) trans_temp_expr(bcx, e)
}; };
bcx = with_cond(guard_cx, Not(guard_cx, val)) {|bcx| bcx = with_cond(guard_cx, Not(guard_cx, val)) {|bcx|
compile_submatch(bcx, vec::tail(m), vals, f, exits); compile_submatch(bcx, vec::tail(m), vals, chk, exits);
bcx bcx
}; };
} }
@ -389,13 +389,10 @@ fn compile_submatch(bcx: block, m: match, vals: [ValueRef], f: mk_fail,
let val = vals[col]; let val = vals[col];
let m = if has_nested_bindings(m, col) { let m = if has_nested_bindings(m, col) {
expand_nested_bindings(m, col, val) expand_nested_bindings(m, col, val)
} else { } else { m };
m
};
let vals_left = let vals_left = vec::slice(vals, 0u, col) +
vec::slice(vals, 0u, col) + vec::slice(vals, col + 1u, vals.len());
vec::slice(vals, col + 1u, vals.len());
let ccx = bcx.fcx.ccx; let ccx = bcx.fcx.ccx;
let pat_id = 0; let pat_id = 0;
for br: match_branch in m { for br: match_branch in m {
@ -417,7 +414,7 @@ fn compile_submatch(bcx: block, m: match, vals: [ValueRef], f: mk_fail,
bcx = r.bcx; bcx = r.bcx;
} }
compile_submatch(bcx, enter_rec(dm, m, col, rec_fields, val), compile_submatch(bcx, enter_rec(dm, m, col, rec_fields, val),
rec_vals + vals_left, f, exits); rec_vals + vals_left, chk, exits);
ret; ret;
} }
@ -435,7 +432,7 @@ fn compile_submatch(bcx: block, m: match, vals: [ValueRef], f: mk_fail,
i += 1u; i += 1u;
} }
compile_submatch(bcx, enter_tup(dm, m, col, val, n_tup_elts), compile_submatch(bcx, enter_tup(dm, m, col, val, n_tup_elts),
tup_vals + vals_left, f, exits); tup_vals + vals_left, chk, exits);
ret; ret;
} }
@ -444,14 +441,14 @@ fn compile_submatch(bcx: block, m: match, vals: [ValueRef], f: mk_fail,
let box = Load(bcx, val); let box = Load(bcx, val);
let unboxed = GEPi(bcx, box, [0, abi::box_field_body]); let unboxed = GEPi(bcx, box, [0, abi::box_field_body]);
compile_submatch(bcx, enter_box(dm, m, col, val), [unboxed] compile_submatch(bcx, enter_box(dm, m, col, val), [unboxed]
+ vals_left, f, exits); + vals_left, chk, exits);
ret; ret;
} }
if any_uniq_pat(m, col) { if any_uniq_pat(m, col) {
let unboxed = Load(bcx, val); let unboxed = Load(bcx, val);
compile_submatch(bcx, enter_uniq(dm, m, col, val), compile_submatch(bcx, enter_uniq(dm, m, col, val),
[unboxed] + vals_left, f, exits); [unboxed] + vals_left, chk, exits);
ret; ret;
} }
@ -499,42 +496,48 @@ fn compile_submatch(bcx: block, m: match, vals: [ValueRef], f: mk_fail,
Switch(bcx, test_val, else_cx.llbb, opts.len()) Switch(bcx, test_val, else_cx.llbb, opts.len())
} else { C_int(ccx, 0) }; // Placeholder for when not using a switch } else { C_int(ccx, 0) }; // Placeholder for when not using a switch
// Compile subtrees for each option let defaults = enter_default(dm, m, col, val);
for opt: opt in opts { let exhaustive = option::is_none(chk) && defaults.len() == 0u;
let opt_cx = sub_block(bcx, "match_case"); let len = opts.len(), i = 0u;
alt kind { // Compile subtrees for each option
single { Br(bcx, opt_cx.llbb); } for opt in opts {
switch { i += 1u;
let res = trans_opt(bcx, opt); let opt_cx = else_cx;
alt check res { if !exhaustive || i < len {
single_result(r) { opt_cx = sub_block(bcx, "match_case");
llvm::LLVMAddCase(sw, r.val, opt_cx.llbb); alt kind {
bcx = r.bcx; single { Br(bcx, opt_cx.llbb); }
} switch {
} alt check trans_opt(bcx, opt) {
} single_result(r) {
compare { llvm::LLVMAddCase(sw, r.val, opt_cx.llbb);
let t = node_id_type(bcx, pat_id); bcx = r.bcx;
let {bcx: after_cx, val: matches} =
with_scope_result(bcx, "compare_scope") {|bcx|
alt trans_opt(bcx, opt) {
single_result({bcx, val}) {
trans_compare(bcx, ast::eq, test_val, t, val, t)
}
range_result({val: vbegin, _}, {bcx, val: vend}) {
let {bcx, val: ge} = trans_compare(bcx, ast::ge, test_val,
t, vbegin, t);
let {bcx, val: le} = trans_compare(bcx, ast::le, test_val,
t, vend, t);
{bcx: bcx, val: And(bcx, ge, le)}
} }
} }
}; }
bcx = sub_block(after_cx, "compare_next"); compare {
CondBr(after_cx, matches, opt_cx.llbb, bcx.llbb); let t = node_id_type(bcx, pat_id);
} let {bcx: after_cx, val: matches} =
_ { } with_scope_result(bcx, "compare_scope") {|bcx|
} alt trans_opt(bcx, opt) {
single_result({bcx, val}) {
trans_compare(bcx, ast::eq, test_val, t, val, t)
}
range_result({val: vbegin, _}, {bcx, val: vend}) {
let {bcx, val: ge} = trans_compare(
bcx, ast::ge, test_val, t, vbegin, t);
let {bcx, val: le} = trans_compare(
bcx, ast::le, test_val, t, vend, t);
{bcx: bcx, val: And(bcx, ge, le)}
}
}
};
bcx = sub_block(after_cx, "compare_next");
CondBr(after_cx, matches, opt_cx.llbb, bcx.llbb);
}
_ { }
}
} else if kind == compare { Br(bcx, else_cx.llbb); }
let size = 0u; let size = 0u;
let unpacked = []; let unpacked = [];
alt opt { alt opt {
@ -547,14 +550,15 @@ fn compile_submatch(bcx: block, m: match, vals: [ValueRef], f: mk_fail,
lit(_) | range(_, _) { } lit(_) | range(_, _) { }
} }
compile_submatch(opt_cx, enter_opt(tcx, m, opt, col, size, val), compile_submatch(opt_cx, enter_opt(tcx, m, opt, col, size, val),
unpacked + vals_left, f, exits); unpacked + vals_left, chk, exits);
} }
// Compile the fall-through case // Compile the fall-through case, if any
if kind == compare { Br(bcx, else_cx.llbb); } if !exhaustive {
if kind != single { if kind == compare { Br(bcx, else_cx.llbb); }
compile_submatch(else_cx, enter_default(dm, m, col, val), vals_left, if kind != single {
f, exits); compile_submatch(else_cx, defaults, vals_left, chk, exits);
}
} }
} }
@ -605,12 +609,14 @@ fn make_phi_bindings(bcx: block, map: [exit_node],
} }
fn trans_alt(bcx: block, expr: @ast::expr, arms: [ast::arm], fn trans_alt(bcx: block, expr: @ast::expr, arms: [ast::arm],
dest: dest) -> block { mode: ast::alt_mode, dest: dest) -> block {
with_scope(bcx, "alt") {|bcx| trans_alt_inner(bcx, expr, arms, dest)} with_scope(bcx, "alt") {|bcx|
trans_alt_inner(bcx, expr, arms, mode, dest)
}
} }
fn trans_alt_inner(scope_cx: block, expr: @ast::expr, arms: [ast::arm], fn trans_alt_inner(scope_cx: block, expr: @ast::expr, arms: [ast::arm],
dest: dest) -> block { mode: ast::alt_mode, dest: dest) -> block {
let bcx = scope_cx, tcx = bcx.tcx(); let bcx = scope_cx, tcx = bcx.tcx();
let bodies = [], match = []; let bodies = [], match = [];
@ -630,22 +636,26 @@ fn trans_alt_inner(scope_cx: block, expr: @ast::expr, arms: [ast::arm],
} }
} }
// Cached fail-on-fallthrough block let mk_fail = alt mode {
let fail_cx = @mutable none; ast::alt_check {
fn mk_fail(bcx: block, sp: span, // Cached fail-on-fallthrough block
done: @mutable option<BasicBlockRef>) -> BasicBlockRef { let fail_cx = @mutable none;
alt *done { some(bb) { ret bb; } _ { } } fn mk_fail(bcx: block, sp: span,
let fail_cx = sub_block(bcx, "case_fallthrough"); done: @mutable option<BasicBlockRef>) -> BasicBlockRef {
trans_fail(fail_cx, some(sp), "non-exhaustive match failure");; alt *done { some(bb) { ret bb; } _ { } }
*done = some(fail_cx.llbb); let fail_cx = sub_block(bcx, "case_fallthrough");
ret fail_cx.llbb; trans_fail(fail_cx, some(sp), "non-exhaustive match failure");;
} *done = some(fail_cx.llbb);
ret fail_cx.llbb;
}
some(bind mk_fail(scope_cx, expr.span, fail_cx))
}
ast::alt_exhaustive { none }
};
let exit_map = []; let exit_map = [];
let t = node_id_type(bcx, expr.id); let t = node_id_type(bcx, expr.id);
let {bcx, val: spilled} = spill_if_immediate(bcx, val, t); let {bcx, val: spilled} = spill_if_immediate(bcx, val, t);
compile_submatch(bcx, match, [spilled], compile_submatch(bcx, match, [spilled], mk_fail, exit_map);
bind mk_fail(scope_cx, expr.span, fail_cx), exit_map);
let arm_cxs = [], arm_dests = [], i = 0u; let arm_cxs = [], arm_dests = [], i = 0u;
for a in arms { for a in arms {

View file

@ -3141,8 +3141,8 @@ fn trans_expr(bcx: block, e: @ast::expr, dest: dest) -> block {
ast::expr_if(cond, thn, els) | ast::expr_if_check(cond, thn, els) { ast::expr_if(cond, thn, els) | ast::expr_if_check(cond, thn, els) {
ret trans_if(bcx, cond, thn, els, dest); ret trans_if(bcx, cond, thn, els, dest);
} }
ast::expr_alt(expr, arms, _) { ast::expr_alt(expr, arms, mode) {
ret alt::trans_alt(bcx, expr, arms, dest); ret alt::trans_alt(bcx, expr, arms, mode, dest);
} }
ast::expr_block(blk) { ast::expr_block(blk) {
ret with_scope(bcx, "block-expr body") {|bcx| ret with_scope(bcx, "block-expr body") {|bcx|