about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorEduard-Mihai Burtescu <edy.burt@gmail.com>2018-05-16 15:38:32 +0300
committerEduard-Mihai Burtescu <edy.burt@gmail.com>2019-04-23 17:27:54 +0300
commit61fcbfcd59dbc2b8b900aff5ea464330637b210b (patch)
tree88a54371c9b74e7e89820ce442a0f49eace351a9 /src
parent3bee49f42b6dfb039d2a8e59e5181e26531c3c11 (diff)
downloadrust-61fcbfcd59dbc2b8b900aff5ea464330637b210b.tar.gz
rust-61fcbfcd59dbc2b8b900aff5ea464330637b210b.zip
rustc_mir: don't rely on mir::UpvarDecl in the MIR borrowck.
Diffstat (limited to 'src')
-rw-r--r--src/librustc/mir/tcx.rs35
-rw-r--r--src/librustc_mir/borrow_check/error_reporting.rs12
-rw-r--r--src/librustc_mir/borrow_check/mod.rs110
-rw-r--r--src/librustc_mir/borrow_check/move_errors.rs14
-rw-r--r--src/librustc_mir/borrow_check/mutability_errors.rs15
-rw-r--r--src/librustc_mir/borrow_check/nll/explain_borrow/mod.rs1
-rw-r--r--src/librustc_mir/borrow_check/nll/mod.rs5
-rw-r--r--src/librustc_mir/borrow_check/nll/region_infer/error_reporting/mod.rs25
-rw-r--r--src/librustc_mir/borrow_check/nll/region_infer/error_reporting/region_name.rs8
-rw-r--r--src/librustc_mir/borrow_check/nll/region_infer/error_reporting/var_name.rs9
-rw-r--r--src/librustc_mir/borrow_check/nll/region_infer/mod.rs14
-rw-r--r--src/librustc_mir/borrow_check/nll/universal_regions.rs2
12 files changed, 159 insertions, 91 deletions
diff --git a/src/librustc/mir/tcx.rs b/src/librustc/mir/tcx.rs
index 23be1bbf6c6..04b763f773d 100644
--- a/src/librustc/mir/tcx.rs
+++ b/src/librustc/mir/tcx.rs
@@ -133,41 +133,6 @@ impl<'tcx> Place<'tcx> {
                 proj.base.ty(local_decls, tcx).projection_ty(tcx, &proj.elem),
         }
     }
