about summary refs log tree commit diff
diff options
context:
space:
mode:
authorVadim Petrochenkov <vadim.petrochenkov@gmail.com>2018-04-05 03:20:21 +0300
committerVadim Petrochenkov <vadim.petrochenkov@gmail.com>2018-04-12 23:06:03 +0300
commit4f69b7fb850cae83ea3d5adfeb8520418c976403 (patch)
treea5c3bdf0d80f9cbc8a0624c4686dd89f444c46fe
parent44acea4d880b646caa00a7237ea1a17031dd2116 (diff)
downloadrust-4f69b7fb850cae83ea3d5adfeb8520418c976403.tar.gz
rust-4f69b7fb850cae83ea3d5adfeb8520418c976403.zip
Avoid comparing fields by name when possible
Resolve them into field indices once and then use those resolutions

+ Fix rebase
-rw-r--r--src/librustc/hir/intravisit.rs2
-rw-r--r--src/librustc/hir/lowering.rs3
-rw-r--r--src/librustc/hir/mod.rs2
-rw-r--r--src/librustc/ich/impls_hir.rs46
-rw-r--r--src/librustc/middle/dead.rs37
-rw-r--r--src/librustc/middle/expr_use_visitor.rs16
-rw-r--r--src/librustc/middle/mem_categorization.rs39
-rw-r--r--src/librustc/ty/context.rs23
-rw-r--r--src/librustc/ty/mod.rs39
-rw-r--r--src/librustc/ty/util.rs38
-rw-r--r--src/librustc_borrowck/borrowck/gather_loans/restrictions.rs5
-rw-r--r--src/librustc_borrowck/borrowck/mod.rs6
-rw-r--r--src/librustc_borrowck/borrowck/move_data.rs8
-rw-r--r--src/librustc_lint/builtin.rs21
-rw-r--r--src/librustc_mir/hair/cx/expr.rs29
-rw-r--r--src/librustc_mir/hair/pattern/mod.rs20
-rw-r--r--src/librustc_privacy/lib.rs14
-rw-r--r--src/librustc_save_analysis/dump_visitor.rs10
-rw-r--r--src/librustc_save_analysis/lib.rs9
-rw-r--r--src/librustc_typeck/check/_match.rs16
-rw-r--r--src/librustc_typeck/check/method/suggest.rs6
-rw-r--r--src/librustc_typeck/check/mod.rs29
-rw-r--r--src/librustc_typeck/check/writeback.rs35
-rw-r--r--src/librustc_typeck/collect.rs6
-rw-r--r--src/libsyntax/ext/build.rs4
-rw-r--r--src/libsyntax/parse/parser.rs5
-rw-r--r--src/test/compile-fail/borrowck/borrowck-uninit-field-access.rs2
27 files changed, 244 insertions, 226 deletions
diff --git a/src/librustc/hir/intravisit.rs b/src/librustc/hir/intravisit.rs
index 0a7f0e4dc4c..be9f8b8dac5 100644
--- a/src/librustc/hir/intravisit.rs
+++ b/src/librustc/hir/intravisit.rs
@@ -658,6 +658,7 @@ pub fn walk_pat<'v, V: Visitor<'v>>(visitor: &mut V, pattern: &'v Pat) {
         PatKind::Struct(ref qpath, ref fields, _) => {
             visitor.visit_qpath(qpath, pattern.id, pattern.span);
             for field in fields {
+                visitor.visit_id(field.node.id);
                 visitor.visit_name(field.span, field.node.name);
                 visitor.visit_pat(&field.node.pat)
             }
@@ -959,6 +960,7 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr) {
         ExprStruct(ref qpath, ref fields, ref optional_base) => {
             visitor.visit_qpath(qpath, expression.id, expression.span);
             for field in fields {
+                visitor.visit_id(field.id);
                 visitor.visit_name(field.name.span, field.name.node);
                 visitor.visit_expr(&field.expr)
             }
diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs
index 262c307feed..fee076acb20 100644
--- a/src/librustc/hir/lowering.rs
+++ b/src/librustc/hir/lowering.rs
@@ -2100,6 +2100,7 @@ impl<'a> LoweringContext<'a> {
 
     fn lower_field(&mut self, f: &Field) -> hir::Field {
         hir::Field {
+            id: self.next_id().node_id,
             name: respan(f.ident.span, self.lower_ident(f.ident)),
             expr: P(self.lower_expr(&f.expr)),
             span: f.span,
@@ -2863,6 +2864,7 @@ impl<'a> LoweringContext<'a> {
                     .map(|f| Spanned {
                         span: f.span,
                         node: hir::FieldPat {
+                            id: self.next_id().node_id,
                             name: self.lower_ident(f.node.ident),
                             pat: self.lower_pat(&f.node.pat),
                             is_shorthand: f.node.is_shorthand,
@@ -3741,6 +3743,7 @@ impl<'a> LoweringContext<'a> {
 
     fn field(&mut self, name: Name, expr: P<hir::Expr>, span: Span) -> hir::Field {
         hir::Field {
+            id: self.next_id().node_id,
             name: Spanned { node: name, span },
             span,
             expr,
diff --git a/src/librustc/hir/mod.rs b/src/librustc/hir/mod.rs
index 79b39be3eb2..e6080fad91d 100644
--- a/src/librustc/hir/mod.rs
+++ b/src/librustc/hir/mod.rs
@@ -827,6 +827,7 @@ impl Pat {
 /// except is_shorthand is true
 #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
 pub struct FieldPat {
+    pub id: NodeId,
     /// The identifier for the field
     pub name: Name,
     /// The pattern the field is destructured to
@@ -1172,6 +1173,7 @@ pub struct Arm {
 
 #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
 pub struct Field {
+    pub id: NodeId,
     pub name: Spanned<Name>,
     pub expr: P<Expr>,
     pub span: Span,
diff --git a/src/librustc/ich/impls_hir.rs b/src/librustc/ich/impls_hir.rs
index ec573e3d681..4a001802eac 100644
--- a/src/librustc/ich/impls_hir.rs
+++ b/src/librustc/ich/impls_hir.rs
@@ -420,11 +420,23 @@ impl<'a> HashStable<StableHashingContext<'a>> for hir::Pat {
 }
 
 impl_stable_hash_for_spanned!(hir::FieldPat);
-impl_stable_hash_for!(struct hir::FieldPat {
-    name,
-    pat,
-    is_shorthand
-});
+
+impl<'a> HashStable<StableHashingContext<'a>> for hir::FieldPat {
+    fn hash_stable<W: StableHasherResult>(&self,
+                                          hcx: &mut StableHashingContext<'a>,
+                                          hasher: &mut StableHasher<W>) {
+        let hir::FieldPat {
+            id: _,
+            name,
+            ref pat,
+            is_shorthand,
+        } = *self;
+
+        name.hash_stable(hcx, hasher);
+        pat.hash_stable(hcx, hasher);
+        is_shorthand.hash_stable(hcx, hasher);
+    }
+}
 
 impl_stable_hash_for!(enum hir::BindingAnnotation {
     Unannotated,
@@ -507,12 +519,24 @@ impl_stable_hash_for!(struct hir::Arm {
     body
 });
 
-impl_stable_hash_for!(struct hir::Field {
-    name,
-    expr,
-    span,
-    is_shorthand
-});
+impl<'a> HashStable<StableHashingContext<'a>> for hir::Field {
+    fn hash_stable<W: StableHasherResult>(&self,
+                                          hcx: &mut StableHashingContext<'a>,
+                                          hasher: &mut StableHasher<W>) {
+        let hir::Field {
+            id: _,
+            name,
+            ref expr,
+            span,
+            is_shorthand,
+        } = *self;
+
+        name.hash_stable(hcx, hasher);
+        expr.hash_stable(hcx, hasher);
+        span.hash_stable(hcx, hasher);
+        is_shorthand.hash_stable(hcx, hasher);
+    }
+}
 
 impl_stable_hash_for_spanned!(ast::Name);
 
diff --git a/src/librustc/middle/dead.rs b/src/librustc/middle/dead.rs
index 5800988344a..9ec3d2e2460 100644
--- a/src/librustc/middle/dead.rs
+++ b/src/librustc/middle/dead.rs
@@ -13,7 +13,7 @@
 // from live codes are live, and everything else is dead.
 
 use hir::map as hir_map;
-use hir::{self, Item_, PatKind};
+use hir::{self, PatKind};
 use hir::intravisit::{self, Visitor, NestedVisitorMap};
 use hir::itemlikevisit::ItemLikeVisitor;
 
@@ -99,10 +99,11 @@ impl<'a, 'tcx> MarkSymbolVisitor<'a, 'tcx> {
         self.check_def_id(self.tables.type_dependent_defs()[id].def_id());
     }
 
-    fn handle_field_access(&mut self, lhs: &hir::Expr, name: ast::Name) {
+    fn handle_field_access(&mut self, lhs: &hir::Expr, node_id: ast::NodeId) {
         match self.tables.expr_ty_adjusted(lhs).sty {
             ty::TyAdt(def, _) => {
-                self.insert_def_id(def.non_enum_variant().field_named(name).did);
+                let index = self.tcx.field_index(node_id, self.tables);
+                self.insert_def_id(def.non_enum_variant().fields[index].did);
             }
             ty::TyTuple(..) => {}
             _ => span_bug!(lhs.span, "named field access on non-ADT"),
@@ -119,7 +120,8 @@ impl<'a, 'tcx> MarkSymbolVisitor<'a, 'tcx> {
             if let PatKind::Wild = pat.node.pat.node {
                 continue;
             }
-            self.insert_def_id(variant.field_named(pat.node.name).did);
+            let index = self.tcx.field_index(pat.node.id, self.tables);
+            self.insert_def_id(variant.fields[index].did);
         }
     }
 
@@ -182,18 +184,11 @@ impl<'a, 'tcx> MarkSymbolVisitor<'a, 'tcx> {
         self.inherited_pub_visibility = had_inherited_pub_visibility;
     }
 
-    fn mark_as_used_if_union(&mut self, did: DefId, fields: &hir::HirVec<hir::Field>) {
-        if let Some(node_id) = self.tcx.hir.as_local_node_id(did) {
-            if let Some(hir_map::NodeItem(item)) = self.tcx.hir.find(node_id) {
-                if let Item_::ItemUnion(ref variant, _) = item.node {
-                    if variant.fields().len() > 1 {
-                        for field in variant.fields() {
-                            if fields.iter().find(|x| x.name.node == field.name).is_some() {
-                                self.live_symbols.insert(field.id);
-                            }
-                        }
-                    }
-                }
+    fn mark_as_used_if_union(&mut self, adt: &ty::AdtDef, fields: &hir::HirVec<hir::Field>) {
+        if adt.is_union() && adt.non_enum_variant().fields.len() > 1 && adt.did.is_local() {
+            for field in fields {
+                let index = self.tcx.field_index(field.id, self.tables);
+                self.insert_def_id(adt.non_enum_variant().fields[index].did);
             }
         }
     }
@@ -233,14 +228,12 @@ impl<'a, 'tcx> Visitor<'tcx> for MarkSymbolVisitor<'a, 'tcx> {
             hir::ExprMethodCall(..) => {
                 self.lookup_and_handle_method(expr.hir_id);
             }
-            hir::ExprField(ref lhs, ref name) => {
-                self.handle_field_access(&lhs, name.node);
+            hir::ExprField(ref lhs, ..) => {
+                self.handle_field_access(&lhs, expr.id);
             }
             hir::ExprStruct(_, ref fields, _) => {
-                if let ty::TypeVariants::TyAdt(ref def, _) = self.tables.expr_ty(expr).sty {
-                    if def.is_union() {
-                        self.mark_as_used_if_union(def.did, fields);
-                    }
+                if let ty::TypeVariants::TyAdt(ref adt, _) = self.tables.expr_ty(expr).sty {
+                    self.mark_as_used_if_union(adt, fields);
                 }
             }
             _ => ()
diff --git a/src/librustc/middle/expr_use_visitor.rs b/src/librustc/middle/expr_use_visitor.rs
index fb83e563ffc..2cc5a4a8fe6 100644
--- a/src/librustc/middle/expr_use_visitor.rs
+++ b/src/librustc/middle/expr_use_visitor.rs
@@ -659,11 +659,15 @@ impl<'a, 'gcx, 'tcx> ExprUseVisitor<'a, 'gcx, 'tcx> {
         match with_cmt.ty.sty {
             ty::TyAdt(adt, substs) if adt.is_struct() => {
                 // Consume those fields of the with expression that are needed.
-                for with_field in &adt.non_enum_variant().fields {
-                    if !contains_field_named(with_field, fields) {
+                for (f_index, with_field) in adt.non_enum_variant().fields.iter().enumerate() {
+                    let is_mentioned = fields.iter().any(|f| {
+                        self.tcx().field_index(f.id, self.mc.tables) == f_index
+                    });
+                    if !is_mentioned {
                         let cmt_field = self.mc.cat_field(
                             &*with_expr,
                             with_cmt.clone(),
+                            f_index,
                             with_field.name,
                             with_field.ty(self.tcx(), substs)
                         );
@@ -687,14 +691,6 @@ impl<'a, 'gcx, 'tcx> ExprUseVisitor<'a, 'gcx, 'tcx> {
         // walk the with expression so that complex expressions
         // are properly handled.
         self.walk_expr(with_expr);
-
-        fn contains_field_named(field: &ty::FieldDef,
-                                fields: &[hir::Field])
-                                -> bool
-        {
-            fields.iter().any(
-                |f| f.name.node == field.name)
-        }
     }
 
     // Invoke the appropriate delegate calls for anything that gets
diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs
index c7449325f75..6f41f07dce8 100644
--- a/src/librustc/middle/mem_categorization.rs
+++ b/src/librustc/middle/mem_categorization.rs
@@ -84,6 +84,7 @@ use syntax::ast::{self, Name};
 use syntax_pos::Span;
 
 use std::fmt;
+use std::hash::{Hash, Hasher};
 use rustc_data_structures::sync::Lrc;
 use std::rc::Rc;
 use util::nodemap::ItemLocalSet;
@@ -132,9 +133,22 @@ pub enum InteriorKind {
     InteriorElement(InteriorOffsetKind),
 }
 
-// FIXME: Use actual index instead of `ast::Name` with questionable hygiene
-#[derive(Clone, Copy, PartialEq, Eq, Hash)]
-pub struct FieldIndex(pub ast::Name);
+// Contains index of a field that is actually used for loan path comparisons and
+// string representation of the field that should be used only for diagnostics.
+#[derive(Clone, Copy, Eq)]
+pub struct FieldIndex(pub usize, pub Name);
+
+impl PartialEq for FieldIndex {
+    fn eq(&self, rhs: &Self) -> bool {
+        self.0 == rhs.0
+    }
+}
+
+impl Hash for FieldIndex {
+    fn hash<H: Hasher>(&self, h: &mut H) {
+        self.0.hash(h)
+    }
+}
 
 #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
 pub enum InteriorOffsetKind {
@@ -195,7 +209,7 @@ pub enum ImmutabilityBlame<'tcx> {
 }
 
 impl<'tcx> cmt_<'tcx> {
-    fn resolve_field(&self, field_name: Name) -> Option<(&'tcx ty::AdtDef, &'tcx ty::FieldDef)>
+    fn resolve_field(&self, field_index: usize) -> Option<(&'tcx ty::AdtDef, &'tcx ty::FieldDef)>
     {
         let adt_def = match self.ty.sty {
             ty::TyAdt(def, _) => def,
@@ -212,7 +226,7 @@ impl<'tcx> cmt_<'tcx> {
                 &adt_def.variants[0]
             }
         };
-        Some((adt_def, variant_def.field_named(field_name)))
+        Some((adt_def, &variant_def.fields[field_index]))
     }
 
     pub fn immutability_blame(&self) -> Option<ImmutabilityBlame<'tcx>> {
@@ -639,7 +653,8 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> {
                    expr.id,
                    expr,
                    base_cmt);
-            Ok(self.cat_field(expr, base_cmt, f_name.node, expr_ty))
+            let f_index = self.tcx.field_index(expr.id, self.tables);
+            Ok(self.cat_field(expr, base_cmt, f_index, f_name.node, expr_ty))
           }
 
           hir::ExprIndex(ref base, _) => {
@@ -967,6 +982,7 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> {
     pub fn cat_field<N:ast_node>(&self,
                                  node: &N,
                                  base_cmt: cmt<'tcx>,
+                                 f_index: usize,
                                  f_name: Name,
                                  f_ty: Ty<'tcx>)
                                  -> cmt<'tcx> {
@@ -974,7 +990,7 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> {
             id: node.id(),
             span: node.span(),
             mutbl: base_cmt.mutbl.inherit(),
-            cat: Categorization::Interior(base_cmt, InteriorField(FieldIndex(f_name))),
+            cat: Categorization::Interior(base_cmt, InteriorField(FieldIndex(f_index, f_name))),
             ty: f_ty,
             note: NoteNone
         });
@@ -1262,7 +1278,7 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> {
 
             for (i, subpat) in subpats.iter().enumerate_and_adjust(expected_len, ddpos) {
                 let subpat_ty = self.pat_ty(&subpat)?; // see (*2)
-                let interior = InteriorField(FieldIndex(Name::intern(&i.to_string())));
+                let interior = InteriorField(FieldIndex(i, Name::intern(&i.to_string())));
                 let subcmt = self.cat_imm_interior(pat, cmt.clone(), subpat_ty, interior);
                 self.cat_pattern_(subcmt, &subpat, op)?;
             }
@@ -1285,7 +1301,8 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> {
 
             for fp in field_pats {
                 let field_ty = self.pat_ty(&fp.node.pat)?; // see (*2)
-                let cmt_field = self.cat_field(pat, cmt.clone(), fp.node.name, field_ty);
+                let f_index = self.tcx.field_index(fp.node.id, self.tables);
+                let cmt_field = self.cat_field(pat, cmt.clone(), f_index, fp.node.name, field_ty);
                 self.cat_pattern_(cmt_field, &fp.node.pat, op)?;
             }
           }
@@ -1302,7 +1319,7 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> {
             };
             for (i, subpat) in subpats.iter().enumerate_and_adjust(expected_len, ddpos) {
                 let subpat_ty = self.pat_ty(&subpat)?; // see (*2)
-                let interior = InteriorField(FieldIndex(Name::intern(&i.to_string())));
+                let interior = InteriorField(FieldIndex(i, Name::intern(&i.to_string())));
                 let subcmt = self.cat_imm_interior(pat, cmt.clone(), subpat_ty, interior);
                 self.cat_pattern_(subcmt, &subpat, op)?;
             }
@@ -1521,7 +1538,7 @@ pub fn ptr_sigil(ptr: PointerKind) -> &'static str {
 impl fmt::Debug for InteriorKind {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         match *self {
-            InteriorField(FieldIndex(name)) => write!(f, "{}", name),
+            InteriorField(FieldIndex(_, info)) => write!(f, "{}", info),
             InteriorElement(..) => write!(f, "[]"),
         }
     }
diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs
index 1e5d0753e69..76ec8c21743 100644
--- a/src/librustc/ty/context.rs
+++ b/src/librustc/ty/context.rs
@@ -346,6 +346,12 @@ pub struct TypeckTables<'tcx> {
     /// method calls, including those of overloaded operators.
     type_dependent_defs: ItemLocalMap<Def>,
 
+    /// Resolved field indices for field accesses in expressions (`S { field }`, `obj.field`)
+    /// or patterns (`S { field }`). The index is often useful by itself, but to learn more
+    /// about the field you also need definition of the variant to which the field
+    /// belongs, but it may not exist if it's a tuple field (`tuple.0`).
+    field_indices: ItemLocalMap<usize>,
+
     /// Stores the canonicalized types provided by the user. See also `UserAssertTy` statement in
     /// MIR.
     user_provided_tys: ItemLocalMap<CanonicalTy<'tcx>>,
@@ -426,6 +432,7 @@ impl<'tcx> TypeckTables<'tcx> {
         TypeckTables {
             local_id_root,
             type_dependent_defs: ItemLocalMap(),
+            field_indices: ItemLocalMap(),
             user_provided_tys: ItemLocalMap(),
             node_types: ItemLocalMap(),
             node_substs: ItemLocalMap(),
@@ -468,6 +475,20 @@ impl<'tcx> TypeckTables<'tcx> {
         }
     }
 
+    pub fn field_indices(&self) -> LocalTableInContext<usize> {
+        LocalTableInContext {
+            local_id_root: self.local_id_root,
+            data: &self.field_indices
+        }
+    }
+
+    pub fn field_indices_mut(&mut self) -> LocalTableInContextMut<usize> {
+        LocalTableInContextMut {
+            local_id_root: self.local_id_root,
+            data: &mut self.field_indices
+        }
+    }
+
     pub fn user_provided_tys(&self) -> LocalTableInContext<CanonicalTy<'tcx>> {
         LocalTableInContext {
             local_id_root: self.local_id_root,
@@ -706,6 +727,7 @@ impl<'a, 'gcx> HashStable<StableHashingContext<'a>> for TypeckTables<'gcx> {
         let ty::TypeckTables {
             local_id_root,
             ref type_dependent_defs,
+            ref field_indices,
             ref user_provided_tys,
             ref node_types,
             ref node_substs,
@@ -726,6 +748,7 @@ impl<'a, 'gcx> HashStable<StableHashingContext<'a>> for TypeckTables<'gcx> {
 
         hcx.with_node_id_hashing_mode(NodeIdHashingMode::HashDefPath, |hcx| {
             type_dependent_defs.hash_stable(hcx, hasher);
+            field_indices.hash_stable(hcx, hasher);
             user_provided_tys.hash_stable(hcx, hasher);
             node_types.hash_stable(hcx, hasher);
             node_substs.hash_stable(hcx, hasher);
diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs
index 33b59eda7ce..d0850f5ba6e 100644
--- a/src/librustc/ty/mod.rs
+++ b/src/librustc/ty/mod.rs
@@ -50,7 +50,7 @@ use std::vec::IntoIter;
 use std::mem;
 use syntax::ast::{self, DUMMY_NODE_ID, Name, Ident, NodeId};
 use syntax::attr;
-use syntax::ext::hygiene::{Mark, SyntaxContext};
+use syntax::ext::hygiene::Mark;
 use syntax::symbol::{Symbol, InternedString};
 use syntax_pos::{DUMMY_SP, Span};
 
@@ -2091,32 +2091,6 @@ impl<'a, 'gcx, 'tcx> AdtDef {
     }
 }
 
-impl<'a, 'gcx, 'tcx> VariantDef {
-    #[inline]
-    pub fn find_field_named(&self, name: ast::Name) -> Option<&FieldDef> {
-        self.index_of_field_named(name).map(|index| &self.fields[index])
-    }
-
-    pub fn index_of_field_named(&self, name: ast::Name) -> Option<usize> {
-        if let Some(index) = self.fields.iter().position(|f| f.name == name) {
-            return Some(index);
-        }
-        let mut ident = name.to_ident();
-        while ident.span.ctxt() != SyntaxContext::empty() {
-            ident.span.remove_mark();
-            if let Some(field) = self.fields.iter().position(|f| f.name.to_ident() == ident) {
-                return Some(field);
-            }
-        }
-        None
-    }
-
-    #[inline]
-    pub fn field_named(&self, name: ast::Name) -> &FieldDef {
-        self.find_field_named(name).unwrap()
-    }
-}
-
 impl<'a, 'gcx, 'tcx> FieldDef {
     pub fn ty(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>, subst: &Substs<'tcx>) -> Ty<'tcx> {
         tcx.type_of(self.did).subst(tcx, subst)
@@ -2383,6 +2357,17 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
         }
     }
 
+    pub fn field_index(self, node_id: NodeId, tables: &TypeckTables) -> usize {
+        let hir_id = self.hir.node_to_hir_id(node_id);
+        tables.field_indices().get(hir_id).cloned().expect("no index for a field")
+    }
+
+    pub fn find_field_index(self, ident: Ident, variant: &VariantDef) -> Option<usize> {
+        variant.fields.iter().position(|field| {
+            self.adjust_ident(ident.modern(), variant.did, DUMMY_NODE_ID).0 == field.name.to_ident()
+        })
+    }
+
     pub fn associated_items(
         self,
         def_id: DefId,
diff --git a/src/librustc/ty/util.rs b/src/librustc/ty/util.rs
index c170c2a63e8..77eff49d19f 100644
--- a/src/librustc/ty/util.rs
+++ b/src/librustc/ty/util.rs
@@ -33,7 +33,7 @@ use rustc_data_structures::fx::FxHashMap;
 use std::{cmp, fmt};
 use std::hash::Hash;
 use std::intrinsics;
-use syntax::ast::{self, Name};
+use syntax::ast;
 use syntax::attr::{self, SignedInt, UnsignedInt};
 use syntax_pos::{Span, DUMMY_SP};
 
@@ -270,42 +270,6 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
         false
     }
 
-    /// Returns the type of element at index `i` in tuple or tuple-like type `t`.
-    /// For an enum `t`, `variant` is None only if `t` is a univariant enum.
-    pub fn positional_element_ty(self,
-                                 ty: Ty<'tcx>,
-                                 i: usize,
-                                 variant: Option<DefId>) -> Option<Ty<'tcx>> {
-        match (&ty.sty, variant) {
-            (&TyAdt(adt, substs), Some(vid)) => {
-                adt.variant_with_id(vid).fields.get(i).map(|f| f.ty(self, substs))
-            }
-            (&TyAdt(adt, substs), None) => {
-                // Don't use `non_enum_variant`, this may be a univariant enum.
-                adt.variants[0].fields.get(i).map(|f| f.ty(self, substs))
-            }
-            (&TyTuple(ref v), None) => v.get(i).cloned(),
-            _ => None,
-        }
-    }
-
-    /// Returns the type of element at field `n` in struct or struct-like type `t`.
-    /// For an enum `t`, `variant` must be some def id.
-    pub fn named_element_ty(self,
-                            ty: Ty<'tcx>,
-                            n: Name,
-                            variant: Option<DefId>) -> Option<Ty<'tcx>> {
-        match (&ty.sty, variant) {
-            (&TyAdt(adt, substs), Some(vid)) => {
-                adt.variant_with_id(vid).find_field_named(n).map(|f| f.ty(self, substs))
-            }
-            (&TyAdt(adt, substs), None) => {
-                adt.non_enum_variant().find_field_named(n).map(|f| f.ty(self, substs))
-            }
-            _ => return None
-        }
-    }
-
     /// 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.
diff --git a/src/librustc_borrowck/borrowck/gather_loans/restrictions.rs b/src/librustc_borrowck/borrowck/gather_loans/restrictions.rs
index 2bd40890ca3..e3adb51433b 100644
--- a/src/librustc_borrowck/borrowck/gather_loans/restrictions.rs
+++ b/src/librustc_borrowck/borrowck/gather_loans/restrictions.rs
@@ -107,8 +107,9 @@ impl<'a, 'tcx> RestrictionsContext<'a, 'tcx> {
                     ty::TyAdt(adt_def, _) if adt_def.is_union() => match result {
                         RestrictionResult::Safe => RestrictionResult::Safe,
                         RestrictionResult::SafeIf(base_lp, mut base_vec) => {
-                            for field in &adt_def.non_enum_variant().fields {
-                                let field = InteriorKind::InteriorField(mc::FieldIndex(field.name));
+                            for (i, field) in adt_def.non_enum_variant().fields.iter().enumerate() {
+                                let field =
+                                    InteriorKind::InteriorField(mc::FieldIndex(i, field.name));
                                 let field_ty = if field == interior {
                                     cmt.ty
                                 } else {
diff --git a/src/librustc_borrowck/borrowck/mod.rs b/src/librustc_borrowck/borrowck/mod.rs
index 12c5fcdf13d..6d832d4060a 100644
--- a/src/librustc_borrowck/borrowck/mod.rs
+++ b/src/librustc_borrowck/borrowck/mod.rs
@@ -1336,10 +1336,10 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
                 out.push(')');
             }
 
-            LpExtend(ref lp_base, _, LpInterior(_, InteriorField(mc::FieldIndex(fname)))) => {
+            LpExtend(ref lp_base, _, LpInterior(_, InteriorField(mc::FieldIndex(_, info)))) => {
                 self.append_autoderefd_loan_path_to_string(&lp_base, out);
                 out.push('.');
-                out.push_str(&fname.as_str());
+                out.push_str(&info.as_str());
             }
 
             LpExtend(ref lp_base, _, LpInterior(_, InteriorElement)) => {
@@ -1414,7 +1414,7 @@ impl DataFlowOperator for LoanDataFlowOperator {
 impl<'tcx> fmt::Debug for InteriorKind {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         match *self {
-            InteriorField(mc::FieldIndex(name)) => write!(f, "{}", name),
+            InteriorField(mc::FieldIndex(_, info)) => write!(f, "{}", info),
             InteriorElement => write!(f, "[]"),
         }
     }
diff --git a/src/librustc_borrowck/borrowck/move_data.rs b/src/librustc_borrowck/borrowck/move_data.rs
index a69b9dc0d4f..1f4050a5b36 100644
--- a/src/librustc_borrowck/borrowck/move_data.rs
+++ b/src/librustc_borrowck/borrowck/move_data.rs
@@ -342,8 +342,8 @@ impl<'a, 'tcx> MoveData<'tcx> {
             if let (&ty::TyAdt(adt_def, _), LpInterior(opt_variant_id, interior))
                     = (&base_lp.ty.sty, lp_elem) {
                 if adt_def.is_union() {
-                    for field in &adt_def.non_enum_variant().fields {
-                        let field = InteriorKind::InteriorField(mc::FieldIndex(field.name));
+                    for (i, field) in adt_def.non_enum_variant().fields.iter().enumerate() {
+                        let field = InteriorKind::InteriorField(mc::FieldIndex(i, field.name));
                         if field != interior {
                             let sibling_lp_kind =
                                 LpExtend(base_lp.clone(), mutbl, LpInterior(opt_variant_id, field));
@@ -394,8 +394,8 @@ impl<'a, 'tcx> MoveData<'tcx> {
         if let LpExtend(ref base_lp, mutbl, LpInterior(opt_variant_id, interior)) = lp.kind {
             if let ty::TyAdt(adt_def, _) = base_lp.ty.sty {
                 if adt_def.is_union() {
-                    for field in &adt_def.non_enum_variant().fields {
-                        let field = InteriorKind::InteriorField(mc::FieldIndex(field.name));
+                    for (i, field) in adt_def.non_enum_variant().fields.iter().enumerate() {
+                        let field = InteriorKind::InteriorField(mc::FieldIndex(i, field.name));
                         let field_ty = if field == interior {
                             lp.ty
                         } else {
diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs
index 48e9cc498dc..6f2c51b0f18 100644
--- a/src/librustc_lint/builtin.rs
+++ b/src/librustc_lint/builtin.rs
@@ -166,19 +166,24 @@ impl LintPass for NonShorthandFieldPatterns {
 
 impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NonShorthandFieldPatterns {
     fn check_pat(&mut self, cx: &LateContext, pat: &hir::Pat) {
-        if let PatKind::Struct(_, ref field_pats, _) = pat.node {
+        if let PatKind::Struct(ref qpath, ref field_pats, _) = pat.node {
+            let variant = cx.tables.pat_ty(pat).ty_adt_def()
+                                   .expect("struct pattern type is not an ADT")
+                                   .variant_of_def(cx.tables.qpath_def(qpath, pat.hir_id));
             for fieldpat in field_pats {
                 if fieldpat.node.is_shorthand {
                     continue;
                 }
+                if fieldpat.span.ctxt().outer().expn_info().is_some() {
+                    // Don't lint if this is a macro expansion: macro authors
+                    // shouldn't have to worry about this kind of style issue
+                    // (Issue #49588)
+                    continue;
+                }
                 if let PatKind::Binding(_, _, name, None) = fieldpat.node.pat.node {
-                    if name.node == fieldpat.node.name {
-                        if let Some(_) = fieldpat.span.ctxt().outer().expn_info() {
-                            // Don't lint if this is a macro expansion: macro authors
-                            // shouldn't have to worry about this kind of style issue
-                            // (Issue #49588)
-                            return;
-                        }
+                    let binding_ident = ast::Ident::new(name.node, name.span);
+                    if cx.tcx.find_field_index(binding_ident, &variant) ==
+                       Some(cx.tcx.field_index(fieldpat.node.id, cx.tables)) {
                         let mut err = cx.struct_span_lint(NON_SHORTHAND_FIELD_PATTERNS,
                                      fieldpat.span,
                                      &format!("the `{}:` in this pattern is redundant",
diff --git a/src/librustc_mir/hair/cx/expr.rs b/src/librustc_mir/hair/cx/expr.rs
index efe88c6789f..c0d28280946 100644
--- a/src/librustc_mir/hair/cx/expr.rs
+++ b/src/librustc_mir/hair/cx/expr.rs
@@ -16,7 +16,7 @@ use hair::cx::to_ref::ToRef;
 use rustc::hir::def::{Def, CtorKind};
 use rustc::middle::const_val::ConstVal;
 use rustc::mir::interpret::{GlobalId, Value, PrimVal};
-use rustc::ty::{self, AdtKind, VariantDef, Ty};
+use rustc::ty::{self, AdtKind, Ty};
 use rustc::ty::adjustment::{Adjustment, Adjust, AutoBorrow, AutoBorrowMutability};
 use rustc::ty::cast::CastKind as TyCastKind;
 use rustc::hir;
@@ -420,12 +420,11 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
                 ty::TyAdt(adt, substs) => {
                     match adt.adt_kind() {
                         AdtKind::Struct | AdtKind::Union => {
-                            let field_refs = field_refs(&adt.variants[0], fields);
                             ExprKind::Adt {
                                 adt_def: adt,
                                 variant_index: 0,
                                 substs,
-                                fields: field_refs,
+                                fields: field_refs(cx, fields),
                                 base: base.as_ref().map(|base| {
                                     FruInfo {
                                         base: base.to_ref(),
@@ -446,12 +445,11 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
                                     assert!(base.is_none());
 
                                     let index = adt.variant_index_with_id(variant_id);
-                                    let field_refs = field_refs(&adt.variants[index], fields);
                                     ExprKind::Adt {
                                         adt_def: adt,
                                         variant_index: index,
                                         substs,
-                                        fields: field_refs,
+                                        fields: field_refs(cx, fields),
                                         base: None,
                                     }
                                 }
@@ -581,19 +579,10 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
                 body: block::to_expr_ref(cx, body),
             }
         }
-        hir::ExprField(ref source, name) => {
-            let index = match cx.tables().expr_ty_adjusted(source).sty {
-                ty::TyAdt(adt_def, _) => adt_def.variants[0].index_of_field_named(name.node),
-                ty::TyTuple(..) => name.node.as_str().parse::<usize>().ok(),
-                ref ty => span_bug!(expr.span, "field of non-ADT: {:?}", ty),
-            };
-            let index =
-                index.unwrap_or_else(|| {
-                    span_bug!(expr.span, "no index found for field `{}`", name.node)
-                });
+        hir::ExprField(ref source, ..) => {
             ExprKind::Field {
                 lhs: source.to_ref(),
-                name: Field::new(index),
+                name: Field::new(cx.tcx.field_index(expr.id, cx.tables)),
             }
         }
         hir::ExprCast(ref source, _) => {
@@ -994,13 +983,13 @@ fn capture_freevar<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
 }
 
 /// Converts a list of named fields (i.e. for struct-like struct/enum ADTs) into FieldExprRef.
-fn field_refs<'tcx>(variant: &'tcx VariantDef,
-                    fields: &'tcx [hir::Field])
-                    -> Vec<FieldExprRef<'tcx>> {
+fn field_refs<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
+                              fields: &'tcx [hir::Field])
+                              -> Vec<FieldExprRef<'tcx>> {
     fields.iter()
         .map(|field| {
             FieldExprRef {
-                name: Field::new(variant.index_of_field_named(field.name.node).unwrap()),
+                name: Field::new(cx.tcx.field_index(field.id, cx.tables)),
                 expr: field.expr.to_ref(),
             }
         })
diff --git a/src/librustc_mir/hair/pattern/mod.rs b/src/librustc_mir/hair/pattern/mod.rs
index c3f41e8ac48..8d2b73d6ba0 100644
--- a/src/librustc_mir/hair/pattern/mod.rs
+++ b/src/librustc_mir/hair/pattern/mod.rs
@@ -528,28 +528,12 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> {
 
             PatKind::Struct(ref qpath, ref fields, _) => {
                 let def = self.tables.qpath_def(qpath, pat.hir_id);
-                let adt_def = match ty.sty {
-                    ty::TyAdt(adt_def, _) => adt_def,
-                    _ => {
-                        span_bug!(
-                            pat.span,
-                            "struct pattern not applied to an ADT");
-                    }
-                };
-                let variant_def = adt_def.variant_of_def(def);
-
                 let subpatterns =
                     fields.iter()
                           .map(|field| {
-                              let index = variant_def.index_of_field_named(field.node.name);
-                              let index = index.unwrap_or_else(|| {
-                                  span_bug!(
-                                      pat.span,
-                                      "no field with name {:?}",
-                                      field.node.name);
-                              });
                               FieldPattern {
-                                  field: Field::new(index),
+                                  field: Field::new(self.tcx.field_index(field.node.id,
+                                                                         self.tables)),
                                   pattern: self.lower_pattern(&field.node.pat),
                               }
                           })
diff --git a/src/librustc_privacy/lib.rs b/src/librustc_privacy/lib.rs
index ef710ff7a7e..ee08e622390 100644
--- a/src/librustc_privacy/lib.rs
+++ b/src/librustc_privacy/lib.rs
@@ -568,8 +568,10 @@ impl<'a, 'tcx> Visitor<'tcx> for NamePrivacyVisitor<'a, 'tcx> {
                     // If the expression uses FRU we need to make sure all the unmentioned fields
                     // are checked for privacy (RFC 736). Rather than computing the set of
                     // unmentioned fields, just check them all.
-                    for variant_field in &variant.fields {
-                        let field = fields.iter().find(|f| f.name.node == variant_field.name);
+                    for (vf_index, variant_field) in variant.fields.iter().enumerate() {
+                        let field = fields.iter().find(|f| {
+                            self.tcx.field_index(f.id, self.tables) == vf_index
+                        });
                         let (use_ctxt, span) = match field {
                             Some(field) => (field.name.node.to_ident().span, field.span),
                             None => (base.span, base.span),
@@ -579,8 +581,8 @@ impl<'a, 'tcx> Visitor<'tcx> for NamePrivacyVisitor<'a, 'tcx> {
                 } else {
                     for field in fields {
                         let use_ctxt = field.name.node.to_ident().span;
-                        let field_def = variant.field_named(field.name.node);
-                        self.check_field(use_ctxt, field.span, adt, field_def);
+                        let index = self.tcx.field_index(field.id, self.tables);
+                        self.check_field(use_ctxt, field.span, adt, &variant.fields[index]);
                     }
                 }
             }
@@ -598,8 +600,8 @@ impl<'a, 'tcx> Visitor<'tcx> for NamePrivacyVisitor<'a, 'tcx> {
                 let variant = adt.variant_of_def(def);
                 for field in fields {
                     let use_ctxt = field.node.name.to_ident().span;
-                    let field_def = variant.field_named(field.node.name);
-                    self.check_field(use_ctxt, field.span, adt, field_def);
+                    let index = self.tcx.field_index(field.node.id, self.tables);
+                    self.check_field(use_ctxt, field.span, adt, &variant.fields[index]);
                 }
             }
             _ => {}
diff --git a/src/librustc_save_analysis/dump_visitor.rs b/src/librustc_save_analysis/dump_visitor.rs
index 811ebe57669..abaa02a856e 100644
--- a/src/librustc_save_analysis/dump_visitor.rs
+++ b/src/librustc_save_analysis/dump_visitor.rs
@@ -1005,20 +1005,16 @@ impl<'l, 'tcx: 'l, 'll, O: DumpOutput + 'll> DumpVisitor<'l, 'tcx, 'll, O> {
                 };
                 let variant = adt.variant_of_def(self.save_ctxt.get_path_def(p.id));
 
-                for &Spanned {
-                    node: ref field,
-                    span,
-                } in fields
-                {
+                for &Spanned { node: ref field, span } in fields {
                     let sub_span = self.span.span_for_first_ident(span);
-                    if let Some(f) = variant.find_field_named(field.ident.name) {
+                    if let Some(index) = self.tcx.find_field_index(field.ident, variant) {
                         if !self.span.filter_generated(sub_span, span) {
                             let span =
                                 self.span_from_span(sub_span.expect("No span fund for var ref"));
                             self.dumper.dump_ref(Ref {
                                 kind: RefKind::Variable,
                                 span,
-                                ref_id: ::id_from_def_id(f.did),
+                                ref_id: ::id_from_def_id(variant.fields[index].did),
                             });
                         }
                     }
diff --git a/src/librustc_save_analysis/lib.rs b/src/librustc_save_analysis/lib.rs
index 9da692851d9..ca19ed0df67 100644
--- a/src/librustc_save_analysis/lib.rs
+++ b/src/librustc_save_analysis/lib.rs
@@ -553,14 +553,15 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> {
                 };
                 match self.tables.expr_ty_adjusted(&hir_node).sty {
                     ty::TyAdt(def, _) if !def.is_enum() => {
-                        let f = def.non_enum_variant().field_named(ident.name);
+                        let variant = &def.non_enum_variant();
+                        let index = self.tcx.find_field_index(ident, variant).unwrap();
                         let sub_span = self.span_utils.span_for_last_ident(expr.span);
                         filter!(self.span_utils, sub_span, expr.span, None);
                         let span = self.span_from_span(sub_span.unwrap());
                         return Some(Data::RefData(Ref {
                             kind: RefKind::Variable,
                             span,
-                            ref_id: id_from_def_id(f.did),
+                            ref_id: id_from_def_id(variant.fields[index].did),
                         }));
                     }
                     ty::TyTuple(..) => None,
@@ -817,7 +818,7 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> {
         field_ref: &ast::Field,
         variant: &ty::VariantDef,
     ) -> Option<Ref> {
-        let f = variant.find_field_named(field_ref.ident.name)?;
+        let index = self.tcx.find_field_index(field_ref.ident, variant).unwrap();
         // 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);
         filter!(self.span_utils, sub_span, field_ref.ident.span, None);
@@ -825,7 +826,7 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> {
         Some(Ref {
             kind: RefKind::Variable,
             span,
-            ref_id: id_from_def_id(f.did),
+            ref_id: id_from_def_id(variant.fields[index].did),
         })
     }
 
diff --git a/src/librustc_typeck/check/_match.rs b/src/librustc_typeck/check/_match.rs
index ae373fbad22..7b4dc60409b 100644
--- a/src/librustc_typeck/check/_match.rs
+++ b/src/librustc_typeck/check/_match.rs
@@ -860,7 +860,8 @@ https://doc.rust-lang.org/reference/types.html#trait-objects");
         // Index the struct fields' types.
         let field_map = variant.fields
             .iter()
-            .map(|field| (field.name, field))
+            .enumerate()
+            .map(|(i, field)| (field.name.to_ident(), (i, field)))
             .collect::<FxHashMap<_, _>>();
 
         // Keep track of which fields have already appeared in the pattern.
@@ -869,7 +870,8 @@ https://doc.rust-lang.org/reference/types.html#trait-objects");
         let mut inexistent_fields = vec![];
         // Typecheck each field.
         for &Spanned { node: ref field, span } in fields {
-            let field_ty = match used_fields.entry(field.name) {
+            let ident = tcx.adjust(field.name, variant.did, self.body_id).0;
+            let field_ty = match used_fields.entry(ident) {
                 Occupied(occupied) => {
                     struct_span_err!(tcx.sess, span, E0025,
                                      "field `{}` bound multiple times \
@@ -883,10 +885,10 @@ https://doc.rust-lang.org/reference/types.html#trait-objects");
                 }
                 Vacant(vacant) => {
                     vacant.insert(span);
-                    field_map.get(&field.name)
-                        .map(|f| {
+                    field_map.get(&ident)
+                        .map(|(i, f)| {
+                            self.write_field_index(field.id, *i);
                             self.tcx.check_stability(f.did, Some(pat_id), span);
-
                             self.field_ty(span, f, substs)
                         })
                         .unwrap_or_else(|| {
@@ -958,8 +960,8 @@ https://doc.rust-lang.org/reference/types.html#trait-objects");
         } else if !etc {
             let unmentioned_fields = variant.fields
                 .iter()
-                .map(|field| field.name)
-                .filter(|field| !used_fields.contains_key(&field))
+                .map(|field| field.name.to_ident())
+                .filter(|ident| !used_fields.contains_key(&ident))
                 .collect::<Vec<_>>();
             if unmentioned_fields.len() > 0 {
                 let field_names = if unmentioned_fields.len() == 1 {
diff --git a/src/librustc_typeck/check/method/suggest.rs b/src/librustc_typeck/check/method/suggest.rs
index 2dbc590bbf7..d8907866467 100644
--- a/src/librustc_typeck/check/method/suggest.rs
+++ b/src/librustc_typeck/check/method/suggest.rs
@@ -304,8 +304,10 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                     for (ty, _) in self.autoderef(span, rcvr_ty) {
                         match ty.sty {
                             ty::TyAdt(def, substs) if !def.is_enum() => {
-                                if let Some(field) = def.non_enum_variant()
-                                    .find_field_named(item_name) {
+                                let variant = &def.non_enum_variant();
+                                if let Some(index) =
+                                        self.tcx.find_field_index(item_name.to_ident(), variant) {
+                                    let field = &variant.fields[index];
                                     let snippet = tcx.sess.codemap().span_to_snippet(expr.span);
                                     let expr_string = match snippet {
                                         Ok(expr_string) => expr_string,
diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs
index 9d0e0191ffb..ca35153d571 100644
--- a/src/librustc_typeck/check/mod.rs
+++ b/src/librustc_typeck/check/mod.rs
@@ -1938,6 +1938,11 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
         }
     }
 
+    pub fn write_field_index(&self, node_id: ast::NodeId, index: usize) {
+        let hir_id = self.tcx.hir.node_to_hir_id(node_id);
+        self.tables.borrow_mut().field_indices_mut().insert(hir_id, index);
+    }
+
     // The NodeId and the ItemLocalId must identify the same item. We just pass
     // both of them for consistency checking.
     pub fn write_method_call(&self,
@@ -3069,15 +3074,16 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                     let (ident, def_scope) =
                         self.tcx.adjust(field.node, base_def.did, self.body_id);
                     let fields = &base_def.non_enum_variant().fields;
-                    if let Some(field) = fields.iter().find(|f| f.name.to_ident() == ident) {
+                    if let Some(index) = fields.iter().position(|f| f.name.to_ident() == ident) {
+                        let field = &fields[index];
                         let field_ty = self.field_ty(expr.span, field, substs);
                         if field.vis.is_accessible_from(def_scope, self.tcx) {
                             let adjustments = autoderef.adjust_steps(needs);
                             self.apply_adjustments(base, adjustments);
                             autoderef.finalize();
 
+                            self.write_field_index(expr.id, index);
                             self.tcx.check_stability(field.did, Some(expr.id), expr.span);
-
                             return field_ty;
                         }
                         private_candidate = Some((base_def.did, field_ty));
@@ -3092,6 +3098,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                                 self.apply_adjustments(base, adjustments);
                                 autoderef.finalize();
 
+                                self.write_field_index(expr.id, index);
                                 return field_ty;
                             }
                         }
@@ -3284,8 +3291,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
         };
 
         let mut remaining_fields = FxHashMap();
-        for field in &variant.fields {
-            remaining_fields.insert(field.name.to_ident(), field);
+        for (i, field) in variant.fields.iter().enumerate() {
+            remaining_fields.insert(field.name.to_ident(), (i, field));
         }
 
         let mut seen_fields = FxHashMap();
@@ -3295,8 +3302,9 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
         // Typecheck each field.
         for field in ast_fields {
             let ident = tcx.adjust(field.name.node, variant.did, self.body_id).0;
-            let field_type = if let Some(v_field) = remaining_fields.remove(&ident) {
-                seen_fields.insert(field.name.node, field.span);
+            let field_type = if let Some((i, v_field)) = remaining_fields.remove(&ident) {
+                seen_fields.insert(ident, field.span);
+                self.write_field_index(field.id, i);
 
                 // we don't look at stability attributes on
                 // struct-like enums (yet...), but it's definitely not
@@ -3308,18 +3316,15 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                 self.field_ty(field.span, v_field, substs)
             } else {
                 error_happened = true;
-                if let Some(_) = variant.find_field_named(field.name.node) {
+                if let Some(prev_span) = seen_fields.get(&ident) {
                     let mut err = struct_span_err!(self.tcx.sess,
                                                 field.name.span,
                                                 E0062,
                                                 "field `{}` specified more than once",
-                                                field.name.node);
+                                                ident);
 
                     err.span_label(field.name.span, "used more than once");
-
-                    if let Some(prev_span) = seen_fields.get(&field.name.node) {
-                        err.span_label(*prev_span, format!("first use of `{}`", field.name.node));
-                    }
+                    err.span_label(*prev_span, format!("first use of `{}`", ident));
 
                     err.emit();
                 } else {
diff --git a/src/librustc_typeck/check/writeback.rs b/src/librustc_typeck/check/writeback.rs
index bbd04e0b19a..6e0d7dd8508 100644
--- a/src/librustc_typeck/check/writeback.rs
+++ b/src/librustc_typeck/check/writeback.rs
@@ -226,13 +226,24 @@ impl<'cx, 'gcx, 'tcx> Visitor<'gcx> for WritebackCx<'cx, 'gcx, 'tcx> {
 
         self.visit_node_id(e.span, e.hir_id);
 
-        if let hir::ExprClosure(_, _, body, _, _) = e.node {
-            let body = self.fcx.tcx.hir.body(body);
-            for arg in &body.arguments {
-                self.visit_node_id(e.span, arg.hir_id);
-            }
+        match e.node {
+            hir::ExprClosure(_, _, body, _, _) => {
+                let body = self.fcx.tcx.hir.body(body);
+                for arg in &body.arguments {
+                    self.visit_node_id(e.span, arg.hir_id);
+                }
 
-            self.visit_body(body);
+                self.visit_body(body);
+            }
+            hir::ExprStruct(_, ref fields, _) => {
+                for field in fields {
+                    self.visit_field_id(field.id);
+                }
+            }
+            hir::ExprField(..) => {
+                self.visit_field_id(e.id);
+            }
+            _ => {}
         }
 
         intravisit::walk_expr(self, e);
@@ -254,6 +265,11 @@ impl<'cx, 'gcx, 'tcx> Visitor<'gcx> for WritebackCx<'cx, 'gcx, 'tcx> {
                     .expect("missing binding mode");
                 self.tables.pat_binding_modes_mut().insert(p.hir_id, bm);
             }
+            hir::PatKind::Struct(_, ref fields, _) => {
+                for field in fields {
+                    self.visit_field_id(field.node.id);
+                }
+            }
             _ => {}
         };
 
@@ -384,6 +400,13 @@ impl<'cx, 'gcx, 'tcx> WritebackCx<'cx, 'gcx, 'tcx> {
         }
     }
 
+    fn visit_field_id(&mut self, node_id: ast::NodeId) {
+        let hir_id = self.tcx().hir.node_to_hir_id(node_id);
+        if let Some(index) = self.fcx.tables.borrow_mut().field_indices_mut().remove(hir_id) {
+            self.tables.field_indices_mut().insert(hir_id, index);
+        }
+    }
+
     fn visit_node_id(&mut self, span: Span, hir_id: hir::HirId) {
         // Export associated path extensions and method resultions.
         if let Some(def) = self.fcx
diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs
index a4f820d1fdc..640e6488862 100644
--- a/src/librustc_typeck/collect.rs
+++ b/src/librustc_typeck/collect.rs
@@ -513,11 +513,11 @@ fn convert_struct_variant<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
                                     discr: ty::VariantDiscr,
                                     def: &hir::VariantData)
                                     -> ty::VariantDef {
-    let mut seen_fields: FxHashMap<ast::Name, Span> = FxHashMap();
+    let mut seen_fields: FxHashMap<ast::Ident, Span> = FxHashMap();
     let node_id = tcx.hir.as_local_node_id(did).unwrap();
     let fields = def.fields().iter().map(|f| {
         let fid = tcx.hir.local_def_id(f.id);
-        let dup_span = seen_fields.get(&f.name).cloned();
+        let dup_span = seen_fields.get(&f.name.to_ident()).cloned();
         if let Some(prev_span) = dup_span {
             struct_span_err!(tcx.sess, f.span, E0124,
                              "field `{}` is already declared",
@@ -526,7 +526,7 @@ fn convert_struct_variant<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
                 .span_label(prev_span, format!("`{}` first declared here", f.name))
                 .emit();
         } else {
-            seen_fields.insert(f.name, f.span);
+            seen_fields.insert(f.name.to_ident(), f.span);
         }
 
         ty::FieldDef {
diff --git a/src/libsyntax/ext/build.rs b/src/libsyntax/ext/build.rs
index 2e1a4ee5851..36244f0a3c4 100644
--- a/src/libsyntax/ext/build.rs
+++ b/src/libsyntax/ext/build.rs
@@ -636,8 +636,8 @@ impl<'a> AstBuilder for ExtCtxt<'a> {
         self.expr(sp, ast::ExprKind::Field(expr, ident.with_span_pos(sp)))
     }
     fn expr_tup_field_access(&self, sp: Span, expr: P<ast::Expr>, idx: usize) -> P<ast::Expr> {
-        let id = Spanned { node: Ident::from_str(&idx.to_string()), span: sp };
-        self.expr(sp, ast::ExprKind::Field(expr, id))
+        let ident = Ident::from_str(&idx.to_string()).with_span_pos(sp);
+        self.expr(sp, ast::ExprKind::Field(expr, ident))
     }
     fn expr_addr_of(&self, sp: Span, e: P<ast::Expr>) -> P<ast::Expr> {
         self.expr(sp, ast::ExprKind::AddrOf(ast::Mutability::Immutable, e))
diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs
index 33a602a26fc..a7a9ce74512 100644
--- a/src/libsyntax/parse/parser.rs
+++ b/src/libsyntax/parse/parser.rs
@@ -2604,8 +2604,7 @@ impl<'a> Parser<'a> {
                   token::Literal(token::Integer(name), _) => {
                     let span = self.span;
                     self.bump();
-                    let ident = Ident { name, ctxt: span.ctxt() };
-                    let field = ExprKind::Field(e, respan(span, ident));
+                    let field = ExprKind::Field(e, Ident::new(name, span));
                     e = self.mk_expr(lo.to(span), field, ThinVec::new());
                   }
                   token::Literal(token::Float(n), _suf) => {
@@ -7031,7 +7030,7 @@ impl<'a> Parser<'a> {
             match self.token {
                 token::Ident(ident, false) if ident.name == keywords::Underscore.name() => {
                     self.bump(); // `_`
-                    Ok(Some(Ident { name: ident.name.gensymed(), ..ident }))
+                    Ok(Some(Ident::new(ident.name.gensymed(), ident.span)))
                 }
                 _ => self.parse_ident().map(Some),
             }
diff --git a/src/test/compile-fail/borrowck/borrowck-uninit-field-access.rs b/src/test/compile-fail/borrowck/borrowck-uninit-field-access.rs
index a214e3c126e..eec7df84c82 100644
--- a/src/test/compile-fail/borrowck/borrowck-uninit-field-access.rs
+++ b/src/test/compile-fail/borrowck/borrowck-uninit-field-access.rs
@@ -36,7 +36,7 @@ fn main() {
 
     let mut line1 = Line::default();
     let _moved = line1.origin;
-    let _ = line1.origin.x + 1; //[ast]~ ERROR use of collaterally moved value: `line1.origin.x`
+    let _ = line1.origin.x + 1; //[ast]~ ERROR use of moved value: `line1.origin.x`
                                 //[mir]~^ [E0382]
 
     let mut line2 = Line::default();