about summary refs log tree commit diff
diff options
context:
space:
mode:
authorDavid Wood <david@davidtw.co>2018-07-20 17:30:31 +0100
committerDavid Wood <david@davidtw.co>2018-07-22 12:49:35 +0100
commit571eec627c63cea40ea9fba16b86cfafb1446c34 (patch)
treed2c72ef7ef4e6357c3380cf58b1f026d9ddb39ab
parent24c5751197f4971c7f7e387c035cac3565f2f629 (diff)
downloadrust-571eec627c63cea40ea9fba16b86cfafb1446c34.tar.gz
rust-571eec627c63cea40ea9fba16b86cfafb1446c34.zip
Improved closure errors.
-rw-r--r--src/librustc/mir/tcx.rs31
-rw-r--r--src/librustc_mir/borrow_check/error_reporting.rs8
-rw-r--r--src/librustc_mir/borrow_check/mod.rs34
-rw-r--r--src/librustc_mir/borrow_check/nll/region_infer/error_reporting/mod.rs98
-rw-r--r--src/librustc_mir/borrow_check/nll/region_infer/error_reporting/region_name.rs66
-rw-r--r--src/librustc_mir/borrow_check/nll/region_infer/error_reporting/var_name.rs137
-rw-r--r--src/test/ui/borrowck/issue-45983.nll.stderr8
-rw-r--r--src/test/ui/borrowck/regions-escape-bound-fn-2.nll.stderr8
-rw-r--r--src/test/ui/borrowck/regions-escape-bound-fn.nll.stderr8
-rw-r--r--src/test/ui/borrowck/regions-escape-unboxed-closure.nll.stderr8
-rw-r--r--src/test/ui/closure-expected-type/expect-region-supply-region.nll.stderr24
11 files changed, 299 insertions, 131 deletions
diff --git a/src/librustc/mir/tcx.rs b/src/librustc/mir/tcx.rs
index 6876b1490f3..c46f561e7b3 100644
--- a/src/librustc/mir/tcx.rs
+++ b/src/librustc/mir/tcx.rs
@@ -119,6 +119,37 @@ 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>,
+                                                recurse: bool) -> Option<Field> {
+        match *self {
+            Place::Projection(ref proj) => match proj.elem {
+                ProjectionElem::Field(field, _ty) => {
+                    let base_ty = proj.base.ty(mir, *tcx).to_ty(*tcx);
+
+                    if  base_ty.is_closure() || base_ty.is_generator() {
+                        Some(field)
+                    } else {
+                        None
+                    }
+                },
+                ProjectionElem::Deref => {
+                    if recurse {
+                        proj.base.is_upvar_field_projection(mir, tcx, recurse)
+                    } 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 3dca7768d70..9f5f1389c66 100644
--- a/src/librustc_mir/borrow_check/error_reporting.rs
+++ b/src/librustc_mir/borrow_check/error_reporting.rs
@@ -726,7 +726,9 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
             Place::Projection(ref proj) => {
                 match proj.elem {
                     ProjectionElem::Deref => {
-                        if let Some(field) = self.is_upvar_field_projection(&proj.base) {
+                        let upvar_field_projection = proj.base.is_upvar_field_projection(
+                            self.mir, &self.tcx, false);
+                        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 {
@@ -785,7 +787,9 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
                     ProjectionElem::Field(field, _ty) => {
                         autoderef = true;
 
-                        if let Some(field) = self.is_upvar_field_projection(place) {
+                        let upvar_field_projection = place.is_upvar_field_projection(
+                            self.mir, &self.tcx, false);
+                        if let Some(field) = upvar_field_projection {
                             let var_index = field.index();
                             let name = self.mir.upvar_decls[var_index].debug_name.to_string();
                             buf.push_str(&name);
diff --git a/src/librustc_mir/borrow_check/mod.rs b/src/librustc_mir/borrow_check/mod.rs
index 2e0ab522e3a..f37052470c8 100644
--- a/src/librustc_mir/borrow_check/mod.rs
+++ b/src/librustc_mir/borrow_check/mod.rs
@@ -1214,7 +1214,8 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
                                 }
                                 Operand::Move(ref place @ Place::Projection(_))
                                 | Operand::Copy(ref place @ Place::Projection(_)) => {
-                                    if let Some(field) = self.is_upvar_field_projection(place) {
+                                    if let Some(field) = place.is_upvar_field_projection(
+                                            self.mir, &self.tcx, false) {
                                         self.used_mut_upvars.push(field);
                                     }
                                 }
@@ -1803,7 +1804,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
                 place: place @ Place::Projection(_),
                 is_local_mutation_allowed: _,
             } => {
-                if let Some(field) = self.is_upvar_field_projection(&place) {
+                if let Some(field) = place.is_upvar_field_projection(self.mir, &self.tcx, false) {
                     self.used_mut_upvars.push(field);
                 }
             }
@@ -1866,7 +1867,8 @@ 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 self.is_upvar_field_projection(&proj.base)
+                                        let mode = match proj.base.is_upvar_field_projection(
+                                            self.mir, &self.tcx, false)
                                         {
                                             Some(field)
                                                 if {
@@ -1911,7 +1913,9 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
                     | ProjectionElem::ConstantIndex { .. }
                     | ProjectionElem::Subslice { .. }
                     | ProjectionElem::Downcast(..) => {
-                        if let Some(field) = self.is_upvar_field_projection(place) {
+                        let upvar_field_projection = place.is_upvar_field_projection(
+                            self.mir, &self.tcx, false);
+                        if let Some(field) = upvar_field_projection {
                             let decl = &self.mir.upvar_decls[field.index()];
                             debug!(
                                 "decl.mutability={:?} local_mutation_is_allowed={:?} place={:?}",
@@ -1965,28 +1969,6 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
             }
         }
     }
-
-    /// 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.
-    fn is_upvar_field_projection(&self, place: &Place<'tcx>) -> Option<Field> {
-        match *place {
-            Place::Projection(ref proj) => match proj.elem {
-                ProjectionElem::Field(field, _ty) => {
-                    let base_ty = proj.base.ty(self.mir, self.tcx).to_ty(self.tcx);
-
-                    if base_ty.is_closure() || base_ty.is_generator() {
-                        Some(field)
-                    } else {
-                        None
-                    }
-                }
-                _ => None,
-            },
-            _ => None,
-        }
-    }
 }
 
 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
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 9b478cd85bb..93e8f3f8657 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
@@ -22,6 +22,7 @@ use std::fmt;
 use syntax_pos::Span;
 
 mod region_name;
+mod var_name;
 
 /// Constraints that are considered interesting can be categorized to
 /// determine why they are interesting. Order of variants indicates
@@ -30,6 +31,7 @@ mod region_name;
 enum ConstraintCategory {
     Cast,
     Assignment,
+    AssignmentToUpvar,
     Return,
     CallArgument,
     Other,
@@ -39,7 +41,8 @@ enum ConstraintCategory {
 impl fmt::Display for ConstraintCategory {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         match self {
-            ConstraintCategory::Assignment => write!(f, "assignment"),
+            ConstraintCategory::Assignment |
+            ConstraintCategory::AssignmentToUpvar => write!(f, "assignment"),
             ConstraintCategory::Return => write!(f, "return"),
             ConstraintCategory::Cast => write!(f, "cast"),
             ConstraintCategory::CallArgument => write!(f, "argument"),
@@ -130,6 +133,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         &self,
         index: ConstraintIndex,
         mir: &Mir<'tcx>,
+        infcx: &InferCtxt<'_, '_, 'tcx>,
     ) -> (ConstraintCategory, Span) {
         let constraint = self.constraints[index];
         debug!("classify_constraint: constraint={:?}", constraint);
@@ -159,7 +163,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
             match statement.kind {
                 StatementKind::Assign(ref place, ref rvalue) => {
                     debug!("classify_constraint: place={:?} rvalue={:?}", place, rvalue);
-                    if *place == Place::Local(mir::RETURN_PLACE) {
+                    let initial_category = if *place == Place::Local(mir::RETURN_PLACE) {
                         ConstraintCategory::Return
                     } else {
                         match rvalue {
@@ -168,6 +172,13 @@ impl<'tcx> RegionInferenceContext<'tcx> {
                             Rvalue::Aggregate(..) => ConstraintCategory::Assignment,
                             _ => ConstraintCategory::Other,
                         }
+                    };
+
+                    if initial_category == ConstraintCategory::Assignment
+                            && place.is_upvar_field_projection(mir, &infcx.tcx, true).is_some() {
+                        ConstraintCategory::AssignmentToUpvar
+                    } else {
+                        initial_category
                     }
                 }
                 _ => ConstraintCategory::Other,
@@ -214,7 +225,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
 
         // Classify each of the constraints along the path.
         let mut categorized_path: Vec<(ConstraintCategory, Span)> = path.iter()
-            .map(|&index| self.classify_constraint(index, mir))
+            .map(|&index| self.classify_constraint(index, mir, infcx))
             .collect();
         debug!("report_error: categorized_path={:?}", categorized_path);
 
@@ -224,30 +235,75 @@ impl<'tcx> RegionInferenceContext<'tcx> {
 
         // Get a span
         let (category, span) = categorized_path.first().unwrap();
+
+        match category {
+            ConstraintCategory::AssignmentToUpvar =>
+                self.report_closure_error(mir, infcx, fr, outlived_fr, span),
+            _ =>
+                self.report_general_error(mir, infcx, mir_def_id, fr, outlived_fr, category, span),
+        }
+    }
+
+    fn report_closure_error(
+        &self,
+        mir: &Mir<'tcx>,
+        infcx: &InferCtxt<'_, '_, 'tcx>,
+        fr: RegionVid,
+        outlived_fr: RegionVid,
+        span: &Span,
+    ) {
         let diag = &mut infcx.tcx.sess.struct_span_err(
-            *span,
-            &format!("unsatisfied lifetime constraints"), // FIXME
+            *span, &format!("borrowed data escapes outside of closure"),
+        );
+
+        let (outlived_fr_name, outlived_fr_span) = self.get_var_name_and_span_for_region(
+            infcx.tcx, mir, outlived_fr);
+
+        if let Some(name) = outlived_fr_name {
+            diag.span_label(
+                outlived_fr_span,
+                format!("`{}` is declared here, outside of the closure body", name),
+            );
+        }
+
+        let (fr_name, fr_span) = self.get_var_name_and_span_for_region(infcx.tcx, mir, fr);
+
+        if let Some(name) = fr_name {
+            diag.span_label(
+                fr_span,
+                format!("`{}` is a reference that is only valid in the closure body", name),
+            );
+
+            diag.span_label(*span, format!("`{}` escapes the closure body here", name));
+        }
+
+        diag.emit();
+    }
+
+    fn report_general_error(
+        &self,
+        mir: &Mir<'tcx>,
+        infcx: &InferCtxt<'_, '_, 'tcx>,
+        mir_def_id: DefId,
+        fr: RegionVid,
+        outlived_fr: RegionVid,
+        category: &ConstraintCategory,
+        span: &Span,
+    ) {
+        let diag = &mut infcx.tcx.sess.struct_span_err(
+            *span, &format!("unsatisfied lifetime constraints"), // FIXME
         );
 
-        // Figure out how we can refer
         let counter = &mut 1;
-        let fr_name = self.give_region_a_name(infcx.tcx, mir, mir_def_id, fr, counter, diag);
+        let fr_name = self.give_region_a_name(
+            infcx.tcx, mir, mir_def_id, fr, counter, diag);
         let outlived_fr_name = self.give_region_a_name(
-            infcx.tcx,
-            mir,
-            mir_def_id,
-            outlived_fr,
-            counter,
-            diag,
-        );
+            infcx.tcx, mir, mir_def_id, outlived_fr, counter, diag);
 
-        diag.span_label(
-            *span,
-            format!(
-                "{} requires that `{}` must outlive `{}`",
-                category, fr_name, outlived_fr_name,
-            ),
-        );
+        diag.span_label(*span, format!(
+            "{} requires that `{}` must outlive `{}`",
+            category, fr_name, outlived_fr_name,
+        ));
 
         diag.emit();
     }
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 fc0e64d0a8a..756afcdfae0 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
@@ -12,10 +12,9 @@ use borrow_check::nll::region_infer::RegionInferenceContext;
 use borrow_check::nll::ToRegionVid;
 use rustc::hir;
 use rustc::hir::def_id::DefId;
-use rustc::mir::{Local, Mir};
+use rustc::mir::Mir;
 use rustc::ty::subst::{Substs, UnpackedKind};
 use rustc::ty::{self, RegionVid, Ty, TyCtxt};
-use rustc_data_structures::indexed_vec::Idx;
 use rustc_errors::DiagnosticBuilder;
 use syntax::ast::Name;
 use syntax::symbol::keywords;
@@ -63,11 +62,11 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         self.give_name_from_error_region(tcx, mir_def_id, fr, counter, diag)
             .or_else(|| {
                 self.give_name_if_anonymous_region_appears_in_arguments(
-                    tcx, mir, mir_def_id, fr, counter, diag,
-                )
+                    tcx, mir, mir_def_id, fr, counter, diag)
             })
             .or_else(|| {
-                self.give_name_if_anonymous_region_appears_in_upvars(tcx, mir, fr, counter, diag)
+                self.give_name_if_anonymous_region_appears_in_upvars(
+                    tcx, mir, fr, counter, diag)
             })
             .or_else(|| {
                 self.give_name_if_anonymous_region_appears_in_output(tcx, mir, fr, counter, diag)
@@ -139,24 +138,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         diag: &mut DiagnosticBuilder<'_>,
     ) -> Option<InternedString> {
         let implicit_inputs = self.universal_regions.defining_ty.implicit_inputs();
-        let argument_index = self
-            .universal_regions
-            .unnormalized_input_tys
-            .iter()
-            .skip(implicit_inputs)
-            .position(|arg_ty| {
-                debug!(
-                    "give_name_if_anonymous_region_appears_in_arguments: arg_ty = {:?}",
-                    arg_ty
-                );
-                tcx.any_free_region_meets(arg_ty, |r| r.to_region_vid() == fr)
-            })?;
-
-        debug!(
-            "give_name_if_anonymous_region_appears_in_arguments: \
-             found {:?} in argument {} which has type {:?}",
-            fr, argument_index, self.universal_regions.unnormalized_input_tys[argument_index],
-        );
+        let argument_index = self.get_argument_index_for_region(tcx, fr)?;
 
         let arg_ty =
             self.universal_regions.unnormalized_input_tys[implicit_inputs + argument_index];
@@ -172,10 +154,11 @@ impl<'tcx> RegionInferenceContext<'tcx> {
             return Some(region_name);
         }
 
+        let (_argument_name, argument_span) = self.get_argument_name_and_span_for_region(
+            mir, argument_index);
+
         let region_name = self.synthesize_region_name(counter);
 
-        let argument_local = Local::new(argument_index + implicit_inputs + 1);
-        let argument_span = mir.local_decls[argument_local].source_info.span;
         diag.span_label(
             argument_span,
             format!("lifetime `{}` appears in this argument", region_name,),
@@ -440,41 +423,16 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         counter: &mut usize,
         diag: &mut DiagnosticBuilder<'_>,
     ) -> Option<InternedString> {
-        let upvar_index = self
-            .universal_regions
-            .defining_ty
-            .upvar_tys(tcx)
-            .position(|upvar_ty| {
-                debug!(
-                    "give_name_if_anonymous_region_appears_in_upvars: upvar_ty = {:?}",
-                    upvar_ty,
-                );
-                tcx.any_free_region_meets(&upvar_ty, |r| r.to_region_vid() == fr)
-            })?;
-
-        let upvar_ty = self
-            .universal_regions
-            .defining_ty
-            .upvar_tys(tcx)
-            .nth(upvar_index);
-
-        debug!(
-            "give_name_if_anonymous_region_appears_in_upvars: \
-             found {:?} in upvar {} which has type {:?}",
-            fr, upvar_index, upvar_ty,
-        );
-
+        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);
         let region_name = self.synthesize_region_name(counter);
 
-        let upvar_hir_id = mir.upvar_decls[upvar_index].var_hir_id.assert_crate_local();
-        let upvar_node_id = tcx.hir.hir_to_node_id(upvar_hir_id);
-        let upvar_span = tcx.hir.span(upvar_node_id);
-        let upvar_name = tcx.hir.name(upvar_node_id);
         diag.span_label(
             upvar_span,
             format!(
                 "lifetime `{}` appears in the type of `{}`",
-                region_name, upvar_name,
+                region_name, upvar_name.unwrap(),
             ),
         );
 
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
new file mode 100644
index 00000000000..a86ee5a4f91
--- /dev/null
+++ b/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/var_name.rs
@@ -0,0 +1,137 @@
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use borrow_check::nll::region_infer::RegionInferenceContext;
+use borrow_check::nll::ToRegionVid;
+use rustc::mir::{Local, Mir};
+use rustc::ty::{RegionVid, TyCtxt};
+use rustc_data_structures::indexed_vec::Idx;
+use syntax::codemap::Span;
+use syntax_pos::symbol::Symbol;
+
+impl<'tcx> RegionInferenceContext<'tcx> {
+    crate fn get_var_name_and_span_for_region(
+        &self,
+        tcx: TyCtxt<'_, '_, 'tcx>,
+        mir: &Mir<'tcx>,
+        fr: RegionVid,
+    ) -> (Option<Symbol>, Span) {
+        debug!("get_var_name_and_span_for_region(fr={:?})", fr);
+        assert!(self.universal_regions.is_universal_region(fr));
+
+        debug!("get_var_name_and_span_for_region: attempting upvar");
+        self.get_upvar_index_for_region(tcx, fr)
+            .map(|index| self.get_upvar_name_and_span_for_region(tcx, mir, index))
+            .or_else(|| {
+                debug!("get_var_name_and_span_for_region: attempting argument");
+                self.get_argument_index_for_region(tcx, fr)
+                    .map(|index| self.get_argument_name_and_span_for_region(mir, index))
+            })
+            .unwrap_or_else(|| span_bug!(mir.span, "can't find var name for free region {:?}", fr))
+    }
+
+    /// Get upvar index for a region.
+    crate fn get_upvar_index_for_region(
+        &self,
+        tcx: TyCtxt<'_, '_, 'tcx>,
+        fr: RegionVid,
+    ) -> Option<usize> {
+        let upvar_index = self
+            .universal_regions
+            .defining_ty
+            .upvar_tys(tcx)
+            .position(|upvar_ty| {
+                debug!(
+                    "get_upvar_index_for_region: upvar_ty = {:?}",
+                    upvar_ty,
+                );
+                tcx.any_free_region_meets(&upvar_ty, |r| r.to_region_vid() == fr)
+            })?;
+
+        let upvar_ty = self
+            .universal_regions
+            .defining_ty
+            .upvar_tys(tcx)
+            .nth(upvar_index);
+
+        debug!(
+            "get_upvar_index_for_region: found {:?} in upvar {} which has type {:?}",
+            fr, upvar_index, upvar_ty,
+        );
+
+        Some(upvar_index)
+    }
+
+    /// Get upvar name and span for a region.
+    crate fn get_upvar_name_and_span_for_region(
+        &self,
+        tcx: TyCtxt<'_, '_, 'tcx>,
+        mir: &Mir<'tcx>,
+        upvar_index: usize,
+    ) -> (Option<Symbol>, Span) {
+        let upvar_hir_id = mir.upvar_decls[upvar_index].var_hir_id.assert_crate_local();
+        let upvar_node_id = tcx.hir.hir_to_node_id(upvar_hir_id);
+        debug!("get_upvar_name_and_span_for_region: upvar_node_id={:?}", upvar_node_id);
+
+        let upvar_name = tcx.hir.name(upvar_node_id);
+        let upvar_span = tcx.hir.span(upvar_node_id);
+        debug!("get_upvar_name_and_span_for_region: upvar_name={:?} upvar_span={:?}",
+               upvar_name, upvar_span);
+
+        (Some(upvar_name), upvar_span)
+    }
+
+    /// Get argument index for a region.
+    crate fn get_argument_index_for_region(
+        &self,
+        tcx: TyCtxt<'_, '_, 'tcx>,
+        fr: RegionVid,
+    ) -> Option<usize> {
+        let implicit_inputs = self.universal_regions.defining_ty.implicit_inputs();
+        let argument_index = self
+            .universal_regions
+            .unnormalized_input_tys
+            .iter()
+            .skip(implicit_inputs)
+            .position(|arg_ty| {
+                debug!(
+                    "get_argument_index_for_region: arg_ty = {:?}",
+                    arg_ty
+                );
+                tcx.any_free_region_meets(arg_ty, |r| r.to_region_vid() == fr)
+            })?;
+
+        debug!(
+            "get_argument_index_for_region: found {:?} in argument {} which has type {:?}",
+            fr, argument_index, self.universal_regions.unnormalized_input_tys[argument_index],
+        );
+
+        Some(argument_index)
+    }
+
+    /// Get argument name and span for a region.
+    crate fn get_argument_name_and_span_for_region(
+        &self,
+        mir: &Mir<'tcx>,
+        argument_index: usize,
+    ) -> (Option<Symbol>, Span) {
+        let implicit_inputs = self.universal_regions.defining_ty.implicit_inputs();
+        let argument_local = Local::new(implicit_inputs + argument_index + 1);
+        debug!("get_argument_name_and_span_for_region: argument_local={:?}", argument_local);
+
+        let argument_name = mir.local_decls[argument_local].name;
+        let argument_span = mir.local_decls[argument_local].source_info.span;
+        debug!("get_argument_name_and_span_for_region: argument_name={:?} argument_span={:?}",
+               argument_name, argument_span);
+
+        (argument_name, argument_span)
+    }
+
+}
diff --git a/src/test/ui/borrowck/issue-45983.nll.stderr b/src/test/ui/borrowck/issue-45983.nll.stderr
index 4edec568737..64086cb0791 100644
--- a/src/test/ui/borrowck/issue-45983.nll.stderr
+++ b/src/test/ui/borrowck/issue-45983.nll.stderr
@@ -4,15 +4,15 @@ warning: not reporting region error due to nll
 LL |     give_any(|y| x = Some(y));
    |                           ^
 
-error: unsatisfied lifetime constraints
+error: borrowed data escapes outside of closure
   --> $DIR/issue-45983.rs:17:18
    |
 LL |     let x = None;
-   |         - lifetime `'2` appears in the type of `x`
+   |         - `x` is declared here, outside of the closure body
 LL |     give_any(|y| x = Some(y));
-   |               -  ^^^^^^^^^^^ assignment requires that `'1` must outlive `'2`
+   |               -  ^^^^^^^^^^^ `y` escapes the closure body here
    |               |
-   |               lifetime `'1` appears in this argument
+   |               `y` is a reference that is only valid in the closure body
 
 error[E0594]: cannot assign to `x`, as it is not declared as mutable
   --> $DIR/issue-45983.rs:17:18
diff --git a/src/test/ui/borrowck/regions-escape-bound-fn-2.nll.stderr b/src/test/ui/borrowck/regions-escape-bound-fn-2.nll.stderr
index a162d9ea7f1..1a18817e943 100644
--- a/src/test/ui/borrowck/regions-escape-bound-fn-2.nll.stderr
+++ b/src/test/ui/borrowck/regions-escape-bound-fn-2.nll.stderr
@@ -4,15 +4,15 @@ warning: not reporting region error due to nll
 LL |     with_int(|y| x = Some(y));
    |                           ^
 
-error: unsatisfied lifetime constraints
+error: borrowed data escapes outside of closure
   --> $DIR/regions-escape-bound-fn-2.rs:18:18
    |
 LL |     let mut x = None;
-   |         ----- lifetime `'2` appears in the type of `x`
+   |         ----- `x` is declared here, outside of the closure body
 LL |     with_int(|y| x = Some(y));
-   |               -  ^^^^^^^^^^^ assignment requires that `'1` must outlive `'2`
+   |               -  ^^^^^^^^^^^ `y` escapes the closure body here
    |               |
-   |               lifetime `'1` appears in this argument
+   |               `y` is a reference that is only valid in the closure body
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/borrowck/regions-escape-bound-fn.nll.stderr b/src/test/ui/borrowck/regions-escape-bound-fn.nll.stderr
index af5db0e3ff6..62ea9a0854b 100644
--- a/src/test/ui/borrowck/regions-escape-bound-fn.nll.stderr
+++ b/src/test/ui/borrowck/regions-escape-bound-fn.nll.stderr
@@ -4,15 +4,15 @@ warning: not reporting region error due to nll
 LL |     with_int(|y| x = Some(y));
    |                      ^^^^^^^
 
-error: unsatisfied lifetime constraints
+error: borrowed data escapes outside of closure
   --> $DIR/regions-escape-bound-fn.rs:18:18
    |
 LL |     let mut x: Option<&isize> = None;
-   |         ----- lifetime `'2` appears in the type of `x`
+   |         ----- `x` is declared here, outside of the closure body
 LL |     with_int(|y| x = Some(y));
-   |               -  ^^^^^^^^^^^ assignment requires that `'1` must outlive `'2`
+   |               -  ^^^^^^^^^^^ `y` escapes the closure body here
    |               |
-   |               lifetime `'1` appears in this argument
+   |               `y` is a reference that is only valid in the closure body
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/borrowck/regions-escape-unboxed-closure.nll.stderr b/src/test/ui/borrowck/regions-escape-unboxed-closure.nll.stderr
index df93a1204b2..44eead9fb5a 100644
--- a/src/test/ui/borrowck/regions-escape-unboxed-closure.nll.stderr
+++ b/src/test/ui/borrowck/regions-escape-unboxed-closure.nll.stderr
@@ -4,15 +4,15 @@ warning: not reporting region error due to nll
 LL |     with_int(&mut |y| x = Some(y));
    |                           ^^^^^^^
 
-error: unsatisfied lifetime constraints
+error: borrowed data escapes outside of closure
   --> $DIR/regions-escape-unboxed-closure.rs:16:23
    |
 LL |     let mut x: Option<&isize> = None;
-   |         ----- lifetime `'2` appears in the type of `x`
+   |         ----- `x` is declared here, outside of the closure body
 LL |     with_int(&mut |y| x = Some(y));
-   |                    -  ^^^^^^^^^^^ assignment requires that `'1` must outlive `'2`
+   |                    -  ^^^^^^^^^^^ `y` escapes the closure body here
    |                    |
-   |                    lifetime `'1` appears in this argument
+   |                    `y` is a reference that is only valid in the closure body
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/closure-expected-type/expect-region-supply-region.nll.stderr b/src/test/ui/closure-expected-type/expect-region-supply-region.nll.stderr
index 1559b35c446..8658c618bf2 100644
--- a/src/test/ui/closure-expected-type/expect-region-supply-region.nll.stderr
+++ b/src/test/ui/closure-expected-type/expect-region-supply-region.nll.stderr
@@ -22,37 +22,37 @@ warning: not reporting region error due to nll
 LL |         f = Some(x);
    |             ^^^^^^^
 
-error: unsatisfied lifetime constraints
+error: borrowed data escapes outside of closure
   --> $DIR/expect-region-supply-region.rs:28:9
    |
 LL |     let mut f: Option<&u32> = None;
-   |         ----- lifetime `'2` appears in the type of `f`
+   |         ----- `f` is declared here, outside of the closure body
 LL |     closure_expecting_bound(|x| {
-   |                              - lifetime `'1` appears in this argument
+   |                              - `x` is a reference that is only valid in the closure body
 LL |         f = Some(x); //~ ERROR borrowed data cannot be stored outside of its closure
-   |         ^^^^^^^^^^^ assignment requires that `'1` must outlive `'2`
+   |         ^^^^^^^^^^^ `x` escapes the closure body here
 
-error: unsatisfied lifetime constraints
+error: borrowed data escapes outside of closure
   --> $DIR/expect-region-supply-region.rs:38:9
    |
 LL |     let mut f: Option<&u32> = None;
-   |         ----- lifetime `'2` appears in the type of `f`
+   |         ----- `f` is declared here, outside of the closure body
 LL |     closure_expecting_bound(|x: &u32| {
-   |                                 - let's call the lifetime of this reference `'1`
+   |                              - `x` is a reference that is only valid in the closure body
 LL |         f = Some(x); //~ ERROR borrowed data cannot be stored outside of its closure
-   |         ^^^^^^^^^^^ assignment requires that `'1` must outlive `'2`
+   |         ^^^^^^^^^^^ `x` escapes the closure body here
 
-error: unsatisfied lifetime constraints
+error: borrowed data escapes outside of closure
   --> $DIR/expect-region-supply-region.rs:52:9
    |
 LL |     let mut f: Option<&u32> = None;
-   |         ----- lifetime `'2` appears in the type of `f`
+   |         ----- `f` is declared here, outside of the closure body
 ...
 LL |     closure_expecting_bound(|x: &'x u32| {
-   |                                 - let's call the lifetime of this reference `'1`
+   |                              - `x` is a reference that is only valid in the closure body
 ...
 LL |         f = Some(x);
-   |         ^^^^^^^^^^^ assignment requires that `'1` must outlive `'2`
+   |         ^^^^^^^^^^^ `x` escapes the closure body here
 
 error: aborting due to 3 previous errors