Make alts on uninhabited enum types typecheck and translate properly

Possibly one of the silliest Rust commits ever.

Closes #3037
This commit is contained in:
Tim Chevalier 2012-07-27 13:13:03 -07:00
parent 8fdf77a20d
commit 300f54ebc0
4 changed files with 66 additions and 10 deletions

View File

@ -26,6 +26,23 @@ fn check_expr(tcx: ty::ctxt, ex: @expr, &&s: (), v: visit::vt<()>) {
expr_alt(scrut, arms, mode) { expr_alt(scrut, arms, mode) {
check_arms(tcx, arms); check_arms(tcx, arms);
/* Check for exhaustiveness */ /* Check for exhaustiveness */
// Check for empty enum, because is_useful only works on inhabited
// types.
let pat_ty = node_id_to_type(tcx, scrut.id);
if type_is_empty(tcx, pat_ty) && arms.is_empty() {
// Vacuously exhaustive
ret;
}
alt ty::get(pat_ty).struct {
ty_enum(did, _) {
if (*enum_variants(tcx, did)).is_empty() && arms.is_empty() {
ret;
}
}
_ { /* We assume only enum types can be uninhabited */ }
}
if mode == alt_exhaustive { if mode == alt_exhaustive {
let arms = vec::concat(vec::filter_map(arms, unguarded_pat)); let arms = vec::concat(vec::filter_map(arms, unguarded_pat));
check_exhaustive(tcx, ex.span, arms); check_exhaustive(tcx, ex.span, arms);
@ -60,6 +77,7 @@ fn raw_pat(p: @pat) -> @pat {
} }
fn check_exhaustive(tcx: ty::ctxt, sp: span, pats: ~[@pat]) { fn check_exhaustive(tcx: ty::ctxt, sp: span, pats: ~[@pat]) {
assert(pats.is_not_empty());
let ext = alt is_useful(tcx, vec::map(pats, |p| ~[p]), ~[wild()]) { let ext = alt is_useful(tcx, vec::map(pats, |p| ~[p]), ~[wild()]) {
not_useful { ret; } // This is good, wildcard pattern isn't reachable not_useful { ret; } // This is good, wildcard pattern isn't reachable
useful_ { none } useful_ { none }
@ -111,6 +129,8 @@ enum ctor {
// checking (if a wildcard pattern is useful in relation to a matrix, the // checking (if a wildcard pattern is useful in relation to a matrix, the
// matrix isn't exhaustive). // matrix isn't exhaustive).
// Note: is_useful doesn't work on empty types, as the paper notes.
// So it assumes that v is non-empty.
fn is_useful(tcx: ty::ctxt, m: matrix, v: ~[@pat]) -> useful { fn is_useful(tcx: ty::ctxt, m: matrix, v: ~[@pat]) -> useful {
if m.len() == 0u { ret useful_; } if m.len() == 0u { ret useful_; }
if m[0].len() == 0u { ret not_useful; } if m[0].len() == 0u { ret not_useful; }

View File

@ -385,6 +385,10 @@ fn score(p: @ast::pat) -> uint {
fn compile_submatch(bcx: block, m: match, vals: ~[ValueRef], fn compile_submatch(bcx: block, m: match, vals: ~[ValueRef],
chk: option<mk_fail>, &exits: ~[exit_node]) { chk: option<mk_fail>, &exits: ~[exit_node]) {
/*
For an empty match, a fall-through case must exist
*/
assert(m.len() > 0u || is_some(chk));
let _icx = bcx.insn_ctxt(~"alt::compile_submatch"); let _icx = bcx.insn_ctxt(~"alt::compile_submatch");
let mut bcx = bcx; let mut bcx = bcx;
let tcx = bcx.tcx(), dm = tcx.def_map; let tcx = bcx.tcx(), dm = tcx.def_map;
@ -664,24 +668,35 @@ fn trans_alt_inner(scope_cx: block, expr: @ast::expr, arms: ~[ast::arm],
} }
} }
let mk_fail = alt mode { fn mk_fail(bcx: block, sp: span, msg: ~str,
ast::alt_check {
// Cached fail-on-fallthrough block
let fail_cx = @mut none;
fn mk_fail(bcx: block, sp: span,
done: @mut option<BasicBlockRef>) -> BasicBlockRef { done: @mut option<BasicBlockRef>) -> BasicBlockRef {
alt *done { some(bb) { ret bb; } _ { } } alt *done { some(bb) { ret bb; } _ { } }
let fail_cx = sub_block(bcx, ~"case_fallthrough"); let fail_cx = sub_block(bcx, ~"case_fallthrough");
trans_fail(fail_cx, some(sp), ~"non-exhaustive match failure");; trans_fail(fail_cx, some(sp), msg);
*done = some(fail_cx.llbb); *done = some(fail_cx.llbb);
ret fail_cx.llbb; ret fail_cx.llbb;
} }
some(|| mk_fail(scope_cx, expr.span, fail_cx)) let t = node_id_type(bcx, expr.id);
let mk_fail = alt mode {
ast::alt_check {
let fail_cx = @mut none;
// Cached fail-on-fallthrough block
some(|| mk_fail(scope_cx, expr.span, ~"non-exhaustive match failure",
fail_cx))
}
ast::alt_exhaustive {
let fail_cx = @mut none;
// special case for uninhabited type
if ty::type_is_empty(tcx, t) {
some(|| mk_fail(scope_cx, expr.span,
~"scrutinizing value that can't exist", fail_cx))
}
else {
none
}
} }
ast::alt_exhaustive { none }
}; };
let mut exit_map = ~[]; let mut exit_map = ~[];
let t = node_id_type(bcx, expr.id);
let spilled = spill_if_immediate(bcx, val, t); let spilled = spill_if_immediate(bcx, val, t);
compile_submatch(bcx, match, ~[spilled], mk_fail, exit_map); compile_submatch(bcx, match, ~[spilled], mk_fail, exit_map);

View File

@ -117,6 +117,7 @@
export type_err, terr_vstore_kind; export type_err, terr_vstore_kind;
export type_err_to_str; export type_err_to_str;
export type_needs_drop; export type_needs_drop;
export type_is_empty;
export type_is_integral; export type_is_integral;
export type_is_numeric; export type_is_numeric;
export type_is_pod; export type_is_pod;
@ -2748,6 +2749,15 @@ fn enum_is_univariant(cx: ctxt, id: ast::def_id) -> bool {
vec::len(*enum_variants(cx, id)) == 1u vec::len(*enum_variants(cx, id)) == 1u
} }
fn type_is_empty(cx: ctxt, t: t) -> bool {
alt ty::get(t).struct {
ty_enum(did, _) {
(*enum_variants(cx, did)).is_empty()
}
_ { false }
}
}
fn enum_variants(cx: ctxt, id: ast::def_id) -> @~[variant_info] { fn enum_variants(cx: ctxt, id: ast::def_id) -> @~[variant_info] {
alt cx.enum_var_cache.find(id) { alt cx.enum_var_cache.find(id) {
some(variants) { ret variants; } some(variants) { ret variants; }

View File

@ -0,0 +1,11 @@
enum what { }
fn what_to_str(x: what) -> ~str
{
alt x {
}
}
fn main()
{
}