about summary refs log tree commit diff
diff options
context:
space:
mode:
authorPatrick Walton <pcwalton@mimiga.net>2012-08-08 14:17:52 -0700
committerPatrick Walton <pcwalton@mimiga.net>2012-08-08 14:19:21 -0700
commitf110e8f21c707cb4bbb5e54b45f4458987920322 (patch)
treed4999199d287f0e3e53569ba8bdc82a593654a6c
parent166cb1b28bc23303d15e8c1c4a71d0cdff0556a2 (diff)
downloadrust-f110e8f21c707cb4bbb5e54b45f4458987920322.tar.gz
rust-f110e8f21c707cb4bbb5e54b45f4458987920322.zip
rustc: Do some plumbing work on nested enums
-rw-r--r--src/libsyntax/ast.rs3
-rw-r--r--src/libsyntax/ext/auto_serialize.rs4
-rw-r--r--src/libsyntax/fold.rs5
-rw-r--r--src/libsyntax/parse/parser.rs95
-rw-r--r--src/libsyntax/print/pprust.rs31
-rw-r--r--src/libsyntax/visit.rs30
-rw-r--r--src/rustc/metadata/encoder.rs3
-rw-r--r--src/rustc/middle/resolve3.rs36
-rw-r--r--src/rustc/middle/trans/base.rs57
-rw-r--r--src/rustc/middle/ty.rs6
-rw-r--r--src/rustc/middle/typeck/check.rs107
-rw-r--r--src/rustc/middle/typeck/collect.rs30
12 files changed, 250 insertions, 157 deletions
diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs
index f1c5f63bd1d..d2a6aa1f7b0 100644
--- a/src/libsyntax/ast.rs
+++ b/src/libsyntax/ast.rs
@@ -636,7 +636,8 @@ type variant_arg = {ty: @ty, id: node_id};
 #[auto_serialize]
 enum variant_kind {
     tuple_variant_kind(~[variant_arg]),
-    struct_variant_kind(@struct_def)
+    struct_variant_kind(@struct_def),
+    enum_variant_kind(~[variant])
 }
 
 #[auto_serialize]
diff --git a/src/libsyntax/ext/auto_serialize.rs b/src/libsyntax/ext/auto_serialize.rs
index 20cc04b2c79..d0a55b6d95c 100644
--- a/src/libsyntax/ext/auto_serialize.rs
+++ b/src/libsyntax/ext/auto_serialize.rs
@@ -913,7 +913,9 @@ fn deser_enum(cx: ext_ctxt, tps: deser_tps_map, e_name: ast::ident,
                 };
             }
             ast::struct_variant_kind(*) =>
-                fail ~"struct variants unimplemented"
+                fail ~"struct variants unimplemented",
+            ast::enum_variant_kind(*) =>
+                fail ~"enum variants unimplemented"
         }
 
         {pats: ~[@{id: cx.next_id(),
diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs
index 22713618222..d84c981504f 100644
--- a/src/libsyntax/fold.rs
+++ b/src/libsyntax/fold.rs
@@ -564,6 +564,11 @@ fn noop_fold_variant(v: variant_, fld: ast_fold) -> variant_ {
                 dtor: dtor
             })
         }
+
+        enum_variant_kind(variants) => {
+            let variants = vec::map(variants, |x| fld.fold_variant(x));
+            kind = enum_variant_kind(variants);
+        }
     }
 
     let fold_attribute = |x| fold_attribute_(x, fld);
diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs
index 6f8128b9b76..1cc3f197721 100644
--- a/src/libsyntax/parse/parser.rs
+++ b/src/libsyntax/parse/parser.rs
@@ -17,28 +17,27 @@ import dvec::{dvec, extensions};
 import vec::{push};
 import ast::{_mod, add, alt_check, alt_exhaustive, arg, arm, attribute,
              bind_by_ref, bind_by_implicit_ref, bind_by_value,
-             bitand, bitor, bitxor, blk,
-             blk_check_mode, bound_const, bound_copy, bound_send, bound_trait,
-             bound_owned, box, by_copy, by_move, by_mutbl_ref, by_ref, by_val,
-             capture_clause, capture_item, cdir_dir_mod, cdir_src_mod,
-             cdir_view_item, class_immutable, class_member, class_method,
-             class_mutable, crate, crate_cfg, crate_directive, decl,
-             decl_item, decl_local, default_blk, deref, div, expl, expr,
-             expr_, expr_addr_of, expr_match, expr_again, expr_assert,
-             expr_assign, expr_assign_op, expr_binary, expr_block, expr_break,
-             expr_call, expr_cast, expr_copy, expr_do_body,
-             expr_fail, expr_field, expr_fn, expr_fn_block, expr_if,
-             expr_index, expr_lit, expr_log, expr_loop,
-             expr_loop_body, expr_mac, expr_move, expr_path, expr_rec,
-             expr_repeat, expr_ret, expr_swap, expr_struct, expr_tup,
-             expr_unary, expr_unary_move, expr_vec, expr_vstore, expr_while,
-             extern_fn, field, fn_decl, foreign_item, foreign_item_fn,
-             foreign_mod, ident, impure_fn, infer, inherited, init_assign,
-             init_move, initializer, instance_var, item, item_, item_class,
-             item_const, item_enum, item_fn, item_foreign_mod, item_impl,
-             item_mac, item_mod, item_trait, item_ty, lit, lit_, lit_bool,
-             lit_float, lit_int, lit_int_unsuffixed, lit_nil, lit_str,
-             lit_uint, local, m_const, m_imm, m_mutbl, mac_, mac_aq,
+             bitand, bitor, bitxor, blk, blk_check_mode, bound_const,
+             bound_copy, bound_send, bound_trait, bound_owned, box, by_copy,
+             by_move, by_mutbl_ref, by_ref, by_val, capture_clause,
+             capture_item, cdir_dir_mod, cdir_src_mod, cdir_view_item,
+             class_immutable, class_member, class_method, class_mutable,
+             crate, crate_cfg, crate_directive, decl, decl_item, decl_local,
+             default_blk, deref, div, enum_variant_kind, expl, expr, expr_,
+             expr_addr_of, expr_match, expr_again, expr_assert, expr_assign,
+             expr_assign_op, expr_binary, expr_block, expr_break, expr_call,
+             expr_cast, expr_copy, expr_do_body, expr_fail, expr_field,
+             expr_fn, expr_fn_block, expr_if, expr_index, expr_lit, expr_log,
+             expr_loop, expr_loop_body, expr_mac, expr_move, expr_path,
+             expr_rec, expr_repeat, expr_ret, expr_swap, expr_struct,
+             expr_tup, expr_unary, expr_unary_move, expr_vec, expr_vstore,
+             expr_while, extern_fn, field, fn_decl, foreign_item,
+             foreign_item_fn, foreign_mod, ident, impure_fn, infer, inherited,
+             init_assign, init_move, initializer, instance_var, item, item_,
+             item_class, item_const, item_enum, item_fn, item_foreign_mod,
+             item_impl, item_mac, item_mod, item_trait, item_ty, lit, lit_,
+             lit_bool, lit_float, lit_int, lit_int_unsuffixed, lit_nil,
+             lit_str, lit_uint, local, m_const, m_imm, m_mutbl, mac_, mac_aq,
              mac_ellipsis, mac_invoc, mac_invoc_tt, mac_var, matcher,
              match_nonterminal, match_seq, match_tok, method, mode, mt, mul,
              mutability, neg, noreturn, not, pat, pat_box, pat_enum,
@@ -2842,30 +2841,8 @@ class parser {
         }
     }
 
