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:
parent
8fdf77a20d
commit
300f54ebc0
@ -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; }
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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; }
|
||||||
|
11
src/test/run-pass/issue-3037.rs
Normal file
11
src/test/run-pass/issue-3037.rs
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
enum what { }
|
||||||
|
|
||||||
|
fn what_to_str(x: what) -> ~str
|
||||||
|
{
|
||||||
|
alt x {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main()
|
||||||
|
{
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user