Skip to content

Commit 300f54e

Browse files
committed
Make alts on uninhabited enum types typecheck and translate properly
Possibly one of the silliest Rust commits ever. Closes #3037
1 parent 8fdf77a commit 300f54e

File tree

4 files changed

+66
-10
lines changed

4 files changed

+66
-10
lines changed

src/rustc/middle/check_alt.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,23 @@ fn check_expr(tcx: ty::ctxt, ex: @expr, &&s: (), v: visit::vt<()>) {
2626
expr_alt(scrut, arms, mode) {
2727
check_arms(tcx, arms);
2828
/* Check for exhaustiveness */
29+
// Check for empty enum, because is_useful only works on inhabited
30+
// types.
31+
let pat_ty = node_id_to_type(tcx, scrut.id);
32+
if type_is_empty(tcx, pat_ty) && arms.is_empty() {
33+
// Vacuously exhaustive
34+
ret;
35+
}
36+
alt ty::get(pat_ty).struct {
37+
ty_enum(did, _) {
38+
if (*enum_variants(tcx, did)).is_empty() && arms.is_empty() {
39+
40+
ret;
41+
}
42+
}
43+
_ { /* We assume only enum types can be uninhabited */ }
44+
}
45+
2946
if mode == alt_exhaustive {
3047
let arms = vec::concat(vec::filter_map(arms, unguarded_pat));
3148
check_exhaustive(tcx, ex.span, arms);
@@ -60,6 +77,7 @@ fn raw_pat(p: @pat) -> @pat {
6077
}
6178

6279
fn check_exhaustive(tcx: ty::ctxt, sp: span, pats: ~[@pat]) {
80+
assert(pats.is_not_empty());
6381
let ext = alt is_useful(tcx, vec::map(pats, |p| ~[p]), ~[wild()]) {
6482
not_useful { ret; } // This is good, wildcard pattern isn't reachable
6583
useful_ { none }
@@ -111,6 +129,8 @@ enum ctor {
111129
// checking (if a wildcard pattern is useful in relation to a matrix, the
112130
// matrix isn't exhaustive).
113131

132+
// Note: is_useful doesn't work on empty types, as the paper notes.
133+
// So it assumes that v is non-empty.
114134
fn is_useful(tcx: ty::ctxt, m: matrix, v: ~[@pat]) -> useful {
115135
if m.len() == 0u { ret useful_; }
116136
if m[0].len() == 0u { ret not_useful; }

src/rustc/middle/trans/alt.rs

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,10 @@ fn pick_col(m: match) -> uint {
385385

386386
fn compile_submatch(bcx: block, m: match, vals: ~[ValueRef],
387387
chk: option<mk_fail>, &exits: ~[exit_node]) {
388+
/*
389+
For an empty match, a fall-through case must exist
390+
*/
391+
assert(m.len() > 0u || is_some(chk));
388392
let _icx = bcx.insn_ctxt(~"alt::compile_submatch");
389393
let mut bcx = bcx;
390394
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],
664668
}
665669
}
666670

667-
let mk_fail = alt mode {
668-
ast::alt_check {
669-
// Cached fail-on-fallthrough block
670-
let fail_cx = @mut none;
671-
fn mk_fail(bcx: block, sp: span,
671+
fn mk_fail(bcx: block, sp: span, msg: ~str,
672672
done: @mut option<BasicBlockRef>) -> BasicBlockRef {
673673
alt *done { some(bb) { ret bb; } _ { } }
674674
let fail_cx = sub_block(bcx, ~"case_fallthrough");
675-
trans_fail(fail_cx, some(sp), ~"non-exhaustive match failure");;
675+
trans_fail(fail_cx, some(sp), msg);
676676
*done = some(fail_cx.llbb);
677677
ret fail_cx.llbb;
678-
}
679-
some(|| mk_fail(scope_cx, expr.span, fail_cx))
678+
}
679+
let t = node_id_type(bcx, expr.id);
680+
let mk_fail = alt mode {
681+
ast::alt_check {
682+
let fail_cx = @mut none;
683+
// Cached fail-on-fallthrough block
684+
some(|| mk_fail(scope_cx, expr.span, ~"non-exhaustive match failure",
685+
fail_cx))
686+
}
687+
ast::alt_exhaustive {
688+
let fail_cx = @mut none;
689+
// special case for uninhabited type
690+
if ty::type_is_empty(tcx, t) {
691+
some(|| mk_fail(scope_cx, expr.span,
692+
~"scrutinizing value that can't exist", fail_cx))
693+
}
694+
else {
695+
none
696+
}
680697
}
681-
ast::alt_exhaustive { none }
682698
};
683699
let mut exit_map = ~[];
684-
let t = node_id_type(bcx, expr.id);
685700
let spilled = spill_if_immediate(bcx, val, t);
686701
compile_submatch(bcx, match, ~[spilled], mk_fail, exit_map);
687702

src/rustc/middle/ty.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ export operators;
117117
export type_err, terr_vstore_kind;
118118
export type_err_to_str;
119119
export type_needs_drop;
120+
export type_is_empty;
120121
export type_is_integral;
121122
export type_is_numeric;
122123
export type_is_pod;
@@ -2748,6 +2749,15 @@ fn enum_is_univariant(cx: ctxt, id: ast::def_id) -> bool {
27482749
vec::len(*enum_variants(cx, id)) == 1u
27492750
}
27502751

2752+
fn type_is_empty(cx: ctxt, t: t) -> bool {
2753+
alt ty::get(t).struct {
2754+
ty_enum(did, _) {
2755+
(*enum_variants(cx, did)).is_empty()
2756+
}
2757+
_ { false }
2758+
}
2759+
}
2760+
27512761
fn enum_variants(cx: ctxt, id: ast::def_id) -> @~[variant_info] {
27522762
alt cx.enum_var_cache.find(id) {
27532763
some(variants) { ret variants; }

src/test/run-pass/issue-3037.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
enum what { }
2+
3+
fn what_to_str(x: what) -> ~str
4+
{
5+
alt x {
6+
}
7+
}
8+
9+
fn main()
10+
{
11+
}

0 commit comments

Comments
 (0)