about summary refs log tree commit diff
path: root/src/rustc
diff options
context:
space:
mode:
authorTim Chevalier <chevalier@alum.wellesley.edu>2012-07-27 13:13:03 -0700
committerTim Chevalier <chevalier@alum.wellesley.edu>2012-07-27 13:14:03 -0700
commit300f54ebc0aaecb05cf4b7efd5bc17d326ec9535 (patch)
treea34ff08b483ba26369df7b7dbe83f7c17e0387a1 /src/rustc
parent8fdf77a20daba89f117521b471ef77b087c94c4b (diff)
downloadrust-300f54ebc0aaecb05cf4b7efd5bc17d326ec9535.tar.gz
rust-300f54ebc0aaecb05cf4b7efd5bc17d326ec9535.zip
Make alts on uninhabited enum types typecheck and translate properly
Possibly one of the silliest Rust commits ever.

Closes #3037
Diffstat (limited to 'src/rustc')
-rw-r--r--src/rustc/middle/check_alt.rs20
-rw-r--r--src/rustc/middle/trans/alt.rs35
-rw-r--r--src/rustc/middle/ty.rs10
3 files changed, 55 insertions, 10 deletions
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 pick_col(m: match) -> uint {
 
 fn compile_submatch(bcx: block, m: match, vals: ~[ValueRef],
                     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 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>) -> 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 operators;
 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; }