-
-    /// If this is a field projection, and the field is being projected from a closure type,
-    /// then returns the index of the field being projected. Note that this closure will always
-    /// be `self` in the current MIR, because that is the only time we directly access the fields
-    /// of a closure type.
-    pub fn is_upvar_field_projection<'cx, 'gcx>(&self, mir: &'cx Mir<'tcx>,
-                                                tcx: &TyCtxt<'cx, 'gcx, 'tcx>) -> Option<Field> {
-        let (place, by_ref) = if let Place::Projection(ref proj) = self {
-            if let ProjectionElem::Deref = proj.elem {
-                (&proj.base, true)
-            } else {
-                (self, false)
-            }
-        } else {
-            (self, false)
-        };
-
-        match place {
-            Place::Projection(ref proj) => match proj.elem {
-                ProjectionElem::Field(field, _ty) => {
-                    let base_ty = proj.base.ty(mir, *tcx).ty;
-
-                    if (base_ty.is_closure() || base_ty.is_generator()) &&
-                        (!by_ref || mir.upvar_decls[field.index()].by_ref)
-                    {
-                        Some(field)
-                    } else {
-                        None
-                    }
-                },
-                _ => None,
-            }
-            _ => None,
-        }
-    }
 }
 
 pub enum RvalueInitializationState {
diff --git a/src/librustc_mir/borrow_check/error_reporting.rs b/src/librustc_mir/borrow_check/error_reporting.rs
index 3ab0996d3a1..12dcea7bd59 100644
--- a/src/librustc_mir/borrow_check/error_reporting.rs
+++ b/src/librustc_mir/borrow_check/error_reporting.rs
@@ -1088,7 +1088,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
                         | LocalKind::Temp => bug!("temporary or return pointer with a name"),
                         LocalKind::Var => "local variable ",
                         LocalKind::Arg
-                        if !self.mir.upvar_decls.is_empty()
+                        if !self.upvars.is_empty()
                             && local == Local::new(1) => {
                             "variable captured by `move` "
                         }
@@ -1632,11 +1632,11 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
                 match proj.elem {
                     ProjectionElem::Deref => {
                         let upvar_field_projection =
-                            place.is_upvar_field_projection(self.mir, &self.infcx.tcx);
+                            self.is_upvar_field_projection(place);
                         if let Some(field) = upvar_field_projection {
                             let var_index = field.index();
-                            let name = self.mir.upvar_decls[var_index].debug_name.to_string();
-                            if self.mir.upvar_decls[var_index].by_ref {
+                            let name = self.upvars[var_index].name.to_string();
+                            if self.upvars[var_index].by_ref {
                                 buf.push_str(&name);
                             } else {
                                 buf.push_str(&format!("*{}", &name));
@@ -1694,10 +1694,10 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
                         autoderef = true;
 
                         let upvar_field_projection =
-                            place.is_upvar_field_projection(self.mir, &self.infcx.tcx);
+                            self.is_upvar_field_projection(place);
                         if let Some(field) = upvar_field_projection {
                             let var_index = field.index();
-                            let name = self.mir.upvar_decls[var_index].debug_name.to_string();
+                            let name = self.upvars[var_index].name.to_string();
                             buf.push_str(&name);
                         } else {
                             let field_name = self.describe_field(&proj.base, field);
diff --git a/src/librustc_mir/borrow_check/mod.rs b/src/librustc_mir/borrow_check/mod.rs
index 4a3159d8a9d..14cafdef67d 100644
--- a/src/librustc_mir/borrow_check/mod.rs
+++ b/src/librustc_mir/borrow_check/mod.rs
@@ -1,7 +1,7 @@
 //! This query borrow-checks the MIR to (further) ensure it is not broken.
 
 use crate::borrow_check::nll::region_infer::RegionInferenceContext;
-use rustc::hir;
+use rustc::hir::{self, HirId};
 use rustc::hir::Node;
 use rustc::hir::def_id::DefId;
 use rustc::infer::InferCtxt;
@@ -27,6 +27,7 @@ use std::collections::BTreeMap;
 use std::mem;
 use std::rc::Rc;
 
+use syntax::ast::Name;
 use syntax_pos::{Span, DUMMY_SP};
 
 use crate::dataflow::indexes::{BorrowIndex, InitIndex, MoveOutIndex, MovePathIndex};
@@ -63,6 +64,19 @@ mod used_muts;
 
 pub(crate) mod nll;
 
+// FIXME(eddyb) perhaps move this somewhere more centrally.
+#[derive(Debug)]
+crate struct Upvar {
+    name: Name,
+
+    var_hir_id: HirId,
+
+    /// If true, the capture is behind a reference.
+    by_ref: bool,
+
+    mutability: Mutability,
+}
+
 pub fn provide(providers: &mut Providers<'_>) {
     *providers = Providers {
         mir_borrowck,
@@ -126,6 +140,36 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(
         .as_local_hir_id(def_id)
         .expect("do_mir_borrowck: non-local DefId");
 
+    // Gather the upvars of a closure, if any.
+    let tables = tcx.typeck_tables_of(def_id);
+    let upvars: Vec<_> = tables
+        .upvar_list
+        .get(&def_id)
+        .into_iter()
+        .flatten()
+        .map(|upvar_id| {
+            let var_hir_id = upvar_id.var_path.hir_id;
+            let var_node_id = tcx.hir().hir_to_node_id(var_hir_id);
+            let capture = tables.upvar_capture(*upvar_id);
+            let by_ref = match capture {
+                ty::UpvarCapture::ByValue => false,
+                ty::UpvarCapture::ByRef(..) => true,
+            };
+            let mut upvar = Upvar {
+                name: tcx.hir().name(var_node_id),
+                var_hir_id,
+                by_ref,
+                mutability: Mutability::Not,
+            };
+            let bm = *tables.pat_binding_modes().get(var_hir_id)
+                .expect("missing binding mode");
+            if bm == ty::BindByValue(hir::MutMutable) {
+                upvar.mutability = Mutability::Mut;
+            }
+            upvar
+        })
+        .collect();
+
     // Replace all regions with fresh inference variables. This
     // requires first making our own copy of the MIR. This copy will
     // be modified (in place) to contain non-lexical lifetimes. It
@@ -168,6 +212,7 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(
         def_id,
         free_regions,
         mir,
+        &upvars,
         location_table,
         param_env,
         &mut flow_inits,
@@ -240,6 +285,7 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(
         used_mut_upvars: SmallVec::new(),
         borrow_set,
         dominators,
+        upvars,
     };
 
     let mut state = Flows::new(
@@ -475,6 +521,9 @@ pub struct MirBorrowckCtxt<'cx, 'gcx: 'tcx, 'tcx: 'cx> {
 
     /// Dominators for MIR
     dominators: Dominators<BasicBlock>,
+
+    /// Information about upvars not necessarily preserved in types or MIR
+    upvars: Vec<Upvar>,
 }
 
 // Check that:
@@ -1287,8 +1336,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
         let propagate_closure_used_mut_place = |this: &mut Self, place: &Place<'tcx>| {
             match *place {
                 Place::Projection { .. } => {
-                    if let Some(field) = place.is_upvar_field_projection(
-                            this.mir, &this.infcx.tcx) {
+                    if let Some(field) = this.is_upvar_field_projection(place) {
                         this.used_mut_upvars.push(field);
                     }
                 }
@@ -2057,7 +2105,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
                 place: place @ Place::Projection(_),
                 is_local_mutation_allowed: _,
             } => {
-                if let Some(field) = place.is_upvar_field_projection(self.mir, &self.infcx.tcx) {
+                if let Some(field) = self.is_upvar_field_projection(place) {
                     self.used_mut_upvars.push(field);
                 }
             }
@@ -2127,13 +2175,9 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
                                     // Mutably borrowed data is mutable, but only if we have a
                                     // unique path to the `&mut`
                                     hir::MutMutable => {
-                                        let mode = match place.is_upvar_field_projection(
-                                            self.mir, &self.infcx.tcx)
-                                        {
+                                        let mode = match self.is_upvar_field_projection(place) {
                                             Some(field)
-                                                if {
-                                                    self.mir.upvar_decls[field.index()].by_ref
-                                                } =>
+                                                if self.upvars[field.index()].by_ref =>
                                             {
                                                 is_local_mutation_allowed
                                             }
@@ -2173,15 +2217,14 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
                     | ProjectionElem::ConstantIndex { .. }
                     | ProjectionElem::Subslice { .. }
                     | ProjectionElem::Downcast(..) => {
-                        let upvar_field_projection = place.is_upvar_field_projection(
-                            self.mir, &self.infcx.tcx);
+                        let upvar_field_projection = self.is_upvar_field_projection(place);
                         if let Some(field) = upvar_field_projection {
-                            let decl = &self.mir.upvar_decls[field.index()];
+                            let upvar = &self.upvars[field.index()];
                             debug!(
-                                "decl.mutability={:?} local_mutation_is_allowed={:?} place={:?}",
-                                decl, is_local_mutation_allowed, place
+                                "upvar.mutability={:?} local_mutation_is_allowed={:?} place={:?}",
+                                upvar, is_local_mutation_allowed, place
                             );
-                            match (decl.mutability, is_local_mutation_allowed) {
+                            match (upvar.mutability, is_local_mutation_allowed) {
                                 (Mutability::Not, LocalMutationIsAllowed::No)
                                 | (Mutability::Not, LocalMutationIsAllowed::ExceptUpvars) => {
                                     Err(place)
@@ -2229,6 +2272,41 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
             }
         }
     }
+
+    /// If `place` is a field projection, and the field is being projected from a closure type,
+    /// then returns the index of the field being projected. Note that this closure will always
+    /// be `self` in the current MIR, because that is the only time we directly access the fields
+    /// of a closure type.
+    pub fn is_upvar_field_projection(&self, place: &Place<'tcx>) -> Option<Field> {
+        let (place, by_ref) = if let Place::Projection(ref proj) = place {
+            if let ProjectionElem::Deref = proj.elem {
+                (&proj.base, true)
+            } else {
+                (place, false)
+            }
+        } else {
+            (place, false)
+        };
+
+        match place {
+            Place::Projection(ref proj) => match proj.elem {
+                ProjectionElem::Field(field, _ty) => {
+                    let tcx = self.infcx.tcx;
+                    let base_ty = proj.base.ty(self.mir, tcx).ty;
+
+                    if (base_ty.is_closure() || base_ty.is_generator()) &&
+                        (!by_ref || self.upvars[field.index()].by_ref)
+                    {
+                        Some(field)
+                    } else {
+                        None
+                    }
+                },
+                _ => None,
+            }
+            _ => None,
+        }
+    }
 }
 
 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
diff --git a/src/librustc_mir/borrow_check/move_errors.rs b/src/librustc_mir/borrow_check/move_errors.rs
index 7efe1d83c2e..a7bad44c42c 100644
--- a/src/librustc_mir/borrow_check/move_errors.rs
+++ b/src/librustc_mir/borrow_check/move_errors.rs
@@ -256,7 +256,7 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> {
             let origin = Origin::Mir;
             debug!("report: original_path={:?} span={:?}, kind={:?} \
                    original_path.is_upvar_field_projection={:?}", original_path, span, kind,
-                   original_path.is_upvar_field_projection(self.mir, &self.infcx.tcx));
+                   self.is_upvar_field_projection(original_path));
             (
                 match kind {
                     IllegalMoveOriginKind::Static => {
@@ -269,8 +269,7 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> {
                         let ty = place.ty(self.mir, self.infcx.tcx).ty;
                         let is_upvar_field_projection =
                             self.prefixes(&original_path, PrefixSet::All)
-                            .any(|p| p.is_upvar_field_projection(self.mir, &self.infcx.tcx)
-                                 .is_some());
+                            .any(|p| self.is_upvar_field_projection(p).is_some());
                         debug!("report: ty={:?}", ty);
                         match ty.sty {
                             ty::Array(..) | ty::Slice(..) =>
@@ -278,7 +277,7 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> {
                                     span, ty, None, origin
                                 ),
                             ty::Closure(def_id, closure_substs)
-                                if !self.mir.upvar_decls.is_empty() && is_upvar_field_projection
+                                if def_id == self.mir_def_id && is_upvar_field_projection
                             => {
                                 let closure_kind_ty =
                                     closure_substs.closure_kind_ty(def_id, self.infcx.tcx);
@@ -303,11 +302,8 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> {
                                     span, place_description, origin);
 
                                 for prefix in self.prefixes(&original_path, PrefixSet::All) {
-                                    if let Some(field) = prefix.is_upvar_field_projection(
-                                            self.mir, &self.infcx.tcx) {
-                                        let upvar_decl = &self.mir.upvar_decls[field.index()];
-                                        let upvar_hir_id =
-                                            upvar_decl.var_hir_id.assert_crate_local();
+                                    if let Some(field) = self.is_upvar_field_projection(prefix) {
+                                        let upvar_hir_id = self.upvars[field.index()].var_hir_id;
                                         let upvar_span = self.infcx.tcx.hir().span_by_hir_id(
                                             upvar_hir_id);
                                         diag.span_label(upvar_span, "captured outer variable");
diff --git a/src/librustc_mir/borrow_check/mutability_errors.rs b/src/librustc_mir/borrow_check/mutability_errors.rs
index b780511315d..c5ad2b18c23 100644
--- a/src/librustc_mir/borrow_check/mutability_errors.rs
+++ b/src/librustc_mir/borrow_check/mutability_errors.rs
@@ -68,10 +68,10 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> {
                 ));
 
                 item_msg = format!("`{}`", access_place_desc.unwrap());
-                if access_place.is_upvar_field_projection(self.mir, &self.infcx.tcx).is_some() {
+                if self.is_upvar_field_projection(access_place).is_some() {
                     reason = ", as it is not declared as mutable".to_string();
                 } else {
-                    let name = self.mir.upvar_decls[upvar_index.index()].debug_name;
+                    let name = self.upvars[upvar_index.index()].name;
                     reason = format!(", as `{}` is not declared as mutable", name);
                 }
             }
@@ -81,15 +81,14 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> {
                 elem: ProjectionElem::Deref,
             }) => {
                 if *base == Place::Base(PlaceBase::Local(Local::new(1))) &&
-                    !self.mir.upvar_decls.is_empty() {
+                    !self.upvars.is_empty() {
                     item_msg = format!("`{}`", access_place_desc.unwrap());
                     debug_assert!(self.mir.local_decls[Local::new(1)].ty.is_region_ptr());
                     debug_assert!(is_closure_or_generator(
                         the_place_err.ty(self.mir, self.infcx.tcx).ty
                     ));
 
-                    reason = if access_place.is_upvar_field_projection(self.mir,
-                                                                       &self.infcx.tcx).is_some() {
+                    reason = if self.is_upvar_field_projection(access_place).is_some() {
                         ", as it is a captured variable in a `Fn` closure".to_string()
                     } else {
                         ", as `Fn` closures cannot mutate their captured variables".to_string()
@@ -309,9 +308,7 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> {
 
                 err.span_label(span, format!("cannot {ACT}", ACT = act));
 
-                let upvar_hir_id = self.mir.upvar_decls[upvar_index.index()]
-                    .var_hir_id
-                    .assert_crate_local();
+                let upvar_hir_id = self.upvars[upvar_index.index()].var_hir_id;
                 let upvar_node_id = self.infcx.tcx.hir().hir_to_node_id(upvar_hir_id);
                 if let Some(Node::Binding(pat)) = self.infcx.tcx.hir().find(upvar_node_id) {
                     if let hir::PatKind::Binding(
@@ -452,7 +449,7 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> {
                 base,
                 elem: ProjectionElem::Deref,
             }) if *base == Place::Base(PlaceBase::Local(Local::new(1))) &&
-                  !self.mir.upvar_decls.is_empty() =>
+                  !self.upvars.is_empty() =>
             {
                 err.span_label(span, format!("cannot {ACT}", ACT = act));
                 err.span_help(
diff --git a/src/librustc_mir/borrow_check/nll/explain_borrow/mod.rs b/src/librustc_mir/borrow_check/nll/explain_borrow/mod.rs
index 5ad54080c5a..0430ad307ce 100644
--- a/src/librustc_mir/borrow_check/nll/explain_borrow/mod.rs
+++ b/src/librustc_mir/borrow_check/nll/explain_borrow/mod.rs
@@ -298,6 +298,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
                     let (category, from_closure, span, region_name) =
                         self.nonlexical_regioncx.free_region_constraint_info(
                             self.mir,
+                        &self.upvars,
                             self.mir_def_id,
                             self.infcx,
                             borrow_region_vid,
diff --git a/src/librustc_mir/borrow_check/nll/mod.rs b/src/librustc_mir/borrow_check/nll/mod.rs
index 3e1b93fb417..ad43c8ef66f 100644
--- a/src/librustc_mir/borrow_check/nll/mod.rs
+++ b/src/librustc_mir/borrow_check/nll/mod.rs
@@ -8,6 +8,7 @@ use crate::dataflow::move_paths::MoveData;
 use crate::dataflow::FlowAtLocation;
 use crate::dataflow::MaybeInitializedPlaces;
 use crate::transform::MirSource;
+use crate::borrow_check::Upvar;
 use rustc::hir::def_id::DefId;
 use rustc::infer::InferCtxt;
 use rustc::mir::{ClosureOutlivesSubject, ClosureRegionRequirements, Mir};
@@ -72,6 +73,7 @@ pub(in crate::borrow_check) fn compute_regions<'cx, 'gcx, 'tcx>(
     def_id: DefId,
     universal_regions: UniversalRegions<'tcx>,
     mir: &Mir<'tcx>,
+    upvars: &[Upvar],
     location_table: &LocationTable,
     param_env: ty::ParamEnv<'gcx>,
     flow_inits: &mut FlowAtLocation<'tcx, MaybeInitializedPlaces<'cx, 'gcx, 'tcx>>,
@@ -187,7 +189,8 @@ pub(in crate::borrow_check) fn compute_regions<'cx, 'gcx, 'tcx>(
     });
 
     // Solve the region constraints.
-    let closure_region_requirements = regioncx.solve(infcx, &mir, def_id, errors_buffer);
+    let closure_region_requirements =
+        regioncx.solve(infcx, &mir, upvars, def_id, errors_buffer);
 
     // Dump MIR results into a file, if that is enabled. This let us
     // write unit-tests, as well as helping with debugging.
diff --git a/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/mod.rs b/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/mod.rs
index 917e383cae8..abb30d042ca 100644
--- a/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/mod.rs
+++ b/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/mod.rs
@@ -4,6 +4,7 @@ use crate::borrow_check::nll::type_check::Locations;
 use crate::borrow_check::nll::universal_regions::DefiningTy;
 use crate::borrow_check::nll::ConstraintDescription;
 use crate::util::borrowck_errors::{BorrowckErrors, Origin};
+use crate::borrow_check::Upvar;
 use rustc::hir::def_id::DefId;
 use rustc::infer::error_reporting::nice_region_error::NiceRegionError;
 use rustc::infer::InferCtxt;
@@ -237,6 +238,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
     pub(super) fn report_error(
         &self,
         mir: &Mir<'tcx>,
+        upvars: &[Upvar],
         infcx: &InferCtxt<'_, '_, 'tcx>,
         mir_def_id: DefId,
         fr: RegionVid,
@@ -273,6 +275,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
             (ConstraintCategory::Return, true, false) if self.is_closure_fn_mut(infcx, fr) => {
                 self.report_fnmut_error(
                     mir,
+                    upvars,
                     infcx,
                     mir_def_id,
                     fr,
@@ -284,6 +287,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
             (ConstraintCategory::Assignment, true, false)
             | (ConstraintCategory::CallArgument, true, false) => self.report_escaping_data_error(
                 mir,
+                upvars,
                 infcx,
                 mir_def_id,
                 fr,
@@ -294,6 +298,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
             ),
             _ => self.report_general_error(
                 mir,
+                upvars,
                 infcx,
                 mir_def_id,
                 fr,
@@ -353,6 +358,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
     fn report_fnmut_error(
         &self,
         mir: &Mir<'tcx>,
+        upvars: &[Upvar],
         infcx: &InferCtxt<'_, '_, 'tcx>,
         mir_def_id: DefId,
         _fr: RegionVid,
@@ -377,7 +383,9 @@ impl<'tcx> RegionInferenceContext<'tcx> {
 
         diag.span_label(span, message);
 
-        match self.give_region_a_name(infcx, mir, mir_def_id, outlived_fr, &mut 1).unwrap().source {
+        match self.give_region_a_name(infcx, mir, upvars, mir_def_id, outlived_fr, &mut 1)
+            .unwrap().source
+        {
             RegionNameSource::NamedEarlyBoundRegion(fr_span)
             | RegionNameSource::NamedFreeRegion(fr_span)
             | RegionNameSource::SynthesizedFreeEnvRegion(fr_span, _)
@@ -415,6 +423,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
     fn report_escaping_data_error(
         &self,
         mir: &Mir<'tcx>,
+        upvars: &[Upvar],
         infcx: &InferCtxt<'_, '_, 'tcx>,
         mir_def_id: DefId,
         fr: RegionVid,
@@ -423,9 +432,10 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         span: Span,
         errors_buffer: &mut Vec<Diagnostic>,
     ) {
-        let fr_name_and_span = self.get_var_name_and_span_for_region(infcx.tcx, mir, fr);
+        let fr_name_and_span =
+            self.get_var_name_and_span_for_region(infcx.tcx, mir, upvars, fr);
         let outlived_fr_name_and_span =
-            self.get_var_name_and_span_for_region(infcx.tcx, mir, outlived_fr);
+            self.get_var_name_and_span_for_region(infcx.tcx, mir, upvars, outlived_fr);
 
         let escapes_from = match self.universal_regions.defining_ty {
             DefiningTy::Closure(..) => "closure",
@@ -442,6 +452,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         {
             return self.report_general_error(
                 mir,
+                upvars,
                 infcx,
                 mir_def_id,
                 fr,
@@ -504,6 +515,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
     fn report_general_error(
         &self,
         mir: &Mir<'tcx>,
+        upvars: &[Upvar],
         infcx: &InferCtxt<'_, '_, 'tcx>,
         mir_def_id: DefId,
         fr: RegionVid,
@@ -520,10 +532,10 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         );
 
         let counter = &mut 1;
-        let fr_name = self.give_region_a_name(infcx, mir, mir_def_id, fr, counter).unwrap();
+        let fr_name = self.give_region_a_name(infcx, mir, upvars, mir_def_id, fr, counter).unwrap();
         fr_name.highlight_region_name(&mut diag);
         let outlived_fr_name =
-            self.give_region_a_name(infcx, mir, mir_def_id, outlived_fr, counter).unwrap();
+            self.give_region_a_name(infcx, mir, upvars, mir_def_id, outlived_fr, counter).unwrap();
         outlived_fr_name.highlight_region_name(&mut diag);
 
         let mir_def_name = if infcx.tcx.is_closure(mir_def_id) {
@@ -656,6 +668,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
     crate fn free_region_constraint_info(
         &self,
         mir: &Mir<'tcx>,
+        upvars: &[Upvar],
         mir_def_id: DefId,
         infcx: &InferCtxt<'_, '_, 'tcx>,
         borrow_region: RegionVid,
@@ -664,7 +677,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         let (category, from_closure, span) =
             self.best_blame_constraint(mir, borrow_region, |r| r == outlived_region);
         let outlived_fr_name =
-            self.give_region_a_name(infcx, mir, mir_def_id, outlived_region, &mut 1);
+            self.give_region_a_name(infcx, mir, upvars, mir_def_id, outlived_region, &mut 1);
         (category, from_closure, span, outlived_fr_name)
     }
 
diff --git a/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/region_name.rs b/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/region_name.rs
index 362214d3257..680e7ce576a 100644
--- a/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/region_name.rs
+++ b/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/region_name.rs
@@ -2,6 +2,7 @@ use std::fmt::{self, Display};
 use crate::borrow_check::nll::region_infer::RegionInferenceContext;
 use crate::borrow_check::nll::universal_regions::DefiningTy;
 use crate::borrow_check::nll::ToRegionVid;
+use crate::borrow_check::Upvar;
 use rustc::hir;
 use rustc::hir::def_id::DefId;
 use rustc::infer::InferCtxt;
@@ -144,6 +145,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         &self,
         infcx: &InferCtxt<'_, '_, 'tcx>,
         mir: &Mir<'tcx>,
+        upvars: &[Upvar],
         mir_def_id: DefId,
         fr: RegionVid,
         counter: &mut usize,
@@ -160,7 +162,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
             })
             .or_else(|| {
                 self.give_name_if_anonymous_region_appears_in_upvars(
-                    infcx.tcx, mir, fr, counter,
+                    infcx.tcx, upvars, fr, counter,
                 )
             })
             .or_else(|| {
@@ -639,13 +641,13 @@ impl<'tcx> RegionInferenceContext<'tcx> {
     fn give_name_if_anonymous_region_appears_in_upvars(
         &self,
         tcx: TyCtxt<'_, '_, 'tcx>,
-        mir: &Mir<'tcx>,
+        upvars: &[Upvar],
         fr: RegionVid,
         counter: &mut usize,
     ) -> Option<RegionName> {
         let upvar_index = self.get_upvar_index_for_region(tcx, fr)?;
         let (upvar_name, upvar_span) =
-            self.get_upvar_name_and_span_for_region(tcx, mir, upvar_index);
+            self.get_upvar_name_and_span_for_region(tcx, upvars, upvar_index);
         let region_name = self.synthesize_region_name(counter);
 
         Some(RegionName {
diff --git a/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/var_name.rs b/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/var_name.rs
index f6bbaf2db03..d8f34233839 100644
--- a/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/var_name.rs
+++ b/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/var_name.rs
@@ -1,5 +1,6 @@
 use crate::borrow_check::nll::region_infer::RegionInferenceContext;
 use crate::borrow_check::nll::ToRegionVid;
+use crate::borrow_check::Upvar;
 use rustc::mir::{Local, Mir};
 use rustc::ty::{RegionVid, TyCtxt};
 use rustc_data_structures::indexed_vec::Idx;
@@ -11,6 +12,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         &self,
         tcx: TyCtxt<'_, '_, 'tcx>,
         mir: &Mir<'tcx>,
+        upvars: &[Upvar],
         fr: RegionVid,
     ) -> Option<(Option<Symbol>, Span)> {
         debug!("get_var_name_and_span_for_region(fr={:?})", fr);
@@ -19,7 +21,8 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         debug!("get_var_name_and_span_for_region: attempting upvar");
         self.get_upvar_index_for_region(tcx, fr)
             .map(|index| {
-                let (name, span) = self.get_upvar_name_and_span_for_region(tcx, mir, index);
+                let (name, span) =
+                    self.get_upvar_name_and_span_for_region(tcx, upvars, index);
                 (Some(name), span)
             })
             .or_else(|| {
@@ -67,10 +70,10 @@ impl<'tcx> RegionInferenceContext<'tcx> {
     crate fn get_upvar_name_and_span_for_region(
         &self,
         tcx: TyCtxt<'_, '_, 'tcx>,
-        mir: &Mir<'tcx>,
+        upvars: &[Upvar],
         upvar_index: usize,
     ) -> (Symbol, Span) {
-        let upvar_hir_id = mir.upvar_decls[upvar_index].var_hir_id.assert_crate_local();
+        let upvar_hir_id = upvars[upvar_index].var_hir_id;
         debug!("get_upvar_name_and_span_for_region: upvar_hir_id={:?}", upvar_hir_id);
 
         let upvar_name = tcx.hir().name_by_hir_id(upvar_hir_id);
diff --git a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs
index ac10683598a..9dd18ab76a5 100644
--- a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs
+++ b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs
@@ -4,6 +4,7 @@ use crate::borrow_check::nll::constraints::{ConstraintSccIndex, ConstraintSet, O
 use crate::borrow_check::nll::region_infer::values::{
     PlaceholderIndices, RegionElement, ToElementIndex
 };
+use crate::borrow_check::Upvar;
 use crate::borrow_check::nll::type_check::free_region_relations::UniversalRegionRelations;
 use crate::borrow_check::nll::type_check::Locations;
 use rustc::hir::def_id::DefId;
@@ -400,6 +401,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         &mut self,
         infcx: &InferCtxt<'_, 'gcx, 'tcx>,
         mir: &Mir<'tcx>,
+        upvars: &[Upvar],
         mir_def_id: DefId,
         errors_buffer: &mut Vec<Diagnostic>,
     ) -> Option<ClosureRegionRequirements<'gcx>> {
@@ -407,7 +409,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
             infcx.tcx.sess.time_extended(),
             Some(infcx.tcx.sess),
             &format!("solve_nll_region_constraints({:?})", mir_def_id),
-            || self.solve_inner(infcx, mir, mir_def_id, errors_buffer),
+            || self.solve_inner(infcx, mir, upvars, mir_def_id, errors_buffer),
         )
     }
 
@@ -415,6 +417,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         &mut self,
         infcx: &InferCtxt<'_, 'gcx, 'tcx>,
         mir: &Mir<'tcx>,
+        upvars: &[Upvar],
         mir_def_id: DefId,
         errors_buffer: &mut Vec<Diagnostic>,
     ) -> Option<ClosureRegionRequirements<'gcx>> {
@@ -442,6 +445,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         self.check_universal_regions(
             infcx,
             mir,
+            upvars,
             mir_def_id,
             outlives_requirements.as_mut(),
             errors_buffer,
@@ -1102,6 +1106,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         &self,
         infcx: &InferCtxt<'_, 'gcx, 'tcx>,
         mir: &Mir<'tcx>,
+        upvars: &[Upvar],
         mir_def_id: DefId,
         mut propagated_outlives_requirements: Option<&mut Vec<ClosureOutlivesRequirement<'gcx>>>,
         errors_buffer: &mut Vec<Diagnostic>,
@@ -1115,6 +1120,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
                     self.check_universal_region(
                         infcx,
                         mir,
+                        upvars,
                         mir_def_id,
                         fr,
                         &mut propagated_outlives_requirements,
@@ -1145,6 +1151,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         &self,
         infcx: &InferCtxt<'_, 'gcx, 'tcx>,
         mir: &Mir<'tcx>,
+        upvars: &[Upvar],
         mir_def_id: DefId,
         longer_fr: RegionVid,
         propagated_outlives_requirements: &mut Option<&mut Vec<ClosureOutlivesRequirement<'gcx>>>,
@@ -1177,6 +1184,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
                 representative,
                 infcx,
                 mir,
+                upvars,
                 mir_def_id,
                 propagated_outlives_requirements,
                 errors_buffer,
@@ -1192,6 +1200,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
                 shorter_fr,
                 infcx,
                 mir,
+                upvars,
                 mir_def_id,
                 propagated_outlives_requirements,
                 errors_buffer,
@@ -1208,6 +1217,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         shorter_fr: RegionVid,
         infcx: &InferCtxt<'_, 'gcx, 'tcx>,
         mir: &Mir<'tcx>,
+        upvars: &[Upvar],
         mir_def_id: DefId,
         propagated_outlives_requirements: &mut Option<&mut Vec<ClosureOutlivesRequirement<'gcx>>>,
         errors_buffer: &mut Vec<Diagnostic>,
@@ -1265,7 +1275,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         //
         // Note: in this case, we use the unapproximated regions to report the
         // error. This gives better error messages in some cases.
-        self.report_error(mir, infcx, mir_def_id, longer_fr, shorter_fr, errors_buffer);
+        self.report_error(mir, upvars, infcx, mir_def_id, longer_fr, shorter_fr, errors_buffer);
         Some(ErrorReported)
     }
 
diff --git a/src/librustc_mir/borrow_check/nll/universal_regions.rs b/src/librustc_mir/borrow_check/nll/universal_regions.rs
index ae8dfa8144f..d3cef46b402 100644
--- a/src/librustc_mir/borrow_check/nll/universal_regions.rs
+++ b/src/librustc_mir/borrow_check/nll/universal_regions.rs
@@ -105,7 +105,7 @@ impl<'tcx> DefiningTy<'tcx> {
     /// Returns a list of all the upvar types for this MIR. If this is
     /// not a closure or generator, there are no upvars, and hence it
     /// will be an empty list. The order of types in this list will
-    /// match up with the `upvar_decls` field of `Mir`.
+    /// match up with the upvar order in the HIR, typesystem, and MIR.
     pub fn upvar_tys(self, tcx: TyCtxt<'_, '_, 'tcx>) -> impl Iterator<Item = Ty<'tcx>> + 'tcx {
         match self {
             DefiningTy::Closure(def_id, substs) => Either::Left(substs.upvar_tys(def_id, tcx)),