about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/librustc/mir/tcx.rs33
-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.rs126
-rw-r--r--src/librustc_mir/borrow_check/nll/region_infer/error_reporting/region_name.rs69
-rw-r--r--src/librustc_mir/borrow_check/nll/region_infer/error_reporting/var_name.rs145
-rw-r--r--src/test/ui/borrowck/issue-45983.nll.stderr8
-rw-r--r--src/test/ui/borrowck/issue-7573.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
-rw-r--r--src/test/ui/in-band-lifetimes/impl/dyn-trait.nll.stderr6
-rw-r--r--src/test/ui/issue-16683.nll.stderr6
-rw-r--r--src/test/ui/issue-17758.nll.stderr6
-rw-r--r--src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.stderr8
-rw-r--r--src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.stderr6
-rw-r--r--src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.stderr6
-rw-r--r--src/test/ui/nll/issue-50716.stderr7
19 files changed, 367 insertions, 157 deletions
diff --git a/src/librustc/mir/tcx.rs b/src/librustc/mir/tcx.rs
index 6876b1490f3..0359eb9b95d 100644
--- a/src/librustc/mir/tcx.rs
+++ b/src/librustc/mir/tcx.rs
@@ -119,6 +119,39 @@ 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 = if let Place::Projection(ref proj) = self {
+            if let ProjectionElem::Deref = proj.elem {
+                &proj.base
+            } else {
+                self
+            }
+        } else {
+            self
+        };
+
+        match place {
+            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
+                    }
+                },
+                _ => 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..5dca01f8842 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 = place.is_upvar_field_projection(
+                            self.mir, &self.tcx);
+                        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);
+                        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..62bf2b0abe4 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) {
                                         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) {
                     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 place.is_upvar_field_projection(
+                                            self.mir, &self.tcx)
                                         {
                                             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);
+                        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 c1b73fac893..c89dc889b5e 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,7 +31,9 @@ mod region_name;
 enum ConstraintCategory {
     Cast,
     Assignment,
+    AssignmentToUpvar,
     Return,
+    CallArgumentToUpvar,
     CallArgument,
     Other,
     Boring,
@@ -39,10 +42,12 @@ 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"),
+            ConstraintCategory::CallArgument |
+            ConstraintCategory::CallArgumentToUpvar => write!(f, "argument"),
             _ => write!(f, "free region"),
         }
     }
@@ -130,8 +135,10 @@ 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);
         let span = constraint.locations.span(mir);
         let location = constraint.locations.from_location().unwrap_or(Location::START);
 
@@ -140,8 +147,10 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         }
 
         let data = &mir[location.block];