-    fn parse_item_enum() -> item_info {
-        let id = self.parse_ident();
-        self.parse_region_param();
-        let ty_params = self.parse_ty_params();
+    fn parse_enum_body(ty_params: ~[ast::ty_param]) -> ~[ast::variant] {
         let mut variants: ~[variant] = ~[];
-        // Newtype syntax
-        if self.token == token::EQ {
-            self.check_restricted_keywords_(*id);
-            self.bump();
-            let ty = self.parse_ty(false);
-            self.expect(token::SEMI);
-            let variant =
-                spanned(ty.span.lo, ty.span.hi,
-                        {name: id,
-                         attrs: ~[],
-                         kind: tuple_variant_kind
-                            (~[{ty: ty, id: self.get_id()}]),
-                         id: self.get_id(),
-                         disr_expr: none,
-                         vis: public});
-            return (id, item_enum(~[variant], ty_params), none);
-        }
-        self.expect(token::LBRACE);
-
         let mut all_nullary = true, have_disr = false;
 
         while self.token != token::RBRACE {
@@ -2954,6 +2931,34 @@ class parser {
             self.fatal(~"discriminator values can only be used with a c-like \
                         enum");
         }
+
+        return variants;
+    }
+
+    fn parse_item_enum() -> item_info {
+        let id = self.parse_ident();
+        self.parse_region_param();
+        let ty_params = self.parse_ty_params();
+        // Newtype syntax
+        if self.token == token::EQ {
+            self.check_restricted_keywords_(*id);
+            self.bump();
+            let ty = self.parse_ty(false);
+            self.expect(token::SEMI);
+            let variant =
+                spanned(ty.span.lo, ty.span.hi,
+                        {name: id,
+                         attrs: ~[],
+                         kind: tuple_variant_kind
+                            (~[{ty: ty, id: self.get_id()}]),
+                         id: self.get_id(),
+                         disr_expr: none,
+                         vis: public});
+            return (id, item_enum(~[variant], ty_params), none);
+        }
+        self.expect(token::LBRACE);
+
+        let variants = self.parse_enum_body(ty_params);
         (id, item_enum(variants, ty_params), none)
     }
 
diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs
index 4b23fbf74a9..56b3197c336 100644
--- a/src/libsyntax/print/pprust.rs
+++ b/src/libsyntax/print/pprust.rs
@@ -516,18 +516,7 @@ fn print_item(s: ps, &&item: @ast::item) {
             word(s.s, ~";");
             end(s);
         } else {
-            bopen(s);
-            for variants.each |v| {
-                space_if_not_bol(s);
-                maybe_print_comment(s, v.span.lo);
-                print_outer_attributes(s, v.node.attrs);
-                ibox(s, indent_unit);
-                print_variant(s, v);
-                word(s.s, ~",");
-                end(s);
-                maybe_print_trailing_comment(s, v.span, none::<uint>);
-            }
-            bclose(s, item.span);
+            print_variants(s, variants, item.span);
         }
       }
       ast::item_class(struct_def, tps) => {
@@ -582,6 +571,21 @@ fn print_item(s: ps, &&item: @ast::item) {
     s.ann.post(ann_node);
 }
 
+fn print_variants(s: ps, variants: ~[ast::variant], span: ast::span) {
+    bopen(s);
+    for variants.each |v| {
+        space_if_not_bol(s);
+        maybe_print_comment(s, v.span.lo);
+        print_outer_attributes(s, v.node.attrs);
+        ibox(s, indent_unit);
+        print_variant(s, v);
+        word(s.s, ~",");
+        end(s);
+        maybe_print_trailing_comment(s, v.span, none::<uint>);
+    }
+    bclose(s, span);
+}
+
 fn print_struct(s: ps, struct_def: @ast::struct_def, tps: ~[ast::ty_param],
                 ident: ast::ident, span: ast::span) {
     word_nbsp(s, *ident);
@@ -710,6 +714,9 @@ fn print_variant(s: ps, v: ast::variant) {
             head(s, ~"");
             print_struct(s, struct_def, ~[], v.node.name, v.span);
         }
+        ast::enum_variant_kind(variants) => {
+            print_variants(s, variants, v.span);
+        }
     }
     match v.node.disr_expr {
       some(d) => {
diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs
index a47685dd42c..f526f8c73e1 100644
--- a/src/libsyntax/visit.rs
+++ b/src/libsyntax/visit.rs
@@ -138,17 +138,7 @@ fn visit_item<E>(i: @item, e: E, v: vt<E>) {
       }
       item_enum(variants, tps) => {
         v.visit_ty_params(tps, e, v);
-        for variants.each |vr| {
-            match vr.node.kind {
-                tuple_variant_kind(variant_args) => {
-                    for variant_args.each |va| { v.visit_ty(va.ty, e, v); }
-                }
-                struct_variant_kind(struct_def) => {
-                    v.visit_struct_def(struct_def, vr.node.name, tps,
-                                       vr.node.id, e, v);
-                }
-            }
-        }
+        visit_variants(variants, tps, e, v);
       }
       item_impl(tps, traits, ty, methods) => {
         v.visit_ty_params(tps, e, v);
@@ -175,6 +165,24 @@ fn visit_item<E>(i: @item, e: E, v: vt<E>) {
     }
 }
 
+fn visit_variants<E>(variants: ~[ast::variant], tps: ~[ast::ty_param], e: E,
+                     v: vt<E>) {
+    for variants.each |vr| {
+        match vr.node.kind {
+            tuple_variant_kind(variant_args) => {
+                for variant_args.each |va| { v.visit_ty(va.ty, e, v); }
+            }
+            struct_variant_kind(struct_def) => {
+                v.visit_struct_def(struct_def, vr.node.name, tps,
+                                   vr.node.id, e, v);
+            }
+            enum_variant_kind(variants) => {
+                visit_variants(variants, tps, e, v);
+            }
+        }
+    }
+}
+
 fn visit_class_item<E>(cm: @class_member, e:E, v:vt<E>) {
     match cm.node {
       instance_var(_, t, _, _, _) => v.visit_ty(t, e, v),
diff --git a/src/rustc/metadata/encoder.rs b/src/rustc/metadata/encoder.rs
index 07d12a12866..de0dca66f9c 100644
--- a/src/rustc/metadata/encoder.rs
+++ b/src/rustc/metadata/encoder.rs
@@ -398,7 +398,8 @@ fn encode_enum_variant_info(ecx: @encode_ctxt, ebml_w: ebml::writer,
                     if args.len() > 0 && ty_params.len() == 0 => {
                 encode_symbol(ecx, ebml_w, variant.node.id);
             }
-            ast::tuple_variant_kind(_) | ast::struct_variant_kind(_) => {}
+            ast::tuple_variant_kind(_) | ast::struct_variant_kind(_) |
+            ast::enum_variant_kind(_) => {}
         }
         encode_discriminant(ecx, ebml_w, variant.node.id);
         if vi[i].disr_val != disr_val {
diff --git a/src/rustc/middle/resolve3.rs b/src/rustc/middle/resolve3.rs
index 3bd47b8d5f2..d17542b797a 100644
--- a/src/rustc/middle/resolve3.rs
+++ b/src/rustc/middle/resolve3.rs
@@ -21,19 +21,19 @@ import syntax::ast::{def_upvar, def_use, def_variant, expr, expr_assign_op};
 import syntax::ast::{expr_binary, expr_cast, expr_field, expr_fn};
 import syntax::ast::{expr_fn_block, expr_index, expr_path};
 import syntax::ast::{def_prim_ty, def_region, def_self, def_ty, def_ty_param};
-import syntax::ast::{def_upvar, def_use, def_variant, div, eq, expr};
-import syntax::ast::{expr_assign_op, expr_binary, expr_cast, expr_field};
-import syntax::ast::{expr_fn, expr_fn_block, expr_index, expr_path};
-import syntax::ast::{expr_struct, expr_unary, fn_decl, foreign_item};
-import syntax::ast::{foreign_item_fn, ge, gt, ident, trait_ref, impure_fn};
-import syntax::ast::{instance_var, item, item_class, item_const, item_enum};
-import syntax::ast::{item_fn, item_mac, item_foreign_mod, item_impl};
-import syntax::ast::{item_mod, item_trait, item_ty, le, local, local_crate};
-import syntax::ast::{lt, method, mul, ne, neg, node_id, pat, pat_enum};
-import syntax::ast::{pat_ident, path, prim_ty, pat_box, pat_uniq, pat_lit};
-import syntax::ast::{pat_range, pat_rec, pat_struct, pat_tup, pat_wild};
-import syntax::ast::{provided, required, rem, self_ty_, shl, stmt_decl};
-import syntax::ast::{struct_variant_kind, sty_static, subtract};
+import syntax::ast::{def_upvar, def_use, def_variant, div, eq};
+import syntax::ast::{enum_variant_kind, expr, expr_assign_op, expr_binary};
+import syntax::ast::{expr_cast, expr_field, expr_fn, expr_fn_block};
+import syntax::ast::{expr_index, expr_path, expr_struct, expr_unary, fn_decl};
+import syntax::ast::{foreign_item, foreign_item_fn, ge, gt, ident, trait_ref};
+import syntax::ast::{impure_fn, instance_var, item, item_class, item_const};
+import syntax::ast::{item_enum, item_fn, item_mac, item_foreign_mod};
+import syntax::ast::{item_impl, item_mod, item_trait, item_ty, le, local};
+import syntax::ast::{local_crate, lt, method, mul, ne, neg, node_id, pat};
+import syntax::ast::{pat_enum, pat_ident, path, prim_ty, pat_box, pat_uniq};
+import syntax::ast::{pat_lit, pat_range, pat_rec, pat_struct, pat_tup};
+import syntax::ast::{pat_wild, provided, required, rem, self_ty_, shl};
+import syntax::ast::{stmt_decl, struct_variant_kind, sty_static, subtract};
 import syntax::ast::{tuple_variant_kind, ty};
 import syntax::ast::{ty_bool, ty_char, ty_f, ty_f32, ty_f64, ty_float, ty_i};
 import syntax::ast::{ty_i16, ty_i32, ty_i64, ty_i8, ty_int, ty_param};
@@ -1128,7 +1128,7 @@ class Resolver {
     fn build_reduced_graph_for_variant(variant: variant,
                                        item_id: def_id,
                                        parent: ReducedGraphParent,
-                                       &&_visitor: vt<ReducedGraphParent>) {
+                                       &&visitor: vt<ReducedGraphParent>) {
 
         let atom = (*self.atom_table).intern(variant.node.name);
         let (child, _) = self.add_child(atom, parent, ~[ValueNS],
@@ -1146,6 +1146,14 @@ class Resolver {
                                      variant.span);
                 self.structs.insert(local_def(variant.node.id), false);
             }
+            enum_variant_kind(variants) => {
+                (*child).define_type(def_ty(local_def(variant.node.id)),
+                                     variant.span);
+                for variants.each |variant| {
+                    self.build_reduced_graph_for_variant(variant, item_id,
+                                                         parent, visitor);
+                }
+            }
         }
     }
 
diff --git a/src/rustc/middle/trans/base.rs b/src/rustc/middle/trans/base.rs
index ce6669f9799..15f7de84c5f 100644
--- a/src/rustc/middle/trans/base.rs
+++ b/src/rustc/middle/trans/base.rs
@@ -2235,7 +2235,9 @@ fn monomorphic_fn(ccx: @crate_ctxt, fn_id: ast::def_id,
                                    psubsts, d);
             }
             ast::struct_variant_kind(_) =>
-                ccx.tcx.sess.bug(~"can't monomorphize struct variants")
+                ccx.tcx.sess.bug(~"can't monomorphize struct variants"),
+            ast::enum_variant_kind(_) =>
+                ccx.tcx.sess.bug(~"can't monomorphize enum variants")
         }
         d
       }
@@ -4893,6 +4895,34 @@ fn trans_class_dtor(ccx: @crate_ctxt, path: path,
   lldecl
 }
 
+fn trans_variants(ccx: @crate_ctxt, variants: ~[ast::variant],
+                  id: ast::node_id, tps: ~[ast::ty_param], degen: bool,
+                  path: @ast_map::path, vi: @~[ty::variant_info],
+                  i: &mut uint) {
+    for vec::each(variants) |variant| {
+        let disr_val = vi[*i].disr_val;
+        *i += 1;
+
+        match variant.node.kind {
+            ast::tuple_variant_kind(args) if args.len() > 0 => {
+                let llfn = get_item_val(ccx, variant.node.id);
+                trans_enum_variant(ccx, id, variant, args, disr_val,
+                                   degen, none, llfn);
+            }
+            ast::tuple_variant_kind(_) => {
+                // Nothing to do.
+            }
+            ast::struct_variant_kind(struct_def) => {
+                trans_struct_def(ccx, struct_def, tps, path,
+                                 variant.node.name, variant.node.id);
+            }
+            ast::enum_variant_kind(variants) => {
+                trans_variants(ccx, variants, id, tps, degen, path, vi, i);
+            }
+        }
+    }
+}
+
 fn trans_item(ccx: @crate_ctxt, item: ast::item) {
     let _icx = ccx.insn_ctxt(~"trans_item");
     let path = match check ccx.tcx.items.get(item.id) {
@@ -4934,24 +4964,8 @@ fn trans_item(ccx: @crate_ctxt, item: ast::item) {
             let degen = variants.len() == 1u;
             let vi = ty::enum_variants(ccx.tcx, local_def(item.id));
             let mut i = 0;
-            for vec::each(variants) |variant| {
-                match variant.node.kind {
-                    ast::tuple_variant_kind(args) if args.len() > 0 => {
-                        let llfn = get_item_val(ccx, variant.node.id);
-                        trans_enum_variant(ccx, item.id, variant, args,
-                                           vi[i].disr_val, degen,
-                                           none, llfn);
-                    }
-                    ast::tuple_variant_kind(_) => {
-                        // Nothing to do.
-                    }
-                    ast::struct_variant_kind(struct_def) => {
-                        trans_struct_def(ccx, struct_def, tps, path,
-                                         variant.node.name, variant.node.id);
-                    }
-                }
-                i += 1;
-            }
+            trans_variants(ccx, variants, item.id, tps, degen, path, vi,
+                           &mut i);
         }
       }
       ast::item_const(_, expr) => consts::trans_const(ccx, expr, item.id),
@@ -5263,7 +5277,10 @@ fn get_item_val(ccx: @crate_ctxt, id: ast::node_id) -> ValueRef {
                     };
                 }
                 ast::struct_variant_kind(_) => {
-                    fail ~"struct unexpected in get_item_val"
+                    fail ~"struct variant kind unexpected in get_item_val"
+                }
+                ast::enum_variant_kind(_) => {
+                    fail ~"enum variant kind unexpected in get_item_val"
                 }
             }
             set_inline_hint(llfn);
diff --git a/src/rustc/middle/ty.rs b/src/rustc/middle/ty.rs
index ad7824fabe5..ff807cbcab2 100644
--- a/src/rustc/middle/ty.rs
+++ b/src/rustc/middle/ty.rs
@@ -2868,8 +2868,12 @@ fn enum_variants(cx: ctxt, id: ast::def_id) -> @~[variant_info] {
                           disr_val: disr_val
                          }
                     }
-                    ast::struct_variant_kind(_) =>
+                    ast::struct_variant_kind(_) => {
                         fail ~"struct variant kinds unimpl in enum_variants"
+                    }
+                    ast::enum_variant_kind(_) => {
+                        fail ~"enum variant kinds unimpl in enum_variants"
+                    }
                 }
             })
           }
