about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2018-07-21 14:37:45 +0000
committerbors <bors@rust-lang.org>2018-07-21 14:37:45 +0000
commit942b384d61590c39eb635596de0370a823001817 (patch)
treec336f80f6cc2db5da6d2141794afc7158e5dd490
parent606713f37aadbdb314137df464f099130c2062bd (diff)
parenta06b2433fc2963dba4ef25deee6720c63ffcc03b (diff)
downloadrust-942b384d61590c39eb635596de0370a823001817.tar.gz
rust-942b384d61590c39eb635596de0370a823001817.zip
Auto merge of #52405 - matthewjasper:mutability-errors, r=pnkfelix
[NLL] Mutability errors

cc #51028
cc #51170
cc #46559
Closes #46629

* Better explain why the place is immutable ("immutable item" is gone)
* Distinguish &T and *const T
* Use better spans when a mutable borrow is for a closure capture

r? @pnkfelix
-rw-r--r--src/librustc/mir/mod.rs3
-rw-r--r--src/librustc_mir/borrow_check/error_reporting.rs20
-rw-r--r--src/librustc_mir/borrow_check/mod.rs229
-rw-r--r--src/librustc_mir/borrow_check/mutability_errors.rs469
-rw-r--r--src/librustc_mir/build/expr/as_rvalue.rs117
-rw-r--r--src/librustc_mir/build/matches/mod.rs2
-rw-r--r--src/librustc_mir/build/mod.rs100
-rw-r--r--src/librustc_mir/util/borrowck_errors.rs17
-rw-r--r--src/test/compile-fail/E0594.rs2
-rw-r--r--src/test/compile-fail/borrowck/borrowck-assign-to-constants.rs2
-rw-r--r--src/test/compile-fail/cannot-mutate-captured-non-mut-var.rs4
-rw-r--r--src/test/compile-fail/nll/constant-thread-locals-issue-47053.rs2
-rw-r--r--src/test/ui/augmented-assignments.nll.stderr2
-rw-r--r--src/test/ui/borrowck/issue-45983.nll.stderr15
-rw-r--r--src/test/ui/borrowck/mut-borrow-of-mut-ref.nll.stderr2
-rw-r--r--src/test/ui/borrowck/mutability-errors.nll.stderr379
-rw-r--r--src/test/ui/borrowck/mutability-errors.rs92
-rw-r--r--src/test/ui/borrowck/mutability-errors.stderr308
-rw-r--r--src/test/ui/closure-immutable-outer-variable.nll.stderr4
-rw-r--r--src/test/ui/codemap_tests/huge_multispan_highlight.nll.stderr2
-rw-r--r--src/test/ui/did_you_mean/issue-31424.nll.stderr4
-rw-r--r--src/test/ui/did_you_mean/issue-34126.nll.stderr2
-rw-r--r--src/test/ui/did_you_mean/issue-34337.nll.stderr2
-rw-r--r--src/test/ui/did_you_mean/issue-35937.nll.stderr2
-rw-r--r--src/test/ui/did_you_mean/issue-37139.nll.stderr2
-rw-r--r--src/test/ui/did_you_mean/issue-38147-1.nll.stderr2
-rw-r--r--src/test/ui/did_you_mean/issue-38147-2.nll.stderr2
-rw-r--r--src/test/ui/did_you_mean/issue-38147-3.nll.stderr2
-rw-r--r--src/test/ui/did_you_mean/issue-38147-4.nll.stderr2
-rw-r--r--src/test/ui/did_you_mean/issue-39544.nll.stderr22
-rw-r--r--src/test/ui/did_you_mean/issue-40823.nll.stderr2
-rw-r--r--src/test/ui/error-codes/E0017.nll.stderr2
-rw-r--r--src/test/ui/error-codes/E0388.nll.stderr2
-rw-r--r--src/test/ui/fn-closure-mutable-capture.nll.stderr8
-rw-r--r--src/test/ui/issue-21600.nll.stderr25
-rw-r--r--src/test/ui/issue-27282-reborrow-ref-mut-in-guard.rs2
-rw-r--r--src/test/ui/issue-27282-reborrow-ref-mut-in-guard.stderr10
-rw-r--r--src/test/ui/issue-33819.nll.stderr2
-rw-r--r--src/test/ui/issue-36400.nll.stderr2
-rw-r--r--src/test/ui/issue-41726.nll.stderr2
-rw-r--r--src/test/ui/issue-42344.nll.stderr2
-rw-r--r--src/test/ui/issue-46023.ast.nll.stderr5
-rw-r--r--src/test/ui/issue-46023.mir.stderr5
-rw-r--r--src/test/ui/issue-46023.rs2
-rw-r--r--src/test/ui/issue-46604.ast.nll.stderr2
-rw-r--r--src/test/ui/issue-46604.mir.stderr2
-rw-r--r--src/test/ui/lifetime-errors/ex3-both-anon-regions-using-fn-items.nll.stderr2
-rw-r--r--src/test/ui/lifetime-errors/ex3-both-anon-regions-using-trait-objects.nll.stderr2
-rw-r--r--src/test/ui/macros/span-covering-argument-1.nll.stderr2
-rw-r--r--src/test/ui/nll/closure-captures.rs68
-rw-r--r--src/test/ui/nll/closure-captures.stderr160
-rw-r--r--src/test/ui/nll/generator-upvar-mutability.stderr5
-rw-r--r--src/test/ui/span/borrowck-borrow-overloaded-auto-deref-mut.nll.stderr16
-rw-r--r--src/test/ui/span/borrowck-borrow-overloaded-deref-mut.nll.stderr8
-rw-r--r--src/test/ui/span/borrowck-call-is-borrow-issue-12224.nll.stderr4
-rw-r--r--src/test/ui/span/borrowck-call-method-from-mut-aliasable.nll.stderr2
-rw-r--r--src/test/ui/span/borrowck-fn-in-const-b.nll.stderr2
-rw-r--r--src/test/ui/span/borrowck-object-mutability.nll.stderr4
-rw-r--r--src/test/ui/span/mut-arg-hint.nll.stderr6
-rw-r--r--src/test/ui/trivial-bounds-inconsistent-copy-reborrow.nll.stderr4
60 files changed, 1822 insertions, 351 deletions
diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs
index f6076896385..458c2f3885f 100644
--- a/src/librustc/mir/mod.rs
+++ b/src/librustc/mir/mod.rs
@@ -531,6 +531,8 @@ pub enum BindingForm<'tcx> {
     Var(VarBindingForm<'tcx>),
     /// Binding for a `self`/`&self`/`&mut self` binding where the type is implicit.
     ImplicitSelf,
+    /// Reference used in a guard expression to ensure immutability.
+    RefForGuard,
 }
 
 CloneTypeFoldableAndLiftImpls! { BindingForm<'tcx>, }
@@ -555,6 +557,7 @@ mod binding_form_impl {
             match self {
                 Var(binding) => binding.hash_stable(hcx, hasher),
                 ImplicitSelf => (),
+                RefForGuard => (),
             }
         }
     }
diff --git a/src/librustc_mir/borrow_check/error_reporting.rs b/src/librustc_mir/borrow_check/error_reporting.rs
index c481d1d325b..3dca7768d70 100644
--- a/src/librustc_mir/borrow_check/error_reporting.rs
+++ b/src/librustc_mir/borrow_check/error_reporting.rs
@@ -202,7 +202,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
     /// the local assigned at `location`.
     /// This is done by searching in statements succeeding `location`
     /// and originating from `maybe_closure_span`.
-    fn find_closure_span(
+    pub(super) fn find_closure_span(
         &self,
         maybe_closure_span: Span,
         location: Location,
@@ -742,6 +742,24 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
                                     autoderef,
                                     &including_downcast,
                                 )?;
+                            } else if let Place::Local(local) = proj.base {
+                                if let Some(ClearCrossCrate::Set(BindingForm::RefForGuard))
+                                    = self.mir.local_decls[local].is_user_variable {
+                                    self.append_place_to_string(
+                                        &proj.base,
+                                        buf,
+                                        autoderef,
+                                        &including_downcast,
+                                    )?;
+                                } else {
+                                    buf.push_str(&"*");
+                                    self.append_place_to_string(
+                                        &proj.base,
+                                        buf,
+                                        autoderef,
+                                        &including_downcast,
+                                    )?;
+                                }
                             } else {
                                 buf.push_str(&"*");
                                 self.append_place_to_string(
diff --git a/src/librustc_mir/borrow_check/mod.rs b/src/librustc_mir/borrow_check/mod.rs
index e7f00b577b3..c212c1b826b 100644
--- a/src/librustc_mir/borrow_check/mod.rs
+++ b/src/librustc_mir/borrow_check/mod.rs
@@ -16,7 +16,7 @@ use rustc::hir::def_id::DefId;
 use rustc::hir::map::definitions::DefPathData;
 use rustc::infer::InferCtxt;
 use rustc::lint::builtin::UNUSED_MUT;
-use rustc::mir::{self, AggregateKind, BasicBlock, BorrowCheckResult, BorrowKind};
+use rustc::mir::{AggregateKind, BasicBlock, BorrowCheckResult, BorrowKind};
 use rustc::mir::{ClearCrossCrate, Local, Location, Mir, Mutability, Operand, Place};
 use rustc::mir::{Field, Projection, ProjectionElem, Rvalue, Statement, StatementKind};
 use rustc::mir::{Terminator, TerminatorKind};
@@ -43,14 +43,13 @@ use dataflow::{do_dataflow, DebugFormatted};
 use dataflow::{EverInitializedPlaces, MovingOutStatements};
 use dataflow::{MaybeInitializedPlaces, MaybeUninitializedPlaces};
 use util::borrowck_errors::{BorrowckErrors, Origin};
-use util::collect_writes::FindAssignments;
-use util::suggest_ref_mut;
 
 use self::borrow_set::{BorrowData, BorrowSet};
 use self::flows::Flows;
 use self::location::LocationTable;
 use self::prefixes::PrefixSet;
 use self::MutateMode::{JustWrite, WriteAndRead};
+use self::mutability_errors::AccessKind;
 
 use self::path_utils::*;
 
@@ -58,12 +57,13 @@ crate mod borrow_set;
 mod error_reporting;
 mod flows;
 mod location;
+mod move_errors;
+mod mutability_errors;
 mod path_utils;
 crate mod place_ext;
 mod places_conflict;
 mod prefixes;
 mod used_muts;
-mod move_errors;
 
 pub(crate) mod nll;
 
@@ -922,7 +922,13 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
         }
 
         let mutability_error =
-            self.check_access_permissions(place_span, rw, is_local_mutation_allowed, flow_state);
+            self.check_access_permissions(
+                place_span,
+                rw,
+                is_local_mutation_allowed,
+                flow_state,
+                context.loc,
+            );
         let conflict_error =
             self.check_access_for_conflict(context, place_span, sd, rw, flow_state);
 
@@ -1668,6 +1674,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
         }
     }
 
+
     /// Check the permissions for the given place and read or write kind
     ///
     /// Returns true if an error is reported, false otherwise.
@@ -1677,17 +1684,13 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
         kind: ReadOrWrite,
         is_local_mutation_allowed: LocalMutationIsAllowed,
         flow_state: &Flows<'cx, 'gcx, 'tcx>,
+        location: Location,
     ) -> bool {
         debug!(
             "check_access_permissions({:?}, {:?}, {:?})",
             place, kind, is_local_mutation_allowed
         );
 
-        #[derive(Copy, Clone, Debug)]
-        enum AccessKind {
-            MutableBorrow,
-            Mutate,
-        }
         let error_access;
         let the_place_err;
 
@@ -1756,206 +1759,14 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
         }
 
         // at this point, we have set up the error reporting state.
-
-        let mut err;
-        let item_msg = match self.describe_place(place) {
-            Some(name) => format!("immutable item `{}`", name),
-            None => "immutable item".to_owned(),
-        };
-
-        // `act` and `acted_on` are strings that let us abstract over
-        // the verbs used in some diagnostic messages.
-        let act;
-        let acted_on;
-
-        match error_access {
-            AccessKind::Mutate => {
-                let item_msg = match the_place_err {
-                    Place::Projection(box Projection {
-                        base: _,
-                        elem: ProjectionElem::Deref,
-                    }) => match self.describe_place(place) {
-                        Some(description) => {
-                            format!("`{}` which is behind a `&` reference", description)
-                        }
-                        None => format!("data in a `&` reference"),
-                    },
-                    _ => item_msg,
-                };
-                err = self.tcx.cannot_assign(span, &item_msg, Origin::Mir);
-                act = "assign";
-                acted_on = "written";
-            }
-            AccessKind::MutableBorrow => {
-                err = self
-                    .tcx
-                    .cannot_borrow_path_as_mutable(span, &item_msg, Origin::Mir);
-                act = "borrow as mutable";
-                acted_on = "borrowed as mutable";
-            }
-        }
-
-        match the_place_err {
-            // We want to suggest users use `let mut` for local (user
-            // variable) mutations...
-            Place::Local(local) if self.mir.local_decls[*local].can_be_made_mutable() => {
-                // ... but it doesn't make sense to suggest it on
-                // variables that are `ref x`, `ref mut x`, `&self`,
-                // or `&mut self` (such variables are simply not
-                // mutable)..
-                let local_decl = &self.mir.local_decls[*local];
-                assert_eq!(local_decl.mutability, Mutability::Not);
-
-                err.span_label(span, format!("cannot {ACT}", ACT = act));
-                err.span_suggestion(
-                    local_decl.source_info.span,
-                    "consider changing this to be mutable",
-                    format!("mut {}", local_decl.name.unwrap()),
-                );
-            }
-
-            // complete hack to approximate old AST-borrowck
-            // diagnostic: if the span starts with a mutable borrow of
-            // a local variable, then just suggest the user remove it.
-            Place::Local(_)
-                if {
-                    if let Ok(snippet) = self.tcx.sess.codemap().span_to_snippet(span) {
-                        snippet.starts_with("&mut ")
-                    } else {
-                        false
-                    }
-                } =>
-            {
-                err.span_label(span, format!("cannot {ACT}", ACT = act));
-                err.span_label(span, "try removing `&mut` here");
-            }
-
-            // We want to point out when a `&` can be readily replaced
-            // with an `&mut`.
-            //
-            // FIXME: can this case be generalized to work for an
-            // arbitrary base for the projection?
-            Place::Projection(box Projection {
-                base: Place::Local(local),
-                elem: ProjectionElem::Deref,
-            }) if self.mir.local_decls[*local].is_user_variable.is_some() => {
-                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(local_decl))
-                    },
-
-                    ClearCrossCrate::Set(mir::BindingForm::Var(mir::VarBindingForm {
-                        binding_mode: ty::BindingMode::BindByValue(_),
-                        opt_ty_info,
-                        ..
-                    })) => Some(suggest_ampmut(
-                        self.tcx,
-                        self.mir,
-                        *local,
-                        local_decl,
-                        *opt_ty_info,
-                    )),
-
-                    ClearCrossCrate::Set(mir::BindingForm::Var(mir::VarBindingForm {
-                        binding_mode: ty::BindingMode::BindByReference(_),
-                        ..
-                    })) => suggest_ref_mut(self.tcx, local_decl.source_info.span),
-
-                    ClearCrossCrate::Clear => bug!("saw cleared local state"),
-                };
-
-                if let Some((err_help_span, suggested_code)) = suggestion {
-                    err.span_suggestion(
-                        err_help_span,
-                        "consider changing this to be a mutable reference",
-                        suggested_code,
-                    );
-                }
-
-                if let Some(name) = local_decl.name {
-                    err.span_label(
-                        span,
-                        format!(
-                            "`{NAME}` is a `&` reference, \
-                             so the data it refers to cannot be {ACTED_ON}",
-                            NAME = name,
-                            ACTED_ON = acted_on
-                        ),
-                    );
-                } else {
-                    err.span_label(
-                        span,
-                        format!("cannot {ACT} through `&`-reference", ACT = act),
-                    );
-                }
-            }
-
-            _ => {
-                err.span_label(span, format!("cannot {ACT}", ACT = act));
-            }
-        }
-
-        err.emit();
+        self.report_mutability_error(
+            place,
+            span,
+            the_place_err,
+            error_access,
+            location,
+        );
         return true;
-
-        fn suggest_ampmut_self<'cx, 'gcx, 'tcx>(
-            local_decl: &mir::LocalDecl<'tcx>,
-        ) -> (Span, String) {
-            (local_decl.source_info.span, "&mut self".to_string())
-        }
-
-        // When we want to suggest a user change a local variable to be a `&mut`, there
-        // are three potential "obvious" things to highlight:
-        //
-        // let ident [: Type] [= RightHandSideExpression];
-        //     ^^^^^    ^^^^     ^^^^^^^^^^^^^^^^^^^^^^^
-        //     (1.)     (2.)              (3.)
-        //
-        // We can always fallback on highlighting the first. But chances are good that
-        // the user experience will be better if we highlight one of the others if possible;
-        // for example, if the RHS is present and the Type is not, then the type is going to
-        // be inferred *from* the RHS, which means we should highlight that (and suggest
-        // that they borrow the RHS mutably).
-        //
-        // This implementation attempts to emulate AST-borrowck prioritization
-        // by trying (3.), then (2.) and finally falling back on (1.).
-        fn suggest_ampmut<'cx, 'gcx, 'tcx>(
-            tcx: TyCtxt<'cx, 'gcx, 'tcx>,
-            mir: &Mir<'tcx>,
-            local: Local,
-            local_decl: &mir::LocalDecl<'tcx>,
-            opt_ty_info: Option<Span>,
-        ) -> (Span, String) {
-            let locations = mir.find_assignments(local);
-            if locations.len() > 0 {
-                let assignment_rhs_span = mir.source_info(locations[0]).span;
-                let snippet = tcx.sess.codemap().span_to_snippet(assignment_rhs_span);
-                if let Ok(src) = snippet {
-                    if src.starts_with('&') {
-                        let borrowed_expr = src[1..].to_string();
-                        return (
-                            assignment_rhs_span,
-                            format!("&mut {}", borrowed_expr),
-                        );
-                    }
-                }
-            }
-
-            let highlight_span = match opt_ty_info {
-                // if this is a variable binding with an explicit type,
-                // try to highlight that for the suggestion.
-                Some(ty_span) => ty_span,
-
-                // otherwise, just highlight the span associated with
-                // the (MIR) LocalDecl.
-                None => local_decl.source_info.span,
-            };
-
-            let ty_mut = local_decl.ty.builtin_deref(true).unwrap();
-            assert_eq!(ty_mut.mutbl, hir::MutImmutable);
-            (highlight_span, format!("&mut {}", ty_mut.ty))
-        }
     }
 
     /// Adds the place into the used mutable variables set
