diff --git a/src/rustc/middle/check_alt.rs b/src/rustc/middle/check_alt.rs index 9b2bcc31572..2b6e09a2ad5 100644 --- a/src/rustc/middle/check_alt.rs +++ b/src/rustc/middle/check_alt.rs @@ -26,6 +26,23 @@ fn check_expr(tcx: ty::ctxt, ex: @expr, &&s: (), v: visit::vt<()>) { expr_alt(scrut, arms, mode) { check_arms(tcx, arms); /* 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 { let arms = vec::concat(vec::filter_map(arms, unguarded_pat)); 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]) { + assert(pats.is_not_empty()); let ext = alt is_useful(tcx, vec::map(pats, |p| ~[p]), ~[wild()]) { not_useful { ret; } // This is good, wildcard pattern isn't reachable useful_ { none } @@ -111,6 +129,8 @@ enum ctor { // checking (if a wildcard pattern is useful in relation to a matrix, the // 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 { if m.len() == 0u { ret useful_; } if m[0].len() == 0u { ret not_useful; } diff --git a/src/rustc/middle/trans/alt.rs b/src/rustc/middle/trans/alt.rs index 7921afbd039..c652facd30e 100644 --- a/src/rustc/middle/trans/alt.rs +++ b/src/rustc/middle/trans/alt.rs @@ -385,6 +385,10 @@ fn score(p: @ast::pat) -> uint { fn compile_submatch(bcx: block, m: match, vals: ~[ValueRef], chk: option, &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 mut bcx = bcx; 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 { - ast::alt_check { - // Cached fail-on-fallthrough block - let fail_cx = @mut none; - fn mk_fail(bcx: block, sp: span, + fn mk_fail(bcx: block, sp: span, msg: ~str, done: @mut option) -> BasicBlockRef { alt *done { some(bb) { ret bb; } _ { } } 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); 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 t = node_id_type(bcx, expr.id); let spilled = spill_if_immediate(bcx, val, t); compile_submatch(bcx, match, ~[spilled], mk_fail, exit_map); diff --git a/src/rustc/middle/ty.rs b/src/rustc/middle/ty.rs index 7b8c9958d0f..d99e35ce418 100644 --- a/src/rustc/middle/ty.rs +++ b/src/rustc/middle/ty.rs @@ -117,6 +117,7 @@ export type_err, terr_vstore_kind; export type_err_to_str; export type_needs_drop; +export type_is_empty; export type_is_integral; export type_is_numeric; 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 } +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] { alt cx.enum_var_cache.find(id) { some(variants) { ret variants; } diff --git a/src/test/run-pass/issue-3037.rs b/src/test/run-pass/issue-3037.rs new file mode 100644 index 00000000000..92e18bde012 --- /dev/null +++ b/src/test/run-pass/issue-3037.rs @@ -0,0 +1,11 @@ +enum what { } + +fn what_to_str(x: what) -> ~str +{ + alt x { + } +} + +fn main() +{ +}