diff --git a/src/rustc/middle/typeck/check.rs b/src/rustc/middle/typeck/check.rs
index ec7e642c09d..16d93137ac7 100644
--- a/src/rustc/middle/typeck/check.rs
+++ b/src/rustc/middle/typeck/check.rs
@@ -2043,53 +2043,76 @@ fn check_enum_variants(ccx: @crate_ctxt,
                        sp: span,
                        vs: ~[ast::variant],
                        id: ast::node_id) {
-    let rty = ty::node_id_to_type(ccx.tcx, id);
-    let mut disr_vals: ~[int] = ~[];
-    let mut disr_val = 0;
-    let mut variants = ~[];
-    for vs.each |v| {
-        match v.node.disr_expr {
-          some(e) => {
-            let fcx = blank_fn_ctxt(ccx, rty, e.id);
-            check_expr(fcx, e, none);
-            let cty = fcx.expr_ty(e);
-            let declty = ty::mk_int(ccx.tcx);
-            demand::suptype(fcx, e.span, declty, cty);
-            // FIXME: issue #1417
-            // Also, check_expr (from check_const pass) doesn't guarantee that
-            // the expression in an form that eval_const_expr can handle, so
-            // we may still get an internal compiler error
-            match const_eval::eval_const_expr(ccx.tcx, e) {
-              const_eval::const_int(val) => {
-                disr_val = val as int;
-              }
-              _ => {
-                ccx.tcx.sess.span_err(e.span,
-                                      ~"expected signed integer constant");
+    fn do_check(ccx: @crate_ctxt, sp: span, vs: ~[ast::variant],
+                id: ast::node_id, disr_vals: &mut ~[int], disr_val: &mut int,
+                variants: &mut ~[ty::variant_info]) {
+        let rty = ty::node_id_to_type(ccx.tcx, id);
+        for vs.each |v| {
+            match v.node.disr_expr {
+              some(e) => {
+                let fcx = blank_fn_ctxt(ccx, rty, e.id);
+                check_expr(fcx, e, none);
+                let cty = fcx.expr_ty(e);
+                let declty = ty::mk_int(ccx.tcx);
+                demand::suptype(fcx, e.span, declty, cty);
+                // FIXME: issue #1417
+                // Also, check_expr (from check_const pass) doesn't guarantee
+                // that the expression is in an form that eval_const_expr can
+                // handle, so we may still get an internal compiler error
+                match const_eval::eval_const_expr(ccx.tcx, e) {
+                  const_eval::const_int(val) => {
+                    *disr_val = val as int;
+                  }
+                  _ => {
+                    ccx.tcx.sess.span_err(e.span, ~"expected signed integer \
+                                                    constant");
+                  }
+                }
               }
+              _ => ()
+            }
+            if vec::contains(*disr_vals, *disr_val) {
+                ccx.tcx.sess.span_err(v.span,
+                                      ~"discriminator value already exists");
+            }
+            vec::push(*disr_vals, *disr_val);
+            let ctor_ty = ty::node_id_to_type(ccx.tcx, v.node.id);
+            let arg_tys;
+
+            let this_disr_val = *disr_val;
+            *disr_val += 1;
+
+            match v.node.kind {
+                ast::tuple_variant_kind(args) if args.len() > 0u => {
+                    arg_tys = some(ty::ty_fn_args(ctor_ty).map(|a| a.ty));
+                }
+                ast::tuple_variant_kind(_) | ast::struct_variant_kind(_) => {
+                    arg_tys = some(~[]);
+                }
+                ast::enum_variant_kind(subvariants) => {
+                    arg_tys = none;
+                    do_check(ccx, sp, vs, id, disr_vals, disr_val, variants);
+                }
+            }
+
+            match arg_tys {
+                none => {}
+                some(arg_tys) => {
+                    vec::push(*variants, @{args: arg_tys, ctor_ty: ctor_ty,
+                          name: v.node.name, id: local_def(v.node.id),
+                          disr_val: this_disr_val});
+                }
             }
-          }
-          _ => ()
-        }
-        if vec::contains(disr_vals, disr_val) {
-            ccx.tcx.sess.span_err(v.span,
-                                  ~"discriminator value already exists");
         }
-        vec::push(disr_vals, disr_val);
-        let ctor_ty = ty::node_id_to_type(ccx.tcx, v.node.id);
-        let arg_tys;
-        match v.node.kind {
-            ast::tuple_variant_kind(args) if args.len() > 0u =>
-                arg_tys = ty::ty_fn_args(ctor_ty).map(|a| a.ty),
-            ast::tuple_variant_kind(_) | ast::struct_variant_kind(_) =>
-                arg_tys = ~[]
-        };
-        vec::push(variants, @{args: arg_tys, ctor_ty: ctor_ty,
-              name: v.node.name, id: local_def(v.node.id),
-              disr_val: disr_val});
-        disr_val += 1;
     }
 
+    let rty = ty::node_id_to_type(ccx.tcx, id);
+    let mut disr_vals: ~[int] = ~[];
+    let mut disr_val = 0;
+    let mut variants = ~[];
+
+    do_check(ccx, sp, vs, id, &mut disr_vals, &mut disr_val, &mut variants);
+
     // cache so that ty::enum_variants won't repeat this work
     ccx.tcx.enum_var_cache.insert(local_def(id), @variants);
 
diff --git a/src/rustc/middle/typeck/collect.rs b/src/rustc/middle/typeck/collect.rs
index 64021f216c3..08eaad73e65 100644
--- a/src/rustc/middle/typeck/collect.rs
+++ b/src/rustc/middle/typeck/collect.rs
@@ -124,21 +124,33 @@ fn get_enum_variant_types(ccx: @crate_ctxt,
                     let arg_ty = ccx.to_ty(rs, va.ty);
                     {mode: ast::expl(ast::by_copy), ty: arg_ty}
                 });
-                result_ty = ty::mk_fn(tcx, {purity: ast::pure_fn,
+                result_ty = some(ty::mk_fn(tcx,
+                                           {purity: ast::pure_fn,
                                             proto: ast::proto_box,
                                             bounds: @~[],
                                             inputs: args,
                                             output: enum_ty,
-                                            ret_style: ast::return_val});
+                                            ret_style: ast::return_val}));
+            }
+            ast::tuple_variant_kind(_) | ast::struct_variant_kind(_) => {
+                result_ty = some(enum_ty);
+            }
+            ast::enum_variant_kind(variants) => {
+                get_enum_variant_types(ccx, enum_ty, variants, ty_params, rp);
+                result_ty = none;
             }
-            ast::tuple_variant_kind(_) | ast::struct_variant_kind(_) =>
-                result_ty = enum_ty
         };
-        let tpt = {bounds: ty_param_bounds(ccx, ty_params),
-                   rp: rp,
-                   ty: result_ty};
-        tcx.tcache.insert(local_def(variant.node.id), tpt);
-        write_ty_to_tcx(tcx, variant.node.id, result_ty);
+
+        match result_ty {
+            none => {}
+            some(result_ty) => {
+                let tpt = {bounds: ty_param_bounds(ccx, ty_params),
+                           rp: rp,
+                           ty: result_ty};
+                tcx.tcache.insert(local_def(variant.node.id), tpt);
+                write_ty_to_tcx(tcx, variant.node.id, result_ty);
+            }
+        }
     }
 }