about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2018-09-23 15:00:53 +0000
committerbors <bors@rust-lang.org>2018-09-23 15:00:53 +0000
commitf49f6e73a8a2dd6ec9f86df9922ccfa2210d9eda (patch)
treec690cdcb18ef4d4ab0260686d22c509a1b2a7cdf /src
parentbe91c35f3466843713b9b6c5a7238ba83aef3602 (diff)
parentb342f0017931180097f17905da8640f674165255 (diff)
downloadrust-f49f6e73a8a2dd6ec9f86df9922ccfa2210d9eda.tar.gz
rust-f49f6e73a8a2dd6ec9f86df9922ccfa2210d9eda.zip
Auto merge of #54229 - davidtwco:issue-52534, r=pnkfelix
[nll] borrows that must be valid for a free lifetime should explain why

Fixes #52534.

r? @nikomatsakis
Diffstat (limited to 'src')
-rw-r--r--src/librustc/mir/mod.rs12
-rw-r--r--src/librustc/ty/sty.rs17
-rw-r--r--src/librustc/util/ppaux.rs52
-rw-r--r--src/librustc_mir/borrow_check/error_reporting.rs702
-rw-r--r--src/librustc_mir/borrow_check/mod.rs57
-rw-r--r--src/librustc_mir/borrow_check/move_errors.rs45
-rw-r--r--src/librustc_mir/borrow_check/mutability_errors.rs40
-rw-r--r--src/librustc_mir/borrow_check/nll/explain_borrow/find_use.rs2
-rw-r--r--src/librustc_mir/borrow_check/nll/explain_borrow/mod.rs212
-rw-r--r--src/librustc_mir/borrow_check/nll/region_infer/error_reporting/mod.rs15
-rw-r--r--src/librustc_mir/borrow_check/nll/region_infer/error_reporting/region_name.rs285
-rw-r--r--src/librustc_mir/borrow_check/prefixes.rs2
-rw-r--r--src/libsyntax/parse/parser.rs2
-rw-r--r--src/test/ui/issues/issue-30438-c.nll.stderr17
-rw-r--r--src/test/ui/nll/borrowed-universal-error-2.stderr17
-rw-r--r--src/test/ui/nll/issue-52534-1.rs53
-rw-r--r--src/test/ui/nll/issue-52534-1.stderr140
-rw-r--r--src/test/ui/nll/issue-52534-2.rs26
-rw-r--r--src/test/ui/nll/issue-52534-2.stderr14
-rw-r--r--src/test/ui/nll/issue-52534.rs30
-rw-r--r--src/test/ui/nll/issue-52534.stderr29
21 files changed, 1338 insertions, 431 deletions
diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs
index 57aa2186927..98d9a0a7c6f 100644
--- a/src/librustc/mir/mod.rs
+++ b/src/librustc/mir/mod.rs
@@ -1883,6 +1883,18 @@ impl<'tcx> Place<'tcx> {
     pub fn elem(self, elem: PlaceElem<'tcx>) -> Place<'tcx> {
         Place::Projection(Box::new(PlaceProjection { base: self, elem }))
     }
+
+    /// Find the innermost `Local` from this `Place`.
+    pub fn local(&self) -> Option<Local> {
+        match self {
+            Place::Local(local) |
+            Place::Projection(box Projection {
+                base: Place::Local(local),
+                elem: ProjectionElem::Deref,
+            }) => Some(*local),
+            _ => None,
+        }
+    }
 }
 
 impl<'tcx> Debug for Place<'tcx> {
diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs
index ea547c592d0..8fb5ed66a82 100644
--- a/src/librustc/ty/sty.rs
+++ b/src/librustc/ty/sty.rs
@@ -1324,6 +1324,23 @@ impl_stable_hash_for!(struct DebruijnIndex { private });
 
 /// Region utilities
 impl RegionKind {
+    /// Is this region named by the user?
+    pub fn has_name(&self) -> bool {
+        match *self {
+            RegionKind::ReEarlyBound(ebr) => ebr.has_name(),
+            RegionKind::ReLateBound(_, br) => br.is_named(),
+            RegionKind::ReFree(fr) => fr.bound_region.is_named(),
+            RegionKind::ReScope(..) => false,
+            RegionKind::ReStatic => true,
+            RegionKind::ReVar(..) => false,
+            RegionKind::ReSkolemized(_, br) => br.is_named(),
+            RegionKind::ReEmpty => false,
+            RegionKind::ReErased => false,
+            RegionKind::ReClosureBound(..) => false,
+            RegionKind::ReCanonical(..) => false,
+        }
+    }
+
     pub fn is_late_bound(&self) -> bool {
         match *self {
             ty::ReLateBound(..) => true,
diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs
index 9a1add26bb0..59dd90dbd32 100644
--- a/src/librustc/util/ppaux.rs
+++ b/src/librustc/util/ppaux.rs
@@ -34,7 +34,14 @@ use hir;
 thread_local! {
     /// Mechanism for highlighting of specific regions for display in NLL region inference errors.
     /// Contains region to highlight and counter for number to use when highlighting.
-    static HIGHLIGHT_REGION: Cell<Option<(RegionVid, usize)>> = Cell::new(None)
+    static HIGHLIGHT_REGION_FOR_REGIONVID: Cell<Option<(RegionVid, usize)>> = Cell::new(None)
+}
+
+thread_local! {
+    /// Mechanism for highlighting of specific regions for display in NLL's 'borrow does not live
+    /// long enough' errors. Contains a region to highlight and a counter to use.
+    static HIGHLIGHT_REGION_FOR_BOUND_REGION: Cell<Option<(ty::BoundRegion, usize)>> =
+        Cell::new(None)
 }
 
 macro_rules! gen_display_debug_body {
@@ -564,12 +571,34 @@ pub fn parameterized<F: fmt::Write>(f: &mut F,
     PrintContext::new().parameterized(f, substs, did, projections)
 }
 
-fn get_highlight_region() -> Option<(RegionVid, usize)> {
-    HIGHLIGHT_REGION.with(|hr| hr.get())
+fn get_highlight_region_for_regionvid() -> Option<(RegionVid, usize)> {
+    HIGHLIGHT_REGION_FOR_REGIONVID.with(|hr| hr.get())
 }
 
-pub fn with_highlight_region<R>(r: RegionVid, counter: usize, op: impl FnOnce() -> R) -> R {
-    HIGHLIGHT_REGION.with(|hr| {
+pub fn with_highlight_region_for_regionvid<R>(
+    r: RegionVid,
+    counter: usize,
+    op: impl FnOnce() -> R
+) -> R {
+    HIGHLIGHT_REGION_FOR_REGIONVID.with(|hr| {
+        assert_eq!(hr.get(), None);
+        hr.set(Some((r, counter)));
+        let r = op();
+        hr.set(None);
+        r
+    })
+}
+
+fn get_highlight_region_for_bound_region() -> Option<(ty::BoundRegion, usize)> {
+    HIGHLIGHT_REGION_FOR_BOUND_REGION.with(|hr| hr.get())
+}
+
+pub fn with_highlight_region_for_bound_region<R>(
+    r: ty::BoundRegion,
+    counter: usize,
+    op: impl Fn() -> R
+) -> R {
+    HIGHLIGHT_REGION_FOR_BOUND_REGION.with(|hr| {
         assert_eq!(hr.get(), None);
         hr.set(Some((r, counter)));
         let r = op();
@@ -726,6 +755,15 @@ define_print! {
                 return self.print_debug(f, cx);
             }
 
+            if let Some((region, counter)) = get_highlight_region_for_bound_region() {
+                if *self == region {
+                    return match *self {
+                        BrNamed(_, name) => write!(f, "{}", name),
+                        BrAnon(_) | BrFresh(_) | BrEnv => write!(f, "'{}", counter)
+                    };
+                }
+            }
+
             match *self {
                 BrNamed(_, name) => write!(f, "{}", name),
                 BrAnon(_) | BrFresh(_) | BrEnv => Ok(())
@@ -748,7 +786,7 @@ define_print! {
 define_print! {
     () ty::RegionKind, (self, f, cx) {
         display {
-            if cx.is_verbose || get_highlight_region().is_some() {
+            if cx.is_verbose || get_highlight_region_for_regionvid().is_some() {
                 return self.print_debug(f, cx);
             }
 
@@ -923,7 +961,7 @@ impl fmt::Debug for ty::FloatVid {
 
 impl fmt::Debug for ty::RegionVid {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        if let Some((region, counter)) = get_highlight_region() {
+        if let Some((region, counter)) = get_highlight_region_for_regionvid() {
             debug!("RegionVid.fmt: region={:?} self={:?} counter={:?}", region, self, counter);
             return if *self == region {
                 write!(f, "'{:?}", counter)
diff --git a/src/librustc_mir/borrow_check/error_reporting.rs b/src/librustc_mir/borrow_check/error_reporting.rs
index 82eca4a18c8..3fdb7d7f27d 100644
--- a/src/librustc_mir/borrow_check/error_reporting.rs
+++ b/src/librustc_mir/borrow_check/error_reporting.rs
@@ -10,22 +10,26 @@
 
 use borrow_check::{WriteKind, StorageDeadOrDrop};
 use borrow_check::prefixes::IsPrefixOf;
+use borrow_check::nll::explain_borrow::BorrowExplanation;
 use rustc::middle::region::ScopeTree;
-use rustc::mir::VarBindingForm;
-use rustc::mir::{BindingForm, BorrowKind, ClearCrossCrate, Field, Local};
-use rustc::mir::{FakeReadCause, LocalDecl, LocalKind, Location, Operand, Place};
-use rustc::mir::{ProjectionElem, Rvalue, Statement, StatementKind};
+use rustc::mir::{
+    self, AggregateKind, BindingForm, BorrowKind, ClearCrossCrate, FakeReadCause, Field, Local,
+    LocalDecl, LocalKind, Location, Operand, Place, ProjectionElem, Rvalue, Statement,
+    StatementKind, TerminatorKind, VarBindingForm,
+};
+use rustc::hir;
+use rustc::hir::def_id::DefId;
 use rustc::ty;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_data_structures::sync::Lrc;
 use rustc_errors::{Applicability, DiagnosticBuilder};
+use rustc::util::ppaux::with_highlight_region_for_bound_region;
 use syntax_pos::Span;
 
 use super::borrow_set::BorrowData;
 use super::{Context, MirBorrowckCtxt};
 use super::{InitializationRequiringAction, PrefixSet};
 
-use borrow_check::nll::explain_borrow::BorrowContainsPointReason;
 use dataflow::drop_flag_effects;
 use dataflow::move_paths::indexes::MoveOutIndex;
 use dataflow::move_paths::MovePathIndex;
@@ -70,7 +74,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
                 Some(name) => format!("`{}`", name),
                 None => "value".to_owned(),
             };
-            let mut err = self.tcx.cannot_act_on_uninitialized_variable(
+            let mut err = self.infcx.tcx.cannot_act_on_uninitialized_variable(
                 span,
                 desired_action.as_noun(),
                 &self
@@ -97,7 +101,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
 
             let msg = ""; //FIXME: add "partially " or "collaterally "
 
-            let mut err = self.tcx.cannot_act_on_moved_value(
+            let mut err = self.infcx.tcx.cannot_act_on_moved_value(
                 span,
                 desired_action.as_noun(),
                 msg,
@@ -149,9 +153,9 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
             if let Some(ty) = self.retrieve_type_for_place(place) {
                 let needs_note = match ty.sty {
                     ty::Closure(id, _) => {
-                        let tables = self.tcx.typeck_tables_of(id);
-                        let node_id = self.tcx.hir.as_local_node_id(id).unwrap();
-                        let hir_id = self.tcx.hir.node_to_hir_id(node_id);
+                        let tables = self.infcx.tcx.typeck_tables_of(id);
+                        let node_id = self.infcx.tcx.hir.as_local_node_id(id).unwrap();
+                        let hir_id = self.infcx.tcx.hir.node_to_hir_id(node_id);
                         if tables.closure_kind_origins().get(hir_id).is_some() {
                             false
                         } else {
@@ -198,7 +202,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
         (place, _span): (&Place<'tcx>, Span),
         borrow: &BorrowData<'tcx>,
     ) {
-        let tcx = self.tcx;
+        let tcx = self.infcx.tcx;
         let value_msg = match self.describe_place(place) {
             Some(name) => format!("`{}`", name),
             None => "value".to_owned(),
@@ -226,7 +230,8 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
 
         move_spans.var_span_label(&mut err, "move occurs due to use in closure");
 
-        self.explain_why_borrow_contains_point(context, borrow, None, &mut err);
+        self.explain_why_borrow_contains_point(context, borrow, None)
+            .emit(self.infcx.tcx, &mut err);
         err.buffer(&mut self.errors_buffer);
     }
 
@@ -236,7 +241,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
         (place, _span): (&Place<'tcx>, Span),
         borrow: &BorrowData<'tcx>,
     ) {
-        let tcx = self.tcx;
+        let tcx = self.infcx.tcx;
 
         let borrow_spans = self.retrieve_borrow_spans(borrow);
         let borrow_span = borrow_spans.args_or_use();
@@ -263,7 +268,8 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
             format!("borrow occurs due to use of `{}` in closure", desc_place)
         });
 
-        self.explain_why_borrow_contains_point(context, borrow, None, &mut err);
+        self.explain_why_borrow_contains_point(context, borrow, None)
+            .emit(self.infcx.tcx, &mut err);
         err.buffer(&mut self.errors_buffer);
     }
 
@@ -281,7 +287,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
         let span = borrow_spans.args_or_use();
 
         let desc_place = self.describe_place(place).unwrap_or("_".to_owned());
-        let tcx = self.tcx;
+        let tcx = self.infcx.tcx;
 
         // FIXME: supply non-"" `opt_via` when appropriate
         let mut err = match (
@@ -390,7 +396,8 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
             );
         }
 
-        self.explain_why_borrow_contains_point(context, issued_borrow, None, &mut err);
+        self.explain_why_borrow_contains_point(context, issued_borrow, None)
+            .emit(self.infcx.tcx, &mut err);
 
         err.buffer(&mut self.errors_buffer);
     }
@@ -417,7 +424,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
         );
 
         let drop_span = place_span.1;
-        let scope_tree = self.tcx.region_scope_tree(self.mir_def_id);
+        let scope_tree = self.infcx.tcx.region_scope_tree(self.mir_def_id);
         let root_place = self
             .prefixes(&borrow.borrowed_place, PrefixSet::All)
             .last()
@@ -445,47 +452,39 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
         self.access_place_error_reported
             .insert((root_place.clone(), borrow_span));
 
-        let borrow_reason = self.find_why_borrow_contains_point(context, borrow);
-
-        if let Some(WriteKind::StorageDeadOrDrop(StorageDeadOrDrop::Destructor)) = kind
-        {
+        if let Some(WriteKind::StorageDeadOrDrop(StorageDeadOrDrop::Destructor)) = kind {
             // If a borrow of path `B` conflicts with drop of `D` (and
             // we're not in the uninteresting case where `B` is a
             // prefix of `D`), then report this as a more interesting
             // destructor conflict.
             if !borrow.borrowed_place.is_prefix_of(place_span.0) {
-                self.report_borrow_conflicts_with_destructor(
-                    context, borrow, borrow_reason, place_span, kind);
+                self.report_borrow_conflicts_with_destructor(context, borrow, place_span, kind);
                 return;
             }
         }
 
-        let mut err = match &self.describe_place(&borrow.borrowed_place) {
-            Some(_) if self.is_place_thread_local(root_place) => {
-                self.report_thread_local_value_does_not_live_long_enough(drop_span, borrow_span)
-            }
+        let err = match &self.describe_place(&borrow.borrowed_place) {
+            Some(_) if self.is_place_thread_local(root_place) =>
+                self.report_thread_local_value_does_not_live_long_enough(drop_span, borrow_span),
             Some(name) => self.report_local_value_does_not_live_long_enough(
                 context,
                 name,
                 &scope_tree,
                 &borrow,
-                borrow_reason,
                 drop_span,
-                borrow_span,
+                borrow_spans,
                 kind.map(|k| (k, place_span.0)),
             ),
             None => self.report_temporary_value_does_not_live_long_enough(
                 context,
                 &scope_tree,
                 &borrow,
-                borrow_reason,
                 drop_span,
+                borrow_spans,
                 proper_span,
             ),
         };
 
-        borrow_spans.args_span_label(&mut err, "value captured here");
-
         err.buffer(&mut self.errors_buffer);
     }
 
@@ -495,31 +494,69 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
         name: &String,
         scope_tree: &Lrc<ScopeTree>,
         borrow: &BorrowData<'tcx>,
-        reason: BorrowContainsPointReason<'tcx>,
         drop_span: Span,
-        borrow_span: Span,
+        borrow_spans: UseSpans,
         kind_place: Option<(WriteKind, &Place<'tcx>)>,
     ) -> DiagnosticBuilder<'cx> {
         debug!(
             "report_local_value_does_not_live_long_enough(\
-             {:?}, {:?}, {:?}, {:?}, {:?}, {:?}, {:?}\
+             {:?}, {:?}, {:?}, {:?}, {:?}, {:?}\
              )",
-            context, name, scope_tree, borrow, reason, drop_span, borrow_span
+            context, name, scope_tree, borrow, drop_span, borrow_spans
         );
 
-        let mut err = self.tcx.path_does_not_live_long_enough(
+        let borrow_span = borrow_spans.var_or_use();
+        let mut err = self.infcx.tcx.path_does_not_live_long_enough(
             borrow_span,
             &format!("`{}`", name),
             Origin::Mir,
         );
 
-        err.span_label(borrow_span, "borrowed value does not live long enough");
-        err.span_label(
-            drop_span,
-            format!("`{}` dropped here while still borrowed", name),
-        );
+        let explanation = self.explain_why_borrow_contains_point(context, borrow, kind_place);
+        if let Some(annotation) = self.annotate_argument_and_return_for_borrow(borrow) {
+            let region_name = annotation.emit(&mut err);
+
+            err.span_label(
+                borrow_span,
+                format!("`{}` would have to be valid for `{}`...", name, region_name)
+            );
+
+            if let Some(fn_node_id) = self.infcx.tcx.hir.as_local_node_id(self.mir_def_id) {
+                err.span_label(
+                    drop_span,
+                    format!(
+                        "...but `{}` will be dropped here, when the function `{}` returns",
+                        name, self.infcx.tcx.hir.name(fn_node_id),
+                    )
+                );
+
+                err.note(
+                    "functions cannot return a borrow to data owned within the function's scope, \
+                     functions can only return borrows to data passed as arguments",
+                );
+                err.note(
+                    "to learn more, visit <https://doc.rust-lang.org/book/second-edition/ch04-02-\
+                     references-and-borrowing.html#dangling-references>",
+                );
+            } else {
+                err.span_label(
+                    drop_span,
+                    format!("...but `{}` dropped here while still borrowed", name)
+                );
+            }
+
+            if let BorrowExplanation::MustBeValidFor(..) = explanation { } else {
+                explanation.emit(self.infcx.tcx, &mut err);
+            }
+        } else {
+            err.span_label(borrow_span, "borrowed value does not live long enough");
+            err.span_label(drop_span, format!("`{}` dropped here while still borrowed", name));
+
+            borrow_spans.args_span_label(&mut err, "value captured here");
+
+            explanation.emit(self.infcx.tcx, &mut err);
+        }
 
-        self.report_why_borrow_contains_point(&mut err, reason, kind_place);
         err
     }
 
@@ -527,36 +564,32 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
         &mut self,
         context: Context,
         borrow: &BorrowData<'tcx>,
-        borrow_reason: BorrowContainsPointReason<'tcx>,
-        place_span: (&Place<'tcx>, Span),
+        (place, drop_span): (&Place<'tcx>, Span),
         kind: Option<WriteKind>,
     ) {
         debug!(
             "report_borrow_conflicts_with_destructor(\
-             {:?}, {:?}, {:?}, {:?} {:?}\
+             {:?}, {:?}, ({:?}, {:?}), {:?}\
              )",
-            context, borrow, borrow_reason, place_span, kind,
+            context, borrow, place, drop_span, kind,
         );
 
         let borrow_spans = self.retrieve_borrow_spans(borrow);
         let borrow_span = borrow_spans.var_or_use();
 
-        let mut err = self.tcx.cannot_borrow_across_destructor(borrow_span, Origin::Mir);
-
-        let drop_span = place_span.1;
+        let mut err = self.infcx.tcx.cannot_borrow_across_destructor(borrow_span, Origin::Mir);
 
         let (what_was_dropped, dropped_ty) = {
-            let place = place_span.0;
             let desc = match self.describe_place(place) {
                 Some(name) => format!("`{}`", name.as_str()),
                 None => format!("temporary value"),
             };
-            let ty = place.ty(self.mir, self.tcx).to_ty(self.tcx);
+            let ty = place.ty(self.mir, self.infcx.tcx).to_ty(self.infcx.tcx);
             (desc, ty)
         };
 
         let label = match dropped_ty.sty {
-            ty::Adt(adt, _) if adt.has_dtor(self.tcx) && !adt.is_box() => {
+            ty::Adt(adt, _) if adt.has_dtor(self.infcx.tcx) && !adt.is_box() => {
                 match self.describe_place(&borrow.borrowed_place) {
                     Some(borrowed) =>
                         format!("here, drop of {D} needs exclusive access to `{B}`, \
@@ -571,17 +604,19 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
         };
         err.span_label(drop_span, label);
 
-        // Only give this note and suggestion if they could be relevant
-        match borrow_reason {
-            BorrowContainsPointReason::Liveness {..}
-            | BorrowContainsPointReason::DropLiveness {..} => {
+        // Only give this note and suggestion if they could be relevant.
+        let explanation = self.explain_why_borrow_contains_point(
+            context, borrow, kind.map(|k| (k, place)),
+        );
+        match explanation {
+            BorrowExplanation::UsedLater {..} |
+            BorrowExplanation::UsedLaterWhenDropped {..} => {
                 err.note("consider using a `let` binding to create a longer lived value");
-            }
-            BorrowContainsPointReason::OutlivesFreeRegion {..} => (),
+            },
+            _ => {},
         }
 
-        self.report_why_borrow_contains_point(
-            &mut err, borrow_reason, kind.map(|k| (k, place_span.0)));
+        explanation.emit(self.infcx.tcx, &mut err);
 
         err.buffer(&mut self.errors_buffer);
     }
@@ -598,15 +633,16 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
             drop_span, borrow_span
         );
 
-        let mut err = self
-            .tcx
-            .thread_local_value_does_not_live_long_enough(borrow_span, Origin::Mir);
+        let mut err = self.infcx.tcx.thread_local_value_does_not_live_long_enough(
+            borrow_span, Origin::Mir
+        );
 
         err.span_label(
             borrow_span,
             "thread-local variables cannot be borrowed beyond the end of the function",
         );
         err.span_label(drop_span, "end of enclosing function is here");
+
         err
     }
 
@@ -615,33 +651,37 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
         context: Context,
         scope_tree: &Lrc<ScopeTree>,
         borrow: &BorrowData<'tcx>,
-        reason: BorrowContainsPointReason<'tcx>,
         drop_span: Span,
+        borrow_spans: UseSpans,
         proper_span: Span,
     ) -> DiagnosticBuilder<'cx> {
         debug!(
             "report_temporary_value_does_not_live_long_enough(\
-             {:?}, {:?}, {:?}, {:?}, {:?}, {:?}\
+             {:?}, {:?}, {:?}, {:?}, {:?}\
              )",
-            context, scope_tree, borrow, reason, drop_span, proper_span
+            context, scope_tree, borrow, drop_span, proper_span
         );
 
-        let tcx = self.tcx;
+        let tcx = self.infcx.tcx;
         let mut err =
             tcx.path_does_not_live_long_enough(proper_span, "borrowed value", Origin::Mir);
         err.span_label(proper_span, "temporary value does not live long enough");
         err.span_label(drop_span, "temporary value only lives until here");
 
-        // Only give this note and suggestion if they could be relevant
-        match reason {
-            BorrowContainsPointReason::Liveness {..}
-            | BorrowContainsPointReason::DropLiveness {..} => {
+        let explanation = self.explain_why_borrow_contains_point(context, borrow, None);
+        match explanation {
+            BorrowExplanation::UsedLater(..) |
+            BorrowExplanation::UsedLaterInLoop(..) |
+            BorrowExplanation::UsedLaterWhenDropped(..) => {
+                // Only give this note and suggestion if it could be relevant.
                 err.note("consider using a `let` binding to create a longer lived value");
-            }
-            BorrowContainsPointReason::OutlivesFreeRegion {..} => (),
+            },
+            _ => {},
         }
+        explanation.emit(self.infcx.tcx, &mut err);
+
+        borrow_spans.args_span_label(&mut err, "value captured here");
 
-        self.report_why_borrow_contains_point(&mut err, reason, None);
         err
     }
 
@@ -715,7 +755,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
 
             // check for inits
             let mut any_match = false;
-            drop_flag_effects::for_location_inits(self.tcx, self.mir, self.move_data, l, |m| {
+            drop_flag_effects::for_location_inits(self.infcx.tcx, self.mir, self.move_data, l, |m| {
                 if m == mpi {
                     any_match = true;
                 }
@@ -739,7 +779,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
         let loan_spans = self.retrieve_borrow_spans(loan);
         let loan_span = loan_spans.args_or_use();
 
-        let tcx = self.tcx;
+        let tcx = self.infcx.tcx;
         let mut err = tcx.cannot_assign_to_borrowed(
             span,
             loan_span,
@@ -749,7 +789,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
 
         loan_spans.var_span_label(&mut err, "borrow occurs due to use in closure");
 
-        self.explain_why_borrow_contains_point(context, loan, None, &mut err);
+        self.explain_why_borrow_contains_point(context, loan, None).emit(self.infcx.tcx, &mut err);
 
         err.buffer(&mut self.errors_buffer);
     }
@@ -801,7 +841,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
             Some(decl) => (self.describe_place(err_place), decl.source_info.span),
         };
 
-        let mut err = self.tcx.cannot_reassign_immutable(
+        let mut err = self.infcx.tcx.cannot_reassign_immutable(
             span,
             place_description.as_ref().map(AsRef::as_ref).unwrap_or("_"),
             from_arg,
@@ -879,13 +919,13 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
                 self.append_local_to_string(local, buf)?;
             }
             Place::Static(ref static_) => {
-                buf.push_str(&self.tcx.item_name(static_.def_id).to_string());
+                buf.push_str(&self.infcx.tcx.item_name(static_.def_id).to_string());
             }
             Place::Projection(ref proj) => {
                 match proj.elem {
                     ProjectionElem::Deref => {
                         let upvar_field_projection =
-                            place.is_upvar_field_projection(self.mir, &self.tcx);
+                            place.is_upvar_field_projection(self.mir, &self.infcx.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();
@@ -947,7 +987,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
                         autoderef = true;
 
                         let upvar_field_projection =
-                            place.is_upvar_field_projection(self.mir, &self.tcx);
+                            place.is_upvar_field_projection(self.mir, &self.infcx.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();
@@ -1062,10 +1102,10 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
                     // the local code in the current crate, so this returns an `Option` in case
                     // the closure comes from another crate. But in that case we wouldn't
                     // be borrowck'ing it, so we can just unwrap:
-                    let node_id = self.tcx.hir.as_local_node_id(def_id).unwrap();
-                    let freevar = self.tcx.with_freevars(node_id, |fv| fv[field.index()]);
+                    let node_id = self.infcx.tcx.hir.as_local_node_id(def_id).unwrap();
+                    let freevar = self.infcx.tcx.with_freevars(node_id, |fv| fv[field.index()]);
 
-                    self.tcx.hir.name(freevar.var_id()).to_string()
+                    self.infcx.tcx.hir.name(freevar.var_id()).to_string()
                 }
                 _ => {
                     // Might need a revision when the fields in trait RFC is implemented
@@ -1098,7 +1138,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
     /// Check if a place is a thread-local static.
     pub fn is_place_thread_local(&self, place: &Place<'tcx>) -> bool {
         if let Place::Static(statik) = place {
-            let attrs = self.tcx.get_attrs(statik.def_id);
+            let attrs = self.infcx.tcx.get_attrs(statik.def_id);
             let is_thread_local = attrs.iter().any(|attr| attr.check_name("thread_local"));
 
             debug!(
@@ -1126,6 +1166,481 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
             None
         }
     }
+
+    /// Annotate argument and return type of function and closure with (synthesized) lifetime for
+    /// borrow of local value that does not live long enough.
+    fn annotate_argument_and_return_for_borrow(
+        &self,
+        borrow: &BorrowData<'tcx>,
+    ) -> Option<AnnotatedBorrowFnSignature> {
+        // Define a fallback for when we can't match a closure.
+        let fallback = || {
+            let is_closure = self.infcx.tcx.is_closure(self.mir_def_id);
+            if is_closure {
+                None
+            } else {
+                 let ty = self.infcx.tcx.type_of(self.mir_def_id);
+                 match ty.sty {
+                     ty::TyKind::FnDef(_, _) | ty::TyKind::FnPtr(_) =>
+                        self.annotate_fn_sig(
+                            self.mir_def_id,
+                            self.infcx.tcx.fn_sig(self.mir_def_id)
+                        ),
+                    _ => None,
+                }
+            }
+        };
+
+        // In order to determine whether we need to annotate, we need to check whether the reserve
+        // place was an assignment into a temporary.
+        //
+        // If it was, we check whether or not that temporary is eventually assigned into the return
+        // place. If it was, we can add annotations about the function's return type and arguments
+        // and it'll make sense.
+        let location = borrow.reserve_location;
+        debug!("annotate_argument_and_return_for_borrow: location={:?}", location);
+        match &self.mir[location.block].statements.get(location.statement_index) {
+            Some(&Statement {
+                kind: StatementKind::Assign(ref reservation, _),
+                ..
+            }) => {
+                debug!("annotate_argument_and_return_for_borrow: reservation={:?}", reservation);
+                // Check that the initial assignment of the reserve location is into a temporary.
+                let mut target = *match reservation {
+                    Place::Local(local) if self.mir.local_kind(*local) == LocalKind::Temp => local,
+                    _ => return None,
+                };
+
+                // Next, look through the rest of the block, checking if we are assigning the
+                // `target` (that is, the place that contains our borrow) to anything.
+                let mut annotated_closure = None;
+                for stmt in &self.mir[location.block].statements[location.statement_index + 1..] {
+                    debug!(
+                        "annotate_argument_and_return_for_borrow: target={:?} stmt={:?}",
+                        target, stmt
+                    );
+                    if let StatementKind::Assign(
+                        Place::Local(assigned_to),
+                        rvalue,
+                    ) = &stmt.kind {
+                        debug!("annotate_argument_and_return_for_borrow: assigned_to={:?} \
+                               rvalue={:?}", assigned_to, rvalue);
+                        // Check if our `target` was captured by a closure.
+                        if let Rvalue::Aggregate(
+                                box AggregateKind::Closure(def_id, substs),
+                                operands,
+                        ) = rvalue {
+                            for operand in operands {
+                                let assigned_from = match operand {
+                                    Operand::Copy(assigned_from) |
+                                    Operand::Move(assigned_from) => assigned_from,
+                                    _ => continue,
+                                };
+                                debug!(
+                                    "annotate_argument_and_return_for_borrow: assigned_from={:?}",
+                                    assigned_from
+                                );
+
+                                // Find the local from the operand.
+                                let assigned_from_local = match assigned_from.local() {
+                                    Some(local) => local,
+                                    None => continue,
+                                };
+
+                                if assigned_from_local != target {
+                                    continue;
+                                }
+
+                                // If a closure captured our `target` and then assigned
+                                // into a place then we should annotate the closure in
+                                // case it ends up being assigned into the return place.
+                                annotated_closure = self.annotate_fn_sig(
+                                    *def_id,
+                                    self.infcx.closure_sig(*def_id, *substs)
+                                );
+                                debug!(
+                                    "annotate_argument_and_return_for_borrow: \
+                                     annotated_closure={:?} assigned_from_local={:?} \
+                                     assigned_to={:?}",
+                                     annotated_closure, assigned_from_local, assigned_to
+                                );
+
+                                if *assigned_to == mir::RETURN_PLACE {
+                                    // If it was assigned directly into the return place, then
+                                    // return now.
+                                    return annotated_closure;
+                                } else {
+                                    // Otherwise, update the target.
+                                    target = *assigned_to;
+                                }
+                            }
+
+                            // If none of our closure's operands matched, then skip to the next
+                            // statement.
+                            continue;
+                        }
+
+                        // Otherwise, look at other types of assignment.
+                        let assigned_from = match rvalue {
+                            Rvalue::Ref(_, _, assigned_from) => assigned_from,
+                            Rvalue::Use(operand) => match operand {
+                                Operand::Copy(assigned_from) |
+                                Operand::Move(assigned_from) => assigned_from,
+                                _ => continue,
+                            },
+                            _ => continue,
+                        };
+                        debug!(
+                            "annotate_argument_and_return_for_borrow: \
+                             assigned_from={:?}", assigned_from,
+                        );
+
+                        // Find the local from the rvalue.
+                        let assigned_from_local = match assigned_from.local() {
+                            Some(local) => local,
+                            None => continue,
+                        };
+                        debug!(
+                            "annotate_argument_and_return_for_borrow: \
+                             assigned_from_local={:?}", assigned_from_local,
+                        );
+
+                        // Check if our local matches the target - if so, we've assigned our
+                        // borrow to a new place.
+                        if assigned_from_local != target {
+                            continue;
+                        }
+
+                        // If we assigned our `target` into a new place, then we should
+                        // check if it was the return place.
+                        debug!(
+                            "annotate_argument_and_return_for_borrow: \
+                             assigned_from_local={:?} assigned_to={:?}",
+                             assigned_from_local, assigned_to
+                        );
+                        if *assigned_to == mir::RETURN_PLACE {
+                            // If it was then return the annotated closure if there was one,
+                            // else, annotate this function.
+                            return annotated_closure.or_else(fallback);
+                        }
+
+                        // If we didn't assign into the return place, then we just update
+                        // the target.
+                        target = *assigned_to;
+                    }
+                }
+
+                // Check the terminator if we didn't find anything in the statements.
+                let terminator = &self.mir[location.block].terminator();
+                debug!(
+                    "annotate_argument_and_return_for_borrow: target={:?} terminator={:?}",
+                    target, terminator
+                );
+                if let TerminatorKind::Call {
+                    destination: Some((Place::Local(assigned_to), _)),
+                    args,
+                    ..
+                } = &terminator.kind {
+                    debug!(
+                        "annotate_argument_and_return_for_borrow: assigned_to={:?} args={:?}",
+                        assigned_to, args
+                    );
+                    for operand in args {
+                        let assigned_from = match operand {
+                            Operand::Copy(assigned_from) |
+                            Operand::Move(assigned_from) => assigned_from,
+                            _ => continue,
+                        };
+                        debug!(
+                            "annotate_argument_and_return_for_borrow: assigned_from={:?}",
+                            assigned_from,
+                        );
+
+                        if let Some(assigned_from_local) = assigned_from.local() {
+                            debug!(
+                                "annotate_argument_and_return_for_borrow: assigned_from_local={:?}",
+                                assigned_from_local,
+                            );
+
+                            if *assigned_to == mir::RETURN_PLACE &&
+                                assigned_from_local == target
+                            {
+                                return annotated_closure.or_else(fallback);
+                            }
+                        }
+                    }
+                }
+            }
+            _ => {}
+        }
+
+        // If we haven't found an assignment into the return place, then we need not add
+        // any annotations.
+        debug!("annotate_argument_and_return_for_borrow: none found");
+        None
+    }
+
+    /// Annotate the first argument and return type of a function signature if they are
+    /// references.
+    fn annotate_fn_sig(
+        &self,
+        did: DefId,
+        sig: ty::PolyFnSig<'tcx>,
+    ) -> Option<AnnotatedBorrowFnSignature> {
+        debug!("annotate_fn_sig: did={:?} sig={:?}", did, sig);
+        let is_closure = self.infcx.tcx.is_closure(did);
+        let fn_node_id = self.infcx.tcx.hir.as_local_node_id(did)?;
+        let fn_decl = self.infcx.tcx.hir.fn_decl(fn_node_id)?;
+
+        // We need to work out which arguments to highlight. We do this by looking
+        // at the return type, where there are three cases:
+        //
+        // 1. If there are named arguments, then we should highlight the return type and
+        //    highlight any of the arguments that are also references with that lifetime.
+        //    If there are no arguments that have the same lifetime as the return type,
+        //    then don't highlight anything.
+        // 2. The return type is a reference with an anonymous lifetime. If this is
+        //    the case, then we can take advantage of (and teach) the lifetime elision
+        //    rules.
+        //
+        //    We know that an error is being reported. So the arguments and return type
+        //    must satisfy the elision rules. Therefore, if there is a single argument
+        //    then that means the return type and first (and only) argument have the same
+        //    lifetime and the borrow isn't meeting that, we can highlight the argument
+        //    and return type.
+        //
+        //    If there are multiple arguments then the first argument must be self (else
+        //    it would not satisfy the elision rules), so we can highlight self and the
+        //    return type.
+        // 3. The return type is not a reference. In this case, we don't highlight
+        //    anything.
+        let return_ty = sig.output();
+        match return_ty.skip_binder().sty {
+            ty::TyKind::Ref(return_region, _, _) if return_region.has_name() && !is_closure => {
+                // This is case 1 from above, return type is a named reference so we need to
+                // search for relevant arguments.
+                let mut arguments = Vec::new();
+                for (index, argument) in sig.inputs().skip_binder().iter().enumerate() {
+                    if let ty::TyKind::Ref(argument_region, _, _) = argument.sty {
+                        if argument_region == return_region {
+                            // Need to use the `rustc::ty` types to compare against the
+                            // `return_region`. Then use the `rustc::hir` type to get only
+                            // the lifetime span.
+                            match &fn_decl.inputs[index].node {
+                                hir::TyKind::Rptr(lifetime, _) => {
+                                    // With access to the lifetime, we can get
+                                    // the span of it.
+                                    arguments.push((*argument, lifetime.span));
+                                },
+                                _ => bug!("ty type is a ref but hir type is not"),
+                            }
+                        }
+                    }
+                }
+
+                // We need to have arguments. This shouldn't happen, but it's worth checking.
+                if arguments.is_empty() {
+                    return None;
+                }
+
+                // We use a mix of the HIR and the Ty types to get information
+                // as the HIR doesn't have full types for closure arguments.
+                let return_ty = *sig.output().skip_binder();
+                let mut return_span = fn_decl.output.span();
+                if let hir::FunctionRetTy::Return(ty) = fn_decl.output {
+                    if let hir::TyKind::Rptr(lifetime, _) = ty.into_inner().node {
+                        return_span = lifetime.span;
+                    }
+                }
+
+                Some(AnnotatedBorrowFnSignature::NamedFunction {
+                    arguments,
+                    return_ty,
+                    return_span,
+                })
+            },
+            ty::TyKind::Ref(_, _, _) if is_closure => {
+                // This is case 2 from above but only for closures, return type is anonymous
+                // reference so we select
+                // the first argument.
+                let argument_span = fn_decl.inputs.first()?.span;
+                let argument_ty = sig.inputs().skip_binder().first()?;
+
+                // Closure arguments are wrapped in a tuple, so we need to get the first
+                // from that.
+                if let ty::TyKind::Tuple(elems) = argument_ty.sty {
+                    let argument_ty = elems.first()?;
+                    if let ty::TyKind::Ref(_, _, _) = argument_ty.sty {
+                        return Some(AnnotatedBorrowFnSignature::Closure {
+                            argument_ty,
+                            argument_span,
+                        });
+                    }
+                }
+
+                None
+            },
+            ty::TyKind::Ref(_, _, _) => {
+                // This is also case 2 from above but for functions, return type is still an
+                // anonymous reference so we select the first argument.
+                let argument_span = fn_decl.inputs.first()?.span;
+                let argument_ty = sig.inputs().skip_binder().first()?;
+
+                let return_span = fn_decl.output.span();
+                let return_ty = *sig.output().skip_binder();
+
+                // We expect the first argument to be a reference.
+                match argument_ty.sty {
+                    ty::TyKind::Ref(_, _, _) => {},
+                    _ => return None,
+                }
+
+                Some(AnnotatedBorrowFnSignature::AnonymousFunction {
+                    argument_ty,
+                    argument_span,
+                    return_ty,
+                    return_span,
+                })
+            },
+            _ => {
+                // This is case 3 from above, return type is not a reference so don't highlight
+                // anything.
+                None
+            },
+        }
+    }
+}
+
+#[derive(Debug)]
+enum AnnotatedBorrowFnSignature<'tcx> {
+    NamedFunction {
+        arguments: Vec<(ty::Ty<'tcx>, Span)>,
+        return_ty: ty::Ty<'tcx>,
+        return_span: Span,
+    },
+    AnonymousFunction {
+        argument_ty: ty::Ty<'tcx>,
+        argument_span: Span,
+        return_ty: ty::Ty<'tcx>,
+        return_span: Span,
+    },
+    Closure {
+        argument_ty: ty::Ty<'tcx>,
+        argument_span: Span,
+    }
+}
+
+impl<'tcx> AnnotatedBorrowFnSignature<'tcx> {
+    /// Annotate the provided diagnostic with information about borrow from the fn signature that
+    /// helps explain.
+    fn emit(
+        &self,
+        diag: &mut DiagnosticBuilder<'_>
+    ) -> String {
+        match self {
+            AnnotatedBorrowFnSignature::Closure { argument_ty, argument_span } => {
+                diag.span_label(
+                    *argument_span,
+                    format!("has type `{}`", self.get_name_for_ty(argument_ty, 0)),
+                );
+
+                self.get_region_name_for_ty(argument_ty, 0)
+            },
+            AnnotatedBorrowFnSignature::AnonymousFunction {
+                argument_ty,
+                argument_span,
+                return_ty,
+                return_span,
+            } => {
+                let argument_ty_name = self.get_name_for_ty(argument_ty, 0);
+                diag.span_label(
+                    *argument_span,
+                    format!("has type `{}`", argument_ty_name)
+                );
+
+                let return_ty_name = self.get_name_for_ty(return_ty, 0);
+                let types_equal = return_ty_name == argument_ty_name;
+                diag.span_label(
+                    *return_span,
+                    format!(
+                        "{}has type `{}`",
+                        if types_equal { "also " } else { "" },
+                        return_ty_name,
+                    )
+                );
+
+                diag.note(
+                    "argument and return type have the same lifetime due to lifetime elision rules",
+                );
+                diag.note(
+                    "to learn more, visit <https://doc.rust-lang.org/book/second-edition/ch10-03-\
+                     lifetime-syntax.html#lifetime-elision>",
+                );
+
+                self.get_region_name_for_ty(return_ty, 0)
+            },
+            AnnotatedBorrowFnSignature::NamedFunction {
+                arguments,
+                return_ty,
+                return_span,
+            } => {
+                // Region of return type and arguments checked to be the same earlier.
+                let region_name = self.get_region_name_for_ty(return_ty, 0);
+                for (_, argument_span) in arguments {
+                    diag.span_label(
+                        *argument_span,
+                        format!("has lifetime `{}`", region_name)
+                    );
+                }
+
+                diag.span_label(
+                    *return_span,
+                    format!(
+                        "also has lifetime `{}`",
+                        region_name,
+                    )
+                );
+
+                diag.help(
+                    &format!(
+                        "use data from the highlighted arguments which match the `{}` lifetime of \
+                         the return type",
+                         region_name,
+                    ),
+                );
+
+                region_name
+            },
+        }
+    }
+
+    /// Return the name of the provided `Ty` (that must be a reference) with a synthesized lifetime
+    /// name where required.
+    fn get_name_for_ty(&self, ty: ty::Ty<'tcx>, counter: usize) -> String {
+        // We need to add synthesized lifetimes where appropriate. We do
+        // this by hooking into the pretty printer and telling it to label the
+        // lifetimes without names with the value `'0`.
+        match ty.sty {
+            ty::TyKind::Ref(ty::RegionKind::ReLateBound(_, br), _, _) |
+            ty::TyKind::Ref(ty::RegionKind::ReSkolemized(_, br), _, _) =>
+                with_highlight_region_for_bound_region(*br, counter, || format!("{}", ty)),
+            _ => format!("{}", ty),
+        }
+    }
+
+    /// Return the name of the provided `Ty` (that must be a reference)'s region with a
+    /// synthesized lifetime name where required.
+    fn get_region_name_for_ty(&self, ty: ty::Ty<'tcx>, counter: usize) -> String {
+        match ty.sty {
+            ty::TyKind::Ref(region, _, _) => match region {
+                ty::RegionKind::ReLateBound(_, br) |
+                ty::RegionKind::ReSkolemized(_, br) =>
+                    with_highlight_region_for_bound_region(*br, counter, || format!("{}", region)),
+                _ => format!("{}", region),
+            }
+            _ => bug!("ty for annotation of borrow region is not a reference"),
+        }
+    }
 }
 
 // The span(s) associated to a use of a place.
@@ -1214,9 +1729,11 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
             if let AggregateKind::Closure(def_id, _) = **kind {
                 debug!("find_closure_move_span: found closure {:?}", places);
 
-                if let Some(node_id) = self.tcx.hir.as_local_node_id(def_id) {
-                    if let Closure(_, _, _, args_span, _) = self.tcx.hir.expect_expr(node_id).node {
-                        if let Some(var_span) = self.tcx.with_freevars(node_id, |freevars| {
+                if let Some(node_id) = self.infcx.tcx.hir.as_local_node_id(def_id) {
+                    if let Closure(
+                        _, _, _, args_span, _
+                    ) = self.infcx.tcx.hir.expect_expr(node_id).node {
+                        if let Some(var_span) = self.infcx.tcx.with_freevars(node_id, |freevars| {
                             for (v, place) in freevars.iter().zip(places) {
                                 match place {
                                     Operand::Copy(place) | Operand::Move(place)
@@ -1253,7 +1770,6 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
     pub(super) fn borrow_spans(&self, use_span: Span, location: Location) -> UseSpans {
         use self::UseSpans::*;
         use rustc::hir::ExprKind::Closure;
-        use rustc::mir::AggregateKind;
 
         let local = match self.mir[location.block]
             .statements
@@ -1276,16 +1792,16 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
                 if let AggregateKind::Closure(def_id, _) = **kind {
                     debug!("find_closure_borrow_span: found closure {:?}", places);
 
-                    return if let Some(node_id) = self.tcx.hir.as_local_node_id(def_id) {
+                    return if let Some(node_id) = self.infcx.tcx.hir.as_local_node_id(def_id) {
                         let args_span = if let Closure(_, _, _, span, _) =
-                            self.tcx.hir.expect_expr(node_id).node
+                            self.infcx.tcx.hir.expect_expr(node_id).node
                         {
                             span
                         } else {
                             return OtherUse(use_span);
                         };
 
-                        self.tcx
+                        self.infcx.tcx
                             .with_freevars(node_id, |freevars| {
                                 for (v, place) in freevars.iter().zip(places) {
                                     match *place {
diff --git a/src/librustc_mir/borrow_check/mod.rs b/src/librustc_mir/borrow_check/mod.rs
index 09fb37bccd9..06884875598 100644
--- a/src/librustc_mir/borrow_check/mod.rs
+++ b/src/librustc_mir/borrow_check/mod.rs
@@ -247,8 +247,8 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(
     let dominators = mir.dominators();
 
     let mut mbcx = MirBorrowckCtxt {
-        tcx: tcx,
-        mir: mir,
+        infcx,
+        mir,
         mir_def_id: def_id,
         move_data: &mdpe.move_data,
         param_env: param_env,
@@ -369,7 +369,7 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(
         }
 
         for diag in mbcx.errors_buffer.drain(..) {
-            DiagnosticBuilder::new_diagnostic(mbcx.tcx.sess.diagnostic(), diag).emit();
+            DiagnosticBuilder::new_diagnostic(mbcx.infcx.tcx.sess.diagnostic(), diag).emit();
         }
     }
 
@@ -384,7 +384,7 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(
 }
 
 pub struct MirBorrowckCtxt<'cx, 'gcx: 'tcx, 'tcx: 'cx> {
-    tcx: TyCtxt<'cx, 'gcx, 'tcx>,
+    infcx: &'cx InferCtxt<'cx, 'gcx, 'tcx>,
     mir: &'cx Mir<'tcx>,
     mir_def_id: DefId,
     move_data: &'cx MoveData<'tcx>,
@@ -612,13 +612,14 @@ impl<'cx, 'gcx, 'tcx> DataflowResultsConsumer<'cx, 'tcx> for MirBorrowckCtxt<'cx
                 target: _,
                 unwind: _,
             } => {
-                let gcx = self.tcx.global_tcx();
+                let gcx = self.infcx.tcx.global_tcx();
 
                 // Compute the type with accurate region information.
-                let drop_place_ty = drop_place.ty(self.mir, self.tcx);
+                let drop_place_ty = drop_place.ty(self.mir, self.infcx.tcx);
 
                 // Erase the regions.
-                let drop_place_ty = self.tcx.erase_regions(&drop_place_ty).to_ty(self.tcx);
+                let drop_place_ty = self.infcx.tcx.erase_regions(&drop_place_ty)
+                    .to_ty(self.infcx.tcx);
 
                 // "Lift" into the gcx -- once regions are erased, this type should be in the
                 // global arenas; this "lift" operation basically just asserts that is true, but
@@ -953,7 +954,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
             return;
         }
 
-        let gcx = self.tcx.global_tcx();
+        let gcx = self.infcx.tcx.global_tcx();
         let drop_field = |mir: &mut MirBorrowckCtxt<'cx, 'gcx, 'tcx>,
                           (index, field): (usize, ty::Ty<'gcx>)| {
             let field_ty = gcx.normalize_erasing_regions(mir.param_env, field);
@@ -971,7 +972,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
             // individual fields instead. This way if `foo` has a
             // destructor but `bar` does not, we will only check for
             // borrows of `x.foo` and not `x.bar`. See #47703.
-            ty::Adt(def, substs) if def.is_struct() && !def.has_dtor(self.tcx) => {
+            ty::Adt(def, substs) if def.is_struct() && !def.has_dtor(self.infcx.tcx) => {
                 def.all_fields()
                     .map(|field| field.ty(gcx, substs))
                     .enumerate()
@@ -991,7 +992,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
                     && !self.mir.upvar_decls.is_empty() =>
             {
                 substs
-                    .upvar_tys(def, self.tcx)
+                    .upvar_tys(def, self.infcx.tcx)
                     .enumerate()
                     .for_each(|field| drop_field(self, field));
             }
@@ -1002,7 +1003,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
                     && !self.mir.upvar_decls.is_empty() =>
             {
                 substs
-                    .upvar_tys(def, self.tcx)
+                    .upvar_tys(def, self.infcx.tcx)
                     .enumerate()
                     .for_each(|field| drop_field(self, field));
             }
@@ -1168,7 +1169,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
         );
 
         let mut error_reported = false;
-        let tcx = self.tcx;
+        let tcx = self.infcx.tcx;
         let mir = self.mir;
         let location = self.location_table.start_index(context.loc);
         let borrow_set = self.borrow_set.clone();
@@ -1206,7 +1207,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
                 (Read(kind), BorrowKind::Unique) | (Read(kind), BorrowKind::Mut { .. }) => {
                     // Reading from mere reservations of mutable-borrows is OK.
                     if !is_active(&this.dominators, borrow, context.loc) {
-                        assert!(allow_two_phase_borrow(&this.tcx, borrow.kind));
+                        assert!(allow_two_phase_borrow(&this.infcx.tcx, borrow.kind));
                         return Control::Continue;
                     }
 
@@ -1338,7 +1339,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
                     BorrowKind::Shared => (Deep, Read(ReadKind::Borrow(bk))),
                     BorrowKind::Unique | BorrowKind::Mut { .. } => {
                         let wk = WriteKind::MutableBorrow(bk);
-                        if allow_two_phase_borrow(&self.tcx, bk) {
+                        if allow_two_phase_borrow(&self.infcx.tcx, bk) {
                             (Deep, Reservation(wk))
                         } else {
                             (Deep, Write(wk))
@@ -1413,7 +1414,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
                     | AggregateKind::Generator(def_id, _, _) => {
                         let BorrowCheckResult {
                             used_mut_upvars, ..
-                        } = self.tcx.mir_borrowck(def_id);
+                        } = self.infcx.tcx.mir_borrowck(def_id);
                         debug!("{:?} used_mut_upvars={:?}", def_id, used_mut_upvars);
                         for field in used_mut_upvars {
                             // This relies on the current way that by-value
@@ -1427,7 +1428,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
                                 Operand::Move(ref place @ Place::Projection(_))
                                 | Operand::Copy(ref place @ Place::Projection(_)) => {
                                     if let Some(field) = place.is_upvar_field_projection(
-                                            self.mir, &self.tcx) {
+                                            self.mir, &self.infcx.tcx) {
                                         self.used_mut_upvars.push(field);
                                     }
                                 }
@@ -1546,11 +1547,11 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
         // that is merged.
         let sd = if might_be_alive { Deep } else { Shallow(None) };
 
-        if places_conflict::places_conflict(self.tcx, self.mir, place, root_place, sd) {
+        if places_conflict::places_conflict(self.infcx.tcx, self.mir, place, root_place, sd) {
             debug!("check_for_invalidation_at_exit({:?}): INVALID", place);
             // FIXME: should be talking about the region lifetime instead
             // of just a span here.
-            let span = self.tcx.sess.source_map().end_point(span);
+            let span = self.infcx.tcx.sess.source_map().end_point(span);
             self.report_borrowed_value_does_not_live_long_enough(
                 context,
                 borrow,
@@ -1566,7 +1567,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
         debug!("check_for_local_borrow({:?})", borrow);
 
         if borrow_of_local_data(&borrow.borrowed_place) {
-            let err = self.tcx
+            let err = self.infcx.tcx
                 .cannot_borrow_across_generator_yield(
                     self.retrieve_borrow_spans(borrow).var_or_use(),
                     yield_span,
@@ -1583,7 +1584,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
         span: Span,
         flow_state: &Flows<'cx, 'gcx, 'tcx>,
     ) {
-        if !self.tcx.two_phase_borrows() {
+        if !self.infcx.tcx.two_phase_borrows() {
             return;
         }
 
@@ -1845,7 +1846,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
                             // if type of `P` has a dtor, then
                             // assigning to `P.f` requires `P` itself
                             // be already initialized
-                            let tcx = self.tcx;
+                            let tcx = self.infcx.tcx;
                             match base.ty(self.mir, tcx).to_ty(tcx).sty {
                                 ty::Adt(def, _) if def.has_dtor(tcx) => {
                                     self.check_if_path_or_subpath_is_moved(
@@ -1929,7 +1930,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
             | Write(wk @ WriteKind::StorageDeadOrDrop(_))
             | Write(wk @ WriteKind::MutableBorrow(BorrowKind::Shared)) => {
                 if let Err(_place_err) = self.is_mutable(place, is_local_mutation_allowed) {
-                    if self.tcx.migrate_borrowck() {
+                    if self.infcx.tcx.migrate_borrowck() {
                         // rust-lang/rust#46908: In pure NLL mode this
                         // code path should be unreachable (and thus
                         // we signal an ICE in the else branch
@@ -1952,7 +1953,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
                             location,
                         );
                     } else {
-                        self.tcx.sess.delay_span_bug(
+                        self.infcx.tcx.sess.delay_span_bug(
                             span,
                             &format!(
                                 "Accessing `{:?}` with the kind `{:?}` shouldn't be possible",
@@ -2020,7 +2021,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.tcx) {
+                if let Some(field) = place.is_upvar_field_projection(self.mir, &self.infcx.tcx) {
                     self.used_mut_upvars.push(field);
                 }
             }
@@ -2070,7 +2071,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
                 is_local_mutation_allowed,
             }),
             Place::Static(ref static_) => {
-                if self.tcx.is_static(static_.def_id) != Some(hir::Mutability::MutMutable) {
+                if self.infcx.tcx.is_static(static_.def_id) != Some(hir::Mutability::MutMutable) {
                     Err(place)
                 } else {
                     Ok(RootPlace {
@@ -2082,7 +2083,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
             Place::Projection(ref proj) => {
                 match proj.elem {
                     ProjectionElem::Deref => {
-                        let base_ty = proj.base.ty(self.mir, self.tcx).to_ty(self.tcx);
+                        let base_ty = proj.base.ty(self.mir, self.infcx.tcx).to_ty(self.infcx.tcx);
 
                         // Check the kind of deref to decide
                         match base_ty.sty {
@@ -2094,7 +2095,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
                                     // unique path to the `&mut`
                                     hir::MutMutable => {
                                         let mode = match place.is_upvar_field_projection(
-                                            self.mir, &self.tcx)
+                                            self.mir, &self.infcx.tcx)
                                         {
                                             Some(field)
                                                 if {
@@ -2140,7 +2141,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
                     | ProjectionElem::Subslice { .. }
                     | ProjectionElem::Downcast(..) => {
                         let upvar_field_projection = place.is_upvar_field_projection(
-                            self.mir, &self.tcx);
+                            self.mir, &self.infcx.tcx);
                         if let Some(field) = upvar_field_projection {
                             let decl = &self.mir.upvar_decls[field.index()];
                             debug!(
diff --git a/src/librustc_mir/borrow_check/move_errors.rs b/src/librustc_mir/borrow_check/move_errors.rs
index 52d051ebe7b..8a97f25ef58 100644
--- a/src/librustc_mir/borrow_check/move_errors.rs
+++ b/src/librustc_mir/borrow_check/move_errors.rs
@@ -244,30 +244,31 @@ 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.tcx));
+                   original_path.is_upvar_field_projection(self.mir, &self.infcx.tcx));
             (
                 match kind {
                     IllegalMoveOriginKind::Static => {
-                        self.tcx.cannot_move_out_of(span, "static item", origin)
+                        self.infcx.tcx.cannot_move_out_of(span, "static item", origin)
                     }
                     IllegalMoveOriginKind::BorrowedContent { target_place: place } => {
                         // Inspect the type of the content behind the
                         // borrow to provide feedback about why this
                         // was a move rather than a copy.
-                        let ty = place.ty(self.mir, self.tcx).to_ty(self.tcx);
+                        let ty = place.ty(self.mir, self.infcx.tcx).to_ty(self.infcx.tcx);
                         let is_upvar_field_projection =
                             self.prefixes(&original_path, PrefixSet::All)
-                            .any(|p| p.is_upvar_field_projection(self.mir, &self.tcx)
+                            .any(|p| p.is_upvar_field_projection(self.mir, &self.infcx.tcx)
                                  .is_some());
                         match ty.sty {
-                            ty::Array(..) | ty::Slice(..) => self
-                                .tcx
-                                .cannot_move_out_of_interior_noncopy(span, ty, None, origin),
+                            ty::Array(..) | ty::Slice(..) =>
+                                self.infcx.tcx.cannot_move_out_of_interior_noncopy(
+                                    span, ty, None, origin
+                                ),
                             ty::Closure(def_id, closure_substs)
                                 if !self.mir.upvar_decls.is_empty() && is_upvar_field_projection
                             => {
                                 let closure_kind_ty =
-                                    closure_substs.closure_kind_ty(def_id, self.tcx);
+                                    closure_substs.closure_kind_ty(def_id, self.infcx.tcx);
                                 let closure_kind = closure_kind_ty.to_opt_closure_kind();
                                 let place_description = match closure_kind {
                                     Some(ty::ClosureKind::Fn) => {
@@ -285,18 +286,18 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> {
                                        place_description={:?}", closure_kind_ty, closure_kind,
                                        place_description);
 
-                                let mut diag = self.tcx.cannot_move_out_of(
+                                let mut diag = self.infcx.tcx.cannot_move_out_of(
                                     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.tcx) {
+                                            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();
                                         let upvar_node_id =
-                                            self.tcx.hir.hir_to_node_id(upvar_hir_id);
-                                        let upvar_span = self.tcx.hir.span(upvar_node_id);
+                                            self.infcx.tcx.hir.hir_to_node_id(upvar_hir_id);
+                                        let upvar_span = self.infcx.tcx.hir.span(upvar_node_id);
                                         diag.span_label(upvar_span, "captured outer variable");
                                         break;
                                     }
@@ -304,18 +305,19 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> {
 
                                 diag
                             }
-                            _ => self
-                                .tcx
-                                .cannot_move_out_of(span, "borrowed content", origin),
+                            _ => self.infcx.tcx.cannot_move_out_of(
+                                span, "borrowed content", origin
+                            ),
                         }
                     }
                     IllegalMoveOriginKind::InteriorOfTypeWithDestructor { container_ty: ty } => {
-                        self.tcx
+                        self.infcx.tcx
                             .cannot_move_out_of_interior_of_drop(span, ty, origin)
                     }
-                    IllegalMoveOriginKind::InteriorOfSliceOrArray { ty, is_index } => self
-                        .tcx
-                        .cannot_move_out_of_interior_noncopy(span, ty, Some(*is_index), origin),
+                    IllegalMoveOriginKind::InteriorOfSliceOrArray { ty, is_index } =>
+                        self.infcx.tcx.cannot_move_out_of_interior_noncopy(
+                            span, ty, Some(*is_index), origin
+                        ),
                 },
                 span,
             )
@@ -331,7 +333,7 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> {
         err: &mut DiagnosticBuilder<'a>,
         span: Span,
     ) {
-        let snippet = self.tcx.sess.source_map().span_to_snippet(span).unwrap();
+        let snippet = self.infcx.tcx.sess.source_map().span_to_snippet(span).unwrap();
         match error {
             GroupedMoveError::MovesFromPlace {
                 mut binds_to,
@@ -394,8 +396,7 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> {
                     ..
                 }))
             ) = bind_to.is_user_variable {
-                let pat_snippet = self
-                    .tcx.sess.source_map()
+                let pat_snippet = self.infcx.tcx.sess.source_map()
                     .span_to_snippet(pat_span)
                     .unwrap();
                 if pat_snippet.starts_with('&') {
diff --git a/src/librustc_mir/borrow_check/mutability_errors.rs b/src/librustc_mir/borrow_check/mutability_errors.rs
index a078aa59a7d..30555dbf260 100644
--- a/src/librustc_mir/borrow_check/mutability_errors.rs
+++ b/src/librustc_mir/borrow_check/mutability_errors.rs
@@ -71,11 +71,11 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> {
                 elem: ProjectionElem::Field(upvar_index, _),
             }) => {
                 debug_assert!(is_closure_or_generator(
-                    base.ty(self.mir, self.tcx).to_ty(self.tcx)
+                    base.ty(self.mir, self.infcx.tcx).to_ty(self.infcx.tcx)
                 ));
 
                 item_msg = format!("`{}`", access_place_desc.unwrap());
-                if access_place.is_upvar_field_projection(self.mir, &self.tcx).is_some() {
+                if access_place.is_upvar_field_projection(self.mir, &self.infcx.tcx).is_some() {
                     reason = ", as it is not declared as mutable".to_string();
                 } else {
                     let name = self.mir.upvar_decls[upvar_index.index()].debug_name;
@@ -91,11 +91,11 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> {
                     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.tcx).to_ty(self.tcx)
+                        the_place_err.ty(self.mir, self.infcx.tcx).to_ty(self.infcx.tcx)
                     ));
 
                     reason = if access_place.is_upvar_field_projection(self.mir,
-                                                                       &self.tcx).is_some() {
+                                                                       &self.infcx.tcx).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()
@@ -116,7 +116,7 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> {
                     reason = ", as it is immutable for the pattern guard".to_string();
                 } else {
                     let pointer_type =
-                        if base.ty(self.mir, self.tcx).to_ty(self.tcx).is_region_ptr() {
+                        if base.ty(self.mir, self.infcx.tcx).to_ty(self.infcx.tcx).is_region_ptr() {
                             "`&` reference"
                         } else {
                             "`*const` pointer"
@@ -145,7 +145,7 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> {
                     reason = String::new();
                 } else {
                     item_msg = format!("`{}`", access_place_desc.unwrap());
-                    let static_name = &self.tcx.item_name(*def_id);
+                    let static_name = &self.infcx.tcx.item_name(*def_id);
                     reason = format!(", as `{}` is an immutable static item", static_name);
                 }
             }
@@ -177,14 +177,14 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> {
 
         let span = match error_access {
             AccessKind::Move => {
-                err = self.tcx
+                err = self.infcx.tcx
                     .cannot_move_out_of(span, &(item_msg + &reason), Origin::Mir);
                 act = "move";
                 acted_on = "moved";
                 span
             }
             AccessKind::Mutate => {
-                err = self.tcx
+                err = self.infcx.tcx
                     .cannot_assign(span, &(item_msg + &reason), Origin::Mir);
                 act = "assign";
                 acted_on = "written";
@@ -196,7 +196,7 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> {
 
                 let borrow_spans = self.borrow_spans(span, location);
                 let borrow_span = borrow_spans.args_or_use();
-                err = self.tcx.cannot_borrow_path_as_mutable_because(
+                err = self.infcx.tcx.cannot_borrow_path_as_mutable_because(
                     borrow_span,
                     &item_msg,
                     &reason,
@@ -242,7 +242,7 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> {
                 elem: ProjectionElem::Field(upvar_index, _),
             }) => {
                 debug_assert!(is_closure_or_generator(
-                    base.ty(self.mir, self.tcx).to_ty(self.tcx)
+                    base.ty(self.mir, self.infcx.tcx).to_ty(self.infcx.tcx)
                 ));
 
                 err.span_label(span, format!("cannot {ACT}", ACT = act));
@@ -250,8 +250,8 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> {
                 let upvar_hir_id = self.mir.upvar_decls[upvar_index.index()]
                     .var_hir_id
                     .assert_crate_local();
-                let upvar_node_id = self.tcx.hir.hir_to_node_id(upvar_hir_id);
-                if let Some(Node::Binding(pat)) = self.tcx.hir.find(upvar_node_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(
                         hir::BindingAnnotation::Unannotated,
                         _,
@@ -274,7 +274,7 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> {
             // a local variable, then just suggest the user remove it.
             Place::Local(_)
                 if {
-                    if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) {
+                    if let Ok(snippet) = self.infcx.tcx.sess.source_map().span_to_snippet(span) {
                         snippet.starts_with("&mut ")
                     } else {
                         false
@@ -317,7 +317,7 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> {
                 let local_decl = &self.mir.local_decls[*local];
                 let suggestion = match local_decl.is_user_variable.as_ref().unwrap() {
                     ClearCrossCrate::Set(mir::BindingForm::ImplicitSelf) => {
-                        Some(suggest_ampmut_self(self.tcx, local_decl))
+                        Some(suggest_ampmut_self(self.infcx.tcx, local_decl))
                     }
 
                     ClearCrossCrate::Set(mir::BindingForm::Var(mir::VarBindingForm {
@@ -325,7 +325,7 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> {
                         opt_ty_info,
                         ..
                     })) => Some(suggest_ampmut(
-                        self.tcx,
+                        self.infcx.tcx,
                         self.mir,
                         *local,
                         local_decl,
@@ -337,7 +337,7 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> {
                         ..
                     })) => {
                         let pattern_span = local_decl.source_info.span;
-                        suggest_ref_mut(self.tcx, pattern_span)
+                        suggest_ref_mut(self.infcx.tcx, pattern_span)
                             .map(|replacement| (pattern_span, replacement))
                     }
 
@@ -426,11 +426,11 @@ impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> {
                                 ..
                             }
                         ) = &self.mir.basic_blocks()[location.block].terminator {
-                            if self.tcx.parent(id) == self.tcx.lang_items().index_trait() {
-
+                            let index_trait = self.infcx.tcx.lang_items().index_trait();
+                            if self.infcx.tcx.parent(id) == index_trait {
                                 let mut found = false;
-                                self.tcx.for_each_relevant_impl(
-                                    self.tcx.lang_items().index_mut_trait().unwrap(),
+                                self.infcx.tcx.for_each_relevant_impl(
+                                    self.infcx.tcx.lang_items().index_mut_trait().unwrap(),
                                     substs.type_at(0),
                                     |_relevant_impl| {
                                         found = true;
diff --git a/src/librustc_mir/borrow_check/nll/explain_borrow/find_use.rs b/src/librustc_mir/borrow_check/nll/explain_borrow/find_use.rs
index a35117f2e35..d4adff7c443 100644
--- a/src/librustc_mir/borrow_check/nll/explain_borrow/find_use.rs
+++ b/src/librustc_mir/borrow_check/nll/explain_borrow/find_use.rs
@@ -121,9 +121,7 @@ struct DefUseVisitor<'cx, 'gcx: 'tcx, 'tcx: 'cx> {
 
 enum DefUseResult {
     Def,
-
     UseLive { local: Local },
-
     UseDrop { local: Local },
 }
 
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 61972629e7b..755148b6992 100644
--- a/src/librustc_mir/borrow_check/nll/explain_borrow/mod.rs
+++ b/src/librustc_mir/borrow_check/nll/explain_borrow/mod.rs
@@ -11,26 +11,71 @@
 use borrow_check::borrow_set::BorrowData;
 use borrow_check::nll::region_infer::Cause;
 use borrow_check::{Context, MirBorrowckCtxt, WriteKind};
-use rustc::mir::{FakeReadCause, Local, Location, Place, TerminatorKind};
+use rustc::ty::{Region, TyCtxt};
+use rustc::mir::{FakeReadCause, Location, Place, TerminatorKind};
 use rustc_errors::DiagnosticBuilder;
-use rustc::ty::Region;
+use syntax_pos::Span;
+use syntax_pos::symbol::Symbol;
 
 mod find_use;
 
-#[derive(Copy, Clone, Debug)]
-pub enum BorrowContainsPointReason<'tcx> {
-    Liveness {
-        local: Local,
-        location: Location,
-        in_loop: bool,
-    },
-    DropLiveness {
-        local: Local,
-        location: Location,
-    },
-    OutlivesFreeRegion {
-        outlived_region: Option<Region<'tcx>>,
-    },
+pub(in borrow_check) enum BorrowExplanation<'tcx> {
+    UsedLater(bool, Option<FakeReadCause>, Span),
+    UsedLaterInLoop(bool, Span),
+    UsedLaterWhenDropped(Span, Symbol, bool),
+    MustBeValidFor(Region<'tcx>),
+    Unexplained,
+}
+
+impl<'tcx> BorrowExplanation<'tcx> {
+    pub(in borrow_check) fn emit<'cx, 'gcx>(
+        &self,
+        tcx: TyCtxt<'cx, 'gcx, 'tcx>,
+        err: &mut DiagnosticBuilder<'_>
+    ) {
+        match *self {
+            BorrowExplanation::UsedLater(is_in_closure, fake_read_cause, var_or_use_span) => {
+                let message = if is_in_closure {
+                    "borrow later captured here by closure"
+                } else if let Some(FakeReadCause::ForLet) = fake_read_cause {
+                    "borrow later stored here"
+                } else {
+                    "borrow later used here"
+                };
+                err.span_label(var_or_use_span, message);
+            },
+            BorrowExplanation::UsedLaterInLoop(is_in_closure, var_or_use_span) => {
+                let message = if is_in_closure {
+                    "borrow captured here by closure in later iteration of loop"
+                } else {
+                    "borrow used here in later iteration of loop"
+                };
+                err.span_label(var_or_use_span, message);
+            },
+            BorrowExplanation::UsedLaterWhenDropped(span, local_name, should_note_order) => {
+                err.span_label(
+                    span,
+                    format!("borrow later used here, when `{}` is dropped", local_name),
+                );
+
+                if should_note_order {
+                    err.note(
+                        "values in a scope are dropped \
+                         in the opposite order they are defined",
+                    );
+                }
+            },
+            BorrowExplanation::MustBeValidFor(region) => {
+                tcx.note_and_explain_free_region(
+                    err,
+                    "borrowed value must be valid for ",
+                    region,
+                    "...",
+                );
+            },
+            _ => {},
+        }
+    }
 }
 
 impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
@@ -53,23 +98,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
         context: Context,
         borrow: &BorrowData<'tcx>,
         kind_place: Option<(WriteKind, &Place<'tcx>)>,
-        err: &mut DiagnosticBuilder<'_>,
-    ) {
-        let reason = self.find_why_borrow_contains_point(context, borrow);
-        self.report_why_borrow_contains_point(err, reason, kind_place);
-    }
-
-    /// Finds the reason that [explain_why_borrow_contains_point] will report
-    /// but doesn't add it to any message. This is a separate function in case
-    /// the caller wants to change the error they report based on the reason
-    /// that will be reported.
-    pub(in borrow_check) fn find_why_borrow_contains_point(
-        &self,
-        context: Context,
-        borrow: &BorrowData<'tcx>
-    ) -> BorrowContainsPointReason<'tcx> {
-        use self::BorrowContainsPointReason::*;
-
+    ) -> BorrowExplanation<'tcx> {
         debug!(
             "find_why_borrow_contains_point(context={:?}, borrow={:?})",
             context, borrow,
@@ -77,116 +106,70 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
 
         let regioncx = &self.nonlexical_regioncx;
         let mir = self.mir;
-        let tcx = self.tcx;
+        let tcx = self.infcx.tcx;
 
         let borrow_region_vid = regioncx.to_region_vid(borrow.region);
-
         debug!(
             "explain_why_borrow_contains_point: borrow_region_vid={:?}",
             borrow_region_vid
         );
 
         let region_sub = regioncx.find_sub_region_live_at(borrow_region_vid, context.loc);
-
         debug!(
             "explain_why_borrow_contains_point: region_sub={:?}",
             region_sub
         );
 
-        match find_use::find(mir, regioncx, tcx, region_sub, context.loc) {
-            Some(Cause::LiveVar(local, location)) => Liveness {
-                local,
-                location,
-                in_loop: self.is_borrow_location_in_loop(context.loc),
-            },
-            Some(Cause::DropVar(local, location)) => DropLiveness {
-                local,
-                location,
-            },
-            None => OutlivesFreeRegion {
-                outlived_region: regioncx.to_error_region(region_sub),
-            },
-        }
-    }
-
-    /// Adds annotations to `err` for the explanation `reason`. This is a
-    /// separate method so that the caller can change their error message based
-    /// on the reason that is going to be reported.
-    pub (in borrow_check) fn report_why_borrow_contains_point(
-        &self,
-        err: &mut DiagnosticBuilder,
-        reason: BorrowContainsPointReason<'tcx>,
-        kind_place: Option<(WriteKind, &Place<'tcx>)>,
-    ) {
-        use self::BorrowContainsPointReason::*;
-
-        debug!(
-            "find_why_borrow_contains_point(reason={:?}, kind_place={:?})",
-            reason, kind_place,
-        );
-
-        let mir = self.mir;
-
-        match reason {
-            Liveness { local, location, in_loop } => {
+         match find_use::find(mir, regioncx, tcx, region_sub, context.loc) {
+            Some(Cause::LiveVar(local, location)) => {
                 let span = mir.source_info(location).span;
                 let spans = self.move_spans(&Place::Local(local), location)
                     .or_else(|| self.borrow_spans(span, location));
-                let message = if in_loop {
-                    if spans.for_closure() {
-                        "borrow captured here by closure in later iteration of loop"
-                    } else {
-                        "borrow used here in later iteration of loop"
-                    }
+
+                if self.is_borrow_location_in_loop(context.loc) {
+                    BorrowExplanation::UsedLaterInLoop(spans.for_closure(), spans.var_or_use())
                 } else {
-                    if spans.for_closure() {
-                        "borrow later captured here by closure"
-                    } else {
-                        // Check if the location represents a `FakeRead`, and adapt the error
-                        // message to the `FakeReadCause` it is from: in particular,
-                        // the ones inserted in optimized `let var = <expr>` patterns.
-                        match self.retrieve_fake_read_cause_for_location(&location) {
-                            Some(FakeReadCause::ForLet) => "borrow later stored here",
-                            _ => "borrow later used here"
-                        }
-                    }
-                };
-                err.span_label(spans.var_or_use(), message);
+                    // Check if the location represents a `FakeRead`, and adapt the error
+                    // message to the `FakeReadCause` it is from: in particular,
+                    // the ones inserted in optimized `let var = <expr>` patterns.
+                    BorrowExplanation::UsedLater(
+                        spans.for_closure(),
+                        self.retrieve_fake_read_cause_for_location(&location),
+                        spans.var_or_use()
+                    )
+                }
             }
-            DropLiveness { local, location } => match &mir.local_decls[local].name {
-                Some(local_name) => {
-                    err.span_label(
-                        mir.source_info(location).span,
-                        format!("borrow later used here, when `{}` is dropped", local_name),
-                    );
 
+            Some(Cause::DropVar(local, location)) => match &mir.local_decls[local].name {
+                Some(local_name) => {
+                    let mut should_note_order = false;
                     if let Some((WriteKind::StorageDeadOrDrop(_), place)) = kind_place {
                         if let Place::Local(borrowed_local) = place {
                             let dropped_local_scope = mir.local_decls[local].visibility_scope;
                             let borrowed_local_scope =
-                            mir.local_decls[*borrowed_local].visibility_scope;
+                                mir.local_decls[*borrowed_local].visibility_scope;
 
                             if mir.is_sub_scope(borrowed_local_scope, dropped_local_scope) {
-                                err.note(
-                                "values in a scope are dropped \
-                                                     in the opposite order they are defined",
-                                );
+                                should_note_order = true;
                             }
                         }
                     }
-                }
 
-                None => {}
-            }
-            OutlivesFreeRegion { outlived_region: Some(region) } => {
-                self.tcx.note_and_explain_free_region(
-                    err,
-                    "borrowed value must be valid for ",
-                    region,
-                    "...",
-                );
-            }
-            OutlivesFreeRegion { outlived_region: None } => (),
+                    BorrowExplanation::UsedLaterWhenDropped(
+                        mir.source_info(location).span,
+                        *local_name,
+                        should_note_order
+                    )
+                },
+
+                None => BorrowExplanation::Unexplained,
+            },
+
+            None => if let Some(region) = regioncx.to_error_region(region_sub) {
+                BorrowExplanation::MustBeValidFor(region)
+            } else {
+                BorrowExplanation::Unexplained
+            },
         }
     }
 
@@ -262,4 +245,3 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
         false
     }
 }
-
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 d287116029a..31d7c7c631e 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
@@ -339,10 +339,11 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         );
 
         let counter = &mut 1;
-        let fr_name = self.give_region_a_name(
-            infcx, mir, mir_def_id, fr, counter, &mut diag);
+        let fr_name = self.give_region_a_name(infcx, mir, mir_def_id, fr, counter);
+        fr_name.highlight_region_name(&mut diag);
         let outlived_fr_name = self.give_region_a_name(
-            infcx, mir, mir_def_id, outlived_fr, counter, &mut diag);
+            infcx, mir, mir_def_id, outlived_fr, counter);
+        outlived_fr_name.highlight_region_name(&mut diag);
 
         let mir_def_name = if infcx.tcx.is_closure(mir_def_id) { "closure" } else { "function" };
 
@@ -430,10 +431,12 @@ impl<'tcx> RegionInferenceContext<'tcx> {
                     // Otherwise, we should suggest adding a constraint on the return type.
                     let span = infcx.tcx.def_span(*did);
                     if let Ok(snippet) = infcx.tcx.sess.source_map().span_to_snippet(span) {
-                        let suggestable_fr_name = match fr_name {
-                            RegionName::Named(name) => format!("{}", name),
-                            RegionName::Synthesized(_) => "'_".to_string(),
+                        let suggestable_fr_name = if fr_name.was_named() {
+                            format!("{}", fr_name)
+                        } else {
+                            "'_".to_string()
                         };
+
                         diag.span_suggestion_with_applicability(
                             span,
                             &format!(
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 f704ee61c88..be05e006608 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
@@ -18,36 +18,109 @@ use rustc::infer::InferCtxt;
 use rustc::mir::Mir;
 use rustc::ty::subst::{Substs, UnpackedKind};
 use rustc::ty::{self, RegionKind, RegionVid, Ty, TyCtxt};
-use rustc::util::ppaux::with_highlight_region;
+use rustc::util::ppaux::with_highlight_region_for_regionvid;
 use rustc_errors::DiagnosticBuilder;
 use syntax::ast::{Name, DUMMY_NODE_ID};
 use syntax::symbol::keywords;
 use syntax_pos::Span;
 use syntax_pos::symbol::InternedString;
 
-/// Name of a region used in error reporting. Variants denote the source of the region name -
-/// whether it was synthesized for the error message and therefore should not be used in
-/// suggestions; or whether it was found from the region.
 #[derive(Debug)]
-pub(crate) enum RegionName {
-    Named(InternedString),
-    Synthesized(InternedString),
+crate struct RegionName {
+    name: InternedString,
+    source: RegionNameSource,
+}
+
+#[derive(Debug)]
+crate enum RegionNameSource {
+    NamedEarlyBoundRegion(Span),
+    NamedFreeRegion(Span),
+    Static,
+    SynthesizedFreeEnvRegion(Span, String),
+    CannotMatchHirTy(Span, String),
+    MatchedHirTy(Span),
+    MatchedAdtAndSegment(Span),
+    AnonRegionFromUpvar(Span, String),
+    AnonRegionFromOutput(Span, String, String),
 }
 
 impl RegionName {
-    fn as_interned_string(&self) -> &InternedString {
-        match self {
-            RegionName::Named(name) | RegionName::Synthesized(name) => name,
+    #[allow(dead_code)]
+    crate fn was_named(&self) -> bool {
+        match self.source {
+            RegionNameSource::NamedEarlyBoundRegion(..) |
+            RegionNameSource::NamedFreeRegion(..) |
+            RegionNameSource::Static => true,
+            RegionNameSource::SynthesizedFreeEnvRegion(..) |
+            RegionNameSource::CannotMatchHirTy(..) |
+            RegionNameSource::MatchedHirTy(..) |
+            RegionNameSource::MatchedAdtAndSegment(..) |
+            RegionNameSource::AnonRegionFromUpvar(..) |
+            RegionNameSource::AnonRegionFromOutput(..) => false,
+        }
+    }
+
+    #[allow(dead_code)]
+    crate fn was_synthesized(&self) -> bool {
+        !self.was_named()
+    }
+
+    #[allow(dead_code)]
+    crate fn name(&self) -> &InternedString {
+        &self.name
+    }
+
+    crate fn highlight_region_name(
+        &self,
+        diag: &mut DiagnosticBuilder<'_>
+    ) {
+        match &self.source {
+            RegionNameSource::NamedFreeRegion(span) |
+            RegionNameSource::NamedEarlyBoundRegion(span) => {
+                diag.span_label(
+                    *span,
+                    format!("lifetime `{}` defined here", self),
+                );
+            },
+            RegionNameSource::SynthesizedFreeEnvRegion(span, note) => {
+                diag.span_label(
+                    *span,
+                    format!("lifetime `{}` represents this closure's body", self),
+                );
+                diag.note(&note);
+            },
+            RegionNameSource::CannotMatchHirTy(span, type_name) => {
+                diag.span_label(*span, format!("has type `{}`", type_name));
+            },
+            RegionNameSource::MatchedHirTy(span) => {
+                diag.span_label(
+                    *span,
+                    format!("let's call the lifetime of this reference `{}`", self),
+                );
+            },
+            RegionNameSource::MatchedAdtAndSegment(span) => {
+                diag.span_label(*span, format!("let's call this `{}`", self));
+            },
+            RegionNameSource::AnonRegionFromUpvar(span, upvar_name) => {
+                diag.span_label(
+                    *span,
+                    format!("lifetime `{}` appears in the type of `{}`", self, upvar_name),
+                );
+            },
+            RegionNameSource::AnonRegionFromOutput(span, mir_description, type_name) => {
+                diag.span_label(
+                    *span,
+                    format!("return type{} is {}", mir_description, type_name),
+                );
+            },
+            RegionNameSource::Static => {},
         }
     }
 }
 
 impl Display for RegionName {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        match self {
-            RegionName::Named(name) | RegionName::Synthesized(name) =>
-                write!(f, "{}", name),
-        }
+        write!(f, "{}", self.name)
     }
 }
 
@@ -84,26 +157,25 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         mir_def_id: DefId,
         fr: RegionVid,
         counter: &mut usize,
-        diag: &mut DiagnosticBuilder,
     ) -> RegionName {
         debug!("give_region_a_name(fr={:?}, counter={})", fr, counter);
 
         assert!(self.universal_regions.is_universal_region(fr));
 
-        let value = self.give_name_from_error_region(infcx.tcx, mir_def_id, fr, counter, diag)
+        let value = self.give_name_from_error_region(infcx.tcx, mir_def_id, fr, counter)
             .or_else(|| {
                 self.give_name_if_anonymous_region_appears_in_arguments(
-                    infcx, mir, mir_def_id, fr, counter, diag,
+                    infcx, mir, mir_def_id, fr, counter,
                 )
             })
             .or_else(|| {
                 self.give_name_if_anonymous_region_appears_in_upvars(
-                    infcx.tcx, mir, fr, counter, diag,
+                    infcx.tcx, mir, fr, counter,
                 )
             })
             .or_else(|| {
                 self.give_name_if_anonymous_region_appears_in_output(
-                    infcx, mir, mir_def_id, fr, counter, diag,
+                    infcx, mir, mir_def_id, fr, counter,
                 )
             })
             .unwrap_or_else(|| span_bug!(mir.span, "can't make a name for free region {:?}", fr));
@@ -122,7 +194,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         mir_def_id: DefId,
         fr: RegionVid,
         counter: &mut usize,
-        diag: &mut DiagnosticBuilder<'_>,
     ) -> Option<RegionName> {
         let error_region = self.to_error_region(fr)?;
 
@@ -130,23 +201,28 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         match error_region {
             ty::ReEarlyBound(ebr) => {
                 if ebr.has_name() {
-                    let name = RegionName::Named(ebr.name);
-                    self.highlight_named_span(tcx, error_region, &name, diag);
-                    Some(name)
+                    let span = self.get_named_span(tcx, error_region, &ebr.name);
+                    Some(RegionName {
+                        name: ebr.name,
+                        source: RegionNameSource::NamedEarlyBoundRegion(span)
+                    })
                 } else {
                     None
                 }
             }
 
-            ty::ReStatic => Some(RegionName::Named(
-                keywords::StaticLifetime.name().as_interned_str()
-            )),
+            ty::ReStatic => Some(RegionName {
+                name: keywords::StaticLifetime.name().as_interned_str(),
+                source: RegionNameSource::Static
+            }),
 
             ty::ReFree(free_region) => match free_region.bound_region {
                 ty::BoundRegion::BrNamed(_, name) => {
-                    let name = RegionName::Named(name);
-                    self.highlight_named_span(tcx, error_region, &name, diag);
-                    Some(name)
+                    let span = self.get_named_span(tcx, error_region, &name);
+                    Some(RegionName {
+                        name,
+                        source: RegionNameSource::NamedFreeRegion(span),
+                    })
                 },
 
                 ty::BoundRegion::BrEnv => {
@@ -162,13 +238,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
                             bug!("Closure is not defined by a closure expr");
                         };
                         let region_name = self.synthesize_region_name(counter);
-                        diag.span_label(
-                            args_span,
-                            format!(
-                                "lifetime `{}` represents this closure's body",
-                                region_name
-                            ),
-                        );
 
                         let closure_kind_ty = substs.closure_kind_ty(def_id, tcx);
                         let note = match closure_kind_ty.to_opt_closure_kind() {
@@ -186,9 +255,13 @@ impl<'tcx> RegionInferenceContext<'tcx> {
                             None => bug!("Closure kind not inferred in borrow check"),
                         };
 
-                        diag.note(note);
-
-                        Some(region_name)
+                        Some(RegionName {
+                            name: region_name,
+                            source: RegionNameSource::SynthesizedFreeEnvRegion(
+                                args_span,
+                                note.to_string()
+                            ),
+                        })
                     } else {
                         // Can't have BrEnv in functions, constants or generators.
                         bug!("BrEnv outside of closure.");
@@ -209,27 +282,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         }
     }
 
-    /// Get the span of a named region.
-    pub(super) fn get_span_of_named_region(
-        &self,
-        tcx: TyCtxt<'_, '_, 'tcx>,
-        error_region: &RegionKind,
-        name: &RegionName,
-    ) -> Span {
-        let scope = error_region.free_region_binding_scope(tcx);
-        let node = tcx.hir.as_local_node_id(scope).unwrap_or(DUMMY_NODE_ID);
-
-        let span = tcx.sess.source_map().def_span(tcx.hir.span(node));
-        if let Some(param) = tcx.hir.get_generics(scope).and_then(|generics| {
-            generics.get_named(name.as_interned_string())
-        }) {
-            param.span
-        } else {
-            span
-        }
-    }
-
-    /// Highlight a named span to provide context for error messages that
+    /// Get a span of a named region to provide context for error messages that
     /// mention that span, for example:
     ///
     /// ```
@@ -243,19 +296,24 @@ impl<'tcx> RegionInferenceContext<'tcx> {
     ///  |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ argument requires that `'b` must
     ///  |                                                         outlive `'a`
     /// ```
-    fn highlight_named_span(
+    fn get_named_span(
         &self,
         tcx: TyCtxt<'_, '_, 'tcx>,
         error_region: &RegionKind,
-        name: &RegionName,
-        diag: &mut DiagnosticBuilder<'_>,
-    ) {
-        let span = self.get_span_of_named_region(tcx, error_region, name);
+        name: &InternedString,
+    ) -> Span {
+        let scope = error_region.free_region_binding_scope(tcx);
+        let node = tcx.hir.as_local_node_id(scope).unwrap_or(DUMMY_NODE_ID);
 
-        diag.span_label(
-            span,
-            format!("lifetime `{}` defined here", name),
-        );
+        let span = tcx.sess.source_map().def_span(tcx.hir.span(node));
+        if let Some(param) = tcx.hir
+            .get_generics(scope)
+            .and_then(|generics| generics.get_named(name))
+        {
+            param.span
+        } else {
+            span
+        }
     }
 
     /// Find an argument that contains `fr` and label it with a fully
@@ -273,7 +331,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         mir_def_id: DefId,
         fr: RegionVid,
         counter: &mut usize,
-        diag: &mut DiagnosticBuilder<'_>,
     ) -> Option<RegionName> {
         let implicit_inputs = self.universal_regions.defining_ty.implicit_inputs();
         let argument_index = self.get_argument_index_for_region(infcx.tcx, fr)?;
@@ -288,12 +345,11 @@ impl<'tcx> RegionInferenceContext<'tcx> {
             arg_ty,
             argument_index,
             counter,
-            diag,
         ) {
             return Some(region_name);
         }
 
-        self.give_name_if_we_cannot_match_hir_ty(infcx, mir, fr, arg_ty, counter, diag)
+        self.give_name_if_we_cannot_match_hir_ty(infcx, mir, fr, arg_ty, counter)
     }
 
     fn give_name_if_we_can_match_hir_ty_from_argument(
@@ -305,7 +361,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         argument_ty: Ty<'tcx>,
         argument_index: usize,
         counter: &mut usize,
-        diag: &mut DiagnosticBuilder<'_>,
     ) -> Option<RegionName> {
         let mir_node_id = infcx.tcx.hir.as_local_node_id(mir_def_id)?;
         let fn_decl = infcx.tcx.hir.fn_decl(mir_node_id)?;
@@ -320,7 +375,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
                 needle_fr,
                 argument_ty,
                 counter,
-                diag,
             ),
 
             _ => self.give_name_if_we_can_match_hir_ty(
@@ -329,7 +383,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
                 argument_ty,
                 argument_hir_ty,
                 counter,
-                diag,
             ),
         }
     }
@@ -352,9 +405,8 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         needle_fr: RegionVid,
         argument_ty: Ty<'tcx>,
         counter: &mut usize,
-        diag: &mut DiagnosticBuilder<'_>,
     ) -> Option<RegionName> {
-        let type_name = with_highlight_region(needle_fr, *counter, || {
+        let type_name = with_highlight_region_for_regionvid(needle_fr, *counter, || {
             infcx.extract_type_name(&argument_ty)
         });
 
@@ -366,12 +418,14 @@ impl<'tcx> RegionInferenceContext<'tcx> {
             // Only add a label if we can confirm that a region was labelled.
             let argument_index = self.get_argument_index_for_region(infcx.tcx, needle_fr)?;
             let (_, span) = self.get_argument_name_and_span_for_region(mir, argument_index);
-            diag.span_label(span, format!("has type `{}`", type_name));
 
-            // This counter value will already have been used, so this function will increment it
-            // so the next value will be used next and return the region name that would have been
-            // used.
-            Some(self.synthesize_region_name(counter))
+            Some(RegionName {
+                // This counter value will already have been used, so this function will increment
+                // it so the next value will be used next and return the region name that would
+                // have been used.
+                name: self.synthesize_region_name(counter),
+                source: RegionNameSource::CannotMatchHirTy(span, type_name),
+            })
         } else {
             None
         };
@@ -407,7 +461,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         argument_ty: Ty<'tcx>,
         argument_hir_ty: &hir::Ty,
         counter: &mut usize,
-        diag: &mut DiagnosticBuilder<'_>,
     ) -> Option<RegionName> {
         let search_stack: &mut Vec<(Ty<'tcx>, &hir::Ty)> = &mut Vec::new();
 
@@ -432,15 +485,10 @@ impl<'tcx> RegionInferenceContext<'tcx> {
                         let source_map = tcx.sess.source_map();
                         let ampersand_span = source_map.start_point(hir_ty.span);
 
-                        diag.span_label(
-                            ampersand_span,
-                            format!(
-                                "let's call the lifetime of this reference `{}`",
-                                region_name
-                            ),
-                        );
-
-                        return Some(region_name);
+                        return Some(RegionName {
+                            name: region_name,
+                            source: RegionNameSource::MatchedHirTy(ampersand_span),
+                        });
                     }
 
                     // Otherwise, let's descend into the referent types.
@@ -464,7 +512,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
                                 needle_fr,
                                 last_segment,
                                 counter,
-                                diag,
                                 search_stack,
                             ) {
                                 return Some(name);
@@ -509,7 +556,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         needle_fr: RegionVid,
         last_segment: &'hir hir::PathSegment,
         counter: &mut usize,
-        diag: &mut DiagnosticBuilder<'_>,
         search_stack: &mut Vec<(Ty<'tcx>, &'hir hir::Ty)>,
     ) -> Option<RegionName> {
         // Did the user give explicit arguments? (e.g., `Foo<..>`)
@@ -521,11 +567,10 @@ impl<'tcx> RegionInferenceContext<'tcx> {
             | hir::LifetimeName::Underscore => {
                 let region_name = self.synthesize_region_name(counter);
                 let ampersand_span = lifetime.span;
-                diag.span_label(
-                    ampersand_span,
-                    format!("let's call this `{}`", region_name)
-                );
-                return Some(region_name);
+                return Some(RegionName {
+                    name: region_name,
+                    source: RegionNameSource::MatchedAdtAndSegment(ampersand_span),
+                });
             }
 
             hir::LifetimeName::Implicit => {
@@ -600,22 +645,16 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         mir: &Mir<'tcx>,
         fr: RegionVid,
         counter: &mut usize,
-        diag: &mut DiagnosticBuilder<'_>,
     ) -> 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);
         let region_name = self.synthesize_region_name(counter);
 
-        diag.span_label(
-            upvar_span,
-            format!(
-                "lifetime `{}` appears in the type of `{}`",
-                region_name, upvar_name
-            ),
-        );
-
-        Some(region_name)
+        Some(RegionName {
+            name: region_name,
+            source: RegionNameSource::AnonRegionFromUpvar(upvar_span, upvar_name.to_string()),
+        })
     }
 
     /// Check for arguments appearing in the (closure) return type. It
@@ -629,7 +668,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         mir_def_id: DefId,
         fr: RegionVid,
         counter: &mut usize,
-        diag: &mut DiagnosticBuilder<'_>,
     ) -> Option<RegionName> {
         let tcx = infcx.tcx;
 
@@ -645,7 +683,8 @@ impl<'tcx> RegionInferenceContext<'tcx> {
             return None;
         }
 
-        let type_name = with_highlight_region(fr, *counter, || infcx.extract_type_name(&return_ty));
+        let type_name = with_highlight_region_for_regionvid(
+            fr, *counter, || infcx.extract_type_name(&return_ty));
 
         let mir_node_id = tcx.hir.as_local_node_id(mir_def_id).expect("non-local mir");
 
@@ -666,23 +705,25 @@ impl<'tcx> RegionInferenceContext<'tcx> {
                 (mir.span, "")
             };
 
-        diag.span_label(
-            return_span,
-            format!("return type{} is {}", mir_description, type_name),
-        );
-
-        // This counter value will already have been used, so this function will increment it
-        // so the next value will be used next and return the region name that would have been
-        // used.
-        Some(self.synthesize_region_name(counter))
+        Some(RegionName {
+            // This counter value will already have been used, so this function will increment it
+            // so the next value will be used next and return the region name that would have been
+            // used.
+            name: self.synthesize_region_name(counter),
+            source: RegionNameSource::AnonRegionFromOutput(
+                return_span,
+                mir_description.to_string(),
+                type_name
+            ),
+        })
     }
 
     /// Create a synthetic region named `'1`, incrementing the
     /// counter.
-    fn synthesize_region_name(&self, counter: &mut usize) -> RegionName {
+    fn synthesize_region_name(&self, counter: &mut usize) -> InternedString {
         let c = *counter;
         *counter += 1;
 
-        RegionName::Synthesized(Name::intern(&format!("'{:?}", c)).as_interned_str())
+        Name::intern(&format!("'{:?}", c)).as_interned_str()
     }
 }
diff --git a/src/librustc_mir/borrow_check/prefixes.rs b/src/librustc_mir/borrow_check/prefixes.rs
index 8dcc1143306..f73e08eb135 100644
--- a/src/librustc_mir/borrow_check/prefixes.rs
+++ b/src/librustc_mir/borrow_check/prefixes.rs
@@ -79,7 +79,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
             next: Some(place),
             kind,
             mir: self.mir,
-            tcx: self.tcx,
+            tcx: self.infcx.tcx,
         }
     }
 }
diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs
index 7a13beb7852..d6cbe47a66e 100644
--- a/src/libsyntax/parse/parser.rs
+++ b/src/libsyntax/parse/parser.rs
@@ -1848,7 +1848,7 @@ impl<'a> Parser<'a> {
             P(Ty {
                 id: ast::DUMMY_NODE_ID,
                 node: TyKind::Infer,
-                span: self.span,
+                span: self.prev_span,
             })
         };
         Ok(Arg {
diff --git a/src/test/ui/issues/issue-30438-c.nll.stderr b/src/test/ui/issues/issue-30438-c.nll.stderr
index 28e63b2d36b..3d2c95013ab 100644
--- a/src/test/ui/issues/issue-30438-c.nll.stderr
+++ b/src/test/ui/issues/issue-30438-c.nll.stderr
@@ -1,17 +1,20 @@
 error[E0597]: `x` does not live long enough
   --> $DIR/issue-30438-c.rs:19:5
    |
+LL | fn silly<'y, 'z>(_s: &'y Test<'z>) -> &'y <Test<'z> as Trait>::Out where 'z: 'static {
+   |                       --               -- also has lifetime `'y`
+   |                       |
+   |                       has lifetime `'y`
+LL |     let x = Test { s: "this cannot last" };
 LL |     &x
-   |     ^^ borrowed value does not live long enough
+   |     ^^ `x` would have to be valid for `'y`...
 LL |     //~^ ERROR: `x` does not live long enough
 LL | }
-   | - `x` dropped here while still borrowed
-   |
-note: borrowed value must be valid for the lifetime 'y as defined on the function body at 17:10...
-  --> $DIR/issue-30438-c.rs:17:10
+   | - ...but `x` will be dropped here, when the function `silly` returns
    |
-LL | fn silly<'y, 'z>(_s: &'y Test<'z>) -> &'y <Test<'z> as Trait>::Out where 'z: 'static {
-   |          ^^
+   = help: use data from the highlighted arguments which match the `'y` lifetime of the return type
+   = note: functions cannot return a borrow to data owned within the function's scope, functions can only return borrows to data passed as arguments
+   = note: to learn more, visit <https://doc.rust-lang.org/book/second-edition/ch04-02-references-and-borrowing.html#dangling-references>
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/nll/borrowed-universal-error-2.stderr b/src/test/ui/nll/borrowed-universal-error-2.stderr
index 867e473af2c..c35a14ca704 100644
--- a/src/test/ui/nll/borrowed-universal-error-2.stderr
+++ b/src/test/ui/nll/borrowed-universal-error-2.stderr
@@ -1,17 +1,20 @@
 error[E0597]: `v` does not live long enough
   --> $DIR/borrowed-universal-error-2.rs:16:5
    |
+LL | fn foo<'a>(x: &'a (u32,)) -> &'a u32 {
+   |                --             -- also has lifetime `'a`
+   |                |
+   |                has lifetime `'a`
+LL |     let v = 22;
 LL |     &v
-   |     ^^ borrowed value does not live long enough
+   |     ^^ `v` would have to be valid for `'a`...
 LL |     //~^ ERROR `v` does not live long enough [E0597]
 LL | }
-   | - `v` dropped here while still borrowed
-   |
-note: borrowed value must be valid for the lifetime 'a as defined on the function body at 14:8...
-  --> $DIR/borrowed-universal-error-2.rs:14:8
+   | - ...but `v` will be dropped here, when the function `foo` returns
    |
-LL | fn foo<'a>(x: &'a (u32,)) -> &'a u32 {
-   |        ^^
+   = help: use data from the highlighted arguments which match the `'a` lifetime of the return type
+   = note: functions cannot return a borrow to data owned within the function's scope, functions can only return borrows to data passed as arguments
+   = note: to learn more, visit <https://doc.rust-lang.org/book/second-edition/ch04-02-references-and-borrowing.html#dangling-references>
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/nll/issue-52534-1.rs b/src/test/ui/nll/issue-52534-1.rs
new file mode 100644
index 00000000000..cd6c10335cc
--- /dev/null
+++ b/src/test/ui/nll/issue-52534-1.rs
@@ -0,0 +1,53 @@
+// Copyright 2018 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.
+
+#![feature(nll)]
+#![allow(warnings)]
+
+struct Test;
+
+impl Test {
+    fn bar(&self, x: &u32) -> &u32 {
+        let x = 22;
+        &x
+    }
+}
+
+fn foo(x: &u32) -> &u32 {
+    let x = 22;
+    &x
+}
+
+fn baz(x: &u32) -> &&u32 {
+    let x = 22;
+    &&x
+}
+
+fn foobazbar<'a>(x: u32, y: &'a u32) -> &'a u32 {
+    let x = 22;
+    &x
+}
+
+fn foobar<'a>(x: &'a u32) -> &'a u32 {
+    let x = 22;
+    &x
+}
+
+fn foobaz<'a, 'b>(x: &'a u32, y: &'b u32) -> &'a u32 {
+    let x = 22;
+    &x
+}
+
+fn foobarbaz<'a, 'b>(x: &'a u32, y: &'b u32, z: &'a u32) -> &'a u32 {
+    let x = 22;
+    &x
+}
+
+fn main() { }
diff --git a/src/test/ui/nll/issue-52534-1.stderr b/src/test/ui/nll/issue-52534-1.stderr
new file mode 100644
index 00000000000..e2f8134e3be
--- /dev/null
+++ b/src/test/ui/nll/issue-52534-1.stderr
@@ -0,0 +1,140 @@
+error[E0597]: `x` does not live long enough
+  --> $DIR/issue-52534-1.rs:19:9
+   |
+LL |     fn bar(&self, x: &u32) -> &u32 {
+   |            -----              ---- has type `&'0 u32`
+   |            |
+   |            has type `&'0 Test`
+LL |         let x = 22;
+LL |         &x
+   |         ^^ `x` would have to be valid for `'0`...
+LL |     }
+   |     - ...but `x` will be dropped here, when the function `bar` returns
+   |
+   = note: argument and return type have the same lifetime due to lifetime elision rules
+   = note: to learn more, visit <https://doc.rust-lang.org/book/second-edition/ch10-03-lifetime-syntax.html#lifetime-elision>
+   = note: functions cannot return a borrow to data owned within the function's scope, functions can only return borrows to data passed as arguments
+   = note: to learn more, visit <https://doc.rust-lang.org/book/second-edition/ch04-02-references-and-borrowing.html#dangling-references>
+
+error[E0597]: `x` does not live long enough
+  --> $DIR/issue-52534-1.rs:25:5
+   |
+LL | fn foo(x: &u32) -> &u32 {
+   |           ----     ---- also has type `&'0 u32`
+   |           |
+   |           has type `&'0 u32`
+LL |     let x = 22;
+LL |     &x
+   |     ^^ `x` would have to be valid for `'0`...
+LL | }
+   | - ...but `x` will be dropped here, when the function `foo` returns
+   |
+   = note: argument and return type have the same lifetime due to lifetime elision rules
+   = note: to learn more, visit <https://doc.rust-lang.org/book/second-edition/ch10-03-lifetime-syntax.html#lifetime-elision>
+   = note: functions cannot return a borrow to data owned within the function's scope, functions can only return borrows to data passed as arguments
+   = note: to learn more, visit <https://doc.rust-lang.org/book/second-edition/ch04-02-references-and-borrowing.html#dangling-references>
+
+error[E0597]: `x` does not live long enough
+  --> $DIR/issue-52534-1.rs:30:6
+   |
+LL | fn baz(x: &u32) -> &&u32 {
+   |           ----     ----- has type `&'0 &'0 u32`
+   |           |
+   |           has type `&'0 u32`
+LL |     let x = 22;
+LL |     &&x
+   |      ^^ `x` would have to be valid for `'0`...
+LL | }
+   | - ...but `x` will be dropped here, when the function `baz` returns
+   |
+   = note: argument and return type have the same lifetime due to lifetime elision rules
+   = note: to learn more, visit <https://doc.rust-lang.org/book/second-edition/ch10-03-lifetime-syntax.html#lifetime-elision>
+   = note: functions cannot return a borrow to data owned within the function's scope, functions can only return borrows to data passed as arguments
+   = note: to learn more, visit <https://doc.rust-lang.org/book/second-edition/ch04-02-references-and-borrowing.html#dangling-references>
+
+error[E0597]: borrowed value does not live long enough
+  --> $DIR/issue-52534-1.rs:30:6
+   |
+LL |     &&x
+   |      ^^ temporary value does not live long enough
+LL | }
+   | - temporary value only lives until here
+   |
+note: borrowed value must be valid for the anonymous lifetime #1 defined on the function body at 28:1...
+  --> $DIR/issue-52534-1.rs:28:1
+   |
+LL | / fn baz(x: &u32) -> &&u32 {
+LL | |     let x = 22;
+LL | |     &&x
+LL | | }
+   | |_^
+
+error[E0597]: `x` does not live long enough
+  --> $DIR/issue-52534-1.rs:35:5
+   |
+LL | fn foobazbar<'a>(x: u32, y: &'a u32) -> &'a u32 {
+   |                              --          -- also has lifetime `'a`
+   |                              |
+   |                              has lifetime `'a`
+LL |     let x = 22;
+LL |     &x
+   |     ^^ `x` would have to be valid for `'a`...
+LL | }
+   | - ...but `x` will be dropped here, when the function `foobazbar` returns
+   |
+   = help: use data from the highlighted arguments which match the `'a` lifetime of the return type
+   = note: functions cannot return a borrow to data owned within the function's scope, functions can only return borrows to data passed as arguments
+   = note: to learn more, visit <https://doc.rust-lang.org/book/second-edition/ch04-02-references-and-borrowing.html#dangling-references>
+
+error[E0597]: `x` does not live long enough
+  --> $DIR/issue-52534-1.rs:40:5
+   |
+LL | fn foobar<'a>(x: &'a u32) -> &'a u32 {
+   |                   --          -- also has lifetime `'a`
+   |                   |
+   |                   has lifetime `'a`
+LL |     let x = 22;
+LL |     &x
+   |     ^^ `x` would have to be valid for `'a`...
+LL | }
+   | - ...but `x` will be dropped here, when the function `foobar` returns
+   |
+   = help: use data from the highlighted arguments which match the `'a` lifetime of the return type
+   = note: functions cannot return a borrow to data owned within the function's scope, functions can only return borrows to data passed as arguments
+   = note: to learn more, visit <https://doc.rust-lang.org/book/second-edition/ch04-02-references-and-borrowing.html#dangling-references>
+
+error[E0597]: `x` does not live long enough
+  --> $DIR/issue-52534-1.rs:45:5
+   |
+LL | fn foobaz<'a, 'b>(x: &'a u32, y: &'b u32) -> &'a u32 {
+   |                       -- has lifetime `'a`    -- also has lifetime `'a`
+LL |     let x = 22;
+LL |     &x
+   |     ^^ `x` would have to be valid for `'a`...
+LL | }
+   | - ...but `x` will be dropped here, when the function `foobaz` returns
+   |
+   = help: use data from the highlighted arguments which match the `'a` lifetime of the return type
+   = note: functions cannot return a borrow to data owned within the function's scope, functions can only return borrows to data passed as arguments
+   = note: to learn more, visit <https://doc.rust-lang.org/book/second-edition/ch04-02-references-and-borrowing.html#dangling-references>
+
+error[E0597]: `x` does not live long enough
+  --> $DIR/issue-52534-1.rs:50:5
+   |
+LL | fn foobarbaz<'a, 'b>(x: &'a u32, y: &'b u32, z: &'a u32) -> &'a u32 {
+   |                          --                      --          -- also has lifetime `'a`
+   |                          |                       |
+   |                          has lifetime `'a`       has lifetime `'a`
+LL |     let x = 22;
+LL |     &x
+   |     ^^ `x` would have to be valid for `'a`...
+LL | }
+   | - ...but `x` will be dropped here, when the function `foobarbaz` returns
+   |
+   = help: use data from the highlighted arguments which match the `'a` lifetime of the return type
+   = note: functions cannot return a borrow to data owned within the function's scope, functions can only return borrows to data passed as arguments
+   = note: to learn more, visit <https://doc.rust-lang.org/book/second-edition/ch04-02-references-and-borrowing.html#dangling-references>
+
+error: aborting due to 8 previous errors
+
+For more information about this error, try `rustc --explain E0597`.
diff --git a/src/test/ui/nll/issue-52534-2.rs b/src/test/ui/nll/issue-52534-2.rs
new file mode 100644
index 00000000000..4eac6feac0d
--- /dev/null
+++ b/src/test/ui/nll/issue-52534-2.rs
@@ -0,0 +1,26 @@
+// Copyright 2018 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.
+
+#![feature(nll)]
+#![allow(warnings)]
+
+fn foo(x: &u32) -> &u32 {
+    let y;
+
+    {
+        let x = 32;
+        y = &x
+    }
+
+    println!("{}", y);
+    x
+}
+
+fn main() { }
diff --git a/src/test/ui/nll/issue-52534-2.stderr b/src/test/ui/nll/issue-52534-2.stderr
new file mode 100644
index 00000000000..51cd7c7bf3b
--- /dev/null
+++ b/src/test/ui/nll/issue-52534-2.stderr
@@ -0,0 +1,14 @@
+error[E0597]: `x` does not live long enough
+  --> $DIR/issue-52534-2.rs:19:9
+   |
+LL |         y = &x
+   |         ^^^^^^ borrowed value does not live long enough
+LL |     }
+   |     - `x` dropped here while still borrowed
+LL | 
+LL |     println!("{}", y);
+   |                    - borrow later used here
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0597`.
diff --git a/src/test/ui/nll/issue-52534.rs b/src/test/ui/nll/issue-52534.rs
new file mode 100644
index 00000000000..273c9b3c802
--- /dev/null
+++ b/src/test/ui/nll/issue-52534.rs
@@ -0,0 +1,30 @@
+// Copyright 2018 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.
+
+#![feature(nll)]
+#![allow(warnings)]
+
+fn foo(_: impl FnOnce(&u32) -> &u32) {
+}
+
+fn baz(_: impl FnOnce(&u32, u32) -> &u32) {
+}
+
+fn bar() {
+    let x = 22;
+    foo(|a| &x)
+}
+
+fn foobar() {
+    let y = 22;
+    baz(|first, second| &y)
+}
+
+fn main() { }
diff --git a/src/test/ui/nll/issue-52534.stderr b/src/test/ui/nll/issue-52534.stderr
new file mode 100644
index 00000000000..00d72546ebc
--- /dev/null
+++ b/src/test/ui/nll/issue-52534.stderr
@@ -0,0 +1,29 @@
+error[E0597]: `x` does not live long enough
+  --> $DIR/issue-52534.rs:22:14
+   |
+LL |     foo(|a| &x)
+   |          -   ^ `x` would have to be valid for `'0`...
+   |          |
+   |          has type `&'0 u32`
+LL | }
+   | - ...but `x` will be dropped here, when the function `bar` returns
+   |
+   = note: functions cannot return a borrow to data owned within the function's scope, functions can only return borrows to data passed as arguments
+   = note: to learn more, visit <https://doc.rust-lang.org/book/second-edition/ch04-02-references-and-borrowing.html#dangling-references>
+
+error[E0597]: `y` does not live long enough
+  --> $DIR/issue-52534.rs:27:26
+   |
+LL |     baz(|first, second| &y)
+   |          -----           ^ `y` would have to be valid for `'0`...
+   |          |
+   |          has type `&'0 u32`
+LL | }
+   | - ...but `y` will be dropped here, when the function `foobar` returns
+   |
+   = note: functions cannot return a borrow to data owned within the function's scope, functions can only return borrows to data passed as arguments
+   = note: to learn more, visit <https://doc.rust-lang.org/book/second-edition/ch04-02-references-and-borrowing.html#dangling-references>
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0597`.