diff --git a/src/librustc_mir/borrow_check/mutability_errors.rs b/src/librustc_mir/borrow_check/mutability_errors.rs
new file mode 100644
index 00000000000..2a074a84e63
--- /dev/null
+++ b/src/librustc_mir/borrow_check/mutability_errors.rs
@@ -0,0 +1,469 @@
+// 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.
+
+use rustc::hir;
+use rustc::mir::{self, BindingForm, ClearCrossCrate, Local, Location, Mir};
+use rustc::mir::{Mutability, Place, Projection, ProjectionElem, Static};
+use rustc::ty::{self, TyCtxt};
+use rustc_data_structures::indexed_vec::Idx;
+use syntax_pos::Span;
+
+use borrow_check::MirBorrowckCtxt;
+use util::borrowck_errors::{BorrowckErrors, Origin};
+use util::collect_writes::FindAssignments;
+use util::suggest_ref_mut;
+
+#[derive(Copy, Clone, Debug)]
+pub(super) enum AccessKind {
+    MutableBorrow,
+    Mutate,
+}
+
+impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> {
+    pub(super) fn report_mutability_error(
+        &mut self,
+        access_place: &Place<'tcx>,
+        span: Span,
+        the_place_err: &Place<'tcx>,
+        error_access: AccessKind,
+        location: Location,
+    ) {
+        let mut err;
+        let item_msg;
+        let reason;
+        let access_place_desc = self.describe_place(access_place);
+
+        match the_place_err {
+            Place::Local(local) => {
+                item_msg = format!("`{}`", access_place_desc.unwrap());
+                if let Place::Local(_) = access_place {
+                    reason = ", as it is not declared as mutable".to_string();
+                } else {
+                    let name = self.mir.local_decls[*local]
+                        .name
+                        .expect("immutable unnamed local");
+                    reason = format!(", as `{}` is not declared as mutable", name);
+                }
+            }
+
+            Place::Projection(box Projection {
+                base,
+                elem: ProjectionElem::Field(upvar_index, _),
+            }) => {
+                debug_assert!(is_closure_or_generator(
+                    base.ty(self.mir, self.tcx).to_ty(self.tcx)
+                ));
+
+                item_msg = format!("`{}`", access_place_desc.unwrap());
+                if self.is_upvar(access_place) {
+                    reason = ", as it is not declared as mutable".to_string();
+                } else {
+                    let name = self.mir.upvar_decls[upvar_index.index()].debug_name;
+                    reason = format!(", as `{}` is not declared as mutable", name);
+                }
+            }
+
+            Place::Projection(box Projection {
+                base,
+                elem: ProjectionElem::Deref,
+            }) => {
+                if *base == Place::Local(Local::new(1)) && !self.mir.upvar_decls.is_empty() {
+                    item_msg = format!("`{}`", access_place_desc.unwrap());
+                    debug_assert!(self.mir.local_decls[Local::new(1)].ty.is_region_ptr());
+                    debug_assert!(is_closure_or_generator(
+                        the_place_err.ty(self.mir, self.tcx).to_ty(self.tcx)
+                    ));
+
+                    reason = if self.is_upvar(access_place) {
+                        ", as it is a captured variable in a `Fn` closure".to_string()
+                    } else {
+                        format!(", as `Fn` closures cannot mutate their captured variables")
+                    }
+                } else if {
+                    if let Place::Local(local) = *base {
+                        if let Some(ClearCrossCrate::Set(BindingForm::RefForGuard))
+                            = self.mir.local_decls[local].is_user_variable {
+                                true
+                        } else {
+                            false
+                        }
+                    } else {
+                        false
+                    }
+                } {
+                    item_msg = format!("`{}`", access_place_desc.unwrap());
+                    reason = format!(", as it is immutable for the pattern guard");
+                } else {
+                    let pointer_type =
+                        if base.ty(self.mir, self.tcx).to_ty(self.tcx).is_region_ptr() {
+                            "`&` reference"
+                        } else {
+                            "`*const` pointer"
+                        };
+                    if let Some(desc) = access_place_desc {
+                        item_msg = format!("`{}`", desc);
+                        reason = match error_access {
+                            AccessKind::Mutate => format!(" which is behind a {}", pointer_type),
+                            AccessKind::MutableBorrow => {
+                                format!(", as it is behind a {}", pointer_type)
+                            }
+                        }
+                    } else {
+                        item_msg = format!("data in a {}", pointer_type);
+                        reason = "".to_string();
+                    }
+                }
+            }
+
+            Place::Static(box Static { def_id, ty: _ }) => {
+                if let Place::Static(_) = access_place {
+                    item_msg = format!("immutable static item `{}`", access_place_desc.unwrap());
+                    reason = "".to_string();
+                } else {
+                    item_msg = format!("`{}`", access_place_desc.unwrap());
+                    let static_name = &self.tcx.item_name(*def_id);
+                    reason = format!(", as `{}` is an immutable static item", static_name);
+                }
+            }
+
+            Place::Projection(box Projection {
+                base: _,
+                elem: ProjectionElem::Index(_),
+            })
+            | Place::Projection(box Projection {
+                base: _,
+                elem: ProjectionElem::ConstantIndex { .. },
+            })
+            | Place::Projection(box Projection {
+                base: _,
+                elem: ProjectionElem::Subslice { .. },
+            })
+            | Place::Projection(box Projection {
+                base: _,
+                elem: ProjectionElem::Downcast(..),
+            }) => bug!("Unexpected immutable place."),
+        }
+
+        // `act` and `acted_on` are strings that let us abstract over
+        // the verbs used in some diagnostic messages.
+        let act;
+        let acted_on;
+
+
+        let span = match error_access {
+            AccessKind::Mutate => {
+                err = self.tcx
+                    .cannot_assign(span, &(item_msg + &reason), Origin::Mir);
+                act = "assign";
+                acted_on = "written";
+                span
+            }
+            AccessKind::MutableBorrow => {
+                act = "borrow as mutable";
+                acted_on = "borrowed as mutable";
+
+                let closure_span = self.find_closure_span(span, location);
+                if let Some((args, var)) = closure_span {
+                    err = self.tcx.cannot_borrow_path_as_mutable_because(
+                        args,
+                        &item_msg,
+                        &reason,
+                        Origin::Mir,
+                    );
+                    err.span_label(
+                        var,
+                        format!(
+                            "mutable borrow occurs due to use of `{}` in closure",
+                            self.describe_place(access_place).unwrap(),
+                        ),
+                    );
+                    args
+                } else {
+                    err = self.tcx.cannot_borrow_path_as_mutable_because(
+                        span,
+                        &item_msg,
+                        &reason,
+                        Origin::Mir,
+                    );
+                    span
+                }
+            }
+        };
+
+        match the_place_err {
+            // We want to suggest users use `let mut` for local (user
+            // variable) mutations...
+            Place::Local(local) if self.mir.local_decls[*local].can_be_made_mutable() => {
+                // ... but it doesn't make sense to suggest it on
+                // variables that are `ref x`, `ref mut x`, `&self`,
+                // or `&mut self` (such variables are simply not
+                // mutable).
+                let local_decl = &self.mir.local_decls[*local];
+                assert_eq!(local_decl.mutability, Mutability::Not);
+
+                err.span_label(span, format!("cannot {ACT}", ACT = act));
+                err.span_suggestion(
+                    local_decl.source_info.span,
+                    "consider changing this to be mutable",
+                    format!("mut {}", local_decl.name.unwrap()),
+                );
+            }
+
+            // Also suggest adding mut for upvars
+            Place::Projection(box Projection {
+                base,
+                elem: ProjectionElem::Field(upvar_index, _),
+            }) => {
+                debug_assert!(is_closure_or_generator(
+                    base.ty(self.mir, self.tcx).to_ty(self.tcx)
+                ));
+
+                err.span_label(span, format!("cannot {ACT}", ACT = act));
+
+                let upvar_hir_id = self.mir.upvar_decls[upvar_index.index()]
+                    .var_hir_id
+                    .assert_crate_local();
+                let upvar_node_id = self.tcx.hir.hir_to_node_id(upvar_hir_id);
+                if let Some(hir::map::NodeBinding(pat)) = self.tcx.hir.find(upvar_node_id) {
+                    if let hir::PatKind::Binding(
+                        hir::BindingAnnotation::Unannotated,
+                        _,
+                        upvar_ident,
+                        _,
+                    ) = pat.node
+                    {
+                        err.span_suggestion(
+                            upvar_ident.span,
+                            "consider changing this to be mutable",
+                            format!("mut {}", upvar_ident.name),
+                        );
+                    }
+                }
+            }
+
+            // complete hack to approximate old AST-borrowck
+            // diagnostic: if the span starts with a mutable borrow of
+            // a local variable, then just suggest the user remove it.
+            Place::Local(_)
+                if {
+                    if let Ok(snippet) = self.tcx.sess.codemap().span_to_snippet(span) {
+                        snippet.starts_with("&mut ")
+                    } else {
+                        false
+                    }
+                } =>
+            {
+                err.span_label(span, format!("cannot {ACT}", ACT = act));
+                err.span_label(span, "try removing `&mut` here");
+            }
+
+            Place::Projection(box Projection {
+                base: Place::Local(local),
+                elem: ProjectionElem::Deref,
+            }) if {
+                if let Some(ClearCrossCrate::Set(BindingForm::RefForGuard)) =
+                    self.mir.local_decls[*local].is_user_variable
+                {
+                    true
+                } else {
+                    false
+                }
+            } =>
+            {
+                err.span_label(span, format!("cannot {ACT}", ACT = act));
+                err.note(
+                    "variables bound in patterns are immutable until the end of the pattern guard",
+                );
+            }
+
+            // We want to point out when a `&` can be readily replaced
+            // with an `&mut`.
+            //
+            // FIXME: can this case be generalized to work for an
+            // arbitrary base for the projection?
+            Place::Projection(box Projection {
+                base: Place::Local(local),
+                elem: ProjectionElem::Deref,
+            }) if self.mir.local_decls[*local].is_user_variable.is_some() =>
+            {
+                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(local_decl))
+                    }
+
+                    ClearCrossCrate::Set(mir::BindingForm::Var(mir::VarBindingForm {
+                        binding_mode: ty::BindingMode::BindByValue(_),
+                        opt_ty_info,
+                        ..
+                    })) => Some(suggest_ampmut(
+                        self.tcx,
+                        self.mir,
+                        *local,
+                        local_decl,
+                        *opt_ty_info,
+                    )),
+
+                    ClearCrossCrate::Set(mir::BindingForm::Var(mir::VarBindingForm {
+                        binding_mode: ty::BindingMode::BindByReference(_),
+                        ..
+                    })) => suggest_ref_mut(self.tcx, local_decl.source_info.span),
+
+                    //
+                    ClearCrossCrate::Set(mir::BindingForm::RefForGuard) => unreachable!(),
+
+                    ClearCrossCrate::Clear => bug!("saw cleared local state"),
+                };
+
+                let (pointer_sigil, pointer_desc) = if local_decl.ty.is_region_ptr() {
+                    ("&", "reference")
+                } else {
+                    ("*const", "pointer")
+                };
+
+                if let Some((err_help_span, suggested_code)) = suggestion {
+                    err.span_suggestion(
+                        err_help_span,
+                        &format!("consider changing this to be a mutable {}", pointer_desc),
+                        suggested_code,
+                    );
+                }
+
+                if let Some(name) = local_decl.name {
+                    err.span_label(
+                        span,
+                        format!(
+                            "`{NAME}` is a `{SIGIL}` {DESC}, \
+                             so the data it refers to cannot be {ACTED_ON}",
+                            NAME = name,
+                            SIGIL = pointer_sigil,
+                            DESC = pointer_desc,
+                            ACTED_ON = acted_on
+                        ),
+                    );
+                } else {
+                    err.span_label(
+                        span,
+                        format!(
+                            "cannot {ACT} through `{SIGIL}` {DESC}",
+                            ACT = act,
+                            SIGIL = pointer_sigil,
+                            DESC = pointer_desc
+                        ),
+                    );
+                }
+            }
+
+            Place::Projection(box Projection {
+                base,
+                elem: ProjectionElem::Deref,
+            }) if *base == Place::Local(Local::new(1)) && !self.mir.upvar_decls.is_empty() =>
+            {
+                err.span_label(span, format!("cannot {ACT}", ACT = act));
+                err.span_help(
+                    self.mir.span,
+                    "consider changing this to accept closures that implement `FnMut`"
+                );
+            }
+
+            _ => {
+                err.span_label(span, format!("cannot {ACT}", ACT = act));
+            }
+        }
+
+        err.emit();
+    }
+
+    // Does this place refer to what the user sees as an upvar
+    fn is_upvar(&self, place: &Place<'tcx>) -> bool {
+        match *place {
+            Place::Projection(box Projection {
+                ref base,
+                elem: ProjectionElem::Field(_, _),
+            }) => {
+                let base_ty = base.ty(self.mir, self.tcx).to_ty(self.tcx);
+                is_closure_or_generator(base_ty)
+            }
+            Place::Projection(box Projection {
+                base:
+                    Place::Projection(box Projection {
+                        ref base,
+                        elem: ProjectionElem::Field(upvar_index, _),
+                    }),
+                elem: ProjectionElem::Deref,
+            }) => {
+                let base_ty = base.ty(self.mir, self.tcx).to_ty(self.tcx);
+                is_closure_or_generator(base_ty) && self.mir.upvar_decls[upvar_index.index()].by_ref
+            }
+            _ => false,
+        }
+    }
+}
+
+fn suggest_ampmut_self<'cx, 'gcx, 'tcx>(local_decl: &mir::LocalDecl<'tcx>) -> (Span, String) {
+    (local_decl.source_info.span, "&mut self".to_string())
+}
+
+// When we want to suggest a user change a local variable to be a `&mut`, there
+// are three potential "obvious" things to highlight:
+//
+// let ident [: Type] [= RightHandSideExpression];
+//     ^^^^^    ^^^^     ^^^^^^^^^^^^^^^^^^^^^^^
+//     (1.)     (2.)              (3.)
+//
+// We can always fallback on highlighting the first. But chances are good that
+// the user experience will be better if we highlight one of the others if possible;
+// for example, if the RHS is present and the Type is not, then the type is going to
+// be inferred *from* the RHS, which means we should highlight that (and suggest
+// that they borrow the RHS mutably).
+//
+// This implementation attempts to emulate AST-borrowck prioritization
+// by trying (3.), then (2.) and finally falling back on (1.).
+fn suggest_ampmut<'cx, 'gcx, 'tcx>(
+    tcx: TyCtxt<'cx, 'gcx, 'tcx>,
+    mir: &Mir<'tcx>,
+    local: Local,
+    local_decl: &mir::LocalDecl<'tcx>,
+    opt_ty_info: Option<Span>,
+) -> (Span, String) {
+    let locations = mir.find_assignments(local);
+    if locations.len() > 0 {
+        let assignment_rhs_span = mir.source_info(locations[0]).span;
+        let snippet = tcx.sess.codemap().span_to_snippet(assignment_rhs_span);
+        if let Ok(src) = snippet {
+            if src.starts_with('&') {
+                let borrowed_expr = src[1..].to_string();
+                return (assignment_rhs_span, format!("&mut {}", borrowed_expr));
+            }
+        }
+    }
+
+    let highlight_span = match opt_ty_info {
+        // if this is a variable binding with an explicit type,
+        // try to highlight that for the suggestion.
+        Some(ty_span) => ty_span,
+
+        // otherwise, just highlight the span associated with
+        // the (MIR) LocalDecl.
+        None => local_decl.source_info.span,
+    };
+
+    let ty_mut = local_decl.ty.builtin_deref(true).unwrap();
+    assert_eq!(ty_mut.mutbl, hir::MutImmutable);
+    if local_decl.ty.is_region_ptr() {
+        (highlight_span, format!("&mut {}", ty_mut.ty))
+    } else {
+        (highlight_span, format!("*mut {}", ty_mut.ty))
+    }
+}
+
+fn is_closure_or_generator(ty: ty::Ty) -> bool {
+    ty.is_closure() || ty.is_generator()
+}
diff --git a/src/librustc_mir/build/expr/as_rvalue.rs b/src/librustc_mir/build/expr/as_rvalue.rs
index 7bd9a241a53..384eb1db04f 100644
--- a/src/librustc_mir/build/expr/as_rvalue.rs
+++ b/src/librustc_mir/build/expr/as_rvalue.rs
@@ -206,7 +206,27 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                                 this.consume_by_copy_or_move(place)
                             }
                             _ => {
-                                unpack!(block = this.as_operand(block, scope, upvar))
+                                // Turn mutable borrow captures into unique
+                                // borrow captures when capturing an immutable
+                                // variable. This is sound because the mutation
+                                // that caused the capture will cause an error.
+                                match upvar.kind {
+                                    ExprKind::Borrow {
+                                        borrow_kind: BorrowKind::Mut {
+                                            allow_two_phase_borrow: false
+                                        },
+                                        region,
+                                        arg,
+                                    } => unpack!(block = this.limit_capture_mutability(
+                                        upvar.span,
+                                        upvar.ty,
+                                        scope,
+                                        block,
+                                        arg,
+                                        region,
+                                    )),
+                                    _ => unpack!(block = this.as_operand(block, scope, upvar)),
+                                }
                             }
                         }
                     })
