about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2015-08-07 12:23:06 +0000
committerbors <bors@rust-lang.org>2015-08-07 12:23:06 +0000
commitab77c1d8d017196a7bd8fac3574eb73dcd54f0a9 (patch)
treedd930ec76aee1e1857d6aebcb0b80ea2ce53aaff
parent9bba7110639cbd1d51977d97106d377fdfac7cdf (diff)
parenteedb1cc5765b043f31ca4316c42f2ac8d7df1919 (diff)
downloadrust-ab77c1d8d017196a7bd8fac3574eb73dcd54f0a9.tar.gz
rust-ab77c1d8d017196a7bd8fac3574eb73dcd54f0a9.zip
Auto merge of #27551 - arielb1:adt-def, r=nikomatsakis
This ended up being a bigger refactoring than I thought, as I also cleaned a few ugly points in rustc. There are still a few areas that need improvements.

Performance numbers:
```
Before:
572.70user 5.52system 7:33.21elapsed 127%CPU (0avgtext+0avgdata 1173368maxresident)k
llvm-time: 385.858

After:
545.27user 5.49system 7:10.22elapsed 128%CPU (0avgtext+0avgdata 1145348maxresident)k
llvm-time: 387.119
```

A good 5% perf improvement. Note that after this patch >70% of the time is spent in LLVM - Amdahl's law is in full effect.

Passes make check locally.

r? @nikomatsakis 
-rw-r--r--src/librustc/diagnostics.rs59
-rw-r--r--src/librustc/lib.rs3
-rw-r--r--src/librustc/metadata/csearch.rs19
-rw-r--r--src/librustc/metadata/decoder.rs201
-rw-r--r--src/librustc/metadata/encoder.rs93
-rw-r--r--src/librustc/metadata/tydecode.rs6
-rw-r--r--src/librustc/metadata/tyencode.rs4
-rw-r--r--src/librustc/middle/cast.rs5
-rw-r--r--src/librustc/middle/check_const.rs4
-rw-r--r--src/librustc/middle/check_match.rs100
-rw-r--r--src/librustc/middle/dead.rs43
-rw-r--r--src/librustc/middle/expr_use_visitor.rs51
-rw-r--r--src/librustc/middle/fast_reject.rs6
-rw-r--r--src/librustc/middle/implicator.rs8
-rw-r--r--src/librustc/middle/mem_categorization.rs2
-rw-r--r--src/librustc/middle/stability.rs54
-rw-r--r--src/librustc/middle/traits/coherence.rs10
-rw-r--r--src/librustc/middle/traits/select.rs51
-rw-r--r--src/librustc/middle/ty.rs1060
-rw-r--r--src/librustc/middle/ty_fold.rs9
-rw-r--r--src/librustc/middle/ty_relate/mod.rs16
-rw-r--r--src/librustc/util/ppaux.rs20
-rw-r--r--src/librustc_borrowck/borrowck/check_loans.rs34
-rw-r--r--src/librustc_borrowck/borrowck/fragments.rs38
-rw-r--r--src/librustc_borrowck/borrowck/gather_loans/gather_moves.rs4
-rw-r--r--src/librustc_borrowck/borrowck/gather_loans/move_error.rs4
-rw-r--r--src/librustc_data_structures/ivar.rs74
-rw-r--r--src/librustc_data_structures/lib.rs1
-rw-r--r--src/librustc_lint/builtin.rs77
-rw-r--r--src/librustc_privacy/lib.rs152
-rw-r--r--src/librustc_resolve/build_reduced_graph.rs4
-rw-r--r--src/librustc_trans/save/dump_csv.rs82
-rw-r--r--src/librustc_trans/save/mod.rs56
-rw-r--r--src/librustc_trans/trans/_match.rs41
-rw-r--r--src/librustc_trans/trans/adt.rs47
-rw-r--r--src/librustc_trans/trans/base.rs60
-rw-r--r--src/librustc_trans/trans/callee.rs6
-rw-r--r--src/librustc_trans/trans/common.rs95
-rw-r--r--src/librustc_trans/trans/consts.rs68
-rw-r--r--src/librustc_trans/trans/debuginfo/metadata.rs132
-rw-r--r--src/librustc_trans/trans/debuginfo/type_names.rs6
-rw-r--r--src/librustc_trans/trans/expr.rs203
-rw-r--r--src/librustc_trans/trans/foreign.rs2
-rw-r--r--src/librustc_trans/trans/glue.rs19
-rw-r--r--src/librustc_trans/trans/inline.rs46
-rw-r--r--src/librustc_trans/trans/monomorphize.rs31
-rw-r--r--src/librustc_trans/trans/type_of.rs14
-rw-r--r--src/librustc_typeck/check/_match.rs71
-rw-r--r--src/librustc_typeck/check/cast.rs11
-rw-r--r--src/librustc_typeck/check/dropck.rs189
-rw-r--r--src/librustc_typeck/check/method/probe.rs7
-rw-r--r--src/librustc_typeck/check/method/suggest.rs10
-rw-r--r--src/librustc_typeck/check/mod.rs281
-rw-r--r--src/librustc_typeck/check/op.rs25
-rw-r--r--src/librustc_typeck/check/wf.rs10
-rw-r--r--src/librustc_typeck/coherence/mod.rs26
-rw-r--r--src/librustc_typeck/coherence/orphan.rs10
-rw-r--r--src/librustc_typeck/collect.rs366
-rw-r--r--src/librustc_typeck/diagnostics.rs59
-rw-r--r--src/librustc_typeck/variance.rs50
-rw-r--r--src/librustdoc/clean/inline.rs10
-rw-r--r--src/librustdoc/clean/mod.rs49
-rw-r--r--src/test/run-pass/enum-null-pointer-opt.rs7
63 files changed, 2091 insertions, 2210 deletions
diff --git a/src/librustc/diagnostics.rs b/src/librustc/diagnostics.rs
index 9c2cdba0ae4..baa9750d311 100644
--- a/src/librustc/diagnostics.rs
+++ b/src/librustc/diagnostics.rs
@@ -692,64 +692,6 @@ There's no easy fix for this, generally code will need to be refactored so that
 you no longer need to derive from `Super<Self>`.
 "####,
 
-E0079: r##"
-Enum variants which contain no data can be given a custom integer
-representation. This error indicates that the value provided is not an integer
-literal and is therefore invalid.
-
-For example, in the following code,
-
-```
-enum Foo {
-    Q = "32"
-}
-```
-
-we try to set the representation to a string.
-
-There's no general fix for this; if you can work with an integer then just set
-it to one:
-
-```
-enum Foo {
-    Q = 32
-}
-```
-
-however if you actually wanted a mapping between variants and non-integer
-objects, it may be preferable to use a method with a match instead:
-
-```
-enum Foo { Q }
-impl Foo {
-    fn get_str(&self) -> &'static str {
-        match *self {
-            Foo::Q => "32",
-        }
-    }
-}
-```
-"##,
-
-E0080: r##"
-This error indicates that the compiler was unable to sensibly evaluate an
-integer expression provided as an enum discriminant. Attempting to divide by 0
-or causing integer overflow are two ways to induce this error. For example:
-
-```
-enum Enum {
-    X = (1 << 500),
-    Y = (1 / 0)
-}
-```
-
-Ensure that the expressions given can be evaluated as the desired integer type.
-See the FFI section of the Reference for more information about using a custom
-integer type:
-
-https://doc.rust-lang.org/reference.html#ffi-attributes
-"##,
-
 E0109: r##"
 You tried to give a type parameter to a type which doesn't need it. Erroneous
 code example:
@@ -1937,6 +1879,5 @@ register_diagnostics! {
     E0314, // closure outlives stack frame
     E0315, // cannot invoke closure outside of its lifetime
     E0316, // nested quantification of lifetimes
-    E0370, // discriminant overflow
     E0400  // overloaded derefs are not allowed in constants
 }
diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs
index c38345a79fd..1a15d98d531 100644
--- a/src/librustc/lib.rs
+++ b/src/librustc/lib.rs
@@ -32,6 +32,7 @@
 #![feature(clone_from_slice)]
 #![feature(collections)]
 #![feature(const_fn)]
+#![feature(core)]
 #![feature(duration)]
 #![feature(duration_span)]
 #![feature(dynamic_lib)]
@@ -42,6 +43,7 @@
 #![feature(iter_cmp)]
 #![feature(iter_arith)]
 #![feature(libc)]
+#![feature(nonzero)]
 #![feature(num_bits_bytes)]
 #![feature(path_ext)]
 #![feature(quote)]
@@ -65,6 +67,7 @@
 #![allow(trivial_casts)]
 
 extern crate arena;
+extern crate core;
 extern crate flate;
 extern crate fmt_macros;
 extern crate getopts;
diff --git a/src/librustc/metadata/csearch.rs b/src/librustc/metadata/csearch.rs
index 9c45ffe35a8..efa17912a32 100644
--- a/src/librustc/metadata/csearch.rs
+++ b/src/librustc/metadata/csearch.rs
@@ -112,13 +112,6 @@ pub fn maybe_get_item_ast<'tcx>(tcx: &ty::ctxt<'tcx>, def: ast::DefId,
     decoder::maybe_get_item_ast(&*cdata, tcx, def.node, decode_inlined_item)
 }
 
-pub fn get_enum_variants<'tcx>(tcx: &ty::ctxt<'tcx>, def: ast::DefId)
-                               -> Vec<Rc<ty::VariantInfo<'tcx>>> {
-    let cstore = &tcx.sess.cstore;
-    let cdata = cstore.get_crate_data(def.krate);
-    decoder::get_enum_variants(cstore.intr.clone(), &*cdata, def.node, tcx)
-}
-
 /// Returns information about the given implementation.
 pub fn get_impl_items(cstore: &cstore::CStore, impl_def_id: ast::DefId)
                       -> Vec<ty::ImplOrTraitItemId> {
@@ -194,11 +187,9 @@ pub fn get_item_attrs(cstore: &cstore::CStore,
     decoder::get_item_attrs(&*cdata, def_id.node)
 }
 
-pub fn get_struct_fields(cstore: &cstore::CStore,
-                         def: ast::DefId)
-                      -> Vec<ty::FieldTy> {
+pub fn get_struct_field_names(cstore: &cstore::CStore, def: ast::DefId) -> Vec<ast::Name> {
     let cdata = cstore.get_crate_data(def.krate);
-    decoder::get_struct_fields(cstore.intr.clone(), &*cdata, def.node)
+    decoder::get_struct_field_names(&cstore.intr, &*cdata, def.node)
 }
 
 pub fn get_struct_field_attrs(cstore: &cstore::CStore, def: ast::DefId) -> HashMap<ast::NodeId,
@@ -221,6 +212,12 @@ pub fn get_trait_def<'tcx>(tcx: &ty::ctxt<'tcx>, def: ast::DefId) -> ty::TraitDe
     decoder::get_trait_def(&*cdata, def.node, tcx)
 }
 
+pub fn get_adt_def<'tcx>(tcx: &ty::ctxt<'tcx>, def: ast::DefId) -> ty::AdtDefMaster<'tcx> {
+    let cstore = &tcx.sess.cstore;
+    let cdata = cstore.get_crate_data(def.krate);
+    decoder::get_adt_def(&cstore.intr, &*cdata, def.node, tcx)
+}
+
 pub fn get_predicates<'tcx>(tcx: &ty::ctxt<'tcx>, def: ast::DefId)
                             -> ty::GenericPredicates<'tcx>
 {
diff --git a/src/librustc/metadata/decoder.rs b/src/librustc/metadata/decoder.rs
index b3ddd3869cb..df5f798217f 100644
--- a/src/librustc/metadata/decoder.rs
+++ b/src/librustc/metadata/decoder.rs
@@ -30,7 +30,7 @@ use middle::def;
 use middle::lang_items;
 use middle::subst;
 use middle::ty::{ImplContainer, TraitContainer};
-use middle::ty::{self, Ty};
+use middle::ty::{self, RegionEscape, Ty};
 use util::nodemap::FnvHashMap;
 
 use std::cell::{Cell, RefCell};
@@ -108,7 +108,7 @@ fn lookup_item<'a>(item_id: ast::NodeId, data: &'a [u8]) -> rbml::Doc<'a> {
     find_item(item_id, items)
 }
 
-#[derive(PartialEq)]
+#[derive(Debug, PartialEq)]
 enum Family {
     ImmStatic,             // c
     MutStatic,             // b
@@ -390,6 +390,119 @@ pub fn get_trait_def<'tcx>(cdata: Cmd,
     }
 }
 
+pub fn get_adt_def<'tcx>(intr: &IdentInterner,
+                         cdata: Cmd,
+                         item_id: ast::NodeId,
+                         tcx: &ty::ctxt<'tcx>) -> ty::AdtDefMaster<'tcx>
+{
+    fn get_enum_variants<'tcx>(intr: &IdentInterner,
+                               cdata: Cmd,
+                               doc: rbml::Doc,
+                               tcx: &ty::ctxt<'tcx>) -> Vec<ty::VariantDefData<'tcx, 'tcx>> {
+        let mut disr_val = 0;
+        reader::tagged_docs(doc, tag_items_data_item_variant).map(|p| {
+            let did = translated_def_id(cdata, p);
+            let item = lookup_item(did.node, cdata.data());
+
+            if let Some(disr) = variant_disr_val(item) {
+                disr_val = disr;
+            }
+            let disr = disr_val;
+            disr_val = disr_val.wrapping_add(1);
+
+            ty::VariantDefData {
+                did: did,
+                name: item_name(intr, item),
+                fields: get_variant_fields(intr, cdata, item, tcx),
+                disr_val: disr
+            }
+        }).collect()
+    }
+    fn get_variant_fields<'tcx>(intr: &IdentInterner,
+                                cdata: Cmd,
+                                doc: rbml::Doc,
+                                tcx: &ty::ctxt<'tcx>) -> Vec<ty::FieldDefData<'tcx, 'tcx>> {
+        reader::tagged_docs(doc, tag_item_field).map(|f| {
+            let ff = item_family(f);
+            match ff {
+                PublicField | InheritedField => {},
+                _ => tcx.sess.bug(&format!("expected field, found {:?}", ff))
+            };
+            ty::FieldDefData::new(item_def_id(f, cdata),
+                                  item_name(intr, f),
+                                  struct_field_family_to_visibility(ff))
+        }).chain(reader::tagged_docs(doc, tag_item_unnamed_field).map(|f| {
+            let ff = item_family(f);
+            ty::FieldDefData::new(item_def_id(f, cdata),
+                                  special_idents::unnamed_field.name,
+                                  struct_field_family_to_visibility(ff))
+        })).collect()
+    }
+    fn get_struct_variant<'tcx>(intr: &IdentInterner,
+                                cdata: Cmd,
+                                doc: rbml::Doc,
+                                did: ast::DefId,
+                                tcx: &ty::ctxt<'tcx>) -> ty::VariantDefData<'tcx, 'tcx> {
+        ty::VariantDefData {
+            did: did,
+            name: item_name(intr, doc),
+            fields: get_variant_fields(intr, cdata, doc, tcx),
+            disr_val: 0
+        }
+    }
+
+    let doc = lookup_item(item_id, cdata.data());
+    let did = ast::DefId { krate: cdata.cnum, node: item_id };
+    let (kind, variants) = match item_family(doc) {
+        Enum => (ty::AdtKind::Enum,
+                 get_enum_variants(intr, cdata, doc, tcx)),
+        Struct => (ty::AdtKind::Struct,
+                   vec![get_struct_variant(intr, cdata, doc, did, tcx)]),
+        _ => tcx.sess.bug("get_adt_def called on a non-ADT")
+    };
+
+    let adt = tcx.intern_adt_def(did, kind, variants);
+
+    // this needs to be done *after* the variant is interned,
+    // to support recursive structures
+    for variant in &adt.variants {
+        if variant.kind() == ty::VariantKind::Tuple &&
+            adt.adt_kind() == ty::AdtKind::Enum {
+            // tuple-like enum variant fields aren't real items - get the types
+            // from the ctor.
+            debug!("evaluating the ctor-type of {:?}",
+                   variant.name);
+            let ctor_ty = get_type(cdata, variant.did.node, tcx).ty;
+            debug!("evaluating the ctor-type of {:?}.. {:?}",
+                   variant.name,
+                   ctor_ty);
+            let field_tys = match ctor_ty.sty {
+                ty::TyBareFn(_, &ty::BareFnTy { sig: ty::Binder(ty::FnSig {
+                    ref inputs, ..
+                }), ..}) => {
+                    // tuple-struct constructors don't have escaping regions
+                    assert!(!inputs.has_escaping_regions());
+                    inputs
+                },
+                _ => tcx.sess.bug("tuple-variant ctor is not an ADT")
+            };
+            for (field, &ty) in variant.fields.iter().zip(field_tys.iter()) {
+                field.fulfill_ty(ty);
+            }
+        } else {
+            for field in &variant.fields {
+                debug!("evaluating the type of {:?}::{:?}", variant.name, field.name);
+                let ty = get_type(cdata, field.did.node, tcx).ty;
+                field.fulfill_ty(ty);
+                debug!("evaluating the type of {:?}::{:?}: {:?}",
+                       variant.name, field.name, ty);
+            }
+        }
+    }
+
+    adt
+}
+
 pub fn get_predicates<'tcx>(cdata: Cmd,
                             item_id: ast::NodeId,
                             tcx: &ty::ctxt<'tcx>)
@@ -687,55 +800,6 @@ pub fn maybe_get_item_ast<'tcx>(cdata: Cmd, tcx: &ty::ctxt<'tcx>, id: ast::NodeI
     }
 }
 
-pub fn get_enum_variants<'tcx>(intr: Rc<IdentInterner>, cdata: Cmd, id: ast::NodeId,
-                               tcx: &ty::ctxt<'tcx>) -> Vec<Rc<ty::VariantInfo<'tcx>>> {
-    let data = cdata.data();
-    let items = reader::get_doc(rbml::Doc::new(data), tag_items);
-    let item = find_item(id, items);
-    let mut disr_val = 0;
-    reader::tagged_docs(item, tag_items_data_item_variant).map(|p| {
-        let did = translated_def_id(cdata, p);
-        let item = find_item(did.node, items);
-        let ctor_ty = item_type(ast::DefId { krate: cdata.cnum, node: id},
-                                item, tcx, cdata);
-        let name = item_name(&*intr, item);
-        let (ctor_ty, arg_tys, arg_names) = match ctor_ty.sty {
-            ty::TyBareFn(_, ref f) =>
-                (Some(ctor_ty), f.sig.0.inputs.clone(), None),
-            _ => { // Nullary or struct enum variant.
-                let mut arg_names = Vec::new();
-                let arg_tys = get_struct_fields(intr.clone(), cdata, did.node)
-                    .iter()
-                    .map(|field_ty| {
-                        arg_names.push(field_ty.name);
-                        get_type(cdata, field_ty.id.node, tcx).ty
-                    })
-                    .collect();
-                let arg_names = if arg_names.is_empty() { None } else { Some(arg_names) };
-
-                (None, arg_tys, arg_names)
-            }
-        };
-        match variant_disr_val(item) {
-            Some(val) => { disr_val = val; }
-            _         => { /* empty */ }
-        }
-        let old_disr_val = disr_val;
-        disr_val = disr_val.wrapping_add(1);
-        Rc::new(ty::VariantInfo {
-            args: arg_tys,
-            arg_names: arg_names,
-            ctor_ty: ctor_ty,
-            name: name,
-            // I'm not even sure if we encode visibility
-            // for variants -- TEST -- tjc
-            id: did,
-            disr_val: old_disr_val,
-            vis: ast::Inherited
-        })
-    }).collect()
-}
-
 fn get_explicit_self(item: rbml::Doc) -> ty::ExplicitSelfCategory {
     fn get_mutability(ch: u8) -> ast::Mutability {
         match ch as char {
@@ -1029,37 +1093,14 @@ fn struct_field_family_to_visibility(family: Family) -> ast::Visibility {
     }
 }
 
-pub fn get_struct_fields(intr: Rc<IdentInterner>, cdata: Cmd, id: ast::NodeId)
-    -> Vec<ty::FieldTy> {
+pub fn get_struct_field_names(intr: &IdentInterner, cdata: Cmd, id: ast::NodeId)
+    -> Vec<ast::Name> {
     let data = cdata.data();
     let item = lookup_item(id, data);
-    reader::tagged_docs(item, tag_item_field).filter_map(|an_item| {
-        let f = item_family(an_item);
-        if f == PublicField || f == InheritedField {
-            let name = item_name(&*intr, an_item);
-            let did = item_def_id(an_item, cdata);
-            let tagdoc = reader::get_doc(an_item, tag_item_field_origin);
-            let origin_id =  translated_def_id(cdata, tagdoc);
-            Some(ty::FieldTy {
-                name: name,
-                id: did,
-                vis: struct_field_family_to_visibility(f),
-                origin: origin_id,
-            })
-        } else {
-            None
-        }
-    }).chain(reader::tagged_docs(item, tag_item_unnamed_field).map(|an_item| {
-        let did = item_def_id(an_item, cdata);
-        let tagdoc = reader::get_doc(an_item, tag_item_field_origin);
-        let f = item_family(an_item);
-        let origin_id =  translated_def_id(cdata, tagdoc);
-        ty::FieldTy {
-            name: special_idents::unnamed_field.name,
-            id: did,
-            vis: struct_field_family_to_visibility(f),
-            origin: origin_id,
-        }
+    reader::tagged_docs(item, tag_item_field).map(|an_item| {
+        item_name(intr, an_item)
+    }).chain(reader::tagged_docs(item, tag_item_unnamed_field).map(|_| {
+        special_idents::unnamed_field.name
     })).collect()
 }
 
diff --git a/src/librustc/metadata/encoder.rs b/src/librustc/metadata/encoder.rs
index 5167014d4ab..d5c189ff044 100644
--- a/src/librustc/metadata/encoder.rs
+++ b/src/librustc/metadata/encoder.rs
@@ -41,7 +41,6 @@ use syntax::attr::AttrMetaMethods;
 use syntax::diagnostic::SpanHandler;
 use syntax::parse::token::special_idents;
 use syntax::print::pprust;
-use syntax::ptr::P;
 use syntax::visit::Visitor;
 use syntax::visit;
 use syntax;
@@ -266,9 +265,9 @@ fn encode_parent_item(rbml_w: &mut Encoder, id: DefId) {
 }
 
 fn encode_struct_fields(rbml_w: &mut Encoder,
-                        fields: &[ty::FieldTy],
+                        variant: ty::VariantDef,
                         origin: DefId) {
-    for f in fields {
+    for f in &variant.fields {
         if f.name == special_idents::unnamed_field.name {
             rbml_w.start_tag(tag_item_unnamed_field);
         } else {
@@ -276,7 +275,7 @@ fn encode_struct_fields(rbml_w: &mut Encoder,
             encode_name(rbml_w, f.name);
         }
         encode_struct_field_family(rbml_w, f.vis);
-        encode_def_id(rbml_w, f.id);
+        encode_def_id(rbml_w, f.did);
         rbml_w.wr_tagged_u64(tag_item_field_origin, def_to_u64(origin));
         rbml_w.end_tag();
     }
@@ -285,57 +284,53 @@ fn encode_struct_fields(rbml_w: &mut Encoder,
 fn encode_enum_variant_info(ecx: &EncodeContext,
                             rbml_w: &mut Encoder,
                             id: NodeId,
-                            variants: &[P<ast::Variant>],
+                            vis: ast::Visibility,
                             index: &mut Vec<entry<i64>>) {
     debug!("encode_enum_variant_info(id={})", id);
 
     let mut disr_val = 0;
-    let mut i = 0;
-    let vi = ecx.tcx.enum_variants(local_def(id));
-    for variant in variants {
-        let def_id = local_def(variant.node.id);
+    let def = ecx.tcx.lookup_adt_def(local_def(id));
+    for variant in &def.variants {
+        let vid = variant.did;
+        assert!(is_local(vid));
         index.push(entry {
-            val: variant.node.id as i64,
+            val: vid.node as i64,
             pos: rbml_w.mark_stable_position(),
         });
         rbml_w.start_tag(tag_items_data_item);
-        encode_def_id(rbml_w, def_id);
-        match variant.node.kind {
-            ast::TupleVariantKind(_) => encode_family(rbml_w, 'v'),
-            ast::StructVariantKind(_) => encode_family(rbml_w, 'V')
-        }
-        encode_name(rbml_w, variant.node.name.name);
+        encode_def_id(rbml_w, vid);
+        encode_family(rbml_w, match variant.kind() {
+            ty::VariantKind::Unit | ty::VariantKind::Tuple => 'v',
+            ty::VariantKind::Dict => 'V'
+        });
+        encode_name(rbml_w, variant.name);
         encode_parent_item(rbml_w, local_def(id));
-        encode_visibility(rbml_w, variant.node.vis);
-        encode_attributes(rbml_w, &variant.node.attrs);
-        encode_repr_attrs(rbml_w, ecx, &variant.node.attrs);
+        encode_visibility(rbml_w, vis);
+
+        let attrs = ecx.tcx.get_attrs(vid);
+        encode_attributes(rbml_w, &attrs);
+        encode_repr_attrs(rbml_w, ecx, &attrs);
 
-        let stab = stability::lookup(ecx.tcx, ast_util::local_def(variant.node.id));
+        let stab = stability::lookup(ecx.tcx, vid);
         encode_stability(rbml_w, stab);
 
-        match variant.node.kind {
-            ast::TupleVariantKind(_) => {},
-            ast::StructVariantKind(_) => {
-                let fields = ecx.tcx.lookup_struct_fields(def_id);
-                let idx = encode_info_for_struct(ecx,
-                                                 rbml_w,
-                                                 &fields[..],
-                                                 index);
-                encode_struct_fields(rbml_w, &fields[..], def_id);
-                encode_index(rbml_w, idx, write_i64);
-            }
+        if let ty::VariantKind::Dict = variant.kind() {
+            let idx = encode_info_for_struct(ecx, rbml_w, variant, index);
+            encode_index(rbml_w, idx, write_i64);
         }
-        let specified_disr_val = vi[i].disr_val;
+
+        encode_struct_fields(rbml_w, variant, vid);
+
+        let specified_disr_val = variant.disr_val;
         if specified_disr_val != disr_val {
             encode_disr_val(ecx, rbml_w, specified_disr_val);
             disr_val = specified_disr_val;
         }
-        encode_bounds_and_type_for_item(rbml_w, ecx, def_id.local_id());
+        encode_bounds_and_type_for_item(rbml_w, ecx, vid.node);
 
-        ecx.tcx.map.with_path(variant.node.id, |path| encode_path(rbml_w, path));
+        ecx.tcx.map.with_path(vid.node, |path| encode_path(rbml_w, path));
         rbml_w.end_tag();
         disr_val = disr_val.wrapping_add(1);
-        i += 1;
     }
 }
 
@@ -630,19 +625,19 @@ fn encode_provided_source(rbml_w: &mut Encoder,
 }
 
 /* Returns an index of items in this class */
-fn encode_info_for_struct(ecx: &EncodeContext,
-                          rbml_w: &mut Encoder,
-                          fields: &[ty::FieldTy],
-                          global_index: &mut Vec<entry<i64>>)
-                          -> Vec<entry<i64>> {
+fn encode_info_for_struct<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>,
+                                    rbml_w: &mut Encoder,
+                                    variant: ty::VariantDef<'tcx>,
+                                    global_index: &mut Vec<entry<i64>>)
+                                    -> Vec<entry<i64>> {
     /* Each class has its own index, since different classes
        may have fields with the same name */
     let mut index = Vec::new();
      /* We encode both private and public fields -- need to include
         private fields to get the offsets right */
-    for field in fields {
+    for field in &variant.fields {
         let nm = field.name;
-        let id = field.id.node;
+        let id = field.did.node;
 
         let pos = rbml_w.mark_stable_position();
         index.push(entry {val: id as i64, pos: pos});
@@ -658,7 +653,7 @@ fn encode_info_for_struct(ecx: &EncodeContext,
         encode_bounds_and_type_for_item(rbml_w, ecx, id);
         encode_def_id(rbml_w, local_def(id));
 
-        let stab = stability::lookup(ecx.tcx, field.id);
+        let stab = stability::lookup(ecx.tcx, field.did);
         encode_stability(rbml_w, stab);
 
         rbml_w.end_tag();
@@ -1150,20 +1145,18 @@ fn encode_info_for_item(ecx: &EncodeContext,
         encode_enum_variant_info(ecx,
                                  rbml_w,
                                  item.id,
-                                 &(*enum_definition).variants,
+                                 vis,
                                  index);
       }
       ast::ItemStruct(ref struct_def, _) => {
-        let fields = tcx.lookup_struct_fields(def_id);
+        let def = ecx.tcx.lookup_adt_def(def_id);
+        let variant = def.struct_variant();
 
         /* First, encode the fields
            These come first because we need to write them to make
            the index, and the index needs to be in the item for the
            class itself */
-        let idx = encode_info_for_struct(ecx,
-                                         rbml_w,
-                                         &fields[..],
-                                         index);
+        let idx = encode_info_for_struct(ecx, rbml_w, variant, index);
 
         /* Index the class*/
         add_to_index(item, rbml_w, index);
@@ -1185,7 +1178,7 @@ fn encode_info_for_item(ecx: &EncodeContext,
         /* Encode def_ids for each field and method
          for methods, write all the stuff get_trait_method
         needs to know*/
-        encode_struct_fields(rbml_w, &fields[..], def_id);
+        encode_struct_fields(rbml_w, variant, def_id);
 
         encode_inlined_item(ecx, rbml_w, IIItemRef(item));
 
diff --git a/src/librustc/metadata/tydecode.rs b/src/librustc/metadata/tydecode.rs
index 54c55d76a82..679213874f9 100644
--- a/src/librustc/metadata/tydecode.rs
+++ b/src/librustc/metadata/tydecode.rs
@@ -468,9 +468,10 @@ fn parse_ty_<'a, 'tcx, F>(st: &mut PState<'a, 'tcx>, conv: &mut F) -> Ty<'tcx> w
       'c' => return tcx.types.char,
       't' => {
         assert_eq!(next(st), '[');
-        let def = parse_def_(st, NominalType, conv);
+        let did = parse_def_(st, NominalType, conv);
         let substs = parse_substs_(st, conv);
         assert_eq!(next(st), ']');
+        let def = st.tcx.lookup_adt_def(did);
         return tcx.mk_enum(def, st.tcx.mk_substs(substs));
       }
       'x' => {
@@ -558,7 +559,8 @@ fn parse_ty_<'a, 'tcx, F>(st: &mut PState<'a, 'tcx>, conv: &mut F) -> Ty<'tcx> w
           let did = parse_def_(st, NominalType, conv);
           let substs = parse_substs_(st, conv);
           assert_eq!(next(st), ']');
-          return st.tcx.mk_struct(did, st.tcx.mk_substs(substs));
+          let def = st.tcx.lookup_adt_def(did);
+          return st.tcx.mk_struct(def, st.tcx.mk_substs(substs));
       }
       'k' => {
           assert_eq!(next(st), '[');
diff --git a/src/librustc/metadata/tyencode.rs b/src/librustc/metadata/tyencode.rs
index 1831c3518d7..88666be6c2c 100644
--- a/src/librustc/metadata/tyencode.rs
+++ b/src/librustc/metadata/tyencode.rs
@@ -86,7 +86,7 @@ pub fn enc_ty<'a, 'tcx>(w: &mut Encoder, cx: &ctxt<'a, 'tcx>, t: Ty<'tcx>) {
             }
         }
         ty::TyEnum(def, substs) => {
-            mywrite!(w, "t[{}|", (cx.ds)(def));
+            mywrite!(w, "t[{}|", (cx.ds)(def.did));
             enc_substs(w, cx, substs);
             mywrite!(w, "]");
         }
@@ -138,7 +138,7 @@ pub fn enc_ty<'a, 'tcx>(w: &mut Encoder, cx: &ctxt<'a, 'tcx>, t: Ty<'tcx>) {
             mywrite!(w, "p[{}|{}|{}]", idx, space.to_uint(), name)
         }
         ty::TyStruct(def, substs) => {
-            mywrite!(w, "a[{}|", (cx.ds)(def));
+            mywrite!(w, "a[{}|", (cx.ds)(def.did));
             enc_substs(w, cx, substs);
             mywrite!(w, "]");
         }
diff --git a/src/librustc/middle/cast.rs b/src/librustc/middle/cast.rs
index bbd452b35ac..8233b6b2b2b 100644
--- a/src/librustc/middle/cast.rs
+++ b/src/librustc/middle/cast.rs
@@ -58,15 +58,14 @@ pub enum CastKind {
 }
 
 impl<'tcx> CastTy<'tcx> {
-    pub fn from_ty(tcx: &ty::ctxt<'tcx>, t: Ty<'tcx>)
-                   -> Option<CastTy<'tcx>> {
+    pub fn from_ty(t: Ty<'tcx>) -> Option<CastTy<'tcx>> {
         match t.sty {
             ty::TyBool => Some(CastTy::Int(IntTy::Bool)),
             ty::TyChar => Some(CastTy::Int(IntTy::Char)),
             ty::TyInt(_) => Some(CastTy::Int(IntTy::I)),
             ty::TyUint(u) => Some(CastTy::Int(IntTy::U(u))),
             ty::TyFloat(_) => Some(CastTy::Float),
-            ty::TyEnum(..) if t.is_c_like_enum(tcx) =>
+            ty::TyEnum(d,_) if d.is_payloadfree() =>
                 Some(CastTy::Int(IntTy::CEnum)),
             ty::TyRawPtr(ref mt) => Some(CastTy::Ptr(mt)),
             ty::TyRef(_, ref mt) => Some(CastTy::RPtr(mt)),
diff --git a/src/librustc/middle/check_const.rs b/src/librustc/middle/check_const.rs
index 9667312b370..4ee8f403e42 100644
--- a/src/librustc/middle/check_const.rs
+++ b/src/librustc/middle/check_const.rs
@@ -546,8 +546,8 @@ impl<'a, 'tcx, 'v> Visitor<'v> for CheckCrateVisitor<'a, 'tcx> {
 fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>,
                         e: &ast::Expr, node_ty: Ty<'tcx>) {
     match node_ty.sty {
-        ty::TyStruct(did, _) |
-        ty::TyEnum(did, _) if v.tcx.has_dtor(did) => {
+        ty::TyStruct(def, _) |
+        ty::TyEnum(def, _) if def.has_dtor(v.tcx) => {
             v.add_qualif(ConstQualif::NEEDS_DROP);
             if v.mode != Mode::Var {
                 v.tcx.sess.span_err(e.span,
diff --git a/src/librustc/middle/check_match.rs b/src/librustc/middle/check_match.rs
index f17cb673a5f..b9d8e4b842d 100644
--- a/src/librustc/middle/check_match.rs
+++ b/src/librustc/middle/check_match.rs
@@ -234,12 +234,12 @@ fn check_for_bindings_named_the_same_as_variants(cx: &MatchCheckCtxt, pat: &Pat)
         match p.node {
             ast::PatIdent(ast::BindByValue(ast::MutImmutable), ident, None) => {
                 let pat_ty = cx.tcx.pat_ty(p);
-                if let ty::TyEnum(def_id, _) = pat_ty.sty {
+                if let ty::TyEnum(edef, _) = pat_ty.sty {
                     let def = cx.tcx.def_map.borrow().get(&p.id).map(|d| d.full_def());
                     if let Some(DefLocal(_)) = def {
-                        if cx.tcx.enum_variants(def_id).iter().any(|variant|
+                        if edef.variants.iter().any(|variant|
                             variant.name == ident.node.name
-                                && variant.args.is_empty()
+                                && variant.kind() == VariantKind::Unit
                         ) {
                             span_warn!(cx.tcx.sess, p.span, E0170,
                                 "pattern binding `{}` is named the same as one \
@@ -501,23 +501,17 @@ impl<'a, 'tcx> Folder for StaticInliner<'a, 'tcx> {
 ///
 /// left_ty: struct X { a: (bool, &'static str), b: usize}
 /// pats: [(false, "foo"), 42]  => X { a: (false, "foo"), b: 42 }
-fn construct_witness(cx: &MatchCheckCtxt, ctor: &Constructor,
-                     pats: Vec<&Pat>, left_ty: Ty) -> P<Pat> {
+fn construct_witness<'a,'tcx>(cx: &MatchCheckCtxt<'a,'tcx>, ctor: &Constructor,
+                              pats: Vec<&Pat>, left_ty: Ty<'tcx>) -> P<Pat> {
     let pats_len = pats.len();
     let mut pats = pats.into_iter().map(|p| P((*p).clone()));
     let pat = match left_ty.sty {
         ty::TyTuple(_) => ast::PatTup(pats.collect()),
 
-        ty::TyEnum(cid, _) | ty::TyStruct(cid, _)  => {
-            let (vid, is_structure) = match ctor {
-                &Variant(vid) =>
-                    (vid, cx.tcx.enum_variant_with_id(cid, vid).arg_names.is_some()),
-                _ =>
-                    (cid, !cx.tcx.is_tuple_struct(cid))
-            };
-            if is_structure {
-                let fields = cx.tcx.lookup_struct_fields(vid);
-                let field_pats: Vec<_> = fields.into_iter()
+        ty::TyEnum(adt, _) | ty::TyStruct(adt, _)  => {
+            let v = adt.variant_of_ctor(ctor);
+            if let VariantKind::Dict = v.kind() {
+                let field_pats: Vec<_> = v.fields.iter()
                     .zip(pats)
                     .filter(|&(_, ref pat)| pat.node != ast::PatWild(ast::PatWildSingle))
                     .map(|(field, pat)| Spanned {
@@ -529,9 +523,9 @@ fn construct_witness(cx: &MatchCheckCtxt, ctor: &Constructor,
                         }
                     }).collect();
                 let has_more_fields = field_pats.len() < pats_len;
-                ast::PatStruct(def_to_path(cx.tcx, vid), field_pats, has_more_fields)
+                ast::PatStruct(def_to_path(cx.tcx, v.did), field_pats, has_more_fields)
             } else {
-                ast::PatEnum(def_to_path(cx.tcx, vid), Some(pats.collect()))
+                ast::PatEnum(def_to_path(cx.tcx, v.did), Some(pats.collect()))
             }
         }
 
@@ -580,6 +574,17 @@ fn construct_witness(cx: &MatchCheckCtxt, ctor: &Constructor,
     })
 }
 
+impl<'tcx, 'container> ty::AdtDefData<'tcx, 'container> {
+    fn variant_of_ctor(&self,
+                       ctor: &Constructor)
+                       -> &VariantDefData<'tcx, 'container> {
+        match ctor {
+            &Variant(vid) => self.variant_with_id(vid),
+            _ => self.struct_variant()
+        }
+    }
+}
+
 fn missing_constructor(cx: &MatchCheckCtxt, &Matrix(ref rows): &Matrix,
                        left_ty: Ty, max_slice_length: usize) -> Option<Constructor> {
     let used_constructors: Vec<Constructor> = rows.iter()
@@ -594,7 +599,7 @@ fn missing_constructor(cx: &MatchCheckCtxt, &Matrix(ref rows): &Matrix,
 /// values of type `left_ty`. For vectors, this would normally be an infinite set
 /// but is instead bounded by the maximum fixed length of slice patterns in
 /// the column of patterns being analyzed.
-fn all_constructors(cx: &MatchCheckCtxt, left_ty: Ty,
+fn all_constructors(_cx: &MatchCheckCtxt, left_ty: Ty,
                     max_slice_length: usize) -> Vec<Constructor> {
     match left_ty.sty {
         ty::TyBool =>
@@ -603,17 +608,11 @@ fn all_constructors(cx: &MatchCheckCtxt, left_ty: Ty,
         ty::TyRef(_, ty::TypeAndMut { ty, .. }) => match ty.sty {
             ty::TySlice(_) =>
                 range_inclusive(0, max_slice_length).map(|length| Slice(length)).collect(),
-            _ => vec!(Single)
+            _ => vec![Single]
         },
 
-        ty::TyEnum(eid, _) =>
-            cx.tcx.enum_variants(eid)
-                .iter()
-                .map(|va| Variant(va.id))
-                .collect(),
-
-        _ =>
-            vec!(Single)
+        ty::TyEnum(def, _) => def.variants.iter().map(|v| Variant(v.did)).collect(),
+        _ => vec![Single]
     }
 }
 
@@ -804,7 +803,7 @@ fn pat_constructors(cx: &MatchCheckCtxt, p: &Pat,
 ///
 /// For instance, a tuple pattern (_, 42, Some([])) has the arity of 3.
 /// A struct pattern's arity is the number of fields it contains, etc.
-pub fn constructor_arity(cx: &MatchCheckCtxt, ctor: &Constructor, ty: Ty) -> usize {
+pub fn constructor_arity(_cx: &MatchCheckCtxt, ctor: &Constructor, ty: Ty) -> usize {
     match ty.sty {
         ty::TyTuple(ref fs) => fs.len(),
         ty::TyBox(_) => 1,
@@ -817,13 +816,9 @@ pub fn constructor_arity(cx: &MatchCheckCtxt, ctor: &Constructor, ty: Ty) -> usi
             ty::TyStr => 0,
             _ => 1
         },
-        ty::TyEnum(eid, _) => {
-            match *ctor {
-                Variant(id) => cx.tcx.enum_variant_with_id(eid, id).args.len(),
-                _ => unreachable!()
-            }
+        ty::TyEnum(adt, _) | ty::TyStruct(adt, _) => {
+            adt.variant_of_ctor(ctor).fields.len()
         }
-        ty::TyStruct(cid, _) => cx.tcx.lookup_struct_fields(cid).len(),
         ty::TyArray(_, n) => n,
         _ => 0
     }
@@ -902,39 +897,20 @@ pub fn specialize<'a>(cx: &MatchCheckCtxt, r: &[&'a Pat],
         }
 
         ast::PatStruct(_, ref pattern_fields, _) => {
-            // Is this a struct or an enum variant?
             let def = cx.tcx.def_map.borrow().get(&pat_id).unwrap().full_def();
-            let class_id = match def {
-                DefConst(..) | DefAssociatedConst(..) =>
-                    cx.tcx.sess.span_bug(pat_span, "const pattern should've \
-                                                    been rewritten"),
-                DefVariant(_, variant_id, _) => if *constructor == Variant(variant_id) {
-                    Some(variant_id)
-                } else {
-                    None
-                },
-                _ => {
-                    // Assume this is a struct.
-                    match cx.tcx.node_id_to_type(pat_id).ty_to_def_id() {
-                        None => {
-                            cx.tcx.sess.span_bug(pat_span,
-                                                 "struct pattern wasn't of a \
-                                                  type with a def ID?!")
-                        }
-                        Some(def_id) => Some(def_id),
-                    }
-                }
-            };
-            class_id.map(|variant_id| {
-                let struct_fields = cx.tcx.lookup_struct_fields(variant_id);
-                let args = struct_fields.iter().map(|sf| {
+            let adt = cx.tcx.node_id_to_type(pat_id).ty_adt_def().unwrap();
+            let variant = adt.variant_of_ctor(constructor);
+            let def_variant = adt.variant_of_def(def);
+            if variant.did == def_variant.did {
+                Some(variant.fields.iter().map(|sf| {
                     match pattern_fields.iter().find(|f| f.node.ident.name == sf.name) {
                         Some(ref f) => &*f.node.pat,
                         _ => DUMMY_WILD_PAT
                     }
-                }).collect();
-                args
-            })
+                }).collect())
+            } else {
+                None
+            }
         }
 
         ast::PatTup(ref args) =>
diff --git a/src/librustc/middle/dead.rs b/src/librustc/middle/dead.rs
index 8d2d6889b5e..bd8a666ffec 100644
--- a/src/librustc/middle/dead.rs
+++ b/src/librustc/middle/dead.rs
@@ -100,51 +100,32 @@ impl<'a, 'tcx> MarkSymbolVisitor<'a, 'tcx> {
     }
 
     fn handle_field_access(&mut self, lhs: &ast::Expr, name: ast::Name) {
-        match self.tcx.expr_ty_adjusted(lhs).sty {
-            ty::TyStruct(id, _) => {
-                let fields = self.tcx.lookup_struct_fields(id);
-                let field_id = fields.iter()
-                    .find(|field| field.name == name).unwrap().id;
-                self.live_symbols.insert(field_id.node);
-            },
-            _ => ()
+        if let ty::TyStruct(def, _) = self.tcx.expr_ty_adjusted(lhs).sty {
+            self.live_symbols.insert(def.struct_variant().field_named(name).did.node);
+        } else {
+            self.tcx.sess.span_bug(lhs.span, "named field access on non-struct")
         }
     }
 
     fn handle_tup_field_access(&mut self, lhs: &ast::Expr, idx: usize) {
-        match self.tcx.expr_ty_adjusted(lhs).sty {
-            ty::TyStruct(id, _) => {
-                let fields = self.tcx.lookup_struct_fields(id);
-                let field_id = fields[idx].id;
-                self.live_symbols.insert(field_id.node);
-            },
-            _ => ()
+        if let ty::TyStruct(def, _) = self.tcx.expr_ty_adjusted(lhs).sty {
+            self.live_symbols.insert(def.struct_variant().fields[idx].did.node);
         }
     }
 
     fn handle_field_pattern_match(&mut self, lhs: &ast::Pat,
                                   pats: &[codemap::Spanned<ast::FieldPat>]) {
-        let id = match self.tcx.def_map.borrow().get(&lhs.id).unwrap().full_def() {
-            def::DefVariant(_, id, _) => id,
-            _ => {
-                match self.tcx.node_id_to_type(lhs.id).ty_to_def_id() {
-                    None => {
-                        self.tcx.sess.span_bug(lhs.span,
-                                               "struct pattern wasn't of a \
-                                                type with a def ID?!")
-                    }
-                    Some(def_id) => def_id,
-                }
-            }
+        let def = self.tcx.def_map.borrow().get(&lhs.id).unwrap().full_def();
+        let pat_ty = self.tcx.node_id_to_type(lhs.id);
+        let variant = match pat_ty.sty {
+            ty::TyStruct(adt, _) | ty::TyEnum(adt, _) => adt.variant_of_def(def),
+            _ => self.tcx.sess.span_bug(lhs.span, "non-ADT in struct pattern")
         };
-        let fields = self.tcx.lookup_struct_fields(id);
         for pat in pats {
             if let ast::PatWild(ast::PatWildSingle) = pat.node.pat.node {
                 continue;
             }
-            let field_id = fields.iter()
-                .find(|field| field.name == pat.node.ident.name).unwrap().id;
-            self.live_symbols.insert(field_id.node);
+            self.live_symbols.insert(variant.field_named(pat.node.ident.name).did.node);
         }
     }
 
diff --git a/src/librustc/middle/expr_use_visitor.rs b/src/librustc/middle/expr_use_visitor.rs
index 469aacaf506..3755b4c57c3 100644
--- a/src/librustc/middle/expr_use_visitor.rs
+++ b/src/librustc/middle/expr_use_visitor.rs
@@ -694,41 +694,36 @@ impl<'d,'t,'a,'tcx> ExprUseVisitor<'d,'t,'a,'tcx> {
 
         // Select just those fields of the `with`
         // expression that will actually be used
-        let with_fields = match with_cmt.ty.sty {
-            ty::TyStruct(did, substs) => {
-                self.tcx().struct_fields(did, substs)
-            }
-            _ => {
-                // the base expression should always evaluate to a
-                // struct; however, when EUV is run during typeck, it
-                // may not. This will generate an error earlier in typeck,
-                // so we can just ignore it.
-                if !self.tcx().sess.has_errors() {
-                    self.tcx().sess.span_bug(
-                        with_expr.span,
-                        "with expression doesn't evaluate to a struct");
+        if let ty::TyStruct(def, substs) = with_cmt.ty.sty {
+            // Consume those fields of the with expression that are needed.
+            for with_field in &def.struct_variant().fields {
+                if !contains_field_named(with_field, fields) {
+                    let cmt_field = self.mc.cat_field(
+                        &*with_expr,
+                        with_cmt.clone(),
+                        with_field.name,
+                        with_field.ty(self.tcx(), substs)
+                    );
+                    self.delegate_consume(with_expr.id, with_expr.span, cmt_field);
                 }
-                assert!(self.tcx().sess.has_errors());
-                vec!()
             }
-        };
-
-        // Consume those fields of the with expression that are needed.
-        for with_field in &with_fields {
-            if !contains_field_named(with_field, fields) {
-                let cmt_field = self.mc.cat_field(&*with_expr,
-                                                  with_cmt.clone(),
-                                                  with_field.name,
-                                                  with_field.mt.ty);
-                self.delegate_consume(with_expr.id, with_expr.span, cmt_field);
+        } else {
+            // the base expression should always evaluate to a
+            // struct; however, when EUV is run during typeck, it
+            // may not. This will generate an error earlier in typeck,
+            // so we can just ignore it.
+            if !self.tcx().sess.has_errors() {
+                self.tcx().sess.span_bug(
+                    with_expr.span,
+                    "with expression doesn't evaluate to a struct");
             }
-        }
+        };
 
         // walk the with expression so that complex expressions
         // are properly handled.
         self.walk_expr(with_expr);
 
-        fn contains_field_named(field: &ty::Field,
+        fn contains_field_named(field: ty::FieldDef,
                                 fields: &Vec<ast::Field>)
                                 -> bool
         {
@@ -1106,7 +1101,7 @@ impl<'d,'t,'a,'tcx> ExprUseVisitor<'d,'t,'a,'tcx> {
 
                         Some(def::DefVariant(enum_did, variant_did, _is_struct)) => {
                             let downcast_cmt =
-                                if tcx.enum_is_univariant(enum_did) {
+                                if tcx.lookup_adt_def(enum_did).is_univariant() {
                                     cmt_pat
                                 } else {
                                     let cmt_pat_ty = cmt_pat.ty;
diff --git a/src/librustc/middle/fast_reject.rs b/src/librustc/middle/fast_reject.rs
index de7582a1371..8ff81635416 100644
--- a/src/librustc/middle/fast_reject.rs
+++ b/src/librustc/middle/fast_reject.rs
@@ -53,15 +53,15 @@ pub fn simplify_type(tcx: &ty::ctxt,
         ty::TyInt(int_type) => Some(IntSimplifiedType(int_type)),
         ty::TyUint(uint_type) => Some(UintSimplifiedType(uint_type)),
         ty::TyFloat(float_type) => Some(FloatSimplifiedType(float_type)),
-        ty::TyEnum(def_id, _) => Some(EnumSimplifiedType(def_id)),
+        ty::TyEnum(def, _) => Some(EnumSimplifiedType(def.did)),
         ty::TyStr => Some(StrSimplifiedType),
         ty::TyArray(..) | ty::TySlice(_) => Some(VecSimplifiedType),
         ty::TyRawPtr(_) => Some(PtrSimplifiedType),
         ty::TyTrait(ref trait_info) => {
             Some(TraitSimplifiedType(trait_info.principal_def_id()))
         }
-        ty::TyStruct(def_id, _) => {
-            Some(StructSimplifiedType(def_id))
+        ty::TyStruct(def, _) => {
+            Some(StructSimplifiedType(def.did))
         }
         ty::TyRef(_, mt) => {
             // since we introduce auto-refs during method lookup, we
diff --git a/src/librustc/middle/implicator.rs b/src/librustc/middle/implicator.rs
index 799d9a653ae..84fc2f7b2e5 100644
--- a/src/librustc/middle/implicator.rs
+++ b/src/librustc/middle/implicator.rs
@@ -144,10 +144,10 @@ impl<'a, 'tcx> Implicator<'a, 'tcx> {
                 self.accumulate_from_object_ty(ty, t.bounds.region_bound, required_region_bounds)
             }
 
-            ty::TyEnum(def_id, substs) |
-            ty::TyStruct(def_id, substs) => {
-                let item_scheme = self.tcx().lookup_item_type(def_id);
-                self.accumulate_from_adt(ty, def_id, &item_scheme.generics, substs)
+            ty::TyEnum(def, substs) |
+            ty::TyStruct(def, substs) => {
+                let item_scheme = def.type_scheme(self.tcx());
+                self.accumulate_from_adt(ty, def.did, &item_scheme.generics, substs)
             }
 
             ty::TyArray(t, _) |
diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs
index ee7079bb47d..3ab0d4c04d7 100644
--- a/src/librustc/middle/mem_categorization.rs
+++ b/src/librustc/middle/mem_categorization.rs
@@ -1216,7 +1216,7 @@ impl<'t, 'a,'tcx> MemCategorizationContext<'t, 'a, 'tcx> {
         let cmt = match opt_def {
             Some(def::DefVariant(enum_did, variant_did, _))
                 // univariant enums do not need downcasts
-                if !self.tcx().enum_is_univariant(enum_did) => {
+                if !self.tcx().lookup_adt_def(enum_did).is_univariant() => {
                     self.cat_downcast(pat, cmt.clone(), cmt.ty, variant_did)
                 }
             _ => cmt
diff --git a/src/librustc/middle/stability.rs b/src/librustc/middle/stability.rs
index 7dcf63fcafb..16f744b6887 100644
--- a/src/librustc/middle/stability.rs
+++ b/src/librustc/middle/stability.rs
@@ -415,16 +415,7 @@ pub fn check_expr(tcx: &ty::ctxt, e: &ast::Expr,
         ast::ExprField(ref base_e, ref field) => {
             span = field.span;
             match tcx.expr_ty_adjusted(base_e).sty {
-                ty::TyStruct(did, _) => {
-                    tcx.lookup_struct_fields(did)
-                        .iter()
-                        .find(|f| f.name == field.node.name)
-                        .unwrap_or_else(|| {
-                            tcx.sess.span_bug(field.span,
-                                              "stability::check_expr: unknown named field access")
-                        })
-                        .id
-                }
+                ty::TyStruct(def, _) => def.struct_variant().field_named(field.node.name).did,
                 _ => tcx.sess.span_bug(e.span,
                                        "stability::check_expr: named field access on non-struct")
             }
@@ -432,15 +423,7 @@ pub fn check_expr(tcx: &ty::ctxt, e: &ast::Expr,
         ast::ExprTupField(ref base_e, ref field) => {
             span = field.span;
             match tcx.expr_ty_adjusted(base_e).sty {
-                ty::TyStruct(did, _) => {
-                    tcx.lookup_struct_fields(did)
-                        .get(field.node)
-                        .unwrap_or_else(|| {
-                            tcx.sess.span_bug(field.span,
-                                              "stability::check_expr: unknown unnamed field access")
-                        })
-                        .id
-                }
+                ty::TyStruct(def, _) => def.struct_variant().fields[field.node].did,
                 ty::TyTuple(..) => return,
                 _ => tcx.sess.span_bug(e.span,
                                        "stability::check_expr: unnamed field access on \
@@ -450,20 +433,13 @@ pub fn check_expr(tcx: &ty::ctxt, e: &ast::Expr,
         ast::ExprStruct(_, ref expr_fields, _) => {
             let type_ = tcx.expr_ty(e);
             match type_.sty {
-                ty::TyStruct(did, _) => {
-                    let struct_fields = tcx.lookup_struct_fields(did);
+                ty::TyStruct(def, _) => {
                     // check the stability of each field that appears
                     // in the construction expression.
                     for field in expr_fields {
-                        let did = struct_fields
-                            .iter()
-                            .find(|f| f.name == field.ident.node.name)
-                            .unwrap_or_else(|| {
-                                tcx.sess.span_bug(field.span,
-                                                  "stability::check_expr: unknown named \
-                                                   field access")
-                            })
-                            .id;
+                        let did = def.struct_variant()
+                            .field_named(field.ident.node.name)
+                            .did;
                         maybe_do_stability_check(tcx, did, field.span, cb);
                     }
 
@@ -505,34 +481,26 @@ pub fn check_pat(tcx: &ty::ctxt, pat: &ast::Pat,
     debug!("check_pat(pat = {:?})", pat);
     if is_internal(tcx, pat.span) { return; }
 
-    let did = match tcx.pat_ty_opt(pat) {
-        Some(&ty::TyS { sty: ty::TyStruct(did, _), .. }) => did,
+    let v = match tcx.pat_ty_opt(pat) {
+        Some(&ty::TyS { sty: ty::TyStruct(def, _), .. }) => def.struct_variant(),
         Some(_) | None => return,
     };
-    let struct_fields = tcx.lookup_struct_fields(did);
     match pat.node {
         // Foo(a, b, c)
         ast::PatEnum(_, Some(ref pat_fields)) => {
-            for (field, struct_field) in pat_fields.iter().zip(&struct_fields) {
+            for (field, struct_field) in pat_fields.iter().zip(&v.fields) {
                 // a .. pattern is fine, but anything positional is
                 // not.
                 if let ast::PatWild(ast::PatWildMulti) = field.node {
                     continue
                 }
-                maybe_do_stability_check(tcx, struct_field.id, field.span, cb)
+                maybe_do_stability_check(tcx, struct_field.did, field.span, cb)
             }
         }
         // Foo { a, b, c }
         ast::PatStruct(_, ref pat_fields, _) => {
             for field in pat_fields {
-                let did = struct_fields
-                    .iter()
-                    .find(|f| f.name == field.node.ident.name)
-                    .unwrap_or_else(|| {
-                        tcx.sess.span_bug(field.span,
-                                          "stability::check_pat: unknown named field access")
-                    })
-                    .id;
+                let did = v.field_named(field.node.ident.name).did;
                 maybe_do_stability_check(tcx, did, field.span, cb);
             }
         }
diff --git a/src/librustc/middle/traits/coherence.rs b/src/librustc/middle/traits/coherence.rs
index 977d0577e48..534a2fc054d 100644
--- a/src/librustc/middle/traits/coherence.rs
+++ b/src/librustc/middle/traits/coherence.rs
@@ -278,8 +278,8 @@ fn fundamental_ty<'tcx>(tcx: &ty::ctxt<'tcx>, ty: Ty<'tcx>) -> bool
     match ty.sty {
         ty::TyBox(..) | ty::TyRef(..) =>
             true,
-        ty::TyEnum(def_id, _) | ty::TyStruct(def_id, _) =>
-            tcx.has_attr(def_id, "fundamental"),
+        ty::TyEnum(def, _) | ty::TyStruct(def, _) =>
+            def.is_fundamental(),
         ty::TyTrait(ref data) =>
             tcx.has_attr(data.principal_def_id(), "fundamental"),
         _ =>
@@ -316,9 +316,9 @@ fn ty_is_local_constructor<'tcx>(tcx: &ty::ctxt<'tcx>,
             infer_is_local.0
         }
 
-        ty::TyEnum(def_id, _) |
-        ty::TyStruct(def_id, _) => {
-            def_id.krate == ast::LOCAL_CRATE
+        ty::TyEnum(def, _) |
+        ty::TyStruct(def, _) => {
+            def.did.krate == ast::LOCAL_CRATE
         }
 
         ty::TyBox(_) => { // Box<T>
diff --git a/src/librustc/middle/traits/select.rs b/src/librustc/middle/traits/select.rs
index 5727f07edb1..0c9cf1a68b7 100644
--- a/src/librustc/middle/traits/select.rs
+++ b/src/librustc/middle/traits/select.rs
@@ -1721,21 +1721,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                 ok_if(substs.upvar_tys.clone())
             }
 
-            ty::TyStruct(def_id, substs) => {
-                let types: Vec<Ty> =
-                    self.tcx().struct_fields(def_id, substs).iter()
-                                                                 .map(|f| f.mt.ty)
-                                                                 .collect();
-                nominal(bound, types)
-            }
-
-            ty::TyEnum(def_id, substs) => {
-                let types: Vec<Ty> =
-                    self.tcx().substd_enum_variants(def_id, substs)
-                    .iter()
-                    .flat_map(|variant| &variant.args)
-                    .cloned()
-                    .collect();
+            ty::TyStruct(def, substs) | ty::TyEnum(def, substs) => {
+                let types: Vec<Ty> = def.all_fields().map(|f| {
+                    f.ty(self.tcx(), substs)
+                }).collect();
                 nominal(bound, types)
             }
 
@@ -1861,25 +1850,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
             }
 
             // for `PhantomData<T>`, we pass `T`
-            ty::TyStruct(def_id, substs)
-                if Some(def_id) == self.tcx().lang_items.phantom_data() =>
-            {
+            ty::TyStruct(def, substs) if def.is_phantom_data() => {
                 substs.types.get_slice(TypeSpace).to_vec()
             }
 
-            ty::TyStruct(def_id, substs) => {
-                self.tcx().struct_fields(def_id, substs)
-                          .iter()
-                          .map(|f| f.mt.ty)
-                          .collect()
-            }
-
-            ty::TyEnum(def_id, substs) => {
-                self.tcx().substd_enum_variants(def_id, substs)
-                          .iter()
-                          .flat_map(|variant| &variant.args)
-                          .map(|&ty| ty)
-                          .collect()
+            ty::TyStruct(def, substs) | ty::TyEnum(def, substs) => {
+                def.all_fields()
+                    .map(|f| f.ty(self.tcx(), substs))
+                    .collect()
             }
         }
     }
@@ -2523,10 +2501,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
             }
 
             // Struct<T> -> Struct<U>.
-            (&ty::TyStruct(def_id, substs_a), &ty::TyStruct(_, substs_b)) => {
-                let fields = tcx.lookup_struct_fields(def_id).iter().map(|f| {
-                    tcx.lookup_field_type_unsubstituted(def_id, f.id)
-                }).collect::<Vec<_>>();
+            (&ty::TyStruct(def, substs_a), &ty::TyStruct(_, substs_b)) => {
+                let fields = def
+                    .all_fields()
+                    .map(|f| f.unsubst_ty())
+                    .collect::<Vec<_>>();
 
                 // The last field of the structure has to exist and contain type parameters.
                 let field = if let Some(&field) = fields.last() {
@@ -2572,7 +2551,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                     let param_b = *substs_b.types.get(TypeSpace, i);
                     new_substs.types.get_mut_slice(TypeSpace)[i] = param_b;
                 }
-                let new_struct = tcx.mk_struct(def_id, tcx.mk_substs(new_substs));
+                let new_struct = tcx.mk_struct(def, tcx.mk_substs(new_substs));
                 let origin = infer::Misc(obligation.cause.span);
                 if self.infcx.sub_types(false, origin, new_struct, target).is_err() {
                     return Err(Unimplemented);
diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs
index 88fc6181f92..2fe1f14d521 100644
--- a/src/librustc/middle/ty.rs
+++ b/src/librustc/middle/ty.rs
@@ -74,22 +74,24 @@ use std::cell::{Cell, RefCell, Ref};
 use std::cmp;
 use std::fmt;
 use std::hash::{Hash, SipHasher, Hasher};
+use std::iter;
+use std::marker::PhantomData;
 use std::mem;
 use std::ops;
 use std::rc::Rc;
+use std::slice;
 use std::vec::IntoIter;
 use collections::enum_set::{self, EnumSet, CLike};
+use core::nonzero::NonZero;
 use std::collections::{HashMap, HashSet};
+use rustc_data_structures::ivar;
 use syntax::abi;
 use syntax::ast::{CrateNum, DefId, ItemImpl, ItemTrait, LOCAL_CRATE};
-use syntax::ast::{MutImmutable, MutMutable, Name, NamedField, NodeId};
-use syntax::ast::{StructField, UnnamedField, Visibility};
+use syntax::ast::{MutImmutable, MutMutable, Name, NodeId, Visibility};
 use syntax::ast_util::{self, is_local, local_def};
 use syntax::attr::{self, AttrMetaMethods, SignedInt, UnsignedInt};
 use syntax::codemap::Span;
-use syntax::parse::token::{self, InternedString, special_idents};
-use syntax::print::pprust;
-use syntax::ptr::P;
+use syntax::parse::token::{InternedString, special_idents};
 use syntax::ast;
 
 pub type Disr = u64;
@@ -109,83 +111,6 @@ pub struct CrateAnalysis {
     pub glob_map: Option<GlobMap>,
 }
 
-#[derive(Copy, Clone, PartialEq, Eq, Hash)]
-pub struct Field<'tcx> {
-    pub name: ast::Name,
-    pub mt: TypeAndMut<'tcx>
-}
-
-// Enum information
-#[derive(Clone)]
-pub struct VariantInfo<'tcx> {
-    pub args: Vec<Ty<'tcx>>,
-    pub arg_names: Option<Vec<ast::Name>>,
-    pub ctor_ty: Option<Ty<'tcx>>,
-    pub name: ast::Name,
-    pub id: ast::DefId,
-    pub disr_val: Disr,
-    pub vis: Visibility
-}
-
-impl<'tcx> VariantInfo<'tcx> {
-
-    /// Creates a new VariantInfo from the corresponding ast representation.
-    ///
-    /// Does not do any caching of the value in the type context.
-    pub fn from_ast_variant(cx: &ctxt<'tcx>,
-                            ast_variant: &ast::Variant,
-                            discriminant: Disr) -> VariantInfo<'tcx> {
-        let ctor_ty = cx.node_id_to_type(ast_variant.node.id);
-
-        match ast_variant.node.kind {
-            ast::TupleVariantKind(ref args) => {
-                let arg_tys = if !args.is_empty() {
-                    // the regions in the argument types come from the
-                    // enum def'n, and hence will all be early bound
-                    cx.no_late_bound_regions(&ctor_ty.fn_args()).unwrap()
-                } else {
-                    Vec::new()
-                };
-
-                return VariantInfo {
-                    args: arg_tys,
-                    arg_names: None,
-                    ctor_ty: Some(ctor_ty),
-                    name: ast_variant.node.name.name,
-                    id: ast_util::local_def(ast_variant.node.id),
-                    disr_val: discriminant,
-                    vis: ast_variant.node.vis
-                };
-            },
-            ast::StructVariantKind(ref struct_def) => {
-                let fields: &[StructField] = &struct_def.fields;
-
-                assert!(!fields.is_empty());
-
-                let arg_tys = struct_def.fields.iter()
-                    .map(|field| cx.node_id_to_type(field.node.id)).collect();
-                let arg_names = fields.iter().map(|field| {
-                    match field.node.kind {
-                        NamedField(ident, _) => ident.name,
-                        UnnamedField(..) => cx.sess.bug(
-                            "enum_variants: all fields in struct must have a name")
-                    }
-                }).collect();
-
-                return VariantInfo {
-                    args: arg_tys,
-                    arg_names: Some(arg_names),
-                    ctor_ty: None,
-                    name: ast_variant.node.name.name,
-                    id: ast_util::local_def(ast_variant.node.id),
-                    disr_val: discriminant,
-                    vis: ast_variant.node.vis
-                };
-            }
-        }
-    }
-}
-
 #[derive(Copy, Clone)]
 pub enum DtorKind {
     NoDtor,
@@ -208,7 +133,7 @@ impl DtorKind {
     }
 }
 
-trait IntTypeExt {
+pub trait IntTypeExt {
     fn to_ty<'tcx>(&self, cx: &ctxt<'tcx>) -> Ty<'tcx>;
     fn i64_to_disr(&self, val: i64) -> Option<Disr>;
     fn u64_to_disr(&self, val: u64) -> Option<Disr>;
@@ -492,14 +417,6 @@ pub struct TypeAndMut<'tcx> {
     pub mutbl: ast::Mutability,
 }
 
-#[derive(Clone, Copy, Debug)]
-pub struct FieldTy {
-    pub name: Name,
-    pub id: DefId,
-    pub vis: ast::Visibility,
-    pub origin: ast::DefId,  // The DefId of the struct in which the field is declared.
-}
-
 #[derive(Clone, PartialEq, RustcDecodable, RustcEncodable)]
 pub struct ItemVariances {
     pub types: VecPerParamSpace<Variance>,
@@ -721,6 +638,7 @@ pub struct CtxtArenas<'tcx> {
 
     // references
     trait_defs: TypedArena<TraitDef<'tcx>>,
+    adt_defs: TypedArena<AdtDefData<'tcx, 'tcx>>,
 }
 
 impl<'tcx> CtxtArenas<'tcx> {
@@ -732,7 +650,8 @@ impl<'tcx> CtxtArenas<'tcx> {
             region: TypedArena::new(),
             stability: TypedArena::new(),
 
-            trait_defs: TypedArena::new()
+            trait_defs: TypedArena::new(),
+            adt_defs: TypedArena::new()
         }
     }
 }
@@ -847,6 +766,7 @@ pub struct ctxt<'tcx> {
 
     pub impl_trait_refs: RefCell<DefIdMap<Option<TraitRef<'tcx>>>>,
     pub trait_defs: RefCell<DefIdMap<&'tcx TraitDef<'tcx>>>,
+    pub adt_defs: RefCell<DefIdMap<AdtDefMaster<'tcx>>>,
 
     /// Maps from the def-id of an item (trait/struct/enum/fn) to its
     /// associated predicates.
@@ -866,13 +786,11 @@ pub struct ctxt<'tcx> {
     pub rcache: RefCell<FnvHashMap<CReaderCacheKey, Ty<'tcx>>>,
     pub tc_cache: RefCell<FnvHashMap<Ty<'tcx>, TypeContents>>,
     pub ast_ty_to_ty_cache: RefCell<NodeMap<Ty<'tcx>>>,
-    pub enum_var_cache: RefCell<DefIdMap<Rc<Vec<Rc<VariantInfo<'tcx>>>>>>,
     pub ty_param_defs: RefCell<NodeMap<TypeParameterDef<'tcx>>>,
     pub normalized_cache: RefCell<FnvHashMap<Ty<'tcx>, Ty<'tcx>>>,
     pub lang_items: middle::lang_items::LanguageItems,
     /// A mapping of fake provided method def_ids to the default implementation
     pub provided_method_sources: RefCell<DefIdMap<ast::DefId>>,
-    pub struct_fields: RefCell<DefIdMap<Rc<Vec<FieldTy>>>>,
 
     /// Maps from def-id of a type or region parameter to its
     /// (inferred) variance.
@@ -1017,6 +935,18 @@ impl<'tcx> ctxt<'tcx> {
         interned
     }
 
+    pub fn intern_adt_def(&self,
+                          did: DefId,
+                          kind: AdtKind,
+                          variants: Vec<VariantDefData<'tcx, 'tcx>>)
+                          -> AdtDefMaster<'tcx> {
+        let def = AdtDefData::new(self, did, kind, variants);
+        let interned = self.arenas.adt_defs.alloc(def);
+        // this will need a transmute when reverse-variance is removed
+        self.adt_defs.borrow_mut().insert(did, interned);
+        interned
+    }
+
     pub fn intern_stability(&self, stab: attr::Stability) -> &'tcx attr::Stability {
         if let Some(st) = self.stability_interner.borrow().get(&stab) {
             return st;
@@ -1385,6 +1315,61 @@ impl<'tcx> Hash for TyS<'tcx> {
 
 pub type Ty<'tcx> = &'tcx TyS<'tcx>;
 
+/// An IVar that contains a Ty. 'lt is a (reverse-variant) upper bound
+/// on the lifetime of the IVar. This is required because of variance
+/// problems: the IVar needs to be variant with respect to 'tcx (so
+/// it can be referred to from Ty) but can only be modified if its
+/// lifetime is exactly 'tcx.
+///
+/// Safety invariants:
+///     (A) self.0, if fulfilled, is a valid Ty<'tcx>
+///     (B) no aliases to this value with a 'tcx longer than this
+///         value's 'lt exist
+///
+/// NonZero is used rather than Unique because Unique isn't Copy.
+pub struct TyIVar<'tcx, 'lt: 'tcx>(ivar::Ivar<NonZero<*const TyS<'static>>>,
+                                   PhantomData<fn(TyS<'lt>)->TyS<'tcx>>);
+
+impl<'tcx, 'lt> TyIVar<'tcx, 'lt> {
+    #[inline]
+    pub fn new() -> Self {
+        // Invariant (A) satisfied because the IVar is unfulfilled
+        // Invariant (B) because 'lt : 'tcx
+        TyIVar(ivar::Ivar::new(), PhantomData)
+    }
+
+    #[inline]
+    pub fn get(&self) -> Option<Ty<'tcx>> {
+        match self.0.get() {
+            None => None,
+            // valid because of invariant (A)
+            Some(v) => Some(unsafe { &*(*v as *const TyS<'tcx>) })
+        }
+    }
+    #[inline]
+    pub fn unwrap(&self) -> Ty<'tcx> {
+        self.get().unwrap()
+    }
+
+    pub fn fulfill(&self, value: Ty<'lt>) {
+        // Invariant (A) is fulfilled, because by (B), every alias
+        // of this has a 'tcx longer than 'lt.
+        let value: *const TyS<'lt> = value;
+        // FIXME(27214): unneeded [as *const ()]
+        let value = value as *const () as *const TyS<'static>;
+        self.0.fulfill(unsafe { NonZero::new(value) })
+    }
+}
+
+impl<'tcx, 'lt> fmt::Debug for TyIVar<'tcx, 'lt> {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        match self.get() {
+            Some(val) => write!(f, "TyIVar({:?})", val),
+            None => f.write_str("TyIVar(<unfulfilled>)")
+        }
+    }
+}
+
 /// An entry in the type interner.
 pub struct InternedTy<'tcx> {
     ty: Ty<'tcx>
@@ -1761,12 +1746,12 @@ pub enum TypeVariants<'tcx> {
     /// from the tcx, use the `NodeId` from the `ast::Ty` and look it up in
     /// the `ast_ty_to_ty_cache`. This is probably true for `TyStruct` as
     /// well.
-    TyEnum(DefId, &'tcx Substs<'tcx>),
+    TyEnum(AdtDef<'tcx>, &'tcx Substs<'tcx>),
 
     /// A structure type, defined with `struct`.
     ///
     /// See warning about substitutions for enumerated types.
-    TyStruct(DefId, &'tcx Substs<'tcx>),
+    TyStruct(AdtDef<'tcx>, &'tcx Substs<'tcx>),
 
     /// `Box<T>`; this is nominally a struct in the documentation, but is
     /// special-cased internally. For example, it is possible to implicitly
@@ -2121,7 +2106,7 @@ pub struct ExistentialBounds<'tcx> {
 pub struct BuiltinBounds(EnumSet<BuiltinBound>);
 
 impl BuiltinBounds {
-       pub fn empty() -> BuiltinBounds {
+    pub fn empty() -> BuiltinBounds {
         BuiltinBounds(EnumSet::new())
     }
 
@@ -2984,33 +2969,31 @@ impl<'a, 'tcx> ParameterEnvironment<'a, 'tcx> {
         // FIXME: (@jroesch) float this code up
         let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, Some(self.clone()), false);
 
-        let did = match self_type.sty {
-            ty::TyStruct(struct_did, substs) => {
-                let fields = tcx.struct_fields(struct_did, substs);
-                for field in &fields {
-                    if infcx.type_moves_by_default(field.mt.ty, span) {
+        let adt = match self_type.sty {
+            ty::TyStruct(struct_def, substs) => {
+                for field in struct_def.all_fields() {
+                    let field_ty = field.ty(tcx, substs);
+                    if infcx.type_moves_by_default(field_ty, span) {
                         return Err(FieldDoesNotImplementCopy(field.name))
                     }
                 }
-                struct_did
-            }
-            ty::TyEnum(enum_did, substs) => {
-                let enum_variants = tcx.enum_variants(enum_did);
-                for variant in enum_variants.iter() {
-                    for variant_arg_type in &variant.args {
-                        let substd_arg_type =
-                            variant_arg_type.subst(tcx, substs);
-                        if infcx.type_moves_by_default(substd_arg_type, span) {
+                struct_def
+            }
+            ty::TyEnum(enum_def, substs) => {
+                for variant in &enum_def.variants {
+                    for field in &variant.fields {
+                        let field_ty = field.ty(tcx, substs);
+                        if infcx.type_moves_by_default(field_ty, span) {
                             return Err(VariantDoesNotImplementCopy(variant.name))
                         }
                     }
                 }
-                enum_did
+                enum_def
             }
             _ => return Err(TypeIsStructural),
         };
 
-        if tcx.has_dtor(did) {
+        if adt.has_dtor(tcx) {
             return Err(TypeHasDestructor)
         }
 
@@ -3193,6 +3176,286 @@ impl<'tcx> TraitDef<'tcx> {
 
 }
 
+bitflags! {
+    flags AdtFlags: u32 {
+        const NO_ADT_FLAGS        = 0,
+        const IS_ENUM             = 1 << 0,
+        const IS_DTORCK           = 1 << 1, // is this a dtorck type?
+        const IS_DTORCK_VALID     = 1 << 2,
+        const IS_PHANTOM_DATA     = 1 << 3,
+        const IS_SIMD             = 1 << 4,
+        const IS_FUNDAMENTAL      = 1 << 5,
+    }
+}
+
+pub type AdtDef<'tcx> = &'tcx AdtDefData<'tcx, 'static>;
+pub type VariantDef<'tcx> = &'tcx VariantDefData<'tcx, 'static>;
+pub type FieldDef<'tcx> = &'tcx FieldDefData<'tcx, 'static>;
+
+// See comment on AdtDefData for explanation
+pub type AdtDefMaster<'tcx> = &'tcx AdtDefData<'tcx, 'tcx>;
+pub type VariantDefMaster<'tcx> = &'tcx VariantDefData<'tcx, 'tcx>;
+pub type FieldDefMaster<'tcx> = &'tcx FieldDefData<'tcx, 'tcx>;
+
+pub struct VariantDefData<'tcx, 'container: 'tcx> {
+    pub did: DefId,
+    pub name: Name, // struct's name if this is a struct
+    pub disr_val: Disr,
+    pub fields: Vec<FieldDefData<'tcx, 'container>>
+}
+
+pub struct FieldDefData<'tcx, 'container: 'tcx> {
+    /// The field's DefId. NOTE: the fields of tuple-like enum variants
+    /// are not real items, and don't have entries in tcache etc.
+    pub did: DefId,
+    /// special_idents::unnamed_field.name
+    /// if this is a tuple-like field
+    pub name: Name,
+    pub vis: ast::Visibility,
+    /// TyIVar is used here to allow for variance (see the doc at
+    /// AdtDefData).
+    ty: TyIVar<'tcx, 'container>
+}
+
+/// The definition of an abstract data type - a struct or enum.
+///
+/// These are all interned (by intern_adt_def) into the adt_defs
+/// table.
+///
+/// Because of the possibility of nested tcx-s, this type
+/// needs 2 lifetimes: the traditional variant lifetime ('tcx)
+/// bounding the lifetime of the inner types is of course necessary.
+/// However, it is not sufficient - types from a child tcx must
+/// not be leaked into the master tcx by being stored in an AdtDefData.
+///
+/// The 'container lifetime ensures that by outliving the container
+/// tcx and preventing shorter-lived types from being inserted. When
+/// write access is not needed, the 'container lifetime can be
+/// erased to 'static, which can be done by the AdtDef wrapper.
+pub struct AdtDefData<'tcx, 'container: 'tcx> {
+    pub did: DefId,
+    pub variants: Vec<VariantDefData<'tcx, 'container>>,
+    flags: Cell<AdtFlags>,
+}
+
+impl<'tcx, 'container> PartialEq for AdtDefData<'tcx, 'container> {
+    // AdtDefData are always interned and this is part of TyS equality
+    #[inline]
+    fn eq(&self, other: &Self) -> bool { self as *const _ == other as *const _ }
+}
+
+impl<'tcx, 'container> Eq for AdtDefData<'tcx, 'container> {}
+
+impl<'tcx, 'container> Hash for AdtDefData<'tcx, 'container> {
+    #[inline]
+    fn hash<H: Hasher>(&self, s: &mut H) {
+        (self as *const AdtDefData).hash(s)
+    }
+}
+
+
+#[derive(Copy, Clone, Debug, Eq, PartialEq)]
+pub enum AdtKind { Struct, Enum }
+
+#[derive(Copy, Clone, Debug, Eq, PartialEq)]
+pub enum VariantKind { Dict, Tuple, Unit }
+
+impl<'tcx, 'container> AdtDefData<'tcx, 'container> {
+    fn new(tcx: &ctxt<'tcx>,
+           did: DefId,
+           kind: AdtKind,
+           variants: Vec<VariantDefData<'tcx, 'container>>) -> Self {
+        let mut flags = AdtFlags::NO_ADT_FLAGS;
+        let attrs = tcx.get_attrs(did);
+        if attrs.iter().any(|item| item.check_name("fundamental")) {
+            flags = flags | AdtFlags::IS_FUNDAMENTAL;
+        }
+        if attrs.iter().any(|item| item.check_name("simd")) {
+            flags = flags | AdtFlags::IS_SIMD;
+        }
+        if Some(did) == tcx.lang_items.phantom_data() {
+            flags = flags | AdtFlags::IS_PHANTOM_DATA;
+        }
+        if let AdtKind::Enum = kind {
+            flags = flags | AdtFlags::IS_ENUM;
+        }
+        AdtDefData {
+            did: did,
+            variants: variants,
+            flags: Cell::new(flags),
+        }
+    }
+
+    fn calculate_dtorck(&'tcx self, tcx: &ctxt<'tcx>) {
+        if tcx.is_adt_dtorck(self) {
+            self.flags.set(self.flags.get() | AdtFlags::IS_DTORCK);
+        }
+        self.flags.set(self.flags.get() | AdtFlags::IS_DTORCK_VALID)
+    }
+
+    /// Returns the kind of the ADT - Struct or Enum.
+    #[inline]
+    pub fn adt_kind(&self) -> AdtKind {
+        if self.flags.get().intersects(AdtFlags::IS_ENUM) {
+            AdtKind::Enum
+        } else {
+            AdtKind::Struct
+        }
+    }
+
+    /// Returns whether this is a dtorck type. If this returns
+    /// true, this type being safe for destruction requires it to be
+    /// alive; Otherwise, only the contents are required to be.
+    #[inline]
+    pub fn is_dtorck(&'tcx self, tcx: &ctxt<'tcx>) -> bool {
+        if !self.flags.get().intersects(AdtFlags::IS_DTORCK_VALID) {
+            self.calculate_dtorck(tcx)
+        }
+        self.flags.get().intersects(AdtFlags::IS_DTORCK)
+    }
+
+    /// Returns whether this type is #[fundamental] for the purposes
+    /// of coherence checking.
+    #[inline]
+    pub fn is_fundamental(&self) -> bool {
+        self.flags.get().intersects(AdtFlags::IS_FUNDAMENTAL)
+    }
+
+    #[inline]
+    pub fn is_simd(&self) -> bool {
+        self.flags.get().intersects(AdtFlags::IS_SIMD)
+    }
+
+    /// Returns true if this is PhantomData<T>.
+    #[inline]
+    pub fn is_phantom_data(&self) -> bool {
+        self.flags.get().intersects(AdtFlags::IS_PHANTOM_DATA)
+    }
+
+    /// Returns whether this type has a destructor.
+    pub fn has_dtor(&self, tcx: &ctxt<'tcx>) -> bool {
+        tcx.destructor_for_type.borrow().contains_key(&self.did)
+    }
+
+    /// Asserts this is a struct and returns the struct's unique
+    /// variant.
+    pub fn struct_variant(&self) -> &VariantDefData<'tcx, 'container> {
+        assert!(self.adt_kind() == AdtKind::Struct);
+        &self.variants[0]
+    }
+
+    #[inline]
+    pub fn type_scheme(&self, tcx: &ctxt<'tcx>) -> TypeScheme<'tcx> {
+        tcx.lookup_item_type(self.did)
+    }
+
+    #[inline]
+    pub fn predicates(&self, tcx: &ctxt<'tcx>) -> GenericPredicates<'tcx> {
+        tcx.lookup_predicates(self.did)
+    }
+
+    /// Returns an iterator over all fields contained
+    /// by this ADT.
+    #[inline]
+    pub fn all_fields(&self) ->
+            iter::FlatMap<
+                slice::Iter<VariantDefData<'tcx, 'container>>,
+                slice::Iter<FieldDefData<'tcx, 'container>>,
+                for<'s> fn(&'s VariantDefData<'tcx, 'container>)
+                    -> slice::Iter<'s, FieldDefData<'tcx, 'container>>
+            > {
+        self.variants.iter().flat_map(VariantDefData::fields_iter)
+    }
+
+    #[inline]
+    pub fn is_empty(&self) -> bool {
+        self.variants.is_empty()
+    }
+
+    #[inline]
+    pub fn is_univariant(&self) -> bool {
+        self.variants.len() == 1
+    }
+
+    pub fn is_payloadfree(&self) -> bool {
+        !self.variants.is_empty() &&
+            self.variants.iter().all(|v| v.fields.is_empty())
+    }
+
+    pub fn variant_with_id(&self, vid: DefId) -> &VariantDefData<'tcx, 'container> {
+        self.variants
+            .iter()
+            .find(|v| v.did == vid)
+            .expect("variant_with_id: unknown variant")
+    }
+
+    pub fn variant_of_def(&self, def: def::Def) -> &VariantDefData<'tcx, 'container> {
+        match def {
+            def::DefVariant(_, vid, _) => self.variant_with_id(vid),
+            def::DefStruct(..) | def::DefTy(..) => self.struct_variant(),
+            _ => panic!("unexpected def {:?} in variant_of_def", def)
+        }
+    }
+}
+
+impl<'tcx, 'container> VariantDefData<'tcx, 'container> {
+    #[inline]
+    fn fields_iter(&self) -> slice::Iter<FieldDefData<'tcx, 'container>> {
+        self.fields.iter()
+    }
+
+    pub fn kind(&self) -> VariantKind {
+        match self.fields.get(0) {
+            None => VariantKind::Unit,
+            Some(&FieldDefData { name, .. }) if name == special_idents::unnamed_field.name => {
+                VariantKind::Tuple
+            }
+            Some(_) => VariantKind::Dict
+        }
+    }
+
+    pub fn is_tuple_struct(&self) -> bool {
+        self.kind() == VariantKind::Tuple
+    }
+
+    #[inline]
+    pub fn find_field_named(&self,
+                            name: ast::Name)
+                            -> Option<&FieldDefData<'tcx, 'container>> {
+        self.fields.iter().find(|f| f.name == name)
+    }
+
+    #[inline]
+    pub fn field_named(&self, name: ast::Name) -> &FieldDefData<'tcx, 'container> {
+        self.find_field_named(name).unwrap()
+    }
+}
+
+impl<'tcx, 'container> FieldDefData<'tcx, 'container> {
+    pub fn new(did: DefId,
+               name: Name,
+               vis: ast::Visibility) -> Self {
+        FieldDefData {
+            did: did,
+            name: name,
+            vis: vis,
+            ty: TyIVar::new()
+        }
+    }
+
+    pub fn ty(&self, tcx: &ctxt<'tcx>, subst: &Substs<'tcx>) -> Ty<'tcx> {
+        self.unsubst_ty().subst(tcx, subst)
+    }
+
+    pub fn unsubst_ty(&self) -> Ty<'tcx> {
+        self.ty.unwrap()
+    }
+
+    pub fn fulfill_ty(&self, ty: Ty<'container>) {
+        self.ty.fulfill(ty);
+    }
+}
+
 /// Records the substitutions used to translate the polytype for an
 /// item into the monotype of an item reference.
 #[derive(Clone)]
@@ -3498,6 +3761,7 @@ impl<'tcx> ctxt<'tcx> {
             tables: RefCell::new(Tables::empty()),
             impl_trait_refs: RefCell::new(DefIdMap()),
             trait_defs: RefCell::new(DefIdMap()),
+            adt_defs: RefCell::new(DefIdMap()),
             predicates: RefCell::new(DefIdMap()),
             super_predicates: RefCell::new(DefIdMap()),
             fulfilled_predicates: RefCell::new(traits::FulfilledPredicates::new()),
@@ -3507,7 +3771,6 @@ impl<'tcx> ctxt<'tcx> {
             rcache: RefCell::new(FnvHashMap()),
             tc_cache: RefCell::new(FnvHashMap()),
             ast_ty_to_ty_cache: RefCell::new(NodeMap()),
-            enum_var_cache: RefCell::new(DefIdMap()),
             impl_or_trait_items: RefCell::new(DefIdMap()),
             trait_item_def_ids: RefCell::new(DefIdMap()),
             trait_items_cache: RefCell::new(DefIdMap()),
@@ -3515,7 +3778,6 @@ impl<'tcx> ctxt<'tcx> {
             normalized_cache: RefCell::new(FnvHashMap()),
             lang_items: lang_items,
             provided_method_sources: RefCell::new(DefIdMap()),
-            struct_fields: RefCell::new(DefIdMap()),
             destructor_for_type: RefCell::new(DefIdMap()),
             destructors: RefCell::new(DefIdSet()),
             inherent_impls: RefCell::new(DefIdMap()),
@@ -3679,9 +3941,9 @@ impl<'tcx> ctxt<'tcx> {
         self.mk_imm_ref(self.mk_region(ty::ReStatic), self.mk_str())
     }
 
-    pub fn mk_enum(&self, did: ast::DefId, substs: &'tcx Substs<'tcx>) -> Ty<'tcx> {
+    pub fn mk_enum(&self, def: AdtDef<'tcx>, substs: &'tcx Substs<'tcx>) -> Ty<'tcx> {
         // take a copy of substs so that we own the vectors inside
-        self.mk_ty(TyEnum(did, substs))
+        self.mk_ty(TyEnum(def, substs))
     }
 
     pub fn mk_box(&self, ty: Ty<'tcx>) -> Ty<'tcx> {
@@ -3781,10 +4043,9 @@ impl<'tcx> ctxt<'tcx> {
         self.mk_ty(TyProjection(inner))
     }
 
-    pub fn mk_struct(&self, struct_id: ast::DefId,
-                     substs: &'tcx Substs<'tcx>) -> Ty<'tcx> {
+    pub fn mk_struct(&self, def: AdtDef<'tcx>, substs: &'tcx Substs<'tcx>) -> Ty<'tcx> {
         // take a copy of substs so that we own the vectors inside
-        self.mk_ty(TyStruct(struct_id, substs))
+        self.mk_ty(TyStruct(def, substs))
     }
 
     pub fn mk_closure(&self,
@@ -3944,9 +4205,10 @@ impl<'tcx> TyS<'tcx> {
         }
     }
 
-    pub fn is_empty(&self, cx: &ctxt) -> bool {
+    pub fn is_empty(&self, _cx: &ctxt) -> bool {
+        // FIXME(#24885): be smarter here
         match self.sty {
-            TyEnum(did, _) => cx.enum_variants(did).is_empty(),
+            TyEnum(def, _) | TyStruct(def, _) => def.is_empty(),
             _ => false
         }
     }
@@ -3985,9 +4247,10 @@ impl<'tcx> TyS<'tcx> {
         }
     }
 
-    pub fn is_simd(&self, cx: &ctxt) -> bool {
+    #[inline]
+    pub fn is_simd(&self) -> bool {
         match self.sty {
-            TyStruct(did, _) => cx.lookup_simd(did),
+            TyStruct(def, _) => def.is_simd(),
             _ => false
         }
     }
@@ -4003,19 +4266,16 @@ impl<'tcx> TyS<'tcx> {
 
     pub fn simd_type(&self, cx: &ctxt<'tcx>) -> Ty<'tcx> {
         match self.sty {
-            TyStruct(did, substs) => {
-                let fields = cx.lookup_struct_fields(did);
-                cx.lookup_field_type(did, fields[0].id, substs)
+            TyStruct(def, substs) => {
+                def.struct_variant().fields[0].ty(cx, substs)
             }
             _ => panic!("simd_type called on invalid type")
         }
     }
 
-    pub fn simd_size(&self, cx: &ctxt) -> usize {
+    pub fn simd_size(&self, _cx: &ctxt) -> usize {
         match self.sty {
-            TyStruct(did, _) => {
-                cx.lookup_struct_fields(did).len()
-            }
+            TyStruct(def, _) => def.struct_variant().fields.len(),
             _ => panic!("simd_size called on invalid type")
         }
     }
@@ -4067,12 +4327,19 @@ impl<'tcx> TyS<'tcx> {
     pub fn ty_to_def_id(&self) -> Option<ast::DefId> {
         match self.sty {
             TyTrait(ref tt) => Some(tt.principal_def_id()),
-            TyStruct(id, _) |
-            TyEnum(id, _) |
+            TyStruct(def, _) |
+            TyEnum(def, _) => Some(def.did),
             TyClosure(id, _) => Some(id),
             _ => None
         }
     }
+
+    pub fn ty_adt_def(&self) -> Option<AdtDef<'tcx>> {
+        match self.sty {
+            TyStruct(adt, _) | TyEnum(adt, _) => Some(adt),
+            _ => None
+        }
+    }
 }
 
 /// Type contents is how the type checker reasons about kinds.
@@ -4275,18 +4542,6 @@ impl<'tcx> TyS<'tcx> {
                 }
                 TyStr => TC::None,
 
-                TyStruct(did, substs) => {
-                    let flds = cx.struct_fields(did, substs);
-                    let mut res =
-                        TypeContents::union(&flds[..],
-                                            |f| tc_ty(cx, f.mt.ty, cache));
-
-                    if cx.has_dtor(did) {
-                        res = res | TC::OwnsDtor;
-                    }
-                    apply_lang_items(cx, did, res)
-                }
-
                 TyClosure(_, ref substs) => {
                     TypeContents::union(&substs.upvar_tys, |ty| tc_ty(cx, &ty, cache))
                 }
@@ -4296,21 +4551,19 @@ impl<'tcx> TyS<'tcx> {
                                         |ty| tc_ty(cx, *ty, cache))
                 }
 
-                TyEnum(did, substs) => {
-                    let variants = cx.substd_enum_variants(did, substs);
+                TyStruct(def, substs) | TyEnum(def, substs) => {
                     let mut res =
-                        TypeContents::union(&variants[..], |variant| {
-                            TypeContents::union(&variant.args,
-                                                |arg_ty| {
-                                tc_ty(cx, *arg_ty, cache)
+                        TypeContents::union(&def.variants, |v| {
+                            TypeContents::union(&v.fields, |f| {
+                                tc_ty(cx, f.ty(cx, substs), cache)
                             })
                         });
 
-                    if cx.has_dtor(did) {
+                    if def.has_dtor(cx) {
                         res = res | TC::OwnsDtor;
                     }
 
-                    apply_lang_items(cx, did, res)
+                    apply_lang_items(cx, def.did, res)
                 }
 
                 TyProjection(..) |
@@ -4431,7 +4684,7 @@ impl<'tcx> TyS<'tcx> {
 
     // True if instantiating an instance of `r_ty` requires an instance of `r_ty`.
     pub fn is_instantiable(&'tcx self, cx: &ctxt<'tcx>) -> bool {
-        fn type_requires<'tcx>(cx: &ctxt<'tcx>, seen: &mut Vec<DefId>,
+        fn type_requires<'tcx>(cx: &ctxt<'tcx>, seen: &mut Vec<AdtDef<'tcx>>,
                                r_ty: Ty<'tcx>, ty: Ty<'tcx>) -> bool {
             debug!("type_requires({:?}, {:?})?",
                    r_ty, ty);
@@ -4443,7 +4696,7 @@ impl<'tcx> TyS<'tcx> {
             return r;
         }
 
-        fn subtypes_require<'tcx>(cx: &ctxt<'tcx>, seen: &mut Vec<DefId>,
+        fn subtypes_require<'tcx>(cx: &ctxt<'tcx>, seen: &mut Vec<AdtDef<'tcx>>,
                                   r_ty: Ty<'tcx>, ty: Ty<'tcx>) -> bool {
             debug!("subtypes_require({:?}, {:?})?",
                    r_ty, ty);
@@ -4482,16 +4735,22 @@ impl<'tcx> TyS<'tcx> {
                     false
                 }
 
-                TyStruct(ref did, _) if seen.contains(did) => {
-                    false
-                }
-
-                TyStruct(did, substs) => {
-                    seen.push(did);
-                    let fields = cx.struct_fields(did, substs);
-                    let r = fields.iter().any(|f| type_requires(cx, seen, r_ty, f.mt.ty));
-                    seen.pop().unwrap();
-                    r
+                TyStruct(def, substs) | TyEnum(def, substs) => {
+                    if seen.contains(&def) {
+                        // FIXME(#27497) ???
+                        false
+                    } else if def.is_empty() {
+                        // HACK: required for empty types to work. This
+                        // check is basically a lint anyway.
+                        false
+                    } else {
+                        seen.push(def);
+                        let r = def.variants.iter().all(|v| v.fields.iter().any(|f| {
+                            type_requires(cx, seen, r_ty, f.ty(cx, substs))
+                        }));
+                        seen.pop().unwrap();
+                        r
+                    }
                 }
 
                 TyError |
@@ -4505,23 +4764,6 @@ impl<'tcx> TyS<'tcx> {
                 TyTuple(ref ts) => {
                     ts.iter().any(|ty| type_requires(cx, seen, r_ty, *ty))
                 }
-
-                TyEnum(ref did, _) if seen.contains(did) => {
-                    false
-                }
-
-                TyEnum(did, substs) => {
-                    seen.push(did);
-                    let vs = cx.enum_variants(did);
-                    let r = !vs.is_empty() && vs.iter().all(|variant| {
-                        variant.args.iter().any(|aty| {
-                            let sty = aty.subst(cx, substs);
-                            type_requires(cx, seen, r_ty, sty)
-                        })
-                    });
-                    seen.pop().unwrap();
-                    r
-                }
             };
 
             debug!("subtypes_require({:?}, {:?})? {:?}",
@@ -4576,17 +4818,11 @@ impl<'tcx> TyS<'tcx> {
                 TyArray(ty, _) => {
                     is_type_structurally_recursive(cx, sp, seen, ty)
                 }
-                TyStruct(did, substs) => {
-                    let fields = cx.struct_fields(did, substs);
-                    find_nonrepresentable(cx, sp, seen, fields.iter().map(|f| f.mt.ty))
-                }
-                TyEnum(did, substs) => {
-                    let vs = cx.enum_variants(did);
-                    let iter = vs.iter()
-                        .flat_map(|variant| &variant.args)
-                        .map(|aty| { aty.subst_spanned(cx, substs, Some(sp)) });
-
-                    find_nonrepresentable(cx, sp, seen, iter)
+                TyStruct(def, substs) | TyEnum(def, substs) => {
+                    find_nonrepresentable(cx,
+                                          sp,
+                                          seen,
+                                          def.all_fields().map(|f| f.ty(cx, substs)))
                 }
                 TyClosure(..) => {
                     // this check is run on type definitions, so we don't expect
@@ -4597,10 +4833,10 @@ impl<'tcx> TyS<'tcx> {
             }
         }
 
-        fn same_struct_or_enum_def_id(ty: Ty, did: DefId) -> bool {
+        fn same_struct_or_enum<'tcx>(ty: Ty<'tcx>, def: AdtDef<'tcx>) -> bool {
             match ty.sty {
-                TyStruct(ty_did, _) | TyEnum(ty_did, _) => {
-                     ty_did == did
+                TyStruct(ty_def, _) | TyEnum(ty_def, _) => {
+                     ty_def == def
                 }
                 _ => false
             }
@@ -4635,7 +4871,7 @@ impl<'tcx> TyS<'tcx> {
             debug!("is_type_structurally_recursive: {:?}", ty);
 
             match ty.sty {
-                TyStruct(did, _) | TyEnum(did, _) => {
+                TyStruct(def, _) | TyEnum(def, _) => {
                     {
                         // Iterate through stack of previously seen types.
                         let mut iter = seen.iter();
@@ -4650,7 +4886,7 @@ impl<'tcx> TyS<'tcx> {
 
                         match iter.next() {
                             Some(&seen_type) => {
-                                if same_struct_or_enum_def_id(seen_type, did) {
+                                if same_struct_or_enum(seen_type, def) {
                                     debug!("SelfRecursive: {:?} contains {:?}",
                                            seen_type,
                                            ty);
@@ -4782,22 +5018,6 @@ impl<'tcx> TyS<'tcx> {
         }
     }
 
-    // Whether a type is enum like, that is an enum type with only nullary
-    // constructors
-    pub fn is_c_like_enum(&self, cx: &ctxt) -> bool {
-        match self.sty {
-            TyEnum(did, _) => {
-                let variants = cx.enum_variants(did);
-                if variants.is_empty() {
-                    false
-                } else {
-                    variants.iter().all(|v| v.args.is_empty())
-                }
-            }
-            _ => false
-        }
-    }
-
     // Returns the type and mutability of *ty.
     //
     // The parameter `explicit` indicates if this is an *explicit* dereference.
@@ -4959,7 +5179,7 @@ impl<'tcx> TyS<'tcx> {
             TyUint(_) | TyFloat(_) | TyStr => self.to_string(),
             TyTuple(ref tys) if tys.is_empty() => self.to_string(),
 
-            TyEnum(id, _) => format!("enum `{}`", cx.item_path_str(id)),
+            TyEnum(def, _) => format!("enum `{}`", cx.item_path_str(def.did)),
             TyBox(_) => "box".to_string(),
             TyArray(_, n) => format!("array of {} elements", n),
             TySlice(_) => "slice".to_string(),
@@ -4970,8 +5190,8 @@ impl<'tcx> TyS<'tcx> {
             TyTrait(ref inner) => {
                 format!("trait {}", cx.item_path_str(inner.principal_def_id()))
             }
-            TyStruct(id, _) => {
-                format!("struct `{}`", cx.item_path_str(id))
+            TyStruct(def, _) => {
+                format!("struct `{}`", cx.item_path_str(def.did))
             }
             TyClosure(..) => "closure".to_string(),
             TyTuple(_) => "tuple".to_string(),
@@ -5196,27 +5416,18 @@ impl<'tcx> ctxt<'tcx> {
                                  ty: Ty<'tcx>,
                                  i: usize,
                                  variant: Option<ast::DefId>) -> Option<Ty<'tcx>> {
-
         match (&ty.sty, variant) {
-            (&TyTuple(ref v), None) => v.get(i).cloned(),
-
-
-            (&TyStruct(def_id, substs), None) => self.lookup_struct_fields(def_id)
-                .get(i)
-                .map(|&t| self.lookup_item_type(t.id).ty.subst(self, substs)),
-
-            (&TyEnum(def_id, substs), Some(variant_def_id)) => {
-                let variant_info = self.enum_variant_with_id(def_id, variant_def_id);
-                variant_info.args.get(i).map(|t|t.subst(self, substs))
+            (&TyStruct(def, substs), None) => {
+                def.struct_variant().fields.get(i).map(|f| f.ty(self, substs))
             }
-
-            (&TyEnum(def_id, substs), None) => {
-                assert!(self.enum_is_univariant(def_id));
-                let enum_variants = self.enum_variants(def_id);
-                let variant_info = &enum_variants[0];
-                variant_info.args.get(i).map(|t|t.subst(self, substs))
+            (&TyEnum(def, substs), Some(vid)) => {
+                def.variant_with_id(vid).fields.get(i).map(|f| f.ty(self, substs))
             }
-
+            (&TyEnum(def, substs), None) => {
+                assert!(def.is_univariant());
+                def.variants[0].fields.get(i).map(|f| f.ty(self, substs))
+            }
+            (&TyTuple(ref v), None) => v.get(i).cloned(),
             _ => None
         }
     }
@@ -5227,22 +5438,14 @@ impl<'tcx> ctxt<'tcx> {
                             ty: Ty<'tcx>,
                             n: ast::Name,
                             variant: Option<ast::DefId>) -> Option<Ty<'tcx>> {
-
         match (&ty.sty, variant) {
-            (&TyStruct(def_id, substs), None) => {
-                let r = self.lookup_struct_fields(def_id);
-                r.iter().find(|f| f.name == n)
-                    .map(|&f| self.lookup_field_type(def_id, f.id, substs))
-            }
-            (&TyEnum(def_id, substs), Some(variant_def_id)) => {
-                let variant_info = self.enum_variant_with_id(def_id, variant_def_id);
-                variant_info.arg_names.as_ref()
-                    .expect("must have struct enum variant if accessing a named fields")
-                    .iter().zip(&variant_info.args)
-                    .find(|&(&name, _)| name == n)
-                    .map(|(_name, arg_t)| arg_t.subst(self, substs))
+            (&TyStruct(def, substs), None) => {
+                def.struct_variant().find_field_named(n).map(|f| f.ty(self, substs))
             }
-            _ => None
+            (&TyEnum(def, substs), Some(vid)) => {
+                def.variant_with_id(vid).find_field_named(n).map(|f| f.ty(self, substs))
+            }
+            _ => return None
         }
     }
 
@@ -5423,18 +5626,6 @@ impl<'tcx> ctxt<'tcx> {
         }
     }
 
-    pub fn field_idx_strict(&self, name: ast::Name, fields: &[Field<'tcx>])
-                            -> usize {
-        let mut i = 0;
-        for f in fields { if f.name == name { return i; } i += 1; }
-        self.sess.bug(&format!(
-            "no field named `{}` found in the list of fields `{:?}`",
-            name,
-            fields.iter()
-                  .map(|f| f.name.to_string())
-                  .collect::<Vec<String>>()));
-    }
-
     pub fn note_and_explain_type_err(&self, err: &TypeError<'tcx>, sp: Span) {
         use self::TypeError::*;
 
@@ -5710,24 +5901,6 @@ impl<'tcx> ctxt<'tcx> {
         }
     }
 
-    pub fn substd_enum_variants(&self,
-                                id: ast::DefId,
-                                substs: &Substs<'tcx>)
-                                -> Vec<Rc<VariantInfo<'tcx>>> {
-        self.enum_variants(id).iter().map(|variant_info| {
-            let substd_args = variant_info.args.iter()
-                .map(|aty| aty.subst(self, substs)).collect::<Vec<_>>();
-
-            let substd_ctor_ty = variant_info.ctor_ty.subst(self, substs);
-
-            Rc::new(VariantInfo {
-                args: substd_args,
-                ctor_ty: substd_ctor_ty,
-                ..(**variant_info).clone()
-            })
-        }).collect()
-    }
-
     pub fn item_path_str(&self, id: ast::DefId) -> String {
         self.with_path(id, |path| ast_map::path_to_string(path))
     }
@@ -5744,10 +5917,6 @@ impl<'tcx> ctxt<'tcx> {
         }
     }
 
-    pub fn has_dtor(&self, struct_id: DefId) -> bool {
-        self.destructor_for_type.borrow().contains_key(&struct_id)
-    }
-
     pub fn with_path<T, F>(&self, id: ast::DefId, f: F) -> T where
         F: FnOnce(ast_map::PathElems) -> T,
     {
@@ -5758,10 +5927,6 @@ impl<'tcx> ctxt<'tcx> {
         }
     }
 
-    pub fn enum_is_univariant(&self, id: ast::DefId) -> bool {
-        self.enum_variants(id).len() == 1
-    }
-
     /// Returns `(normalized_type, ty)`, where `normalized_type` is the
     /// IntType representation of one of {i64,i32,i16,i8,u64,u32,u16,u8},
     /// and `ty` is the original type (i.e. may include `isize` or
@@ -5790,133 +5955,6 @@ impl<'tcx> ctxt<'tcx> {
         (repr_type, repr_type_ty)
     }
 
-    fn report_discrim_overflow(&self,
-                               variant_span: Span,
-                               variant_name: &str,
-                               repr_type: attr::IntType,
-                               prev_val: Disr) {
-        let computed_value = repr_type.disr_wrap_incr(Some(prev_val));
-        let computed_value = repr_type.disr_string(computed_value);
-        let prev_val = repr_type.disr_string(prev_val);
-        let repr_type = repr_type.to_ty(self);
-        span_err!(self.sess, variant_span, E0370,
-                  "enum discriminant overflowed on value after {}: {}; \
-                   set explicitly via {} = {} if that is desired outcome",
-                  prev_val, repr_type, variant_name, computed_value);
-    }
-
-    // This computes the discriminant values for the sequence of Variants
-    // attached to a particular enum, taking into account the #[repr] (if
-    // any) provided via the `opt_hint`.
-    fn compute_enum_variants(&self,
-                             vs: &'tcx [P<ast::Variant>],
-                             opt_hint: Option<&attr::ReprAttr>)
-                             -> Vec<Rc<ty::VariantInfo<'tcx>>> {
-        let mut variants: Vec<Rc<ty::VariantInfo>> = Vec::new();
-        let mut prev_disr_val: Option<ty::Disr> = None;
-
-        let (repr_type, repr_type_ty) = self.enum_repr_type(opt_hint);
-
-        for v in vs {
-            // If the discriminant value is specified explicitly in the
-            // enum, check whether the initialization expression is valid,
-            // otherwise use the last value plus one.
-            let current_disr_val;
-
-            // This closure marks cases where, when an error occurs during
-            // the computation, attempt to assign a (hopefully) fresh
-            // value to avoid spurious error reports downstream.
-            let attempt_fresh_value = move || -> Disr {
-                repr_type.disr_wrap_incr(prev_disr_val)
-            };
-
-            match v.node.disr_expr {
-                Some(ref e) => {
-                    debug!("disr expr, checking {}", pprust::expr_to_string(&**e));
-
-                    let hint = UncheckedExprHint(repr_type_ty);
-                    match const_eval::eval_const_expr_partial(self, &**e, hint) {
-                        Ok(ConstVal::Int(val)) => current_disr_val = val as Disr,
-                        Ok(ConstVal::Uint(val)) => current_disr_val = val as Disr,
-                        Ok(_) => {
-                            let sign_desc = if repr_type.is_signed() {
-                                "signed"
-                            } else {
-                                "unsigned"
-                            };
-                            span_err!(self.sess, e.span, E0079,
-                                      "expected {} integer constant",
-                                      sign_desc);
-                            current_disr_val = attempt_fresh_value();
-                        },
-                        Err(ref err) => {
-                            span_err!(self.sess, err.span, E0080,
-                                      "constant evaluation error: {}",
-                                      err.description());
-                            current_disr_val = attempt_fresh_value();
-                        },
-                    }
-                },
-                None => {
-                    current_disr_val = match prev_disr_val {
-                        Some(prev_disr_val) => {
-                            if let Some(v) = repr_type.disr_incr(prev_disr_val) {
-                                v
-                            } else {
-                                self.report_discrim_overflow(v.span, &v.node.name.name.as_str(),
-                                                             repr_type, prev_disr_val);
-                                attempt_fresh_value()
-                            }
-                        }
-                        None => ty::INITIAL_DISCRIMINANT_VALUE,
-                    }
-                },
-            }
-
-            let variant_info = Rc::new(VariantInfo::from_ast_variant(self, &**v, current_disr_val));
-            prev_disr_val = Some(current_disr_val);
-
-            variants.push(variant_info);
-        }
-
-        variants
-    }
-
-    pub fn enum_variants(&self, id: ast::DefId) -> Rc<Vec<Rc<VariantInfo<'tcx>>>> {
-        memoized(&self.enum_var_cache, id, |id: ast::DefId| {
-            if ast::LOCAL_CRATE != id.krate {
-                Rc::new(csearch::get_enum_variants(self, id))
-            } else {
-                match self.map.get(id.node) {
-                    ast_map::NodeItem(ref item) => {
-                        match item.node {
-                            ast::ItemEnum(ref enum_definition, _) => {
-                                Rc::new(self.compute_enum_variants(
-                                    &enum_definition.variants,
-                                    self.lookup_repr_hints(id).get(0)))
-                            }
-                            _ => {
-                                self.sess.bug("enum_variants: id not bound to an enum")
-                            }
-                        }
-                    }
-                    _ => self.sess.bug("enum_variants: id not bound to an enum")
-                }
-            }
-        })
-    }
-
-    // Returns information about the enum variant with the given ID:
-    pub fn enum_variant_with_id(&self,
-                                enum_id: ast::DefId,
-                                variant_id: ast::DefId)
-                                -> Rc<VariantInfo<'tcx>> {
-        self.enum_variants(enum_id).iter()
-                                   .find(|variant| variant.id == variant_id)
-                                   .expect("enum_variant_with_id(): no variant exists with that ID")
-                                   .clone()
-    }
-
     // Register a given item type
     pub fn register_item_type(&self, did: ast::DefId, ty: TypeScheme<'tcx>) {
         self.tcache.borrow_mut().insert(did, ty);
@@ -5938,6 +5976,23 @@ impl<'tcx> ctxt<'tcx> {
         )
     }
 
+    /// Given the did of an ADT, return a master reference to its
+    /// definition. Unless you are planning on fulfilling the ADT's fields,
+    /// use lookup_adt_def instead.
+    pub fn lookup_adt_def_master(&self, did: ast::DefId) -> AdtDefMaster<'tcx> {
+        lookup_locally_or_in_crate_store(
+            "adt_defs", did, &self.adt_defs,
+            || csearch::get_adt_def(self, did)
+        )
+    }
+
+    /// Given the did of an ADT, return a reference to its definition.
+    pub fn lookup_adt_def(&self, did: ast::DefId) -> AdtDef<'tcx> {
+        // when reverse-variance goes away, a transmute::<AdtDefMaster,AdtDef>
+        // woud be needed here.
+        self.lookup_adt_def_master(did)
+    }
+
     /// Given the did of an item, returns its full set of predicates.
     pub fn lookup_predicates(&self, did: ast::DefId) -> GenericPredicates<'tcx> {
         lookup_locally_or_in_crate_store(
@@ -5989,75 +6044,13 @@ impl<'tcx> ctxt<'tcx> {
         })
     }
 
-    // Look up a field ID, whether or not it's local
-    pub fn lookup_field_type_unsubstituted(&self,
-                                           struct_id: DefId,
-                                           id: DefId)
-                                           -> Ty<'tcx> {
-        if id.krate == ast::LOCAL_CRATE {
-            self.node_id_to_type(id.node)
-        } else {
-            memoized(&self.tcache, id,
-                     |id| csearch::get_field_type(self, struct_id, id)).ty
-        }
-    }
-
-
-    // Look up a field ID, whether or not it's local
-    // Takes a list of type substs in case the struct is generic
-    pub fn lookup_field_type(&self,
-                             struct_id: DefId,
-                             id: DefId,
-                             substs: &Substs<'tcx>)
-                             -> Ty<'tcx> {
-        self.lookup_field_type_unsubstituted(struct_id, id).subst(self, substs)
-    }
-
-    // Look up the list of field names and IDs for a given struct.
-    // Panics if the id is not bound to a struct.
-    pub fn lookup_struct_fields(&self, did: ast::DefId) -> Vec<FieldTy> {
-        if did.krate == ast::LOCAL_CRATE {
-            let struct_fields = self.struct_fields.borrow();
-            match struct_fields.get(&did) {
-                Some(fields) => (**fields).clone(),
-                _ => {
-                    self.sess.bug(
-                        &format!("ID not mapped to struct fields: {}",
-                                self.map.node_to_string(did.node)));
-                }
-            }
-        } else {
-            csearch::get_struct_fields(&self.sess.cstore, did)
-        }
-    }
-
-    pub fn is_tuple_struct(&self, did: ast::DefId) -> bool {
-        let fields = self.lookup_struct_fields(did);
-        !fields.is_empty() && fields.iter().all(|f| f.name == token::special_names::unnamed_field)
-    }
-
-    // Returns a list of fields corresponding to the struct's items. trans uses
-    // this. Takes a list of substs with which to instantiate field types.
-    pub fn struct_fields(&self, did: ast::DefId, substs: &Substs<'tcx>)
-                         -> Vec<Field<'tcx>> {
-        self.lookup_struct_fields(did).iter().map(|f| {
-           Field {
-                name: f.name,
-                mt: TypeAndMut {
-                    ty: self.lookup_field_type(did, f.id, substs),
-                    mutbl: MutImmutable
-                }
-            }
-        }).collect()
-    }
-
     /// Returns the deeply last field of nested structures, or the same type,
     /// if not a structure at all. Corresponds to the only possible unsized
     /// field, and its type can be used to determine unsizing strategy.
     pub fn struct_tail(&self, mut ty: Ty<'tcx>) -> Ty<'tcx> {
-        while let TyStruct(def_id, substs) = ty.sty {
-            match self.struct_fields(def_id, substs).last() {
-                Some(f) => ty = f.mt.ty,
+        while let TyStruct(def, substs) = ty.sty {
+            match def.struct_variant().fields.last() {
+                Some(f) => ty = f.ty(self, substs),
                 None => break
             }
         }
@@ -6074,17 +6067,13 @@ impl<'tcx> ctxt<'tcx> {
                                  target: Ty<'tcx>)
                                  -> (Ty<'tcx>, Ty<'tcx>) {
         let (mut a, mut b) = (source, target);
-        while let (&TyStruct(a_did, a_substs), &TyStruct(b_did, b_substs)) = (&a.sty, &b.sty) {
-            if a_did != b_did {
+        while let (&TyStruct(a_def, a_substs), &TyStruct(b_def, b_substs)) = (&a.sty, &b.sty) {
+            if a_def != b_def {
                 break;
             }
-            if let Some(a_f) = self.struct_fields(a_did, a_substs).last() {
-                if let Some(b_f) = self.struct_fields(b_did, b_substs).last() {
-                    a = a_f.mt.ty;
-                    b = b_f.mt.ty;
-                } else {
-                    break;
-                }
+            if let Some(f) = a_def.struct_variant().fields.last() {
+                a = f.ty(self, a_substs);
+                b = f.ty(self, b_substs);
             } else {
                 break;
             }
@@ -6452,7 +6441,7 @@ impl<'tcx> ctxt<'tcx> {
                     }
                     TyEnum(d, _) => {
                         byte!(8);
-                        did(state, d);
+                        did(state, d.did);
                     }
                     TyBox(_) => {
                         byte!(9);
@@ -6495,7 +6484,7 @@ impl<'tcx> ctxt<'tcx> {
                     }
                     TyStruct(d, _) => {
                         byte!(18);
-                        did(state, d);
+                        did(state, d.did);
                     }
                     TyTuple(ref inner) => {
                         byte!(19);
@@ -6633,6 +6622,85 @@ impl<'tcx> ctxt<'tcx> {
     pub fn upvar_capture(&self, upvar_id: ty::UpvarId) -> Option<ty::UpvarCapture> {
         Some(self.tables.borrow().upvar_capture_map.get(&upvar_id).unwrap().clone())
     }
+
+
+    /// Returns true if this ADT is a dtorck type, i.e. whether it being
+    /// safe for destruction requires it to be alive
+    fn is_adt_dtorck(&self, adt: AdtDef<'tcx>) -> bool {
+        let dtor_method = match self.destructor_for_type.borrow().get(&adt.did) {
+            Some(dtor) => *dtor,
+            None => return false
+        };
+        let impl_did = self.impl_of_method(dtor_method).unwrap_or_else(|| {
+            self.sess.bug(&format!("no Drop impl for the dtor of `{:?}`", adt))
+        });
+        let generics = adt.type_scheme(self).generics;
+
+        // In `impl<'a> Drop ...`, we automatically assume
+        // `'a` is meaningful and thus represents a bound
+        // through which we could reach borrowed data.
+        //
+        // FIXME (pnkfelix): In the future it would be good to
+        // extend the language to allow the user to express,
+        // in the impl signature, that a lifetime is not
+        // actually used (something like `where 'a: ?Live`).
+        if generics.has_region_params(subst::TypeSpace) {
+            debug!("typ: {:?} has interesting dtor due to region params",
+                   adt);
+            return true;
+        }
+
+        let mut seen_items = Vec::new();
+        let mut items_to_inspect = vec![impl_did];
+        while let Some(item_def_id) = items_to_inspect.pop() {
+            if seen_items.contains(&item_def_id) {
+                continue;
+            }
+
+            for pred in self.lookup_predicates(item_def_id).predicates {
+                let result = match pred {
+                    ty::Predicate::Equate(..) |
+                    ty::Predicate::RegionOutlives(..) |
+                    ty::Predicate::TypeOutlives(..) |
+                    ty::Predicate::Projection(..) => {
+                        // For now, assume all these where-clauses
+                        // may give drop implementation capabilty
+                        // to access borrowed data.
+                        true
+                    }
+
+                    ty::Predicate::Trait(ty::Binder(ref t_pred)) => {
+                        let def_id = t_pred.trait_ref.def_id;
+                        if self.trait_items(def_id).len() != 0 {
+                            // If trait has items, assume it adds
+                            // capability to access borrowed data.
+                            true
+                        } else {
+                            // Trait without items is itself
+                            // uninteresting from POV of dropck.
+                            //
+                            // However, may have parent w/ items;
+                            // so schedule checking of predicates,
+                            items_to_inspect.push(def_id);
+                            // and say "no capability found" for now.
+                            false
+                        }
+                    }
+                };
+
+                if result {
+                    debug!("typ: {:?} has interesting dtor due to generic preds, e.g. {:?}",
+                           adt, pred);
+                    return true;
+                }
+            }
+
+            seen_items.push(item_def_id);
+        }
+
+        debug!("typ: {:?} is dtorck-safe", adt);
+        false
+    }
 }
 
 /// The category of explicit self.
@@ -7228,12 +7296,6 @@ impl<'tcx> HasTypeFlags for FnSig<'tcx> {
     }
 }
 
-impl<'tcx> HasTypeFlags for Field<'tcx> {
-    fn has_type_flags(&self, flags: TypeFlags) -> bool {
-        self.mt.ty.has_type_flags(flags)
-    }
-}
-
 impl<'tcx> HasTypeFlags for BareFnTy<'tcx> {
     fn has_type_flags(&self, flags: TypeFlags) -> bool {
         self.sig.has_type_flags(flags)
@@ -7264,12 +7326,6 @@ impl<'tcx> fmt::Debug for ClosureUpvar<'tcx> {
     }
 }
 
-impl<'tcx> fmt::Debug for Field<'tcx> {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        write!(f, "field({},{})", self.name, self.mt)
-    }
-}
-
 impl<'a, 'tcx> fmt::Debug for ParameterEnvironment<'a, 'tcx> {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         write!(f, "ParameterEnvironment(\
diff --git a/src/librustc/middle/ty_fold.rs b/src/librustc/middle/ty_fold.rs
index 0c694926ba4..cc1efeaea08 100644
--- a/src/librustc/middle/ty_fold.rs
+++ b/src/librustc/middle/ty_fold.rs
@@ -275,15 +275,6 @@ impl<'tcx> TypeFoldable<'tcx> for ty::TraitRef<'tcx> {
     }
 }
 
-impl<'tcx> TypeFoldable<'tcx> for ty::Field<'tcx> {
-    fn fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> ty::Field<'tcx> {
-        ty::Field {
-            name: self.name,
-            mt: self.mt.fold_with(folder),
-        }
-    }
-}
-
 impl<'tcx> TypeFoldable<'tcx> for ty::Region {
     fn fold_with<F: TypeFolder<'tcx>>(&self, folder: &mut F) -> ty::Region {
         folder.fold_region(*self)
diff --git a/src/librustc/middle/ty_relate/mod.rs b/src/librustc/middle/ty_relate/mod.rs
index f8678b4d8e3..05b08b356ef 100644
--- a/src/librustc/middle/ty_relate/mod.rs
+++ b/src/librustc/middle/ty_relate/mod.rs
@@ -485,11 +485,11 @@ pub fn super_relate_tys<'a,'tcx:'a,R>(relation: &mut R,
             Ok(a)
         }
 
-        (&ty::TyEnum(a_id, a_substs), &ty::TyEnum(b_id, b_substs))
-            if a_id == b_id =>
+        (&ty::TyEnum(a_def, a_substs), &ty::TyEnum(b_def, b_substs))
+            if a_def == b_def =>
         {
-            let substs = try!(relate_item_substs(relation, a_id, a_substs, b_substs));
-            Ok(tcx.mk_enum(a_id, tcx.mk_substs(substs)))
+            let substs = try!(relate_item_substs(relation, a_def.did, a_substs, b_substs));
+            Ok(tcx.mk_enum(a_def, tcx.mk_substs(substs)))
         }
 
         (&ty::TyTrait(ref a_), &ty::TyTrait(ref b_)) =>
@@ -499,11 +499,11 @@ pub fn super_relate_tys<'a,'tcx:'a,R>(relation: &mut R,
             Ok(tcx.mk_trait(principal, bounds))
         }
 
-        (&ty::TyStruct(a_id, a_substs), &ty::TyStruct(b_id, b_substs))
-            if a_id == b_id =>
+        (&ty::TyStruct(a_def, a_substs), &ty::TyStruct(b_def, b_substs))
+            if a_def == b_def =>
         {
-            let substs = try!(relate_item_substs(relation, a_id, a_substs, b_substs));
-            Ok(tcx.mk_struct(a_id, tcx.mk_substs(substs)))
+            let substs = try!(relate_item_substs(relation, a_def.did, a_substs, b_substs));
+            Ok(tcx.mk_struct(a_def, tcx.mk_substs(substs)))
         }
 
         (&ty::TyClosure(a_id, ref a_substs),
diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs
index d215813b740..da20f730bab 100644
--- a/src/librustc/util/ppaux.rs
+++ b/src/librustc/util/ppaux.rs
@@ -366,6 +366,14 @@ impl<'tcx> fmt::Debug for ty::TraitDef<'tcx> {
     }
 }
 
+impl<'tcx, 'container> fmt::Debug for ty::AdtDefData<'tcx, 'container> {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        ty::tls::with(|tcx| {
+            write!(f, "{}", tcx.item_path_str(self.did))
+        })
+    }
+}
+
 impl fmt::Display for ty::BoundRegion {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         if verbose() {
@@ -648,14 +656,14 @@ impl<'tcx> fmt::Display for ty::TypeVariants<'tcx> {
             TyInfer(infer_ty) => write!(f, "{}", infer_ty),
             TyError => write!(f, "[type error]"),
             TyParam(ref param_ty) => write!(f, "{}", param_ty),
-            TyEnum(did, substs) | TyStruct(did, substs) => {
+            TyEnum(def, substs) | TyStruct(def, substs) => {
                 ty::tls::with(|tcx| {
-                    if did.krate == ast::LOCAL_CRATE &&
-                          !tcx.tcache.borrow().contains_key(&did) {
-                        write!(f, "{}<..>", tcx.item_path_str(did))
+                    if def.did.krate == ast::LOCAL_CRATE &&
+                          !tcx.tcache.borrow().contains_key(&def.did) {
+                        write!(f, "{}<..>", tcx.item_path_str(def.did))
                     } else {
-                        parameterized(f, substs, did, &[],
-                                      |tcx| tcx.lookup_item_type(did).generics)
+                        parameterized(f, substs, def.did, &[],
+                                      |tcx| tcx.lookup_item_type(def.did).generics)
                     }
                 })
             }
diff --git a/src/librustc_borrowck/borrowck/check_loans.rs b/src/librustc_borrowck/borrowck/check_loans.rs
index 8d7c964c7b2..62cb1f73cf8 100644
--- a/src/librustc_borrowck/borrowck/check_loans.rs
+++ b/src/librustc_borrowck/borrowck/check_loans.rs
@@ -746,24 +746,22 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> {
             }
             LpExtend(ref lp_base, _, LpInterior(InteriorField(_))) => {
                 match lp_base.to_type().sty {
-                    ty::TyStruct(def_id, _) | ty::TyEnum(def_id, _) => {
-                        if self.tcx().has_dtor(def_id) {
-                            // In the case where the owner implements drop, then
-                            // the path must be initialized to prevent a case of
-                            // partial reinitialization
-                            //
-                            // FIXME (22079): could refactor via hypothetical
-                            // generalized check_if_path_is_moved
-                            let loan_path = owned_ptr_base_path_rc(lp_base);
-                            self.move_data.each_move_of(id, &loan_path, |_, _| {
-                                self.bccx
-                                    .report_partial_reinitialization_of_uninitialized_structure(
-                                        span,
-                                        &*loan_path);
-                                false
-                            });
-                            return;
-                        }
+                    ty::TyStruct(def, _) | ty::TyEnum(def, _) if def.has_dtor(self.tcx()) => {
+                        // In the case where the owner implements drop, then
+                        // the path must be initialized to prevent a case of
+                        // partial reinitialization
+                        //
+                        // FIXME (22079): could refactor via hypothetical
+                        // generalized check_if_path_is_moved
+                        let loan_path = owned_ptr_base_path_rc(lp_base);
+                        self.move_data.each_move_of(id, &loan_path, |_, _| {
+                            self.bccx
+                                .report_partial_reinitialization_of_uninitialized_structure(
+                                    span,
+                                    &*loan_path);
+                            false
+                        });
+                        return;
                     },
                     _ => {},
                 }
diff --git a/src/librustc_borrowck/borrowck/fragments.rs b/src/librustc_borrowck/borrowck/fragments.rs
index 864bd7190a3..e30b85919e2 100644
--- a/src/librustc_borrowck/borrowck/fragments.rs
+++ b/src/librustc_borrowck/borrowck/fragments.rs
@@ -438,11 +438,10 @@ fn add_fragment_siblings_for_extension<'tcx>(this: &MoveData<'tcx>,
             }
         }
 
-        (&ty::TyStruct(def_id, ref _substs), None) => {
-            let fields = tcx.lookup_struct_fields(def_id);
+        (&ty::TyStruct(def, _), None) => {
             match *origin_field_name {
                 mc::NamedField(ast_name) => {
-                    for f in &fields {
+                    for f in &def.struct_variant().fields {
                         if f.name == ast_name {
                             continue;
                         }
@@ -451,7 +450,7 @@ fn add_fragment_siblings_for_extension<'tcx>(this: &MoveData<'tcx>,
                     }
                 }
                 mc::PositionalField(tuple_idx) => {
-                    for (i, _f) in fields.iter().enumerate() {
+                    for (i, _f) in def.struct_variant().fields.iter().enumerate() {
                         if i == tuple_idx {
                             continue
                         }
@@ -462,35 +461,26 @@ fn add_fragment_siblings_for_extension<'tcx>(this: &MoveData<'tcx>,
             }
         }
 
-        (&ty::TyEnum(enum_def_id, substs), ref enum_variant_info) => {
-            let variant_info = {
-                let mut variants = tcx.substd_enum_variants(enum_def_id, substs);
-                match *enum_variant_info {
-                    Some((variant_def_id, ref _lp2)) =>
-                        variants.iter()
-                        .find(|variant| variant.id == variant_def_id)
-                        .expect("enum_variant_with_id(): no variant exists with that ID")
-                        .clone(),
-                    None => {
-                        assert_eq!(variants.len(), 1);
-                        variants.pop().unwrap()
-                    }
+        (&ty::TyEnum(def, _), ref enum_variant_info) => {
+            let variant = match *enum_variant_info {
+                Some((vid, ref _lp2)) => def.variant_with_id(vid),
+                None => {
+                    assert!(def.is_univariant());
+                    &def.variants[0]
                 }
             };
             match *origin_field_name {
                 mc::NamedField(ast_name) => {
-                    let variant_arg_names = variant_info.arg_names.as_ref().unwrap();
-                    for &variant_arg_name in variant_arg_names {
-                        if variant_arg_name == ast_name {
+                    for field in &variant.fields {
+                        if field.name == ast_name {
                             continue;
                         }
-                        let field_name = mc::NamedField(variant_arg_name);
-                        add_fragment_sibling_local(field_name, Some(variant_info.id));
+                        let field_name = mc::NamedField(field.name);
+                        add_fragment_sibling_local(field_name, Some(variant.did));
                     }
                 }
                 mc::PositionalField(tuple_idx) => {
-                    let variant_arg_types = &variant_info.args;
-                    for (i, _variant_arg_ty) in variant_arg_types.iter().enumerate() {
+                    for (i, _f) in variant.fields.iter().enumerate() {
                         if tuple_idx == i {
                             continue;
                         }
diff --git a/src/librustc_borrowck/borrowck/gather_loans/gather_moves.rs b/src/librustc_borrowck/borrowck/gather_loans/gather_moves.rs
index 2b33dde2cbe..308ae42c16f 100644
--- a/src/librustc_borrowck/borrowck/gather_loans/gather_moves.rs
+++ b/src/librustc_borrowck/borrowck/gather_loans/gather_moves.rs
@@ -179,8 +179,8 @@ fn check_and_get_illegal_move_origin<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
         mc::cat_interior(ref b, mc::InteriorField(_)) |
         mc::cat_interior(ref b, mc::InteriorElement(Kind::Pattern, _)) => {
             match b.ty.sty {
-                ty::TyStruct(did, _) | ty::TyEnum(did, _) => {
-                    if bccx.tcx.has_dtor(did) {
+                ty::TyStruct(def, _) | ty::TyEnum(def, _) => {
+                    if def.has_dtor(bccx.tcx) {
                         Some(cmt.clone())
                     } else {
                         check_and_get_illegal_move_origin(bccx, b)
diff --git a/src/librustc_borrowck/borrowck/gather_loans/move_error.rs b/src/librustc_borrowck/borrowck/gather_loans/move_error.rs
index 5baabebea11..1246449327d 100644
--- a/src/librustc_borrowck/borrowck/gather_loans/move_error.rs
+++ b/src/librustc_borrowck/borrowck/gather_loans/move_error.rs
@@ -136,8 +136,8 @@ fn report_cannot_move_out_of<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>,
         mc::cat_downcast(ref b, _) |
         mc::cat_interior(ref b, mc::InteriorField(_)) => {
             match b.ty.sty {
-                ty::TyStruct(did, _) |
-                ty::TyEnum(did, _) if bccx.tcx.has_dtor(did) => {
+                ty::TyStruct(def, _) |
+                ty::TyEnum(def, _) if def.has_dtor(bccx.tcx) => {
                     bccx.span_err(
                         move_from.span,
                         &format!("cannot move out of type `{}`, \
diff --git a/src/librustc_data_structures/ivar.rs b/src/librustc_data_structures/ivar.rs
new file mode 100644
index 00000000000..dabe1b984df
--- /dev/null
+++ b/src/librustc_data_structures/ivar.rs
@@ -0,0 +1,74 @@
+// Copyright 2015 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.
+
+use std::fmt;
+use std::cell::Cell;
+
+/// A write-once variable. When constructed, it is empty, and
+/// can only be set once.
+///
+/// Ivars ensure that data that can only be initialised once. A full
+/// implementation is used for concurrency and blocks on a read of an
+/// unfulfilled value. This implementation is more minimal and panics
+/// if you attempt to read the value before it has been set. It is also
+/// not `Sync`, but may be extended in the future to be usable as a true
+/// concurrency type.
+///
+/// The `T: Copy` bound is not strictly needed, but it is required by
+/// Cell (so removing it would require using UnsafeCell), and it
+/// suffices for the current purposes.
+#[derive(PartialEq)]
+pub struct Ivar<T: Copy> {
+    data: Cell<Option<T>>
+}
+
+impl<T: Copy> Ivar<T> {
+    pub fn new() -> Ivar<T> {
+        Ivar {
+            data: Cell::new(None)
+        }
+    }
+
+    pub fn get(&self) -> Option<T> {
+        self.data.get()
+    }
+
+    pub fn fulfill(&self, value: T) {
+        assert!(self.data.get().is_none(),
+                "Value already set!");
+        self.data.set(Some(value));
+    }
+
+    pub fn is_fulfilled(&self) -> bool {
+        self.data.get().is_some()
+    }
+
+    pub fn unwrap(&self) -> T {
+        self.get().unwrap()
+    }
+}
+
+impl<T: Copy+fmt::Debug> fmt::Debug for Ivar<T> {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        match self.get() {
+            Some(val) => write!(f, "Ivar({:?})", val),
+            None => f.write_str("Ivar(<unfulfilled>)")
+        }
+    }
+}
+
+impl<T: Copy> Clone for Ivar<T> {
+    fn clone(&self) -> Ivar<T> {
+        match self.get() {
+            Some(val) => Ivar { data: Cell::new(Some(val)) },
+            None => Ivar::new()
+        }
+    }
+}
diff --git a/src/librustc_data_structures/lib.rs b/src/librustc_data_structures/lib.rs
index 1f8f7694ff9..558d15610df 100644
--- a/src/librustc_data_structures/lib.rs
+++ b/src/librustc_data_structures/lib.rs
@@ -36,4 +36,5 @@ extern crate serialize as rustc_serialize; // used by deriving
 pub mod snapshot_vec;
 pub mod graph;
 pub mod bitvec;
+pub mod ivar;
 pub mod unify;
diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs
index ffd09326abc..8f5519faf7a 100644
--- a/src/librustc_lint/builtin.rs
+++ b/src/librustc_lint/builtin.rs
@@ -45,7 +45,6 @@ use std::collections::{HashSet, BitSet};
 use std::collections::hash_map::Entry::{Occupied, Vacant};
 use std::{cmp, slice};
 use std::{i8, i16, i32, i64, u8, u16, u32, u64, f32, f64};
-use std::rc::Rc;
 
 use syntax::{abi, ast};
 use syntax::ast_util::{self, is_shift_binop, local_def};
@@ -413,18 +412,23 @@ enum FfiResult {
 /// to function pointers and references, but could be
 /// expanded to cover NonZero raw pointers and newtypes.
 /// FIXME: This duplicates code in trans.
-fn is_repr_nullable_ptr<'tcx>(variants: &Vec<Rc<ty::VariantInfo<'tcx>>>) -> bool {
-    if variants.len() == 2 {
-        let mut data_idx = 0;
-
-        if variants[0].args.is_empty() {
+fn is_repr_nullable_ptr<'tcx>(tcx: &ty::ctxt<'tcx>,
+                              def: ty::AdtDef<'tcx>,
+                              substs: &Substs<'tcx>)
+                              -> bool {
+    if def.variants.len() == 2 {
+        let data_idx;
+
+        if def.variants[0].fields.is_empty() {
             data_idx = 1;
-        } else if !variants[1].args.is_empty() {
+        } else if def.variants[1].fields.is_empty() {
+            data_idx = 0;
+        } else {
             return false;
         }
 
-        if variants[data_idx].args.len() == 1 {
-            match variants[data_idx].args[0].sty {
+        if def.variants[data_idx].fields.len() == 1 {
+            match def.variants[data_idx].fields[0].ty(tcx, substs).sty {
                 ty::TyBareFn(None, _) => { return true; }
                 ty::TyRef(..) => { return true; }
                 _ => { }
@@ -463,8 +467,8 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
         }
 
         match ty.sty {
-            ty::TyStruct(did, substs) => {
-                if !cx.lookup_repr_hints(did).contains(&attr::ReprExtern) {
+            ty::TyStruct(def, substs) => {
+                if !cx.lookup_repr_hints(def.did).contains(&attr::ReprExtern) {
                     return FfiUnsafe(
                         "found struct without foreign-function-safe \
                          representation annotation in foreign module, \
@@ -474,39 +478,36 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
 
                 // We can't completely trust repr(C) markings; make sure the
                 // fields are actually safe.
-                let fields = cx.struct_fields(did, substs);
-
-                if fields.is_empty() {
+                if def.struct_variant().fields.is_empty() {
                     return FfiUnsafe(
                         "found zero-size struct in foreign module, consider \
                          adding a member to this struct");
                 }
 
-                for field in fields {
-                    let field_ty = infer::normalize_associated_type(cx, &field.mt.ty);
+                for field in &def.struct_variant().fields {
+                    let field_ty = infer::normalize_associated_type(cx, &field.ty(cx, substs));
                     let r = self.check_type_for_ffi(cache, field_ty);
                     match r {
                         FfiSafe => {}
                         FfiBadStruct(..) | FfiBadEnum(..) => { return r; }
-                        FfiUnsafe(s) => { return FfiBadStruct(did, s); }
+                        FfiUnsafe(s) => { return FfiBadStruct(def.did, s); }
                     }
                 }
                 FfiSafe
             }
-            ty::TyEnum(did, substs) => {
-                let variants = cx.substd_enum_variants(did, substs);
-                if variants.is_empty() {
+            ty::TyEnum(def, substs) => {
+                if def.variants.is_empty() {
                     // Empty enums are okay... although sort of useless.
                     return FfiSafe
                 }
 
                 // Check for a repr() attribute to specify the size of the
                 // discriminant.
-                let repr_hints = cx.lookup_repr_hints(did);
+                let repr_hints = cx.lookup_repr_hints(def.did);
                 match &**repr_hints {
                     [] => {
                         // Special-case types like `Option<extern fn()>`.
-                        if !is_repr_nullable_ptr(&variants) {
+                        if !is_repr_nullable_ptr(cx, def, substs) {
                             return FfiUnsafe(
                                 "found enum without foreign-function-safe \
                                  representation annotation in foreign module, \
@@ -537,14 +538,14 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
                 }
 
                 // Check the contained variants.
-                for variant in variants {
-                    for arg in &variant.args {
-                        let arg = infer::normalize_associated_type(cx, arg);
+                for variant in &def.variants {
+                    for field in &variant.fields {
+                        let arg = infer::normalize_associated_type(cx, &field.ty(cx, substs));
                         let r = self.check_type_for_ffi(cache, arg);
                         match r {
                             FfiSafe => {}
                             FfiBadStruct(..) | FfiBadEnum(..) => { return r; }
-                            FfiUnsafe(s) => { return FfiBadEnum(did, s); }
+                            FfiUnsafe(s) => { return FfiBadEnum(def.did, s); }
                         }
                     }
                 }
@@ -842,8 +843,8 @@ impl LintPass for RawPointerDerive {
                 }
 
                 match cx.tcx.node_id_to_type(item.id).sty {
-                    ty::TyEnum(did, _) => did,
-                    ty::TyStruct(did, _) => did,
+                    ty::TyEnum(def, _) => def.did,
+                    ty::TyStruct(def, _) => def.did,
                     _ => return,
                 }
             }
@@ -989,16 +990,16 @@ impl LintPass for UnusedResults {
         let warned = match t.sty {
             ty::TyTuple(ref tys) if tys.is_empty() => return,
             ty::TyBool => return,
-            ty::TyStruct(did, _) |
-            ty::TyEnum(did, _) => {
-                if ast_util::is_local(did) {
-                    if let ast_map::NodeItem(it) = cx.tcx.map.get(did.node) {
+            ty::TyStruct(def, _) |
+            ty::TyEnum(def, _) => {
+                if ast_util::is_local(def.did) {
+                    if let ast_map::NodeItem(it) = cx.tcx.map.get(def.did.node) {
                         check_must_use(cx, &it.attrs, s.span)
                     } else {
                         false
                     }
                 } else {
-                    let attrs = csearch::get_item_attrs(&cx.sess().cstore, did);
+                    let attrs = csearch::get_item_attrs(&cx.sess().cstore, def.did);
                     check_must_use(cx, &attrs[..], s.span)
                 }
             }
@@ -1956,14 +1957,14 @@ impl LintPass for MissingCopyImplementations {
                 if ast_generics.is_parameterized() {
                     return;
                 }
-                cx.tcx.mk_struct(local_def(item.id),
+                cx.tcx.mk_struct(cx.tcx.lookup_adt_def(local_def(item.id)),
                                  cx.tcx.mk_substs(Substs::empty()))
             }
             ast::ItemEnum(_, ref ast_generics) => {
                 if ast_generics.is_parameterized() {
                     return;
                 }
-                cx.tcx.mk_enum(local_def(item.id),
+                cx.tcx.mk_enum(cx.tcx.lookup_adt_def(local_def(item.id)),
                                cx.tcx.mk_substs(Substs::empty()))
             }
             _ => return,
@@ -2575,9 +2576,9 @@ impl LintPass for DropWithReprExtern {
                 };
 
             match dtor_self_type.sty {
-                ty::TyEnum(self_type_did, _) |
-                ty::TyStruct(self_type_did, _) |
-                ty::TyClosure(self_type_did, _) => {
+                ty::TyEnum(self_type_def, _) |
+                ty::TyStruct(self_type_def, _) => {
+                    let self_type_did = self_type_def.did;
                     let hints = ctx.tcx.lookup_repr_hints(self_type_did);
                     if hints.iter().any(|attr| *attr == attr::ReprExtern) &&
                         ctx.tcx.ty_dtor(self_type_did).has_drop_flag() {
diff --git a/src/librustc_privacy/lib.rs b/src/librustc_privacy/lib.rs
index 2ffb4cbd4bf..d10dc2e05ff 100644
--- a/src/librustc_privacy/lib.rs
+++ b/src/librustc_privacy/lib.rs
@@ -34,7 +34,6 @@ use self::FieldName::*;
 use std::mem::replace;
 
 use rustc::ast_map;
-use rustc::metadata::csearch;
 use rustc::middle::def;
 use rustc::middle::privacy::ImportUse::*;
 use rustc::middle::privacy::LastPrivate::*;
@@ -688,28 +687,26 @@ impl<'a, 'tcx> PrivacyVisitor<'a, 'tcx> {
     // Checks that a field is in scope.
     fn check_field(&mut self,
                    span: Span,
-                   id: ast::DefId,
+                   def: ty::AdtDef<'tcx>,
+                   v: ty::VariantDef<'tcx>,
                    name: FieldName) {
-        let fields = self.tcx.lookup_struct_fields(id);
         let field = match name {
             NamedField(f_name) => {
-                debug!("privacy - check named field {} in struct {:?}", f_name, id);
-                fields.iter().find(|f| f.name == f_name).unwrap()
+                debug!("privacy - check named field {} in struct {:?}", f_name, def);
+                v.field_named(f_name)
             }
-            UnnamedField(idx) => &fields[idx]
+            UnnamedField(idx) => &v.fields[idx]
         };
         if field.vis == ast::Public ||
-            (is_local(field.id) && self.private_accessible(field.id.node)) {
+            (is_local(field.did) && self.private_accessible(field.did.node)) {
             return
         }
 
-        let struct_type = self.tcx.lookup_item_type(id).ty;
-        let struct_desc = match struct_type.sty {
-            ty::TyStruct(_, _) =>
-                format!("struct `{}`", self.tcx.item_path_str(id)),
+        let struct_desc = match def.adt_kind() {
+            ty::AdtKind::Struct =>
+                format!("struct `{}`", self.tcx.item_path_str(def.did)),
             // struct variant fields have inherited visibility
-            ty::TyEnum(..) => return,
-            _ => self.tcx.sess.span_bug(span, "can't find struct for field")
+            ty::AdtKind::Enum => return
         };
         let msg = match name {
             NamedField(name) => format!("field `{}` of {} is private",
@@ -883,13 +880,19 @@ impl<'a, 'tcx, 'v> Visitor<'v> for PrivacyVisitor<'a, 'tcx> {
     fn visit_expr(&mut self, expr: &ast::Expr) {
         match expr.node {
             ast::ExprField(ref base, ident) => {
-                if let ty::TyStruct(id, _) = self.tcx.expr_ty_adjusted(&**base).sty {
-                    self.check_field(expr.span, id, NamedField(ident.node.name));
+                if let ty::TyStruct(def, _) = self.tcx.expr_ty_adjusted(&**base).sty {
+                    self.check_field(expr.span,
+                                     def,
+                                     def.struct_variant(),
+                                     NamedField(ident.node.name));
                 }
             }
             ast::ExprTupField(ref base, idx) => {
-                if let ty::TyStruct(id, _) = self.tcx.expr_ty_adjusted(&**base).sty {
-                    self.check_field(expr.span, id, UnnamedField(idx.node));
+                if let ty::TyStruct(def, _) = self.tcx.expr_ty_adjusted(&**base).sty {
+                    self.check_field(expr.span,
+                                     def,
+                                     def.struct_variant(),
+                                     UnnamedField(idx.node));
                 }
             }
             ast::ExprMethodCall(ident, _, _) => {
@@ -898,67 +901,36 @@ impl<'a, 'tcx, 'v> Visitor<'v> for PrivacyVisitor<'a, 'tcx> {
                 debug!("(privacy checking) checking impl method");
                 self.check_method(expr.span, method.def_id, ident.node.name);
             }
-            ast::ExprStruct(_, ref fields, _) => {
-                match self.tcx.expr_ty(expr).sty {
-                    ty::TyStruct(ctor_id, _) => {
-                        // RFC 736: ensure all unmentioned fields are visible.
-                        // Rather than computing the set of unmentioned fields
-                        // (i.e. `all_fields - fields`), just check them all.
-                        let all_fields = self.tcx.lookup_struct_fields(ctor_id);
-                        for field in all_fields {
-                            self.check_field(expr.span, ctor_id,
-                                             NamedField(field.name));
-                        }
-                    }
-                    ty::TyEnum(_, _) => {
-                        match self.tcx.def_map.borrow().get(&expr.id).unwrap().full_def() {
-                            def::DefVariant(_, variant_id, _) => {
-                                for field in fields {
-                                    self.check_field(expr.span, variant_id,
-                                                     NamedField(field.ident.node.name));
-                                }
-                            }
-                            _ => self.tcx.sess.span_bug(expr.span,
-                                                        "resolve didn't \
-                                                         map enum struct \
-                                                         constructor to a \
-                                                         variant def"),
-                        }
-                    }
-                    _ => self.tcx.sess.span_bug(expr.span, "struct expr \
-                                                            didn't have \
-                                                            struct type?!"),
+            ast::ExprStruct(..) => {
+                let adt = self.tcx.expr_ty(expr).ty_adt_def().unwrap();
+                let variant = adt.variant_of_def(self.tcx.resolve_expr(expr));
+                // RFC 736: ensure all unmentioned fields are visible.
+                // Rather than computing the set of unmentioned fields
+                // (i.e. `all_fields - fields`), just check them all.
+                for field in &variant.fields {
+                    self.check_field(expr.span, adt, variant, NamedField(field.name));
                 }
             }
             ast::ExprPath(..) => {
-                let guard = |did: ast::DefId| {
-                    let fields = self.tcx.lookup_struct_fields(did);
-                    let any_priv = fields.iter().any(|f| {
+
+                if let def::DefStruct(_) = self.tcx.resolve_expr(expr) {
+                    let expr_ty = self.tcx.expr_ty(expr);
+                    let def = match expr_ty.sty {
+                        ty::TyBareFn(_, &ty::BareFnTy { sig: ty::Binder(ty::FnSig {
+                            output: ty::FnConverging(ty), ..
+                        }), ..}) => ty,
+                        _ => expr_ty
+                    }.ty_adt_def().unwrap();
+                    let any_priv = def.struct_variant().fields.iter().any(|f| {
                         f.vis != ast::Public && (
-                            !is_local(f.id) ||
-                            !self.private_accessible(f.id.node))
-                    });
+                            !is_local(f.did) ||
+                                    !self.private_accessible(f.did.node))
+                        });
                     if any_priv {
                         self.tcx.sess.span_err(expr.span,
-                            "cannot invoke tuple struct constructor \
-                             with private fields");
-                    }
-                };
-                match self.tcx.def_map.borrow().get(&expr.id).map(|d| d.full_def()) {
-                    Some(def::DefStruct(did)) => {
-                        guard(if is_local(did) {
-                            local_def(self.tcx.map.get_parent(did.node))
-                        } else {
-                            // "tuple structs" with zero fields (such as
-                            // `pub struct Foo;`) don't have a ctor_id, hence
-                            // the unwrap_or to the same struct id.
-                            let maybe_did =
-                                csearch::get_tuple_struct_definition_if_ctor(
-                                    &self.tcx.sess.cstore, did);
-                            maybe_did.unwrap_or(did)
-                        })
+                                               "cannot invoke tuple struct constructor \
+                                                with private fields");
                     }
-                    _ => {}
                 }
             }
             _ => {}
@@ -976,31 +948,12 @@ impl<'a, 'tcx, 'v> Visitor<'v> for PrivacyVisitor<'a, 'tcx> {
 
         match pattern.node {
             ast::PatStruct(_, ref fields, _) => {
-                match self.tcx.pat_ty(pattern).sty {
-                    ty::TyStruct(id, _) => {
-                        for field in fields {
-                            self.check_field(pattern.span, id,
-                                             NamedField(field.node.ident.name));
-                        }
-                    }
-                    ty::TyEnum(_, _) => {
-                        match self.tcx.def_map.borrow().get(&pattern.id).map(|d| d.full_def()) {
-                            Some(def::DefVariant(_, variant_id, _)) => {
-                                for field in fields {
-                                    self.check_field(pattern.span, variant_id,
-                                                     NamedField(field.node.ident.name));
-                                }
-                            }
-                            _ => self.tcx.sess.span_bug(pattern.span,
-                                                        "resolve didn't \
-                                                         map enum struct \
-                                                         pattern to a \
-                                                         variant def"),
-                        }
-                    }
-                    _ => self.tcx.sess.span_bug(pattern.span,
-                                                "struct pattern didn't have \
-                                                 struct type?!"),
+                let adt = self.tcx.pat_ty(pattern).ty_adt_def().unwrap();
+                let def = self.tcx.def_map.borrow().get(&pattern.id).unwrap().full_def();
+                let variant = adt.variant_of_def(def);
+                for field in fields {
+                    self.check_field(pattern.span, adt, variant,
+                                     NamedField(field.node.ident.name));
                 }
             }
 
@@ -1008,12 +961,15 @@ impl<'a, 'tcx, 'v> Visitor<'v> for PrivacyVisitor<'a, 'tcx> {
             // elsewhere).
             ast::PatEnum(_, Some(ref fields)) => {
                 match self.tcx.pat_ty(pattern).sty {
-                    ty::TyStruct(id, _) => {
+                    ty::TyStruct(def, _) => {
                         for (i, field) in fields.iter().enumerate() {
                             if let ast::PatWild(..) = field.node {
                                 continue
                             }
-                            self.check_field(field.span, id, UnnamedField(i));
+                            self.check_field(field.span,
+                                             def,
+                                             def.struct_variant(),
+                                             UnnamedField(i));
                         }
                     }
                     ty::TyEnum(..) => {
diff --git a/src/librustc_resolve/build_reduced_graph.rs b/src/librustc_resolve/build_reduced_graph.rs
index 656d6a36614..190e217aac1 100644
--- a/src/librustc_resolve/build_reduced_graph.rs
+++ b/src/librustc_resolve/build_reduced_graph.rs
@@ -791,9 +791,7 @@ impl<'a, 'b:'a, 'tcx:'b> GraphBuilder<'a, 'b, 'tcx> {
                     crate) building type and value for {}",
                    final_ident);
             child_name_bindings.define_type(def, DUMMY_SP, modifiers);
-            let fields = csearch::get_struct_fields(&self.session.cstore, def_id).iter().map(|f| {
-                f.name
-            }).collect::<Vec<_>>();
+            let fields = csearch::get_struct_field_names(&self.session.cstore, def_id);
 
             if fields.is_empty() {
                 child_name_bindings.define_value(def, DUMMY_SP, modifiers);
diff --git a/src/librustc_trans/save/dump_csv.rs b/src/librustc_trans/save/dump_csv.rs
index 1fbec6f66cf..707d4c4a844 100644
--- a/src/librustc_trans/save/dump_csv.rs
+++ b/src/librustc_trans/save/dump_csv.rs
@@ -742,6 +742,7 @@ impl <'l, 'tcx> DumpCsvVisitor<'l, 'tcx> {
                           ex: &ast::Expr,
                           path: &ast::Path,
                           fields: &Vec<ast::Field>,
+                          variant: ty::VariantDef,
                           base: &Option<P<ast::Expr>>) {
         if generated_code(path.span) {
             return
@@ -756,7 +757,6 @@ impl <'l, 'tcx> DumpCsvVisitor<'l, 'tcx> {
                              Some(struct_lit_data.span),
                              struct_lit_data.ref_id,
                              struct_lit_data.scope);
-            let struct_def = struct_lit_data.ref_id;
             let scope = self.save_ctxt.enclosing_scope(ex.id);
 
             for field in fields {
@@ -765,7 +765,7 @@ impl <'l, 'tcx> DumpCsvVisitor<'l, 'tcx> {
                 }
 
                 let field_data = self.save_ctxt.get_field_ref_data(field,
-                                                                   struct_def,
+                                                                   variant,
                                                                    scope);
                 self.fmt.ref_str(recorder::VarRef,
                                  field.ident.span,
@@ -804,43 +804,24 @@ impl <'l, 'tcx> DumpCsvVisitor<'l, 'tcx> {
         match p.node {
             ast::PatStruct(ref path, ref fields, _) => {
                 visit::walk_path(self, path);
+                let adt = self.tcx.node_id_to_type(p.id).ty_adt_def().unwrap();
+                let def = self.tcx.def_map.borrow()[&p.id].full_def();
+                let variant = adt.variant_of_def(def);
 
-                let def = self.tcx.def_map.borrow().get(&p.id).unwrap().full_def();
-                let struct_def = match def {
-                    def::DefConst(..) | def::DefAssociatedConst(..) => None,
-                    def::DefVariant(_, variant_id, _) => Some(variant_id),
-                    _ => {
-                        match self.tcx.node_id_to_type(p.id).ty_to_def_id() {
-                            None => {
-                                self.sess.span_bug(p.span,
-                                                   &format!("Could not find struct_def for `{}`",
-                                                            self.span.snippet(p.span)));
-                            }
-                            Some(def_id) => Some(def_id),
-                        }
+                for &Spanned { node: ref field, span } in fields {
+                    if generated_code(span) {
+                        continue;
                     }
-                };
 
-                if let Some(struct_def) = struct_def {
-                    let struct_fields = self.tcx.lookup_struct_fields(struct_def);
-                    for &Spanned { node: ref field, span } in fields {
-                        if generated_code(span) {
-                            continue;
-                        }
-
-                        let sub_span = self.span.span_for_first_ident(span);
-                        for f in &struct_fields {
-                            if f.name == field.ident.name {
-                                self.fmt.ref_str(recorder::VarRef,
-                                                 span,
-                                                 sub_span,
-                                                 f.id,
-                                                 self.cur_scope);
-                                break;
-                            }
-                        }
-                        self.visit_pat(&field.pat);
+                    let sub_span = self.span.span_for_first_ident(span);
+                    if let Some(f) = variant.find_field_named(field.ident.name) {
+                        self.fmt.ref_str(recorder::VarRef,
+                                         span,
+                                         sub_span,
+                                         f.did,
+                                         self.cur_scope);
                     }
+                    self.visit_pat(&field.pat);
                 }
             }
             _ => visit::walk_pat(self, p)
@@ -1091,8 +1072,15 @@ impl<'l, 'tcx, 'v> Visitor<'v> for DumpCsvVisitor<'l, 'tcx> {
                 self.process_path(ex.id, path, None);
                 visit::walk_expr(self, ex);
             }
-            ast::ExprStruct(ref path, ref fields, ref base) =>
-                self.process_struct_lit(ex, path, fields, base),
+            ast::ExprStruct(ref path, ref fields, ref base) => {
+                let adt = self.tcx.expr_ty(ex).ty_adt_def().unwrap();
+                let def = self.tcx.resolve_expr(ex);
+                self.process_struct_lit(ex,
+                                        path,
+                                        fields,
+                                        adt.variant_of_def(def),
+                                        base)
+            }
             ast::ExprMethodCall(_, _, ref args) => self.process_method_call(ex, args),
             ast::ExprField(ref sub_ex, _) => {
                 if generated_code(sub_ex.span) {
@@ -1119,19 +1107,13 @@ impl<'l, 'tcx, 'v> Visitor<'v> for DumpCsvVisitor<'l, 'tcx> {
 
                 let ty = &self.tcx.expr_ty_adjusted(&**sub_ex).sty;
                 match *ty {
-                    ty::TyStruct(def_id, _) => {
-                        let fields = self.tcx.lookup_struct_fields(def_id);
-                        for (i, f) in fields.iter().enumerate() {
-                            if i == idx.node {
-                                let sub_span = self.span.sub_span_after_token(ex.span, token::Dot);
-                                self.fmt.ref_str(recorder::VarRef,
-                                                 ex.span,
-                                                 sub_span,
-                                                 f.id,
-                                                 self.cur_scope);
-                                break;
-                            }
-                        }
+                    ty::TyStruct(def, _) => {
+                        let sub_span = self.span.sub_span_after_token(ex.span, token::Dot);
+                        self.fmt.ref_str(recorder::VarRef,
+                                         ex.span,
+                                         sub_span,
+                                         def.struct_variant().fields[idx.node].did,
+                                         self.cur_scope);
                     }
                     ty::TyTuple(_) => {}
                     _ => self.sess.span_bug(ex.span,
diff --git a/src/librustc_trans/save/mod.rs b/src/librustc_trans/save/mod.rs
index 796850ad6f2..5bbd8ce1549 100644
--- a/src/librustc_trans/save/mod.rs
+++ b/src/librustc_trans/save/mod.rs
@@ -447,23 +447,15 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> {
             ast::ExprField(ref sub_ex, ident) => {
                 let ty = &self.tcx.expr_ty_adjusted(&sub_ex).sty;
                 match *ty {
-                    ty::TyStruct(def_id, _) => {
-                        let fields = self.tcx.lookup_struct_fields(def_id);
-                        for f in &fields {
-                            if f.name == ident.node.name {
-                                let sub_span = self.span_utils.span_for_last_ident(expr.span);
-                                return Some(Data::VariableRefData(VariableRefData {
-                                    name: ident.node.to_string(),
-                                    span: sub_span.unwrap(),
-                                    scope: self.enclosing_scope(expr.id),
-                                    ref_id: f.id,
-                                }));
-                            }
-                        }
-
-                        self.tcx.sess.span_bug(expr.span,
-                                               &format!("Couldn't find field {} on {:?}",
-                                                        ident.node, ty))
+                    ty::TyStruct(def, _) => {
+                        let f = def.struct_variant().field_named(ident.node.name);
+                        let sub_span = self.span_utils.span_for_last_ident(expr.span);
+                        return Some(Data::VariableRefData(VariableRefData {
+                            name: ident.node.to_string(),
+                            span: sub_span.unwrap(),
+                            scope: self.enclosing_scope(expr.id),
+                            ref_id: f.did,
+                        }));
                     }
                     _ => {
                         debug!("Expected struct type, found {:?}", ty);
@@ -474,12 +466,12 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> {
             ast::ExprStruct(ref path, _, _) => {
                 let ty = &self.tcx.expr_ty_adjusted(expr).sty;
                 match *ty {
-                    ty::TyStruct(def_id, _) => {
+                    ty::TyStruct(def, _) => {
                         let sub_span = self.span_utils.span_for_last_ident(path.span);
                         Some(Data::TypeRefData(TypeRefData {
                             span: sub_span.unwrap(),
                             scope: self.enclosing_scope(expr.id),
-                            ref_id: def_id,
+                            ref_id: def.did,
                         }))
                     }
                     _ => {
@@ -621,26 +613,18 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> {
 
     pub fn get_field_ref_data(&self,
                               field_ref: &ast::Field,
-                              struct_id: DefId,
+                              variant: ty::VariantDef,
                               parent: NodeId)
                               -> VariableRefData {
-        let fields = self.tcx.lookup_struct_fields(struct_id);
-        let field_name = field_ref.ident.node.to_string();
-        for f in &fields {
-            if f.name == field_ref.ident.node.name {
-                // We don't really need a sub-span here, but no harm done
-                let sub_span = self.span_utils.span_for_last_ident(field_ref.ident.span);
-                return VariableRefData {
-                    name: field_name,
-                    span: sub_span.unwrap(),
-                    scope: parent,
-                    ref_id: f.id,
-                };
-            }
+        let f = variant.field_named(field_ref.ident.node.name);
+        // We don't really need a sub-span here, but no harm done
+        let sub_span = self.span_utils.span_for_last_ident(field_ref.ident.span);
+        VariableRefData {
+            name: field_ref.ident.node.to_string(),
+            span: sub_span.unwrap(),
+            scope: parent,
+            ref_id: f.did,
         }
-
-        self.tcx.sess.span_bug(field_ref.span,
-                               &format!("Couldn't find field {}", field_name));
     }
 
     pub fn get_data_for_id(&self, _id: &NodeId) -> Data {
diff --git a/src/librustc_trans/trans/_match.rs b/src/librustc_trans/trans/_match.rs
index fb3afd16ea0..bd7c67fa8bb 100644
--- a/src/librustc_trans/trans/_match.rs
+++ b/src/librustc_trans/trans/_match.rs
@@ -656,7 +656,7 @@ fn get_branches<'a, 'p, 'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
                 let opt_def = tcx.def_map.borrow().get(&cur.id).map(|d| d.full_def());
                 match opt_def {
                     Some(def::DefVariant(enum_id, var_id, _)) => {
-                        let variant = tcx.enum_variant_with_id(enum_id, var_id);
+                        let variant = tcx.lookup_adt_def(enum_id).variant_with_id(var_id);
                         Variant(variant.disr_val,
                                 adt::represent_node(bcx, cur.id),
                                 var_id,
@@ -1186,14 +1186,12 @@ fn compile_submatch_continue<'a, 'p, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
         ).collect();
 
         match left_ty.sty {
-            ty::TyStruct(def_id, substs) if !type_is_sized(bcx.tcx(), left_ty) => {
+            ty::TyStruct(def, substs) if !type_is_sized(bcx.tcx(), left_ty) => {
                 // The last field is technically unsized but
                 // since we can only ever match that field behind
                 // a reference we construct a fat ptr here.
-                let fields = bcx.tcx().lookup_struct_fields(def_id);
-                let unsized_ty = fields.iter().last().map(|field| {
-                    let fty = bcx.tcx().lookup_field_type(def_id, field.id, substs);
-                    monomorphize::normalize_associated_type(bcx.tcx(), &fty)
+                let unsized_ty = def.struct_variant().fields.last().map(|field| {
+                    monomorphize::field_ty(bcx.tcx(), substs, field)
                 }).unwrap();
                 let llty = type_of::type_of(bcx.ccx(), unsized_ty);
                 let scratch = alloca_no_lifetime(bcx, llty, "__struct_field_fat_ptr");
@@ -1833,7 +1831,7 @@ pub fn bind_irrefutable_pat<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
             match opt_def {
                 Some(def::DefVariant(enum_id, var_id, _)) => {
                     let repr = adt::represent_node(bcx, pat.id);
-                    let vinfo = ccx.tcx().enum_variant_with_id(enum_id, var_id);
+                    let vinfo = ccx.tcx().lookup_adt_def(enum_id).variant_with_id(var_id);
                     let args = extract_variant_args(bcx,
                                                     &*repr,
                                                     vinfo.disr_val,
@@ -1877,21 +1875,20 @@ pub fn bind_irrefutable_pat<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
             let tcx = bcx.tcx();
             let pat_ty = node_id_type(bcx, pat.id);
             let pat_repr = adt::represent_type(bcx.ccx(), pat_ty);
-            expr::with_field_tys(tcx, pat_ty, Some(pat.id), |discr, field_tys| {
-                for f in fields {
-                    let ix = tcx.field_idx_strict(f.node.ident.name, field_tys);
-                    let fldptr = adt::trans_field_ptr(
-                        bcx,
-                        &*pat_repr,
-                        val.val,
-                        discr,
-                        ix);
-                    bcx = bind_irrefutable_pat(bcx,
-                                               &*f.node.pat,
-                                               MatchInput::from_val(fldptr),
-                                               cleanup_scope);
-                }
-            })
+            let pat_v = VariantInfo::of_node(tcx, pat_ty, pat.id);
+            for f in fields {
+                let name = f.node.ident.name;
+                let fldptr = adt::trans_field_ptr(
+                    bcx,
+                    &*pat_repr,
+                    val.val,
+                    pat_v.discr,
+                    pat_v.field_index(name));
+                bcx = bind_irrefutable_pat(bcx,
+                                           &*f.node.pat,
+                                           MatchInput::from_val(fldptr),
+                                           cleanup_scope);
+            }
         }
         ast::PatTup(ref elems) => {
             let repr = adt::represent_node(bcx, pat.id);
diff --git a/src/librustc_trans/trans/adt.rs b/src/librustc_trans/trans/adt.rs
index e79618893c7..326d1e2361e 100644
--- a/src/librustc_trans/trans/adt.rs
+++ b/src/librustc_trans/trans/adt.rs
@@ -245,14 +245,12 @@ fn represent_type_uncached<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
         ty::TyTuple(ref elems) => {
             Univariant(mk_struct(cx, &elems[..], false, t), 0)
         }
-        ty::TyStruct(def_id, substs) => {
-            let fields = cx.tcx().lookup_struct_fields(def_id);
-            let mut ftys = fields.iter().map(|field| {
-                let fty = cx.tcx().lookup_field_type(def_id, field.id, substs);
-                monomorphize::normalize_associated_type(cx.tcx(), &fty)
+        ty::TyStruct(def, substs) => {
+            let mut ftys = def.struct_variant().fields.iter().map(|field| {
+                monomorphize::field_ty(cx.tcx(), substs, field)
             }).collect::<Vec<_>>();
-            let packed = cx.tcx().lookup_packed(def_id);
-            let dtor = cx.tcx().ty_dtor(def_id).has_drop_flag();
+            let packed = cx.tcx().lookup_packed(def.did);
+            let dtor = cx.tcx().ty_dtor(def.did).has_drop_flag();
             if dtor {
                 ftys.push(cx.tcx().dtor_type());
             }
@@ -262,12 +260,12 @@ fn represent_type_uncached<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
         ty::TyClosure(_, ref substs) => {
             Univariant(mk_struct(cx, &substs.upvar_tys, false, t), 0)
         }
-        ty::TyEnum(def_id, substs) => {
-            let cases = get_cases(cx.tcx(), def_id, substs);
-            let hint = *cx.tcx().lookup_repr_hints(def_id).get(0)
+        ty::TyEnum(def, substs) => {
+            let cases = get_cases(cx.tcx(), def, substs);
+            let hint = *cx.tcx().lookup_repr_hints(def.did).get(0)
                 .unwrap_or(&attr::ReprAny);
 
-            let dtor = cx.tcx().ty_dtor(def_id).has_drop_flag();
+            let dtor = cx.tcx().ty_dtor(def.did).has_drop_flag();
 
             if cases.is_empty() {
                 // Uninhabitable; represent as unit
@@ -296,7 +294,7 @@ fn represent_type_uncached<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
             if !cases.iter().enumerate().all(|(i,c)| c.discr == (i as Disr)) {
                 cx.sess().bug(&format!("non-C-like enum {} with specified \
                                         discriminants",
-                                       cx.tcx().item_path_str(def_id)));
+                                       cx.tcx().item_path_str(def.did)));
             }
 
             if cases.len() == 1 {
@@ -443,11 +441,11 @@ fn find_discr_field_candidate<'tcx>(tcx: &ty::ctxt<'tcx>,
         ty::TyBareFn(..) => Some(path),
 
         // Is this the NonZero lang item wrapping a pointer or integer type?
-        ty::TyStruct(did, substs) if Some(did) == tcx.lang_items.non_zero() => {
-            let nonzero_fields = tcx.lookup_struct_fields(did);
+        ty::TyStruct(def, substs) if Some(def.did) == tcx.lang_items.non_zero() => {
+            let nonzero_fields = &def.struct_variant().fields;
             assert_eq!(nonzero_fields.len(), 1);
-            let nonzero_field = tcx.lookup_field_type(did, nonzero_fields[0].id, substs);
-            match nonzero_field.sty {
+            let field_ty = monomorphize::field_ty(tcx, substs, &nonzero_fields[0]);
+            match field_ty.sty {
                 ty::TyRawPtr(ty::TypeAndMut { ty, .. }) if !type_is_sized(tcx, ty) => {
                     path.push_all(&[0, FAT_PTR_ADDR]);
                     Some(path)
@@ -462,10 +460,9 @@ fn find_discr_field_candidate<'tcx>(tcx: &ty::ctxt<'tcx>,
 
         // Perhaps one of the fields of this struct is non-zero
         // let's recurse and find out
-        ty::TyStruct(def_id, substs) => {
-            let fields = tcx.lookup_struct_fields(def_id);
-            for (j, field) in fields.iter().enumerate() {
-                let field_ty = tcx.lookup_field_type(def_id, field.id, substs);
+        ty::TyStruct(def, substs) => {
+            for (j, field) in def.struct_variant().fields.iter().enumerate() {
+                let field_ty = monomorphize::field_ty(tcx, substs, field);
                 if let Some(mut fpath) = find_discr_field_candidate(tcx, field_ty, path.clone()) {
                     fpath.push(j);
                     return Some(fpath);
@@ -530,14 +527,14 @@ impl<'tcx> Case<'tcx> {
 }
 
 fn get_cases<'tcx>(tcx: &ty::ctxt<'tcx>,
-                   def_id: ast::DefId,
+                   adt: ty::AdtDef<'tcx>,
                    substs: &subst::Substs<'tcx>)
                    -> Vec<Case<'tcx>> {
-    tcx.enum_variants(def_id).iter().map(|vi| {
-        let arg_tys = vi.args.iter().map(|&raw_ty| {
-            monomorphize::apply_param_substs(tcx, substs, &raw_ty)
+    adt.variants.iter().map(|vi| {
+        let field_tys = vi.fields.iter().map(|field| {
+            monomorphize::field_ty(tcx, substs, field)
         }).collect();
-        Case { discr: vi.disr_val, tys: arg_tys }
+        Case { discr: vi.disr_val, tys: field_tys }
     }).collect()
 }
 
diff --git a/src/librustc_trans/trans/base.rs b/src/librustc_trans/trans/base.rs
index 7d2185f03fe..d59cc4f4298 100644
--- a/src/librustc_trans/trans/base.rs
+++ b/src/librustc_trans/trans/base.rs
@@ -55,8 +55,8 @@ use trans::cleanup::{self, CleanupMethods, DropHint};
 use trans::closure;
 use trans::common::{Block, C_bool, C_bytes_in_context, C_i32, C_int, C_integral};
 use trans::common::{C_null, C_struct_in_context, C_u64, C_u8, C_undef};
-use trans::common::{CrateContext, DropFlagHintsMap, FunctionContext};
-use trans::common::{Result, NodeIdAndSpan};
+use trans::common::{CrateContext, DropFlagHintsMap, Field, FunctionContext};
+use trans::common::{Result, NodeIdAndSpan, VariantInfo};
 use trans::common::{node_id_type, return_type_is_void};
 use trans::common::{type_is_immediate, type_is_zero_size, val_ty};
 use trans::common;
@@ -386,7 +386,7 @@ pub fn iter_structural_ty<'blk, 'tcx, F>(cx: Block<'blk, 'tcx>,
     fn iter_variant<'blk, 'tcx, F>(cx: Block<'blk, 'tcx>,
                                    repr: &adt::Repr<'tcx>,
                                    av: ValueRef,
-                                   variant: &ty::VariantInfo<'tcx>,
+                                   variant: ty::VariantDef<'tcx>,
                                    substs: &Substs<'tcx>,
                                    f: &mut F)
                                    -> Block<'blk, 'tcx> where
@@ -396,8 +396,8 @@ pub fn iter_structural_ty<'blk, 'tcx, F>(cx: Block<'blk, 'tcx>,
         let tcx = cx.tcx();
         let mut cx = cx;
 
-        for (i, &arg) in variant.args.iter().enumerate() {
-            let arg = monomorphize::apply_param_substs(tcx, substs, &arg);
+        for (i, field) in variant.fields.iter().enumerate() {
+            let arg = monomorphize::field_ty(tcx, substs, field);
             cx = f(cx, adt::trans_field_ptr(cx, repr, av, variant.disr_val, i), arg);
         }
         return cx;
@@ -415,22 +415,20 @@ pub fn iter_structural_ty<'blk, 'tcx, F>(cx: Block<'blk, 'tcx>,
     match t.sty {
       ty::TyStruct(..) => {
           let repr = adt::represent_type(cx.ccx(), t);
-          expr::with_field_tys(cx.tcx(), t, None, |discr, field_tys| {
-              for (i, field_ty) in field_tys.iter().enumerate() {
-                  let field_ty = field_ty.mt.ty;
-                  let llfld_a = adt::trans_field_ptr(cx, &*repr, data_ptr, discr, i);
-
-                  let val = if common::type_is_sized(cx.tcx(), field_ty) {
-                      llfld_a
-                  } else {
-                      let scratch = datum::rvalue_scratch_datum(cx, field_ty, "__fat_ptr_iter");
-                      Store(cx, llfld_a, GEPi(cx, scratch.val, &[0, abi::FAT_PTR_ADDR]));
-                      Store(cx, info.unwrap(), GEPi(cx, scratch.val, &[0, abi::FAT_PTR_EXTRA]));
-                      scratch.val
-                  };
-                  cx = f(cx, val, field_ty);
-              }
-          })
+          let VariantInfo { fields, discr } = VariantInfo::from_ty(cx.tcx(), t, None);
+          for (i, &Field(_, field_ty)) in fields.iter().enumerate() {
+              let llfld_a = adt::trans_field_ptr(cx, &*repr, data_ptr, discr, i);
+
+              let val = if common::type_is_sized(cx.tcx(), field_ty) {
+                  llfld_a
+              } else {
+                  let scratch = datum::rvalue_scratch_datum(cx, field_ty, "__fat_ptr_iter");
+                  Store(cx, llfld_a, GEPi(cx, scratch.val, &[0, abi::FAT_PTR_ADDR]));
+                  Store(cx, info.unwrap(), GEPi(cx, scratch.val, &[0, abi::FAT_PTR_EXTRA]));
+                  scratch.val
+              };
+              cx = f(cx, val, field_ty);
+          }
       }
       ty::TyClosure(_, ref substs) => {
           let repr = adt::represent_type(cx.ccx(), t);
@@ -455,13 +453,12 @@ pub fn iter_structural_ty<'blk, 'tcx, F>(cx: Block<'blk, 'tcx>,
               cx = f(cx, llfld_a, *arg);
           }
       }
-      ty::TyEnum(tid, substs) => {
+      ty::TyEnum(en, substs) => {
           let fcx = cx.fcx;
           let ccx = fcx.ccx;
 
           let repr = adt::represent_type(ccx, t);
-          let variants = ccx.tcx().enum_variants(tid);
-          let n_variants = (*variants).len();
+          let n_variants = en.variants.len();
 
           // NB: we must hit the discriminant first so that structural
           // comparison know not to proceed when the discriminants differ.
@@ -470,7 +467,7 @@ pub fn iter_structural_ty<'blk, 'tcx, F>(cx: Block<'blk, 'tcx>,
               (_match::Single, None) => {
                   if n_variants != 0 {
                       assert!(n_variants == 1);
-                      cx = iter_variant(cx, &*repr, av, &*(*variants)[0],
+                      cx = iter_variant(cx, &*repr, av, &en.variants[0],
                                         substs, &mut f);
                   }
               }
@@ -496,7 +493,7 @@ pub fn iter_structural_ty<'blk, 'tcx, F>(cx: Block<'blk, 'tcx>,
                                         n_variants);
                   let next_cx = fcx.new_temp_block("enum-iter-next");
 
-                  for variant in &(*variants) {
+                  for variant in &en.variants {
                       let variant_cx =
                           fcx.new_temp_block(
                               &format!("enum-iter-variant-{}",
@@ -513,7 +510,7 @@ pub fn iter_structural_ty<'blk, 'tcx, F>(cx: Block<'blk, 'tcx>,
                           iter_variant(variant_cx,
                                        &*repr,
                                        data_ptr,
-                                       &**variant,
+                                       variant,
                                        substs,
                                        &mut f);
                       Br(variant_cx, next_cx.llbb, DebugLoc::None);
@@ -624,7 +621,7 @@ pub fn fail_if_zero_or_overflows<'blk, 'tcx>(
             let zero = C_integral(Type::uint_from_ty(cx.ccx(), t), 0, false);
             (ICmp(cx, llvm::IntEQ, rhs, zero, debug_loc), false)
         }
-        ty::TyStruct(_, _) if rhs_t.is_simd(cx.tcx()) => {
+        ty::TyStruct(def, _) if def.is_simd() => {
             let mut res = C_bool(cx.ccx(), false);
             for i in 0 .. rhs_t.simd_size(cx.tcx()) {
                 res = Or(cx, res,
@@ -1693,9 +1690,7 @@ pub fn trans_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
 }
 
 pub fn trans_enum_variant<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
-                                    _enum_id: ast::NodeId,
-                                    variant: &ast::Variant,
-                                    _args: &[ast::VariantArg],
+                                    ctor_id: ast::NodeId,
                                     disr: ty::Disr,
                                     param_substs: &'tcx Substs<'tcx>,
                                     llfndecl: ValueRef) {
@@ -1703,7 +1698,7 @@ pub fn trans_enum_variant<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
 
     trans_enum_variant_or_tuple_like_struct(
         ccx,
-        variant.node.id,
+        ctor_id,
         disr,
         param_substs,
         llfndecl);
@@ -1775,7 +1770,6 @@ pub fn trans_named_tuple_constructor<'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
 }
 
 pub fn trans_tuple_struct<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
-                                    _fields: &[ast::StructField],
                                     ctor_id: ast::NodeId,
                                     param_substs: &'tcx Substs<'tcx>,
                                     llfndecl: ValueRef) {
diff --git a/src/librustc_trans/trans/callee.rs b/src/librustc_trans/trans/callee.rs
index a60217be409..d0d5b46ab28 100644
--- a/src/librustc_trans/trans/callee.rs
+++ b/src/librustc_trans/trans/callee.rs
@@ -182,10 +182,8 @@ fn trans<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, expr: &ast::Expr)
                 fn_callee(bcx, fn_datum)
             }
             def::DefVariant(tid, vid, _) => {
-                let vinfo = bcx.tcx().enum_variant_with_id(tid, vid);
-
-                // Nullary variants are not callable
-                assert!(!vinfo.args.is_empty());
+                let vinfo = bcx.tcx().lookup_adt_def(tid).variant_with_id(vid);
+                assert_eq!(vinfo.kind(), ty::VariantKind::Tuple);
 
                 Callee {
                     bcx: bcx,
diff --git a/src/librustc_trans/trans/common.rs b/src/librustc_trans/trans/common.rs
index 0de870de7c6..f57612789b5 100644
--- a/src/librustc_trans/trans/common.rs
+++ b/src/librustc_trans/trans/common.rs
@@ -49,6 +49,7 @@ use std::cell::{Cell, RefCell};
 use std::result::Result as StdResult;
 use std::vec::Vec;
 use syntax::ast;
+use syntax::ast_util::local_def;
 use syntax::codemap::{DUMMY_SP, Span};
 use syntax::parse::token::InternedString;
 use syntax::parse::token;
@@ -173,12 +174,10 @@ fn type_needs_drop_given_env<'a,'tcx>(cx: &ty::ctxt<'tcx>,
 
 fn type_is_newtype_immediate<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ty: Ty<'tcx>) -> bool {
     match ty.sty {
-        ty::TyStruct(def_id, substs) => {
-            let fields = ccx.tcx().lookup_struct_fields(def_id);
+        ty::TyStruct(def, substs) => {
+            let fields = &def.struct_variant().fields;
             fields.len() == 1 && {
-                let ty = ccx.tcx().lookup_field_type(def_id, fields[0].id, substs);
-                let ty = monomorphize::normalize_associated_type(ccx.tcx(), &ty);
-                type_is_immediate(ccx, ty)
+                type_is_immediate(ccx, monomorphize::field_ty(ccx.tcx(), substs, &fields[0]))
             }
         }
         _ => false
@@ -193,7 +192,7 @@ pub fn type_is_immediate<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ty: Ty<'tcx>) -
     let simple = ty.is_scalar() ||
         ty.is_unique() || ty.is_region_ptr() ||
         type_is_newtype_immediate(ccx, ty) ||
-        ty.is_simd(tcx);
+        ty.is_simd();
     if simple && !type_is_fat_ptr(tcx, ty) {
         return true;
     }
@@ -271,6 +270,67 @@ pub fn expr_info(expr: &ast::Expr) -> NodeIdAndSpan {
     NodeIdAndSpan { id: expr.id, span: expr.span }
 }
 
+/// The concrete version of ty::FieldDef. The name is the field index if
+/// the field is numeric.
+pub struct Field<'tcx>(pub ast::Name, pub Ty<'tcx>);
+
+/// The concrete version of ty::VariantDef
+pub struct VariantInfo<'tcx> {
+    pub discr: ty::Disr,
+    pub fields: Vec<Field<'tcx>>
+}
+
+impl<'tcx> VariantInfo<'tcx> {
+    pub fn from_ty(tcx: &ty::ctxt<'tcx>,
+                   ty: Ty<'tcx>,
+                   opt_def: Option<def::Def>)
+                   -> Self
+    {
+        match ty.sty {
+            ty::TyStruct(adt, substs) | ty::TyEnum(adt, substs) => {
+                let variant = match opt_def {
+                    None => adt.struct_variant(),
+                    Some(def) => adt.variant_of_def(def)
+                };
+
+                VariantInfo {
+                    discr: variant.disr_val,
+                    fields: variant.fields.iter().map(|f| {
+                        Field(f.name, monomorphize::field_ty(tcx, substs, f))
+                    }).collect()
+                }
+            }
+
+            ty::TyTuple(ref v) => {
+                VariantInfo {
+                    discr: 0,
+                    fields: v.iter().enumerate().map(|(i, &t)| {
+                        Field(token::intern(&i.to_string()), t)
+                    }).collect()
+                }
+            }
+
+            _ => {
+                tcx.sess.bug(&format!(
+                    "cannot get field types from the type {:?}",
+                    ty));
+            }
+        }
+    }
+
+    /// Return the variant corresponding to a given node (e.g. expr)
+    pub fn of_node(tcx: &ty::ctxt<'tcx>, ty: Ty<'tcx>, id: ast::NodeId) -> Self {
+        let node_def = tcx.def_map.borrow().get(&id).map(|v| v.full_def());
+        Self::from_ty(tcx, ty, node_def)
+    }
+
+    pub fn field_index(&self, name: ast::Name) -> usize {
+        self.fields.iter().position(|&Field(n,_)| n == name).unwrap_or_else(|| {
+            panic!("unknown field `{}`", name)
+        })
+    }
+}
+
 pub struct BuilderRef_res {
     pub b: BuilderRef,
 }
@@ -1178,3 +1238,26 @@ pub fn langcall(bcx: Block,
         }
     }
 }
+
+/// Return the VariantDef corresponding to an inlined variant node
+pub fn inlined_variant_def<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
+                                     inlined_vid: ast::NodeId)
+                                     -> ty::VariantDef<'tcx>
+{
+
+    let ctor_ty = ccx.tcx().node_id_to_type(inlined_vid);
+    debug!("inlined_variant_def: ctor_ty={:?} inlined_vid={:?}", ctor_ty,
+           inlined_vid);
+    let adt_def = match ctor_ty.sty {
+        ty::TyBareFn(_, &ty::BareFnTy { sig: ty::Binder(ty::FnSig {
+            output: ty::FnConverging(ty), ..
+        }), ..}) => ty,
+        _ => ctor_ty
+    }.ty_adt_def().unwrap();
+    adt_def.variants.iter().find(|v| {
+        local_def(inlined_vid) == v.did ||
+            ccx.external().borrow().get(&v.did) == Some(&Some(inlined_vid))
+    }).unwrap_or_else(|| {
+        ccx.sess().bug(&format!("no variant for {:?}::{}", adt_def, inlined_vid))
+    })
+}
diff --git a/src/librustc_trans/trans/consts.rs b/src/librustc_trans/trans/consts.rs
index 71ba4d73dac..7aaed035f21 100644
--- a/src/librustc_trans/trans/consts.rs
+++ b/src/librustc_trans/trans/consts.rs
@@ -500,7 +500,7 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
             debug!("const_expr_unadjusted: te1={}, ty={:?}",
                    cx.tn().val_to_string(te1),
                    ty);
-            let is_simd = ty.is_simd(cx.tcx());
+            let is_simd = ty.is_simd();
             let intype = if is_simd {
                 ty.simd_type(cx.tcx())
             } else {
@@ -579,17 +579,15 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
         ast::ExprField(ref base, field) => {
             let (bv, bt) = const_expr(cx, &**base, param_substs, fn_args);
             let brepr = adt::represent_type(cx, bt);
-            expr::with_field_tys(cx.tcx(), bt, None, |discr, field_tys| {
-                let ix = cx.tcx().field_idx_strict(field.node.name, field_tys);
-                adt::const_get_field(cx, &*brepr, bv, discr, ix)
-            })
+            let vinfo = VariantInfo::from_ty(cx.tcx(), bt, None);
+            let ix = vinfo.field_index(field.node.name);
+            adt::const_get_field(cx, &*brepr, bv, vinfo.discr, ix)
         },
         ast::ExprTupField(ref base, idx) => {
             let (bv, bt) = const_expr(cx, &**base, param_substs, fn_args);
             let brepr = adt::represent_type(cx, bt);
-            expr::with_field_tys(cx.tcx(), bt, None, |discr, _| {
-                adt::const_get_field(cx, &*brepr, bv, discr, idx.node)
-            })
+            let vinfo = VariantInfo::from_ty(cx.tcx(), bt, None);
+            adt::const_get_field(cx, &*brepr, bv, vinfo.discr, idx.node)
         },
 
         ast::ExprIndex(ref base, ref index) => {
@@ -664,8 +662,8 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
                 }
             }
             unsafe { match (
-                CastTy::from_ty(cx.tcx(), t_expr).expect("bad input type for cast"),
-                CastTy::from_ty(cx.tcx(), t_cast).expect("bad output type for cast"),
+                CastTy::from_ty(t_expr).expect("bad input type for cast"),
+                CastTy::from_ty(t_cast).expect("bad output type for cast"),
             ) {
                 (CastTy::Int(IntTy::CEnum), CastTy::Int(_)) => {
                     let repr = adt::represent_type(cx, t_expr);
@@ -748,21 +746,19 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
                 None => None
             };
 
-            expr::with_field_tys(cx.tcx(), ety, Some(e.id), |discr, field_tys| {
-                let cs = field_tys.iter().enumerate()
-                                  .map(|(ix, &field_ty)| {
-                    match (fs.iter().find(|f| field_ty.name == f.ident.node.name), base_val) {
-                        (Some(ref f), _) => const_expr(cx, &*f.expr, param_substs, fn_args).0,
-                        (_, Some((bv, _))) => adt::const_get_field(cx, &*repr, bv, discr, ix),
-                        (_, None) => cx.sess().span_bug(e.span, "missing struct field"),
-                    }
-                }).collect::<Vec<_>>();
-                if ety.is_simd(cx.tcx()) {
-                    C_vector(&cs[..])
-                } else {
-                    adt::trans_const(cx, &*repr, discr, &cs[..])
+            let VariantInfo { discr, fields } = VariantInfo::of_node(cx.tcx(), ety, e.id);
+            let cs = fields.iter().enumerate().map(|(ix, &Field(f_name, _))| {
+                match (fs.iter().find(|f| f_name == f.ident.node.name), base_val) {
+                    (Some(ref f), _) => const_expr(cx, &*f.expr, param_substs, fn_args).0,
+                    (_, Some((bv, _))) => adt::const_get_field(cx, &*repr, bv, discr, ix),
+                    (_, None) => cx.sess().span_bug(e.span, "missing struct field"),
                 }
-            })
+            }).collect::<Vec<_>>();
+            if ety.is_simd() {
+                C_vector(&cs[..])
+            } else {
+                adt::trans_const(cx, &*repr, discr, &cs[..])
+            }
         },
         ast::ExprVec(ref es) => {
             let unit_ty = ety.sequence_element_type(cx.tcx());
@@ -806,14 +802,18 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
                     const_deref_ptr(cx, get_const_val(cx, def_id, e))
                 }
                 def::DefVariant(enum_did, variant_did, _) => {
-                    let vinfo = cx.tcx().enum_variant_with_id(enum_did, variant_did);
-                    if !vinfo.args.is_empty() {
-                        // N-ary variant.
-                        expr::trans_def_fn_unadjusted(cx, e, def, param_substs).val
-                    } else {
-                        // Nullary variant.
-                        let repr = adt::represent_type(cx, ety);
-                        adt::trans_const(cx, &*repr, vinfo.disr_val, &[])
+                    let vinfo = cx.tcx().lookup_adt_def(enum_did).variant_with_id(variant_did);
+                    match vinfo.kind() {
+                        ty::VariantKind::Unit => {
+                            let repr = adt::represent_type(cx, ety);
+                            adt::trans_const(cx, &*repr, vinfo.disr_val, &[])
+                        }
+                        ty::VariantKind::Tuple => {
+                            expr::trans_def_fn_unadjusted(cx, e, def, param_substs).val
+                        }
+                        ty::VariantKind::Dict => {
+                            cx.sess().span_bug(e.span, "path-expr refers to a dict variant!")
+                        }
                     }
                 }
                 def::DefStruct(_) => {
@@ -850,7 +850,7 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
                     const_fn_call(cx, ExprId(callee.id), did, &arg_vals, param_substs)
                 }
                 def::DefStruct(_) => {
-                    if ety.is_simd(cx.tcx()) {
+                    if ety.is_simd() {
                         C_vector(&arg_vals[..])
                     } else {
                         let repr = adt::represent_type(cx, ety);
@@ -859,7 +859,7 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
                 }
                 def::DefVariant(enum_did, variant_did, _) => {
                     let repr = adt::represent_type(cx, ety);
-                    let vinfo = cx.tcx().enum_variant_with_id(enum_did, variant_did);
+                    let vinfo = cx.tcx().lookup_adt_def(enum_did).variant_with_id(variant_did);
                     adt::trans_const(cx,
                                      &*repr,
                                      vinfo.disr_val,
diff --git a/src/librustc_trans/trans/debuginfo/metadata.rs b/src/librustc_trans/trans/debuginfo/metadata.rs
index b8feecd0a55..0be155b7727 100644
--- a/src/librustc_trans/trans/debuginfo/metadata.rs
+++ b/src/librustc_trans/trans/debuginfo/metadata.rs
@@ -44,7 +44,7 @@ use std::rc::Rc;
 use syntax::util::interner::Interner;
 use syntax::codemap::Span;
 use syntax::{ast, codemap, ast_util};
-use syntax::parse::token::{self, special_idents};
+use syntax::parse::token;
 
 
 const DW_LANG_RUST: c_uint = 0x9000;
@@ -178,13 +178,13 @@ impl<'tcx> TypeMap<'tcx> {
             ty::TyFloat(_) => {
                 push_debuginfo_type_name(cx, type_, false, &mut unique_type_id);
             },
-            ty::TyEnum(def_id, substs) => {
+            ty::TyEnum(def, substs) => {
                 unique_type_id.push_str("enum ");
-                from_def_id_and_substs(self, cx, def_id, substs, &mut unique_type_id);
+                from_def_id_and_substs(self, cx, def.did, substs, &mut unique_type_id);
             },
-            ty::TyStruct(def_id, substs) => {
+            ty::TyStruct(def, substs) => {
                 unique_type_id.push_str("struct ");
-                from_def_id_and_substs(self, cx, def_id, substs, &mut unique_type_id);
+                from_def_id_and_substs(self, cx, def.did, substs, &mut unique_type_id);
             },
             ty::TyTuple(ref component_types) if component_types.is_empty() => {
                 push_debuginfo_type_name(cx, type_, false, &mut unique_type_id);
@@ -710,8 +710,12 @@ pub fn type_metadata<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
         ty::TyTuple(ref elements) if elements.is_empty() => {
             MetadataCreationResult::new(basic_type_metadata(cx, t), false)
         }
-        ty::TyEnum(def_id, _) => {
-            prepare_enum_metadata(cx, t, def_id, unique_type_id, usage_site_span).finalize(cx)
+        ty::TyEnum(def, _) => {
+            prepare_enum_metadata(cx,
+                                  t,
+                                  def.did,
+                                  unique_type_id,
+                                  usage_site_span).finalize(cx)
         }
         ty::TyArray(typ, len) => {
             fixed_vec_metadata(cx, unique_type_id, typ, Some(len as u64), usage_site_span)
@@ -780,11 +784,9 @@ pub fn type_metadata<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
                                    unique_type_id,
                                    usage_site_span).finalize(cx)
         }
-        ty::TyStruct(def_id, substs) => {
+        ty::TyStruct(..) => {
             prepare_struct_metadata(cx,
                                     t,
-                                    def_id,
-                                    substs,
                                     unique_type_id,
                                     usage_site_span).finalize(cx)
         }
@@ -1092,7 +1094,8 @@ impl<'tcx> MemberDescriptionFactory<'tcx> {
 
 // Creates MemberDescriptions for the fields of a struct
 struct StructMemberDescriptionFactory<'tcx> {
-    fields: Vec<ty::Field<'tcx>>,
+    variant: ty::VariantDef<'tcx>,
+    substs: &'tcx subst::Substs<'tcx>,
     is_simd: bool,
     span: Span,
 }
@@ -1100,34 +1103,40 @@ struct StructMemberDescriptionFactory<'tcx> {
 impl<'tcx> StructMemberDescriptionFactory<'tcx> {
     fn create_member_descriptions<'a>(&self, cx: &CrateContext<'a, 'tcx>)
                                       -> Vec<MemberDescription> {
-        if self.fields.is_empty() {
+        if let ty::VariantKind::Unit = self.variant.kind() {
             return Vec::new();
         }
 
         let field_size = if self.is_simd {
-            machine::llsize_of_alloc(cx, type_of::type_of(cx, self.fields[0].mt.ty)) as usize
+            let fty = monomorphize::field_ty(cx.tcx(),
+                                             self.substs,
+                                             &self.variant.fields[0]);
+            Some(machine::llsize_of_alloc(
+                cx,
+                type_of::type_of(cx, fty)
+            ) as usize)
         } else {
-            0xdeadbeef
+            None
         };
 
-        self.fields.iter().enumerate().map(|(i, field)| {
-            let name = if field.name == special_idents::unnamed_field.name {
+        self.variant.fields.iter().enumerate().map(|(i, f)| {
+            let name = if let ty::VariantKind::Tuple = self.variant.kind() {
                 format!("__{}", i)
             } else {
-                field.name.to_string()
+                f.name.to_string()
             };
+            let fty = monomorphize::field_ty(cx.tcx(), self.substs, f);
 
             let offset = if self.is_simd {
-                assert!(field_size != 0xdeadbeef);
-                FixedMemberOffset { bytes: i * field_size }
+                FixedMemberOffset { bytes: i * field_size.unwrap() }
             } else {
                 ComputedMemberOffset
             };
 
             MemberDescription {
                 name: name,
-                llvm_type: type_of::type_of(cx, field.mt.ty),
-                type_metadata: type_metadata(cx, field.mt.ty, self.span),
+                llvm_type: type_of::type_of(cx, fty),
+                type_metadata: type_metadata(cx, fty, self.span),
                 offset: offset,
                 flags: FLAGS_NONE,
             }
@@ -1138,15 +1147,18 @@ impl<'tcx> StructMemberDescriptionFactory<'tcx> {
 
 fn prepare_struct_metadata<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
                                      struct_type: Ty<'tcx>,
-                                     def_id: ast::DefId,
-                                     substs: &subst::Substs<'tcx>,
                                      unique_type_id: UniqueTypeId,
                                      span: Span)
                                      -> RecursiveTypeDescription<'tcx> {
     let struct_name = compute_debuginfo_type_name(cx, struct_type, false);
     let struct_llvm_type = type_of::in_memory_type_of(cx, struct_type);
 
-    let (containing_scope, _) = get_namespace_and_span_for_item(cx, def_id);
+    let (variant, substs) = match struct_type.sty {
+        ty::TyStruct(def, substs) => (def.struct_variant(), substs),
+        _ => cx.tcx().sess.bug("prepare_struct_metadata on a non-struct")
+    };
+
+    let (containing_scope, _) = get_namespace_and_span_for_item(cx, variant.did);
 
     let struct_metadata_stub = create_struct_stub(cx,
                                                   struct_llvm_type,
@@ -1154,14 +1166,6 @@ fn prepare_struct_metadata<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
                                                   unique_type_id,
                                                   containing_scope);
 
-    let mut fields = cx.tcx().struct_fields(def_id, substs);
-
-    // The `Ty` values returned by `ty::struct_fields` can still contain
-    // `TyProjection` variants, so normalize those away.
-    for field in &mut fields {
-        field.mt.ty = monomorphize::normalize_associated_type(cx.tcx(), &field.mt.ty);
-    }
-
     create_and_register_recursive_type_forward_declaration(
         cx,
         struct_type,
@@ -1169,8 +1173,9 @@ fn prepare_struct_metadata<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
         struct_metadata_stub,
         struct_llvm_type,
         StructMDF(StructMemberDescriptionFactory {
-            fields: fields,
-            is_simd: struct_type.is_simd(cx.tcx()),
+            variant: variant,
+            substs: substs,
+            is_simd: struct_type.is_simd(),
             span: span,
         })
     )
@@ -1244,7 +1249,6 @@ fn prepare_tuple_metadata<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
 struct EnumMemberDescriptionFactory<'tcx> {
     enum_type: Ty<'tcx>,
     type_rep: Rc<adt::Repr<'tcx>>,
-    variants: Rc<Vec<Rc<ty::VariantInfo<'tcx>>>>,
     discriminant_type_metadata: Option<DIType>,
     containing_scope: DIScope,
     file_metadata: DIFile,
@@ -1254,11 +1258,11 @@ struct EnumMemberDescriptionFactory<'tcx> {
 impl<'tcx> EnumMemberDescriptionFactory<'tcx> {
     fn create_member_descriptions<'a>(&self, cx: &CrateContext<'a, 'tcx>)
                                       -> Vec<MemberDescription> {
+        let adt = &self.enum_type.ty_adt_def().unwrap();
         match *self.type_rep {
             adt::General(_, ref struct_defs, _) => {
                 let discriminant_info = RegularDiscriminant(self.discriminant_type_metadata
                     .expect(""));
-
                 struct_defs
                     .iter()
                     .enumerate()
@@ -1269,7 +1273,7 @@ impl<'tcx> EnumMemberDescriptionFactory<'tcx> {
                             describe_enum_variant(cx,
                                                   self.enum_type,
                                                   struct_def,
-                                                  &*self.variants[i],
+                                                  &adt.variants[i],
                                                   discriminant_info,
                                                   self.containing_scope,
                                                   self.span);
@@ -1291,9 +1295,9 @@ impl<'tcx> EnumMemberDescriptionFactory<'tcx> {
                     }).collect()
             },
             adt::Univariant(ref struct_def, _) => {
-                assert!(self.variants.len() <= 1);
+                assert!(adt.variants.len() <= 1);
 
-                if self.variants.is_empty() {
+                if adt.variants.is_empty() {
                     vec![]
                 } else {
                     let (variant_type_metadata,
@@ -1302,7 +1306,7 @@ impl<'tcx> EnumMemberDescriptionFactory<'tcx> {
                         describe_enum_variant(cx,
                                               self.enum_type,
                                               struct_def,
-                                              &*self.variants[0],
+                                              &adt.variants[0],
                                               NoDiscriminant,
                                               self.containing_scope,
                                               self.span);
@@ -1331,7 +1335,7 @@ impl<'tcx> EnumMemberDescriptionFactory<'tcx> {
                 // DWARF representation of enums uniform.
 
                 // First create a description of the artificial wrapper struct:
-                let non_null_variant = &self.variants[non_null_variant_index as usize];
+                let non_null_variant = &adt.variants[non_null_variant_index as usize];
                 let non_null_variant_name = non_null_variant.name.as_str();
 
                 // The llvm type and metadata of the pointer
@@ -1346,9 +1350,12 @@ impl<'tcx> EnumMemberDescriptionFactory<'tcx> {
                 // For the metadata of the wrapper struct, we need to create a
                 // MemberDescription of the struct's single field.
                 let sole_struct_member_description = MemberDescription {
-                    name: match non_null_variant.arg_names {
-                        Some(ref names) => names[0].to_string(),
-                        None => "__0".to_string()
+                    name: match non_null_variant.kind() {
+                        ty::VariantKind::Tuple => "__0".to_string(),
+                        ty::VariantKind::Dict => {
+                            non_null_variant.fields[0].name.to_string()
+                        }
+                        ty::VariantKind::Unit => unreachable!()
                     },
                     llvm_type: non_null_llvm_type,
                     type_metadata: non_null_type_metadata,
@@ -1377,7 +1384,7 @@ impl<'tcx> EnumMemberDescriptionFactory<'tcx> {
                 // Encode the information about the null variant in the union
                 // member's name.
                 let null_variant_index = (1 - non_null_variant_index) as usize;
-                let null_variant_name = self.variants[null_variant_index].name;
+                let null_variant_name = adt.variants[null_variant_index].name;
                 let union_member_name = format!("RUST$ENCODED$ENUM${}${}",
                                                 0,
                                                 null_variant_name);
@@ -1402,7 +1409,7 @@ impl<'tcx> EnumMemberDescriptionFactory<'tcx> {
                     describe_enum_variant(cx,
                                           self.enum_type,
                                           struct_def,
-                                          &*self.variants[nndiscr as usize],
+                                          &adt.variants[nndiscr as usize],
                                           OptimizedDiscriminant,
                                           self.containing_scope,
                                           self.span);
@@ -1418,7 +1425,7 @@ impl<'tcx> EnumMemberDescriptionFactory<'tcx> {
                 // Encode the information about the null variant in the union
                 // member's name.
                 let null_variant_index = (1 - nndiscr) as usize;
-                let null_variant_name = self.variants[null_variant_index].name;
+                let null_variant_name = adt.variants[null_variant_index].name;
                 let discrfield = discrfield.iter()
                                            .skip(1)
                                            .map(|x| x.to_string())
@@ -1482,7 +1489,7 @@ enum EnumDiscriminantInfo {
 fn describe_enum_variant<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
                                    enum_type: Ty<'tcx>,
                                    struct_def: &adt::Struct<'tcx>,
-                                   variant_info: &ty::VariantInfo<'tcx>,
+                                   variant: ty::VariantDef<'tcx>,
                                    discriminant_info: EnumDiscriminantInfo,
                                    containing_scope: DIScope,
                                    span: Span)
@@ -1496,7 +1503,7 @@ fn describe_enum_variant<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
                       struct_def.packed);
     // Could do some consistency checks here: size, align, field count, discr type
 
-    let variant_name = variant_info.name.as_str();
+    let variant_name = variant.name.as_str();
     let unique_type_id = debug_context(cx).type_map
                                           .borrow_mut()
                                           .get_unique_type_id_of_enum_variant(
@@ -1511,18 +1518,20 @@ fn describe_enum_variant<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
                                            containing_scope);
 
     // Get the argument names from the enum variant info
-    let mut arg_names: Vec<_> = match variant_info.arg_names {
-        Some(ref names) => {
-            names.iter()
-                 .map(|name| name.to_string())
-                 .collect()
+    let mut arg_names: Vec<_> = match variant.kind() {
+        ty::VariantKind::Unit => vec![],
+        ty::VariantKind::Tuple => {
+            variant.fields
+                   .iter()
+                   .enumerate()
+                   .map(|(i, _)| format!("__{}", i))
+                   .collect()
         }
-        None => {
-            variant_info.args
-                        .iter()
-                        .enumerate()
-                        .map(|(i, _)| format!("__{}", i))
-                        .collect()
+        ty::VariantKind::Dict => {
+            variant.fields
+                   .iter()
+                   .map(|f| f.name.to_string())
+                   .collect()
         }
     };
 
@@ -1565,7 +1574,7 @@ fn prepare_enum_metadata<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
     let loc = span_start(cx, definition_span);
     let file_metadata = file_metadata(cx, &loc.file.name);
 
-    let variants = cx.tcx().enum_variants(enum_def_id);
+    let variants = &enum_type.ty_adt_def().unwrap().variants;
 
     let enumerators_metadata: Vec<DIDescriptor> = variants
         .iter()
@@ -1667,7 +1676,6 @@ fn prepare_enum_metadata<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
         EnumMDF(EnumMemberDescriptionFactory {
             enum_type: enum_type,
             type_rep: type_rep.clone(),
-            variants: variants,
             discriminant_type_metadata: discriminant_type_metadata,
             containing_scope: containing_scope,
             file_metadata: file_metadata,
diff --git a/src/librustc_trans/trans/debuginfo/type_names.rs b/src/librustc_trans/trans/debuginfo/type_names.rs
index 4835cfc4a25..120134800b0 100644
--- a/src/librustc_trans/trans/debuginfo/type_names.rs
+++ b/src/librustc_trans/trans/debuginfo/type_names.rs
@@ -54,9 +54,9 @@ pub fn push_debuginfo_type_name<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
         ty::TyUint(ast::TyU64)  => output.push_str("u64"),
         ty::TyFloat(ast::TyF32) => output.push_str("f32"),
         ty::TyFloat(ast::TyF64) => output.push_str("f64"),
-        ty::TyStruct(def_id, substs) |
-        ty::TyEnum(def_id, substs) => {
-            push_item_name(cx, def_id, qualified, output);
+        ty::TyStruct(def, substs) |
+        ty::TyEnum(def, substs) => {
+            push_item_name(cx, def.did, qualified, output);
             push_type_params(cx, substs, output);
         },
         ty::TyTuple(ref component_types) => {
diff --git a/src/librustc_trans/trans/expr.rs b/src/librustc_trans/trans/expr.rs
index 01173b7f6df..c5043f867de 100644
--- a/src/librustc_trans/trans/expr.rs
+++ b/src/librustc_trans/trans/expr.rs
@@ -68,7 +68,6 @@ use trans::debuginfo::{self, DebugLoc, ToDebugLoc};
 use trans::glue;
 use trans::machine;
 use trans::meth;
-use trans::monomorphize;
 use trans::tvec;
 use trans::type_of;
 use middle::cast::{CastKind, CastTy};
@@ -708,7 +707,7 @@ fn trans_field<'blk, 'tcx, F>(bcx: Block<'blk, 'tcx>,
                               base: &ast::Expr,
                               get_idx: F)
                               -> DatumBlock<'blk, 'tcx, Expr> where
-    F: FnOnce(&'blk ty::ctxt<'tcx>, &[ty::Field<'tcx>]) -> usize,
+    F: FnOnce(&'blk ty::ctxt<'tcx>, &VariantInfo<'tcx>) -> usize,
 {
     let mut bcx = bcx;
     let _icx = push_ctxt("trans_rec_field");
@@ -716,27 +715,26 @@ fn trans_field<'blk, 'tcx, F>(bcx: Block<'blk, 'tcx>,
     let base_datum = unpack_datum!(bcx, trans_to_lvalue(bcx, base, "field"));
     let bare_ty = base_datum.ty;
     let repr = adt::represent_type(bcx.ccx(), bare_ty);
-    with_field_tys(bcx.tcx(), bare_ty, None, move |discr, field_tys| {
-        let ix = get_idx(bcx.tcx(), field_tys);
-        let d = base_datum.get_element(
-            bcx,
-            field_tys[ix].mt.ty,
-            |srcval| adt::trans_field_ptr(bcx, &*repr, srcval, discr, ix));
-
-        if type_is_sized(bcx.tcx(), d.ty) {
-            DatumBlock { datum: d.to_expr_datum(), bcx: bcx }
-        } else {
-            let scratch = rvalue_scratch_datum(bcx, d.ty, "");
-            Store(bcx, d.val, get_dataptr(bcx, scratch.val));
-            let info = Load(bcx, get_len(bcx, base_datum.val));
-            Store(bcx, info, get_len(bcx, scratch.val));
+    let vinfo = VariantInfo::from_ty(bcx.tcx(), bare_ty, None);
 
-            // Always generate an lvalue datum, because this pointer doesn't own
-            // the data and cleanup is scheduled elsewhere.
-            DatumBlock::new(bcx, Datum::new(scratch.val, scratch.ty, LvalueExpr(d.kind)))
-        }
-    })
+    let ix = get_idx(bcx.tcx(), &vinfo);
+    let d = base_datum.get_element(
+        bcx,
+        vinfo.fields[ix].1,
+        |srcval| adt::trans_field_ptr(bcx, &*repr, srcval, vinfo.discr, ix));
 
+    if type_is_sized(bcx.tcx(), d.ty) {
+        DatumBlock { datum: d.to_expr_datum(), bcx: bcx }
+    } else {
+        let scratch = rvalue_scratch_datum(bcx, d.ty, "");
+        Store(bcx, d.val, get_dataptr(bcx, scratch.val));
+        let info = Load(bcx, get_len(bcx, base_datum.val));
+        Store(bcx, info, get_len(bcx, scratch.val));
+
+        // Always generate an lvalue datum, because this pointer doesn't own
+        // the data and cleanup is scheduled elsewhere.
+        DatumBlock::new(bcx, Datum::new(scratch.val, scratch.ty, LvalueExpr(d.kind)))
+    }
 }
 
 /// Translates `base.field`.
@@ -744,7 +742,7 @@ fn trans_rec_field<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
                                base: &ast::Expr,
                                field: ast::Name)
                                -> DatumBlock<'blk, 'tcx, Expr> {
-    trans_field(bcx, base, |tcx, field_tys| tcx.field_idx_strict(field, field_tys))
+    trans_field(bcx, base, |_, vinfo| vinfo.field_index(field))
 }
 
 /// Translates `base.<idx>`.
@@ -1125,7 +1123,8 @@ fn trans_rvalue_dps_unadjusted<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
                              None,
                              expr.span,
                              expr.id,
-                             tcx.mk_struct(did, tcx.mk_substs(substs)),
+                             tcx.mk_struct(tcx.lookup_adt_def(did),
+                                           tcx.mk_substs(substs)),
                              dest)
             } else {
                 tcx.sess.span_bug(expr.span,
@@ -1248,8 +1247,8 @@ fn trans_def_dps_unadjusted<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
 
     match def {
         def::DefVariant(tid, vid, _) => {
-            let variant_info = bcx.tcx().enum_variant_with_id(tid, vid);
-            if !variant_info.args.is_empty() {
+            let variant = bcx.tcx().lookup_adt_def(tid).variant_with_id(vid);
+            if let ty::VariantKind::Tuple = variant.kind() {
                 // N-ary variant.
                 let llfn = callee::trans_fn_ref(bcx.ccx(), vid,
                                                 ExprId(ref_expr.id),
@@ -1260,15 +1259,14 @@ fn trans_def_dps_unadjusted<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
                 // Nullary variant.
                 let ty = expr_ty(bcx, ref_expr);
                 let repr = adt::represent_type(bcx.ccx(), ty);
-                adt::trans_set_discr(bcx, &*repr, lldest,
-                                     variant_info.disr_val);
+                adt::trans_set_discr(bcx, &*repr, lldest, variant.disr_val);
                 return bcx;
             }
         }
         def::DefStruct(_) => {
             let ty = expr_ty(bcx, ref_expr);
             match ty.sty {
-                ty::TyStruct(did, _) if bcx.tcx().has_dtor(did) => {
+                ty::TyStruct(def, _) if def.has_dtor(bcx.tcx()) => {
                     let repr = adt::represent_type(bcx.ccx(), ty);
                     adt::trans_set_discr(bcx, &*repr, lldest, 0);
                 }
@@ -1361,71 +1359,6 @@ pub fn trans_local_var<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
     }
 }
 
-/// Helper for enumerating the field types of structs, enums, or records. The optional node ID here
-/// is the node ID of the path identifying the enum variant in use. If none, this cannot possibly
-/// an enum variant (so, if it is and `node_id_opt` is none, this function panics).
-pub fn with_field_tys<'tcx, R, F>(tcx: &ty::ctxt<'tcx>,
-                                  ty: Ty<'tcx>,
-                                  node_id_opt: Option<ast::NodeId>,
-                                  op: F)
-                                  -> R where
-    F: FnOnce(ty::Disr, &[ty::Field<'tcx>]) -> R,
-{
-    match ty.sty {
-        ty::TyStruct(did, substs) => {
-            let fields = tcx.struct_fields(did, substs);
-            let fields = monomorphize::normalize_associated_type(tcx, &fields);
-            op(0, &fields[..])
-        }
-
-        ty::TyTuple(ref v) => {
-            let fields: Vec<_> = v.iter().enumerate().map(|(i, &f)| {
-                ty::Field {
-                    name: token::intern(&i.to_string()),
-                    mt: ty::TypeAndMut {
-                        ty: f,
-                        mutbl: ast::MutImmutable
-                    }
-                }
-            }).collect();
-            op(0, &fields)
-        }
-
-        ty::TyEnum(_, substs) => {
-            // We want the *variant* ID here, not the enum ID.
-            match node_id_opt {
-                None => {
-                    tcx.sess.bug(&format!(
-                        "cannot get field types from the enum type {:?} \
-                         without a node ID",
-                        ty));
-                }
-                Some(node_id) => {
-                    let def = tcx.def_map.borrow().get(&node_id).unwrap().full_def();
-                    match def {
-                        def::DefVariant(enum_id, variant_id, _) => {
-                            let variant_info = tcx.enum_variant_with_id(enum_id, variant_id);
-                            let fields = tcx.struct_fields(variant_id, substs);
-                            let fields = monomorphize::normalize_associated_type(tcx, &fields);
-                            op(variant_info.disr_val, &fields[..])
-                        }
-                        _ => {
-                            tcx.sess.bug("resolve didn't map this expr to a \
-                                          variant ID")
-                        }
-                    }
-                }
-            }
-        }
-
-        _ => {
-            tcx.sess.bug(&format!(
-                "cannot get field types from the type {:?}",
-                ty));
-        }
-    }
-}
-
 fn trans_struct<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
                             fields: &[ast::Field],
                             base: Option<&ast::Expr>,
@@ -1436,52 +1369,42 @@ fn trans_struct<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
     let _icx = push_ctxt("trans_rec");
 
     let tcx = bcx.tcx();
-    with_field_tys(tcx, ty, Some(expr_id), |discr, field_tys| {
-        let mut need_base = vec![true; field_tys.len()];
-
-        let numbered_fields = fields.iter().map(|field| {
-            let opt_pos =
-                field_tys.iter().position(|field_ty|
-                                          field_ty.name == field.ident.node.name);
-            let result = match opt_pos {
-                Some(i) => {
-                    need_base[i] = false;
-                    (i, &*field.expr)
-                }
-                None => {
-                    tcx.sess.span_bug(field.span,
-                                      "Couldn't find field in struct type")
+    let vinfo = VariantInfo::of_node(tcx, ty, expr_id);
+
+    let mut need_base = vec![true; vinfo.fields.len()];
+
+    let numbered_fields = fields.iter().map(|field| {
+        let pos = vinfo.field_index(field.ident.node.name);
+        need_base[pos] = false;
+        (pos, &*field.expr)
+    }).collect::<Vec<_>>();
+
+    let optbase = match base {
+        Some(base_expr) => {
+            let mut leftovers = Vec::new();
+            for (i, b) in need_base.iter().enumerate() {
+                if *b {
+                    leftovers.push((i, vinfo.fields[i].1));
                 }
-            };
-            result
-        }).collect::<Vec<_>>();
-        let optbase = match base {
-            Some(base_expr) => {
-                let mut leftovers = Vec::new();
-                for (i, b) in need_base.iter().enumerate() {
-                    if *b {
-                        leftovers.push((i, field_tys[i].mt.ty));
-                    }
-                }
-                Some(StructBaseInfo {expr: base_expr,
-                                     fields: leftovers })
             }
-            None => {
-                if need_base.iter().any(|b| *b) {
-                    tcx.sess.span_bug(expr_span, "missing fields and no base expr")
-                }
-                None
+            Some(StructBaseInfo {expr: base_expr,
+                                 fields: leftovers })
+        }
+        None => {
+            if need_base.iter().any(|b| *b) {
+                tcx.sess.span_bug(expr_span, "missing fields and no base expr")
             }
-        };
+            None
+        }
+    };
 
-        trans_adt(bcx,
-                  ty,
-                  discr,
-                  &numbered_fields,
-                  optbase,
-                  dest,
-                  DebugLoc::At(expr_id, expr_span))
-    })
+    trans_adt(bcx,
+              ty,
+              vinfo.discr,
+              &numbered_fields,
+              optbase,
+              dest,
+              DebugLoc::At(expr_id, expr_span))
 }
 
 /// Information that `trans_adt` needs in order to fill in the fields
@@ -1530,7 +1453,7 @@ pub fn trans_adt<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
     // panic occur before the ADT as a whole is ready.
     let custom_cleanup_scope = fcx.push_custom_cleanup_scope();
 
-    if ty.is_simd(bcx.tcx()) {
+    if ty.is_simd() {
         // Issue 23112: The original logic appeared vulnerable to same
         // order-of-eval bug. But, SIMD values are tuple-structs;
         // i.e. functional record update (FRU) syntax is unavailable.
@@ -1774,7 +1697,7 @@ fn trans_eager_binop<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
     let _icx = push_ctxt("trans_eager_binop");
 
     let tcx = bcx.tcx();
-    let is_simd = lhs_t.is_simd(tcx);
+    let is_simd = lhs_t.is_simd();
     let intype = if is_simd {
         lhs_t.simd_type(tcx)
     } else {
@@ -2125,8 +2048,8 @@ fn trans_imm_cast<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
         }
     }
 
-    let r_t_in = CastTy::from_ty(bcx.tcx(), t_in).expect("bad input type for cast");
-    let r_t_out = CastTy::from_ty(bcx.tcx(), t_out).expect("bad output type for cast");
+    let r_t_in = CastTy::from_ty(t_in).expect("bad input type for cast");
+    let r_t_out = CastTy::from_ty(t_out).expect("bad output type for cast");
 
     let (llexpr, signed) = if let Int(CEnum) = r_t_in {
         let repr = adt::represent_type(ccx, t_in);
@@ -2579,7 +2502,7 @@ fn build_unchecked_rshift<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
     // #1877, #10183: Ensure that input is always valid
     let rhs = shift_mask_rhs(bcx, rhs, binop_debug_loc);
     let tcx = bcx.tcx();
-    let is_simd = lhs_t.is_simd(tcx);
+    let is_simd = lhs_t.is_simd();
     let intype = if is_simd {
         lhs_t.simd_type(tcx)
     } else {
diff --git a/src/librustc_trans/trans/foreign.rs b/src/librustc_trans/trans/foreign.rs
index cafc0be74ba..225ff52a63c 100644
--- a/src/librustc_trans/trans/foreign.rs
+++ b/src/librustc_trans/trans/foreign.rs
@@ -449,7 +449,7 @@ pub fn trans_native_call<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
 fn gate_simd_ffi(tcx: &ty::ctxt, decl: &ast::FnDecl, ty: &ty::BareFnTy) {
     if !tcx.sess.features.borrow().simd_ffi {
         let check = |ast_ty: &ast::Ty, ty: ty::Ty| {
-            if ty.is_simd(tcx) {
+            if ty.is_simd() {
                 tcx.sess.span_err(ast_ty.span,
                               &format!("use of SIMD type `{}` in FFI is highly experimental and \
                                         may result in invalid code",
diff --git a/src/librustc_trans/trans/glue.rs b/src/librustc_trans/trans/glue.rs
index 18fedda4919..cf3cde8907f 100644
--- a/src/librustc_trans/trans/glue.rs
+++ b/src/librustc_trans/trans/glue.rs
@@ -415,11 +415,11 @@ pub fn size_and_align_of_dst<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, t: Ty<'tcx>, in
         return (size, align);
     }
     match t.sty {
-        ty::TyStruct(id, substs) => {
+        ty::TyStruct(def, substs) => {
             let ccx = bcx.ccx();
             // First get the size of all statically known fields.
             // Don't use type_of::sizing_type_of because that expects t to be sized.
-            assert!(!t.is_simd(bcx.tcx()));
+            assert!(!t.is_simd());
             let repr = adt::represent_type(ccx, t);
             let sizing_type = adt::sizing_type_context_of(ccx, &*repr, true);
             debug!("DST {} sizing_type: {}", t, sizing_type.to_string());
@@ -432,9 +432,8 @@ pub fn size_and_align_of_dst<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, t: Ty<'tcx>, in
 
             // Recurse to get the size of the dynamically sized field (must be
             // the last field).
-            let fields = bcx.tcx().struct_fields(id, substs);
-            let last_field = fields[fields.len()-1];
-            let field_ty = last_field.mt.ty;
+            let last_field = def.struct_variant().fields.last().unwrap();
+            let field_ty = monomorphize::field_ty(bcx.tcx(), substs, last_field);
             let (unsized_size, unsized_align) = size_and_align_of_dst(bcx, field_ty, info);
 
             let dbloc = DebugLoc::None;
@@ -562,27 +561,27 @@ fn make_drop_glue<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, v0: ValueRef, g: DropGlueK
                 })
             }
         }
-        ty::TyStruct(did, substs) | ty::TyEnum(did, substs) => {
+        ty::TyStruct(def, substs) | ty::TyEnum(def, substs) => {
             let tcx = bcx.tcx();
-            match (tcx.ty_dtor(did), skip_dtor) {
+            match (tcx.ty_dtor(def.did), skip_dtor) {
                 (ty::TraitDtor(dtor, true), false) => {
                     // FIXME(16758) Since the struct is unsized, it is hard to
                     // find the drop flag (which is at the end of the struct).
                     // Lets just ignore the flag and pretend everything will be
                     // OK.
                     if type_is_sized(bcx.tcx(), t) {
-                        trans_struct_drop_flag(bcx, t, v0, dtor, did, substs)
+                        trans_struct_drop_flag(bcx, t, v0, dtor, def.did, substs)
                     } else {
                         // Give the user a heads up that we are doing something
                         // stupid and dangerous.
                         bcx.sess().warn(&format!("Ignoring drop flag in destructor for {}\
                                                  because the struct is unsized. See issue\
                                                  #16758", t));
-                        trans_struct_drop(bcx, t, v0, dtor, did, substs)
+                        trans_struct_drop(bcx, t, v0, dtor, def.did, substs)
                     }
                 }
                 (ty::TraitDtor(dtor, false), false) => {
-                    trans_struct_drop(bcx, t, v0, dtor, did, substs)
+                    trans_struct_drop(bcx, t, v0, dtor, def.did, substs)
                 }
                 (ty::NoDtor, _) | (_, true) => {
                     // No dtor? Just the default case
diff --git a/src/librustc_trans/trans/inline.rs b/src/librustc_trans/trans/inline.rs
index 75c80690f2a..01bfc51a5c0 100644
--- a/src/librustc_trans/trans/inline.rs
+++ b/src/librustc_trans/trans/inline.rs
@@ -100,30 +100,32 @@ fn instantiate_inline(ccx: &CrateContext, fn_id: ast::DefId)
             ccx.external().borrow_mut().insert(parent_id, Some(item.id));
             ccx.external_srcs().borrow_mut().insert(item.id, parent_id);
 
-          let mut my_id = 0;
-          match item.node {
-            ast::ItemEnum(_, _) => {
-              let vs_here = ccx.tcx().enum_variants(local_def(item.id));
-              let vs_there = ccx.tcx().enum_variants(parent_id);
-              for (here, there) in vs_here.iter().zip(vs_there.iter()) {
-                  if there.id == fn_id { my_id = here.id.node; }
-                  ccx.external().borrow_mut().insert(there.id, Some(here.id.node));
-              }
-            }
-            ast::ItemStruct(ref struct_def, _) => {
-              match struct_def.ctor_id {
-                None => {}
-                Some(ctor_id) => {
-                    ccx.external().borrow_mut().insert(fn_id, Some(ctor_id));
-                    my_id = ctor_id;
+            let mut my_id = 0;
+            match item.node {
+                ast::ItemEnum(ref ast_def, _) => {
+                    let ast_vs = &ast_def.variants;
+                    let ty_vs = &ccx.tcx().lookup_adt_def(parent_id).variants;
+                    assert_eq!(ast_vs.len(), ty_vs.len());
+                    for (ast_v, ty_v) in ast_vs.iter().zip(ty_vs.iter()) {
+                        if ty_v.did == fn_id { my_id = ast_v.node.id; }
+                        ccx.external().borrow_mut().insert(ty_v.did, Some(ast_v.node.id));
+                    }
                 }
-              }
-            }
-            _ => ccx.sess().bug("instantiate_inline: item has a \
+                ast::ItemStruct(ref struct_def, _) => {
+                    match struct_def.ctor_id {
+                        None => ccx.sess().bug("instantiate_inline: called on a \
+                                                non-tuple struct"),
+                        Some(ctor_id) => {
+                            ccx.external().borrow_mut().insert(fn_id, Some(ctor_id));
+                            my_id = ctor_id;
+                        }
+                    }
+                }
+                _ => ccx.sess().bug("instantiate_inline: item has a \
                                  non-enum, non-struct parent")
-          }
-          trans_item(ccx, &**item);
-          my_id
+            }
+            trans_item(ccx, &**item);
+            my_id
         }
         csearch::FoundAst::FoundParent(_, _) => {
             ccx.sess().bug("maybe_get_item_ast returned a FoundParent \
diff --git a/src/librustc_trans/trans/monomorphize.rs b/src/librustc_trans/trans/monomorphize.rs
index 31e4b9c48e2..c2d1d19935a 100644
--- a/src/librustc_trans/trans/monomorphize.rs
+++ b/src/librustc_trans/trans/monomorphize.rs
@@ -29,7 +29,6 @@ use middle::ty::{self, HasTypeFlags, Ty};
 
 use syntax::abi;
 use syntax::ast;
-use syntax::ast_util::local_def;
 use syntax::attr;
 use syntax::codemap::DUMMY_SP;
 use std::hash::{Hasher, Hash, SipHasher};
@@ -192,24 +191,11 @@ pub fn monomorphic_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
             }
         }
         ast_map::NodeVariant(v) => {
-            let parent = ccx.tcx().map.get_parent(fn_id.node);
-            let tvs = ccx.tcx().enum_variants(local_def(parent));
-            let this_tv = tvs.iter().find(|tv| { tv.id.node == fn_id.node}).unwrap();
+            let variant = inlined_variant_def(ccx, fn_id.node);
+            assert_eq!(v.node.name.name, variant.name);
             let d = mk_lldecl(abi::Rust);
             attributes::inline(d, attributes::InlineAttr::Hint);
-            match v.node.kind {
-                ast::TupleVariantKind(ref args) => {
-                    trans_enum_variant(ccx,
-                                       parent,
-                                       &*v,
-                                       &args[..],
-                                       this_tv.disr_val,
-                                       psubsts,
-                                       d);
-                }
-                ast::StructVariantKind(_) =>
-                    ccx.sess().bug("can't monomorphize struct variants"),
-            }
+            trans_enum_variant(ccx, fn_id.node, variant.disr_val, psubsts, d);
             d
         }
         ast_map::NodeImplItem(impl_item) => {
@@ -255,7 +241,6 @@ pub fn monomorphic_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
             let d = mk_lldecl(abi::Rust);
             attributes::inline(d, attributes::InlineAttr::Hint);
             base::trans_tuple_struct(ccx,
-                                     &struct_def.fields,
                                      struct_def.ctor_id.expect("ast-mapped tuple struct \
                                                                 didn't have a ctor id"),
                                      psubsts,
@@ -302,6 +287,16 @@ pub fn apply_param_substs<'tcx,T>(tcx: &ty::ctxt<'tcx>,
     normalize_associated_type(tcx, &substituted)
 }
 
+
+/// Returns the normalized type of a struct field
+pub fn field_ty<'tcx>(tcx: &ty::ctxt<'tcx>,
+                      param_substs: &Substs<'tcx>,
+                      f: ty::FieldDef<'tcx>)
+                      -> Ty<'tcx>
+{
+    normalize_associated_type(tcx, &f.ty(tcx, param_substs))
+}
+
 /// Removes associated types, if any. Since this during
 /// monomorphization, we know that only concrete types are involved,
 /// and hence we can be sure that all associated types will be
diff --git a/src/librustc_trans/trans/type_of.rs b/src/librustc_trans/trans/type_of.rs
index c5161a8bc2e..0b969360f53 100644
--- a/src/librustc_trans/trans/type_of.rs
+++ b/src/librustc_trans/trans/type_of.rs
@@ -222,7 +222,7 @@ pub fn sizing_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>) -> Typ
         }
 
         ty::TyStruct(..) => {
-            if t.is_simd(cx.tcx()) {
+            if t.is_simd() {
                 let llet = type_of(cx, t.simd_type(cx.tcx()));
                 let n = t.simd_size(cx.tcx()) as u64;
                 ensure_array_fits_in_address_space(cx, llet, n, t);
@@ -333,14 +333,14 @@ pub fn in_memory_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>) ->
       ty::TyInt(t) => Type::int_from_ty(cx, t),
       ty::TyUint(t) => Type::uint_from_ty(cx, t),
       ty::TyFloat(t) => Type::float_from_ty(cx, t),
-      ty::TyEnum(did, ref substs) => {
+      ty::TyEnum(def, ref substs) => {
           // Only create the named struct, but don't fill it in. We
           // fill it in *after* placing it into the type cache. This
           // avoids creating more than one copy of the enum when one
           // of the enum's variants refers to the enum itself.
           let repr = adt::represent_type(cx, t);
           let tps = substs.types.get_slice(subst::TypeSpace);
-          let name = llvm_type_name(cx, did, tps);
+          let name = llvm_type_name(cx, def.did, tps);
           adt::incomplete_type_of(cx, &*repr, &name[..])
       }
       ty::TyClosure(..) => {
@@ -403,8 +403,8 @@ pub fn in_memory_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>) ->
           let repr = adt::represent_type(cx, t);
           adt::type_of(cx, &*repr)
       }
-      ty::TyStruct(did, ref substs) => {
-          if t.is_simd(cx.tcx()) {
+      ty::TyStruct(def, ref substs) => {
+          if t.is_simd() {
               let llet = in_memory_type_of(cx, t.simd_type(cx.tcx()));
               let n = t.simd_size(cx.tcx()) as u64;
               ensure_array_fits_in_address_space(cx, llet, n, t);
@@ -415,7 +415,7 @@ pub fn in_memory_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>) ->
               // infinite recursion with recursive struct types.
               let repr = adt::represent_type(cx, t);
               let tps = substs.types.get_slice(subst::TypeSpace);
-              let name = llvm_type_name(cx, did, tps);
+              let name = llvm_type_name(cx, def.did, tps);
               adt::incomplete_type_of(cx, &*repr, &name[..])
           }
       }
@@ -436,7 +436,7 @@ pub fn in_memory_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>) ->
     // If this was an enum or struct, fill in the type now.
     match t.sty {
         ty::TyEnum(..) | ty::TyStruct(..) | ty::TyClosure(..)
-                if !t.is_simd(cx.tcx()) => {
+                if !t.is_simd() => {
             let repr = adt::represent_type(cx, t);
             adt::finish_type_of(cx, &*repr, &mut llty);
         }
diff --git a/src/librustc_typeck/check/_match.rs b/src/librustc_typeck/check/_match.rs
index b853a5300e2..883e3659720 100644
--- a/src/librustc_typeck/check/_match.rs
+++ b/src/librustc_typeck/check/_match.rs
@@ -528,7 +528,7 @@ pub fn check_pat_struct<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>, pat: &'tcx ast::Pat,
     let tcx = pcx.fcx.ccx.tcx;
 
     let def = tcx.def_map.borrow().get(&pat.id).unwrap().full_def();
-    let (enum_def_id, variant_def_id) = match def {
+    let (adt_def, variant) = match def {
         def::DefTrait(_) => {
             let name = pprust::path_to_string(path);
             span_err!(tcx.sess, pat.span, E0168,
@@ -543,11 +543,11 @@ pub fn check_pat_struct<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>, pat: &'tcx ast::Pat,
         _ => {
             let def_type = tcx.lookup_item_type(def.def_id());
             match def_type.ty.sty {
-                ty::TyStruct(struct_def_id, _) =>
-                    (struct_def_id, struct_def_id),
-                ty::TyEnum(enum_def_id, _)
-                    if def == def::DefVariant(enum_def_id, def.def_id(), true) =>
-                    (enum_def_id, def.def_id()),
+                ty::TyStruct(struct_def, _) =>
+                    (struct_def, struct_def.struct_variant()),
+                ty::TyEnum(enum_def, _)
+                    if def == def::DefVariant(enum_def.did, def.def_id(), true) =>
+                    (enum_def, enum_def.variant_of_def(def)),
                 _ => {
                     let name = pprust::path_to_string(path);
                     span_err!(tcx.sess, pat.span, E0163,
@@ -565,8 +565,8 @@ pub fn check_pat_struct<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>, pat: &'tcx ast::Pat,
 
     instantiate_path(pcx.fcx,
                      &path.segments,
-                     tcx.lookup_item_type(enum_def_id),
-                     &tcx.lookup_predicates(enum_def_id),
+                     adt_def.type_scheme(tcx),
+                     &adt_def.predicates(tcx),
                      None,
                      def,
                      pat.span,
@@ -581,9 +581,7 @@ pub fn check_pat_struct<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>, pat: &'tcx ast::Pat,
         .map(|substs| substs.substs.clone())
         .unwrap_or_else(|| Substs::empty());
 
-    let struct_fields = tcx.struct_fields(variant_def_id, &item_substs);
-    check_struct_pat_fields(pcx, pat.span, fields, &struct_fields,
-                            variant_def_id, etc);
+    check_struct_pat_fields(pcx, pat.span, fields, variant, &item_substs, etc);
 }
 
 pub fn check_pat_enum<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>,
@@ -647,22 +645,26 @@ pub fn check_pat_enum<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>,
 
     let real_path_ty = fcx.node_ty(pat.id);
     let (arg_tys, kind_name): (Vec<_>, &'static str) = match real_path_ty.sty {
-        ty::TyEnum(enum_def_id, expected_substs)
-            if def == def::DefVariant(enum_def_id, def.def_id(), false) =>
+        ty::TyEnum(enum_def, expected_substs)
+            if def == def::DefVariant(enum_def.did, def.def_id(), false) =>
         {
-            let variant = tcx.enum_variant_with_id(enum_def_id, def.def_id());
-            (variant.args.iter()
-                         .map(|t| fcx.instantiate_type_scheme(pat.span, expected_substs, t))
-                         .collect(),
+            let variant = enum_def.variant_of_def(def);
+            (variant.fields
+                    .iter()
+                    .map(|f| fcx.instantiate_type_scheme(pat.span,
+                                                         expected_substs,
+                                                         &f.unsubst_ty()))
+                    .collect(),
              "variant")
         }
-        ty::TyStruct(struct_def_id, expected_substs) => {
-            let struct_fields = tcx.struct_fields(struct_def_id, expected_substs);
-            (struct_fields.iter()
-                          .map(|field| fcx.instantiate_type_scheme(pat.span,
-                                                                   expected_substs,
-                                                                   &field.mt.ty))
-                          .collect(),
+        ty::TyStruct(struct_def, expected_substs) => {
+            (struct_def.struct_variant()
+                       .fields
+                       .iter()
+                       .map(|f| fcx.instantiate_type_scheme(pat.span,
+                                                            expected_substs,
+                                                            &f.unsubst_ty()))
+                       .collect(),
              "struct")
         }
         _ => {
@@ -715,15 +717,15 @@ pub fn check_pat_enum<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>,
 pub fn check_struct_pat_fields<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>,
                                          span: Span,
                                          fields: &'tcx [Spanned<ast::FieldPat>],
-                                         struct_fields: &[ty::Field<'tcx>],
-                                         struct_id: ast::DefId,
+                                         variant: ty::VariantDef<'tcx>,
+                                         substs: &Substs<'tcx>,
                                          etc: bool) {
     let tcx = pcx.fcx.ccx.tcx;
 
     // Index the struct fields' types.
-    let field_type_map = struct_fields
+    let field_map = variant.fields
         .iter()
-        .map(|field| (field.name, field.mt.ty))
+        .map(|field| (field.name, field))
         .collect::<FnvHashMap<_, _>>();
 
     // Keep track of which fields have already appeared in the pattern.
@@ -731,7 +733,7 @@ pub fn check_struct_pat_fields<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>,
 
     // Typecheck each field.
     for &Spanned { node: ref field, span } in fields {
-        let field_type = match used_fields.entry(field.ident.name) {
+        let field_ty = match used_fields.entry(field.ident.name) {
             Occupied(occupied) => {
                 span_err!(tcx.sess, span, E0025,
                     "field `{}` bound multiple times in the pattern",
@@ -743,25 +745,24 @@ pub fn check_struct_pat_fields<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>,
             }
             Vacant(vacant) => {
                 vacant.insert(span);
-                field_type_map.get(&field.ident.name).cloned()
+                field_map.get(&field.ident.name)
+                    .map(|f| pcx.fcx.field_ty(span, f, substs))
                     .unwrap_or_else(|| {
                         span_err!(tcx.sess, span, E0026,
                             "struct `{}` does not have a field named `{}`",
-                            tcx.item_path_str(struct_id),
+                            tcx.item_path_str(variant.did),
                             field.ident);
                         tcx.types.err
                     })
             }
         };
 
-        let field_type = pcx.fcx.normalize_associated_types_in(span, &field_type);
-
-        check_pat(pcx, &*field.pat, field_type);
+        check_pat(pcx, &*field.pat, field_ty);
     }
 
     // Report an error if not all the fields were specified.
     if !etc {
-        for field in struct_fields
+        for field in variant.fields
             .iter()
             .filter(|field| !used_fields.contains_key(&field.name)) {
             span_err!(tcx.sess, span, E0027,
diff --git a/src/librustc_typeck/check/cast.rs b/src/librustc_typeck/check/cast.rs
index 883b972872f..b6ba62e4f09 100644
--- a/src/librustc_typeck/check/cast.rs
+++ b/src/librustc_typeck/check/cast.rs
@@ -79,10 +79,11 @@ fn unsize_kind<'a,'tcx>(fcx: &FnCtxt<'a, 'tcx>,
     match t.sty {
         ty::TySlice(_) | ty::TyStr => Some(UnsizeKind::Length),
         ty::TyTrait(ref tty) => Some(UnsizeKind::Vtable(tty.principal_def_id())),
-        ty::TyStruct(did, substs) => {
-            match fcx.tcx().struct_fields(did, substs).pop() {
+        ty::TyStruct(def, substs) => {
+            // FIXME(arielb1): do some kind of normalization
+            match def.struct_variant().fields.last() {
                 None => None,
-                Some(f) => unsize_kind(fcx, f.mt.ty)
+                Some(f) => unsize_kind(fcx, f.ty(fcx.tcx(), substs))
             }
         }
         // We should really try to normalize here.
@@ -223,8 +224,8 @@ impl<'tcx> CastCheck<'tcx> {
         use middle::cast::IntTy::*;
         use middle::cast::CastTy::*;
 
-        let (t_from, t_cast) = match (CastTy::from_ty(fcx.tcx(), self.expr_ty),
-                                      CastTy::from_ty(fcx.tcx(), self.cast_ty)) {
+        let (t_from, t_cast) = match (CastTy::from_ty(self.expr_ty),
+                                      CastTy::from_ty(self.cast_ty)) {
             (Some(t_from), Some(t_cast)) => (t_from, t_cast),
             _ => {
                 return Err(CastError::NonScalar)
diff --git a/src/librustc_typeck/check/dropck.rs b/src/librustc_typeck/check/dropck.rs
index 24e67860998..39e67beab58 100644
--- a/src/librustc_typeck/check/dropck.rs
+++ b/src/librustc_typeck/check/dropck.rs
@@ -18,6 +18,7 @@ use util::nodemap::FnvHashSet;
 
 use syntax::ast;
 use syntax::codemap::{self, Span};
+use syntax::parse::token::special_idents;
 
 /// check_drop_impl confirms that the Drop implementation identfied by
 /// `drop_impl_did` is not any more specialized than the type it is
@@ -41,18 +42,18 @@ pub fn check_drop_impl(tcx: &ty::ctxt, drop_impl_did: ast::DefId) -> Result<(),
                          ty: dtor_self_type } = tcx.lookup_item_type(drop_impl_did);
     let dtor_predicates = tcx.lookup_predicates(drop_impl_did);
     match dtor_self_type.sty {
-        ty::TyEnum(self_type_did, self_to_impl_substs) |
-        ty::TyStruct(self_type_did, self_to_impl_substs) => {
+        ty::TyEnum(adt_def, self_to_impl_substs) |
+        ty::TyStruct(adt_def, self_to_impl_substs) => {
             try!(ensure_drop_params_and_item_params_correspond(tcx,
                                                                drop_impl_did,
                                                                dtor_generics,
                                                                &dtor_self_type,
-                                                               self_type_did));
+                                                               adt_def.did));
 
             ensure_drop_predicates_are_implied_by_item_defn(tcx,
                                                             drop_impl_did,
                                                             &dtor_predicates,
-                                                            self_type_did,
+                                                            adt_def.did,
                                                             self_to_impl_substs)
         }
         _ => {
@@ -285,25 +286,26 @@ pub fn check_safety_of_destructor_if_necessary<'a, 'tcx>(rcx: &mut Rcx<'a, 'tcx>
                     // no need for an additional note if the overflow
                     // was somehow on the root.
                 }
-                TypeContext::EnumVariant { def_id, variant, arg_index } => {
-                    // FIXME (pnkfelix): eventually lookup arg_name
-                    // for the given index on struct variants.
-                    span_note!(
-                        rcx.tcx().sess,
-                        span,
-                        "overflowed on enum {} variant {} argument {} type: {}",
-                        tcx.item_path_str(def_id),
-                        variant,
-                        arg_index,
-                        detected_on_typ);
-                }
-                TypeContext::Struct { def_id, field } => {
+                TypeContext::ADT { def_id, variant, field, field_index } => {
+                    let adt = tcx.lookup_adt_def(def_id);
+                    let variant_name = match adt.adt_kind() {
+                        ty::AdtKind::Enum => format!("enum {} variant {}",
+                                                     tcx.item_path_str(def_id),
+                                                     variant),
+                        ty::AdtKind::Struct => format!("struct {}",
+                                                       tcx.item_path_str(def_id))
+                    };
+                    let field_name = if field == special_idents::unnamed_field.name {
+                        format!("#{}", field_index)
+                    } else {
+                        format!("`{}`", field)
+                    };
                     span_note!(
                         rcx.tcx().sess,
                         span,
-                        "overflowed on struct {} field {} type: {}",
-                        tcx.item_path_str(def_id),
-                        field,
+                        "overflowed on {} field {} type: {}",
+                        variant_name,
+                        field_name,
                         detected_on_typ);
                 }
             }
@@ -318,14 +320,11 @@ enum Error<'tcx> {
 #[derive(Copy, Clone)]
 enum TypeContext {
     Root,
-    EnumVariant {
+    ADT {
         def_id: ast::DefId,
         variant: ast::Name,
-        arg_index: usize,
-    },
-    Struct {
-        def_id: ast::DefId,
         field: ast::Name,
+        field_index: usize
     }
 }
 
@@ -357,8 +356,6 @@ fn iterate_over_potentially_unsafe_regions_in_type<'a, 'b, 'tcx>(
         return Err(Error::Overflow(context, ty))
     }
 
-    let opt_phantom_data_def_id = tcx.lang_items.phantom_data();
-
     if !cx.breadcrumbs.insert(ty) {
         debug!("iterate_over_potentially_unsafe_regions_in_type \
                {}ty: {} scope: {:?} - cached",
@@ -399,7 +396,7 @@ fn iterate_over_potentially_unsafe_regions_in_type<'a, 'b, 'tcx>(
     // type parameters are unbounded.  If both conditions hold, we
     // simply skip the `type_must_outlive` call entirely (but
     // resume the recursive checking of the type-substructure).
-    if has_dtor_of_interest(tcx, ty, cx.span) {
+    if has_dtor_of_interest(tcx, ty) {
         debug!("iterate_over_potentially_unsafe_regions_in_type \
                 {}ty: {} - is a dtorck type!",
                (0..depth).map(|_| ' ').collect::<String>(),
@@ -432,46 +429,30 @@ fn iterate_over_potentially_unsafe_regions_in_type<'a, 'b, 'tcx>(
                 cx, context, ity, depth+1)
         }
 
-        ty::TyStruct(did, substs) if Some(did) == opt_phantom_data_def_id => {
+        ty::TyStruct(def, substs) if def.is_phantom_data() => {
             // PhantomData<T> - behaves identically to T
             let ity = *substs.types.get(subst::TypeSpace, 0);
             iterate_over_potentially_unsafe_regions_in_type(
                 cx, context, ity, depth+1)
         }
 
-        ty::TyStruct(did, substs) => {
-            let fields = tcx.lookup_struct_fields(did);
-            for field in &fields {
-                let fty = tcx.lookup_field_type(did, field.id, substs);
-                let fty = cx.rcx.fcx.resolve_type_vars_if_possible(
-                    cx.rcx.fcx.normalize_associated_types_in(cx.span, &fty));
-                try!(iterate_over_potentially_unsafe_regions_in_type(
-                    cx,
-                    TypeContext::Struct {
-                        def_id: did,
-                        field: field.name,
-                    },
-                    fty,
-                    depth+1))
-            }
-            Ok(())
-        }
-
-        ty::TyEnum(did, substs) => {
-            let all_variant_info = tcx.substd_enum_variants(did, substs);
-            for variant_info in &all_variant_info {
-                for (i, fty) in variant_info.args.iter().enumerate() {
+        ty::TyStruct(def, substs) | ty::TyEnum(def, substs) => {
+            let did = def.did;
+            for variant in &def.variants {
+                for (i, field) in variant.fields.iter().enumerate() {
+                    let fty = field.ty(tcx, substs);
                     let fty = cx.rcx.fcx.resolve_type_vars_if_possible(
                         cx.rcx.fcx.normalize_associated_types_in(cx.span, &fty));
                     try!(iterate_over_potentially_unsafe_regions_in_type(
                         cx,
-                        TypeContext::EnumVariant {
+                        TypeContext::ADT {
                             def_id: did,
-                            variant: variant_info.name,
-                            arg_index: i,
+                            field: field.name,
+                            variant: variant.name,
+                            field_index: i
                         },
                         fty,
-                        depth+1));
+                        depth+1))
                 }
             }
             Ok(())
@@ -510,102 +491,10 @@ fn iterate_over_potentially_unsafe_regions_in_type<'a, 'b, 'tcx>(
 }
 
 fn has_dtor_of_interest<'tcx>(tcx: &ty::ctxt<'tcx>,
-                              ty: ty::Ty<'tcx>,
-                              span: Span) -> bool {
+                              ty: ty::Ty<'tcx>) -> bool {
     match ty.sty {
-        ty::TyEnum(def_id, _) | ty::TyStruct(def_id, _) => {
-            let dtor_method_did = match tcx.destructor_for_type.borrow().get(&def_id) {
-                Some(def_id) => *def_id,
-                None => {
-                    debug!("ty: {:?} has no dtor, and thus isn't a dropck type", ty);
-                    return false;
-                }
-            };
-            let impl_did = tcx.impl_of_method(dtor_method_did)
-                .unwrap_or_else(|| {
-                    tcx.sess.span_bug(
-                        span, "no Drop impl found for drop method")
-                });
-
-            let dtor_typescheme = tcx.lookup_item_type(impl_did);
-            let dtor_generics = dtor_typescheme.generics;
-
-            let mut has_pred_of_interest = false;
-
-            let mut seen_items = Vec::new();
-            let mut items_to_inspect = vec![impl_did];
-            'items: while let Some(item_def_id) = items_to_inspect.pop() {
-                if seen_items.contains(&item_def_id) {
-                    continue;
-                }
-
-                for pred in tcx.lookup_predicates(item_def_id).predicates {
-                    let result = match pred {
-                        ty::Predicate::Equate(..) |
-                        ty::Predicate::RegionOutlives(..) |
-                        ty::Predicate::TypeOutlives(..) |
-                        ty::Predicate::Projection(..) => {
-                            // For now, assume all these where-clauses
-                            // may give drop implementation capabilty
-                            // to access borrowed data.
-                            true
-                        }
-
-                        ty::Predicate::Trait(ty::Binder(ref t_pred)) => {
-                            let def_id = t_pred.trait_ref.def_id;
-                            if tcx.trait_items(def_id).len() != 0 {
-                                // If trait has items, assume it adds
-                                // capability to access borrowed data.
-                                true
-                            } else {
-                                // Trait without items is itself
-                                // uninteresting from POV of dropck.
-                                //
-                                // However, may have parent w/ items;
-                                // so schedule checking of predicates,
-                                items_to_inspect.push(def_id);
-                                // and say "no capability found" for now.
-                                false
-                            }
-                        }
-                    };
-
-                    if result {
-                        has_pred_of_interest = true;
-                        debug!("ty: {:?} has interesting dtor due to generic preds, e.g. {:?}",
-                               ty, pred);
-                        break 'items;
-                    }
-                }
-
-                seen_items.push(item_def_id);
-            }
-
-            // In `impl<'a> Drop ...`, we automatically assume
-            // `'a` is meaningful and thus represents a bound
-            // through which we could reach borrowed data.
-            //
-            // FIXME (pnkfelix): In the future it would be good to
-            // extend the language to allow the user to express,
-            // in the impl signature, that a lifetime is not
-            // actually used (something like `where 'a: ?Live`).
-            let has_region_param_of_interest =
-                dtor_generics.has_region_params(subst::TypeSpace);
-
-            let has_dtor_of_interest =
-                has_region_param_of_interest ||
-                has_pred_of_interest;
-
-            if has_dtor_of_interest {
-                debug!("ty: {:?} has interesting dtor, due to \
-                        region params: {} or pred: {}",
-                       ty,
-                       has_region_param_of_interest,
-                       has_pred_of_interest);
-            } else {
-                debug!("ty: {:?} has dtor, but it is uninteresting", ty);
-            }
-            has_dtor_of_interest
+        ty::TyEnum(def, _) | ty::TyStruct(def, _) => {
+            def.is_dtorck(tcx)
         }
         ty::TyTrait(..) | ty::TyProjection(..) => {
             debug!("ty: {:?} isn't known, and therefore is a dropck type", ty);
diff --git a/src/librustc_typeck/check/method/probe.rs b/src/librustc_typeck/check/method/probe.rs
index 44d769a799f..2ff40b3590d 100644
--- a/src/librustc_typeck/check/method/probe.rs
+++ b/src/librustc_typeck/check/method/probe.rs
@@ -286,10 +286,9 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
                 self.assemble_inherent_candidates_from_object(self_ty, data);
                 self.assemble_inherent_impl_candidates_for_type(data.principal_def_id());
             }
-            ty::TyEnum(did, _) |
-            ty::TyStruct(did, _) |
-            ty::TyClosure(did, _) => {
-                self.assemble_inherent_impl_candidates_for_type(did);
+            ty::TyEnum(def, _) |
+            ty::TyStruct(def, _) => {
+                self.assemble_inherent_impl_candidates_for_type(def.did);
             }
             ty::TyBox(_) => {
                 if let Some(box_did) = self.tcx().lang_items.owned_box() {
diff --git a/src/librustc_typeck/check/method/suggest.rs b/src/librustc_typeck/check/method/suggest.rs
index f14886606c2..15cc5ee6eb8 100644
--- a/src/librustc_typeck/check/method/suggest.rs
+++ b/src/librustc_typeck/check/method/suggest.rs
@@ -65,10 +65,8 @@ pub fn report_error<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
                 None);
 
             // If the item has the name of a field, give a help note
-            if let (&ty::TyStruct(did, substs), Some(expr)) = (&rcvr_ty.sty, rcvr_expr) {
-                let fields = cx.lookup_struct_fields(did);
-
-                if let Some(field) = fields.iter().find(|f| f.name == item_name) {
+            if let (&ty::TyStruct(def, substs), Some(expr)) = (&rcvr_ty.sty, rcvr_expr) {
+                if let Some(field) = def.struct_variant().find_field_named(item_name) {
                     let expr_string = match cx.sess.codemap().span_to_snippet(expr.span) {
                         Ok(expr_string) => expr_string,
                         _ => "s".into() // Default to a generic placeholder for the
@@ -89,7 +87,7 @@ pub fn report_error<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
                     };
 
                     // Determine if the field can be used as a function in some way
-                    let field_ty = cx.lookup_field_type(did, field.id, substs);
+                    let field_ty = field.ty(cx, substs);
                     if let Ok(fn_once_trait_did) = cx.lang_items.require(FnOnceTraitLangItem) {
                         let infcx = fcx.infcx();
                         infcx.probe(|_| {
@@ -303,7 +301,7 @@ fn type_derefs_to_local<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
                                   rcvr_expr: Option<&ast::Expr>) -> bool {
     fn is_local(ty: Ty) -> bool {
         match ty.sty {
-            ty::TyEnum(did, _) | ty::TyStruct(did, _) => ast_util::is_local(did),
+            ty::TyEnum(def, _) | ty::TyStruct(def, _) => ast_util::is_local(def.did),
 
             ty::TyTrait(ref tr) => ast_util::is_local(tr.principal_def_id()),
 
diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs
index 6851fb46670..6221134afd3 100644
--- a/src/librustc_typeck/check/mod.rs
+++ b/src/librustc_typeck/check/mod.rs
@@ -1674,34 +1674,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         }
     }
 
-    // Only for fields! Returns <none> for methods>
-    // Indifferent to privacy flags
-    pub fn lookup_field_ty(&self,
-                           span: Span,
-                           class_id: ast::DefId,
-                           items: &[ty::FieldTy],
-                           fieldname: ast::Name,
-                           substs: &subst::Substs<'tcx>)
-                           -> Option<Ty<'tcx>>
-    {
-        let o_field = items.iter().find(|f| f.name == fieldname);
-        o_field.map(|f| self.tcx().lookup_field_type(class_id, f.id, substs))
-               .map(|t| self.normalize_associated_types_in(span, &t))
-    }
-
-    pub fn lookup_tup_field_ty(&self,
-                               span: Span,
-                               class_id: ast::DefId,
-                               items: &[ty::FieldTy],
-                               idx: usize,
-                               substs: &subst::Substs<'tcx>)
-                               -> Option<Ty<'tcx>>
+    // FIXME(arielb1): use this instead of field.ty everywhere
+    pub fn field_ty(&self,
+                    span: Span,
+                    field: ty::FieldDef<'tcx>,
+                    substs: &Substs<'tcx>)
+                    -> Ty<'tcx>
     {
-        let o_field = if idx < items.len() { Some(&items[idx]) } else { None };
-        o_field.map(|f| self.tcx().lookup_field_type(class_id, f.id, substs))
-               .map(|t| self.normalize_associated_types_in(span, &t))
+        self.normalize_associated_types_in(span,
+                                           &field.ty(self.tcx(), substs))
     }
 
+    // Only for fields! Returns <none> for methods>
+    // Indifferent to privacy flags
     fn check_casts(&self) {
         let mut deferred_cast_checks = self.inh.deferred_cast_checks.borrow_mut();
         for cast in deferred_cast_checks.drain(..) {
@@ -2878,11 +2863,11 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
                                                   lvalue_pref,
                                                   |base_t, _| {
                 match base_t.sty {
-                    ty::TyStruct(base_id, substs) => {
+                    ty::TyStruct(base_def, substs) => {
                         debug!("struct named {:?}",  base_t);
-                        let fields = tcx.lookup_struct_fields(base_id);
-                        fcx.lookup_field_ty(expr.span, base_id, &fields[..],
-                                            field.node.name, &(*substs))
+                        base_def.struct_variant()
+                                .find_field_named(field.node.name)
+                                .map(|f| fcx.field_ty(expr.span, f, substs))
                     }
                     _ => None
                 }
@@ -2919,8 +2904,8 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
                             actual)
                 },
                 expr_t, None);
-            if let ty::TyStruct(did, _) = expr_t.sty {
-                suggest_field_names(did, field, tcx, vec![]);
+            if let ty::TyStruct(def, _) = expr_t.sty {
+                suggest_field_names(def.struct_variant(), field, tcx, vec![]);
             }
         }
 
@@ -2928,23 +2913,22 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
     }
 
     // displays hints about the closest matches in field names
-    fn suggest_field_names<'tcx>(id : DefId,
-                                 field : &ast::SpannedIdent,
-                                 tcx : &ty::ctxt<'tcx>,
+    fn suggest_field_names<'tcx>(variant: ty::VariantDef<'tcx>,
+                                 field: &ast::SpannedIdent,
+                                 tcx: &ty::ctxt<'tcx>,
                                  skip : Vec<InternedString>) {
         let name = field.node.name.as_str();
         // only find fits with at least one matching letter
         let mut best_dist = name.len();
-        let fields = tcx.lookup_struct_fields(id);
         let mut best = None;
-        for elem in &fields {
+        for elem in &variant.fields {
             let n = elem.name.as_str();
             // ignore already set fields
             if skip.iter().any(|x| *x == n) {
                 continue;
             }
             // ignore private fields from non-local crates
-            if id.krate != ast::LOCAL_CRATE && elem.vis != Visibility::Public {
+            if variant.did.krate != ast::LOCAL_CRATE && elem.vis != Visibility::Public {
                 continue;
             }
             let dist = lev_distance(&n, &name);
@@ -2965,7 +2949,6 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
                                 lvalue_pref: LvaluePreference,
                                 base: &'tcx ast::Expr,
                                 idx: codemap::Spanned<usize>) {
-        let tcx = fcx.ccx.tcx;
         check_expr_with_lvalue_pref(fcx, base, lvalue_pref);
         let expr_t = structurally_resolved_type(fcx, expr.span,
                                                 fcx.expr_ty(base));
@@ -2979,13 +2962,14 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
                                                   lvalue_pref,
                                                   |base_t, _| {
                 match base_t.sty {
-                    ty::TyStruct(base_id, substs) => {
-                        tuple_like = tcx.is_tuple_struct(base_id);
+                    ty::TyStruct(base_def, substs) => {
+                        tuple_like = base_def.struct_variant().is_tuple_struct();
                         if tuple_like {
                             debug!("tuple struct named {:?}",  base_t);
-                            let fields = tcx.lookup_struct_fields(base_id);
-                            fcx.lookup_tup_field_ty(expr.span, base_id, &fields[..],
-                                                    idx.node, &(*substs))
+                            base_def.struct_variant()
+                                    .fields
+                                    .get(idx.node)
+                                    .map(|f| fcx.field_ty(expr.span, f, substs))
                         } else {
                             None
                         }
@@ -3025,75 +3009,63 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
         fcx.write_error(expr.id);
     }
 
+    fn report_unknown_field<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
+                                      ty: Ty<'tcx>,
+                                      variant: ty::VariantDef<'tcx>,
+                                      field: &ast::Field,
+                                      skip_fields: &[ast::Field]) {
+        fcx.type_error_message(
+            field.ident.span,
+            |actual| if let ty::TyEnum(..) = ty.sty {
+                format!("struct variant `{}::{}` has no field named `{}`",
+                        actual, variant.name.as_str(), field.ident.node)
+            } else {
+                format!("structure `{}` has no field named `{}`",
+                        actual, field.ident.node)
+            },
+            ty,
+            None);
+        // prevent all specified fields from being suggested
+        let skip_fields = skip_fields.iter().map(|ref x| x.ident.node.name.as_str());
+        suggest_field_names(variant, &field.ident, fcx.tcx(), skip_fields.collect());
+    }
+
+
     fn check_struct_or_variant_fields<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
-                                                struct_ty: Ty<'tcx>,
+                                                adt_ty: Ty<'tcx>,
                                                 span: Span,
-                                                class_id: ast::DefId,
-                                                node_id: ast::NodeId,
-                                                substitutions: &'tcx subst::Substs<'tcx>,
-                                                field_types: &[ty::FieldTy],
+                                                variant_id: ast::DefId,
                                                 ast_fields: &'tcx [ast::Field],
-                                                check_completeness: bool,
-                                                enum_id_opt: Option<ast::DefId>)  {
+                                                check_completeness: bool) -> Result<(),()> {
         let tcx = fcx.ccx.tcx;
+        let (adt_def, substs) = match adt_ty.sty {
+            ty::TyStruct(def, substs) | ty::TyEnum(def, substs) => (def, substs),
+            _ => tcx.sess.span_bug(span, "non-ADT passed to check_struct_or_variant_fields")
+        };
+        let variant = adt_def.variant_with_id(variant_id);
 
-        let mut class_field_map = FnvHashMap();
-        let mut fields_found = 0;
-        for field in field_types {
-            class_field_map.insert(field.name, (field.id, false));
+        let mut remaining_fields = FnvHashMap();
+        for field in &variant.fields {
+            remaining_fields.insert(field.name, field);
         }
 
         let mut error_happened = false;
 
         // Typecheck each field.
         for field in ast_fields {
-            let mut expected_field_type = tcx.types.err;
-
-            let pair = class_field_map.get(&field.ident.node.name).cloned();
-            match pair {
-                None => {
-                    fcx.type_error_message(
-                        field.ident.span,
-                        |actual| match enum_id_opt {
-                            Some(enum_id) => {
-                                let variant_type = tcx.enum_variant_with_id(enum_id,
-                                                                            class_id);
-                                format!("struct variant `{}::{}` has no field named `{}`",
-                                        actual, variant_type.name.as_str(),
-                                        field.ident.node)
-                            }
-                            None => {
-                                format!("structure `{}` has no field named `{}`",
-                                        actual,
-                                        field.ident.node)
-                            }
-                        },
-                        struct_ty,
-                        None);
-                    // prevent all specified fields from being suggested
-                    let skip_fields = ast_fields.iter().map(|ref x| x.ident.node.name.as_str());
-                    let actual_id = match enum_id_opt {
-                        Some(_) => class_id,
-                        None => struct_ty.ty_to_def_id().unwrap()
-                    };
-                    suggest_field_names(actual_id, &field.ident, tcx, skip_fields.collect());
-                    error_happened = true;
-                }
-                Some((_, true)) => {
+            let expected_field_type;
+
+            if let Some(v_field) = remaining_fields.remove(&field.ident.node.name) {
+                expected_field_type = fcx.field_ty(field.span, v_field, substs);
+            } else {
+                error_happened = true;
+                expected_field_type = tcx.types.err;
+                if let Some(_) = variant.find_field_named(field.ident.node.name) {
                     span_err!(fcx.tcx().sess, field.ident.span, E0062,
                         "field `{}` specified more than once",
                         field.ident.node);
-                    error_happened = true;
-                }
-                Some((field_id, false)) => {
-                    expected_field_type =
-                        tcx.lookup_field_type(class_id, field_id, substitutions);
-                    expected_field_type =
-                        fcx.normalize_associated_types_in(
-                            field.span, &expected_field_type);
-                    class_field_map.insert(
-                        field.ident.node.name, (field_id, true));
-                    fields_found += 1;
+                } else {
+                    report_unknown_field(fcx, adt_ty, variant, field, ast_fields);
                 }
             }
 
@@ -3102,40 +3074,28 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
             check_expr_coercable_to_type(fcx, &*field.expr, expected_field_type);
         }
 
-        if error_happened {
-            fcx.write_error(node_id);
-        }
-
-        if check_completeness && !error_happened {
             // Make sure the programmer specified all the fields.
-            assert!(fields_found <= field_types.len());
-            if fields_found < field_types.len() {
-                let mut missing_fields = Vec::new();
-                for class_field in field_types {
-                    let name = class_field.name;
-                    let (_, seen) = *class_field_map.get(&name).unwrap();
-                    if !seen {
-                        missing_fields.push(
-                            format!("`{}`", name))
-                    }
-                }
-
-                span_err!(tcx.sess, span, E0063,
-                    "missing field{}: {}",
-                    if missing_fields.len() == 1 {""} else {"s"},
-                    missing_fields.join(", "));
-             }
+        if check_completeness &&
+            !error_happened &&
+            !remaining_fields.is_empty()
+        {
+            error_happened = true;
+            span_err!(tcx.sess, span, E0063,
+                      "missing field{}: {}",
+                      if remaining_fields.len() == 1 {""} else {"s"},
+                      remaining_fields.keys()
+                                      .map(|n| format!("`{}`", n))
+                                      .collect::<Vec<_>>()
+                                      .join(", "));
         }
 
-        if !error_happened {
-            fcx.write_ty(node_id, fcx.ccx.tcx.mk_struct(class_id, substitutions));
-        }
+        if error_happened { Err(()) } else { Ok(()) }
     }
 
     fn check_struct_constructor<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
                                          id: ast::NodeId,
                                          span: codemap::Span,
-                                         class_id: ast::DefId,
+                                         struct_def: ty::AdtDef<'tcx>,
                                          fields: &'tcx [ast::Field],
                                          base_expr: Option<&'tcx ast::Expr>) {
         let tcx = fcx.ccx.tcx;
@@ -3143,22 +3103,17 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
         // Generate the struct type.
         let TypeAndSubsts {
             ty: mut struct_type,
-            substs: struct_substs
-        } = fcx.instantiate_type(span, class_id);
+            substs: _
+        } = fcx.instantiate_type(span, struct_def.did);
 
         // Look up and check the fields.
-        let class_fields = tcx.lookup_struct_fields(class_id);
-        check_struct_or_variant_fields(fcx,
-                                       struct_type,
-                                       span,
-                                       class_id,
-                                       id,
-                                       fcx.ccx.tcx.mk_substs(struct_substs),
-                                       &class_fields[..],
-                                       fields,
-                                       base_expr.is_none(),
-                                       None);
-        if fcx.node_ty(id).references_error() {
+        let res = check_struct_or_variant_fields(fcx,
+                                                 struct_type,
+                                                 span,
+                                                 struct_def.did,
+                                                 fields,
+                                                 base_expr.is_none());
+        if res.is_err() {
             struct_type = tcx.types.err;
         }
 
@@ -3180,27 +3135,20 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
                                           enum_id: ast::DefId,
                                           variant_id: ast::DefId,
                                           fields: &'tcx [ast::Field]) {
-        let tcx = fcx.ccx.tcx;
-
         // Look up the number of type parameters and the raw type, and
         // determine whether the enum is region-parameterized.
         let TypeAndSubsts {
             ty: enum_type,
-            substs: substitutions
+            substs: _
         } = fcx.instantiate_type(span, enum_id);
 
         // Look up and check the enum variant fields.
-        let variant_fields = tcx.lookup_struct_fields(variant_id);
-        check_struct_or_variant_fields(fcx,
-                                       enum_type,
-                                       span,
-                                       variant_id,
-                                       id,
-                                       fcx.ccx.tcx.mk_substs(substitutions),
-                                       &variant_fields[..],
-                                       fields,
-                                       true,
-                                       Some(enum_id));
+        let _ = check_struct_or_variant_fields(fcx,
+                                               enum_type,
+                                               span,
+                                               variant_id,
+                                               fields,
+                                               true);
         fcx.write_ty(id, enum_type);
     }
 
@@ -3695,11 +3643,11 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
                 // Verify that this was actually a struct.
                 let typ = fcx.ccx.tcx.lookup_item_type(def.def_id());
                 match typ.ty.sty {
-                    ty::TyStruct(struct_did, _) => {
+                    ty::TyStruct(struct_def, _) => {
                         check_struct_constructor(fcx,
                                                  id,
                                                  expr.span,
-                                                 struct_did,
+                                                 struct_def,
                                                  &fields[..],
                                                  base_expr.as_ref().map(|e| &**e));
                     }
@@ -3835,6 +3783,7 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
                 };
 
                 if let Some(did) = did {
+                    let def = tcx.lookup_adt_def(did);
                     let predicates = tcx.lookup_predicates(did);
                     let substs = Substs::new_type(vec![idx_type], vec![]);
                     let bounds = fcx.instantiate_bounds(expr.span, &substs, &predicates);
@@ -3844,7 +3793,7 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
                                                      traits::ItemObligation(did)),
                         &bounds);
 
-                    tcx.mk_struct(did, tcx.mk_substs(substs))
+                    tcx.mk_struct(def, tcx.mk_substs(substs))
                 } else {
                     span_err!(tcx.sess, expr.span, E0236, "no lang item for range syntax");
                     fcx.tcx().types.err
@@ -3853,8 +3802,10 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
             None => {
                 // Neither start nor end => RangeFull
                 if let Some(did) = tcx.lang_items.range_full_struct() {
-                    let substs = Substs::new_type(vec![], vec![]);
-                    tcx.mk_struct(did, tcx.mk_substs(substs))
+                    tcx.mk_struct(
+                        tcx.lookup_adt_def(did),
+                        tcx.mk_substs(Substs::empty())
+                    )
                 } else {
                     span_err!(tcx.sess, expr.span, E0237, "no lang item for range syntax");
                     fcx.tcx().types.err
@@ -4305,15 +4256,14 @@ pub fn check_simd(tcx: &ty::ctxt, sp: Span, id: ast::NodeId) {
         return;
     }
     match t.sty {
-        ty::TyStruct(did, substs) => {
-            let fields = tcx.lookup_struct_fields(did);
+        ty::TyStruct(def, substs) => {
+            let fields = &def.struct_variant().fields;
             if fields.is_empty() {
                 span_err!(tcx.sess, sp, E0075, "SIMD vector cannot be empty");
                 return;
             }
-            let e = tcx.lookup_field_type(did, fields[0].id, substs);
-            if !fields.iter().all(
-                         |f| tcx.lookup_field_type(did, f.id, substs) == e) {
+            let e = fields[0].ty(tcx, substs);
+            if !fields.iter().all(|f| f.ty(tcx, substs) == e) {
                 span_err!(tcx.sess, sp, E0076, "SIMD vector should be homogeneous");
                 return;
             }
@@ -4381,10 +4331,7 @@ pub fn check_enum_variants<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>,
 
         let def_id = local_def(id);
 
-        // ty::enum_variants guards against discriminant overflows, so
-        // we need not check for that.
-        let variants = ccx.tcx.enum_variants(def_id);
-
+        let variants = &ccx.tcx.lookup_adt_def(def_id).variants;
         for (v, variant) in vs.iter().zip(variants.iter()) {
             let current_disr_val = variant.disr_val;
 
@@ -4393,7 +4340,7 @@ pub fn check_enum_variants<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>,
                 Some(i) => {
                     span_err!(ccx.tcx.sess, v.span, E0081,
                         "discriminant value `{}` already exists", disr_vals[i]);
-                    span_note!(ccx.tcx.sess, ccx.tcx.map.span(variants[i].id.node),
+                    span_note!(ccx.tcx.sess, ccx.tcx.map.span(variants[i].did.node),
                         "conflicting discriminant here")
                 }
                 None => {}
diff --git a/src/librustc_typeck/check/op.rs b/src/librustc_typeck/check/op.rs
index c6d13d3b0a5..b139cb45bf2 100644
--- a/src/librustc_typeck/check/op.rs
+++ b/src/librustc_typeck/check/op.rs
@@ -21,7 +21,7 @@ use super::{
     structurally_resolved_type,
 };
 use middle::traits;
-use middle::ty::{self, Ty, HasTypeFlags};
+use middle::ty::{Ty, HasTypeFlags};
 use syntax::ast;
 use syntax::ast_util;
 use syntax::parse::token;
@@ -41,7 +41,7 @@ pub fn check_binop_assign<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
     let lhs_ty = structurally_resolved_type(fcx, lhs_expr.span, fcx.expr_ty(lhs_expr));
     let rhs_ty = structurally_resolved_type(fcx, rhs_expr.span, fcx.expr_ty(rhs_expr));
 
-    if is_builtin_binop(fcx.tcx(), lhs_ty, rhs_ty, op) {
+    if is_builtin_binop(lhs_ty, rhs_ty, op) {
         enforce_builtin_binop_types(fcx, lhs_expr, lhs_ty, rhs_expr, rhs_ty, op);
         fcx.write_nil(expr.id);
     } else {
@@ -86,7 +86,7 @@ pub fn check_binop<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
     // traits, because their return type is not bool. Perhaps this
     // should change, but for now if LHS is SIMD we go down a
     // different path that bypassess all traits.
-    if lhs_ty.is_simd(fcx.tcx()) {
+    if lhs_ty.is_simd() {
         check_expr_coercable_to_type(fcx, rhs_expr, lhs_ty);
         let rhs_ty = fcx.resolve_type_vars_if_possible(fcx.expr_ty(lhs_expr));
         let return_ty = enforce_builtin_binop_types(fcx, lhs_expr, lhs_ty, rhs_expr, rhs_ty, op);
@@ -123,7 +123,7 @@ pub fn check_binop<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
             let rhs_ty = fcx.resolve_type_vars_if_possible(rhs_ty);
             if
                 !lhs_ty.is_ty_var() && !rhs_ty.is_ty_var() &&
-                is_builtin_binop(fcx.tcx(), lhs_ty, rhs_ty, op)
+                is_builtin_binop(lhs_ty, rhs_ty, op)
             {
                 let builtin_return_ty =
                     enforce_builtin_binop_types(fcx, lhs_expr, lhs_ty, rhs_expr, rhs_ty, op);
@@ -143,7 +143,7 @@ fn enforce_builtin_binop_types<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
                                          op: ast::BinOp)
                                          -> Ty<'tcx>
 {
-    debug_assert!(is_builtin_binop(fcx.tcx(), lhs_ty, rhs_ty, op));
+    debug_assert!(is_builtin_binop(lhs_ty, rhs_ty, op));
 
     let tcx = fcx.tcx();
     match BinOpCategory::from(op) {
@@ -156,7 +156,7 @@ fn enforce_builtin_binop_types<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
         BinOpCategory::Shift => {
             // For integers, the shift amount can be of any integral
             // type. For simd, the type must match exactly.
-            if lhs_ty.is_simd(tcx) {
+            if lhs_ty.is_simd() {
                 demand::suptype(fcx, rhs_expr.span, lhs_ty, rhs_ty);
             }
 
@@ -176,7 +176,7 @@ fn enforce_builtin_binop_types<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
             demand::suptype(fcx, rhs_expr.span, lhs_ty, rhs_ty);
 
             // if this is simd, result is same as lhs, else bool
-            if lhs_ty.is_simd(tcx) {
+            if lhs_ty.is_simd() {
                 let unit_ty = lhs_ty.simd_type(tcx);
                 debug!("enforce_builtin_binop_types: lhs_ty={:?} unit_ty={:?}",
                        lhs_ty,
@@ -415,8 +415,7 @@ impl BinOpCategory {
 /// Reason #2 is the killer. I tried for a while to always use
 /// overloaded logic and just check the types in constants/trans after
 /// the fact, and it worked fine, except for SIMD types. -nmatsakis
-fn is_builtin_binop<'tcx>(cx: &ty::ctxt<'tcx>,
-                          lhs: Ty<'tcx>,
+fn is_builtin_binop<'tcx>(lhs: Ty<'tcx>,
                           rhs: Ty<'tcx>,
                           op: ast::BinOp)
                           -> bool
@@ -429,28 +428,28 @@ fn is_builtin_binop<'tcx>(cx: &ty::ctxt<'tcx>,
         BinOpCategory::Shift => {
             lhs.references_error() || rhs.references_error() ||
                 lhs.is_integral() && rhs.is_integral() ||
-                lhs.is_simd(cx) && rhs.is_simd(cx)
+                lhs.is_simd() && rhs.is_simd()
         }
 
         BinOpCategory::Math => {
             lhs.references_error() || rhs.references_error() ||
                 lhs.is_integral() && rhs.is_integral() ||
                 lhs.is_floating_point() && rhs.is_floating_point() ||
-                lhs.is_simd(cx) && rhs.is_simd(cx)
+                lhs.is_simd() && rhs.is_simd()
         }
 
         BinOpCategory::Bitwise => {
             lhs.references_error() || rhs.references_error() ||
                 lhs.is_integral() && rhs.is_integral() ||
                 lhs.is_floating_point() && rhs.is_floating_point() ||
-                lhs.is_simd(cx) && rhs.is_simd(cx) ||
+                lhs.is_simd() && rhs.is_simd() ||
                 lhs.is_bool() && rhs.is_bool()
         }
 
         BinOpCategory::Comparison => {
             lhs.references_error() || rhs.references_error() ||
                 lhs.is_scalar() && rhs.is_scalar() ||
-                lhs.is_simd(cx) && rhs.is_simd(cx)
+                lhs.is_simd() && rhs.is_simd()
         }
     }
 }
diff --git a/src/librustc_typeck/check/wf.rs b/src/librustc_typeck/check/wf.rs
index cfb7dfc54aa..7790a29db12 100644
--- a/src/librustc_typeck/check/wf.rs
+++ b/src/librustc_typeck/check/wf.rs
@@ -571,9 +571,9 @@ impl<'cx,'tcx> TypeFolder<'tcx> for BoundsChecker<'cx,'tcx> {
         }
 
         match t.sty{
-            ty::TyStruct(type_id, substs) |
-            ty::TyEnum(type_id, substs) => {
-                let type_predicates = self.fcx.tcx().lookup_predicates(type_id);
+            ty::TyStruct(def, substs) |
+            ty::TyEnum(def, substs) => {
+                let type_predicates = def.predicates(self.fcx.tcx());
                 let bounds = self.fcx.instantiate_bounds(self.span, substs,
                                                          &type_predicates);
 
@@ -581,7 +581,7 @@ impl<'cx,'tcx> TypeFolder<'tcx> for BoundsChecker<'cx,'tcx> {
                     self.fcx.add_obligations_for_parameters(
                         traits::ObligationCause::new(self.span,
                                                      self.fcx.body_id,
-                                                     traits::ItemObligation(type_id)),
+                                                     traits::ItemObligation(def.did)),
                         &bounds);
                 } else {
                     // There are two circumstances in which we ignore
@@ -610,7 +610,7 @@ impl<'cx,'tcx> TypeFolder<'tcx> for BoundsChecker<'cx,'tcx> {
                     self.fcx.add_obligations_for_parameters(
                         traits::ObligationCause::new(self.span,
                                                      self.fcx.body_id,
-                                                     traits::ItemObligation(type_id)),
+                                                     traits::ItemObligation(def.did)),
                         &bounds);
                 }
 
diff --git a/src/librustc_typeck/coherence/mod.rs b/src/librustc_typeck/coherence/mod.rs
index 418d592c962..7ab8d327a80 100644
--- a/src/librustc_typeck/coherence/mod.rs
+++ b/src/librustc_typeck/coherence/mod.rs
@@ -55,9 +55,9 @@ fn get_base_type_def_id<'a, 'tcx>(inference_context: &InferCtxt<'a, 'tcx>,
                                   ty: Ty<'tcx>)
                                   -> Option<DefId> {
     match ty.sty {
-        TyEnum(def_id, _) |
-        TyStruct(def_id, _) => {
-            Some(def_id)
+        TyEnum(def, _) |
+        TyStruct(def, _) => {
+            Some(def.did)
         }
 
         TyTrait(ref t) => {
@@ -310,12 +310,11 @@ impl<'a, 'tcx> CoherenceChecker<'a, 'tcx> {
 
             let self_type = tcx.lookup_item_type(impl_did);
             match self_type.ty.sty {
-                ty::TyEnum(type_def_id, _) |
-                ty::TyStruct(type_def_id, _) |
-                ty::TyClosure(type_def_id, _) => {
+                ty::TyEnum(type_def, _) |
+                ty::TyStruct(type_def, _) => {
                     tcx.destructor_for_type
                        .borrow_mut()
-                       .insert(type_def_id, method_def_id.def_id());
+                       .insert(type_def.did, method_def_id.def_id());
                     tcx.destructors
                        .borrow_mut()
                        .insert(method_def_id.def_id());
@@ -471,10 +470,10 @@ impl<'a, 'tcx> CoherenceChecker<'a, 'tcx> {
                     check_mutbl(mt_a, mt_b, &|ty| tcx.mk_imm_ptr(ty))
                 }
 
-                (&ty::TyStruct(def_id_a, substs_a), &ty::TyStruct(def_id_b, substs_b)) => {
-                    if def_id_a != def_id_b {
-                        let source_path = tcx.item_path_str(def_id_a);
-                        let target_path = tcx.item_path_str(def_id_b);
+                (&ty::TyStruct(def_a, substs_a), &ty::TyStruct(def_b, substs_b)) => {
+                    if def_a != def_b {
+                        let source_path = tcx.item_path_str(def_a.did);
+                        let target_path = tcx.item_path_str(def_b.did);
                         span_err!(tcx.sess, span, E0377,
                                   "the trait `CoerceUnsized` may only be implemented \
                                    for a coercion between structures with the same \
@@ -484,10 +483,9 @@ impl<'a, 'tcx> CoherenceChecker<'a, 'tcx> {
                     }
 
                     let origin = infer::Misc(span);
-                    let fields = tcx.lookup_struct_fields(def_id_a);
+                    let fields = &def_a.struct_variant().fields;
                     let diff_fields = fields.iter().enumerate().filter_map(|(i, f)| {
-                        let ty = tcx.lookup_field_type_unsubstituted(def_id_a, f.id);
-                        let (a, b) = (ty.subst(tcx, substs_a), ty.subst(tcx, substs_b));
+                        let (a, b) = (f.ty(tcx, substs_a), f.ty(tcx, substs_b));
                         if infcx.sub_types(false, origin, b, a).is_ok() {
                             None
                         } else {
diff --git a/src/librustc_typeck/coherence/orphan.rs b/src/librustc_typeck/coherence/orphan.rs
index 494e2eaa77b..8076e63cc55 100644
--- a/src/librustc_typeck/coherence/orphan.rs
+++ b/src/librustc_typeck/coherence/orphan.rs
@@ -69,9 +69,9 @@ impl<'cx, 'tcx> OrphanChecker<'cx, 'tcx> {
                        self.tcx.map.node_to_string(item.id));
                 let self_ty = self.tcx.lookup_item_type(def_id).ty;
                 match self_ty.sty {
-                    ty::TyEnum(def_id, _) |
-                    ty::TyStruct(def_id, _) => {
-                        self.check_def_id(item, def_id);
+                    ty::TyEnum(def, _) |
+                    ty::TyStruct(def, _) => {
+                        self.check_def_id(item, def.did);
                     }
                     ty::TyTrait(ref data) => {
                         self.check_def_id(item, data.principal_def_id());
@@ -279,8 +279,8 @@ impl<'cx, 'tcx> OrphanChecker<'cx, 'tcx> {
                 {
                     let self_ty = trait_ref.self_ty();
                     let opt_self_def_id = match self_ty.sty {
-                        ty::TyStruct(self_def_id, _) | ty::TyEnum(self_def_id, _) =>
-                            Some(self_def_id),
+                        ty::TyStruct(self_def, _) | ty::TyEnum(self_def, _) =>
+                            Some(self_def.did),
                         ty::TyBox(..) =>
                             self.tcx.lang_items.owned_box(),
                         _ =>
diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs
index 5b65f780830..dabc09db68d 100644
--- a/src/librustc_typeck/collect.rs
+++ b/src/librustc_typeck/collect.rs
@@ -71,9 +71,12 @@ use middle::lang_items::SizedTraitLangItem;
 use middle::free_region::FreeRegionMap;
 use middle::region;
 use middle::resolve_lifetime;
+use middle::const_eval::{self, ConstVal};
+use middle::const_eval::EvalHint::UncheckedExprHint;
 use middle::subst::{Substs, FnSpace, ParamSpace, SelfSpace, TypeSpace, VecPerParamSpace};
 use middle::ty::{ToPredicate, ImplContainer, ImplOrTraitItemContainer, TraitContainer};
-use middle::ty::{self, RegionEscape, ToPolyTraitRef, Ty, TypeScheme};
+use middle::ty::{self, RegionEscape, ToPolyTraitRef, Ty, TypeScheme, IntTypeExt};
+use middle::ty::{VariantKind};
 use middle::ty_fold::{self, TypeFolder, TypeFoldable};
 use middle::infer;
 use rscope::*;
@@ -89,8 +92,10 @@ use std::rc::Rc;
 use syntax::abi;
 use syntax::ast;
 use syntax::ast_util::local_def;
+use syntax::attr;
 use syntax::codemap::Span;
 use syntax::parse::token::special_idents;
+use syntax::print::pprust;
 use syntax::ptr::P;
 use syntax::visit;
 
@@ -563,48 +568,6 @@ fn is_param<'tcx>(tcx: &ty::ctxt<'tcx>,
     }
 }
 
-fn get_enum_variant_types<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
-                                    enum_scheme: ty::TypeScheme<'tcx>,
-                                    enum_predicates: ty::GenericPredicates<'tcx>,
-                                    variants: &[P<ast::Variant>]) {
-    let tcx = ccx.tcx;
-    let icx = ccx.icx(&enum_predicates);
-
-    // Create a set of parameter types shared among all the variants.
-    for variant in variants {
-        let variant_def_id = local_def(variant.node.id);
-
-        // Nullary enum constructors get turned into constants; n-ary enum
-        // constructors get turned into functions.
-        let result_ty = match variant.node.kind {
-            ast::TupleVariantKind(ref args) if !args.is_empty() => {
-                let rs = ExplicitRscope;
-                let input_tys: Vec<_> = args.iter().map(|va| icx.to_ty(&rs, &*va.ty)).collect();
-                tcx.mk_ctor_fn(variant_def_id, &input_tys, enum_scheme.ty)
-            }
-
-            ast::TupleVariantKind(_) => {
-                enum_scheme.ty
-            }
-
-            ast::StructVariantKind(ref struct_def) => {
-                convert_struct(ccx, &**struct_def, enum_scheme.clone(),
-                               enum_predicates.clone(), variant.node.id);
-                enum_scheme.ty
-            }
-        };
-
-        let variant_scheme = TypeScheme {
-            generics: enum_scheme.generics.clone(),
-            ty: result_ty
-        };
-
-        tcx.register_item_type(variant_def_id, variant_scheme.clone());
-        tcx.predicates.borrow_mut().insert(variant_def_id, enum_predicates.clone());
-        write_ty_to_tcx(tcx, variant.node.id, result_ty);
-    }
-}
-
 fn convert_method<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
                             container: ImplOrTraitItemContainer,
                             sig: &ast::MethodSig,
@@ -657,10 +620,10 @@ fn convert_field<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
                            struct_generics: &ty::Generics<'tcx>,
                            struct_predicates: &ty::GenericPredicates<'tcx>,
                            v: &ast::StructField,
-                           origin: ast::DefId)
-                           -> ty::FieldTy
+                           ty_f: ty::FieldDefMaster<'tcx>)
 {
     let tt = ccx.icx(struct_predicates).to_ty(&ExplicitRscope, &*v.node.ty);
+    ty_f.fulfill_ty(tt);
     write_ty_to_tcx(ccx.tcx, v.node.id, tt);
 
     /* add the field to the tcache */
@@ -671,25 +634,6 @@ fn convert_field<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
                                });
     ccx.tcx.predicates.borrow_mut().insert(local_def(v.node.id),
                                            struct_predicates.clone());
-
-    match v.node.kind {
-        ast::NamedField(ident, visibility) => {
-            ty::FieldTy {
-                name: ident.name,
-                id: local_def(v.node.id),
-                vis: visibility,
-                origin: origin,
-            }
-        }
-        ast::UnnamedField(visibility) => {
-            ty::FieldTy {
-                name: special_idents::unnamed_field.name,
-                id: local_def(v.node.id),
-                vis: visibility,
-                origin: origin,
-            }
-        }
-    }
 }
 
 fn convert_associated_const<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
@@ -803,10 +747,11 @@ fn convert_item(ccx: &CrateCtxt, it: &ast::Item) {
         ast::ItemEnum(ref enum_definition, _) => {
             let (scheme, predicates) = convert_typed_item(ccx, it);
             write_ty_to_tcx(tcx, it.id, scheme.ty);
-            get_enum_variant_types(ccx,
-                                   scheme,
-                                   predicates,
-                                   &enum_definition.variants);
+            convert_enum_variant_types(ccx,
+                                       tcx.lookup_adt_def_master(local_def(it.id)),
+                                       scheme,
+                                       predicates,
+                                       &enum_definition.variants);
         },
         ast::ItemDefaultImpl(_, ref ast_trait_ref) => {
             let trait_ref =
@@ -1048,10 +993,18 @@ fn convert_item(ccx: &CrateCtxt, it: &ast::Item) {
             }
         },
         ast::ItemStruct(ref struct_def, _) => {
-            // Write the class type.
             let (scheme, predicates) = convert_typed_item(ccx, it);
             write_ty_to_tcx(tcx, it.id, scheme.ty);
-            convert_struct(ccx, &**struct_def, scheme, predicates, it.id);
+
+            let variant = tcx.lookup_adt_def_master(local_def(it.id)).struct_variant();
+
+            for (f, ty_f) in struct_def.fields.iter().zip(variant.fields.iter()) {
+                convert_field(ccx, &scheme.generics, &predicates, f, ty_f)
+            }
+
+            if let Some(ctor_id) = struct_def.ctor_id {
+                convert_variant_ctor(tcx, ctor_id, variant, scheme, predicates);
+            }
         },
         ast::ItemTy(_, ref generics) => {
             ensure_no_ty_param_bounds(ccx, it.span, generics, "type");
@@ -1068,76 +1021,226 @@ fn convert_item(ccx: &CrateCtxt, it: &ast::Item) {
     }
 }
 
-fn convert_struct<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
-                            struct_def: &ast::StructDef,
-                            scheme: ty::TypeScheme<'tcx>,
-                            predicates: ty::GenericPredicates<'tcx>,
-                            id: ast::NodeId) {
+fn convert_variant_ctor<'a, 'tcx>(tcx: &ty::ctxt<'tcx>,
+                                  ctor_id: ast::NodeId,
+                                  variant: ty::VariantDef<'tcx>,
+                                  scheme: ty::TypeScheme<'tcx>,
+                                  predicates: ty::GenericPredicates<'tcx>) {
+    let ctor_ty = match variant.kind() {
+        VariantKind::Unit | VariantKind::Dict => scheme.ty,
+        VariantKind::Tuple => {
+            let inputs: Vec<_> =
+                variant.fields
+                .iter()
+                .map(|field| field.unsubst_ty())
+                .collect();
+            tcx.mk_ctor_fn(local_def(ctor_id),
+                           &inputs[..],
+                           scheme.ty)
+        }
+    };
+    write_ty_to_tcx(tcx, ctor_id, ctor_ty);
+    tcx.predicates.borrow_mut().insert(local_def(ctor_id), predicates);
+    tcx.register_item_type(local_def(ctor_id),
+                           TypeScheme {
+                               generics: scheme.generics,
+                               ty: ctor_ty
+                           });
+}
+
+fn convert_enum_variant_types<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
+                                        def: ty::AdtDefMaster<'tcx>,
+                                        scheme: ty::TypeScheme<'tcx>,
+                                        predicates: ty::GenericPredicates<'tcx>,
+                                        variants: &[P<ast::Variant>]) {
     let tcx = ccx.tcx;
+    let icx = ccx.icx(&predicates);
 
-    // Write the type of each of the members and check for duplicate fields.
-    let mut seen_fields: FnvHashMap<ast::Name, Span> = FnvHashMap();
-    let field_tys = struct_def.fields.iter().map(|f| {
-        let result = convert_field(ccx, &scheme.generics, &predicates, f, local_def(id));
+    // fill the field types
+    for (variant, ty_variant) in variants.iter().zip(def.variants.iter()) {
+        match variant.node.kind {
+            ast::TupleVariantKind(ref args) => {
+                let rs = ExplicitRscope;
+                let input_tys: Vec<_> = args.iter().map(|va| icx.to_ty(&rs, &*va.ty)).collect();
+                for (field, &ty) in ty_variant.fields.iter().zip(input_tys.iter()) {
+                    field.fulfill_ty(ty);
+                }
+            }
 
-        if result.name != special_idents::unnamed_field.name {
-            let dup = match seen_fields.get(&result.name) {
-                Some(prev_span) => {
+            ast::StructVariantKind(ref struct_def) => {
+                for (f, ty_f) in struct_def.fields.iter().zip(ty_variant.fields.iter()) {
+                    convert_field(ccx, &scheme.generics, &predicates, f, ty_f)
+                }
+            }
+        };
+
+        // Convert the ctor, if any. This also registers the variant as
+        // an item.
+        convert_variant_ctor(
+            tcx,
+            variant.node.id,
+            ty_variant,
+            scheme.clone(),
+            predicates.clone()
+        );
+    }
+}
+
+fn convert_struct_variant<'tcx>(tcx: &ty::ctxt<'tcx>,
+                                did: ast::DefId,
+                                name: ast::Name,
+                                disr_val: ty::Disr,
+                                def: &ast::StructDef) -> ty::VariantDefData<'tcx, 'tcx> {
+    let mut seen_fields: FnvHashMap<ast::Name, Span> = FnvHashMap();
+    let fields = def.fields.iter().map(|f| {
+        let fid = local_def(f.node.id);
+        match f.node.kind {
+            ast::NamedField(ident, vis) => {
+                let dup_span = seen_fields.get(&ident.name).cloned();
+                if let Some(prev_span) = dup_span {
                     span_err!(tcx.sess, f.span, E0124,
                               "field `{}` is already declared",
-                              result.name);
-                    span_note!(tcx.sess, *prev_span, "previously declared here");
-                    true
-                },
-                None => false,
-            };
-            // FIXME(#6393) this whole dup thing is just to satisfy
-            // the borrow checker :-(
-            if !dup {
-                seen_fields.insert(result.name, f.span);
+                              ident.name);
+                    span_note!(tcx.sess, prev_span, "previously declared here");
+                } else {
+                    seen_fields.insert(ident.name, f.span);
+                }
+
+                ty::FieldDefData::new(fid, ident.name, vis)
+            },
+            ast::UnnamedField(vis) => {
+                ty::FieldDefData::new(fid, special_idents::unnamed_field.name, vis)
             }
         }
-
-        result
     }).collect();
+    ty::VariantDefData {
+        did: did,
+        name: name,
+        disr_val: disr_val,
+        fields: fields
+    }
+}
 
-    tcx.struct_fields.borrow_mut().insert(local_def(id), Rc::new(field_tys));
+fn convert_struct_def<'tcx>(tcx: &ty::ctxt<'tcx>,
+                            it: &ast::Item,
+                            def: &ast::StructDef)
+                            -> ty::AdtDefMaster<'tcx>
+{
 
-    let substs = mk_item_substs(ccx, &scheme.generics);
-    let selfty = tcx.mk_struct(local_def(id), tcx.mk_substs(substs));
+    let did = local_def(it.id);
+    tcx.intern_adt_def(
+        did,
+        ty::AdtKind::Struct,
+        vec![convert_struct_variant(tcx, did, it.ident.name, 0, def)]
+    )
+}
 
-    // If this struct is enum-like or tuple-like, create the type of its
-    // constructor.
-    match struct_def.ctor_id {
-        None => {}
-        Some(ctor_id) => {
-            if struct_def.fields.is_empty() {
-                // Enum-like.
-                write_ty_to_tcx(tcx, ctor_id, selfty);
-
-                tcx.register_item_type(local_def(ctor_id), scheme);
-                tcx.predicates.borrow_mut().insert(local_def(ctor_id), predicates);
-            } else if struct_def.fields[0].node.kind.is_unnamed() {
-                // Tuple-like.
-                let inputs: Vec<_> =
-                    struct_def.fields
-                              .iter()
-                              .map(|field| tcx.lookup_item_type(
-                                  local_def(field.node.id)).ty)
-                              .collect();
-                let ctor_fn_ty = tcx.mk_ctor_fn(local_def(ctor_id),
-                                                &inputs[..],
-                                                selfty);
-                write_ty_to_tcx(tcx, ctor_id, ctor_fn_ty);
-                tcx.register_item_type(local_def(ctor_id),
-                                       TypeScheme {
-                                           generics: scheme.generics,
-                                           ty: ctor_fn_ty
-                                       });
-                tcx.predicates.borrow_mut().insert(local_def(ctor_id), predicates);
+fn convert_enum_def<'tcx>(tcx: &ty::ctxt<'tcx>,
+                          it: &ast::Item,
+                          def: &ast::EnumDef)
+                          -> ty::AdtDefMaster<'tcx>
+{
+    fn evaluate_disr_expr<'tcx>(tcx: &ty::ctxt<'tcx>,
+                                repr_ty: Ty<'tcx>,
+                                e: &ast::Expr) -> Option<ty::Disr> {
+        debug!("disr expr, checking {}", pprust::expr_to_string(e));
+
+        let hint = UncheckedExprHint(repr_ty);
+        match const_eval::eval_const_expr_partial(tcx, e, hint) {
+            Ok(ConstVal::Int(val)) => Some(val as ty::Disr),
+            Ok(ConstVal::Uint(val)) => Some(val as ty::Disr),
+            Ok(_) => {
+                let sign_desc = if repr_ty.is_signed() {
+                    "signed"
+                } else {
+                    "unsigned"
+                };
+                span_err!(tcx.sess, e.span, E0079,
+                          "expected {} integer constant",
+                          sign_desc);
+                None
+            },
+            Err(err) => {
+              span_err!(tcx.sess, err.span, E0080,
+                        "constant evaluation error: {}",
+                        err.description());
+                None
+            }
+        }
+    }
+
+    fn report_discrim_overflow(tcx: &ty::ctxt,
+                               variant_span: Span,
+                               variant_name: &str,
+                               repr_type: attr::IntType,
+                               prev_val: ty::Disr) {
+        let computed_value = repr_type.disr_wrap_incr(Some(prev_val));
+        let computed_value = repr_type.disr_string(computed_value);
+        let prev_val = repr_type.disr_string(prev_val);
+        let repr_type = repr_type.to_ty(tcx);
+        span_err!(tcx.sess, variant_span, E0370,
+                  "enum discriminant overflowed on value after {}: {}; \
+                   set explicitly via {} = {} if that is desired outcome",
+                  prev_val, repr_type, variant_name, computed_value);
+    }
+
+    fn next_disr(tcx: &ty::ctxt,
+                 v: &ast::Variant,
+                 repr_type: attr::IntType,
+                 prev_disr_val: Option<ty::Disr>) -> Option<ty::Disr> {
+        if let Some(prev_disr_val) = prev_disr_val {
+            let result = repr_type.disr_incr(prev_disr_val);
+            if let None = result {
+                report_discrim_overflow(tcx, v.span, &v.node.name.name.as_str(),
+                                             repr_type, prev_disr_val);
             }
+            result
+        } else {
+            Some(ty::INITIAL_DISCRIMINANT_VALUE)
         }
     }
+    fn convert_enum_variant<'tcx>(tcx: &ty::ctxt<'tcx>,
+                                  v: &ast::Variant,
+                                  disr: ty::Disr)
+                                  -> ty::VariantDefData<'tcx, 'tcx>
+    {
+        let did = local_def(v.node.id);
+        let name = v.node.name.name;
+        match v.node.kind {
+            ast::TupleVariantKind(ref va) => {
+                ty::VariantDefData {
+                    did: did,
+                    name: name,
+                    disr_val: disr,
+                    fields: va.iter().map(|&ast::VariantArg { id, .. }| {
+                        ty::FieldDefData::new(
+                            local_def(id),
+                            special_idents::unnamed_field.name,
+                            ast::Visibility::Public
+                        )
+                    }).collect()
+                }
+            }
+            ast::StructVariantKind(ref def) => {
+                convert_struct_variant(tcx, did, name, disr, &def)
+            }
+        }
+    }
+    let did = local_def(it.id);
+    let repr_hints = tcx.lookup_repr_hints(did);
+    let (repr_type, repr_type_ty) = tcx.enum_repr_type(repr_hints.get(0));
+    let mut prev_disr = None;
+    let variants = def.variants.iter().map(|v| {
+        let disr = match v.node.disr_expr {
+            Some(ref e) => evaluate_disr_expr(tcx, repr_type_ty, e),
+            None => next_disr(tcx, v, repr_type, prev_disr)
+        }.unwrap_or(repr_type.disr_wrap_incr(prev_disr));
+
+        let v = convert_enum_variant(tcx, v, disr);
+        prev_disr = Some(disr);
+        v
+    }).collect();
+    tcx.intern_adt_def(local_def(it.id), ty::AdtKind::Enum, variants)
 }
 
 /// Ensures that the super-predicates of the trait with def-id
@@ -1471,17 +1574,18 @@ fn compute_type_scheme_of_item<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>,
             let ty = ccx.icx(generics).to_ty(&ExplicitRscope, &**t);
             ty::TypeScheme { ty: ty, generics: ty_generics }
         }
-        ast::ItemEnum(_, ref generics) => {
-            // Create a new generic polytype.
+        ast::ItemEnum(ref ei, ref generics) => {
             let ty_generics = ty_generics_for_type_or_impl(ccx, generics);
             let substs = mk_item_substs(ccx, &ty_generics);
-            let t = tcx.mk_enum(local_def(it.id), tcx.mk_substs(substs));
+            let def = convert_enum_def(tcx, it, ei);
+            let t = tcx.mk_enum(def, tcx.mk_substs(substs));
             ty::TypeScheme { ty: t, generics: ty_generics }
         }
-        ast::ItemStruct(_, ref generics) => {
+        ast::ItemStruct(ref si, ref generics) => {
             let ty_generics = ty_generics_for_type_or_impl(ccx, generics);
             let substs = mk_item_substs(ccx, &ty_generics);
-            let t = tcx.mk_struct(local_def(it.id), tcx.mk_substs(substs));
+            let def = convert_struct_def(tcx, it, si);
+            let t = tcx.mk_struct(def, tcx.mk_substs(substs));
             ty::TypeScheme { ty: t, generics: ty_generics }
         }
         ast::ItemDefaultImpl(..) |
diff --git a/src/librustc_typeck/diagnostics.rs b/src/librustc_typeck/diagnostics.rs
index 07c1b5e3d20..4660adaffae 100644
--- a/src/librustc_typeck/diagnostics.rs
+++ b/src/librustc_typeck/diagnostics.rs
@@ -883,6 +883,64 @@ struct Good(u32, u32, u32); // This will not
 ```
 "##,
 
+E0079: r##"
+Enum variants which contain no data can be given a custom integer
+representation. This error indicates that the value provided is not an integer
+literal and is therefore invalid.
+
+For example, in the following code,
+
+```
+enum Foo {
+    Q = "32"
+}
+```
+
+we try to set the representation to a string.
+
+There's no general fix for this; if you can work with an integer then just set
+it to one:
+
+```
+enum Foo {
+    Q = 32
+}
+```
+
+however if you actually wanted a mapping between variants and non-integer
+objects, it may be preferable to use a method with a match instead:
+
+```
+enum Foo { Q }
+impl Foo {
+    fn get_str(&self) -> &'static str {
+        match *self {
+            Foo::Q => "32",
+        }
+    }
+}
+```
+"##,
+
+E0080: r##"
+This error indicates that the compiler was unable to sensibly evaluate an
+integer expression provided as an enum discriminant. Attempting to divide by 0
+or causing integer overflow are two ways to induce this error. For example:
+
+```
+enum Enum {
+    X = (1 << 500),
+    Y = (1 / 0)
+}
+```
+
+Ensure that the expressions given can be evaluated as the desired integer type.
+See the FFI section of the Reference for more information about using a custom
+integer type:
+
+https://doc.rust-lang.org/reference.html#ffi-attributes
+"##,
+
 E0081: r##"
 Enum discriminants are used to differentiate enum variants stored in memory.
 This error indicates that the same value was used for two or more variants,
@@ -2510,6 +2568,7 @@ register_diagnostics! {
     E0366, // dropck forbid specialization to concrete type or region
     E0367, // dropck forbid specialization to predicate not in struct/enum
     E0369, // binary operation `<op>` cannot be applied to types
+    E0370, // discriminant overflow
     E0374, // the trait `CoerceUnsized` may only be implemented for a coercion
            // between structures with one field being coerced, none found
     E0375, // the trait `CoerceUnsized` may only be implemented for a coercion
diff --git a/src/librustc_typeck/variance.rs b/src/librustc_typeck/variance.rs
index 4af23a27c94..69e2141b17b 100644
--- a/src/librustc_typeck/variance.rs
+++ b/src/librustc_typeck/variance.rs
@@ -602,7 +602,7 @@ impl<'a, 'tcx, 'v> Visitor<'v> for ConstraintContext<'a, 'tcx> {
         debug!("visit_item item={}", tcx.map.node_to_string(item.id));
 
         match item.node {
-            ast::ItemEnum(ref enum_definition, _) => {
+            ast::ItemEnum(..) | ast::ItemStruct(..) => {
                 let scheme = tcx.lookup_item_type(did);
 
                 // Not entirely obvious: constraints on structs/enums do not
@@ -611,44 +611,12 @@ impl<'a, 'tcx, 'v> Visitor<'v> for ConstraintContext<'a, 'tcx> {
                 //
                 // self.add_constraints_from_generics(&scheme.generics);
 
-                // Hack: If we directly call `ty::enum_variants`, it
-                // annoyingly takes it upon itself to run off and
-                // evaluate the discriminants eagerly (*grumpy* that's
-                // not the typical pattern). This results in double
-                // error messages because typeck goes off and does
-                // this at a later time. All we really care about is
-                // the types of the variant arguments, so we just call
-                // `ty::VariantInfo::from_ast_variant()` ourselves
-                // here, mainly so as to mask the differences between
-                // struct-like enums and so forth.
-                for ast_variant in &enum_definition.variants {
-                    let variant =
-                        ty::VariantInfo::from_ast_variant(tcx,
-                                                          &**ast_variant,
-                                                          /*discriminant*/ 0);
-                    for arg_ty in &variant.args {
-                        self.add_constraints_from_ty(&scheme.generics, *arg_ty, self.covariant);
-                    }
+                for field in tcx.lookup_adt_def(did).all_fields() {
+                    self.add_constraints_from_ty(&scheme.generics,
+                                                 field.unsubst_ty(),
+                                                 self.covariant);
                 }
             }
-
-            ast::ItemStruct(..) => {
-                let scheme = tcx.lookup_item_type(did);
-
-                // Not entirely obvious: constraints on structs/enums do not
-                // affect the variance of their type parameters. See discussion
-                // in comment at top of module.
-                //
-                // self.add_constraints_from_generics(&scheme.generics);
-
-                let struct_fields = tcx.lookup_struct_fields(did);
-                for field_info in &struct_fields {
-                    assert_eq!(field_info.id.krate, ast::LOCAL_CRATE);
-                    let field_ty = tcx.node_id_to_type(field_info.id.node);
-                    self.add_constraints_from_ty(&scheme.generics, field_ty, self.covariant);
-                }
-            }
-
             ast::ItemTrait(..) => {
                 let trait_def = tcx.lookup_trait_def(did);
                 self.add_constraints_from_trait_ref(&trait_def.generics,
@@ -902,9 +870,9 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> {
                 }
             }
 
-            ty::TyEnum(def_id, substs) |
-            ty::TyStruct(def_id, substs) => {
-                let item_type = self.tcx().lookup_item_type(def_id);
+            ty::TyEnum(def, substs) |
+            ty::TyStruct(def, substs) => {
+                let item_type = self.tcx().lookup_item_type(def.did);
 
                 // All type parameters on enums and structs should be
                 // in the TypeSpace.
@@ -915,7 +883,7 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> {
 
                 self.add_constraints_from_substs(
                     generics,
-                    def_id,
+                    def.did,
                     item_type.generics.types.get_slice(subst::TypeSpace),
                     item_type.generics.regions.get_slice(subst::TypeSpace),
                     substs,
diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs
index 31cd8ce1b53..7fd1555d066 100644
--- a/src/librustdoc/clean/inline.rs
+++ b/src/librustdoc/clean/inline.rs
@@ -185,17 +185,17 @@ fn build_struct(cx: &DocContext, tcx: &ty::ctxt, did: ast::DefId) -> clean::Stru
 
     let t = tcx.lookup_item_type(did);
     let predicates = tcx.lookup_predicates(did);
-    let fields = tcx.lookup_struct_fields(did);
+    let variant = tcx.lookup_adt_def(did).struct_variant();
 
     clean::Struct {
-        struct_type: match &*fields {
+        struct_type: match &*variant.fields {
             [] => doctree::Unit,
             [ref f] if f.name == unnamed_field.name => doctree::Newtype,
             [ref f, ..] if f.name == unnamed_field.name => doctree::Tuple,
             _ => doctree::Plain,
         },
         generics: (&t.generics, &predicates, subst::TypeSpace).clean(cx),
-        fields: fields.clean(cx),
+        fields: variant.fields.clean(cx),
         fields_stripped: false,
     }
 }
@@ -204,11 +204,11 @@ fn build_type(cx: &DocContext, tcx: &ty::ctxt, did: ast::DefId) -> clean::ItemEn
     let t = tcx.lookup_item_type(did);
     let predicates = tcx.lookup_predicates(did);
     match t.ty.sty {
-        ty::TyEnum(edid, _) if !csearch::is_typedef(&tcx.sess.cstore, did) => {
+        ty::TyEnum(edef, _) if !csearch::is_typedef(&tcx.sess.cstore, did) => {
             return clean::EnumItem(clean::Enum {
                 generics: (&t.generics, &predicates, subst::TypeSpace).clean(cx),
                 variants_stripped: false,
-                variants: tcx.enum_variants(edid).clean(cx),
+                variants: edef.variants.clean(cx),
             })
         }
         _ => {}
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index 64575e68569..819457e5239 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -1661,8 +1661,9 @@ impl<'tcx> Clean<Type> for ty::Ty<'tcx> {
                 decl: (ast_util::local_def(0), &fty.sig).clean(cx),
                 abi: fty.abi.to_string(),
             }),
-            ty::TyStruct(did, substs) |
-            ty::TyEnum(did, substs) => {
+            ty::TyStruct(def, substs) |
+            ty::TyEnum(def, substs) => {
+                let did = def.did;
                 let fqn = csearch::get_item_path(cx.tcx(), did);
                 let fqn: Vec<_> = fqn.into_iter().map(|i| i.to_string()).collect();
                 let kind = match self.sty {
@@ -1732,29 +1733,27 @@ impl Clean<Item> for ast::StructField {
     }
 }
 
-impl Clean<Item> for ty::FieldTy {
+impl<'tcx> Clean<Item> for ty::FieldDefData<'tcx, 'static> {
     fn clean(&self, cx: &DocContext) -> Item {
         use syntax::parse::token::special_idents::unnamed_field;
         use rustc::metadata::csearch;
 
-        let attr_map = csearch::get_struct_field_attrs(&cx.tcx().sess.cstore, self.id);
+        let attr_map = csearch::get_struct_field_attrs(&cx.tcx().sess.cstore, self.did);
 
         let (name, attrs) = if self.name == unnamed_field.name {
             (None, None)
         } else {
-            (Some(self.name), Some(attr_map.get(&self.id.node).unwrap()))
+            (Some(self.name), Some(attr_map.get(&self.did.node).unwrap()))
         };
 
-        let ty = cx.tcx().lookup_item_type(self.id);
-
         Item {
             name: name.clean(cx),
             attrs: attrs.unwrap_or(&Vec::new()).clean(cx),
             source: Span::empty(),
             visibility: Some(self.vis),
-            stability: get_stability(cx, self.id),
-            def_id: self.id,
-            inner: StructFieldItem(TypedStructField(ty.ty.clean(cx))),
+            stability: get_stability(cx, self.did),
+            def_id: self.did,
+            inner: StructFieldItem(TypedStructField(self.unsubst_ty().clean(cx))),
         }
     }
 }
@@ -1860,22 +1859,24 @@ impl Clean<Item> for doctree::Variant {
     }
 }
 
-impl<'tcx> Clean<Item> for ty::VariantInfo<'tcx> {
+impl<'tcx> Clean<Item> for ty::VariantDefData<'tcx, 'static> {
     fn clean(&self, cx: &DocContext) -> Item {
         // use syntax::parse::token::special_idents::unnamed_field;
-        let kind = match self.arg_names.as_ref().map(|s| &**s) {
-            None | Some([]) if self.args.is_empty() => CLikeVariant,
-            None | Some([]) => {
-                TupleVariant(self.args.clean(cx))
+        let kind = match self.kind() {
+            ty::VariantKind::Unit => CLikeVariant,
+            ty::VariantKind::Tuple => {
+                TupleVariant(
+                    self.fields.iter().map(|f| f.unsubst_ty().clean(cx)).collect()
+                )
             }
-            Some(s) => {
+            ty::VariantKind::Dict => {
                 StructVariant(VariantStruct {
                     struct_type: doctree::Plain,
                     fields_stripped: false,
-                    fields: s.iter().zip(&self.args).map(|(name, ty)| {
+                    fields: self.fields.iter().map(|field| {
                         Item {
                             source: Span::empty(),
-                            name: Some(name.clean(cx)),
+                            name: Some(field.name.clean(cx)),
                             attrs: Vec::new(),
                             visibility: Some(ast::Public),
                             // FIXME: this is not accurate, we need an id for
@@ -1885,10 +1886,10 @@ impl<'tcx> Clean<Item> for ty::VariantInfo<'tcx> {
                             //        Struct variants are experimental and need
                             //        more infrastructure work before we can get
                             //        at the needed information here.
-                            def_id: self.id,
-                            stability: get_stability(cx, self.id),
+                            def_id: self.did,
+                            stability: get_stability(cx, self.did),
                             inner: StructFieldItem(
-                                TypedStructField(ty.clean(cx))
+                                TypedStructField(field.unsubst_ty().clean(cx))
                             )
                         }
                     }).collect()
@@ -1897,12 +1898,12 @@ impl<'tcx> Clean<Item> for ty::VariantInfo<'tcx> {
         };
         Item {
             name: Some(self.name.clean(cx)),
-            attrs: inline::load_attrs(cx, cx.tcx(), self.id),
+            attrs: inline::load_attrs(cx, cx.tcx(), self.did),
             source: Span::empty(),
             visibility: Some(ast::Public),
-            def_id: self.id,
+            def_id: self.did,
             inner: VariantItem(Variant { kind: kind }),
-            stability: get_stability(cx, self.id),
+            stability: get_stability(cx, self.did),
         }
     }
 }
diff --git a/src/test/run-pass/enum-null-pointer-opt.rs b/src/test/run-pass/enum-null-pointer-opt.rs
index dd88dc11ea7..e296aff2782 100644
--- a/src/test/run-pass/enum-null-pointer-opt.rs
+++ b/src/test/run-pass/enum-null-pointer-opt.rs
@@ -18,6 +18,10 @@ use std::rc::Rc;
 use std::sync::Arc;
 
 trait Trait { fn dummy(&self) { } }
+trait Mirror { type Image; }
+impl<T> Mirror for T { type Image = T; }
+struct ParamTypeStruct<T>(T);
+struct AssocTypeStruct<T>(<T as Mirror>::Image);
 
 fn main() {
     // Functions
@@ -66,4 +70,7 @@ fn main() {
     // Should apply to types that have NonZero transitively
     assert_eq!(size_of::<String>(), size_of::<Option<String>>());
 
+    // Should apply to types where the pointer is substituted
+    assert_eq!(size_of::<&u8>(), size_of::<Option<ParamTypeStruct<&u8>>>());
+    assert_eq!(size_of::<&u8>(), size_of::<Option<AssocTypeStruct<&u8>>>());
 }