+        debug!("classify_constraint: location={:?} data={:?}", location, data);
         let category = if location.statement_index == data.statements.len() {
             if let Some(ref terminator) = data.terminator {
+                debug!("classify_constraint: terminator.kind={:?}", terminator.kind);
                 match terminator.kind {
                     TerminatorKind::DropAndReplace { .. } => ConstraintCategory::Assignment,
                     TerminatorKind::Call { .. } => ConstraintCategory::CallArgument,
@@ -152,14 +161,17 @@ impl<'tcx> RegionInferenceContext<'tcx> {
             }
         } else {
             let statement = &data.statements[location.statement_index];
+            debug!("classify_constraint: statement.kind={:?}", statement.kind);
             match statement.kind {
                 StatementKind::Assign(ref place, ref rvalue) => {
+                    debug!("classify_constraint: place={:?} rvalue={:?}", place, rvalue);
                     if *place == Place::Local(mir::RETURN_PLACE) {
                         ConstraintCategory::Return
                     } else {
                         match rvalue {
                             Rvalue::Cast(..) => ConstraintCategory::Cast,
-                            Rvalue::Use(..) => ConstraintCategory::Assignment,
+                            Rvalue::Use(..) |
+                            Rvalue::Aggregate(..) => ConstraintCategory::Assignment,
                             _ => ConstraintCategory::Other,
                         }
                     }
@@ -208,7 +220,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);
 
@@ -218,30 +230,100 @@ impl<'tcx> RegionInferenceContext<'tcx> {
 
         // Get a span
         let (category, span) = categorized_path.first().unwrap();
+
+        let category = match (
+            category,
+            self.universal_regions.is_local_free_region(fr),
+            self.universal_regions.is_local_free_region(outlived_fr),
+        ) {
+            (ConstraintCategory::Assignment, true, false) =>
+                &ConstraintCategory::AssignmentToUpvar,
+            (ConstraintCategory::CallArgument, true, false) =>
+                &ConstraintCategory::CallArgumentToUpvar,
+            (category, _, _) => category,
+        };
+
+        debug!("report_error: category={:?}", category);
+        match category {
+            ConstraintCategory::AssignmentToUpvar |
+            ConstraintCategory::CallArgumentToUpvar =>
+                self.report_closure_error(mir, infcx, mir_def_id, fr, outlived_fr, category, 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>,
+        mir_def_id: DefId,
+        fr: RegionVid,
+        outlived_fr: RegionVid,
+        category: &ConstraintCategory,
+        span: &Span,
+    ) {
+        let fr_name_and_span  = self.get_var_name_and_span_for_region(
+            infcx.tcx, mir, fr);
+        let outlived_fr_name_and_span = self.get_var_name_and_span_for_region(
+            infcx.tcx, mir,outlived_fr);
+
+        if fr_name_and_span.is_none() && outlived_fr_name_and_span.is_none() {
+            return self.report_general_error(mir, infcx, mir_def_id, fr, outlived_fr, category,
+                                             span);
+        }
+
+        let diag = &mut infcx.tcx.sess.struct_span_err(
+            *span, &format!("borrowed data escapes outside of closure"),
+        );
+
+        if let Some((outlived_fr_name, outlived_fr_span)) = outlived_fr_name_and_span {
+            if let Some(name) = outlived_fr_name {
+                diag.span_label(
+                    outlived_fr_span,
+                    format!("`{}` is declared here, outside of the closure body", name),
+                );
+            }
+        }
+
+        if let Some((fr_name, fr_span)) = fr_name_and_span {
+            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
+            *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..c0eca026331 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,42 +423,14 @@ 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,
-            ),
+            format!("lifetime `{}` appears in the type of `{}`", region_name, upvar_name),
         );
 
         Some(region_name)
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..f1c3a7489ee
--- /dev/null
+++ b/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/var_name.rs
@@ -0,0 +1,145 @@
+// 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<(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| {
+                let (name, span) = self.get_upvar_name_and_span_for_region(tcx, mir, index);
+                (Some(name), span)
+            })
+            .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))
+            })
+    }
+
+    /// Search the upvars (if any) to find one that references fr. Return its index.
+    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)
+    }
+
+    /// Given the index of an upvar, finds its name and the span from where it was
+    /// declared.
+    crate fn get_upvar_name_and_span_for_region(
+        &self,
+        tcx: TyCtxt<'_, '_, 'tcx>,
+        mir: &Mir<'tcx>,
+        upvar_index: usize,
+    ) -> (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);
+
+        (upvar_name, upvar_span)
+    }
+
+    /// Search the argument types for one that references fr (which should be a free region).
+    /// Returns Some(_) with the index of the input if one is found.
+    ///
+    /// NB: In the case of a closure, the index is indexing into the signature as seen by the
+    /// user - in particular, index 0 is not the implicit self parameter.
+    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)
+    }
+
+    /// Given the index of an argument, finds its name (if any) and the span from where it was
+    /// declared.
+    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 5a1f1986fcf..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));
-   |               -  ^^^^^^^^^^^ free region 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/issue-7573.nll.stderr b/src/test/ui/borrowck/issue-7573.nll.stderr
index 5904e987536..b0fbcd3ad9f 100644
--- a/src/test/ui/borrowck/issue-7573.nll.stderr
+++ b/src/test/ui/borrowck/issue-7573.nll.stderr
@@ -4,17 +4,17 @@ warning: not reporting region error due to nll
 LL |     let mut lines_to_use: Vec<&CrateId> = Vec::new();
    |                               ^
 
-error: unsatisfied lifetime constraints
+error: borrowed data escapes outside of closure
   --> $DIR/issue-7573.rs:32:9
    |
 LL |     let mut lines_to_use: Vec<&CrateId> = Vec::new();