@@ -393,6 +413,101 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
         }
     }
 
+    fn limit_capture_mutability(
+        &mut self,
+        upvar_span: Span,
+        upvar_ty: Ty<'tcx>,
+        temp_lifetime: Option<region::Scope>,
+        mut block: BasicBlock,
+        arg: ExprRef<'tcx>,
+        region: &'tcx ty::RegionKind,
+    ) -> BlockAnd<Operand<'tcx>> {
+        let this = self;
+
+        let source_info = this.source_info(upvar_span);
+        let temp = this.local_decls.push(LocalDecl::new_temp(upvar_ty, upvar_span));
+
+        this.cfg.push(block, Statement {
+            source_info,
+            kind: StatementKind::StorageLive(temp)
+        });
+
+        let arg_place = unpack!(block = this.as_place(block, arg));
+
+        let mutability = match arg_place {
+            Place::Local(local) => this.local_decls[local].mutability,
+            Place::Projection(box Projection {
+                base: Place::Local(local),
+                elem: ProjectionElem::Deref,
+            }) => {
+                debug_assert!(
+                    if let Some(ClearCrossCrate::Set(BindingForm::RefForGuard))
+                        = this.local_decls[local].is_user_variable {
+                        true
+                    } else {
+                        false
+                    },
+                    "Unexpected capture place",
+                );
+                this.local_decls[local].mutability
+            }
+            Place::Projection(box Projection {
+                ref base,
+                elem: ProjectionElem::Field(upvar_index, _),
+            })
+            | Place::Projection(box Projection {
+                base: Place::Projection(box Projection {
+                    ref base,
+                    elem: ProjectionElem::Field(upvar_index, _),
+                }),
+                elem: ProjectionElem::Deref,
+            }) => {
+                // Not projected from the implicit `self` in a closure.
+                debug_assert!(
+                    match *base {
+                        Place::Local(local) => local == Local::new(1),
+                        Place::Projection(box Projection {
+                            ref base,
+                            elem: ProjectionElem::Deref,
+                        }) => *base == Place::Local(Local::new(1)),
+                        _ => false,
+                    },
+                    "Unexpected capture place"
+                );
+                // Not in a closure
+                debug_assert!(
+                    this.upvar_decls.len() > upvar_index.index(),
+                    "Unexpected capture place"
+                );
+                this.upvar_decls[upvar_index.index()].mutability
+            }
+            _ => bug!("Unexpected capture place"),
+        };
+
+        let borrow_kind = match mutability {
+            Mutability::Not => BorrowKind::Unique,
+            Mutability::Mut => BorrowKind::Mut { allow_two_phase_borrow: false },
+        };
+
+        this.cfg.push_assign(
+            block,
+            source_info,
+            &Place::Local(temp),
+            Rvalue::Ref(region, borrow_kind, arg_place),
+        );
+
+        // In constants, temp_lifetime is None. We should not need to drop
+        // anything because no values with a destructor can be created in
+        // a constant at this time, even if the type may need dropping.
+        if let Some(temp_lifetime) = temp_lifetime {
+            this.schedule_drop_storage_and_value(
+                upvar_span, temp_lifetime, &Place::Local(temp), upvar_ty,
+            );
+        }
+
+        block.and(Operand::Move(Place::Local(temp)))
+    }
+
     // Helper to get a `-1` value of the appropriate type
     fn neg_1_literal(&mut self, span: Span, ty: Ty<'tcx>) -> Operand<'tcx> {
         let param_ty = ty::ParamEnv::empty().and(self.hir.tcx().lift_to_global(&ty).unwrap());
diff --git a/src/librustc_mir/build/matches/mod.rs b/src/librustc_mir/build/matches/mod.rs
index 3a6c7dc9754..d75b8d506e7 100644
--- a/src/librustc_mir/build/matches/mod.rs
+++ b/src/librustc_mir/build/matches/mod.rs
@@ -1198,7 +1198,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                 visibility_scope,
                 // FIXME: should these secretly injected ref_for_guard's be marked as `internal`?
                 internal: false,
-                is_user_variable: None,
+                is_user_variable: Some(ClearCrossCrate::Set(BindingForm::RefForGuard)),
             });
             LocalsForNode::Three { val_for_guard, ref_for_guard, for_arm_body }
         } else {
diff --git a/src/librustc_mir/build/mod.rs b/src/librustc_mir/build/mod.rs
index cfdb8b0048a..24228389fbf 100644
--- a/src/librustc_mir/build/mod.rs
+++ b/src/librustc_mir/build/mod.rs
@@ -286,6 +286,7 @@ struct Builder<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
     /// (A match binding can have two locals; the 2nd is for the arm's guard.)
     var_indices: NodeMap<LocalsForNode>,
     local_decls: IndexVec<Local, LocalDecl<'tcx>>,
+    upvar_decls: Vec<UpvarDecl>,
     unit_temp: Option<Place<'tcx>>,
 
     /// cached block with the RESUME terminator; this is created
@@ -472,11 +473,52 @@ fn construct_fn<'a, 'gcx, 'tcx, A>(hir: Cx<'a, 'gcx, 'tcx>,
 
     let tcx = hir.tcx();
     let span = tcx.hir.span(fn_id);
+
+    // Gather the upvars of a closure, if any.
+    let upvar_decls: Vec<_> = tcx.with_freevars(fn_id, |freevars| {
+        freevars.iter().map(|fv| {
+            let var_id = fv.var_id();
+            let var_hir_id = tcx.hir.node_to_hir_id(var_id);
+            let closure_expr_id = tcx.hir.local_def_id(fn_id);
+            let capture = hir.tables().upvar_capture(ty::UpvarId {
+                var_id: var_hir_id,
+                closure_expr_id: LocalDefId::from_def_id(closure_expr_id),
+            });
+            let by_ref = match capture {
+                ty::UpvarCapture::ByValue => false,
+                ty::UpvarCapture::ByRef(..) => true
+            };
+            let mut decl = UpvarDecl {
+                debug_name: keywords::Invalid.name(),
+                var_hir_id: ClearCrossCrate::Set(var_hir_id),
+                by_ref,
+                mutability: Mutability::Not,
+            };
+            if let Some(hir::map::NodeBinding(pat)) = tcx.hir.find(var_id) {
+                if let hir::PatKind::Binding(_, _, ident, _) = pat.node {
+                    decl.debug_name = ident.name;
+
+                    if let Some(&bm) = hir.tables.pat_binding_modes().get(pat.hir_id) {
+                        if bm == ty::BindByValue(hir::MutMutable) {
+                            decl.mutability = Mutability::Mut;
+                        } else {
+                            decl.mutability = Mutability::Not;
+                        }
+                    } else {
+                        tcx.sess.delay_span_bug(pat.span, "missing binding mode");
+                    }
+                }
+            }
+            decl
+        }).collect()
+    });
+
     let mut builder = Builder::new(hir.clone(),
         span,
         arguments.len(),
         safety,
-        return_ty);
+        return_ty,
+        upvar_decls);
 
     let fn_def_id = tcx.hir.local_def_id(fn_id);
     let call_site_scope = region::Scope::CallSite(body.value.hir_id.local_id);
@@ -519,46 +561,7 @@ fn construct_fn<'a, 'gcx, 'tcx, A>(hir: Cx<'a, 'gcx, 'tcx>,
     info!("fn_id {:?} has attrs {:?}", closure_expr_id,
           tcx.get_attrs(closure_expr_id));
 
-    // Gather the upvars of a closure, if any.
-    let upvar_decls: Vec<_> = tcx.with_freevars(fn_id, |freevars| {
-        freevars.iter().map(|fv| {
-            let var_id = fv.var_id();
-            let var_hir_id = tcx.hir.node_to_hir_id(var_id);
-            let closure_expr_id = tcx.hir.local_def_id(fn_id);
-            let capture = hir.tables().upvar_capture(ty::UpvarId {
-                var_id: var_hir_id,
-                closure_expr_id: LocalDefId::from_def_id(closure_expr_id),
-            });
-            let by_ref = match capture {
-                ty::UpvarCapture::ByValue => false,
-                ty::UpvarCapture::ByRef(..) => true
-            };
-            let mut decl = UpvarDecl {
-                debug_name: keywords::Invalid.name(),
-                var_hir_id: ClearCrossCrate::Set(var_hir_id),
-                by_ref,
-                mutability: Mutability::Not,
-            };
-            if let Some(hir::map::NodeBinding(pat)) = tcx.hir.find(var_id) {
-                if let hir::PatKind::Binding(_, _, ident, _) = pat.node {
-                    decl.debug_name = ident.name;
-
-                    if let Some(&bm) = hir.tables.pat_binding_modes().get(pat.hir_id) {
-                        if bm == ty::BindByValue(hir::MutMutable) {
-                            decl.mutability = Mutability::Mut;
-                        } else {
-                            decl.mutability = Mutability::Not;
-                        }
-                    } else {
-                        tcx.sess.delay_span_bug(pat.span, "missing binding mode");
-                    }
-                }
-            }
-            decl
-        }).collect()
-    });
-
-    let mut mir = builder.finish(upvar_decls, yield_ty);
+    let mut mir = builder.finish(yield_ty);
     mir.spread_arg = spread_arg;
     mir
 }
@@ -571,7 +574,7 @@ fn construct_const<'a, 'gcx, 'tcx>(hir: Cx<'a, 'gcx, 'tcx>,
     let ty = hir.tables().expr_ty_adjusted(ast_expr);
     let owner_id = tcx.hir.body_owner(body_id);
     let span = tcx.hir.span(owner_id);
-    let mut builder = Builder::new(hir.clone(), span, 0, Safety::Safe, ty);
+    let mut builder = Builder::new(hir.clone(), span, 0, Safety::Safe, ty, vec![]);
 
     let mut block = START_BLOCK;
     let expr = builder.hir.mirror(ast_expr);
@@ -590,7 +593,7 @@ fn construct_const<'a, 'gcx, 'tcx>(hir: Cx<'a, 'gcx, 'tcx>,
                               TerminatorKind::Unreachable);
     }
 
-    builder.finish(vec![], None)
+    builder.finish(None)
 }
 
 fn construct_error<'a, 'gcx, 'tcx>(hir: Cx<'a, 'gcx, 'tcx>,
@@ -599,10 +602,10 @@ fn construct_error<'a, 'gcx, 'tcx>(hir: Cx<'a, 'gcx, 'tcx>,
     let owner_id = hir.tcx().hir.body_owner(body_id);
     let span = hir.tcx().hir.span(owner_id);
     let ty = hir.tcx().types.err;
-    let mut builder = Builder::new(hir, span, 0, Safety::Safe, ty);
+    let mut builder = Builder::new(hir, span, 0, Safety::Safe, ty, vec![]);
     let source_info = builder.source_info(span);
     builder.cfg.terminate(START_BLOCK, source_info, TerminatorKind::Unreachable);
-    builder.finish(vec![], None)
+    builder.finish(None)
 }
 
 impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
@@ -610,7 +613,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
            span: Span,
            arg_count: usize,
            safety: Safety,
-           return_ty: Ty<'tcx>)
+           return_ty: Ty<'tcx>,
+           upvar_decls: Vec<UpvarDecl>)
            -> Builder<'a, 'gcx, 'tcx> {
         let lint_level = LintLevel::Explicit(hir.root_lint_level);
         let mut builder = Builder {
@@ -628,6 +632,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
             breakable_scopes: vec![],
             local_decls: IndexVec::from_elem_n(LocalDecl::new_return_place(return_ty,
                                                                              span), 1),
+            upvar_decls,
             var_indices: NodeMap(),
             unit_temp: None,
             cached_resume_block: None,
@@ -645,7 +650,6 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
     }
 
     fn finish(self,
-              upvar_decls: Vec<UpvarDecl>,
               yield_ty: Option<Ty<'tcx>>)
               -> Mir<'tcx> {
         for (index, block) in self.cfg.basic_blocks.iter().enumerate() {
@@ -661,7 +665,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                  yield_ty,
                  self.local_decls,
                  self.arg_count,
-                 upvar_decls,
+                 self.upvar_decls,
                  self.fn_span
         )
     }
diff --git a/src/librustc_mir/util/borrowck_errors.rs b/src/librustc_mir/util/borrowck_errors.rs
index be87365bdbb..2d6b6cea030 100644
--- a/src/librustc_mir/util/borrowck_errors.rs
+++ b/src/librustc_mir/util/borrowck_errors.rs
@@ -519,24 +519,35 @@ pub trait BorrowckErrors<'cx>: Sized + Copy {
         self.cancel_if_wrong_origin(err, o)
     }
 
-    fn cannot_borrow_path_as_mutable(
+    fn cannot_borrow_path_as_mutable_because(
         self,
         span: Span,
         path: &str,
+        reason: &str,
         o: Origin,
     ) -> DiagnosticBuilder<'cx> {
         let err = struct_span_err!(
             self,
             span,
             E0596,
-            "cannot borrow {} as mutable{OGN}",
+            "cannot borrow {} as mutable{}{OGN}",
             path,
-            OGN = o
+            reason,
+            OGN = o,
         );
 
         self.cancel_if_wrong_origin(err, o)
     }
 
