about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/librustc/middle/check_const.rs4
-rw-r--r--src/librustc/middle/trans/_match.rs169
-rw-r--r--src/librustc/middle/trans/adt.rs554
-rw-r--r--src/librustc/middle/trans/base.rs223
-rw-r--r--src/librustc/middle/trans/common.rs86
-rw-r--r--src/librustc/middle/trans/consts.rs157
-rw-r--r--src/librustc/middle/trans/datum.rs23
-rw-r--r--src/librustc/middle/trans/expr.rs247
-rw-r--r--src/librustc/middle/trans/glue.rs15
-rw-r--r--src/librustc/middle/trans/monomorphize.rs3
-rw-r--r--src/librustc/middle/trans/type_of.rs101
-rw-r--r--src/librustc/rustc.rc1
-rw-r--r--src/test/run-pass/const-enum-structlike.rs23
-rw-r--r--src/test/run-pass/enum-discrim-range-overflow.rs31
-rw-r--r--src/test/run-pass/struct-order-of-eval-1.rs16
-rw-r--r--src/test/run-pass/struct-order-of-eval-2.rs16
16 files changed, 1041 insertions, 628 deletions
diff --git a/src/librustc/middle/check_const.rs b/src/librustc/middle/check_const.rs
index 6eb698d34d2..c00856a0a98 100644
--- a/src/librustc/middle/check_const.rs
+++ b/src/librustc/middle/check_const.rs
@@ -168,8 +168,8 @@ pub fn check_expr(sess: Session,
           expr_field(*) |
           expr_index(*) |
           expr_tup(*) |
-          expr_struct(*) |
-          expr_rec(*) => { }
+          expr_struct(_, _, None) |
+          expr_rec(_, None) => { }
           expr_addr_of(*) => {
                 sess.span_err(
                     e.span,
diff --git a/src/librustc/middle/trans/_match.rs b/src/librustc/middle/trans/_match.rs
index 2db3cae74e3..5bfa314e397 100644
--- a/src/librustc/middle/trans/_match.rs
+++ b/src/librustc/middle/trans/_match.rs
@@ -145,12 +145,12 @@
 use core::prelude::*;
 
 use back::abi;
-use lib;
 use lib::llvm::{llvm, ValueRef, BasicBlockRef};
 use middle::const_eval;
 use middle::borrowck::root_map_key;
 use middle::pat_util::*;
 use middle::resolve::DefMap;
+use middle::trans::adt;
 use middle::trans::base::*;
 use middle::trans::build::*;
 use middle::trans::callee;
@@ -169,9 +169,7 @@ use util::common::indenter;
 
 use core::dvec::DVec;
 use core::dvec;
-use core::libc::c_ulonglong;
 use std::oldmap::HashMap;
-use syntax::ast::def_id;
 use syntax::ast;
 use syntax::ast::ident;
 use syntax::ast_util::path_to_ident;
@@ -191,15 +189,15 @@ pub enum Lit {
 // range)
 pub enum Opt {
     lit(Lit),
-    var(/* disr val */int, /* variant dids (enm, var) */(def_id, def_id)),
+    var(/* disr val */int, @adt::Repr),
     range(@ast::expr, @ast::expr),
     vec_len_eq(uint),
     vec_len_ge(uint)
 }
 
 pub fn opt_eq(tcx: ty::ctxt, a: &Opt, b: &Opt) -> bool {
-    match (*a, *b) {
-      (lit(a), lit(b)) => {
+    match (a, b) {
+      (&lit(a), &lit(b)) => {
         match (a, b) {
             (UnitLikeStructLit(a), UnitLikeStructLit(b)) => a == b,
             _ => {
@@ -233,13 +231,13 @@ pub fn opt_eq(tcx: ty::ctxt, a: &Opt, b: &Opt) -> bool {
             }
         }
       }
-      (range(a1, a2), range(b1, b2)) => {
+      (&range(a1, a2), &range(b1, b2)) => {
         const_eval::compare_lit_exprs(tcx, a1, b1) == 0 &&
         const_eval::compare_lit_exprs(tcx, a2, b2) == 0
       }
-      (var(a, _), var(b, _)) => a == b,
-      (vec_len_eq(a), vec_len_eq(b)) => a == b,
-      (vec_len_ge(a), vec_len_ge(b)) => a == b,
+      (&var(a, _), &var(b, _)) => a == b,
+      (&vec_len_eq(a), &vec_len_eq(b)) => a == b,
+      (&vec_len_ge(a), &vec_len_ge(b)) => a == b,
       _ => false
     }
 }
@@ -267,8 +265,8 @@ pub fn trans_opt(bcx: block, o: &Opt) -> opt_result {
             let llval = consts::get_const_val(bcx.ccx(), lit_id);
             return single_result(rslt(bcx, llval));
         }
-        var(disr_val, _) => {
-            return single_result(rslt(bcx, C_int(ccx, disr_val)));
+        var(disr_val, repr) => {
+            return adt::trans_case(bcx, repr, disr_val);
         }
         range(l1, l2) => {
             return range_result(rslt(bcx, consts::const_expr(ccx, l1)),
@@ -283,13 +281,16 @@ pub fn trans_opt(bcx: block, o: &Opt) -> opt_result {
     }
 }
 
-pub fn variant_opt(tcx: ty::ctxt, pat_id: ast::node_id) -> Opt {
-    match tcx.def_map.get(&pat_id) {
+pub fn variant_opt(bcx: block, pat_id: ast::node_id)
+    -> Opt {
+    let ccx = bcx.ccx();
+    match ccx.tcx.def_map.get(&pat_id) {
         ast::def_variant(enum_id, var_id) => {
-            let variants = ty::enum_variants(tcx, enum_id);
+            let variants = ty::enum_variants(ccx.tcx, enum_id);
             for vec::each(*variants) |v| {
                 if var_id == v.id {
-                    return var(v.disr_val, (enum_id, var_id));
+                    return var(v.disr_val,
+                               adt::represent_node(bcx, pat_id))
                 }
             }
             ::core::util::unreachable();
@@ -298,7 +299,7 @@ pub fn variant_opt(tcx: ty::ctxt, pat_id: ast::node_id) -> Opt {
             return lit(UnitLikeStructLit(pat_id));
         }
         _ => {
-            tcx.sess.bug(~"non-variant or struct in variant_opt()");
+            ccx.sess.bug(~"non-variant or struct in variant_opt()");
         }
     }
 }
@@ -505,7 +506,7 @@ pub fn enter_opt(bcx: block, m: &[@Match/&r], opt: &Opt, col: uint,
     do enter_match(bcx, tcx.def_map, m, col, val) |p| {
         match /*bad*/copy p.node {
             ast::pat_enum(_, subpats) => {
-                if opt_eq(tcx, &variant_opt(tcx, p.id), opt) {
+                if opt_eq(tcx, &variant_opt(bcx, p.id), opt) {
                     Some(option::get_or_default(subpats,
                                              vec::from_elem(variant_size,
                                                             dummy)))
@@ -515,7 +516,7 @@ pub fn enter_opt(bcx: block, m: &[@Match/&r], opt: &Opt, col: uint,
             }
             ast::pat_ident(_, _, None)
                     if pat_is_variant_or_struct(tcx.def_map, p) => {
-                if opt_eq(tcx, &variant_opt(tcx, p.id), opt) {
+                if opt_eq(tcx, &variant_opt(bcx, p.id), opt) {
                     Some(~[])
                 } else {
                     None
@@ -537,7 +538,7 @@ pub fn enter_opt(bcx: block, m: &[@Match/&r], opt: &Opt, col: uint,
                 if opt_eq(tcx, &range(l1, l2), opt) {Some(~[])} else {None}
             }
             ast::pat_struct(_, field_pats, _) => {
-                if opt_eq(tcx, &variant_opt(tcx, p.id), opt) {
+                if opt_eq(tcx, &variant_opt(bcx, p.id), opt) {
                     // Look up the struct variant ID.
                     let struct_id;
                     match tcx.def_map.get(&p.id) {
@@ -762,8 +763,9 @@ pub fn enter_region(bcx: block,
 // Returns the options in one column of matches. An option is something that
 // needs to be conditionally matched at runtime; for example, the discriminant
 // on a set of enum variants or a literal.
-pub fn get_options(ccx: @CrateContext, m: &[@Match], col: uint) -> ~[Opt] {
-    fn add_to_set(tcx: ty::ctxt, set: &DVec<Opt>, val: Opt) {
+pub fn get_options(bcx: block, m: &[@Match], col: uint) -> ~[Opt] {
+    let ccx = bcx.ccx();
+    fn add_to_set(tcx: ty::ctxt, set: &DVec<Opt>, +val: Opt) {
         if set.any(|l| opt_eq(tcx, l, &val)) {return;}
         set.push(val);
     }
@@ -781,7 +783,7 @@ pub fn get_options(ccx: @CrateContext, m: &[@Match], col: uint) -> ~[Opt] {
                 match ccx.tcx.def_map.find(&cur.id) {
                     Some(ast::def_variant(*)) => {
                         add_to_set(ccx.tcx, &found,
-                                   variant_opt(ccx.tcx, cur.id));
+                                   variant_opt(bcx, cur.id));
                     }
                     Some(ast::def_struct(*)) => {
                         add_to_set(ccx.tcx, &found,
@@ -800,7 +802,7 @@ pub fn get_options(ccx: @CrateContext, m: &[@Match], col: uint) -> ~[Opt] {
                 match ccx.tcx.def_map.find(&cur.id) {
                     Some(ast::def_variant(*)) => {
                         add_to_set(ccx.tcx, &found,
-                                   variant_opt(ccx.tcx, cur.id));
+                                   variant_opt(bcx, cur.id));
                     }
                     _ => {}
                 }
@@ -827,34 +829,13 @@ pub struct ExtractedBlock {
 }
 
 pub fn extract_variant_args(bcx: block,
-                            pat_id: ast::node_id,
-                            vdefs: (def_id, def_id),
+                            repr: &adt::Repr,
+                            disr_val: int,
                             val: ValueRef)
-                         -> ExtractedBlock {
-    let (enm, evar) = vdefs;
+    -> ExtractedBlock {
     let _icx = bcx.insn_ctxt("match::extract_variant_args");
-    let ccx = *bcx.fcx.ccx;
-    let enum_ty_substs = match ty::get(node_id_type(bcx, pat_id)).sty {
-      ty::ty_enum(id, ref substs) => {
-        assert id == enm;
-        /*bad*/copy (*substs).tps
-      }
-      _ => bcx.sess().bug(~"extract_variant_args: pattern has non-enum type")
-    };
-    let mut blobptr = val;
-    let variants = ty::enum_variants(ccx.tcx, enm);
-    let size = ty::enum_variant_with_id(ccx.tcx, enm,
-                                        evar).args.len();
-    if size > 0u && (*variants).len() != 1u {
-        let enumptr =
-            PointerCast(bcx, val, T_opaque_enum_ptr(ccx));
-        blobptr = GEPi(bcx, enumptr, [0u, 1u]);
-    }
-    let vdefs_tg = enm;
-    let vdefs_var = evar;
-    let args = do vec::from_fn(size) |i| {
-        GEP_enum(bcx, blobptr, vdefs_tg, vdefs_var,
-                 /*bad*/copy enum_ty_substs, i)
+    let args = do vec::from_fn(adt::num_args(repr, disr_val)) |i| {
+        adt::trans_field_ptr(bcx, repr, val, disr_val, i)
     };
 
     ExtractedBlock { vals: args, bcx: bcx }
@@ -1283,14 +1264,14 @@ pub fn compile_submatch(bcx: block,
     }
 
     bcx = root_pats_as_necessary(bcx, m, col, val);
-
     let rec_fields = collect_record_or_struct_fields(bcx, m, col);
     if rec_fields.len() > 0 {
         let pat_ty = node_id_type(bcx, pat_id);
-        do expr::with_field_tys(tcx, pat_ty, None) |_has_dtor, field_tys| {
+        let pat_repr = adt::represent_type(bcx.ccx(), pat_ty);
+        do expr::with_field_tys(tcx, pat_ty, None) |discr, field_tys| {
             let rec_vals = rec_fields.map(|field_name| {
                 let ix = ty::field_idx_strict(tcx, *field_name, field_tys);
-                GEPi(bcx, val, struct_field(ix))
+                adt::trans_field_ptr(bcx, pat_repr, val, discr, ix)
             });
             compile_submatch(
                 bcx,
@@ -1303,11 +1284,14 @@ pub fn compile_submatch(bcx: block,
 
     if any_tup_pat(m, col) {
         let tup_ty = node_id_type(bcx, pat_id);
+        let tup_repr = adt::represent_type(bcx.ccx(), tup_ty);
         let n_tup_elts = match /*bad*/copy ty::get(tup_ty).sty {
           ty::ty_tup(elts) => elts.len(),
           _ => ccx.sess.bug(~"non-tuple type in tuple pattern")
         };
-        let tup_vals = vec::from_fn(n_tup_elts, |i| GEPi(bcx, val, [0u, i]));
+        let tup_vals = do vec::from_fn(n_tup_elts) |i| {
+            adt::trans_field_ptr(bcx, tup_repr, val, 0, i)
+        };
         compile_submatch(bcx, enter_tup(bcx, dm, m, col, val, n_tup_elts),
                          vec::append(tup_vals, vals_left), chk);
         return;
@@ -1326,8 +1310,10 @@ pub fn compile_submatch(bcx: block,
             }
         }
 
-        let llstructvals = vec::from_fn(
-            struct_element_count, |i| GEPi(bcx, val, struct_field(i)));
+        let struct_repr = adt::represent_type(bcx.ccx(), struct_ty);
+        let llstructvals = do vec::from_fn(struct_element_count) |i| {
+            adt::trans_field_ptr(bcx, struct_repr, val, 0, i)
+        };
         compile_submatch(bcx,
                          enter_tuple_struct(bcx, dm, m, col, val,
                                             struct_element_count),
@@ -1365,37 +1351,15 @@ pub fn compile_submatch(bcx: block,
     }
 
     // Decide what kind of branch we need
-    let opts = get_options(ccx, m, col);
+    let opts = get_options(bcx, m, col);
     let mut kind = no_branch;
     let mut test_val = val;
     if opts.len() > 0u {
         match opts[0] {
-            var(_, (enm, _)) => {
-                let variants = ty::enum_variants(tcx, enm);
-                if variants.len() == 1 {
-                    kind = single;
-                } else {
-                    let enumptr =
-                        PointerCast(bcx, val, T_opaque_enum_ptr(ccx));
-                    let discrimptr = GEPi(bcx, enumptr, [0u, 0u]);
-
-
-                    assert variants.len() > 1;
-                    let min_discrim = do variants.foldr(0) |&x, y| {
-                        int::min(x.disr_val, y)
-                    };
-                    let max_discrim = do variants.foldr(0) |&x, y| {
-                        int::max(x.disr_val, y)
-                    };
-
-                    test_val = LoadRangeAssert(bcx, discrimptr,
-                                               min_discrim as c_ulonglong,
-                                               (max_discrim + 1)
-                                               as c_ulonglong,
-                                               lib::llvm::True);
-
-                    kind = switch;
-                }
+            var(_, repr) => {
+                let (the_kind, val_opt) = adt::trans_switch(bcx, repr, val);
+                kind = the_kind;
+                for val_opt.each |&tval| { test_val = tval; }
             }
             lit(_) => {
                 let pty = node_id_type(bcx, pat_id);
@@ -1544,11 +1508,12 @@ pub fn compile_submatch(bcx: block,
         let mut size = 0u;
         let mut unpacked = ~[];
         match *opt {
-            var(_, vdef) => {
-                let args = extract_variant_args(opt_cx, pat_id, vdef, val);
-                size = args.vals.len();
-                unpacked = /*bad*/copy args.vals;
-                opt_cx = args.bcx;
+            var(disr_val, repr) => {
+                let ExtractedBlock {vals: argvals, bcx: new_bcx} =
+                    extract_variant_args(opt_cx, repr, disr_val, val);
+                size = argvals.len();
+                unpacked = argvals;
+                opt_cx = new_bcx;
             }
             vec_len_eq(n) | vec_len_ge(n) => {
                 let tail = match *opt {
@@ -1757,10 +1722,15 @@ pub fn bind_irrefutable_pat(bcx: block,
         }
         ast::pat_enum(_, sub_pats) => {
             match bcx.tcx().def_map.find(&pat.id) {
-                Some(ast::def_variant(*)) => {
-                    let pat_def = ccx.tcx.def_map.get(&pat.id);
-                    let vdefs = ast_util::variant_def_ids(pat_def);
-                    let args = extract_variant_args(bcx, pat.id, vdefs, val);
+                Some(ast::def_variant(enum_id, var_id)) => {
+                    let repr = adt::represent_node(bcx, pat.id);
+                    let vinfo = ty::enum_variant_with_id(ccx.tcx,
+                                                         enum_id,
+                                                         var_id);
+                    let args = extract_variant_args(bcx,
+                                                    repr,
+                                                    vinfo.disr_val,
+                                                    val);
                     for sub_pats.each |sub_pat| {
                         for vec::eachi(args.vals) |i, argval| {
                             bcx = bind_irrefutable_pat(bcx,
@@ -1777,9 +1747,11 @@ pub fn bind_irrefutable_pat(bcx: block,
                             // This is a unit-like struct. Nothing to do here.
                         }
                         Some(elems) => {
-                            // This is the tuple variant case.
+                            // This is the tuple struct case.
+                            let repr = adt::represent_node(bcx, pat.id);
                             for vec::eachi(elems) |i, elem| {
-                                let fldptr = GEPi(bcx, val, struct_field(i));
+                                let fldptr = adt::trans_field_ptr(bcx, repr,
+                                                            val, 0, i);
                                 bcx = bind_irrefutable_pat(bcx,
                                                            *elem,
                                                            fldptr,
@@ -1797,10 +1769,12 @@ pub fn bind_irrefutable_pat(bcx: block,
         ast::pat_rec(fields, _) | ast::pat_struct(_, fields, _) => {
             let tcx = bcx.tcx();
             let pat_ty = node_id_type(bcx, pat.id);
-            do expr::with_field_tys(tcx, pat_ty, None) |_hd, field_tys| {
+            let pat_repr = adt::represent_type(bcx.ccx(), pat_ty);
+            do expr::with_field_tys(tcx, pat_ty, None) |discr, field_tys| {
                 for vec::each(fields) |f| {
                     let ix = ty::field_idx_strict(tcx, f.ident, field_tys);
-                    let fldptr = GEPi(bcx, val, struct_field(ix));
+                    let fldptr = adt::trans_field_ptr(bcx, pat_repr, val,
+                                                discr, ix);
                     bcx = bind_irrefutable_pat(bcx,
                                                f.pat,
                                                fldptr,
@@ -1810,8 +1784,9 @@ pub fn bind_irrefutable_pat(bcx: block,
             }
         }
         ast::pat_tup(elems) => {
+            let repr = adt::represent_node(bcx, pat.id);
             for vec::eachi(elems) |i, elem| {
-                let fldptr = GEPi(bcx, val, [0u, i]);
+                let fldptr = adt::trans_field_ptr(bcx, repr, val, 0, i);
                 bcx = bind_irrefutable_pat(bcx,
                                            *elem,
                                            fldptr,
diff --git a/src/librustc/middle/trans/adt.rs b/src/librustc/middle/trans/adt.rs
new file mode 100644
index 00000000000..3d3b40e4ff2
--- /dev/null
+++ b/src/librustc/middle/trans/adt.rs
@@ -0,0 +1,554 @@
+// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+/*!
+ * # Representation of Algebraic Data Types
+ *
+ * This module determines how to represent enums, structs, tuples, and
+ * (deprecated) structural records based on their monomorphized types;
+ * it is responsible both for choosing a representation and
+ * translating basic operations on values of those types.
+ *
+ * Note that the interface treats everything as a general case of an
+ * enum, so structs/tuples/etc. have one pseudo-variant with
+ * discriminant 0; i.e., as if they were a univariant enum.
+ *
+ * Having everything in one place will enable improvements to data
+ * structure representation; possibilities include:
+ *
+ * - Aligning enum bodies correctly, which in turn makes possible SIMD
+ *   vector types (which are strict-alignment even on x86) and ports
+ *   to strict-alignment architectures (PowerPC, SPARC, etc.).
+ *
+ * - User-specified alignment (e.g., cacheline-aligning parts of
+ *   concurrently accessed data structures); LLVM can't represent this
+ *   directly, so we'd have to insert padding fields in any structure
+ *   that might contain one and adjust GEP indices accordingly.  See
+ *   issue #4578.
+ *
+ * - Rendering `Option<&T>` as a possibly-null `*T` instead of using
+ *   an extra word (and likewise for `@T` and `~T`).  Can and probably
+ *   should also apply to any enum with one empty case and one case
+ *   starting with a non-null pointer (e.g., `Result<(), ~str>`).
+ *
+ * - Using smaller integer types for discriminants.
+ *
+ * - Store nested enums' discriminants in the same word.  Rather, if
+ *   some variants start with enums, and those enums representations
+ *   have unused alignment padding between discriminant and body, the
+ *   outer enum's discriminant can be stored there and those variants
+ *   can start at offset 0.  Kind of fancy, and might need work to
+ *   make copies of the inner enum type cooperate, but it could help
+ *   with `Option` or `Result` wrapped around another enum.
+ *
+ * - Tagged pointers would be neat, but given that any type can be
+ *   used unboxed and any field can have pointers (including mutable)
+ *   taken to it, implementing them for Rust seems difficult.
+ */
+
+use core::container::Map;
+use core::libc::c_ulonglong;
+use core::option::{Option, Some, None};
+use core::vec;
+
+use lib::llvm::{ValueRef, TypeRef, True, False};
+use middle::trans::_match;
+use middle::trans::build::*;
+use middle::trans::common::*;
+use middle::trans::machine;
+use middle::trans::type_of;
+use middle::ty;
+use syntax::ast;
+use util::ppaux::ty_to_str;
+
+
+/// Representations.
+pub enum Repr {
+    /**
+     * `Unit` exists only so that an enum with a single C-like variant
+     * can occupy no space, for ABI compatibility with rustc from
+     * before (and during) the creation of this module.  It may not be
+     * worth keeping around; `CEnum` and `Univariant` cover it
+     * overwise.
+     */
+    Unit(int),
+    /// C-like enums; basically an int.
+    CEnum(int, int), // discriminant range
+    /// Single-case variants, and structs/tuples/records.
+    Univariant(Struct, Destructor),
+    /**
+     * General-case enums: discriminant as int, followed by fields.
+     * The fields start immediately after the discriminant, meaning
+     * that they may not be correctly aligned for the platform's ABI;
+     * see above.
+     */
+    General(~[Struct])
+}
+
+/**
+ * Structs without destructors have historically had an extra layer of
+ * LLVM-struct to make accessing them work the same as structs with
+ * destructors.  This could probably be flattened to a boolean now
+ * that this module exists.
+ */
+enum Destructor {
+    StructWithDtor,
+    StructWithoutDtor,
+    NonStruct
+}
+
+/// For structs, and struct-like parts of anything fancier.
+struct Struct {
+    size: u64,
+    align: u64,
+    fields: ~[ty::t]
+}
+
+/**
+ * Convenience for `represent_type`.  There should probably be more or
+ * these, for places in trans where the `ty::t` isn't directly
+ * available.
+ */
+pub fn represent_node(bcx: block, node: ast::node_id) -> @Repr {
+    represent_type(bcx.ccx(), node_id_type(bcx, node))
+}
+
+/// Decides how to represent a given type.
+pub fn represent_type(cx: @CrateContext, t: ty::t) -> @Repr {
+    debug!("Representing: %s", ty_to_str(cx.tcx, t));
+    match cx.adt_reprs.find(&t) {
+        Some(repr) => return *repr,
+        None => { }
+    }
+    let repr = @match ty::get(t).sty {
+        ty::ty_tup(ref elems) => {
+            Univariant(mk_struct(cx, *elems), NonStruct)
+        }
+        ty::ty_rec(ref fields) => {
+            // XXX: Are these in the right order?
+            Univariant(mk_struct(cx, fields.map(|f| f.mt.ty)),
+                       StructWithoutDtor)
+        }
+        ty::ty_struct(def_id, ref substs) => {
+            let fields = ty::lookup_struct_fields(cx.tcx, def_id);
+            let dt = ty::ty_dtor(cx.tcx, def_id).is_present();
+            Univariant(mk_struct(cx, fields.map(|field| {
+                ty::lookup_field_type(cx.tcx, def_id, field.id, substs)
+            })), if dt { StructWithDtor } else { StructWithoutDtor })
+        }
+        ty::ty_enum(def_id, ref substs) => {
+            struct Case { discr: int, tys: ~[ty::t] };
+
+            let cases = do ty::enum_variants(cx.tcx, def_id).map |vi| {
+                let arg_tys = do vi.args.map |&raw_ty| {
+                    ty::subst(cx.tcx, substs, raw_ty)
+                };
+                Case { discr: vi.disr_val, tys: arg_tys }
+            };
+            if cases.len() == 0 {
+                // Uninhabitable; represent as unit
+                Unit(0)
+            } else if cases.len() == 1 && cases[0].tys.len() == 0 {
+                // `()`-like; see comment on definition of `Unit`.
+                Unit(cases[0].discr)
+            } else if cases.len() == 1 {
+                // Equivalent to a struct/tuple/newtype.
+                assert cases[0].discr == 0;
+                Univariant(mk_struct(cx, cases[0].tys), NonStruct)
+            } else if cases.all(|c| c.tys.len() == 0) {
+                // All bodies empty -> intlike
+                let discrs = cases.map(|c| c.discr);
+                CEnum(discrs.min(), discrs.max())
+            } else {
+                // The general case.  Since there's at least one
+                // non-empty body, explicit discriminants should have
+                // been rejected by a checker before this point.
+                if !cases.alli(|i,c| c.discr == (i as int)) {
+                    cx.sess.bug(fmt!("non-C-like enum %s with specified \
+                                      discriminants",
+                                     ty::item_path_str(cx.tcx, def_id)))
+                }
+                General(cases.map(|c| mk_struct(cx, c.tys)))
+            }
+        }
+        _ => cx.sess.bug(~"adt::represent_type called on non-ADT type")
+    };
+    cx.adt_reprs.insert(t, repr);
+    return repr;
+}
+
+fn mk_struct(cx: @CrateContext, tys: &[ty::t]) -> Struct {
+    let lltys = tys.map(|&ty| type_of::sizing_type_of(cx, ty));
+    let llty_rec = T_struct(lltys);
+    Struct {
+        size: machine::llsize_of_alloc(cx, llty_rec) /*bad*/as u64,
+        align: machine::llalign_of_min(cx, llty_rec) /*bad*/as u64,
+        fields: vec::from_slice(tys)
+    }
+}
+
+/**
+ * Returns the fields of a struct for the given representation.
+ * All nominal types are LLVM structs, in order to be able to use
+ * forward-declared opaque types to prevent circularity in `type_of`.
+ */
+pub fn fields_of(cx: @CrateContext, r: &Repr) -> ~[TypeRef] {
+    generic_fields_of(cx, r, false)
+}
+/// Like `fields_of`, but for `type_of::sizing_type_of` (q.v.).
+pub fn sizing_fields_of(cx: @CrateContext, r: &Repr) -> ~[TypeRef] {
+    generic_fields_of(cx, r, true)
+}
+fn generic_fields_of(cx: @CrateContext, r: &Repr, sizing: bool)
+    -> ~[TypeRef] {
+    match *r {
+        Unit(*) => ~[],
+        CEnum(*) => ~[T_enum_discrim(cx)],
+        Univariant(ref st, dt) => {
+            let f = if sizing {
+                st.fields.map(|&ty| type_of::sizing_type_of(cx, ty))
+            } else {
+                st.fields.map(|&ty| type_of::type_of(cx, ty))
+            };
+            match dt {
+                NonStruct => f,
+                StructWithoutDtor => ~[T_struct(f)],
+                StructWithDtor => ~[T_struct(f), T_i8()]
+            }
+        }
+        General(ref sts) => {
+            ~[T_enum_discrim(cx),
+              T_array(T_i8(), sts.map(|st| st.size).max() /*bad*/as uint)]
+        }
+    }
+}
+
+/**
+ * Obtain a representation of the discriminant sufficient to translate
+ * destructuring; this may or may not involve the actual discriminant.
+ *
+ * This should ideally be less tightly tied to `_match`.
+ */
+pub fn trans_switch(bcx: block, r: &Repr, scrutinee: ValueRef)
+    -> (_match::branch_kind, Option<ValueRef>) {
+    match *r {
+        CEnum(*) | General(*) => {
+            (_match::switch, Some(trans_get_discr(bcx, r, scrutinee)))
+        }
+        Unit(*) | Univariant(*) => {
+            (_match::single, None)
+        }
+    }
+}
+
+/// Obtain the actual discriminant of a value.
+pub fn trans_get_discr(bcx: block, r: &Repr, scrutinee: ValueRef)
+    -> ValueRef {
+    match *r {
+        Unit(the_disc) => C_int(bcx.ccx(), the_disc),
+        CEnum(min, max) => load_discr(bcx, scrutinee, min, max),
+        Univariant(*) => C_int(bcx.ccx(), 0),
+        General(ref cases) => load_discr(bcx, scrutinee, 0,
+                                         (cases.len() - 1) as int)
+    }
+}
+
+/// Helper for cases where the discriminant is simply loaded.
+fn load_discr(bcx: block, scrutinee: ValueRef, min: int, max: int)
+    -> ValueRef {
+    let ptr = GEPi(bcx, scrutinee, [0, 0]);
+    if max + 1 == min {
+        // i.e., if the range is everything.  The lo==hi case would be
+        // rejected by the LLVM verifier (it would mean either an
+        // empty set, which is impossible, or the entire range of the
+        // type, which is pointless).
+        Load(bcx, ptr)
+    } else {
+        // llvm::ConstantRange can deal with ranges that wrap around,
+        // so an overflow on (max + 1) is fine.
+        LoadRangeAssert(bcx, ptr, min as c_ulonglong,
+                        (max + 1) as c_ulonglong,
+                        /* signed: */ True)
+    }
+}
+
+/**
+ * Yield information about how to dispatch a case of the
+ * discriminant-like value returned by `trans_switch`.
+ *
+ * This should ideally be less tightly tied to `_match`.
+ */
+pub fn trans_case(bcx: block, r: &Repr, discr: int) -> _match::opt_result {
+    match *r {
+        CEnum(*) => {
+            _match::single_result(rslt(bcx, C_int(bcx.ccx(), discr)))
+        }
+        Unit(*) | Univariant(*)=> {
+            bcx.ccx().sess.bug(~"no cases for univariants or structs")
+        }
+        General(*) => {
+            _match::single_result(rslt(bcx, C_int(bcx.ccx(), discr)))
+        }
+    }
+}
+
+/**
+ * Begin initializing a new value of the given case of the given
+ * representation.  The fields, if any, should then be initialized via
+ * `trans_field_ptr`.
+ */
+pub fn trans_start_init(bcx: block, r: &Repr, val: ValueRef, discr: int) {
+    match *r {
+        Unit(the_discr) => {
+            assert discr == the_discr;
+        }
+        CEnum(min, max) => {
+            assert min <= discr && discr <= max;
+            Store(bcx, C_int(bcx.ccx(), discr), GEPi(bcx, val, [0, 0]))
+        }
+        Univariant(_, StructWithDtor) => {
+            assert discr == 0;
+            Store(bcx, C_u8(1), GEPi(bcx, val, [0, 1]))
+        }
+        Univariant(*) => {
+            assert discr == 0;
+        }
+        General(*) => {
+            Store(bcx, C_int(bcx.ccx(), discr), GEPi(bcx, val, [0, 0]))
+        }
+    }
+}
+
+/**
+ * The number of fields in a given case; for use when obtaining this
+ * information from the type or definition is less convenient.
+ */
+pub fn num_args(r: &Repr, discr: int) -> uint {
+    match *r {
+        Unit(*) | CEnum(*) => 0,
+        Univariant(ref st, _dt) => { assert discr == 0; st.fields.len() }
+        General(ref cases) => cases[discr as uint].fields.len()
+    }
+}
+
+/// Access a field, at a point when the value's case is known.
+pub fn trans_field_ptr(bcx: block, r: &Repr, val: ValueRef, discr: int,
+                       ix: uint) -> ValueRef {
+    // Note: if this ever needs to generate conditionals (e.g., if we
+    // decide to do some kind of cdr-coding-like non-unique repr
+    // someday), it will need to return a possibly-new bcx as well.
+    match *r {
+        Unit(*) | CEnum(*) => {
+            bcx.ccx().sess.bug(~"element access in C-like enum")
+        }
+        Univariant(ref st, dt) => {
+            assert discr == 0;
+            let val = match dt {
+                NonStruct => val,
+                StructWithDtor | StructWithoutDtor => GEPi(bcx, val, [0, 0])
+            };
+            struct_field_ptr(bcx, st, val, ix, false)
+        }
+        General(ref cases) => {
+            struct_field_ptr(bcx, &cases[discr as uint],
+                                 GEPi(bcx, val, [0, 1]), ix, true)
+        }
+    }
+}
+
+fn struct_field_ptr(bcx: block, st: &Struct, val: ValueRef, ix: uint,
+              needs_cast: bool) -> ValueRef {
+    let ccx = bcx.ccx();
+
+    let val = if needs_cast {
+        let real_llty = T_struct(st.fields.map(
+            |&ty| type_of::type_of(ccx, ty)));
+        PointerCast(bcx, val, T_ptr(real_llty))
+    } else {
+        val
+    };
+
+    GEPi(bcx, val, [0, ix])
+}
+
+/// Access the struct drop flag, if present.
+pub fn trans_drop_flag_ptr(bcx: block, r: &Repr, val: ValueRef) -> ValueRef {
+    match *r {
+        Univariant(_, StructWithDtor) => GEPi(bcx, val, [0, 1]),
+        _ => bcx.ccx().sess.bug(~"tried to get drop flag of non-droppable \
+                                  type")
+    }
+}
+
+/**
+ * Construct a constant value, suitable for initializing a
+ * GlobalVariable, given a case and constant values for its fields.
+ * Note that this may have a different LLVM type (and different
+ * alignment!) from the representation's `type_of`, so it needs a
+ * pointer cast before use.
+ *
+ * The LLVM type system does not directly support unions, and only
+ * pointers can be bitcast, so a constant (and, by extension, the
+ * GlobalVariable initialized by it) will have a type that can vary
+ * depending on which case of an enum it is.
+ *
+ * To understand the alignment situation, consider `enum E { V64(u64),
+ * V32(u32, u32) }` on win32.  The type should have 8-byte alignment
+ * to accommodate the u64 (currently it doesn't; this is a known bug),
+ * but `V32(x, y)` would have LLVM type `{i32, i32, i32}`, which is
+ * 4-byte aligned.
+ *
+ * Currently the returned value has the same size as the type, but
+ * this may be changed in the future to avoid allocating unnecessary
+ * space after values of shorter-than-maximum cases.
+ */
+pub fn trans_const(ccx: @CrateContext, r: &Repr, discr: int,
+                   vals: &[ValueRef]) -> ValueRef {
+    match *r {
+        Unit(*) => {
+            C_struct(~[])
+        }
+        CEnum(min, max) => {
+            assert vals.len() == 0;
+            assert min <= discr && discr <= max;
+            C_int(ccx, discr)
+        }
+        Univariant(ref st, dt) => {
+            assert discr == 0;
+            let s = C_struct(build_const_struct(ccx, st, vals));
+            match dt {
+                NonStruct => s,
+                // The actual destructor flag doesn't need to be present.
+                // But add an extra struct layer for compatibility.
+                StructWithDtor | StructWithoutDtor => C_struct(~[s])
+            }
+        }
+        General(ref cases) => {
+            let case = &cases[discr as uint];
+            let max_sz = cases.map(|s| s.size).max();
+            let body = build_const_struct(ccx, case, vals);
+
+            // The unary packed struct has alignment 1 regardless of
+            // its contents, so it will always be located at the
+            // expected offset at runtime.
+            C_struct([C_int(ccx, discr),
+                      C_packed_struct([C_struct(body)]),
+                      padding(max_sz - case.size)])
+        }
+    }
+}
+
+/**
+ * Building structs is a little complicated, because we might need to
+ * insert padding if a field's value is less aligned than its type.
+ *
+ * Continuing the example from `trans_const`, a value of type `(u32,
+ * E)` should have the `E` at offset 8, but if that field's
+ * initializer is 4-byte aligned then simply translating the tuple as
+ * a two-element struct will locate it at offset 4, and accesses to it
+ * will read the wrong memory.
+ */
+fn build_const_struct(ccx: @CrateContext, st: &Struct, vals: &[ValueRef])
+    -> ~[ValueRef] {
+    assert vals.len() == st.fields.len();
+
+    let mut offset = 0;
+    let mut cfields = ~[];
+    for st.fields.eachi |i, &ty| {
+        let llty = type_of::sizing_type_of(ccx, ty);
+        let type_align = machine::llalign_of_min(ccx, llty)
+            /*bad*/as u64;
+        let val_align = machine::llalign_of_min(ccx, val_ty(vals[i]))
+            /*bad*/as u64;
+        let target_offset = roundup(offset, type_align);
+        offset = roundup(offset, val_align);
+        if (offset != target_offset) {
+            cfields.push(padding(target_offset - offset));
+            offset = target_offset;
+        }
+        assert !is_undef(vals[i]);
+        // If that assert fails, could change it to wrap in a struct?
+        // (See `const_struct_field` for why real fields must not be undef.)
+        cfields.push(vals[i]);
+    }
+
+    return cfields;
+}
+
+fn padding(size: u64) -> ValueRef {
+    C_undef(T_array(T_i8(), size /*bad*/as uint))
+}
+
+// XXX this utility routine should be somewhere more general
+#[always_inline]
+fn roundup(x: u64, a: u64) -> u64 { ((x + (a - 1)) / a) * a }
+
+/// Get the discriminant of a constant value.  (Not currently used.)
+pub fn const_get_discrim(ccx: @CrateContext, r: &Repr, val: ValueRef)
+    -> int {
+    match *r {
+        Unit(discr) => discr,
+        CEnum(*) => const_to_int(val) as int,
+        Univariant(*) => 0,
+        General(*) => const_to_int(const_get_elt(ccx, val, [0])) as int,
+    }
+}
+
+/**
+ * Extract a field of a constant value, as appropriate for its
+ * representation.
+ *
+ * (Not to be confused with `common::const_get_elt`, which operates on
+ * raw LLVM-level structs and arrays.)
+ */
+pub fn const_get_field(ccx: @CrateContext, r: &Repr, val: ValueRef,
+                       _discr: int, ix: uint) -> ValueRef {
+    match *r {
+        Unit(*) | CEnum(*) => ccx.sess.bug(~"element access in C-like enum \
+                                             const"),
+        Univariant(_, NonStruct) => const_struct_field(ccx, val, ix),
+        Univariant(*) => const_struct_field(ccx, const_get_elt(ccx, val,
+                                                               [0]), ix),
+        General(*) => const_struct_field(ccx, const_get_elt(ccx, val,
+                                                            [1, 0]), ix)
+    }
+}
+
+/// Extract field of struct-like const, skipping our alignment padding.
+fn const_struct_field(ccx: @CrateContext, val: ValueRef, ix: uint)
+    -> ValueRef {
+    // Get the ix-th non-undef element of the struct.
+    let mut real_ix = 0; // actual position in the struct
+    let mut ix = ix; // logical index relative to real_ix
+    let mut field;
+    loop {
+        loop {
+            field = const_get_elt(ccx, val, [real_ix]);
+            if !is_undef(field) {
+                break;
+            }
+            real_ix = real_ix + 1;
+        }
+        if ix == 0 {
+            return field;
+        }
+        ix = ix - 1;
+        real_ix = real_ix + 1;
+    }
+}
+
+/// Is it safe to bitcast a value to the one field of its one variant?
+pub fn is_newtypeish(r: &Repr) -> bool {
+    match *r {
+        Univariant(ref st, StructWithoutDtor)
+        | Univariant(ref st, NonStruct) => st.fields.len() == 1,
+        _ => false
+    }
+}
diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs
index b6701c6c3df..8280243455d 100644
--- a/src/librustc/middle/trans/base.rs
+++ b/src/librustc/middle/trans/base.rs
@@ -39,6 +39,7 @@ use middle::astencode;
 use middle::borrowck::RootInfo;
 use middle::resolve;
 use middle::trans::_match;
+use middle::trans::adt;
 use middle::trans::base;
 use middle::trans::build::*;
 use middle::trans::callee;
@@ -66,6 +67,7 @@ use util::ppaux::{ty_to_str, ty_to_short_str};
 use util::ppaux;
 
 use core::hash;
+use core::hashmap::linear::LinearMap;
 use core::int;
 use core::io;
 use core::libc::{c_uint, c_ulonglong};
@@ -238,25 +240,6 @@ pub fn bump_ptr(bcx: block, t: ty::t, base: ValueRef, sz: ValueRef) ->
     PointerCast(bcx, bumped, typ)
 }
 
-// Replacement for the LLVM 'GEP' instruction when field indexing into a enum.
-// @llblobptr is the data part of a enum value; its actual type
-// is meaningless, as it will be cast away.
-pub fn GEP_enum(bcx: block, llblobptr: ValueRef, enum_id: ast::def_id,
-                variant_id: ast::def_id, ty_substs: &[ty::t],
-                ix: uint) -> ValueRef {
-    let _icx = bcx.insn_ctxt("GEP_enum");
-    let ccx = bcx.ccx();
-    let variant = ty::enum_variant_with_id(ccx.tcx, enum_id, variant_id);
-    assert ix < variant.args.len();
-
-    let arg_lltys = vec::map(variant.args, |aty| {
-        type_of(ccx, ty::subst_tps(ccx.tcx, ty_substs, None, *aty))
-    });
-    let typed_blobptr = PointerCast(bcx, llblobptr,
-                                    T_ptr(T_struct(arg_lltys)));
-    GEPi(bcx, typed_blobptr, [0u, ix])
-}
-
 // Returns a pointer to the body for the box. The box may be an opaque
 // box. The result will be casted to the type of body_t, if it is statically
 // known.
@@ -639,35 +622,17 @@ pub fn iter_structural_ty(cx: block, av: ValueRef, t: ty::t,
                           f: val_and_ty_fn) -> block {
     let _icx = cx.insn_ctxt("iter_structural_ty");
 
-    fn iter_variant(cx: block, a_tup: ValueRef,
+    fn iter_variant(cx: block, repr: &adt::Repr, av: ValueRef,
                     variant: ty::VariantInfo,
-                    tps: &[ty::t], tid: ast::def_id,
-                    f: val_and_ty_fn) -> block {
+                    tps: &[ty::t], f: val_and_ty_fn) -> block {
         let _icx = cx.insn_ctxt("iter_variant");
-        if variant.args.len() == 0u { return cx; }
-        let fn_ty = variant.ctor_ty;
-        let ccx = cx.ccx();
+        let tcx = cx.tcx();
         let mut cx = cx;
-        match ty::get(fn_ty).sty {
-          ty::ty_bare_fn(ref fn_ty) => {
-            let mut j = 0u;
-            let v_id = variant.id;
-            for vec::each(fn_ty.sig.inputs) |a| {
-                let llfldp_a = GEP_enum(cx, a_tup, tid, v_id,
-                                        /*bad*/copy tps, j);
-                // This assumes the self type is absent (it passes
-                // None for the self_ty_opt arg of substs_tps).
-                // I think that's ok since you can't have an enum
-                // inside a trait.
-                let ty_subst = ty::subst_tps(ccx.tcx, tps, None, a.ty);
-                cx = f(cx, llfldp_a, ty_subst);
-                j += 1u;
-            }
-          }
-          _ => cx.tcx().sess.bug(fmt!("iter_variant: not a function type: \
-                                       %s (variant name = %s)",
-                                      cx.ty_to_str(fn_ty),
-                                      *cx.sess().str_of(variant.name)))
+
+        for variant.args.eachi |i, &arg| {
+            cx = f(cx,
+                   adt::trans_field_ptr(cx, repr, av, variant.disr_val, i),
+                   ty::subst_tps(tcx, tps, None, arg));
         }
         return cx;
     }
@@ -675,9 +640,10 @@ pub fn iter_structural_ty(cx: block, av: ValueRef, t: ty::t,
     let mut cx = cx;
     match /*bad*/copy ty::get(t).sty {
       ty::ty_rec(*) | ty::ty_struct(*) => {
-          do expr::with_field_tys(cx.tcx(), t, None) |_has_dtor, field_tys| {
+          let repr = adt::represent_type(cx.ccx(), t);
+          do expr::with_field_tys(cx.tcx(), t, None) |discr, field_tys| {
               for vec::eachi(field_tys) |i, field_ty| {
-                  let llfld_a = GEPi(cx, av, struct_field(i));
+                  let llfld_a = adt::trans_field_ptr(cx, repr, av, discr, i);
                   cx = f(cx, llfld_a, field_ty.mt.ty);
               }
           }
@@ -688,51 +654,56 @@ pub fn iter_structural_ty(cx: block, av: ValueRef, t: ty::t,
         cx = tvec::iter_vec_raw(cx, base, t, len, f);
       }
       ty::ty_tup(args) => {
-        for vec::eachi(args) |i, arg| {
-            let llfld_a = GEPi(cx, av, [0u, i]);
-            cx = f(cx, llfld_a, *arg);
-        }
+          let repr = adt::represent_type(cx.ccx(), t);
+          for vec::eachi(args) |i, arg| {
+              let llfld_a = adt::trans_field_ptr(cx, repr, av, 0, i);
+              cx = f(cx, llfld_a, *arg);
+          }
       }
       ty::ty_enum(tid, ref substs) => {
-        let variants = ty::enum_variants(cx.tcx(), tid);
-        let n_variants = (*variants).len();
-
-        // Cast the enums to types we can GEP into.
-        if n_variants == 1u {
-            return iter_variant(cx,
-                                av,
-                                variants[0],
-                                /*bad*/copy substs.tps,
-                                tid,
-                                f);
-        }
+          let ccx = cx.ccx();
 
-        let ccx = cx.ccx();
-        let llenumty = T_opaque_enum_ptr(ccx);
-        let av_enum = PointerCast(cx, av, llenumty);
-        let lldiscrim_a_ptr = GEPi(cx, av_enum, [0u, 0u]);
-        let llunion_a_ptr = GEPi(cx, av_enum, [0u, 1u]);
-        let lldiscrim_a = Load(cx, lldiscrim_a_ptr);
-
-        // NB: we must hit the discriminant first so that structural
-        // comparison know not to proceed when the discriminants differ.
-        cx = f(cx, lldiscrim_a_ptr, ty::mk_int(cx.tcx()));
-        let unr_cx = sub_block(cx, ~"enum-iter-unr");
-        Unreachable(unr_cx);
-        let llswitch = Switch(cx, lldiscrim_a, unr_cx.llbb, n_variants);
-        let next_cx = sub_block(cx, ~"enum-iter-next");
-        for vec::each(*variants) |variant| {
-            let variant_cx =
-                sub_block(cx,
-                                   ~"enum-iter-variant-" +
-                                       int::to_str(variant.disr_val));
-            AddCase(llswitch, C_int(ccx, variant.disr_val), variant_cx.llbb);
-            let variant_cx =
-                iter_variant(variant_cx, llunion_a_ptr, *variant,
-                             /*bad*/copy (*substs).tps, tid, f);
-            Br(variant_cx, next_cx.llbb);
-        }
-        return next_cx;
+          let repr = adt::represent_type(ccx, t);
+          let variants = ty::enum_variants(ccx.tcx, tid);
+          let n_variants = (*variants).len();
+
+          // NB: we must hit the discriminant first so that structural
+          // comparison know not to proceed when the discriminants differ.
+
+          match adt::trans_switch(cx, repr, av) {
+              (_match::single, None) => {
+                  cx = iter_variant(cx, repr, av, variants[0],
+                                    substs.tps, f);
+              }
+              (_match::switch, Some(lldiscrim_a)) => {
+                  cx = f(cx, lldiscrim_a, ty::mk_int(cx.tcx()));
+                  let unr_cx = sub_block(cx, ~"enum-iter-unr");
+                  Unreachable(unr_cx);
+                  let llswitch = Switch(cx, lldiscrim_a, unr_cx.llbb,
+                                        n_variants);
+                  let next_cx = sub_block(cx, ~"enum-iter-next");
+
+                  for vec::each(*variants) |variant| {
+                      let variant_cx =
+                          sub_block(cx, ~"enum-iter-variant-" +
+                                    int::to_str(variant.disr_val));
+                      let variant_cx =
+                          iter_variant(variant_cx, repr, av, *variant,
+                                       substs.tps, f);
+                      match adt::trans_case(cx, repr, variant.disr_val) {
+                          _match::single_result(r) => {
+                              AddCase(llswitch, r.val, variant_cx.llbb)
+                          }
+                          _ => ccx.sess.unimpl(~"value from adt::trans_case \
+                                                 in iter_structural_ty")
+                      }
+                      Br(variant_cx, next_cx.llbb);
+                  }
+                  cx = next_cx;
+              }
+              _ => ccx.sess.unimpl(~"value from adt::trans_switch \
+                                     in iter_structural_ty")
+          }
       }
       _ => cx.sess().unimpl(~"type in iter_structural_ty")
     }
@@ -828,30 +799,6 @@ pub fn trans_external_path(ccx: @CrateContext, did: ast::def_id, t: ty::t)
     };
 }
 
-pub fn get_discrim_val(cx: @CrateContext, span: span, enum_did: ast::def_id,
-                       variant_did: ast::def_id) -> ValueRef {
-    // Can't use `discrims` from the crate context here because
-    // those discriminants have an extra level of indirection,
-    // and there's no LLVM constant load instruction.
-    let mut lldiscrim_opt = None;
-    for ty::enum_variants(cx.tcx, enum_did).each |variant_info| {
-        if variant_info.id == variant_did {
-            lldiscrim_opt = Some(C_int(cx,
-                                       variant_info.disr_val));
-            break;
-        }
-    }
-
-    match lldiscrim_opt {
-        None => {
-            cx.tcx.sess.span_bug(span, ~"didn't find discriminant?!");
-        }
-        Some(found_lldiscrim) => {
-            found_lldiscrim
-        }
-    }
-}
-
 pub fn invoke(bcx: block, llfn: ValueRef, +llargs: ~[ValueRef]) -> block {
     let _icx = bcx.insn_ctxt("invoke_");
     if bcx.unreachable { return bcx; }
@@ -1885,7 +1832,6 @@ pub fn trans_enum_variant(ccx: @CrateContext,
                           variant: ast::variant,
                           args: &[ast::variant_arg],
                           disr: int,
-                          is_degen: bool,
                           param_substs: Option<@param_substs>,
                           llfndecl: ValueRef) {
     let _icx = ccx.insn_ctxt("trans_enum_variant");
@@ -1914,21 +1860,16 @@ pub fn trans_enum_variant(ccx: @CrateContext,
     let arg_tys = ty::ty_fn_args(node_id_type(bcx, variant.node.id));
     let bcx = copy_args_to_allocas(fcx, bcx, fn_args, raw_llargs, arg_tys);
 
-    // Cast the enum to a type we can GEP into.
-    let llblobptr = if is_degen {
-        fcx.llretptr
-    } else {
-        let llenumptr =
-            PointerCast(bcx, fcx.llretptr, T_opaque_enum_ptr(ccx));
-        let lldiscrimptr = GEPi(bcx, llenumptr, [0u, 0u]);
-        Store(bcx, C_int(ccx, disr), lldiscrimptr);
-        GEPi(bcx, llenumptr, [0u, 1u])
-    };
-    let t_id = local_def(enum_id);
-    let v_id = local_def(variant.node.id);
+    // XXX is there a better way to reconstruct the ty::t?
+    let enum_ty = ty::subst_tps(ccx.tcx, ty_param_substs, None,
+                                ty::node_id_to_type(ccx.tcx, enum_id));
+    let repr = adt::represent_type(ccx, enum_ty);
+
+    adt::trans_start_init(bcx, repr, fcx.llretptr, disr);
     for vec::eachi(args) |i, va| {
-        let lldestptr = GEP_enum(bcx, llblobptr, t_id, v_id,
-                                 /*bad*/copy ty_param_substs, i);
+        let lldestptr = adt::trans_field_ptr(bcx, repr, fcx.llretptr,
+                                             disr, i);
+
         // If this argument to this function is a enum, it'll have come in to
         // this function as an opaque blob due to the way that type_of()
         // works. So we have to cast to the destination's view of the type.
@@ -1981,8 +1922,23 @@ pub fn trans_tuple_struct(ccx: @CrateContext,
     let arg_tys = ty::ty_fn_args(node_id_type(bcx, ctor_id));
     let bcx = copy_args_to_allocas(fcx, bcx, fn_args, raw_llargs, arg_tys);
 
+    // XXX is there a better way to reconstruct the ty::t?
+    let ty_param_substs = match param_substs {
+        Some(ref substs) => /*bad*/copy substs.tys,
+        None => ~[]
+    };
+    let ctor_ty = ty::subst_tps(ccx.tcx, ty_param_substs, None,
+                                ty::node_id_to_type(ccx.tcx, ctor_id));
+    let tup_ty = match ty::get(ctor_ty).sty {
+        ty::ty_bare_fn(ref bft) => bft.sig.output,
+        _ => ccx.sess.bug(fmt!("trans_tuple_struct: unexpected ctor \
+                                return type %s",
+                               ty_to_str(ccx.tcx, ctor_ty)))
+    };
+    let repr = adt::represent_type(ccx, tup_ty);
+
     for fields.eachi |i, field| {
-        let lldestptr = GEPi(bcx, fcx.llretptr, [0, 0, i]);
+        let lldestptr = adt::trans_field_ptr(bcx, repr, fcx.llretptr, 0, i);
         let llarg = match fcx.llargs.get(&field.node.id) {
             local_mem(x) => x,
             _ => {
@@ -2038,7 +1994,7 @@ pub fn trans_struct_dtor(ccx: @CrateContext,
 }
 
 pub fn trans_enum_def(ccx: @CrateContext, enum_definition: ast::enum_def,
-                      id: ast::node_id, degen: bool,
+                      id: ast::node_id,
                       path: @ast_map::path, vi: @~[ty::VariantInfo],
                       i: &mut uint) {
     for vec::each(enum_definition.variants) |variant| {
@@ -2049,7 +2005,7 @@ pub fn trans_enum_def(ccx: @CrateContext, enum_definition: ast::enum_def,
             ast::tuple_variant_kind(ref args) if args.len() > 0 => {
                 let llfn = get_item_val(ccx, variant.node.id);
                 trans_enum_variant(ccx, id, *variant, /*bad*/copy *args,
-                                   disr_val, degen, None, llfn);
+                                   disr_val, None, llfn);
             }
             ast::tuple_variant_kind(_) => {
                 // Nothing to do.
@@ -2062,7 +2018,6 @@ pub fn trans_enum_def(ccx: @CrateContext, enum_definition: ast::enum_def,
                 trans_enum_def(ccx,
                                *enum_definition,
                                id,
-                               degen,
                                path,
                                vi,
                                &mut *i);
@@ -2113,11 +2068,10 @@ pub fn trans_item(ccx: @CrateContext, item: ast::item) {
       }
       ast::item_enum(ref enum_definition, ref generics) => {
         if !generics.is_type_parameterized() {
-            let degen = (*enum_definition).variants.len() == 1u;
             let vi = ty::enum_variants(ccx.tcx, local_def(item.id));
             let mut i = 0;
             trans_enum_def(ccx, (*enum_definition), item.id,
-                           degen, path, vi, &mut i);
+                           path, vi, &mut i);
         }
       }
       ast::item_const(_, expr) => consts::trans_const(ccx, expr, item.id),
@@ -3099,6 +3053,7 @@ pub fn trans_crate(sess: session::Session,
               module_data: HashMap(),
               lltypes: ty::new_ty_hash(),
               llsizingtypes: ty::new_ty_hash(),
+              adt_reprs: @mut LinearMap::new(),
               names: new_namegen(sess.parse_sess.interner),
               next_addrspace: new_addrspace_gen(),
               symbol_hasher: symbol_hasher,
diff --git a/src/librustc/middle/trans/common.rs b/src/librustc/middle/trans/common.rs
index 2d7149fdfb2..a363a950f9b 100644
--- a/src/librustc/middle/trans/common.rs
+++ b/src/librustc/middle/trans/common.rs
@@ -26,6 +26,7 @@ use lib;
 use metadata::common::LinkMeta;
 use middle::astencode;
 use middle::resolve;
+use middle::trans::adt;
 use middle::trans::base;
 use middle::trans::build;
 use middle::trans::callee;
@@ -44,7 +45,8 @@ use util::ppaux::{expr_repr, ty_to_str};
 
 use core::cast;
 use core::hash;
-use core::libc::c_uint;
+use core::hashmap::linear::LinearMap;
+use core::libc::{c_uint, c_longlong, c_ulonglong};
 use core::ptr;
 use core::str;
 use core::to_bytes;
@@ -203,6 +205,7 @@ pub struct CrateContext {
      module_data: HashMap<~str, ValueRef>,
      lltypes: HashMap<ty::t, TypeRef>,
      llsizingtypes: HashMap<ty::t, TypeRef>,
+     adt_reprs: @mut LinearMap<ty::t, @adt::Repr>,
      names: namegen,
      next_addrspace: addrspace_gen,
      symbol_hasher: @hash::State,
@@ -1016,22 +1019,6 @@ pub fn T_chan(cx: @CrateContext, _t: TypeRef) -> TypeRef {
 pub fn T_taskptr(cx: @CrateContext) -> TypeRef { return T_ptr(cx.task_type); }
 
 
-// This type must never be used directly; it must always be cast away.
-pub fn T_typaram(tn: @TypeNames) -> TypeRef {
-    let s = @"typaram";
-    match name_has_type(tn, s) {
-      Some(t) => return t,
-      _ => ()
-    }
-    let t = T_i8();
-    associate_type(tn, s, t);
-    return t;
-}
-
-pub fn T_typaram_ptr(tn: @TypeNames) -> TypeRef {
-    return T_ptr(T_typaram(tn));
-}
-
 pub fn T_opaque_cbox_ptr(cx: @CrateContext) -> TypeRef {
     // closures look like boxes (even when they are ~fn or &fn)
     // see trans_closure.rs
@@ -1042,21 +1029,6 @@ pub fn T_enum_discrim(cx: @CrateContext) -> TypeRef {
     return cx.int_type;
 }
 
-pub fn T_opaque_enum(cx: @CrateContext) -> TypeRef {
-    let s = @"opaque_enum";
-    match name_has_type(cx.tn, s) {
-      Some(t) => return t,
-      _ => ()
-    }
-    let t = T_struct(~[T_enum_discrim(cx), T_i8()]);
-    associate_type(cx.tn, s, t);
-    return t;
-}
-
-pub fn T_opaque_enum_ptr(cx: @CrateContext) -> TypeRef {
-    return T_ptr(T_opaque_enum(cx));
-}
-
 pub fn T_captured_tydescs(cx: @CrateContext, n: uint) -> TypeRef {
     return T_struct(vec::from_elem::<TypeRef>(n, T_ptr(cx.tydesc_type)));
 }
@@ -1087,6 +1059,12 @@ pub fn C_null(t: TypeRef) -> ValueRef {
     }
 }
 
+pub fn C_undef(t: TypeRef) -> ValueRef {
+    unsafe {
+        return llvm::LLVMGetUndef(t);
+    }
+}
+
 pub fn C_integral(t: TypeRef, u: u64, sign_extend: Bool) -> ValueRef {
     unsafe {
         return llvm::LLVMConstInt(t, u, sign_extend);
@@ -1254,6 +1232,38 @@ pub fn get_param(fndecl: ValueRef, param: uint) -> ValueRef {
     }
 }
 
+pub fn const_get_elt(cx: @CrateContext, v: ValueRef, us: &[c_uint])
+                  -> ValueRef {
+    unsafe {
+        let r = do vec::as_imm_buf(us) |p, len| {
+            llvm::LLVMConstExtractValue(v, p, len as c_uint)
+        };
+
+        debug!("const_get_elt(v=%s, us=%?, r=%s)",
+               val_str(cx.tn, v), us, val_str(cx.tn, r));
+
+        return r;
+    }
+}
+
+pub fn const_to_int(v: ValueRef) -> c_longlong {
+    unsafe {
+        llvm::LLVMConstIntGetSExtValue(v)
+    }
+}
+
+pub fn const_to_uint(v: ValueRef) -> c_ulonglong {
+    unsafe {
+        llvm::LLVMConstIntGetZExtValue(v)
+    }
+}
+
+pub fn is_undef(val: ValueRef) -> bool {
+    unsafe {
+        llvm::LLVMIsUndef(val) != False
+    }
+}
+
 // Used to identify cached monomorphized functions and vtables
 #[deriving_eq]
 pub enum mono_param_id {
@@ -1430,18 +1440,6 @@ pub fn dummy_substs(+tps: ~[ty::t]) -> ty::substs {
     }
 }
 
-pub fn struct_field(index: uint) -> [uint * 3] {
-    //! The GEPi sequence to access a field of a record/struct.
-
-    [0, 0, index]
-}
-
-pub fn struct_dtor() -> [uint * 2] {
-    //! The GEPi sequence to access the dtor of a struct.
-
-    [0, 1]
-}
-
 // Casts a Rust bool value to an i1.
 pub fn bool_to_i1(bcx: block, llval: ValueRef) -> ValueRef {
     build::ICmp(bcx, lib::llvm::IntNE, llval, C_bool(false))
diff --git a/src/librustc/middle/trans/consts.rs b/src/librustc/middle/trans/consts.rs
index e585e433ef2..d19ffe8cb21 100644
--- a/src/librustc/middle/trans/consts.rs
+++ b/src/librustc/middle/trans/consts.rs
@@ -12,6 +12,7 @@ use core::prelude::*;
 
 use lib::llvm::{llvm, ValueRef, TypeRef, Bool, True, False};
 use middle::const_eval;
+use middle::trans::adt;
 use middle::trans::base;
 use middle::trans::base::get_insn_ctxt;
 use middle::trans::common::*;
@@ -103,20 +104,6 @@ pub fn const_deref(cx: @CrateContext, v: ValueRef) -> ValueRef {
     }
 }
 
-pub fn const_get_elt(cx: @CrateContext, v: ValueRef, us: &[c_uint])
-                  -> ValueRef {
-    unsafe {
-        let r = do vec::as_imm_buf(us) |p, len| {
-            llvm::LLVMConstExtractValue(v, p, len as c_uint)
-        };
-
-        debug!("const_get_elt(v=%s, us=%?, r=%s)",
-               val_str(cx.tn, v), us, val_str(cx.tn, r));
-
-        return r;
-    }
-}
-
 pub fn const_autoderef(cx: @CrateContext, ty: ty::t, v: ValueRef)
     -> (ty::t, ValueRef) {
     let mut t1 = ty;
@@ -253,16 +240,12 @@ fn const_expr_unchecked(cx: @CrateContext, e: @ast::expr) -> ValueRef {
           }
           ast::expr_field(base, field, _) => {
               let bt = ty::expr_ty(cx.tcx, base);
+              let brepr = adt::represent_type(cx, bt);
               let bv = const_expr(cx, base);
               let (bt, bv) = const_autoderef(cx, bt, bv);
-              do expr::with_field_tys(cx.tcx, bt, None) |_, field_tys| {
+              do expr::with_field_tys(cx.tcx, bt, None) |discr, field_tys| {
                   let ix = ty::field_idx_strict(cx.tcx, field, field_tys);
-
-                  // Note: ideally, we'd use `struct_field()` here instead
-                  // of hardcoding [0, ix], but we can't because it yields
-                  // the wrong type and also inserts an extra 0 that is
-                  // not needed in the constant variety:
-                  const_get_elt(cx, bv, [0, ix as c_uint])
+                  adt::const_get_field(cx, brepr, bv, discr, ix)
               }
           }
 
@@ -342,24 +325,8 @@ fn const_expr_unchecked(cx: @CrateContext, e: @ast::expr) -> ValueRef {
               }
               (expr::cast_enum, expr::cast_integral) |
               (expr::cast_enum, expr::cast_float)  => {
-                let def = ty::resolve_expr(cx.tcx, base);
-                let (enum_did, variant_did) = match def {
-                    ast::def_variant(enum_did, variant_did) => {
-                        (enum_did, variant_did)
-                    }
-                    _ => cx.sess.bug(~"enum cast source is not enum")
-                };
-                // Note that we know this is a C-like (nullary) enum
-                // variant or we wouldn't have gotten here
-                let variants = ty::enum_variants(cx.tcx, enum_did);
-                let iv = if variants.len() == 1 {
-                    // Univariants don't have a discriminant field,
-                    // because there's only one value it could have:
-                    C_integral(T_i64(),
-                               variants[0].disr_val as u64, True)
-                } else {
-                    base::get_discrim_val(cx, e.span, enum_did, variant_did)
-                };
+                let repr = adt::represent_type(cx, basety);
+                let iv = C_int(cx, adt::const_get_discrim(cx, repr, v));
                 let ety_cast = expr::cast_type_kind(ety);
                 match ety_cast {
                     expr::cast_integral => {
@@ -387,18 +354,22 @@ fn const_expr_unchecked(cx: @CrateContext, e: @ast::expr) -> ValueRef {
             gv
           }
           ast::expr_tup(es) => {
-            C_struct(es.map(|e| const_expr(cx, *e)))
+              let ety = ty::expr_ty(cx.tcx, e);
+              let repr = adt::represent_type(cx, ety);
+              adt::trans_const(cx, repr, 0, es.map(|e| const_expr(cx, *e)))
           }
           ast::expr_rec(ref fs, None) => {
-              C_struct([C_struct(
-                  (*fs).map(|f| const_expr(cx, f.node.expr)))])
+              let ety = ty::expr_ty(cx.tcx, e);
+              let repr = adt::represent_type(cx, ety);
+              adt::trans_const(cx, repr, 0,
+                               fs.map(|f| const_expr(cx, f.node.expr)))
           }
-          ast::expr_struct(_, ref fs, _) => {
+          ast::expr_struct(_, ref fs, None) => {
               let ety = ty::expr_ty(cx.tcx, e);
-              let cs = do expr::with_field_tys(cx.tcx,
-                                               ety,
-                                               None) |_hd, field_tys| {
-                  field_tys.map(|field_ty| {
+              let repr = adt::represent_type(cx, ety);
+              do expr::with_field_tys(cx.tcx, ety, Some(e.id))
+                  |discr, field_tys| {
+                  let cs = field_tys.map(|field_ty| {
                       match fs.find(|f| field_ty.ident == f.node.ident) {
                           Some(ref f) => const_expr(cx, (*f).node.expr),
                           None => {
@@ -406,9 +377,9 @@ fn const_expr_unchecked(cx: @CrateContext, e: @ast::expr) -> ValueRef {
                                   e.span, ~"missing struct field");
                           }
                       }
-                  })
-              };
-              C_struct([C_struct(cs)])
+                  });
+                  adt::trans_const(cx, repr, discr, cs)
+              }
           }
           ast::expr_vec(es, ast::m_imm) => {
             let (v, _, _) = const_vec(cx, e, es);
@@ -466,25 +437,12 @@ fn const_expr_unchecked(cx: @CrateContext, e: @ast::expr) -> ValueRef {
                     get_const_val(cx, def_id)
                 }
                 Some(ast::def_variant(enum_did, variant_did)) => {
-                    // Note that we know this is a C-like (nullary) enum
-                    // variant or we wouldn't have gotten here -- the constant
-                    // checker forbids paths that don't map to C-like enum
-                    // variants.
-                    if ty::enum_is_univariant(cx.tcx, enum_did) {
-                        // Univariants have no discriminant field.
-                        C_struct(~[])
-                    } else {
-                    let lldiscrim = base::get_discrim_val(cx, e.span,
-                                                          enum_did,
-                                                          variant_did);
-                    // However, we still have to pad it out to the
-                    // size of the full enum; see the expr_call case,
-                    // below.
                     let ety = ty::expr_ty(cx.tcx, e);
-                    let size = machine::static_size_of_enum(cx, ety);
-                    let padding = C_null(T_array(T_i8(), size));
-                    C_struct(~[lldiscrim, padding])
-                }
+                    let repr = adt::represent_type(cx, ety);
+                    let vinfo = ty::enum_variant_with_id(cx.tcx,
+                                                         enum_did,
+                                                         variant_did);
+                    adt::trans_const(cx, repr, vinfo.disr_val, [])
                 }
                 Some(ast::def_struct(_)) => {
                     let ety = ty::expr_ty(cx.tcx, e);
@@ -492,52 +450,31 @@ fn const_expr_unchecked(cx: @CrateContext, e: @ast::expr) -> ValueRef {
                     C_null(llty)
                 }
                 _ => {
-                    cx.sess.span_bug(e.span,
-                                     ~"expected a const, fn, or variant def")
+                    cx.sess.span_bug(e.span, ~"expected a const, fn, \
+                                               struct, or variant def")
                 }
             }
           }
           ast::expr_call(callee, args, _) => {
-            match cx.tcx.def_map.find(&callee.id) {
-                Some(ast::def_struct(def_id)) => {
-                    let llstructbody =
-                        C_struct(args.map(|a| const_expr(cx, *a)));
-                    if ty::ty_dtor(cx.tcx, def_id).is_present() {
-                        C_struct(~[ llstructbody, C_u8(0) ])
-                    } else {
-                        C_struct(~[ llstructbody ])
-                    }
-                }
-            Some(ast::def_variant(tid, vid)) => {
-                let ety = ty::expr_ty(cx.tcx, e);
-                let univar = ty::enum_is_univariant(cx.tcx, tid);
-                let size = machine::static_size_of_enum(cx, ety);
-
-                let discrim = base::get_discrim_val(cx, e.span, tid, vid);
-                let c_args = C_struct(args.map(|a| const_expr(cx, *a)));
-
-                // FIXME (#1645): enum body alignment is generaly wrong.
-                if !univar {
-                    // Pad out the data to the size of its type_of;
-                    // this is necessary if the enum is contained
-                    // within an aggregate (tuple, struct, vector) so
-                    // that the next element is at the right offset.
-                    let actual_size =
-                        machine::llsize_of_real(cx, llvm::LLVMTypeOf(c_args));
-                    let padding =
-                        C_null(T_array(T_i8(), size - actual_size));
-                    // A packed_struct has an alignment of 1; thus,
-                    // wrapping one around c_args will misalign it the
-                    // same way we normally misalign enum bodies
-                    // without affecting its internal alignment or
-                    // changing the alignment of the enum.
-                    C_struct(~[discrim, C_packed_struct(~[c_args]), padding])
-                } else {
-                    C_struct(~[c_args])
-                }
-            }
-                _ => cx.sess.span_bug(e.span, ~"expected a struct def")
-            }
+              match cx.tcx.def_map.find(&callee.id) {
+                  Some(ast::def_struct(_)) => {
+                      let ety = ty::expr_ty(cx.tcx, e);
+                      let repr = adt::represent_type(cx, ety);
+                      adt::trans_const(cx, repr, 0,
+                                       args.map(|a| const_expr(cx, *a)))
+                  }
+                  Some(ast::def_variant(enum_did, variant_did)) => {
+                      let ety = ty::expr_ty(cx.tcx, e);
+                      let repr = adt::represent_type(cx, ety);
+                      let vinfo = ty::enum_variant_with_id(cx.tcx,
+                                                           enum_did,
+                                                           variant_did);
+                      adt::trans_const(cx, repr, vinfo.disr_val,
+                                       args.map(|a| const_expr(cx, *a)))
+                  }
+                  _ => cx.sess.span_bug(e.span, ~"expected a struct or \
+                                                  variant def")
+              }
           }
           ast::expr_paren(e) => { return const_expr(cx, e); }
           _ => cx.sess.span_bug(e.span,
diff --git a/src/librustc/middle/trans/datum.rs b/src/librustc/middle/trans/datum.rs
index ce472fd9f1f..e6089914564 100644
--- a/src/librustc/middle/trans/datum.rs
+++ b/src/librustc/middle/trans/datum.rs
@@ -90,6 +90,7 @@ use core::prelude::*;
 use lib;
 use lib::llvm::ValueRef;
 use middle::borrowck::{RootInfo, root_map_key};
+use middle::trans::adt;
 use middle::trans::base::*;
 use middle::trans::build::*;
 use middle::trans::callee;
@@ -511,14 +512,13 @@ pub impl Datum {
         }
     }
 
-    fn GEPi(&self, bcx: block,
-            ixs: &[uint],
-            ty: ty::t,
-            source: DatumCleanup)
-         -> Datum {
+    fn get_element(&self, bcx: block,
+                   ty: ty::t,
+                   source: DatumCleanup,
+                   gep: fn(ValueRef) -> ValueRef) -> Datum {
         let base_val = self.to_ref_llval(bcx);
         Datum {
-            val: GEPi(bcx, base_val, ixs),
+            val: gep(base_val),
             mode: ByRef,
             ty: ty,
             source: source
@@ -678,15 +678,17 @@ pub impl Datum {
                     return (None, bcx);
                 }
 
+                let repr = adt::represent_type(ccx, self.ty);
+                assert adt::is_newtypeish(repr);
                 let ty = ty::subst(ccx.tcx, substs, variants[0].args[0]);
                 return match self.mode {
                     ByRef => {
                         // Recast lv.val as a pointer to the newtype
                         // rather than a ptr to the enum type.
-                        let llty = T_ptr(type_of::type_of(ccx, ty));
                         (
                             Some(Datum {
-                                val: PointerCast(bcx, self.val, llty),
+                                val: adt::trans_field_ptr(bcx, repr, self.val,
+                                                    0, 0),
                                 ty: ty,
                                 mode: ByRef,
                                 source: ZeroMem
@@ -716,6 +718,8 @@ pub impl Datum {
                     return (None, bcx);
                 }
 
+                let repr = adt::represent_type(ccx, self.ty);
+                assert adt::is_newtypeish(repr);
                 let ty = fields[0].mt.ty;
                 return match self.mode {
                     ByRef => {
@@ -725,7 +729,8 @@ pub impl Datum {
                         // destructors.
                         (
                             Some(Datum {
-                                val: GEPi(bcx, self.val, [0, 0, 0]),
+                                val: adt::trans_field_ptr(bcx, repr, self.val,
+                                                    0, 0),
                                 ty: ty,
                                 mode: ByRef,
                                 source: ZeroMem
diff --git a/src/librustc/middle/trans/expr.rs b/src/librustc/middle/trans/expr.rs
index d41bf357193..1dae9fccc62 100644
--- a/src/librustc/middle/trans/expr.rs
+++ b/src/librustc/middle/trans/expr.rs
@@ -126,6 +126,7 @@ use lib;
 use lib::llvm::{ValueRef, TypeRef, llvm, True};
 use middle::borrowck::root_map_key;
 use middle::trans::_match;
+use middle::trans::adt;
 use middle::trans::base;
 use middle::trans::base::*;
 use middle::trans::build::*;
@@ -602,7 +603,9 @@ fn trans_rvalue_dps_unadjusted(bcx: block, expr: @ast::expr,
             return trans_rec_or_struct(bcx, (*fields), base, expr.id, dest);
         }
         ast::expr_tup(ref args) => {
-            return trans_tup(bcx, *args, dest);
+            let repr = adt::represent_type(bcx.ccx(), expr_ty(bcx, expr));
+            return trans_adt(bcx, repr, 0, args.mapi(|i, arg| (i, *arg)),
+                             None, dest);
         }
         ast::expr_lit(@codemap::spanned {node: ast::lit_str(s), _}) => {
             return tvec::trans_lit_str(bcx, expr, s, dest);
@@ -719,14 +722,12 @@ fn trans_def_dps_unadjusted(bcx: block, ref_expr: @ast::expr,
                 let fn_data = callee::trans_fn_ref(bcx, vid, ref_expr.id);
                 Store(bcx, fn_data.llfn, lldest);
                 return bcx;
-            } else if !ty::enum_is_univariant(ccx.tcx, tid) {
-                // Nullary variant.
-                let lldiscrimptr = GEPi(bcx, lldest, [0u, 0u]);
-                let lldiscrim = C_int(bcx.ccx(), variant_info.disr_val);
-                Store(bcx, lldiscrim, lldiscrimptr);
-                return bcx;
             } else {
-                // Nullary univariant.
+                // Nullary variant.
+                let ty = expr_ty(bcx, ref_expr);
+                let repr = adt::represent_type(ccx, ty);
+                adt::trans_start_init(bcx, repr, lldest,
+                                      variant_info.disr_val);
                 return bcx;
             }
         }
@@ -883,13 +884,15 @@ fn trans_lvalue_unadjusted(bcx: block, expr: @ast::expr) -> DatumBlock {
         let _icx = bcx.insn_ctxt("trans_rec_field");
 
         let base_datum = unpack_datum!(bcx, trans_to_datum(bcx, base));
-        do with_field_tys(bcx.tcx(), base_datum.ty, None) |_dtor, field_tys| {
+        let repr = adt::represent_type(bcx.ccx(), base_datum.ty);
+        do with_field_tys(bcx.tcx(), base_datum.ty, None) |discr, field_tys| {
             let ix = ty::field_idx_strict(bcx.tcx(), field, field_tys);
             DatumBlock {
-                datum: base_datum.GEPi(bcx,
-                                       [0u, 0u, ix],
-                                       field_tys[ix].mt.ty,
-                                       ZeroMem),
+                datum: do base_datum.get_element(bcx,
+                                                 field_tys[ix].mt.ty,
+                                                 ZeroMem) |srcval| {
+                    adt::trans_field_ptr(bcx, repr, srcval, discr, ix)
+                },
                 bcx: bcx
             }
         }
@@ -1096,15 +1099,14 @@ pub fn trans_local_var(bcx: block, def: ast::def) -> Datum {
 pub fn with_field_tys<R>(tcx: ty::ctxt,
                          ty: ty::t,
                          node_id_opt: Option<ast::node_id>,
-                         op: fn(bool, (&[ty::field])) -> R) -> R {
+                         op: fn(int, (&[ty::field])) -> R) -> R {
     match ty::get(ty).sty {
         ty::ty_rec(ref fields) => {
-            op(false, *fields)
+            op(0, *fields)
         }
 
         ty::ty_struct(did, ref substs) => {
-            let has_dtor = ty::ty_dtor(tcx, did).is_present();
-            op(has_dtor, struct_mutable_fields(tcx, did, substs))
+            op(0, struct_mutable_fields(tcx, did, substs))
         }
 
         ty::ty_enum(_, ref substs) => {
@@ -1118,8 +1120,10 @@ pub fn with_field_tys<R>(tcx: ty::ctxt,
                 }
                 Some(node_id) => {
                     match tcx.def_map.get(&node_id) {
-                        ast::def_variant(_, variant_id) => {
-                            op(false, struct_mutable_fields(
+                        ast::def_variant(enum_id, variant_id) => {
+                            let variant_info = ty::enum_variant_with_id(
+                                tcx, enum_id, variant_id);
+                            op(variant_info.disr_val, struct_mutable_fields(
                                 tcx, variant_id, substs))
                         }
                         _ => {
@@ -1148,133 +1152,122 @@ fn trans_rec_or_struct(bcx: block,
     let _icx = bcx.insn_ctxt("trans_rec");
     let mut bcx = bcx;
 
-    // Handle the case where the result is ignored.
-    let addr;
-    match dest {
-        SaveIn(p) => {
-            addr = p;
-        }
-        Ignore => {
-            // just evaluate the values for each field and drop them
-            // on the floor
-            for vec::each(fields) |fld| {
-                bcx = trans_into(bcx, fld.node.expr, Ignore);
-            }
-            return bcx;
-        }
-    }
-
-    // If this is a struct-like variant, write in the discriminant if
-    // necessary, position the address at the right location, and cast the
-    // address.
     let ty = node_id_type(bcx, id);
     let tcx = bcx.tcx();
-    let addr = match ty::get(ty).sty {
-        ty::ty_enum(_, ref substs) => {
-            match tcx.def_map.get(&id) {
-                ast::def_variant(enum_id, variant_id) => {
-                    let variant_info = ty::enum_variant_with_id(
-                        tcx, enum_id, variant_id);
-                    let addr = if ty::enum_is_univariant(tcx, enum_id) {
-                        addr
-                    } else {
-                        Store(bcx,
-                              C_int(bcx.ccx(), variant_info.disr_val),
-                              GEPi(bcx, addr, [0, 0]));
-                        GEPi(bcx, addr, [0, 1])
-                    };
-                    let fields = ty::struct_mutable_fields(
-                        tcx, variant_id, substs);
-                    let field_lltys = do fields.map |field| {
-                        type_of::type_of(bcx.ccx(),
-                                ty::subst_tps(
-                                    tcx, substs.tps, None, field.mt.ty))
-                    };
-                    PointerCast(bcx, addr,
-                                T_ptr(T_struct(~[T_struct(field_lltys)])))
+    do with_field_tys(tcx, ty, Some(id)) |discr, field_tys| {
+        let mut need_base = vec::from_elem(field_tys.len(), true);
+
+        let numbered_fields = do fields.map |field| {
+            let opt_pos = vec::position(field_tys, |field_ty|
+                                        field_ty.ident == field.node.ident);
+            match opt_pos {
+                Some(i) => {
+                    need_base[i] = false;
+                    (i, field.node.expr)
                 }
-                _ => {
-                    tcx.sess.bug(~"resolve didn't write the right def in for \
-                                   this struct-like variant")
+                None => {
+                    tcx.sess.span_bug(field.span,
+                                      ~"Couldn't find field in struct type")
                 }
             }
-        }
-        _ => addr
-    };
-
-    do with_field_tys(tcx, ty, Some(id)) |has_dtor, field_tys| {
-        // evaluate each of the fields and store them into their
-        // correct locations
-        let mut temp_cleanups = ~[];
-        for fields.each |field| {
-            let ix = ty::field_idx_strict(tcx, field.node.ident, field_tys);
-            let dest = GEPi(bcx, addr, struct_field(ix));
-            bcx = trans_into(bcx, field.node.expr, SaveIn(dest));
-            add_clean_temp_mem(bcx, dest, field_tys[ix].mt.ty);
-            temp_cleanups.push(dest);
-        }
-
-        // copy over any remaining fields from the base (for
-        // functional record update)
-        for base.each |base_expr| {
-            let base_datum = unpack_datum!(
-                bcx, trans_to_datum(bcx, *base_expr));
-
-            // Copy/move over inherited fields
-            for field_tys.eachi |i, field_ty| {
-                if !fields.any(|f| f.node.ident == field_ty.ident) {
-                    let dest = GEPi(bcx, addr, struct_field(i));
-                    let base_field =
-                        base_datum.GEPi(bcx,
-                                        struct_field(i),
-                                        field_ty.mt.ty,
-                                        ZeroMem);
-                    bcx = base_field.store_to(bcx, base_expr.id, INIT, dest);
+        };
+        let optbase = match base {
+            Some(base_expr) => {
+                let mut leftovers = ~[];
+                for need_base.eachi |i, b| {
+                    if *b {
+                        leftovers.push((i, field_tys[i].mt.ty))
+                    }
                 }
+                Some(StructBaseInfo {expr: base_expr,
+                                     fields: leftovers })
             }
-        }
-
-        // Add the drop flag if necessary.
-        if has_dtor {
-            let dest = GEPi(bcx, addr, struct_dtor());
-            Store(bcx, C_u8(1), dest);
-        }
+            None => {
+                if need_base.any(|b| *b) {
+                    // XXX should be span bug
+                    tcx.sess.bug(~"missing fields and no base expr")
+                }
+                None
+            }
+        };
 
-        // Now revoke the cleanups as we pass responsibility for the data
-        // structure on to the caller
-        for temp_cleanups.each |cleanup| {
-            revoke_clean(bcx, *cleanup);
-        }
-        bcx
+        let repr = adt::represent_type(bcx.ccx(), ty);
+        trans_adt(bcx, repr, discr, numbered_fields, optbase, dest)
     }
 }
 
-fn trans_tup(bcx: block, elts: &[@ast::expr], dest: Dest) -> block {
-    let _icx = bcx.insn_ctxt("trans_tup");
+/**
+ * Information that `trans_adt` needs in order to fill in the fields
+ * of a struct copied from a base struct (e.g., from an expression
+ * like `Foo { a: b, ..base }`.
+ *
+ * Note that `fields` may be empty; the base expression must always be
+ * evaluated for side-effects.
+ */
+struct StructBaseInfo {
+    /// The base expression; will be evaluated after all explicit fields.
+    expr: @ast::expr,
+    /// The indices of fields to copy paired with their types.
+    fields: ~[(uint, ty::t)]
+}
+
+/**
+ * Constructs an ADT instance:
+ *
+ * - `fields` should be a list of field indices paired with the
+ * expression to store into that field.  The initializers will be
+ * evaluated in the order specified by `fields`.
+ *
+ * - `optbase` contains information on the base struct (if any) from
+ * which remaining fields are copied; see comments on `StructBaseInfo`.
+ */
+fn trans_adt(bcx: block, repr: &adt::Repr, discr: int,
+             fields: &[(uint, @ast::expr)],
+             optbase: Option<StructBaseInfo>,
+             dest: Dest) -> block {
+    let _icx = bcx.insn_ctxt("trans_adt");
     let mut bcx = bcx;
     let addr = match dest {
         Ignore => {
-            for vec::each(elts) |ex| {
-                bcx = trans_into(bcx, *ex, Ignore);
+            for fields.each |&(_i, e)| {
+                bcx = trans_into(bcx, e, Ignore);
+            }
+            for optbase.each |sbi| {
+                bcx = trans_into(bcx, sbi.expr, Ignore);
             }
             return bcx;
         }
-        SaveIn(pos) => pos,
+        SaveIn(pos) => pos
     };
     let mut temp_cleanups = ~[];
-    for vec::eachi(elts) |i, e| {
-        let dest = GEPi(bcx, addr, [0u, i]);
-        let e_ty = expr_ty(bcx, *e);
-        bcx = trans_into(bcx, *e, SaveIn(dest));
+    adt::trans_start_init(bcx, repr, addr, discr);
+    for fields.each |&(i, e)| {
+        let dest = adt::trans_field_ptr(bcx, repr, addr, discr, i);
+        let e_ty = expr_ty(bcx, e);
+        bcx = trans_into(bcx, e, SaveIn(dest));
         add_clean_temp_mem(bcx, dest, e_ty);
         temp_cleanups.push(dest);
     }
+    for optbase.each |base| {
+        // XXX is it sound to use the destination's repr on the base?
+        // XXX would it ever be reasonable to be here with discr != 0?
+        let base_datum = unpack_datum!(bcx, trans_to_datum(bcx, base.expr));
+        for base.fields.each |&(i, t)| {
+            let datum = do base_datum.get_element(bcx, t, ZeroMem) |srcval| {
+                adt::trans_field_ptr(bcx, repr, srcval, discr, i)
+            };
+            let dest = adt::trans_field_ptr(bcx, repr, addr, discr, i);
+            bcx = datum.store_to(bcx, base.expr.id, INIT, dest);
+        }
+    }
+
     for vec::each(temp_cleanups) |cleanup| {
         revoke_clean(bcx, *cleanup);
     }
     return bcx;
 }
 
+
 fn trans_immediate_lit(bcx: block, expr: @ast::expr,
                        lit: ast::lit) -> DatumBlock {
     // must not be a string constant, that is a RvalueDpsExpr
@@ -1671,22 +1664,8 @@ fn trans_imm_cast(bcx: block, expr: @ast::expr,
             (cast_enum, cast_integral) |
             (cast_enum, cast_float) => {
                 let bcx = bcx;
-                let in_tid = match ty::get(t_in).sty {
-                    ty::ty_enum(did, _) => did,
-                    _ => ccx.sess.bug(~"enum cast source is not enum")
-                };
-                let variants = ty::enum_variants(ccx.tcx, in_tid);
-                let lldiscrim_a = if variants.len() == 1 {
-                    // Univariants don't have a discriminant field,
-                    // because there's only one value it could have:
-                    C_integral(T_enum_discrim(ccx),
-                               variants[0].disr_val as u64, True)
-                } else {
-                    let llenumty = T_opaque_enum_ptr(ccx);
-                    let av_enum = PointerCast(bcx, llexpr, llenumty);
-                    let lldiscrim_a_ptr = GEPi(bcx, av_enum, [0u, 0u]);
-                    Load(bcx, lldiscrim_a_ptr)
-                };
+                let repr = adt::represent_type(ccx, t_in);
+                let lldiscrim_a = adt::trans_get_discr(bcx, repr, llexpr);
                 match k_out {
                     cast_integral => int_cast(bcx, ll_t_out,
                                               val_ty(lldiscrim_a),
diff --git a/src/librustc/middle/trans/glue.rs b/src/librustc/middle/trans/glue.rs
index 7bed3e86190..b692ae67950 100644
--- a/src/librustc/middle/trans/glue.rs
+++ b/src/librustc/middle/trans/glue.rs
@@ -19,6 +19,7 @@ use back::link::*;
 use driver::session;
 use lib;
 use lib::llvm::{llvm, ValueRef, TypeRef, True};
+use middle::trans::adt;
 use middle::trans::base::*;
 use middle::trans::callee;
 use middle::trans::closure;
@@ -447,10 +448,10 @@ pub fn make_free_glue(bcx: block, v: ValueRef, t: ty::t) {
         match ty::ty_dtor(bcx.tcx(), did) {
             ty::NoDtor => bcx,
             ty::LegacyDtor(ref dt_id) => {
-                trans_struct_drop(bcx, v, *dt_id, did, substs, false)
+                trans_struct_drop(bcx, t, v, *dt_id, did, substs, false)
             }
             ty::TraitDtor(ref dt_id) => {
-                trans_struct_drop(bcx, v, *dt_id, did, substs, true)
+                trans_struct_drop(bcx, t, v, *dt_id, did, substs, true)
             }
         }
       }
@@ -460,13 +461,15 @@ pub fn make_free_glue(bcx: block, v: ValueRef, t: ty::t) {
 }
 
 pub fn trans_struct_drop(bcx: block,
+                         t: ty::t,
                          v0: ValueRef,
                          dtor_did: ast::def_id,
                          class_did: ast::def_id,
                          substs: &ty::substs,
                          take_ref: bool)
                       -> block {
-    let drop_flag = GEPi(bcx, v0, struct_dtor());
+    let repr = adt::represent_type(bcx.ccx(), t);
+    let drop_flag = adt::trans_drop_flag_ptr(bcx, repr, v0);
     do with_cond(bcx, IsNotNull(bcx, Load(bcx, drop_flag))) |cx| {
         let mut bcx = cx;
 
@@ -504,7 +507,7 @@ pub fn trans_struct_drop(bcx: block,
             ty::struct_mutable_fields(bcx.tcx(), class_did,
                                               substs);
         for vec::eachi(field_tys) |i, fld| {
-            let llfld_a = GEPi(bcx, v0, struct_field(i));
+            let llfld_a = adt::trans_field_ptr(bcx, repr, v0, 0, i);
             bcx = drop_ty(bcx, llfld_a, fld.mt.ty);
         }
 
@@ -534,10 +537,10 @@ pub fn make_drop_glue(bcx: block, v0: ValueRef, t: ty::t) {
         let tcx = bcx.tcx();
         match ty::ty_dtor(tcx, did) {
           ty::TraitDtor(dtor) => {
-            trans_struct_drop(bcx, v0, dtor, did, substs, true)
+            trans_struct_drop(bcx, t, v0, dtor, did, substs, true)
           }
           ty::LegacyDtor(dtor) => {
-            trans_struct_drop(bcx, v0, dtor, did, substs, false)
+            trans_struct_drop(bcx, t, v0, dtor, did, substs, false)
           }
           ty::NoDtor => {
             // No dtor? Just the default case
diff --git a/src/librustc/middle/trans/monomorphize.rs b/src/librustc/middle/trans/monomorphize.rs
index c0af1f4fad2..ffc5d132c9f 100644
--- a/src/librustc/middle/trans/monomorphize.rs
+++ b/src/librustc/middle/trans/monomorphize.rs
@@ -196,8 +196,7 @@ pub fn monomorphic_fn(ccx: @CrateContext,
         match (*v).node.kind {
             ast::tuple_variant_kind(ref args) => {
                 trans_enum_variant(ccx, enum_item.id, *v, /*bad*/copy *args,
-                                   this_tv.disr_val, tvs.len() == 1u,
-                                   psubsts, d);
+                                   this_tv.disr_val, psubsts, d);
             }
             ast::struct_variant_kind(_) =>
                 ccx.tcx.sess.bug(~"can't monomorphize struct variants"),
diff --git a/src/librustc/middle/trans/type_of.rs b/src/librustc/middle/trans/type_of.rs
index 26cf91b03e1..8dac607bd52 100644
--- a/src/librustc/middle/trans/type_of.rs
+++ b/src/librustc/middle/trans/type_of.rs
@@ -11,15 +11,14 @@
 
 use lib::llvm::llvm;
 use lib::llvm::{TypeRef};
+use middle::trans::adt;
 use middle::trans::base;
 use middle::trans::common::*;
 use middle::trans::common;
-use middle::trans::machine;
 use middle::ty;
 use util::ppaux;
 
 use core::option::None;
-use core::vec;
 use syntax::ast;
 
 pub fn type_of_explicit_arg(ccx: @CrateContext, arg: ty::arg) -> TypeRef {
@@ -143,32 +142,12 @@ pub fn sizing_type_of(cx: @CrateContext, t: ty::t) -> TypeRef {
 
         ty::ty_unboxed_vec(mt) => T_vec(cx, sizing_type_of(cx, mt.ty)),
 
-        ty::ty_tup(ref elems) => {
-            T_struct(elems.map(|&t| sizing_type_of(cx, t)))
+        ty::ty_tup(*) | ty::ty_rec(*) | ty::ty_struct(*)
+        | ty::ty_enum(*) => {
+            let repr = adt::represent_type(cx, t);
+            T_struct(adt::sizing_fields_of(cx, repr))
         }
 
-        ty::ty_rec(ref fields) => {
-            T_struct(fields.map(|f| sizing_type_of(cx, f.mt.ty)))
-        }
-
-        ty::ty_struct(def_id, ref substs) => {
-            let fields = ty::lookup_struct_fields(cx.tcx, def_id);
-            let lltype = T_struct(fields.map(|field| {
-                let field_type = ty::lookup_field_type(cx.tcx,
-                                                       def_id,
-                                                       field.id,
-                                                       substs);
-                sizing_type_of(cx, field_type)
-            }));
-            if ty::ty_dtor(cx.tcx, def_id).is_present() {
-                T_struct(~[lltype, T_i8()])
-            } else {
-                lltype
-            }
-        }
-
-        ty::ty_enum(def_id, _) => T_struct(enum_body_types(cx, def_id, t)),
-
         ty::ty_self | ty::ty_infer(*) | ty::ty_param(*) | ty::ty_err(*) => {
             cx.tcx.sess.bug(
                 fmt!("fictitious type %? in sizing_type_of()",
@@ -257,28 +236,13 @@ pub fn type_of(cx: @CrateContext, t: ty::t) -> TypeRef {
         T_array(type_of(cx, mt.ty), n)
       }
 
-      ty::ty_rec(fields) => {
-        let mut tys: ~[TypeRef] = ~[];
-        for vec::each(fields) |f| {
-            let mt_ty = f.mt.ty;
-            tys.push(type_of(cx, mt_ty));
-        }
-
-        // n.b.: introduce an extra layer of indirection to match
-        // structs
-        T_struct(~[T_struct(tys)])
-      }
-
       ty::ty_bare_fn(_) => T_ptr(type_of_fn_from_ty(cx, t)),
       ty::ty_closure(_) => T_fn_pair(cx, type_of_fn_from_ty(cx, t)),
       ty::ty_trait(_, _, vstore) => T_opaque_trait(cx, vstore),
       ty::ty_type => T_ptr(cx.tydesc_type),
-      ty::ty_tup(elts) => {
-        let mut tys = ~[];
-        for vec::each(elts) |elt| {
-            tys.push(type_of(cx, *elt));
-        }
-        T_struct(tys)
+      ty::ty_tup(*) | ty::ty_rec(*) => {
+          let repr = adt::represent_type(cx, t);
+          T_struct(adt::fields_of(cx, repr))
       }
       ty::ty_opaque_closure_ptr(_) => T_opaque_box_ptr(cx),
       ty::ty_struct(did, ref substs) => {
@@ -301,24 +265,9 @@ pub fn type_of(cx: @CrateContext, t: ty::t) -> TypeRef {
 
     // If this was an enum or struct, fill in the type now.
     match ty::get(t).sty {
-      ty::ty_enum(did, _) => {
-        fill_type_of_enum(cx, did, t, llty);
-      }
-      ty::ty_struct(did, ref substs) => {
-        // Only instance vars are record fields at runtime.
-        let fields = ty::lookup_struct_fields(cx.tcx, did);
-        let mut tys = do vec::map(fields) |f| {
-            let t = ty::lookup_field_type(cx.tcx, did, f.id, substs);
-            type_of(cx, t)
-        };
-
-        // include a byte flag if there is a dtor so that we know when we've
-        // been dropped
-        if ty::ty_dtor(cx.tcx, did).is_present() {
-            common::set_struct_body(llty, ~[T_struct(tys), T_i8()]);
-        } else {
-            common::set_struct_body(llty, ~[T_struct(tys)]);
-        }
+      ty::ty_enum(*) | ty::ty_struct(*) => {
+          let repr = adt::represent_type(cx, t);
+          common::set_struct_body(llty, adt::fields_of(cx, repr));
       }
       _ => ()
     }
@@ -326,34 +275,6 @@ pub fn type_of(cx: @CrateContext, t: ty::t) -> TypeRef {
     return llty;
 }
 
-pub fn enum_body_types(cx: @CrateContext, did: ast::def_id, t: ty::t)
-                    -> ~[TypeRef] {
-    let univar = ty::enum_is_univariant(cx.tcx, did);
-    if !univar {
-        let size = machine::static_size_of_enum(cx, t);
-        ~[T_enum_discrim(cx), T_array(T_i8(), size)]
-    }
-    else {
-        // Use the actual fields, so we get the alignment right.
-        match ty::get(t).sty {
-            ty::ty_enum(_, ref substs) => {
-                do ty::enum_variants(cx.tcx, did)[0].args.map |&field_ty| {
-                    sizing_type_of(cx, ty::subst(cx.tcx, substs, field_ty))
-                }
-            }
-            _ => cx.sess.bug(~"enum is not an enum")
-        }
-    }
-}
-
-pub fn fill_type_of_enum(cx: @CrateContext,
-                         did: ast::def_id,
-                         t: ty::t,
-                         llty: TypeRef) {
-    debug!("type_of_enum %?: %?", t, ty::get(t));
-    common::set_struct_body(llty, enum_body_types(cx, did, t));
-}
-
 // Want refinements! (Or case classes, I guess
 pub enum named_ty { a_struct, an_enum }
 
diff --git a/src/librustc/rustc.rc b/src/librustc/rustc.rc
index b204c458d65..355ecaed7d7 100644
--- a/src/librustc/rustc.rc
+++ b/src/librustc/rustc.rc
@@ -65,6 +65,7 @@ pub mod middle {
         pub mod type_use;
         pub mod reachable;
         pub mod machine;
+        pub mod adt;
     }
     pub mod ty;
     pub mod resolve;
diff --git a/src/test/run-pass/const-enum-structlike.rs b/src/test/run-pass/const-enum-structlike.rs
new file mode 100644
index 00000000000..83d81740759
--- /dev/null
+++ b/src/test/run-pass/const-enum-structlike.rs
@@ -0,0 +1,23 @@
+// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+enum E {
+    S0 { s: ~str },
+    S1 { u: uint }
+}
+
+const C: E = S1 { u: 23 };
+
+fn main() {
+    match C {
+        S0 { _ } => fail!(),
+        S1 { u } => assert u == 23
+    }
+}
diff --git a/src/test/run-pass/enum-discrim-range-overflow.rs b/src/test/run-pass/enum-discrim-range-overflow.rs
new file mode 100644
index 00000000000..a6806fba142
--- /dev/null
+++ b/src/test/run-pass/enum-discrim-range-overflow.rs
@@ -0,0 +1,31 @@
+// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+pub enum E64 {
+	H64 = 0x7FFF_FFFF_FFFF_FFFF,
+	L64 = 0x8000_0000_0000_0000
+}
+pub enum E32 {
+	H32 = 0x7FFF_FFFF,
+	L32 = 0x8000_0000
+}
+
+pub fn f(e64: E64, e32: E32) -> (bool,bool) {
+	(match e64 {
+		H64 => true,
+		L64 => false
+	},
+	 match e32 {
+		H32 => true,
+		L32 => false
+	})
+}
+
+pub fn main() { }
diff --git a/src/test/run-pass/struct-order-of-eval-1.rs b/src/test/run-pass/struct-order-of-eval-1.rs
new file mode 100644
index 00000000000..db7c73cbfc5
--- /dev/null
+++ b/src/test/run-pass/struct-order-of-eval-1.rs
@@ -0,0 +1,16 @@
+// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+struct S { f0: ~str, f1: int }
+
+pub fn main() {
+    let s = ~"Hello, world!";
+    let _s = S { f0: str::from_slice(s), ..S { f0: s, f1: 23 } };
+}
diff --git a/src/test/run-pass/struct-order-of-eval-2.rs b/src/test/run-pass/struct-order-of-eval-2.rs
new file mode 100644
index 00000000000..413f185659a
--- /dev/null
+++ b/src/test/run-pass/struct-order-of-eval-2.rs
@@ -0,0 +1,16 @@
+// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+struct S { f0: ~str, f1: ~str }
+
+pub fn main() {
+    let s = ~"Hello, world!";
+    let _s = S { f1: str::from_slice(s), f0: s };
+}