-   |         ---------------- lifetime `'2` appears in the type of `lines_to_use`
+   |         ---------------- `lines_to_use` is declared here, outside of the closure body
 LL |         //~^ NOTE cannot infer an appropriate lifetime
 LL |     let push_id = |installed_id: &CrateId| {
-   |                                  - let's call the lifetime of this reference `'1`
+   |                    ------------ `installed_id` is a reference that is only valid in the closure body
 ...
 LL |         lines_to_use.push(installed_id);
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ argument requires that `'1` must outlive `'2`
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `installed_id` escapes the closure body here
 
 error: aborting due to previous error
 
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 4f7843b7248..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));
-   |               -  ^^^^^^^^^^^ free region 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 9b107ae08b4..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));
-   |               -  ^^^^^^^^^^^ free region 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 8095330154d..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));
-   |                    -  ^^^^^^^^^^^ free region 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 c8c8ef8215a..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
-   |         ^^^^^^^^^^^ free region 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
-   |         ^^^^^^^^^^^ free region 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);
-   |         ^^^^^^^^^^^ free region requires that `'1` must outlive `'2`
+   |         ^^^^^^^^^^^ `x` escapes the closure body here
 
 error: aborting due to 3 previous errors
 
diff --git a/src/test/ui/in-band-lifetimes/impl/dyn-trait.nll.stderr b/src/test/ui/in-band-lifetimes/impl/dyn-trait.nll.stderr
index 19e10ba6da8..e26b1956d5e 100644
--- a/src/test/ui/in-band-lifetimes/impl/dyn-trait.nll.stderr
+++ b/src/test/ui/in-band-lifetimes/impl/dyn-trait.nll.stderr
@@ -4,11 +4,13 @@ warning: not reporting region error due to nll
 LL |     static_val(x); //~ ERROR cannot infer
    |                ^
 
-error: unsatisfied lifetime constraints
+error: borrowed data escapes outside of closure
   --> $DIR/dyn-trait.rs:32:5
    |
+LL | fn with_dyn_debug_static<'a>(x: Box<dyn Debug + 'a>) {
+   |                              - `x` is a reference that is only valid in the closure body
 LL |     static_val(x); //~ ERROR cannot infer
-   |     ^^^^^^^^^^^^^ argument requires that `'a` must outlive `'static`
+   |     ^^^^^^^^^^^^^ `x` escapes the closure body here
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/issue-16683.nll.stderr b/src/test/ui/issue-16683.nll.stderr
index d789f580b27..f9dda27da09 100644
--- a/src/test/ui/issue-16683.nll.stderr
+++ b/src/test/ui/issue-16683.nll.stderr
@@ -10,13 +10,13 @@ warning: not reporting region error due to nll
 LL |         self.a(); //~ ERROR cannot infer
    |              ^
 
-error: unsatisfied lifetime constraints
+error: borrowed data escapes outside of closure
   --> $DIR/issue-16683.rs:14:9
    |
 LL |     fn b(&self) {
-   |          - let's call the lifetime of this reference `'1`
+   |          ----- `self` is a reference that is only valid in the closure body
 LL |         self.a(); //~ ERROR cannot infer
-   |         ^^^^^^^^ argument requires that `'1` must outlive `'a`
+   |         ^^^^^^^^ `self` escapes the closure body here
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/issue-17758.nll.stderr b/src/test/ui/issue-17758.nll.stderr
index 124fc6f0b39..5775135aefc 100644
--- a/src/test/ui/issue-17758.nll.stderr
+++ b/src/test/ui/issue-17758.nll.stderr
@@ -10,13 +10,13 @@ warning: not reporting region error due to nll
 LL |         self.foo();
    |              ^^^
 
-error: unsatisfied lifetime constraints
+error: borrowed data escapes outside of closure
   --> $DIR/issue-17758.rs:17:9
    |
 LL |     fn bar(&self) {
-   |            - let's call the lifetime of this reference `'1`
+   |            ----- `self` is a reference that is only valid in the closure body
 LL |         self.foo();
-   |         ^^^^^^^^^^ argument requires that `'1` must outlive `'a`
+   |         ^^^^^^^^^^ `self` escapes the closure body here
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.stderr b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.stderr
index 8fd5e898c8d..d51ba8201aa 100644
--- a/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.stderr
+++ b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.stderr
@@ -4,16 +4,16 @@ warning: not reporting region error due to nll
 LL |     foo(cell, |cell_a, cell_x| {
    |     ^^^
 
-error: unsatisfied lifetime constraints
+error: borrowed data escapes outside of closure
   --> $DIR/propagate-approximated-shorter-to-static-comparing-against-free.rs:33:20
    |
 LL |     foo(cell, |cell_a, cell_x| {
-   |                ------  ------ lifetime `'1` appears in this argument
+   |                ------  ------ `cell_x` is a reference that is only valid in the closure body
    |                |
-   |                lifetime `'2` appears in this argument
+   |                `cell_a` is declared here, outside of the closure body
 LL |         //~^ WARNING not reporting region error due to nll
 LL |         cell_a.set(cell_x.get()); // forces 'x: 'a, error in closure
-   |                    ^^^^^^^^^^^^ argument requires that `'1` must outlive `'2`
+   |                    ^^^^^^^^^^^^ `cell_x` escapes the closure body here
 
 note: No external requirements
   --> $DIR/propagate-approximated-shorter-to-static-comparing-against-free.rs:31:15
diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.stderr b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.stderr
index f65e7161ca8..3177cd7c28f 100644
--- a/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.stderr
+++ b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.stderr
@@ -23,16 +23,18 @@ LL | |     });
    = note: number of external vids: 2
    = note: where '_#1r: '_#0r
 