+    fn cannot_borrow_path_as_mutable(
+        self,
+        span: Span,
+        path: &str,
+        o: Origin,
+    ) -> DiagnosticBuilder<'cx> {
+        self.cannot_borrow_path_as_mutable_because(span, path, "", o)
+    }
+
     fn cannot_borrow_across_generator_yield(
         self,
         span: Span,
diff --git a/src/test/compile-fail/E0594.rs b/src/test/compile-fail/E0594.rs
index f3fbc3b8b54..e5e96451d13 100644
--- a/src/test/compile-fail/E0594.rs
+++ b/src/test/compile-fail/E0594.rs
@@ -15,5 +15,5 @@ static NUM: i32 = 18;
 
 fn main() {
     NUM = 20; //[ast]~ ERROR E0594
-              //[mir]~^ ERROR cannot assign to immutable item `NUM`
+              //[mir]~^ ERROR cannot assign to immutable static item `NUM`
 }
diff --git a/src/test/compile-fail/borrowck/borrowck-assign-to-constants.rs b/src/test/compile-fail/borrowck/borrowck-assign-to-constants.rs
index 76a670af353..57002dd40fc 100644
--- a/src/test/compile-fail/borrowck/borrowck-assign-to-constants.rs
+++ b/src/test/compile-fail/borrowck/borrowck-assign-to-constants.rs
@@ -16,5 +16,5 @@ static foo: isize = 5;
 fn main() {
     // assigning to various global constants
     foo = 6; //[ast]~ ERROR cannot assign to immutable static item
-             //[mir]~^ ERROR cannot assign to immutable item `foo`
+             //[mir]~^ ERROR cannot assign to immutable static item `foo`
 }
diff --git a/src/test/compile-fail/cannot-mutate-captured-non-mut-var.rs b/src/test/compile-fail/cannot-mutate-captured-non-mut-var.rs
index 8eed61ec8d5..6bd52f00788 100644
--- a/src/test/compile-fail/cannot-mutate-captured-non-mut-var.rs
+++ b/src/test/compile-fail/cannot-mutate-captured-non-mut-var.rs
@@ -22,10 +22,10 @@ fn main() {
     let x = 1;
     to_fn_once(move|| { x = 2; });
     //[ast]~^ ERROR: cannot assign to immutable captured outer variable
-    //[mir]~^^ ERROR: cannot assign to immutable item `x`
+    //[mir]~^^ ERROR: cannot assign to `x`, as it is not declared as mutable
 
     let s = std::io::stdin();
     to_fn_once(move|| { s.read_to_end(&mut Vec::new()); });
     //[ast]~^ ERROR: cannot borrow immutable captured outer variable
-    //[mir]~^^ ERROR: cannot borrow immutable item `s` as mutable
+    //[mir]~^^ ERROR: cannot borrow `s` as mutable, as it is not declared as mutable
 }
diff --git a/src/test/compile-fail/nll/constant-thread-locals-issue-47053.rs b/src/test/compile-fail/nll/constant-thread-locals-issue-47053.rs
index c7dc84c1a91..7b1dd9265af 100644
--- a/src/test/compile-fail/nll/constant-thread-locals-issue-47053.rs
+++ b/src/test/compile-fail/nll/constant-thread-locals-issue-47053.rs
@@ -17,5 +17,5 @@
 static FOO: isize = 5;
 
 fn main() {
-    FOO = 6; //~ ERROR cannot assign to immutable item `FOO` [E0594]
+    FOO = 6; //~ ERROR cannot assign to immutable static item `FOO` [E0594]
 }
diff --git a/src/test/ui/augmented-assignments.nll.stderr b/src/test/ui/augmented-assignments.nll.stderr
index 592f666eff8..57a86227f76 100644
--- a/src/test/ui/augmented-assignments.nll.stderr
+++ b/src/test/ui/augmented-assignments.nll.stderr
@@ -14,7 +14,7 @@ LL | |     x;  //~ value moved here
    | |_____move out of `x` occurs here
    |       borrow later used here
 
-error[E0596]: cannot borrow immutable item `y` as mutable
+error[E0596]: cannot borrow `y` as mutable, as it is not declared as mutable
   --> $DIR/augmented-assignments.rs:30:5
    |
 LL |     let y = Int(2);
diff --git a/src/test/ui/borrowck/issue-45983.nll.stderr b/src/test/ui/borrowck/issue-45983.nll.stderr
index 68a039262c1..5a1f1986fcf 100644
--- a/src/test/ui/borrowck/issue-45983.nll.stderr
+++ b/src/test/ui/borrowck/issue-45983.nll.stderr
@@ -14,21 +14,14 @@ LL |     give_any(|y| x = Some(y));
    |               |
    |               lifetime `'1` appears in this argument
 
-error[E0594]: cannot assign to immutable item `x`
+error[E0594]: cannot assign to `x`, as it is not declared as mutable
   --> $DIR/issue-45983.rs:17:18
    |
-LL |     give_any(|y| x = Some(y));
-   |                  ^^^^^^^^^^^ cannot assign
-
-error[E0596]: cannot borrow immutable item `x` as mutable
-  --> $DIR/issue-45983.rs:17:14
-   |
 LL |     let x = None;
    |         - help: consider changing this to be mutable: `mut x`
 LL |     give_any(|y| x = Some(y));
-   |              ^^^^^^^^^^^^^^^ cannot borrow as mutable
+   |                  ^^^^^^^^^^^ cannot assign
 
-error: aborting due to 3 previous errors
+error: aborting due to 2 previous errors
 
-Some errors occurred: E0594, E0596.
-For more information about an error, try `rustc --explain E0594`.
+For more information about this error, try `rustc --explain E0594`.
diff --git a/src/test/ui/borrowck/mut-borrow-of-mut-ref.nll.stderr b/src/test/ui/borrowck/mut-borrow-of-mut-ref.nll.stderr
index 0674c823016..ece3c62e6a9 100644
--- a/src/test/ui/borrowck/mut-borrow-of-mut-ref.nll.stderr
+++ b/src/test/ui/borrowck/mut-borrow-of-mut-ref.nll.stderr
@@ -1,4 +1,4 @@
-error[E0596]: cannot borrow immutable item `b` as mutable
+error[E0596]: cannot borrow `b` as mutable, as it is not declared as mutable
   --> $DIR/mut-borrow-of-mut-ref.rs:18:7
    |
 LL | fn f(b: &mut i32) {
diff --git a/src/test/ui/borrowck/mutability-errors.nll.stderr b/src/test/ui/borrowck/mutability-errors.nll.stderr
new file mode 100644
index 00000000000..14c41bb81b2
--- /dev/null
+++ b/src/test/ui/borrowck/mutability-errors.nll.stderr
@@ -0,0 +1,379 @@
+error[E0594]: cannot assign to `*x` which is behind a `&` reference
+  --> $DIR/mutability-errors.rs:19:5
+   |
+LL | fn named_ref(x: &(i32,)) {
+   |                 ------- help: consider changing this to be a mutable reference: `&mut (i32,)`
+LL |     *x = (1,); //~ ERROR
+   |     ^^^^^^^^^ `x` is a `&` reference, so the data it refers to cannot be written
+
+error[E0594]: cannot assign to `x.0` which is behind a `&` reference
+  --> $DIR/mutability-errors.rs:20:5
+   |
+LL | fn named_ref(x: &(i32,)) {
+   |                 ------- help: consider changing this to be a mutable reference: `&mut (i32,)`
+LL |     *x = (1,); //~ ERROR
+LL |     x.0 = 1; //~ ERROR
+   |     ^^^^^^^ `x` is a `&` reference, so the data it refers to cannot be written
+
+error[E0596]: cannot borrow `*x` as mutable, as it is behind a `&` reference
+  --> $DIR/mutability-errors.rs:21:5
+   |
+LL | fn named_ref(x: &(i32,)) {
+   |                 ------- help: consider changing this to be a mutable reference: `&mut (i32,)`
+...
+LL |     &mut *x; //~ ERROR
+   |     ^^^^^^^ `x` is a `&` reference, so the data it refers to cannot be borrowed as mutable
+
+error[E0596]: cannot borrow `x.0` as mutable, as it is behind a `&` reference
+  --> $DIR/mutability-errors.rs:22:5
+   |
+LL | fn named_ref(x: &(i32,)) {
+   |                 ------- help: consider changing this to be a mutable reference: `&mut (i32,)`
+...
+LL |     &mut x.0; //~ ERROR
+   |     ^^^^^^^^ `x` is a `&` reference, so the data it refers to cannot be borrowed as mutable
+
+error[E0594]: cannot assign to data in a `&` reference
+  --> $DIR/mutability-errors.rs:26:5
+   |
+LL |     *f() = (1,); //~ ERROR
+   |     ^^^^^^^^^^^ cannot assign
+
+error[E0594]: cannot assign to data in a `&` reference
+  --> $DIR/mutability-errors.rs:27:5
+   |
+LL |     f().0 = 1; //~ ERROR
+   |     ^^^^^^^^^ cannot assign
+
+error[E0596]: cannot borrow data in a `&` reference as mutable
+  --> $DIR/mutability-errors.rs:28:5
+   |
+LL |     &mut *f(); //~ ERROR
+   |     ^^^^^^^^^ cannot borrow as mutable
+
+error[E0596]: cannot borrow data in a `&` reference as mutable
+  --> $DIR/mutability-errors.rs:29:5
+   |
+LL |     &mut f().0; //~ ERROR
+   |     ^^^^^^^^^^ cannot borrow as mutable
+
+error[E0594]: cannot assign to `*x` which is behind a `*const` pointer
+  --> $DIR/mutability-errors.rs:33:5
+   |
+LL | unsafe fn named_ptr(x: *const (i32,)) {
+   |                        ------------- help: consider changing this to be a mutable pointer: `*mut (i32,)`
+LL |     *x = (1,); //~ ERROR
+   |     ^^^^^^^^^ `x` is a `*const` pointer, so the data it refers to cannot be written
+
+error[E0594]: cannot assign to `x.0` which is behind a `*const` pointer
+  --> $DIR/mutability-errors.rs:34:5
+   |
+LL | unsafe fn named_ptr(x: *const (i32,)) {
+   |                        ------------- help: consider changing this to be a mutable pointer: `*mut (i32,)`
+LL |     *x = (1,); //~ ERROR
+LL |     (*x).0 = 1; //~ ERROR
+   |     ^^^^^^^^^^ `x` is a `*const` pointer, so the data it refers to cannot be written
+
+error[E0596]: cannot borrow `*x` as mutable, as it is behind a `*const` pointer
+  --> $DIR/mutability-errors.rs:35:5
+   |
+LL | unsafe fn named_ptr(x: *const (i32,)) {
+   |                        ------------- help: consider changing this to be a mutable pointer: `*mut (i32,)`
+...
+LL |     &mut *x; //~ ERROR
+   |     ^^^^^^^ `x` is a `*const` pointer, so the data it refers to cannot be borrowed as mutable
+
+error[E0596]: cannot borrow `x.0` as mutable, as it is behind a `*const` pointer
+  --> $DIR/mutability-errors.rs:36:5
+   |
+LL | unsafe fn named_ptr(x: *const (i32,)) {
+   |                        ------------- help: consider changing this to be a mutable pointer: `*mut (i32,)`
+...
+LL |     &mut (*x).0; //~ ERROR
+   |     ^^^^^^^^^^^ `x` is a `*const` pointer, so the data it refers to cannot be borrowed as mutable
+
+error[E0594]: cannot assign to data in a `*const` pointer
+  --> $DIR/mutability-errors.rs:40:5
+   |
+LL |     *f() = (1,); //~ ERROR
+   |     ^^^^^^^^^^^ cannot assign
+
+error[E0594]: cannot assign to data in a `*const` pointer
+  --> $DIR/mutability-errors.rs:41:5
+   |
+LL |     (*f()).0 = 1; //~ ERROR
+   |     ^^^^^^^^^^^^ cannot assign
+
+error[E0596]: cannot borrow data in a `*const` pointer as mutable
+  --> $DIR/mutability-errors.rs:42:5
+   |
+LL |     &mut *f(); //~ ERROR
+   |     ^^^^^^^^^ cannot borrow as mutable
+
+error[E0596]: cannot borrow data in a `*const` pointer as mutable
+  --> $DIR/mutability-errors.rs:43:5
+   |
+LL |     &mut (*f()).0; //~ ERROR
+   |     ^^^^^^^^^^^^^ cannot borrow as mutable
+
+error[E0594]: cannot assign to `x`, as it is a captured variable in a `Fn` closure
+  --> $DIR/mutability-errors.rs:50:9
+   |
+LL |         x = (1,); //~ ERROR
+   |         ^^^^^^^^ cannot assign
+   |
+help: consider changing this to accept closures that implement `FnMut`
+  --> $DIR/mutability-errors.rs:49:12
+   |
+LL |       fn_ref(|| {
+   |  ____________^
+LL | |         x = (1,); //~ ERROR
+LL | |         x.0 = 1; //~ ERROR
+LL | |         &mut x; //~ ERROR
+LL | |         &mut x.0; //~ ERROR
+LL | |     });
+   | |_____^
+
+error[E0594]: cannot assign to `x.0`, as `Fn` closures cannot mutate their captured variables
+  --> $DIR/mutability-errors.rs:51:9
+   |
+LL |         x.0 = 1; //~ ERROR
+   |         ^^^^^^^ cannot assign
+   |
+help: consider changing this to accept closures that implement `FnMut`
+  --> $DIR/mutability-errors.rs:49:12
+   |
+LL |       fn_ref(|| {
+   |  ____________^
+LL | |         x = (1,); //~ ERROR
+LL | |         x.0 = 1; //~ ERROR
+LL | |         &mut x; //~ ERROR
+LL | |         &mut x.0; //~ ERROR
+LL | |     });
+   | |_____^
+
+error[E0596]: cannot borrow `x` as mutable, as it is a captured variable in a `Fn` closure
+  --> $DIR/mutability-errors.rs:52:9
+   |
+LL |         &mut x; //~ ERROR
+   |         ^^^^^^ cannot borrow as mutable
+   |
+help: consider changing this to accept closures that implement `FnMut`
+  --> $DIR/mutability-errors.rs:49:12
+   |
+LL |       fn_ref(|| {
+   |  ____________^
+LL | |         x = (1,); //~ ERROR
+LL | |         x.0 = 1; //~ ERROR
+LL | |         &mut x; //~ ERROR
+LL | |         &mut x.0; //~ ERROR
+LL | |     });
+   | |_____^
+
+error[E0596]: cannot borrow `x.0` as mutable, as `Fn` closures cannot mutate their captured variables
+  --> $DIR/mutability-errors.rs:53:9
+   |
+LL |         &mut x.0; //~ ERROR
+   |         ^^^^^^^^ cannot borrow as mutable
+   |
+help: consider changing this to accept closures that implement `FnMut`
+  --> $DIR/mutability-errors.rs:49:12
+   |
+LL |       fn_ref(|| {
+   |  ____________^
+LL | |         x = (1,); //~ ERROR
+LL | |         x.0 = 1; //~ ERROR
+LL | |         &mut x; //~ ERROR
+LL | |         &mut x.0; //~ ERROR
+LL | |     });
+   | |_____^
+
+error[E0594]: cannot assign to `x`, as it is a captured variable in a `Fn` closure
+  --> $DIR/mutability-errors.rs:56:9
+   |
+LL |         x = (1,); //~ ERROR
+   |         ^^^^^^^^ cannot assign
+   |
+help: consider changing this to accept closures that implement `FnMut`
+  --> $DIR/mutability-errors.rs:55:12
+   |
+LL |       fn_ref(move || {
+   |  ____________^
+LL | |         x = (1,); //~ ERROR
+LL | |         x.0 = 1; //~ ERROR
+LL | |         &mut x; //~ ERROR
+LL | |         &mut x.0; //~ ERROR
+LL | |     });
+   | |_____^
+
+error[E0594]: cannot assign to `x.0`, as `Fn` closures cannot mutate their captured variables
+  --> $DIR/mutability-errors.rs:57:9
+   |
+LL |         x.0 = 1; //~ ERROR
+   |         ^^^^^^^ cannot assign
+   |
+help: consider changing this to accept closures that implement `FnMut`
+  --> $DIR/mutability-errors.rs:55:12
+   |
+LL |       fn_ref(move || {
+   |  ____________^
+LL | |         x = (1,); //~ ERROR
+LL | |         x.0 = 1; //~ ERROR
+LL | |         &mut x; //~ ERROR
+LL | |         &mut x.0; //~ ERROR
+LL | |     });
+   | |_____^
+
+error[E0596]: cannot borrow `x` as mutable, as it is a captured variable in a `Fn` closure
+  --> $DIR/mutability-errors.rs:58:9
+   |
+LL |         &mut x; //~ ERROR
+   |         ^^^^^^ cannot borrow as mutable
+   |
+help: consider changing this to accept closures that implement `FnMut`
+  --> $DIR/mutability-errors.rs:55:12
+   |
+LL |       fn_ref(move || {
+   |  ____________^
+LL | |         x = (1,); //~ ERROR
+LL | |         x.0 = 1; //~ ERROR
+LL | |         &mut x; //~ ERROR
+LL | |         &mut x.0; //~ ERROR
+LL | |     });
+   | |_____^
+
+error[E0596]: cannot borrow `x.0` as mutable, as `Fn` closures cannot mutate their captured variables
+  --> $DIR/mutability-errors.rs:59:9
+   |
+LL |         &mut x.0; //~ ERROR
+   |         ^^^^^^^^ cannot borrow as mutable
+   |
+help: consider changing this to accept closures that implement `FnMut`
+  --> $DIR/mutability-errors.rs:55:12
+   |
+LL |       fn_ref(move || {
+   |  ____________^
+LL | |         x = (1,); //~ ERROR
+LL | |         x.0 = 1; //~ ERROR
+LL | |         &mut x; //~ ERROR
+LL | |         &mut x.0; //~ ERROR
+LL | |     });
+   | |_____^
+
+error[E0596]: cannot borrow `x` as mutable, as it is not declared as mutable
+  --> $DIR/mutability-errors.rs:64:5
+   |
+LL | fn imm_local(x: (i32,)) {
+   |              - help: consider changing this to be mutable: `mut x`
+LL |     &mut x; //~ ERROR
+   |     ^^^^^^ cannot borrow as mutable
+
+error[E0596]: cannot borrow `x.0` as mutable, as `x` is not declared as mutable
+  --> $DIR/mutability-errors.rs:65:5
+   |
+LL | fn imm_local(x: (i32,)) {
+   |              - help: consider changing this to be mutable: `mut x`
+LL |     &mut x; //~ ERROR
+LL |     &mut x.0; //~ ERROR
+   |     ^^^^^^^^ cannot borrow as mutable
+
+error[E0594]: cannot assign to `x`, as it is not declared as mutable
+  --> $DIR/mutability-errors.rs:70:9
+   |
+LL | fn imm_capture(x: (i32,)) {
+   |                - help: consider changing this to be mutable: `mut x`
+LL |     || { //~ ERROR
+LL |         x = (1,);
+   |         ^^^^^^^^ cannot assign
+
+error[E0594]: cannot assign to `x.0`, as `x` is not declared as mutable
+  --> $DIR/mutability-errors.rs:71:9
+   |
+LL | fn imm_capture(x: (i32,)) {
+   |                - help: consider changing this to be mutable: `mut x`
+...
+LL |         x.0 = 1;
+   |         ^^^^^^^ cannot assign
+
+error[E0596]: cannot borrow `x` as mutable, as it is not declared as mutable
+  --> $DIR/mutability-errors.rs:72:9
+   |
+LL | fn imm_capture(x: (i32,)) {
+   |                - help: consider changing this to be mutable: `mut x`
+...
+LL |         &mut x;
+   |         ^^^^^^ cannot borrow as mutable
+
+error[E0596]: cannot borrow `x.0` as mutable, as `x` is not declared as mutable
+  --> $DIR/mutability-errors.rs:73:9
+   |
+LL | fn imm_capture(x: (i32,)) {
+   |                - help: consider changing this to be mutable: `mut x`
+...
+LL |         &mut x.0;
+   |         ^^^^^^^^ cannot borrow as mutable
+
+error[E0594]: cannot assign to `x`, as it is not declared as mutable
+  --> $DIR/mutability-errors.rs:76:9
+   |
+LL | fn imm_capture(x: (i32,)) {
+   |                - help: consider changing this to be mutable: `mut x`
+...
+LL |         x = (1,); //~ ERROR
+   |         ^^^^^^^^ cannot assign
+
+error[E0594]: cannot assign to `x.0`, as `x` is not declared as mutable
+  --> $DIR/mutability-errors.rs:77:9
+   |
+LL | fn imm_capture(x: (i32,)) {
+   |                - help: consider changing this to be mutable: `mut x`
+...
+LL |         x.0 = 1; //~ ERROR
+   |         ^^^^^^^ cannot assign
+
+error[E0596]: cannot borrow `x` as mutable, as it is not declared as mutable
+  --> $DIR/mutability-errors.rs:78:9
+   |
+LL | fn imm_capture(x: (i32,)) {
+   |                - help: consider changing this to be mutable: `mut x`
+...
+LL |         &mut x; //~ ERROR
+   |         ^^^^^^ cannot borrow as mutable
+
+error[E0596]: cannot borrow `x.0` as mutable, as `x` is not declared as mutable
+  --> $DIR/mutability-errors.rs:79:9
+   |
+LL | fn imm_capture(x: (i32,)) {
+   |                - help: consider changing this to be mutable: `mut x`
+...
+LL |         &mut x.0; //~ ERROR
+   |         ^^^^^^^^ cannot borrow as mutable
+
+error[E0594]: cannot assign to immutable static item `X`
+  --> $DIR/mutability-errors.rs:86:5
+   |
+LL |     X = (1,); //~ ERROR
+   |     ^^^^^^^^ cannot assign
+
+error[E0594]: cannot assign to `X.0`, as `X` is an immutable static item
+  --> $DIR/mutability-errors.rs:87:5
+   |
+LL |     X.0 = 1; //~ ERROR
+   |     ^^^^^^^ cannot assign
+
+error[E0596]: cannot borrow immutable static item `X` as mutable
+  --> $DIR/mutability-errors.rs:88:5
+   |
+LL |     &mut X; //~ ERROR
+   |     ^^^^^^ cannot borrow as mutable
+
+error[E0596]: cannot borrow `X.0` as mutable, as `X` is an immutable static item
+  --> $DIR/mutability-errors.rs:89:5
+   |
+LL |     &mut X.0; //~ ERROR
+   |     ^^^^^^^^ cannot borrow as mutable
+
+error: aborting due to 38 previous errors
+
+Some errors occurred: E0594, E0596.
+For more information about an error, try `rustc --explain E0594`.
diff --git a/src/test/ui/borrowck/mutability-errors.rs b/src/test/ui/borrowck/mutability-errors.rs
new file mode 100644
index 00000000000..0b4548cbebb
--- /dev/null
+++ b/src/test/ui/borrowck/mutability-errors.rs
@@ -0,0 +1,92 @@
+// 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.
+
+// All the possible mutability error cases.
+
+#![allow(unused)]
+
+type MakeRef = fn() -> &'static (i32,);
+type MakePtr = fn() -> *const (i32,);
+
+fn named_ref(x: &(i32,)) {
+    *x = (1,); //~ ERROR
+    x.0 = 1; //~ ERROR
+    &mut *x; //~ ERROR
+    &mut x.0; //~ ERROR
+}
+
+fn unnamed_ref(f: MakeRef) {
+    *f() = (1,); //~ ERROR
+    f().0 = 1; //~ ERROR
+    &mut *f(); //~ ERROR
+    &mut f().0; //~ ERROR
+}
+
+unsafe fn named_ptr(x: *const (i32,)) {
+    *x = (1,); //~ ERROR
+    (*x).0 = 1; //~ ERROR
+    &mut *x; //~ ERROR
+    &mut (*x).0; //~ ERROR
+}
+
+unsafe fn unnamed_ptr(f: MakePtr) {
+    *f() = (1,); //~ ERROR
+    (*f()).0 = 1; //~ ERROR
+    &mut *f(); //~ ERROR
+    &mut (*f()).0; //~ ERROR
+}
+
+fn fn_ref<F: Fn()>(f: F) -> F { f }
+
+fn ref_closure(mut x: (i32,)) {
+    fn_ref(|| {
+        x = (1,); //~ ERROR
+        x.0 = 1; //~ ERROR
+        &mut x; //~ ERROR
+        &mut x.0; //~ ERROR
+    });
+    fn_ref(move || {
+        x = (1,); //~ ERROR
+        x.0 = 1; //~ ERROR
+        &mut x; //~ ERROR
+        &mut x.0; //~ ERROR
+    });
+}
+
+fn imm_local(x: (i32,)) {
+    &mut x; //~ ERROR
+    &mut x.0; //~ ERROR
+}
+
+fn imm_capture(x: (i32,)) {
+    || { //~ ERROR
+        x = (1,);
+        x.0 = 1;
+        &mut x;
+        &mut x.0;
+    };
+    move || {
+        x = (1,); //~ ERROR
+        x.0 = 1; //~ ERROR
+        &mut x; //~ ERROR
+        &mut x.0; //~ ERROR
+    };
+}
+
+static X: (i32,) = (0,);
+
+fn imm_static() {
+    X = (1,); //~ ERROR
+    X.0 = 1; //~ ERROR
+    &mut X; //~ ERROR
+    &mut X.0; //~ ERROR
+}
+
+fn main() {}
diff --git a/src/test/ui/borrowck/mutability-errors.stderr b/src/test/ui/borrowck/mutability-errors.stderr
new file mode 100644
index 00000000000..d77254f3141
--- /dev/null
+++ b/src/test/ui/borrowck/mutability-errors.stderr
@@ -0,0 +1,308 @@
+error[E0594]: cannot assign to immutable borrowed content `*x`
+  --> $DIR/mutability-errors.rs:19:5
+   |
+LL | fn named_ref(x: &(i32,)) {
+   |                 ------- use `&mut (i32,)` here to make mutable
+LL |     *x = (1,); //~ ERROR
+   |     ^^^^^^^^^ cannot borrow as mutable
+
+error[E0594]: cannot assign to field `x.0` of immutable binding
+  --> $DIR/mutability-errors.rs:20:5
+   |
+LL | fn named_ref(x: &(i32,)) {
+   |                 ------- use `&mut (i32,)` here to make mutable
+LL |     *x = (1,); //~ ERROR
+LL |     x.0 = 1; //~ ERROR
+   |     ^^^^^^^ cannot mutably borrow field of immutable binding
+
+error[E0596]: cannot borrow immutable borrowed content `*x` as mutable
+  --> $DIR/mutability-errors.rs:21:10
+   |
+LL | fn named_ref(x: &(i32,)) {
+   |                 ------- use `&mut (i32,)` here to make mutable
+...
+LL |     &mut *x; //~ ERROR
+   |          ^^ cannot borrow as mutable
+
+error[E0596]: cannot borrow field `x.0` of immutable binding as mutable
+  --> $DIR/mutability-errors.rs:22:10
+   |
+LL | fn named_ref(x: &(i32,)) {
+   |                 ------- use `&mut (i32,)` here to make mutable
+...
+LL |     &mut x.0; //~ ERROR
+   |          ^^^ cannot mutably borrow field of immutable binding
+
+error[E0594]: cannot assign to immutable borrowed content
+  --> $DIR/mutability-errors.rs:26:5
+   |
+LL |     *f() = (1,); //~ ERROR
+   |     ^^^^^^^^^^^ cannot borrow as mutable
+
+error[E0594]: cannot assign to field of immutable binding
+  --> $DIR/mutability-errors.rs:27:5
+   |
+LL |     f().0 = 1; //~ ERROR
+   |     ^^^^^^^^^ cannot mutably borrow field of immutable binding
+
+error[E0596]: cannot borrow immutable borrowed content as mutable
+  --> $DIR/mutability-errors.rs:28:10
+   |
+LL |     &mut *f(); //~ ERROR
+   |          ^^^^ cannot borrow as mutable
+
+error[E0596]: cannot borrow field of immutable binding as mutable
+  --> $DIR/mutability-errors.rs:29:10
+   |
+LL |     &mut f().0; //~ ERROR
+   |          ^^^^^ cannot mutably borrow field of immutable binding
+
+error[E0594]: cannot assign to immutable dereference of raw pointer `*x`
+  --> $DIR/mutability-errors.rs:33:5
+   |
+LL |     *x = (1,); //~ ERROR
+   |     ^^^^^^^^^ cannot borrow as mutable
+
+error[E0594]: cannot assign to field `x.0` of immutable binding
+  --> $DIR/mutability-errors.rs:34:5
+   |
+LL |     (*x).0 = 1; //~ ERROR
+   |     ^^^^^^^^^^ cannot mutably borrow field of immutable binding
+
+error[E0596]: cannot borrow immutable dereference of raw pointer `*x` as mutable
+  --> $DIR/mutability-errors.rs:35:10
+   |
+LL |     &mut *x; //~ ERROR
+   |          ^^ cannot borrow as mutable
+
+error[E0596]: cannot borrow field `x.0` of immutable binding as mutable
+  --> $DIR/mutability-errors.rs:36:10
+   |
+LL |     &mut (*x).0; //~ ERROR
+   |          ^^^^^^ cannot mutably borrow field of immutable binding
+
+error[E0594]: cannot assign to immutable dereference of raw pointer
+  --> $DIR/mutability-errors.rs:40:5
+   |
+LL |     *f() = (1,); //~ ERROR
+   |     ^^^^^^^^^^^ cannot borrow as mutable
+
+error[E0594]: cannot assign to field of immutable binding
+  --> $DIR/mutability-errors.rs:41:5
+   |
+LL |     (*f()).0 = 1; //~ ERROR
+   |     ^^^^^^^^^^^^ cannot mutably borrow field of immutable binding
+
+error[E0596]: cannot borrow immutable dereference of raw pointer as mutable
+  --> $DIR/mutability-errors.rs:42:10
+   |
+LL |     &mut *f(); //~ ERROR
+   |          ^^^^ cannot borrow as mutable
+
+error[E0596]: cannot borrow field of immutable binding as mutable
+  --> $DIR/mutability-errors.rs:43:10
+   |
+LL |     &mut (*f()).0; //~ ERROR
+   |          ^^^^^^^^ cannot mutably borrow field of immutable binding
+
+error[E0387]: cannot assign to data in a captured outer variable in an `Fn` closure
+  --> $DIR/mutability-errors.rs:50:9
+   |
+LL |         x = (1,); //~ ERROR
+   |         ^^^^^^^^
+   |
+help: consider changing this closure to take self by mutable reference
+  --> $DIR/mutability-errors.rs:49:12
+   |
+LL |       fn_ref(|| {
+   |  ____________^
+LL | |         x = (1,); //~ ERROR
+LL | |         x.0 = 1; //~ ERROR
+LL | |         &mut x; //~ ERROR
+LL | |         &mut x.0; //~ ERROR
+LL | |     });
+   | |_____^
+
+error[E0387]: cannot assign to data in a captured outer variable in an `Fn` closure
+  --> $DIR/mutability-errors.rs:51:9
+   |
+LL |         x.0 = 1; //~ ERROR
+   |         ^^^^^^^
+   |
+help: consider changing this closure to take self by mutable reference
+  --> $DIR/mutability-errors.rs:49:12
+   |
+LL |       fn_ref(|| {
+   |  ____________^
+LL | |         x = (1,); //~ ERROR
+LL | |         x.0 = 1; //~ ERROR
+LL | |         &mut x; //~ ERROR
+LL | |         &mut x.0; //~ ERROR
+LL | |     });
+   | |_____^
+
+error[E0387]: cannot borrow data mutably in a captured outer variable in an `Fn` closure
+  --> $DIR/mutability-errors.rs:52:14
+   |
+LL |         &mut x; //~ ERROR
+   |              ^
+   |
+help: consider changing this closure to take self by mutable reference
+  --> $DIR/mutability-errors.rs:49:12
+   |
+LL |       fn_ref(|| {
+   |  ____________^
+LL | |         x = (1,); //~ ERROR
+LL | |         x.0 = 1; //~ ERROR
+LL | |         &mut x; //~ ERROR
+LL | |         &mut x.0; //~ ERROR
+LL | |     });
+   | |_____^
+
+error[E0387]: cannot borrow data mutably in a captured outer variable in an `Fn` closure
+  --> $DIR/mutability-errors.rs:53:14
+   |
+LL |         &mut x.0; //~ ERROR
+   |              ^^^
+   |
+help: consider changing this closure to take self by mutable reference
+  --> $DIR/mutability-errors.rs:49:12
+   |
+LL |       fn_ref(|| {
+   |  ____________^
+LL | |         x = (1,); //~ ERROR
+LL | |         x.0 = 1; //~ ERROR
+LL | |         &mut x; //~ ERROR
+LL | |         &mut x.0; //~ ERROR
+LL | |     });
+   | |_____^
+
+error[E0594]: cannot assign to captured outer variable in an `Fn` closure
+  --> $DIR/mutability-errors.rs:56:9
+   |
+LL |         x = (1,); //~ ERROR
+   |         ^^^^^^^^
+   |
+   = note: `Fn` closures cannot capture their enclosing environment for modifications
+help: consider changing this closure to take self by mutable reference
+  --> $DIR/mutability-errors.rs:55:12
+   |
+LL |       fn_ref(move || {
+   |  ____________^
+LL | |         x = (1,); //~ ERROR
+LL | |         x.0 = 1; //~ ERROR
+LL | |         &mut x; //~ ERROR
+LL | |         &mut x.0; //~ ERROR
+LL | |     });
+   | |_____^
+
+error[E0594]: cannot assign to field `x.0` of immutable binding
+  --> $DIR/mutability-errors.rs:57:9
+   |
+LL |         x.0 = 1; //~ ERROR
+   |         ^^^^^^^ cannot mutably borrow field of immutable binding
+
+error[E0596]: cannot borrow captured outer variable in an `Fn` closure as mutable
+  --> $DIR/mutability-errors.rs:58:14
+   |
+LL |         &mut x; //~ ERROR
+   |              ^
+   |
+help: consider changing this closure to take self by mutable reference
+  --> $DIR/mutability-errors.rs:55:12
+   |
+LL |       fn_ref(move || {
+   |  ____________^
+LL | |         x = (1,); //~ ERROR
+LL | |         x.0 = 1; //~ ERROR
+LL | |         &mut x; //~ ERROR
+LL | |         &mut x.0; //~ ERROR
+LL | |     });
+   | |_____^
+
+error[E0596]: cannot borrow field `x.0` of immutable binding as mutable
+  --> $DIR/mutability-errors.rs:59:14
+   |
+LL |         &mut x.0; //~ ERROR
+   |              ^^^ cannot mutably borrow field of immutable binding
+
+error[E0596]: cannot borrow immutable argument `x` as mutable
+  --> $DIR/mutability-errors.rs:64:10
+   |
+LL | fn imm_local(x: (i32,)) {
+   |              - consider changing this to `mut x`
+LL |     &mut x; //~ ERROR
+   |          ^ cannot borrow mutably
+
+error[E0596]: cannot borrow field `x.0` of immutable binding as mutable
+  --> $DIR/mutability-errors.rs:65:10
+   |
+LL | fn imm_local(x: (i32,)) {
+   |              - consider changing this to `mut x`
+LL |     &mut x; //~ ERROR
+LL |     &mut x.0; //~ ERROR
+   |          ^^^ cannot mutably borrow field of immutable binding
+
+error[E0595]: closure cannot assign to immutable argument `x`
+  --> $DIR/mutability-errors.rs:69:5
+   |
+LL | fn imm_capture(x: (i32,)) {
+   |                - consider changing this to `mut x`
+LL |     || { //~ ERROR
+   |     ^^ cannot borrow mutably
+
+error[E0594]: cannot assign to captured outer variable in an `FnMut` closure
+  --> $DIR/mutability-errors.rs:76:9
+   |
+LL | fn imm_capture(x: (i32,)) {
+   |                - help: consider making `x` mutable: `mut x`
+...
+LL |         x = (1,); //~ ERROR
+   |         ^^^^^^^^
+
+error[E0594]: cannot assign to field `x.0` of immutable binding
+  --> $DIR/mutability-errors.rs:77:9
+   |
+LL |         x.0 = 1; //~ ERROR
+   |         ^^^^^^^ cannot mutably borrow field of immutable binding
+
+error[E0596]: cannot borrow captured outer variable in an `FnMut` closure as mutable
+  --> $DIR/mutability-errors.rs:78:14
+   |
+LL |         &mut x; //~ ERROR
+   |              ^
+
+error[E0596]: cannot borrow field `x.0` of immutable binding as mutable
+  --> $DIR/mutability-errors.rs:79:14
+   |
+LL |         &mut x.0; //~ ERROR
+   |              ^^^ cannot mutably borrow field of immutable binding
+
+error[E0594]: cannot assign to immutable static item
+  --> $DIR/mutability-errors.rs:86:5
+   |
+LL |     X = (1,); //~ ERROR
+   |     ^^^^^^^^
+
+error[E0594]: cannot assign to field of immutable binding
+  --> $DIR/mutability-errors.rs:87:5
+   |
+LL |     X.0 = 1; //~ ERROR
+   |     ^^^^^^^ cannot mutably borrow field of immutable binding
+
+error[E0596]: cannot borrow immutable static item as mutable
+  --> $DIR/mutability-errors.rs:88:10
+   |
+LL |     &mut X; //~ ERROR
+   |          ^
+
+error[E0596]: cannot borrow field of immutable binding as mutable
+  --> $DIR/mutability-errors.rs:89:10
+   |
+LL |     &mut X.0; //~ ERROR
+   |          ^^^ cannot mutably borrow field of immutable binding
+
+error: aborting due to 35 previous errors
+
+Some errors occurred: E0387, E0594, E0595, E0596.
+For more information about an error, try `rustc --explain E0387`.
diff --git a/src/test/ui/closure-immutable-outer-variable.nll.stderr b/src/test/ui/closure-immutable-outer-variable.nll.stderr
index 335ccefe8a0..d91c1199b3f 100644
--- a/src/test/ui/closure-immutable-outer-variable.nll.stderr
+++ b/src/test/ui/closure-immutable-outer-variable.nll.stderr
@@ -1,6 +1,8 @@
-error[E0594]: cannot assign to immutable item `y`
+error[E0594]: cannot assign to `y`, as it is not declared as mutable
   --> $DIR/closure-immutable-outer-variable.rs:21:26
    |
+LL |     let y = true;
+   |         - help: consider changing this to be mutable: `mut y`
 LL |     foo(Box::new(move || y = false) as Box<_>); //~ ERROR cannot assign to captured outer variable
    |                          ^^^^^^^^^ cannot assign
 
diff --git a/src/test/ui/codemap_tests/huge_multispan_highlight.nll.stderr b/src/test/ui/codemap_tests/huge_multispan_highlight.nll.stderr
index da0b201f5d4..8251f04d353 100644
--- a/src/test/ui/codemap_tests/huge_multispan_highlight.nll.stderr
+++ b/src/test/ui/codemap_tests/huge_multispan_highlight.nll.stderr
@@ -1,4 +1,4 @@
-error[E0596]: cannot borrow immutable item `x` as mutable
+error[E0596]: cannot borrow `x` as mutable, as it is not declared as mutable
   --> $DIR/huge_multispan_highlight.rs:100:13
    |
 LL |     let x = "foo";
diff --git a/src/test/ui/did_you_mean/issue-31424.nll.stderr b/src/test/ui/did_you_mean/issue-31424.nll.stderr
index 21c5e3608c0..eae834e2b1a 100644
--- a/src/test/ui/did_you_mean/issue-31424.nll.stderr
+++ b/src/test/ui/did_you_mean/issue-31424.nll.stderr
@@ -1,10 +1,10 @@
-error[E0596]: cannot borrow immutable item `self` as mutable
+error[E0596]: cannot borrow `self` as mutable, as it is not declared as mutable
   --> $DIR/issue-31424.rs:17:9
    |
 LL |         (&mut self).bar(); //~ ERROR cannot borrow
    |         ^^^^^^^^^^^ cannot borrow as mutable
 
-error[E0596]: cannot borrow immutable item `self` as mutable
+error[E0596]: cannot borrow `self` as mutable, as it is not declared as mutable
   --> $DIR/issue-31424.rs:23:9
    |
 LL |     fn bar(self: &mut Self) {
diff --git a/src/test/ui/did_you_mean/issue-34126.nll.stderr b/src/test/ui/did_you_mean/issue-34126.nll.stderr
index 8dedb6ec4db..004c43a7343 100644
--- a/src/test/ui/did_you_mean/issue-34126.nll.stderr
+++ b/src/test/ui/did_you_mean/issue-34126.nll.stderr
@@ -1,4 +1,4 @@
-error[E0596]: cannot borrow immutable item `self` as mutable
+error[E0596]: cannot borrow `self` as mutable, as it is not declared as mutable
   --> $DIR/issue-34126.rs:16:18
    |
 LL |         self.run(&mut self); //~ ERROR cannot borrow
diff --git a/src/test/ui/did_you_mean/issue-34337.nll.stderr b/src/test/ui/did_you_mean/issue-34337.nll.stderr
index d2271e8e7de..38315a067c0 100644
--- a/src/test/ui/did_you_mean/issue-34337.nll.stderr
+++ b/src/test/ui/did_you_mean/issue-34337.nll.stderr
@@ -1,4 +1,4 @@
-error[E0596]: cannot borrow immutable item `key` as mutable
+error[E0596]: cannot borrow `key` as mutable, as it is not declared as mutable
   --> $DIR/issue-34337.rs:16:9
    |
 LL |     get(&mut key); //~ ERROR cannot borrow
diff --git a/src/test/ui/did_you_mean/issue-35937.nll.stderr b/src/test/ui/did_you_mean/issue-35937.nll.stderr
index 7eaa4c7d5fe..804e5f0531f 100644
--- a/src/test/ui/did_you_mean/issue-35937.nll.stderr
+++ b/src/test/ui/did_you_mean/issue-35937.nll.stderr
@@ -1,4 +1,4 @@
-error[E0596]: cannot borrow immutable item `f.v` as mutable
+error[E0596]: cannot borrow `f.v` as mutable, as `f` is not declared as mutable
   --> $DIR/issue-35937.rs:17:5
    |
 LL |     let f = Foo { v: Vec::new() };
diff --git a/src/test/ui/did_you_mean/issue-37139.nll.stderr b/src/test/ui/did_you_mean/issue-37139.nll.stderr
index e51a1baad25..7cc7ca14638 100644
--- a/src/test/ui/did_you_mean/issue-37139.nll.stderr
+++ b/src/test/ui/did_you_mean/issue-37139.nll.stderr
@@ -1,4 +1,4 @@
-error[E0596]: cannot borrow immutable item `x` as mutable
+error[E0596]: cannot borrow `x` as mutable, as it is not declared as mutable
   --> $DIR/issue-37139.rs:22:18
    |
 LL |             test(&mut x); //~ ERROR cannot borrow immutable
diff --git a/src/test/ui/did_you_mean/issue-38147-1.nll.stderr b/src/test/ui/did_you_mean/issue-38147-1.nll.stderr
index d156d64b9d6..25613561bba 100644
--- a/src/test/ui/did_you_mean/issue-38147-1.nll.stderr
+++ b/src/test/ui/did_you_mean/issue-38147-1.nll.stderr
@@ -1,4 +1,4 @@
-error[E0596]: cannot borrow immutable item `*self.s` as mutable
+error[E0596]: cannot borrow `*self.s` as mutable, as it is behind a `&` reference
   --> $DIR/issue-38147-1.rs:27:9
    |
 LL |     fn f(&self) {
diff --git a/src/test/ui/did_you_mean/issue-38147-2.nll.stderr b/src/test/ui/did_you_mean/issue-38147-2.nll.stderr
index c8e231ea3b3..21fc4079d5b 100644
--- a/src/test/ui/did_you_mean/issue-38147-2.nll.stderr
+++ b/src/test/ui/did_you_mean/issue-38147-2.nll.stderr
@@ -1,4 +1,4 @@
-error[E0596]: cannot borrow immutable item `*self.s` as mutable
+error[E0596]: cannot borrow `*self.s` as mutable, as it is behind a `&` reference
   --> $DIR/issue-38147-2.rs:17:9
    |
 LL |         self.s.push('x');
diff --git a/src/test/ui/did_you_mean/issue-38147-3.nll.stderr b/src/test/ui/did_you_mean/issue-38147-3.nll.stderr
index c5d6551b647..d426c1f37fc 100644
--- a/src/test/ui/did_you_mean/issue-38147-3.nll.stderr
+++ b/src/test/ui/did_you_mean/issue-38147-3.nll.stderr
@@ -1,4 +1,4 @@
-error[E0596]: cannot borrow immutable item `*self.s` as mutable
+error[E0596]: cannot borrow `*self.s` as mutable, as it is behind a `&` reference
   --> $DIR/issue-38147-3.rs:17:9
    |
 LL |         self.s.push('x');
diff --git a/src/test/ui/did_you_mean/issue-38147-4.nll.stderr b/src/test/ui/did_you_mean/issue-38147-4.nll.stderr
index c875957623b..eccbe19ac94 100644
--- a/src/test/ui/did_you_mean/issue-38147-4.nll.stderr
+++ b/src/test/ui/did_you_mean/issue-38147-4.nll.stderr
@@ -1,4 +1,4 @@
-error[E0596]: cannot borrow immutable item `*f.s` as mutable
+error[E0596]: cannot borrow `*f.s` as mutable, as it is behind a `&` reference
   --> $DIR/issue-38147-4.rs:16:5
    |
 LL | fn f(x: usize, f: &Foo) {
diff --git a/src/test/ui/did_you_mean/issue-39544.nll.stderr b/src/test/ui/did_you_mean/issue-39544.nll.stderr
index e2d2fcd63db..00e4cc6b0c3 100644
--- a/src/test/ui/did_you_mean/issue-39544.nll.stderr
+++ b/src/test/ui/did_you_mean/issue-39544.nll.stderr
@@ -1,4 +1,4 @@
-error[E0596]: cannot borrow immutable item `z.x` as mutable
+error[E0596]: cannot borrow `z.x` as mutable, as `z` is not declared as mutable
   --> $DIR/issue-39544.rs:21:13
    |
 LL |     let z = Z { x: X::Y };
@@ -6,7 +6,7 @@ LL |     let z = Z { x: X::Y };
 LL |     let _ = &mut z.x; //~ ERROR cannot borrow
    |             ^^^^^^^^ cannot borrow as mutable
 
-error[E0596]: cannot borrow immutable item `self.x` as mutable
+error[E0596]: cannot borrow `self.x` as mutable, as it is behind a `&` reference
   --> $DIR/issue-39544.rs:26:17
    |
 LL |     fn foo<'z>(&'z self) {
@@ -14,7 +14,7 @@ LL |     fn foo<'z>(&'z self) {
 LL |         let _ = &mut self.x; //~ ERROR cannot borrow
    |                 ^^^^^^^^^^^ `self` is a `&` reference, so the data it refers to cannot be borrowed as mutable
 
-error[E0596]: cannot borrow immutable item `self.x` as mutable
+error[E0596]: cannot borrow `self.x` as mutable, as it is behind a `&` reference
   --> $DIR/issue-39544.rs:30:17
    |
 LL |     fn foo1(&self, other: &Z) {
@@ -22,7 +22,7 @@ LL |     fn foo1(&self, other: &Z) {
 LL |         let _ = &mut self.x; //~ ERROR cannot borrow
    |                 ^^^^^^^^^^^ `self` is a `&` reference, so the data it refers to cannot be borrowed as mutable
 
-error[E0596]: cannot borrow immutable item `other.x` as mutable
+error[E0596]: cannot borrow `other.x` as mutable, as it is behind a `&` reference
   --> $DIR/issue-39544.rs:31:17
    |
 LL |     fn foo1(&self, other: &Z) {
@@ -31,7 +31,7 @@ LL |         let _ = &mut self.x; //~ ERROR cannot borrow
 LL |         let _ = &mut other.x; //~ ERROR cannot borrow
    |                 ^^^^^^^^^^^^ `other` is a `&` reference, so the data it refers to cannot be borrowed as mutable
 
-error[E0596]: cannot borrow immutable item `self.x` as mutable
+error[E0596]: cannot borrow `self.x` as mutable, as it is behind a `&` reference
   --> $DIR/issue-39544.rs:35:17
    |
 LL |     fn foo2<'a>(&'a self, other: &Z) {
@@ -39,7 +39,7 @@ LL |     fn foo2<'a>(&'a self, other: &Z) {
 LL |         let _ = &mut self.x; //~ ERROR cannot borrow
    |                 ^^^^^^^^^^^ `self` is a `&` reference, so the data it refers to cannot be borrowed as mutable
 
-error[E0596]: cannot borrow immutable item `other.x` as mutable
+error[E0596]: cannot borrow `other.x` as mutable, as it is behind a `&` reference
   --> $DIR/issue-39544.rs:36:17
    |
 LL |     fn foo2<'a>(&'a self, other: &Z) {
@@ -48,7 +48,7 @@ LL |         let _ = &mut self.x; //~ ERROR cannot borrow
 LL |         let _ = &mut other.x; //~ ERROR cannot borrow
    |                 ^^^^^^^^^^^^ `other` is a `&` reference, so the data it refers to cannot be borrowed as mutable
 
-error[E0596]: cannot borrow immutable item `self.x` as mutable
+error[E0596]: cannot borrow `self.x` as mutable, as it is behind a `&` reference
   --> $DIR/issue-39544.rs:40:17
    |
 LL |     fn foo3<'a>(self: &'a Self, other: &Z) {
@@ -56,7 +56,7 @@ LL |     fn foo3<'a>(self: &'a Self, other: &Z) {
 LL |         let _ = &mut self.x; //~ ERROR cannot borrow
    |                 ^^^^^^^^^^^ `self` is a `&` reference, so the data it refers to cannot be borrowed as mutable
 
-error[E0596]: cannot borrow immutable item `other.x` as mutable
+error[E0596]: cannot borrow `other.x` as mutable, as it is behind a `&` reference
   --> $DIR/issue-39544.rs:41:17
    |
 LL |     fn foo3<'a>(self: &'a Self, other: &Z) {
@@ -65,7 +65,7 @@ LL |         let _ = &mut self.x; //~ ERROR cannot borrow
 LL |         let _ = &mut other.x; //~ ERROR cannot borrow
    |                 ^^^^^^^^^^^^ `other` is a `&` reference, so the data it refers to cannot be borrowed as mutable
 
-error[E0596]: cannot borrow immutable item `other.x` as mutable
+error[E0596]: cannot borrow `other.x` as mutable, as it is behind a `&` reference
   --> $DIR/issue-39544.rs:45:17
    |
 LL |     fn foo4(other: &Z) {
@@ -73,7 +73,7 @@ LL |     fn foo4(other: &Z) {
 LL |         let _ = &mut other.x; //~ ERROR cannot borrow
    |                 ^^^^^^^^^^^^ `other` is a `&` reference, so the data it refers to cannot be borrowed as mutable
 
-error[E0596]: cannot borrow immutable item `z.x` as mutable
+error[E0596]: cannot borrow `z.x` as mutable, as `z` is not declared as mutable
   --> $DIR/issue-39544.rs:51:13
    |
 LL | pub fn with_arg(z: Z, w: &Z) {
@@ -81,7 +81,7 @@ LL | pub fn with_arg(z: Z, w: &Z) {
 LL |     let _ = &mut z.x; //~ ERROR cannot borrow
    |             ^^^^^^^^ cannot borrow as mutable
 
-error[E0596]: cannot borrow immutable item `w.x` as mutable
+error[E0596]: cannot borrow `w.x` as mutable, as it is behind a `&` reference
   --> $DIR/issue-39544.rs:52:13
    |
 LL | pub fn with_arg(z: Z, w: &Z) {
diff --git a/src/test/ui/did_you_mean/issue-40823.nll.stderr b/src/test/ui/did_you_mean/issue-40823.nll.stderr
index 1fd75de8128..44ae058cc28 100644
--- a/src/test/ui/did_you_mean/issue-40823.nll.stderr
+++ b/src/test/ui/did_you_mean/issue-40823.nll.stderr
@@ -1,4 +1,4 @@
-error[E0596]: cannot borrow immutable item `*buf` as mutable
+error[E0596]: cannot borrow `*buf` as mutable, as it is behind a `&` reference
   --> $DIR/issue-40823.rs:13:5
    |
 LL |     let mut buf = &[1, 2, 3, 4];
diff --git a/src/test/ui/error-codes/E0017.nll.stderr b/src/test/ui/error-codes/E0017.nll.stderr
index addbbf4434f..08708d213d3 100644
--- a/src/test/ui/error-codes/E0017.nll.stderr
+++ b/src/test/ui/error-codes/E0017.nll.stderr
@@ -10,7 +10,7 @@ error[E0017]: references in statics may only refer to immutable values
 LL | static STATIC_REF: &'static mut i32 = &mut X; //~ ERROR E0017
    |                                       ^^^^^^ statics require immutable values
 
-error[E0596]: cannot borrow immutable item `X` as mutable
+error[E0596]: cannot borrow immutable static item `X` as mutable
   --> $DIR/E0017.rs:15:39
    |
 LL | static STATIC_REF: &'static mut i32 = &mut X; //~ ERROR E0017
diff --git a/src/test/ui/error-codes/E0388.nll.stderr b/src/test/ui/error-codes/E0388.nll.stderr
index 0238ca6b623..a048374a497 100644
--- a/src/test/ui/error-codes/E0388.nll.stderr
+++ b/src/test/ui/error-codes/E0388.nll.stderr
@@ -10,7 +10,7 @@ error[E0017]: references in statics may only refer to immutable values
 LL | static STATIC_REF: &'static mut i32 = &mut X; //~ ERROR E0017
    |                                       ^^^^^^ statics require immutable values
 
-error[E0596]: cannot borrow immutable item `X` as mutable
+error[E0596]: cannot borrow immutable static item `X` as mutable
   --> $DIR/E0388.rs:15:39
    |
 LL | static STATIC_REF: &'static mut i32 = &mut X; //~ ERROR E0017
diff --git a/src/test/ui/fn-closure-mutable-capture.nll.stderr b/src/test/ui/fn-closure-mutable-capture.nll.stderr
index 7ef21d3720d..ff06256088f 100644
--- a/src/test/ui/fn-closure-mutable-capture.nll.stderr
+++ b/src/test/ui/fn-closure-mutable-capture.nll.stderr
@@ -1,8 +1,14 @@
-error[E0594]: cannot assign to `x` which is behind a `&` reference
+error[E0594]: cannot assign to `x`, as it is a captured variable in a `Fn` closure
   --> $DIR/fn-closure-mutable-capture.rs:15:17
    |
 LL |     bar(move || x = 1);
    |                 ^^^^^ cannot assign
+   |
+help: consider changing this to accept closures that implement `FnMut`
+  --> $DIR/fn-closure-mutable-capture.rs:15:9
+   |
+LL |     bar(move || x = 1);
+   |         ^^^^^^^^^^^^^
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/issue-21600.nll.stderr b/src/test/ui/issue-21600.nll.stderr
index b5b9cefb706..5fe8dc45ccc 100644
--- a/src/test/ui/issue-21600.nll.stderr
+++ b/src/test/ui/issue-21600.nll.stderr
@@ -1,14 +1,33 @@
-error[E0596]: cannot borrow immutable item `x` as mutable
+error[E0596]: cannot borrow `x` as mutable, as it is a captured variable in a `Fn` closure
   --> $DIR/issue-21600.rs:24:20
    |
 LL |         call_it(|| x.gen_mut()); //~ ERROR cannot borrow data mutably in a captured outer
    |                    ^ cannot borrow as mutable
+   |
+help: consider changing this to accept closures that implement `FnMut`
+  --> $DIR/issue-21600.rs:24:17
+   |
+LL |         call_it(|| x.gen_mut()); //~ ERROR cannot borrow data mutably in a captured outer
+   |                 ^^^^^^^^^^^^^^
 
-error[E0596]: cannot borrow immutable item `x` as mutable
+error[E0596]: cannot borrow `x` as mutable, as it is a captured variable in a `Fn` closure
   --> $DIR/issue-21600.rs:24:17
    |
 LL |         call_it(|| x.gen_mut()); //~ ERROR cannot borrow data mutably in a captured outer
-   |                 ^^^^^^^^^^^^^^ cannot borrow as mutable
+   |                 ^^ - mutable borrow occurs due to use of `x` in closure
+   |                 |
+   |                 cannot borrow as mutable
+   |
+help: consider changing this to accept closures that implement `FnMut`
+  --> $DIR/issue-21600.rs:22:13
+   |
+LL |       call_it(|| {
+   |  _____________^
+LL | |         call_it(|| x.gen());
+LL | |         call_it(|| x.gen_mut()); //~ ERROR cannot borrow data mutably in a captured outer
+LL | |         //~^ ERROR cannot borrow data mutably in a captured outer
+LL | |     });
+   | |_____^
 
 error: aborting due to 2 previous errors
 
diff --git a/src/test/ui/issue-27282-reborrow-ref-mut-in-guard.rs b/src/test/ui/issue-27282-reborrow-ref-mut-in-guard.rs
index 5d445c63ef4..39d54f6e7ae 100644
--- a/src/test/ui/issue-27282-reborrow-ref-mut-in-guard.rs
+++ b/src/test/ui/issue-27282-reborrow-ref-mut-in-guard.rs
@@ -24,7 +24,7 @@ fn main() {
     match b {
         &mut false => {},
         ref mut r if { (|| { let bar = &mut *r; **bar = false; })();
-                             //~^ ERROR cannot borrow immutable item `*r` as mutable
+        //~^ ERROR cannot borrow `r` as mutable, as it is immutable for the pattern guard
                              false } => { &mut *r; },
         &mut true => { println!("You might think we should get here"); },
         _ => panic!("surely we could never get here, since rustc warns it is unreachable."),
diff --git a/src/test/ui/issue-27282-reborrow-ref-mut-in-guard.stderr b/src/test/ui/issue-27282-reborrow-ref-mut-in-guard.stderr
index d767fdde9f2..660902880ac 100644
--- a/src/test/ui/issue-27282-reborrow-ref-mut-in-guard.stderr
+++ b/src/test/ui/issue-27282-reborrow-ref-mut-in-guard.stderr
@@ -1,8 +1,12 @@
-error[E0596]: cannot borrow immutable item `*r` as mutable
-  --> $DIR/issue-27282-reborrow-ref-mut-in-guard.rs:26:24
+error[E0596]: cannot borrow `r` as mutable, as it is immutable for the pattern guard
+  --> $DIR/issue-27282-reborrow-ref-mut-in-guard.rs:26:25
    |
 LL |         ref mut r if { (|| { let bar = &mut *r; **bar = false; })();
-   |                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot borrow as mutable
+   |                         ^^                   - mutable borrow occurs due to use of `r` in closure
+   |                         |
+   |                         cannot borrow as mutable
+   |
+   = note: variables bound in patterns are immutable until the end of the pattern guard
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/issue-33819.nll.stderr b/src/test/ui/issue-33819.nll.stderr
index 88728207921..78ad31a04c8 100644
--- a/src/test/ui/issue-33819.nll.stderr
+++ b/src/test/ui/issue-33819.nll.stderr
@@ -1,4 +1,4 @@
-error[E0596]: cannot borrow immutable item `v` as mutable
+error[E0596]: cannot borrow `v` as mutable, as it is not declared as mutable
   --> $DIR/issue-33819.rs:13:34
    |
 LL |         Some(ref v) => { let a = &mut v; },
diff --git a/src/test/ui/issue-36400.nll.stderr b/src/test/ui/issue-36400.nll.stderr
index be10b1d517b..e4347f0ece8 100644
--- a/src/test/ui/issue-36400.nll.stderr
+++ b/src/test/ui/issue-36400.nll.stderr
@@ -1,4 +1,4 @@
-error[E0596]: cannot borrow immutable item `*x` as mutable
+error[E0596]: cannot borrow `*x` as mutable, as `x` is not declared as mutable
   --> $DIR/issue-36400.rs:15:7
    |
 LL |     let x = Box::new(3);
diff --git a/src/test/ui/issue-41726.nll.stderr b/src/test/ui/issue-41726.nll.stderr
index feeb63e1ebb..06ff743a0f5 100644
--- a/src/test/ui/issue-41726.nll.stderr
+++ b/src/test/ui/issue-41726.nll.stderr
@@ -1,4 +1,4 @@
-error[E0596]: cannot borrow immutable item as mutable
+error[E0596]: cannot borrow data in a `&` reference as mutable
   --> $DIR/issue-41726.rs:15:9
    |
 LL |         things[src.as_str()].sort(); //~ ERROR cannot borrow immutable
diff --git a/src/test/ui/issue-42344.nll.stderr b/src/test/ui/issue-42344.nll.stderr
index 02f6ffe1734..1abc448dbb0 100644
--- a/src/test/ui/issue-42344.nll.stderr
+++ b/src/test/ui/issue-42344.nll.stderr
@@ -1,4 +1,4 @@
-error[E0596]: cannot borrow immutable item `*TAB[..]` as mutable
+error[E0596]: cannot borrow `*TAB[..]` as mutable, as `TAB` is an immutable static item
   --> $DIR/issue-42344.rs:14:5
    |
 LL |     TAB[0].iter_mut(); //~ ERROR cannot borrow data mutably in a `&` reference [E0389]
diff --git a/src/test/ui/issue-46023.ast.nll.stderr b/src/test/ui/issue-46023.ast.nll.stderr
index 2fc2924c2ec..bda629d48af 100644
--- a/src/test/ui/issue-46023.ast.nll.stderr
+++ b/src/test/ui/issue-46023.ast.nll.stderr
@@ -1,6 +1,9 @@
-error[E0594]: cannot assign to immutable item `x`
+error[E0594]: cannot assign to `x`, as it is not declared as mutable
   --> $DIR/issue-46023.rs:18:9
    |
+LL |     let x = 0;
+   |         - help: consider changing this to be mutable: `mut x`
+...
 LL |         x = 1;
    |         ^^^^^ cannot assign
 
diff --git a/src/test/ui/issue-46023.mir.stderr b/src/test/ui/issue-46023.mir.stderr
index 2fc2924c2ec..bda629d48af 100644
--- a/src/test/ui/issue-46023.mir.stderr
+++ b/src/test/ui/issue-46023.mir.stderr
@@ -1,6 +1,9 @@
-error[E0594]: cannot assign to immutable item `x`
+error[E0594]: cannot assign to `x`, as it is not declared as mutable
   --> $DIR/issue-46023.rs:18:9
    |
+LL |     let x = 0;
+   |         - help: consider changing this to be mutable: `mut x`
+...
 LL |         x = 1;
    |         ^^^^^ cannot assign
 
diff --git a/src/test/ui/issue-46023.rs b/src/test/ui/issue-46023.rs
index d51d92570fd..d5c8cd6d0f8 100644
--- a/src/test/ui/issue-46023.rs
+++ b/src/test/ui/issue-46023.rs
@@ -16,7 +16,7 @@ fn main() {
 
     (move || {
         x = 1;
-        //[mir]~^ ERROR cannot assign to immutable item `x` [E0594]
+        //[mir]~^ ERROR cannot assign to `x`, as it is not declared as mutable [E0594]
         //[ast]~^^ ERROR cannot assign to captured outer variable in an `FnMut` closure [E0594]
     })()
 }
diff --git a/src/test/ui/issue-46604.ast.nll.stderr b/src/test/ui/issue-46604.ast.nll.stderr
index b947bec6397..a4ee0d4e2a3 100644
--- a/src/test/ui/issue-46604.ast.nll.stderr
+++ b/src/test/ui/issue-46604.ast.nll.stderr
@@ -4,7 +4,7 @@ error[E0017]: references in statics may only refer to immutable values
 LL | static buf: &mut [u8] = &mut [1u8,2,3,4,5,7];   //[ast]~ ERROR E0017
    |                         ^^^^^^^^^^^^^^^^^^^^ statics require immutable values
 
-error[E0594]: cannot assign to immutable item `buf[..]`
+error[E0594]: cannot assign to `buf[..]`, as `buf` is an immutable static item
   --> $DIR/issue-46604.rs:20:5
    |
 LL |     buf[0]=2;                                   //[ast]~ ERROR E0389
diff --git a/src/test/ui/issue-46604.mir.stderr b/src/test/ui/issue-46604.mir.stderr
index b947bec6397..a4ee0d4e2a3 100644
--- a/src/test/ui/issue-46604.mir.stderr
+++ b/src/test/ui/issue-46604.mir.stderr
@@ -4,7 +4,7 @@ error[E0017]: references in statics may only refer to immutable values
 LL | static buf: &mut [u8] = &mut [1u8,2,3,4,5,7];   //[ast]~ ERROR E0017
    |                         ^^^^^^^^^^^^^^^^^^^^ statics require immutable values
 
-error[E0594]: cannot assign to immutable item `buf[..]`
+error[E0594]: cannot assign to `buf[..]`, as `buf` is an immutable static item
   --> $DIR/issue-46604.rs:20:5
    |
 LL |     buf[0]=2;                                   //[ast]~ ERROR E0389
diff --git a/src/test/ui/lifetime-errors/ex3-both-anon-regions-using-fn-items.nll.stderr b/src/test/ui/lifetime-errors/ex3-both-anon-regions-using-fn-items.nll.stderr
index 01e26980323..d5bba6649a2 100644
--- a/src/test/ui/lifetime-errors/ex3-both-anon-regions-using-fn-items.nll.stderr
+++ b/src/test/ui/lifetime-errors/ex3-both-anon-regions-using-fn-items.nll.stderr
@@ -12,7 +12,7 @@ LL | fn foo(x:fn(&u8, &u8), y: Vec<&u8>, z: &u8) {
 LL |   y.push(z); //~ ERROR lifetime mismatch
    |   ^ ...but data from `z` flows into `y` here
 
-error[E0596]: cannot borrow immutable item `y` as mutable
+error[E0596]: cannot borrow `y` as mutable, as it is not declared as mutable
   --> $DIR/ex3-both-anon-regions-using-fn-items.rs:11:3
    |
 LL | fn foo(x:fn(&u8, &u8), y: Vec<&u8>, z: &u8) {
diff --git a/src/test/ui/lifetime-errors/ex3-both-anon-regions-using-trait-objects.nll.stderr b/src/test/ui/lifetime-errors/ex3-both-anon-regions-using-trait-objects.nll.stderr
index a61d49fc953..0608b3be8b3 100644
--- a/src/test/ui/lifetime-errors/ex3-both-anon-regions-using-trait-objects.nll.stderr
+++ b/src/test/ui/lifetime-errors/ex3-both-anon-regions-using-trait-objects.nll.stderr
@@ -12,7 +12,7 @@ LL | fn foo(x:Box<Fn(&u8, &u8)> , y: Vec<&u8>, z: &u8) {
 LL |   y.push(z); //~ ERROR lifetime mismatch
    |   ^ ...but data from `z` flows into `y` here
 
-error[E0596]: cannot borrow immutable item `y` as mutable
+error[E0596]: cannot borrow `y` as mutable, as it is not declared as mutable
   --> $DIR/ex3-both-anon-regions-using-trait-objects.rs:11:3
    |
 LL | fn foo(x:Box<Fn(&u8, &u8)> , y: Vec<&u8>, z: &u8) {
diff --git a/src/test/ui/macros/span-covering-argument-1.nll.stderr b/src/test/ui/macros/span-covering-argument-1.nll.stderr
index 213eddee720..3947f6c0f11 100644
--- a/src/test/ui/macros/span-covering-argument-1.nll.stderr
+++ b/src/test/ui/macros/span-covering-argument-1.nll.stderr
@@ -1,4 +1,4 @@
-error[E0596]: cannot borrow immutable item `foo` as mutable
+error[E0596]: cannot borrow `foo` as mutable, as it is not declared as mutable
   --> $DIR/span-covering-argument-1.rs:15:14
    |
 LL |             let $s = 0;
diff --git a/src/test/ui/nll/closure-captures.rs b/src/test/ui/nll/closure-captures.rs
new file mode 100644
index 00000000000..03a22bb79a8
--- /dev/null
+++ b/src/test/ui/nll/closure-captures.rs
@@ -0,0 +1,68 @@
+// 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.
+
+// Some cases with closures that might be problems
+
+#![allow(unused)]
+#![feature(nll)]
+
+// Should have one error per assigment
+
+fn one_closure(x: i32) {
+    ||
+    x = 1; //~ ERROR
+    move ||
+    x = 1; //~ ERROR
+}
+
+fn two_closures(x: i32) {
+    || {
+        ||
+        x = 1; //~ ERROR
+    };
+    move || {
+        ||
+        x = 1; //~ ERROR
+    };
+}
+
+fn fn_ref<F: Fn()>(f: F) -> F { f }
+
+fn two_closures_ref_mut(mut x: i32) {
+    fn_ref(|| {
+        || //~ ERROR
+         x = 1;}
+    );
+    fn_ref(move || {
+        ||  //~ ERROR
+    x = 1;});
+}
+
+// This still gives two messages, but it requires two things to be fixed.
+fn two_closures_ref(x: i32) {
+    fn_ref(|| {
+        || //~ ERROR
+         x = 1;} //~ ERROR
+    );
+    fn_ref(move || {
+        ||  //~ ERROR
+    x = 1;}); //~ ERROR
+}
+
+fn two_closures_two_refs(x: &mut i32) {
+    fn_ref(|| {
+        || //~ ERROR
+        *x = 1;});
+    fn_ref(move || {
+        || //~ ERROR
+        *x = 1;});
+}
+
+fn main() {}
diff --git a/src/test/ui/nll/closure-captures.stderr b/src/test/ui/nll/closure-captures.stderr
new file mode 100644
index 00000000000..a03e104a1b1
--- /dev/null
+++ b/src/test/ui/nll/closure-captures.stderr
@@ -0,0 +1,160 @@
+error[E0594]: cannot assign to `x`, as it is not declared as mutable
+  --> $DIR/closure-captures.rs:20:5
+   |
+LL | fn one_closure(x: i32) {
+   |                - help: consider changing this to be mutable: `mut x`
+LL |     ||
+LL |     x = 1; //~ ERROR
+   |     ^^^^^ cannot assign
+
+error[E0594]: cannot assign to `x`, as it is not declared as mutable
+  --> $DIR/closure-captures.rs:22:5
+   |
+LL | fn one_closure(x: i32) {
+   |                - help: consider changing this to be mutable: `mut x`
+...
+LL |     x = 1; //~ ERROR
+   |     ^^^^^ cannot assign
+
+error[E0594]: cannot assign to `x`, as it is not declared as mutable
+  --> $DIR/closure-captures.rs:28:9
+   |
+LL | fn two_closures(x: i32) {
+   |                 - help: consider changing this to be mutable: `mut x`
+...
+LL |         x = 1; //~ ERROR
+   |         ^^^^^ cannot assign
+
+error[E0594]: cannot assign to `x`, as it is not declared as mutable
+  --> $DIR/closure-captures.rs:32:9
+   |
+LL | fn two_closures(x: i32) {
+   |                 - help: consider changing this to be mutable: `mut x`
+...
+LL |         x = 1; //~ ERROR
+   |         ^^^^^ cannot assign
+
+error[E0596]: cannot borrow `x` as mutable, as it is a captured variable in a `Fn` closure
+  --> $DIR/closure-captures.rs:40:9
+   |
+LL |         || //~ ERROR
+   |         ^^ cannot borrow as mutable
+LL |          x = 1;}
+   |          - mutable borrow occurs due to use of `x` in closure
+   |
+help: consider changing this to accept closures that implement `FnMut`
+  --> $DIR/closure-captures.rs:39:12
+   |
+LL |       fn_ref(|| {
+   |  ____________^
+LL | |         || //~ ERROR
+LL | |          x = 1;}
+   | |________________^
+
+error[E0596]: cannot borrow `x` as mutable, as it is a captured variable in a `Fn` closure
+  --> $DIR/closure-captures.rs:44:9
+   |
+LL |         ||  //~ ERROR
+   |         ^^ cannot borrow as mutable
+LL |     x = 1;});
+   |     - mutable borrow occurs due to use of `x` in closure
+   |
+help: consider changing this to accept closures that implement `FnMut`
+  --> $DIR/closure-captures.rs:43:12
+   |
+LL |       fn_ref(move || {
+   |  ____________^
+LL | |         ||  //~ ERROR
+LL | |     x = 1;});
+   | |___________^
+
+error[E0594]: cannot assign to `x`, as it is not declared as mutable
+  --> $DIR/closure-captures.rs:52:10
+   |
+LL | fn two_closures_ref(x: i32) {
+   |                     - help: consider changing this to be mutable: `mut x`
+...
+LL |          x = 1;} //~ ERROR
+   |          ^^^^^ cannot assign
+
+error[E0596]: cannot borrow `x` as mutable, as it is a captured variable in a `Fn` closure
+  --> $DIR/closure-captures.rs:51:9
+   |
+LL |         || //~ ERROR
+   |         ^^ cannot borrow as mutable
+LL |          x = 1;} //~ ERROR
+   |          - mutable borrow occurs due to use of `x` in closure
+   |
+help: consider changing this to accept closures that implement `FnMut`
+  --> $DIR/closure-captures.rs:50:12
+   |
+LL |       fn_ref(|| {
+   |  ____________^
+LL | |         || //~ ERROR
+LL | |          x = 1;} //~ ERROR
+   | |________________^
+
+error[E0594]: cannot assign to `x`, as it is not declared as mutable
+  --> $DIR/closure-captures.rs:56:5
+   |
+LL | fn two_closures_ref(x: i32) {
+   |                     - help: consider changing this to be mutable: `mut x`
+...
+LL |     x = 1;}); //~ ERROR
+   |     ^^^^^ cannot assign
+
+error[E0596]: cannot borrow `x` as mutable, as it is a captured variable in a `Fn` closure
+  --> $DIR/closure-captures.rs:55:9
+   |
+LL |         ||  //~ ERROR
+   |         ^^ cannot borrow as mutable
+LL |     x = 1;}); //~ ERROR
+   |     - mutable borrow occurs due to use of `x` in closure
+   |
+help: consider changing this to accept closures that implement `FnMut`
+  --> $DIR/closure-captures.rs:54:12
+   |
+LL |       fn_ref(move || {
+   |  ____________^
+LL | |         ||  //~ ERROR
+LL | |     x = 1;}); //~ ERROR
+   | |___________^
+
+error[E0596]: cannot borrow `x` as mutable, as it is a captured variable in a `Fn` closure
+  --> $DIR/closure-captures.rs:61:9
+   |
+LL |         || //~ ERROR
+   |         ^^ cannot borrow as mutable
+LL |         *x = 1;});
+   |          - mutable borrow occurs due to use of `x` in closure
+   |
+help: consider changing this to accept closures that implement `FnMut`
+  --> $DIR/closure-captures.rs:60:12
+   |
+LL |       fn_ref(|| {
+   |  ____________^
+LL | |         || //~ ERROR
+LL | |         *x = 1;});
+   | |________________^
+
+error[E0596]: cannot borrow `x` as mutable, as it is a captured variable in a `Fn` closure
+  --> $DIR/closure-captures.rs:64:9
+   |
+LL |         || //~ ERROR
+   |         ^^ cannot borrow as mutable
+LL |         *x = 1;});
+   |          - mutable borrow occurs due to use of `x` in closure
+   |
+help: consider changing this to accept closures that implement `FnMut`
+  --> $DIR/closure-captures.rs:63:12
+   |
+LL |       fn_ref(move || {
+   |  ____________^
+LL | |         || //~ ERROR
+LL | |         *x = 1;});
+   | |________________^
+
+error: aborting due to 12 previous errors
+
+Some errors occurred: E0594, E0596.
+For more information about an error, try `rustc --explain E0594`.
diff --git a/src/test/ui/nll/generator-upvar-mutability.stderr b/src/test/ui/nll/generator-upvar-mutability.stderr
index 9c5c57687a2..180a330a30e 100644
--- a/src/test/ui/nll/generator-upvar-mutability.stderr
+++ b/src/test/ui/nll/generator-upvar-mutability.stderr
@@ -1,6 +1,9 @@
-error[E0594]: cannot assign to immutable item `x`
+error[E0594]: cannot assign to `x`, as it is not declared as mutable
   --> $DIR/generator-upvar-mutability.rs:18:9
    |
+LL |     let x = 0;
+   |         - help: consider changing this to be mutable: `mut x`
+LL |     move || {
 LL |         x = 1;
    |         ^^^^^ cannot assign
 
diff --git a/src/test/ui/span/borrowck-borrow-overloaded-auto-deref-mut.nll.stderr b/src/test/ui/span/borrowck-borrow-overloaded-auto-deref-mut.nll.stderr
index 3282fbba6c5..366bfc8fa20 100644
--- a/src/test/ui/span/borrowck-borrow-overloaded-auto-deref-mut.nll.stderr
+++ b/src/test/ui/span/borrowck-borrow-overloaded-auto-deref-mut.nll.stderr
@@ -1,4 +1,4 @@
-error[E0596]: cannot borrow immutable item `x` as mutable
+error[E0596]: cannot borrow `x` as mutable, as it is not declared as mutable
   --> $DIR/borrowck-borrow-overloaded-auto-deref-mut.rs:63:24
    |
 LL | fn deref_mut_field1(x: Own<Point>) {
@@ -6,7 +6,7 @@ LL | fn deref_mut_field1(x: Own<Point>) {
 LL |     let __isize = &mut x.y; //~ ERROR cannot borrow
    |                        ^ cannot borrow as mutable
 
-error[E0596]: cannot borrow immutable item `*x` as mutable
+error[E0596]: cannot borrow `*x` as mutable, as it is behind a `&` reference
   --> $DIR/borrowck-borrow-overloaded-auto-deref-mut.rs:75:10
    |
 LL | fn deref_extend_mut_field1(x: &Own<Point>) -> &mut isize {
@@ -14,7 +14,7 @@ LL | fn deref_extend_mut_field1(x: &Own<Point>) -> &mut isize {
 LL |     &mut x.y //~ ERROR cannot borrow
    |          ^ `x` is a `&` reference, so the data it refers to cannot be borrowed as mutable
 
-error[E0596]: cannot borrow immutable item `x` as mutable
+error[E0596]: cannot borrow `x` as mutable, as it is not declared as mutable
   --> $DIR/borrowck-borrow-overloaded-auto-deref-mut.rs:98:5
    |
 LL | fn assign_field1<'a>(x: Own<Point>) {
@@ -22,7 +22,7 @@ LL | fn assign_field1<'a>(x: Own<Point>) {
 LL |     x.y = 3; //~ ERROR cannot borrow
    |     ^ cannot borrow as mutable
 
-error[E0596]: cannot borrow immutable item `*x` as mutable
+error[E0596]: cannot borrow `*x` as mutable, as it is behind a `&` reference
   --> $DIR/borrowck-borrow-overloaded-auto-deref-mut.rs:102:5
    |
 LL | fn assign_field2<'a>(x: &'a Own<Point>) {
@@ -30,7 +30,7 @@ LL | fn assign_field2<'a>(x: &'a Own<Point>) {
 LL |     x.y = 3; //~ ERROR cannot borrow
    |     ^ `x` is a `&` reference, so the data it refers to cannot be borrowed as mutable
 
-error[E0596]: cannot borrow immutable item `x` as mutable
+error[E0596]: cannot borrow `x` as mutable, as it is not declared as mutable
   --> $DIR/borrowck-borrow-overloaded-auto-deref-mut.rs:119:5
    |
 LL | fn deref_mut_method1(x: Own<Point>) {
@@ -38,7 +38,7 @@ LL | fn deref_mut_method1(x: Own<Point>) {
 LL |     x.set(0, 0); //~ ERROR cannot borrow
    |     ^ cannot borrow as mutable
 
-error[E0596]: cannot borrow immutable item `*x` as mutable
+error[E0596]: cannot borrow `*x` as mutable, as it is behind a `&` reference
   --> $DIR/borrowck-borrow-overloaded-auto-deref-mut.rs:131:5
    |
 LL | fn deref_extend_mut_method1(x: &Own<Point>) -> &mut isize {
@@ -46,7 +46,7 @@ LL | fn deref_extend_mut_method1(x: &Own<Point>) -> &mut isize {
 LL |     x.y_mut() //~ ERROR cannot borrow
    |     ^ `x` is a `&` reference, so the data it refers to cannot be borrowed as mutable
 
-error[E0596]: cannot borrow immutable item `x` as mutable
+error[E0596]: cannot borrow `x` as mutable, as it is not declared as mutable
   --> $DIR/borrowck-borrow-overloaded-auto-deref-mut.rs:139:6
    |
 LL | fn assign_method1<'a>(x: Own<Point>) {
@@ -54,7 +54,7 @@ LL | fn assign_method1<'a>(x: Own<Point>) {
 LL |     *x.y_mut() = 3; //~ ERROR cannot borrow
    |      ^ cannot borrow as mutable
 
-error[E0596]: cannot borrow immutable item `*x` as mutable
+error[E0596]: cannot borrow `*x` as mutable, as it is behind a `&` reference
   --> $DIR/borrowck-borrow-overloaded-auto-deref-mut.rs:143:6
    |
 LL | fn assign_method2<'a>(x: &'a Own<Point>) {
diff --git a/src/test/ui/span/borrowck-borrow-overloaded-deref-mut.nll.stderr b/src/test/ui/span/borrowck-borrow-overloaded-deref-mut.nll.stderr
index 0b1bfd8cee6..69bf246ff3f 100644
--- a/src/test/ui/span/borrowck-borrow-overloaded-deref-mut.nll.stderr
+++ b/src/test/ui/span/borrowck-borrow-overloaded-deref-mut.nll.stderr
@@ -1,4 +1,4 @@
-error[E0596]: cannot borrow immutable item `x` as mutable
+error[E0596]: cannot borrow `x` as mutable, as it is not declared as mutable
   --> $DIR/borrowck-borrow-overloaded-deref-mut.rs:39:25
    |
 LL | fn deref_mut1(x: Own<isize>) {
@@ -6,7 +6,7 @@ LL | fn deref_mut1(x: Own<isize>) {
 LL |     let __isize = &mut *x; //~ ERROR cannot borrow
    |                         ^ cannot borrow as mutable
 
-error[E0596]: cannot borrow immutable item `*x` as mutable
+error[E0596]: cannot borrow `*x` as mutable, as it is behind a `&` reference
   --> $DIR/borrowck-borrow-overloaded-deref-mut.rs:51:11
    |
 LL | fn deref_extend_mut1<'a>(x: &'a Own<isize>) -> &'a mut isize {
@@ -14,7 +14,7 @@ LL | fn deref_extend_mut1<'a>(x: &'a Own<isize>) -> &'a mut isize {
 LL |     &mut **x //~ ERROR cannot borrow
    |           ^^ `x` is a `&` reference, so the data it refers to cannot be borrowed as mutable
 
-error[E0596]: cannot borrow immutable item `x` as mutable
+error[E0596]: cannot borrow `x` as mutable, as it is not declared as mutable
   --> $DIR/borrowck-borrow-overloaded-deref-mut.rs:59:6
    |
 LL | fn assign1<'a>(x: Own<isize>) {
@@ -22,7 +22,7 @@ LL | fn assign1<'a>(x: Own<isize>) {
 LL |     *x = 3; //~ ERROR cannot borrow
    |      ^ cannot borrow as mutable
 
-error[E0596]: cannot borrow immutable item `*x` as mutable
+error[E0596]: cannot borrow `*x` as mutable, as it is behind a `&` reference
   --> $DIR/borrowck-borrow-overloaded-deref-mut.rs:63:6
    |
 LL | fn assign2<'a>(x: &'a Own<isize>) {
diff --git a/src/test/ui/span/borrowck-call-is-borrow-issue-12224.nll.stderr b/src/test/ui/span/borrowck-call-is-borrow-issue-12224.nll.stderr
index c4bdef21de4..f752015c650 100644
--- a/src/test/ui/span/borrowck-call-is-borrow-issue-12224.nll.stderr
+++ b/src/test/ui/span/borrowck-call-is-borrow-issue-12224.nll.stderr
@@ -12,7 +12,7 @@ LL | |         f((Box::new(|| {})))
 LL | |     }));
    | |_______- borrow later used here
 
-error[E0596]: cannot borrow immutable item `*f` as mutable
+error[E0596]: cannot borrow `*f` as mutable, as it is behind a `&` reference
   --> $DIR/borrowck-call-is-borrow-issue-12224.rs:35:5
    |
 LL | fn test2<F>(f: &F) where F: FnMut() {
@@ -20,7 +20,7 @@ LL | fn test2<F>(f: &F) where F: FnMut() {
 LL |     (*f)();
    |     ^^^^ `f` is a `&` reference, so the data it refers to cannot be borrowed as mutable
 
-error[E0596]: cannot borrow immutable item `*f.f` as mutable
+error[E0596]: cannot borrow `*f.f` as mutable, as it is behind a `&` reference
   --> $DIR/borrowck-call-is-borrow-issue-12224.rs:44:5
    |
 LL | fn test4(f: &Test) {
diff --git a/src/test/ui/span/borrowck-call-method-from-mut-aliasable.nll.stderr b/src/test/ui/span/borrowck-call-method-from-mut-aliasable.nll.stderr
index 0bc614589e3..e9c0505af68 100644
--- a/src/test/ui/span/borrowck-call-method-from-mut-aliasable.nll.stderr
+++ b/src/test/ui/span/borrowck-call-method-from-mut-aliasable.nll.stderr
@@ -1,4 +1,4 @@
-error[E0596]: cannot borrow immutable item `*x` as mutable
+error[E0596]: cannot borrow `*x` as mutable, as it is behind a `&` reference
   --> $DIR/borrowck-call-method-from-mut-aliasable.rs:27:5
    |
 LL | fn b(x: &Foo) {
diff --git a/src/test/ui/span/borrowck-fn-in-const-b.nll.stderr b/src/test/ui/span/borrowck-fn-in-const-b.nll.stderr
index 1cb2c92833c..5e47d3271d4 100644
--- a/src/test/ui/span/borrowck-fn-in-const-b.nll.stderr
+++ b/src/test/ui/span/borrowck-fn-in-const-b.nll.stderr
@@ -1,4 +1,4 @@
-error[E0596]: cannot borrow immutable item `*x` as mutable
+error[E0596]: cannot borrow `*x` as mutable, as it is behind a `&` reference
   --> $DIR/borrowck-fn-in-const-b.rs:17:9
    |
 LL |     fn broken(x: &Vec<String>) {
diff --git a/src/test/ui/span/borrowck-object-mutability.nll.stderr b/src/test/ui/span/borrowck-object-mutability.nll.stderr
index b318e778f87..3f0eb97c7af 100644
--- a/src/test/ui/span/borrowck-object-mutability.nll.stderr
+++ b/src/test/ui/span/borrowck-object-mutability.nll.stderr
@@ -1,4 +1,4 @@
-error[E0596]: cannot borrow immutable item `*x` as mutable
+error[E0596]: cannot borrow `*x` as mutable, as it is behind a `&` reference
   --> $DIR/borrowck-object-mutability.rs:19:5
    |
 LL | fn borrowed_receiver(x: &Foo) {
@@ -7,7 +7,7 @@ LL |     x.borrowed();
 LL |     x.borrowed_mut(); //~ ERROR cannot borrow
    |     ^ `x` is a `&` reference, so the data it refers to cannot be borrowed as mutable
 
-error[E0596]: cannot borrow immutable item `*x` as mutable
+error[E0596]: cannot borrow `*x` as mutable, as `x` is not declared as mutable
   --> $DIR/borrowck-object-mutability.rs:29:5
    |
 LL | fn owned_receiver(x: Box<Foo>) {
diff --git a/src/test/ui/span/mut-arg-hint.nll.stderr b/src/test/ui/span/mut-arg-hint.nll.stderr
index cd19059fdae..f264ea1f916 100644
--- a/src/test/ui/span/mut-arg-hint.nll.stderr
+++ b/src/test/ui/span/mut-arg-hint.nll.stderr
@@ -1,4 +1,4 @@
-error[E0596]: cannot borrow immutable item `*a` as mutable
+error[E0596]: cannot borrow `*a` as mutable, as it is behind a `&` reference
   --> $DIR/mut-arg-hint.rs:13:9
    |
 LL |     fn foo(mut a: &String) {
@@ -6,7 +6,7 @@ LL |     fn foo(mut a: &String) {
 LL |         a.push_str("bar"); //~ ERROR cannot borrow immutable borrowed content
    |         ^ `a` is a `&` reference, so the data it refers to cannot be borrowed as mutable
 
-error[E0596]: cannot borrow immutable item `*a` as mutable
+error[E0596]: cannot borrow `*a` as mutable, as it is behind a `&` reference
   --> $DIR/mut-arg-hint.rs:18:5
    |
 LL | pub fn foo<'a>(mut a: &'a String) {
@@ -14,7 +14,7 @@ LL | pub fn foo<'a>(mut a: &'a String) {
 LL |     a.push_str("foo"); //~ ERROR cannot borrow immutable borrowed content
    |     ^ `a` is a `&` reference, so the data it refers to cannot be borrowed as mutable
 
-error[E0596]: cannot borrow immutable item `*a` as mutable
+error[E0596]: cannot borrow `*a` as mutable, as it is behind a `&` reference
   --> $DIR/mut-arg-hint.rs:25:9
    |
 LL |     pub fn foo(mut a: &String) {
diff --git a/src/test/ui/trivial-bounds-inconsistent-copy-reborrow.nll.stderr b/src/test/ui/trivial-bounds-inconsistent-copy-reborrow.nll.stderr
index 1a36b0c4839..215faf6c73c 100644
--- a/src/test/ui/trivial-bounds-inconsistent-copy-reborrow.nll.stderr
+++ b/src/test/ui/trivial-bounds-inconsistent-copy-reborrow.nll.stderr
@@ -1,4 +1,4 @@
-error[E0596]: cannot borrow immutable item `**t` as mutable
+error[E0596]: cannot borrow `**t` as mutable, as it is behind a `&` reference
   --> $DIR/trivial-bounds-inconsistent-copy-reborrow.rs:16:5
    |
 LL | fn reborrow_mut<'a>(t: &'a &'a mut i32) -> &'a mut i32 where &'a mut i32: Copy {
@@ -6,7 +6,7 @@ LL | fn reborrow_mut<'a>(t: &'a &'a mut i32) -> &'a mut i32 where &'a mut i32: C
 LL |     *t //~ ERROR
    |     ^^ `t` is a `&` reference, so the data it refers to cannot be borrowed as mutable
 
-error[E0596]: cannot borrow immutable item `**t` as mutable
+error[E0596]: cannot borrow `**t` as mutable, as it is behind a `&` reference
   --> $DIR/trivial-bounds-inconsistent-copy-reborrow.rs:20:6
    |
 LL | fn copy_reborrow_mut<'a>(t: &'a &'a mut i32) -> &'a mut i32 where &'a mut i32: Copy {