-error: unsatisfied lifetime constraints
+error: borrowed data escapes outside of closure
   --> $DIR/propagate-approximated-shorter-to-static-no-bound.rs:45:5
    |
+LL |   fn supply<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) {
+   |                     ------ `cell_a` is a reference that is only valid in the closure body
 LL | /     establish_relationships(&cell_a, &cell_b, |_outlives, x, y| {
 LL | |         //~^ ERROR
 LL | |
 LL | |         // Only works if 'x: 'y:
 LL | |         demand_y(x, y, x.get()) //~ WARNING not reporting region error due to nll
 LL | |     });
-   | |______^ argument requires that `'a` must outlive `'static`
+   | |______^ `cell_a` escapes the closure body here
 
 note: No external requirements
   --> $DIR/propagate-approximated-shorter-to-static-no-bound.rs:44:1
diff --git a/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.stderr b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.stderr
index f1b2c9f198d..089c88abcdd 100644
--- a/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.stderr
+++ b/src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.stderr
@@ -23,16 +23,18 @@ LL | |     });
    = note: number of external vids: 3
    = note: where '_#1r: '_#0r
 
-error: unsatisfied lifetime constraints
+error: borrowed data escapes outside of closure
   --> $DIR/propagate-approximated-shorter-to-static-wrong-bound.rs:48:5
    |
+LL |   fn supply<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) {
+   |                     ------ `cell_a` is a reference that is only valid in the closure body
 LL | /     establish_relationships(&cell_a, &cell_b, |_outlives1, _outlives2, x, y| {
 LL | |         //~^ ERROR
 LL | |         // Only works if 'x: 'y:
 LL | |         demand_y(x, y, x.get())
 LL | |         //~^ WARNING not reporting region error due to nll
 LL | |     });
-   | |______^ argument requires that `'a` must outlive `'static`
+   | |______^ `cell_a` escapes the closure body here
 
 note: No external requirements
   --> $DIR/propagate-approximated-shorter-to-static-wrong-bound.rs:47:1
diff --git a/src/test/ui/nll/issue-50716.stderr b/src/test/ui/nll/issue-50716.stderr
index 48862166f89..8acf2ef51ec 100644
--- a/src/test/ui/nll/issue-50716.stderr
+++ b/src/test/ui/nll/issue-50716.stderr
@@ -1,8 +1,11 @@
-error: unsatisfied lifetime constraints
+error: borrowed data escapes outside of closure
   --> $DIR/issue-50716.rs:25:14
    |
+LL | fn foo<'a, T: 'static>(s: Box<<&'a T as A>::X>)
+   |                        - `s` is a reference that is only valid in the closure body
+...
 LL |     let _x = *s; //~ ERROR
-   |              ^^ assignment requires that `'a` must outlive `'static`
+   |              ^^ `s` escapes the closure body here
 
 error: aborting due to previous error