about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2018-09-25 01:04:12 +0000
committerbors <bors@rust-lang.org>2018-09-25 01:04:12 +0000
commit3a2190a9cd4e38a9543ff697dd51ca8b6b68f50d (patch)
treefdf86e21d868b4f8851f7aee915cf383d33cc240
parent5c875d93855c6d577962b0f74f17374f37b219c9 (diff)
parenta830732090d49c799b3e97e70c1c4e5c011a784c (diff)
downloadrust-3a2190a9cd4e38a9543ff697dd51ca8b6b68f50d.tar.gz
rust-3a2190a9cd4e38a9543ff697dd51ca8b6b68f50d.zip
Auto merge of #53438 - matthewjasper:permissive-match-access, r=pnkfelix
[NLL] Be more permissive when checking access due to Match

Partially addresses #53114. notably, we should now have parity with AST borrowck. Matching on uninitialized values is still forbidden.

* ~~Give fake borrows for match their own `BorrowKind`~~
* ~~Allow borrows with this kind to happen on values that are already mutably borrowed.~~
* ~~Track borrows with this type even behind shared reference dereferences and consider all accesses to be deep when checking for conflicts with this borrow type. See [src/test/ui/issues/issue-27282-mutate-before-diverging-arm-3.rs](https://github.com/rust-lang/rust/commit/cb5c989598178af505fb215dd97afca8cc2b659f#diff-a2126cd3263a1f5342e2ecd5e699fbc6) for an example soundness issue this fixes (a case of #27282 that wasn't handled correctly).~~
* Create a new `BorrowKind`: `Shallow` (name can be bike-shed)
* `Shallow` borrows differ from shared borrows in that
  * When we check for access we treat them as a `Shallow(Some(_))` read
  * When we check for conflicts with them, if the borrow place is a strict prefix of the access place then we don't consider that a conflict.
    * For example, a `Shallow` borrow of `x` does not conflict with any access or borrow of `x.0` or `*x`
* Remove the current fake borrow in matches.
* When building matches, we take a `Shallow` borrow of any `Place` that we switch on or bind in a match, and any prefix of those places. (There are some optimizations where we do fewer borrows, but this shouldn't change semantics)
  * `match x { &Some(1) => (),  _ => (), }` would `Shallow` borrow `x`, `*x` and `(*x as Some).0` (the `*x` borrow is unnecessary, but I'm not sure how easy it would be to remove.)
* Replace the fake discriminant read with a `ReadForMatch`.
* Change ReadForMatch to only check for initializedness (to prevent `let x: !; match x {}`), but not conflicting borrows. It is still considered a use for liveness and `unsafe` checking.
* Give special cased error messages for this kind of borrow.

Table from the above issue after this PR

| Thing | AST | MIR | Want | Example |
| --- | --- | --- | --- |---|
| `let _ = <unsafe-field>` | 💚  | 💚  | ❌ |  [playground](https://play.rust-lang.org/?gist=bb7843e42fa5318c1043d04bd72abfe4&version=nightly&mode=debug&edition=2015) |
| `match <unsafe_field> { _ => () }` | ❌  | ❌ | ❌ | [playground](https://play.rust-lang.org/?gist=3e3af05fbf1fae28fab2aaf9412fb2ea&version=nightly&mode=debug&edition=2015) |
| `let _ = <moved>` | 💚  | 💚 | 💚 | [playground](https://play.rust-lang.org/?gist=91a6efde8288558e584aaeee0a50558b&version=nightly&mode=debug&edition=2015) |
| `match <moved> { _ => () }` | ❌ | ❌  | 💚 | [playground](https://play.rust-lang.org/?gist=804f8185040b2fe131f2c4a64b3048ca&version=nightly&mode=debug&edition=2015) |
| `let _ = <borrowed>` | 💚  | 💚 | 💚 | [playground](https://play.rust-lang.org/?gist=0e487c2893b89cb772ec2f2b7c5da876&version=nightly&mode=debug&edition=2015) |
| `match <borrowed> { _ => () }` | 💚  | 💚 | 💚 | [playground](https://play.rust-lang.org/?gist=0e487c2893b89cb772ec2f2b7c5da876&version=nightly&mode=debug&edition=2015) |

r? @nikomatsakis
-rw-r--r--src/librustc/ich/impls_mir.rs3
-rw-r--r--src/librustc/mir/mod.rs40
-rw-r--r--src/librustc/mir/tcx.rs4
-rw-r--r--src/librustc/mir/visit.rs5
-rw-r--r--src/librustc_mir/borrow_check/borrow_set.rs4
-rw-r--r--src/librustc_mir/borrow_check/error_reporting.rs54
-rw-r--r--src/librustc_mir/borrow_check/mod.rs60
-rw-r--r--src/librustc_mir/borrow_check/nll/invalidation.rs8
-rw-r--r--src/librustc_mir/borrow_check/path_utils.rs9
-rw-r--r--src/librustc_mir/borrow_check/places_conflict.rs48
-rw-r--r--src/librustc_mir/build/matches/mod.rs215
-rw-r--r--src/librustc_mir/diagnostics.rs20
-rw-r--r--src/librustc_mir/lib.rs2
-rw-r--r--src/librustc_mir/transform/cleanup_post_borrowck.rs62
-rw-r--r--src/librustc_mir/transform/mod.rs5
-rw-r--r--src/librustc_mir/util/borrowck_errors.rs23
-rw-r--r--src/test/mir-opt/box_expr.rs1
-rw-r--r--src/test/mir-opt/issue-49232.rs21
-rw-r--r--src/test/mir-opt/match_false_edges.rs143
-rw-r--r--src/test/mir-opt/remove_fake_borrows.rs122
-rw-r--r--src/test/mir-opt/validate_1.rs1
-rw-r--r--src/test/mir-opt/validate_2.rs1
-rw-r--r--src/test/mir-opt/validate_3.rs2
-rw-r--r--src/test/ui/borrowck/borrowck-anon-fields-struct.nll.stderr29
-rw-r--r--src/test/ui/borrowck/borrowck-anon-fields-tuple.nll.stderr29
-rw-r--r--src/test/ui/borrowck/borrowck-anon-fields-variant.nll.stderr29
-rw-r--r--src/test/ui/borrowck/borrowck-borrow-from-owned-ptr.nll.stderr13
-rw-r--r--src/test/ui/borrowck/borrowck-borrow-from-stack-variable.nll.stderr13
-rw-r--r--src/test/ui/borrowck/borrowck-describe-lvalue.ast.nll.stderr179
-rw-r--r--src/test/ui/borrowck/borrowck-describe-lvalue.ast.stderr12
-rw-r--r--src/test/ui/borrowck/borrowck-describe-lvalue.mir.stderr179
-rw-r--r--src/test/ui/borrowck/borrowck-describe-lvalue.rs28
-rw-r--r--src/test/ui/borrowck/borrowck-match-already-borrowed.ast.nll.stderr28
-rw-r--r--src/test/ui/borrowck/borrowck-match-already-borrowed.ast.stderr2
-rw-r--r--src/test/ui/borrowck/borrowck-match-already-borrowed.mir.stderr28
-rw-r--r--src/test/ui/borrowck/borrowck-match-already-borrowed.rs4
-rw-r--r--src/test/ui/consts/const-eval/conditional_array_execution.nll.stderr47
-rw-r--r--src/test/ui/consts/const-eval/issue-43197.nll.stderr70
-rw-r--r--src/test/ui/consts/const-eval/issue-44578.nll.stderr33
-rw-r--r--src/test/ui/issues/issue-27282-move-match-input-into-guard.rs1
-rw-r--r--src/test/ui/issues/issue-27282-move-match-input-into-guard.stderr23
-rw-r--r--src/test/ui/issues/issue-27282-mutate-before-diverging-arm-1.rs2
-rw-r--r--src/test/ui/issues/issue-27282-mutate-before-diverging-arm-1.stderr13
-rw-r--r--src/test/ui/issues/issue-27282-mutate-before-diverging-arm-2.rs2
-rw-r--r--src/test/ui/issues/issue-27282-mutate-before-diverging-arm-2.stderr13
-rw-r--r--src/test/ui/issues/issue-27282-mutate-before-diverging-arm-3.rs30
-rw-r--r--src/test/ui/issues/issue-27282-mutate-before-diverging-arm-3.stderr14
-rw-r--r--src/test/ui/nll/borrowed-match-issue-45045.rs2
-rw-r--r--src/test/ui/nll/borrowed-match-issue-45045.stderr14
-rw-r--r--src/test/ui/nll/match-guards-partially-borrow.rs164
-rw-r--r--src/test/ui/nll/match-guards-partially-borrow.stderr132
-rw-r--r--src/test/ui/nll/match-on-borrowed.rs95
-rw-r--r--src/test/ui/nll/match-on-borrowed.stderr22
53 files changed, 1197 insertions, 906 deletions
diff --git a/src/librustc/ich/impls_mir.rs b/src/librustc/ich/impls_mir.rs
index 313ef054829..ec54613d1db 100644
--- a/src/librustc/ich/impls_mir.rs
+++ b/src/librustc/ich/impls_mir.rs
@@ -46,6 +46,7 @@ for mir::BorrowKind {
 
         match *self {
             mir::BorrowKind::Shared |
+            mir::BorrowKind::Shallow |
             mir::BorrowKind::Unique => {}
             mir::BorrowKind::Mut { allow_two_phase_borrow } => {
                 allow_two_phase_borrow.hash_stable(hcx, hasher);
@@ -272,7 +273,7 @@ for mir::StatementKind<'gcx> {
     }
 }
 
-impl_stable_hash_for!(enum mir::FakeReadCause { ForMatch, ForLet });
+impl_stable_hash_for!(enum mir::FakeReadCause { ForMatchGuard, ForMatchedPlace, ForLet });
 
 impl<'a, 'gcx, T> HashStable<StableHashingContext<'a>>
     for mir::ValidationOperand<'gcx, T>
diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs
index 98d9a0a7c6f..f856475c337 100644
--- a/src/librustc/mir/mod.rs
+++ b/src/librustc/mir/mod.rs
@@ -451,11 +451,32 @@ impl From<Mutability> for hir::Mutability {
     }
 }
 
-#[derive(Copy, Clone, Debug, PartialEq, Eq, RustcEncodable, RustcDecodable)]
+#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, RustcEncodable, RustcDecodable)]
 pub enum BorrowKind {
     /// Data must be immutable and is aliasable.
     Shared,
 
+    /// The immediately borrowed place must be immutable, but projections from
+    /// it don't need to be. For example, a shallow borrow of `a.b` doesn't
+    /// conflict with a mutable borrow of `a.b.c`.
+    ///
+    /// This is used when lowering matches: when matching on a place we want to
+    /// ensure that place have the same value from the start of the match until
+    /// an arm is selected. This prevents this code from compiling:
+    ///
+    ///     let mut x = &Some(0);
+    ///     match *x {
+    ///         None => (),
+    ///         Some(_) if { x = &None; false } => (),
+    ///         Some(_) => (),
+    ///     }
+    ///
+    /// This can't be a shared borrow because mutably borrowing (*x as Some).0
+    /// should not prevent `if let None = x { ... }`, for example, becase the
+    /// mutating `(*x as Some).0` can't affect the discriminant of `x`.
+    /// We can also report errors with this kind of borrow differently.
+    Shallow,
+
     /// Data must be immutable but not aliasable.  This kind of borrow
     /// cannot currently be expressed by the user and is used only in
     /// implicit closure bindings. It is needed when the closure is
@@ -504,7 +525,7 @@ pub enum BorrowKind {
 impl BorrowKind {
     pub fn allows_two_phase_borrow(&self) -> bool {
         match *self {
-            BorrowKind::Shared | BorrowKind::Unique => false,
+            BorrowKind::Shared | BorrowKind::Shallow | BorrowKind::Unique => false,
             BorrowKind::Mut {
                 allow_two_phase_borrow,
             } => allow_two_phase_borrow,
@@ -1672,7 +1693,11 @@ pub enum FakeReadCause {
     ///
     /// This should ensure that you cannot change the variant for an enum
     /// while you are in the midst of matching on it.
-    ForMatch,
+    ForMatchGuard,
+
+    /// `let x: !; match x {}` doesn't generate any read of x so we need to
+    /// generate a read of x to check that it is initialized and safe.
+    ForMatchedPlace,
 
     /// Officially, the semantics of
     ///
@@ -1773,7 +1798,7 @@ impl<'tcx> Debug for Statement<'tcx> {
 
 /// A path to a value; something that can be evaluated without
 /// changing or disturbing program state.
-#[derive(Clone, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)]
+#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, RustcEncodable, RustcDecodable)]
 pub enum Place<'tcx> {
     /// local variable
     Local(Local),
@@ -1790,7 +1815,7 @@ pub enum Place<'tcx> {
 
 /// The def-id of a static, along with its normalized type (which is
 /// stored to avoid requiring normalization when reading MIR).
-#[derive(Clone, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)]
+#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, RustcEncodable, RustcDecodable)]
 pub struct Static<'tcx> {
     pub def_id: DefId,
     pub ty: Ty<'tcx>,
@@ -1805,13 +1830,13 @@ impl_stable_hash_for!(struct Static<'tcx> {
 /// or `*B` or `B[index]`. Note that it is parameterized because it is
 /// shared between `Constant` and `Place`. See the aliases
 /// `PlaceProjection` etc below.
-#[derive(Clone, Debug, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)]
+#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, RustcEncodable, RustcDecodable)]
 pub struct Projection<'tcx, B, V, T> {
     pub base: B,
     pub elem: ProjectionElem<'tcx, V, T>,
 }
 
-#[derive(Clone, Debug, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)]
+#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, RustcEncodable, RustcDecodable)]
 pub enum ProjectionElem<'tcx, V, T> {
     Deref,
     Field(Field, T),
@@ -2198,6 +2223,7 @@ impl<'tcx> Debug for Rvalue<'tcx> {
             Ref(region, borrow_kind, ref place) => {
                 let kind_str = match borrow_kind {
                     BorrowKind::Shared => "",
+                    BorrowKind::Shallow => "shallow ",
                     BorrowKind::Mut { .. } | BorrowKind::Unique => "mut ",
                 };
 
diff --git a/src/librustc/mir/tcx.rs b/src/librustc/mir/tcx.rs
index c928be4f9df..2a25e057a71 100644
--- a/src/librustc/mir/tcx.rs
+++ b/src/librustc/mir/tcx.rs
@@ -287,6 +287,10 @@ impl BorrowKind {
             // use `&mut`. It gives all the capabilities of an `&uniq`
             // and hence is a safe "over approximation".
             BorrowKind::Unique => hir::MutMutable,
+
+            // We have no type corresponding to a shallow borrow, so use
+            // `&` as an approximation.
+            BorrowKind::Shallow => hir::MutImmutable,
         }
     }
 }
diff --git a/src/librustc/mir/visit.rs b/src/librustc/mir/visit.rs
index 91c83ecb2e2..6de7e2215bf 100644
--- a/src/librustc/mir/visit.rs
+++ b/src/librustc/mir/visit.rs
@@ -963,6 +963,7 @@ impl<'tcx> PlaceContext<'tcx> {
 
             PlaceContext::Inspect |
             PlaceContext::Borrow { kind: BorrowKind::Shared, .. } |
+            PlaceContext::Borrow { kind: BorrowKind::Shallow, .. } |
             PlaceContext::Borrow { kind: BorrowKind::Unique, .. } |
             PlaceContext::Projection(Mutability::Not) |
             PlaceContext::Copy | PlaceContext::Move |
@@ -974,7 +975,9 @@ impl<'tcx> PlaceContext<'tcx> {
     /// Returns true if this place context represents a use that does not change the value.
     pub fn is_nonmutating_use(&self) -> bool {
         match *self {
-            PlaceContext::Inspect | PlaceContext::Borrow { kind: BorrowKind::Shared, .. } |
+            PlaceContext::Inspect |
+            PlaceContext::Borrow { kind: BorrowKind::Shared, .. } |
+            PlaceContext::Borrow { kind: BorrowKind::Shallow, .. } |
             PlaceContext::Borrow { kind: BorrowKind::Unique, .. } |
             PlaceContext::Projection(Mutability::Not) |
             PlaceContext::Copy | PlaceContext::Move => true,
diff --git a/src/librustc_mir/borrow_check/borrow_set.rs b/src/librustc_mir/borrow_check/borrow_set.rs
index bb70b4b76c2..bcf37722130 100644
--- a/src/librustc_mir/borrow_check/borrow_set.rs
+++ b/src/librustc_mir/borrow_check/borrow_set.rs
@@ -87,6 +87,7 @@ impl<'tcx> fmt::Display for BorrowData<'tcx> {
     fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result {
         let kind = match self.kind {
             mir::BorrowKind::Shared => "",
+            mir::BorrowKind::Shallow => "shallow ",
             mir::BorrowKind::Unique => "uniq ",
             mir::BorrowKind::Mut { .. } => "mut ",
         };
@@ -287,7 +288,8 @@ impl<'a, 'gcx, 'tcx> Visitor<'tcx> for GatherBorrows<'a, 'gcx, 'tcx> {
                     borrow_data.activation_location = match context {
                         // The use of TMP in a shared borrow does not
                         // count as an actual activation.
-                        PlaceContext::Borrow { kind: mir::BorrowKind::Shared, .. } => {
+                        PlaceContext::Borrow { kind: mir::BorrowKind::Shared, .. }
+                        | PlaceContext::Borrow { kind: mir::BorrowKind::Shallow, .. } => {
                             TwoPhaseActivation::NotActivated
                         }
                         _ => {
diff --git a/src/librustc_mir/borrow_check/error_reporting.rs b/src/librustc_mir/borrow_check/error_reporting.rs
index 1d91fa365d5..b775fc81d4f 100644
--- a/src/librustc_mir/borrow_check/error_reporting.rs
+++ b/src/librustc_mir/borrow_check/error_reporting.rs
@@ -333,6 +333,27 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
                     Origin::Mir,
                 ),
 
+            (BorrowKind::Mut { .. }, _, _, BorrowKind::Shallow, _, _)
+            | (BorrowKind::Unique, _, _, BorrowKind::Shallow, _, _) => {
+                let mut err = tcx.cannot_mutate_in_match_guard(
+                    span,
+                    issued_span,
+                    &desc_place,
+                    "mutably borrow",
+                    Origin::Mir,
+                );
+                borrow_spans.var_span_label(
+                    &mut err,
+                    format!(
+                        "borrow occurs due to use of `{}` in closure",
+                        desc_place
+                    ),
+                );
+                err.buffer(&mut self.errors_buffer);
+
+                return;
+            }
+
             (BorrowKind::Unique, _, _, _, _, _) => tcx.cannot_uniquely_borrow_by_one_closure(
                 span,
                 &desc_place,
@@ -368,7 +389,16 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
                     Origin::Mir,
                 ),
 
-            (BorrowKind::Shared, _, _, BorrowKind::Shared, _, _) => unreachable!(),
+            (BorrowKind::Shallow, _, _, BorrowKind::Unique, _, _)
+            | (BorrowKind::Shallow, _, _, BorrowKind::Mut { .. }, _, _) => {
+                // Shallow borrows are uses from the user's point of view.
+                self.report_use_while_mutably_borrowed(context, (place, span), issued_borrow);
+                return
+            }
+            (BorrowKind::Shared, _, _, BorrowKind::Shared, _, _)
+            | (BorrowKind::Shared, _, _, BorrowKind::Shallow, _, _)
+            | (BorrowKind::Shallow, _, _, BorrowKind::Shared, _, _)
+            | (BorrowKind::Shallow, _, _, BorrowKind::Shallow, _, _) => unreachable!(),
         };
 
         if issued_spans == borrow_spans {
@@ -780,12 +810,22 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
         let loan_span = loan_spans.args_or_use();
 
         let tcx = self.infcx.tcx;
-        let mut err = tcx.cannot_assign_to_borrowed(
-            span,
-            loan_span,
-            &self.describe_place(place).unwrap_or("_".to_owned()),
-            Origin::Mir,
-        );
+        let mut err = if loan.kind == BorrowKind::Shallow {
+            tcx.cannot_mutate_in_match_guard(
+                span,
+                loan_span,
+                &self.describe_place(place).unwrap_or("_".to_owned()),
+                "assign",
+                Origin::Mir,
+            )
+        } else {
+            tcx.cannot_assign_to_borrowed(
+                span,
+                loan_span,
+                &self.describe_place(place).unwrap_or("_".to_owned()),
+                Origin::Mir,
+            )
+        };
 
         loan_spans.var_span_label(&mut err, "borrow occurs due to use in closure");
 
diff --git a/src/librustc_mir/borrow_check/mod.rs b/src/librustc_mir/borrow_check/mod.rs
index 769e1097bff..06394ee44cc 100644
--- a/src/librustc_mir/borrow_check/mod.rs
+++ b/src/librustc_mir/borrow_check/mod.rs
@@ -499,11 +499,20 @@ impl<'cx, 'gcx, 'tcx> DataflowResultsConsumer<'cx, 'tcx> for MirBorrowckCtxt<'cx
                 );
             }
             StatementKind::FakeRead(_, ref place) => {
-                self.access_place(
+                // Read for match doesn't access any memory and is used to
+                // assert that a place is safe and live. So we don't have to
+                // do any checks here.
+                //
+                // FIXME: Remove check that the place is initialized. This is
+                // needed for now because matches don't have never patterns yet.
+                // So this is the only place we prevent
+                //      let x: !;
+                //      match x {};
+                // from compiling.
+                self.check_if_path_or_subpath_is_moved(
                     ContextKind::FakeRead.new(location),
+                    InitializationRequiringAction::Use,
                     (place, span),
-                    (Deep, Read(ReadKind::Borrow(BorrowKind::Shared))),
-                    LocalMutationIsAllowed::No,
                     flow_state,
                 );
             }
@@ -755,6 +764,7 @@ use self::AccessDepth::{Deep, Shallow};
 enum ArtificialField {
     Discriminant,
     ArrayLength,
+    ShallowBorrow,
 }
 
 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
@@ -835,6 +845,7 @@ enum LocalMutationIsAllowed {
 enum InitializationRequiringAction {
     Update,
     Borrow,
+    MatchOn,
     Use,
     Assignment,
 }
@@ -849,6 +860,7 @@ impl InitializationRequiringAction {
         match self {
             InitializationRequiringAction::Update => "update",
             InitializationRequiringAction::Borrow => "borrow",
+            InitializationRequiringAction::MatchOn => "use", // no good noun
             InitializationRequiringAction::Use => "use",
             InitializationRequiringAction::Assignment => "assign",
         }
@@ -858,6 +870,7 @@ impl InitializationRequiringAction {
         match self {
             InitializationRequiringAction::Update => "updated",
             InitializationRequiringAction::Borrow => "borrowed",
+            InitializationRequiringAction::MatchOn => "matched on",
             InitializationRequiringAction::Use => "used",
             InitializationRequiringAction::Assignment => "assigned",
         }
@@ -972,7 +985,13 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
                     Control::Continue
                 }
 
-                (Read(_), BorrowKind::Shared) | (Reservation(..), BorrowKind::Shared) => {
+                (Read(_), BorrowKind::Shared) | (Reservation(..), BorrowKind::Shared)
+                | (Read(_), BorrowKind::Shallow) | (Reservation(..), BorrowKind::Shallow) => {
+                    Control::Continue
+                }
+
+                (Write(WriteKind::Move), BorrowKind::Shallow) => {
+                    // Handled by initialization checks.
                     Control::Continue
                 }
 
@@ -984,7 +1003,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
                     }
 
                     match kind {
-                        ReadKind::Copy => {
+                        ReadKind::Copy  => {
                             error_reported = true;
                             this.report_use_while_mutably_borrowed(context, place_span, borrow)
                         }
@@ -1108,6 +1127,9 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
         match *rvalue {
             Rvalue::Ref(_ /*rgn*/, bk, ref place) => {
                 let access_kind = match bk {
+                    BorrowKind::Shallow => {
+                        (Shallow(Some(ArtificialField::ShallowBorrow)), Read(ReadKind::Borrow(bk)))
+                    },
                     BorrowKind::Shared => (Deep, Read(ReadKind::Borrow(bk))),
                     BorrowKind::Unique | BorrowKind::Mut { .. } => {
                         let wk = WriteKind::MutableBorrow(bk);
@@ -1127,9 +1149,15 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
                     flow_state,
                 );
 
+                let action = if bk == BorrowKind::Shallow {
+                    InitializationRequiringAction::MatchOn
+                } else {
+                    InitializationRequiringAction::Borrow
+                };
+
                 self.check_if_path_or_subpath_is_moved(
                     context,
-                    InitializationRequiringAction::Borrow,
+                    action,
                     (place, span),
                     flow_state,
                 );
@@ -1315,11 +1343,16 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
             return;
         }
 
-        // FIXME: replace this with a proper borrow_conflicts_with_place when
-        // that is merged.
         let sd = if might_be_alive { Deep } else { Shallow(None) };
 
-        if places_conflict::places_conflict(self.infcx.tcx, self.mir, place, root_place, sd) {
+        if places_conflict::borrow_conflicts_with_place(
+            self.infcx.tcx,
+            self.mir,
+            place,
+            borrow.kind,
+            root_place,
+            sd
+        ) {
             debug!("check_for_invalidation_at_exit({:?}): INVALID", place);
             // FIXME: should be talking about the region lifetime instead
             // of just a span here.
@@ -1369,7 +1402,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
 
             // only mutable borrows should be 2-phase
             assert!(match borrow.kind {
-                BorrowKind::Shared => false,
+                BorrowKind::Shared | BorrowKind::Shallow => false,
                 BorrowKind::Unique | BorrowKind::Mut { .. } => true,
             });
 
@@ -1669,7 +1702,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
                 let is_local_mutation_allowed = match borrow_kind {
                     BorrowKind::Unique => LocalMutationIsAllowed::Yes,
                     BorrowKind::Mut { .. } => is_local_mutation_allowed,
-                    BorrowKind::Shared => unreachable!(),
+                    BorrowKind::Shared | BorrowKind::Shallow => unreachable!(),
                 };
                 match self.is_mutable(place, is_local_mutation_allowed) {
                     Ok(root_place) => {
@@ -1699,8 +1732,10 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
             | Write(wk @ WriteKind::Move)
             | Reservation(wk @ WriteKind::StorageDeadOrDrop)
             | Reservation(wk @ WriteKind::MutableBorrow(BorrowKind::Shared))
+            | Reservation(wk @ WriteKind::MutableBorrow(BorrowKind::Shallow))
             | Write(wk @ WriteKind::StorageDeadOrDrop)
-            | Write(wk @ WriteKind::MutableBorrow(BorrowKind::Shared)) => {
+            | Write(wk @ WriteKind::MutableBorrow(BorrowKind::Shared))
+            | Write(wk @ WriteKind::MutableBorrow(BorrowKind::Shallow)) => {
                 if let Err(_place_err) = self.is_mutable(place, is_local_mutation_allowed) {
                     if self.infcx.tcx.migrate_borrowck() {
                         // rust-lang/rust#46908: In pure NLL mode this
@@ -1743,6 +1778,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
             Read(ReadKind::Borrow(BorrowKind::Unique))
             | Read(ReadKind::Borrow(BorrowKind::Mut { .. }))
             | Read(ReadKind::Borrow(BorrowKind::Shared))
+            | Read(ReadKind::Borrow(BorrowKind::Shallow))
             | Read(ReadKind::Copy) => {
                 // Access authorized
                 return false;
diff --git a/src/librustc_mir/borrow_check/nll/invalidation.rs b/src/librustc_mir/borrow_check/nll/invalidation.rs
index 3d387963da9..1246f7120c4 100644
--- a/src/librustc_mir/borrow_check/nll/invalidation.rs
+++ b/src/librustc_mir/borrow_check/nll/invalidation.rs
@@ -329,6 +329,9 @@ impl<'cg, 'cx, 'tcx, 'gcx> InvalidationGenerator<'cx, 'tcx, 'gcx> {
         match *rvalue {
             Rvalue::Ref(_ /*rgn*/, bk, ref place) => {
                 let access_kind = match bk {
+                    BorrowKind::Shallow => {
+                        (Shallow(Some(ArtificialField::ShallowBorrow)), Read(ReadKind::Borrow(bk)))
+                    },
                     BorrowKind::Shared => (Deep, Read(ReadKind::Borrow(bk))),
                     BorrowKind::Unique | BorrowKind::Mut { .. } => {
                         let wk = WriteKind::MutableBorrow(bk);
@@ -439,8 +442,9 @@ impl<'cg, 'cx, 'tcx, 'gcx> InvalidationGenerator<'cx, 'tcx, 'gcx> {
                         // have already taken the reservation
                     }
 
-                    (Read(_), BorrowKind::Shared) | (Reservation(..), BorrowKind::Shared) => {
-                        // Reads/reservations don't invalidate shared borrows
+                    (Read(_), BorrowKind::Shallow) | (Reservation(..), BorrowKind::Shallow)
+                    | (Read(_), BorrowKind::Shared) | (Reservation(..), BorrowKind::Shared) => {
+                        // Reads/reservations don't invalidate shared or shallow borrows
                     }
 
                     (Read(_), BorrowKind::Unique) | (Read(_), BorrowKind::Mut { .. }) => {
diff --git a/src/librustc_mir/borrow_check/path_utils.rs b/src/librustc_mir/borrow_check/path_utils.rs
index 67787d23db6..9250c04969f 100644
--- a/src/librustc_mir/borrow_check/path_utils.rs
+++ b/src/librustc_mir/borrow_check/path_utils.rs
@@ -61,7 +61,14 @@ pub(super) fn each_borrow_involving_path<'a, 'tcx, 'gcx: 'tcx, F, I, S> (
     for i in candidates {
         let borrowed = &borrow_set[i];
 
-        if places_conflict::places_conflict(tcx, mir, &borrowed.borrowed_place, place, access) {
+        if places_conflict::borrow_conflicts_with_place(
+            tcx,
+            mir,
+            &borrowed.borrowed_place,
+            borrowed.kind,
+            place,
+            access,
+        ) {
             debug!(
                 "each_borrow_involving_path: {:?} @ {:?} vs. {:?}/{:?}",
                 i, borrowed, place, access
diff --git a/src/librustc_mir/borrow_check/places_conflict.rs b/src/librustc_mir/borrow_check/places_conflict.rs
index e371b34c480..c0f059619a4 100644
--- a/src/librustc_mir/borrow_check/places_conflict.rs
+++ b/src/librustc_mir/borrow_check/places_conflict.rs
@@ -12,20 +12,21 @@ use borrow_check::ArtificialField;
 use borrow_check::Overlap;
 use borrow_check::{Deep, Shallow, AccessDepth};
 use rustc::hir;
-use rustc::mir::{Mir, Place};
+use rustc::mir::{BorrowKind, Mir, Place};
 use rustc::mir::{Projection, ProjectionElem};
 use rustc::ty::{self, TyCtxt};
 use std::cmp::max;
 
-pub(super) fn places_conflict<'gcx, 'tcx>(
+pub(super) fn borrow_conflicts_with_place<'gcx, 'tcx>(
     tcx: TyCtxt<'_, 'gcx, 'tcx>,
     mir: &Mir<'tcx>,
     borrow_place: &Place<'tcx>,
+    borrow_kind: BorrowKind,
     access_place: &Place<'tcx>,
     access: AccessDepth,
 ) -> bool {
     debug!(
-        "places_conflict({:?},{:?},{:?})",
+        "borrow_conflicts_with_place({:?},{:?},{:?})",
         borrow_place, access_place, access
     );
 
@@ -39,7 +40,14 @@ pub(super) fn places_conflict<'gcx, 'tcx>(
 
     unroll_place(borrow_place, None, |borrow_components| {
         unroll_place(access_place, None, |access_components| {
-            place_components_conflict(tcx, mir, borrow_components, access_components, access)
+            place_components_conflict(
+                tcx,
+                mir,
+                borrow_components,
+                borrow_kind,
+                access_components,
+                access
+            )
         })
     })
 }
@@ -48,6 +56,7 @@ fn place_components_conflict<'gcx, 'tcx>(
     tcx: TyCtxt<'_, 'gcx, 'tcx>,
     mir: &Mir<'tcx>,
     mut borrow_components: PlaceComponentsIter<'_, 'tcx>,
+    borrow_kind: BorrowKind,
     mut access_components: PlaceComponentsIter<'_, 'tcx>,
     access: AccessDepth,
 ) -> bool {
@@ -95,10 +104,10 @@ fn place_components_conflict<'gcx, 'tcx>(
     loop {
         // loop invariant: borrow_c is always either equal to access_c or disjoint from it.
         if let Some(borrow_c) = borrow_components.next() {
-            debug!("places_conflict: borrow_c = {:?}", borrow_c);
+            debug!("borrow_conflicts_with_place: borrow_c = {:?}", borrow_c);
 
             if let Some(access_c) = access_components.next() {
-                debug!("places_conflict: access_c = {:?}", access_c);
+                debug!("borrow_conflicts_with_place: access_c = {:?}", access_c);
 
                 // Borrow and access path both have more components.
                 //
@@ -127,7 +136,7 @@ fn place_components_conflict<'gcx, 'tcx>(
                         // idea, at least for now, so just give up and
                         // report a conflict. This is unsafe code anyway so
                         // the user could always use raw pointers.
-                        debug!("places_conflict: arbitrary -> conflict");
+                        debug!("borrow_conflicts_with_place: arbitrary -> conflict");
                         return true;
                     }
                     Overlap::EqualOrDisjoint => {
@@ -136,7 +145,7 @@ fn place_components_conflict<'gcx, 'tcx>(
                     Overlap::Disjoint => {
                         // We have proven the borrow disjoint - further
                         // projections will remain disjoint.
-                        debug!("places_conflict: disjoint");
+                        debug!("borrow_conflicts_with_place: disjoint");
                         return false;
                     }
                 }
@@ -157,7 +166,8 @@ fn place_components_conflict<'gcx, 'tcx>(
 
                 match (elem, &base_ty.sty, access) {
                     (_, _, Shallow(Some(ArtificialField::Discriminant)))
-                    | (_, _, Shallow(Some(ArtificialField::ArrayLength))) => {
+                    | (_, _, Shallow(Some(ArtificialField::ArrayLength)))
+                    | (_, _, Shallow(Some(ArtificialField::ShallowBorrow))) => {
                         // The discriminant and array length are like
                         // additional fields on the type; they do not
                         // overlap any existing data there. Furthermore,
@@ -167,7 +177,7 @@ fn place_components_conflict<'gcx, 'tcx>(
                         //
                         // e.g. a (mutable) borrow of `a[5]` while we read the
                         // array length of `a`.
-                        debug!("places_conflict: implicit field");
+                        debug!("borrow_conflicts_with_place: implicit field");
                         return false;
                     }
 
@@ -175,7 +185,7 @@ fn place_components_conflict<'gcx, 'tcx>(
                         // e.g. a borrow of `*x.y` while we shallowly access `x.y` or some
                         // prefix thereof - the shallow access can't touch anything behind
                         // the pointer.
-                        debug!("places_conflict: shallow access behind ptr");
+                        debug!("borrow_conflicts_with_place: shallow access behind ptr");
                         return false;
                     }
                     (ProjectionElem::Deref, ty::Ref(_, _, hir::MutImmutable), _) => {
@@ -185,7 +195,7 @@ fn place_components_conflict<'gcx, 'tcx>(
                     (ProjectionElem::Deref, ty::Ref(_, _, hir::MutMutable), AccessDepth::Drop) => {
                         // Values behind a mutatble reference are not access either by Dropping a
                         // value, or by StorageDead
-                        debug!("places_conflict: drop access behind ptr");
+                        debug!("borrow_conflicts_with_place: drop access behind ptr");
                         return false;
                     }
 
@@ -225,11 +235,13 @@ fn place_components_conflict<'gcx, 'tcx>(
             // If the second example, where we did, then we still know
             // that the borrow can access a *part* of our place that
             // our access cares about, so we still have a conflict.
-            //
-            // FIXME: Differs from AST-borrowck; includes drive-by fix
-            // to #38899. Will probably need back-compat mode flag.
-            debug!("places_conflict: full borrow, CONFLICT");
-            return true;
+            if borrow_kind == BorrowKind::Shallow && access_components.next().is_some() {
+                debug!("borrow_conflicts_with_place: shallow borrow");
+                return false;
+            } else {
+                debug!("borrow_conflicts_with_place: full borrow, CONFLICT");
+                return true;
+            }
         }
     }
 }
@@ -241,7 +253,7 @@ fn place_components_conflict<'gcx, 'tcx>(
 ///
 /// NB: This particular impl strategy is not the most obvious.  It was
 /// chosen because it makes a measurable difference to NLL
-/// performance, as this code (`places_conflict`) is somewhat hot.
+/// performance, as this code (`borrow_conflicts_with_place`) is somewhat hot.
 struct PlaceComponents<'p, 'tcx: 'p> {
     component: &'p Place<'tcx>,
     next: Option<&'p PlaceComponents<'p, 'tcx>>,
diff --git a/src/librustc_mir/build/matches/mod.rs b/src/librustc_mir/build/matches/mod.rs
index c30dcdafdb4..e40ed51f7d3 100644
--- a/src/librustc_mir/build/matches/mod.rs
+++ b/src/librustc_mir/build/matches/mod.rs
@@ -57,39 +57,22 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
         // See issue #47412 for this hole being discovered in the wild.
         //
         // HACK(eddyb) Work around the above issue by adding a dummy inspection
-        // of `discriminant_place`, specifically by applying `Rvalue::Discriminant`
-        // (which will work regardless of type) and storing the result in a temp.
+        // of `discriminant_place`, specifically by applying `ReadForMatch`.
         //
-        // NOTE: Under NLL, the above issue should no longer occur because it
-        // injects a borrow of the matched input, which should have the same effect
-        // as eddyb's hack. Once NLL is the default, we can remove the hack.
-
-        let dummy_source_info = self.source_info(discriminant_span);
-        let dummy_access = Rvalue::Discriminant(discriminant_place.clone());
-        let dummy_ty = dummy_access.ty(&self.local_decls, tcx);
-        let dummy_temp = self.temp(dummy_ty, dummy_source_info.span);
-        self.cfg
-            .push_assign(block, dummy_source_info, &dummy_temp, dummy_access);
+        // NOTE: ReadForMatch also checks that the discriminant is initialized.
+        // This is currently needed to not allow matching on an uninitialized,
+        // uninhabited value. If we get never patterns, those will check that
+        // the place is initialized, and so this read would only be used to
+        // check safety.
 
         let source_info = self.source_info(discriminant_span);
-        let borrowed_input_temp = if tcx.generate_borrow_of_any_match_input() {
-            // The region is unknown at this point; we rely on NLL
-            // inference to find an appropriate one. Therefore you can
-            // only use this when NLL is turned on.
-            assert!(tcx.use_mir_borrowck());
-            let borrowed_input = Rvalue::Ref(
-                tcx.types.re_empty,
-                BorrowKind::Shared,
+        self.cfg.push(block, Statement {
+            source_info,
+            kind: StatementKind::FakeRead(
+                FakeReadCause::ForMatchedPlace,
                 discriminant_place.clone(),
-            );
-            let borrowed_input_ty = borrowed_input.ty(&self.local_decls, tcx);
-            let borrowed_input_temp = self.temp(borrowed_input_ty, span);
-            self.cfg
-                .push_assign(block, source_info, &borrowed_input_temp, borrowed_input);
-            Some(borrowed_input_temp)
-        } else {
-            None
-        };
+            ),
+        });
 
         let mut arm_blocks = ArmBlocks {
             blocks: arms.iter().map(|_| self.cfg.start_new_block()).collect(),
@@ -118,6 +101,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
             .map(|_| self.cfg.start_new_block())
             .collect();
 
+        let mut has_guard = false;
+
         // assemble a list of candidates: there is one candidate per
         // pattern, which means there may be more than one candidate
         // *per arm*. These candidates are kept sorted such that the
@@ -140,24 +125,9 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
             .map(
                 |(
                     (arm_index, pat_index, pattern, guard),
-                    (pre_binding_block, next_candidate_pre_binding_block),
+                    (pre_binding_block, next_candidate_pre_binding_block)
                 )| {
-                    if let (true, Some(borrow_temp)) =
-                        (tcx.emit_read_for_match(), borrowed_input_temp.clone())
-                    {
-                        // Inject a fake read, see comments on `FakeReadCause::ForMatch`.
-                        let pattern_source_info = self.source_info(pattern.span);
-                        self.cfg.push(
-                            *pre_binding_block,
-                            Statement {
-                                source_info: pattern_source_info,
-                                kind: StatementKind::FakeRead(
-                                    FakeReadCause::ForMatch,
-                                    borrow_temp.clone(),
-                                ),
-                            },
-                        );
-                    }
+                    has_guard |= guard.is_some();
 
                     // One might ask: why not build up the match pair such that it
                     // matches via `borrowed_input_temp.deref()` instead of
@@ -202,9 +172,31 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
             TerminatorKind::Unreachable,
         );
 
+        // Maps a place to the kind of Fake borrow that we want to perform on
+        // it: either Shallow or Shared, depending on whether the place is
+        // bound in the match, or just switched on.
+        // If there are no match guards then we don't need any fake borrows,
+        // so don't track them.
+        let mut fake_borrows = if has_guard && tcx.generate_borrow_of_any_match_input() {
+            Some(FxHashMap())
+        } else {
+            None
+        };
+
+        let pre_binding_blocks: Vec<_> = candidates
+            .iter()
+            .map(|cand| (cand.pre_binding_block, cand.span))
+            .collect();
+
         // this will generate code to test discriminant_place and
         // branch to the appropriate arm block
-        let otherwise = self.match_candidates(span, &mut arm_blocks, candidates, block);
+        let otherwise = self.match_candidates(
+            discriminant_span,
+            &mut arm_blocks,
+            candidates,
+            block,
+            &mut fake_borrows,
+        );
 
         if !otherwise.is_empty() {
             // All matches are exhaustive. However, because some matches
@@ -224,6 +216,10 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
             }
         }
 
+        if let Some(fake_borrows) = fake_borrows {
+            self.add_fake_borrows(&pre_binding_blocks, fake_borrows, source_info, block);
+        }
+
         // all the arm blocks will rejoin here
         let end_block = self.cfg.start_new_block();
 
@@ -714,12 +710,16 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
     /// up the list of candidates and recurse with a non-exhaustive
     /// list. This is important to keep the size of the generated code
     /// under control. See `test_candidates` for more details.
+    ///
+    /// If `add_fake_borrows` is true, then places which need fake borrows
+    /// will be added to it.
     fn match_candidates<'pat>(
         &mut self,
         span: Span,
         arm_blocks: &mut ArmBlocks,
         mut candidates: Vec<Candidate<'pat, 'tcx>>,
         mut block: BasicBlock,
+        fake_borrows: &mut Option<FxHashMap<Place<'tcx>, BorrowKind>>,
     ) -> Vec<BasicBlock> {
         debug!(
             "matched_candidate(span={:?}, block={:?}, candidates={:?})",
@@ -747,6 +747,15 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
         );
         let mut unmatched_candidates = candidates.split_off(fully_matched);
 
+        // Insert a *Shared* borrow of any places that are bound.
+        if let Some(fake_borrows) = fake_borrows {
+            for Binding { source, .. }
+                in candidates.iter().flat_map(|candidate| &candidate.bindings)
+            {
+                fake_borrows.insert(source.clone(), BorrowKind::Shared);
+            }
+        }
+
         let fully_matched_with_guard = candidates.iter().take_while(|c| c.guard.is_some()).count();
 
         let unreachable_candidates = if fully_matched_with_guard + 1 < candidates.len() {
@@ -783,7 +792,13 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                     return vec![];
                 } else {
                     let target = self.cfg.start_new_block();
-                    return self.match_candidates(span, arm_blocks, unmatched_candidates, target);
+                    return self.match_candidates(
+                        span,
+                        arm_blocks,
+                        unmatched_candidates,
+                        target,
+                        &mut None,
+                    );
                 }
             }
         }
@@ -796,7 +811,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
 
         // Test candidates where possible.
         let (otherwise, tested_candidates) =
-            self.test_candidates(span, arm_blocks, &unmatched_candidates, block);
+            self.test_candidates(span, arm_blocks, &unmatched_candidates, block, fake_borrows);
 
         // If the target candidates were exhaustive, then we are done.
         // But for borrowck continue build decision tree.
@@ -810,7 +825,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
 
         // Otherwise, let's process those remaining candidates.
         let join_block = self.join_otherwise_blocks(span, otherwise);
-        self.match_candidates(span, arm_blocks, untested_candidates, join_block)
+        self.match_candidates(span, arm_blocks, untested_candidates, join_block, &mut None)
     }
 
     fn join_otherwise_blocks(&mut self, span: Span, mut otherwise: Vec<BasicBlock>) -> BasicBlock {
@@ -950,6 +965,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
         arm_blocks: &mut ArmBlocks,
         candidates: &[Candidate<'pat, 'tcx>],
         block: BasicBlock,
+        fake_borrows: &mut Option<FxHashMap<Place<'tcx>, BorrowKind>>,
     ) -> (Vec<BasicBlock>, usize) {
         // extract the match-pair from the highest priority candidate
         let match_pair = &candidates.first().unwrap().match_pairs[0];
@@ -990,6 +1006,11 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
             _ => {}
         }
 
+        // Insert a Shallow borrow of any places that is switched on.
+        fake_borrows.as_mut().map(|fb| {
+            fb.entry(match_pair.place.clone()).or_insert(BorrowKind::Shallow)
+        });
+
         // perform the test, branching to one of N blocks. For each of
         // those N possible outcomes, create a (initially empty)
         // vector of candidates. Those are the candidates that still
@@ -1026,7 +1047,13 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
             .into_iter()
             .zip(target_candidates)
             .flat_map(|(target_block, target_candidates)| {
-                self.match_candidates(span, arm_blocks, target_candidates, target_block)
+                self.match_candidates(
+                    span,
+                    arm_blocks,
+                    target_candidates,
+                    target_block,
+                    fake_borrows,
+                )
             })
             .collect();
 
@@ -1363,7 +1390,9 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                     // borrow of the whole match input. See additional
                     // discussion on rust-lang/rust#49870.
                     let borrow_kind = match borrow_kind {
-                        BorrowKind::Shared | BorrowKind::Unique => borrow_kind,
+                        BorrowKind::Shared
+                        | BorrowKind::Shallow
+                        | BorrowKind::Unique => borrow_kind,
                         BorrowKind::Mut { .. } => BorrowKind::Mut {
                             allow_two_phase_borrow: true,
                         },
@@ -1502,4 +1531,86 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
         debug!("declare_binding: vars={:?}", locals);
         self.var_indices.insert(var_id, locals);
     }
+
+    // Determine the fake borrows that are needed to ensure that the place
+    // will evaluate to the same thing until an arm has been chosen.
+    fn add_fake_borrows<'pat>(
+        &mut self,
+        pre_binding_blocks: &[(BasicBlock, Span)],
+        fake_borrows: FxHashMap<Place<'tcx>, BorrowKind>,
+        source_info: SourceInfo,
+        start_block: BasicBlock,
+    ) {
+        let tcx = self.hir.tcx();
+
+        debug!("add_fake_borrows pre_binding_blocks = {:?}, fake_borrows = {:?}",
+               pre_binding_blocks, fake_borrows);
+
+        let mut all_fake_borrows = Vec::with_capacity(fake_borrows.len());
+
+        // Insert a Shallow borrow of the prefixes of any fake borrows.
+        for (place, borrow_kind) in fake_borrows
+        {
+            {
+                let mut prefix_cursor = &place;
+                while let Place::Projection(box Projection { base, elem }) = prefix_cursor {
+                    if let ProjectionElem::Deref = elem {
+                        // Insert a shallow borrow after a deref. For other
+                        // projections the borrow of prefix_cursor will
+                        // conflict with any mutation of base.
+                        all_fake_borrows.push((base.clone(), BorrowKind::Shallow));
+                    }
+                    prefix_cursor = base;
+                }
+            }
+
+            all_fake_borrows.push((place, borrow_kind));
+        }
+
+        // Deduplicate and ensure a deterministic order.
+        all_fake_borrows.sort();
+        all_fake_borrows.dedup();
+
+        debug!("add_fake_borrows all_fake_borrows = {:?}", all_fake_borrows);
+
+        // Add fake borrows to the start of the match and reads of them before
+        // the start of each arm.
+        let mut borrowed_input_temps = Vec::with_capacity(all_fake_borrows.len());
+
+        for (matched_place, borrow_kind) in all_fake_borrows {
+            let borrowed_input =
+                Rvalue::Ref(tcx.types.re_empty, borrow_kind, matched_place.clone());
+            let borrowed_input_ty = borrowed_input.ty(&self.local_decls, tcx);
+            let borrowed_input_temp = self.temp(borrowed_input_ty, source_info.span);
+            self.cfg.push_assign(
+                start_block,
+                source_info,
+                &borrowed_input_temp,
+                borrowed_input
+            );
+            borrowed_input_temps.push(borrowed_input_temp);
+        }
+
+        // FIXME: This could be a lot of reads (#fake borrows * #patterns).
+        // The false edges that we currently generate would allow us to only do
+        // this on the last Candidate, but it's possible that there might not be
+        // so many false edges in the future, so we read for all Candidates for
+        // now.
+        // Another option would be to make our own block and add our own false
+        // edges to it.
+        if tcx.emit_read_for_match() {
+            for &(pre_binding_block, span) in pre_binding_blocks {
+                let pattern_source_info = self.source_info(span);
+                for temp in &borrowed_input_temps {
+                    self.cfg.push(pre_binding_block, Statement {
+                        source_info: pattern_source_info,
+                        kind: StatementKind::FakeRead(
+                            FakeReadCause::ForMatchGuard,
+                            temp.clone(),
+                        ),
+                    });
+                }
+            }
+        }
+    }
 }
diff --git a/src/librustc_mir/diagnostics.rs b/src/librustc_mir/diagnostics.rs
index 24197c9e4b8..0c31e5c4da8 100644
--- a/src/librustc_mir/diagnostics.rs
+++ b/src/librustc_mir/diagnostics.rs
@@ -1991,6 +1991,26 @@ fn main() {
 ```
 "##,
 
+E0510: r##"
+Cannot mutate place in this match guard.
+
+When matching on a variable it cannot be mutated in the match guards, as this
+could cause the match to be non-exhaustive:
+
+```compile_fail,E0510
+#![feature(nll, bind_by_move_pattern_guards)]
+let mut x = Some(0);
+match x {
+    None => (),
+    Some(v) if { x = None; false } => (),
+    Some(_) => (), // No longer matches
+}
+```
+
+Here executing `x = None` would modify the value being matched and require us
+to go "back in time" to the `None` arm.
+"##,
+
 E0579: r##"
 When matching against an exclusive range, the compiler verifies that the range
 is non-empty. Exclusive range patterns include the start point but not the end
diff --git a/src/librustc_mir/lib.rs b/src/librustc_mir/lib.rs
index 4546e0bf253..a2d70bc05c1 100644
--- a/src/librustc_mir/lib.rs
+++ b/src/librustc_mir/lib.rs
@@ -14,7 +14,7 @@ Rust MIR: a lowered representation of Rust. Also: an experiment!
 
 */
 
-#![cfg_attr(not(stage0), feature(nll))]
+#![feature(nll)]
 #![feature(in_band_lifetimes)]
 #![feature(impl_header_lifetime_elision)]
 #![feature(slice_patterns)]
diff --git a/src/librustc_mir/transform/cleanup_post_borrowck.rs b/src/librustc_mir/transform/cleanup_post_borrowck.rs
index 9edb1a1f76a..aaba7ab8418 100644
--- a/src/librustc_mir/transform/cleanup_post_borrowck.rs
+++ b/src/librustc_mir/transform/cleanup_post_borrowck.rs
@@ -33,7 +33,8 @@
 use rustc_data_structures::fx::FxHashSet;
 
 use rustc::middle::region;
-use rustc::mir::{BasicBlock, Location, Mir, Rvalue, Statement, StatementKind};
+use rustc::mir::{BasicBlock, FakeReadCause, Local, Location, Mir, Place};
+use rustc::mir::{Rvalue, Statement, StatementKind};
 use rustc::mir::visit::{MutVisitor, Visitor, TyContext};
 use rustc::ty::{Ty, RegionKind, TyCtxt};
 use transform::{MirPass, MirSource};
@@ -135,3 +136,62 @@ impl<'tcx> MutVisitor<'tcx> for DeleteAscribeUserType {
         self.super_statement(block, statement, location);
     }
 }
+
+pub struct CleanFakeReadsAndBorrows;
+
+pub struct DeleteAndRecordFakeReads {
+    fake_borrow_temporaries: FxHashSet<Local>,
+}
+
+pub struct DeleteFakeBorrows {
+    fake_borrow_temporaries: FxHashSet<Local>,
+}
+
+// Removes any FakeReads from the MIR
+impl MirPass for CleanFakeReadsAndBorrows {
+    fn run_pass<'a, 'tcx>(&self,
+                          _tcx: TyCtxt<'a, 'tcx, 'tcx>,
+                          _source: MirSource,
+                          mir: &mut Mir<'tcx>) {
+        let mut delete_reads = DeleteAndRecordFakeReads {
+            fake_borrow_temporaries: FxHashSet(),
+        };
+        delete_reads.visit_mir(mir);
+        let mut delete_borrows = DeleteFakeBorrows {
+            fake_borrow_temporaries: delete_reads.fake_borrow_temporaries,
+        };
+        delete_borrows.visit_mir(mir);
+    }
+}
+
+impl<'tcx> MutVisitor<'tcx> for DeleteAndRecordFakeReads {
+    fn visit_statement(&mut self,
+                       block: BasicBlock,
+                       statement: &mut Statement<'tcx>,
+                       location: Location) {
+        if let StatementKind::FakeRead(cause, ref place) = statement.kind {
+            if let FakeReadCause::ForMatchGuard = cause {
+                match *place {
+                    Place::Local(local) => self.fake_borrow_temporaries.insert(local),
+                    _ => bug!("Fake match guard read of non-local: {:?}", place),
+                };
+            }
+            statement.make_nop();
+        }
+        self.super_statement(block, statement, location);
+    }
+}
+
+impl<'tcx> MutVisitor<'tcx> for DeleteFakeBorrows {
+    fn visit_statement(&mut self,
+                       block: BasicBlock,
+                       statement: &mut Statement<'tcx>,
+                       location: Location) {
+        if let StatementKind::Assign(Place::Local(local), _) = statement.kind {
+            if self.fake_borrow_temporaries.contains(&local) {
+                statement.make_nop();
+            }
+        }
+        self.super_statement(block, statement, location);
+    }
+}
diff --git a/src/librustc_mir/transform/mod.rs b/src/librustc_mir/transform/mod.rs
index 19fb35be9d4..d18836999dc 100644
--- a/src/librustc_mir/transform/mod.rs
+++ b/src/librustc_mir/transform/mod.rs
@@ -237,9 +237,12 @@ fn optimized_mir<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> &'tcx
         no_landing_pads::NoLandingPads,
         simplify_branches::SimplifyBranches::new("initial"),
         remove_noop_landing_pads::RemoveNoopLandingPads,
-        simplify::SimplifyCfg::new("early-opt"),
         // Remove all `AscribeUserType` statements.
         cleanup_post_borrowck::CleanAscribeUserType,
+        // Remove all `FakeRead` statements and the borrows that are only
+        // used for checking matches
+        cleanup_post_borrowck::CleanFakeReadsAndBorrows,
+        simplify::SimplifyCfg::new("early-opt"),
 
         // These next passes must be executed together
         add_call_guards::CriticalCallEdges,
diff --git a/src/librustc_mir/util/borrowck_errors.rs b/src/librustc_mir/util/borrowck_errors.rs
index 82617ee1074..6d5d3ba88f2 100644
--- a/src/librustc_mir/util/borrowck_errors.rs
+++ b/src/librustc_mir/util/borrowck_errors.rs
@@ -555,6 +555,29 @@ pub trait BorrowckErrors<'cx>: Sized + Copy {
         self.cannot_borrow_path_as_mutable_because(span, path, "", o)
     }
 
+    fn cannot_mutate_in_match_guard(
+        self,
+        mutate_span: Span,
+        match_span: Span,
+        match_place: &str,
+        action: &str,
+        o: Origin,
+    ) -> DiagnosticBuilder<'cx> {
+        let mut err = struct_span_err!(
+            self,
+            mutate_span,
+            E0510,
+            "cannot {} `{}` in match guard{OGN}",
+            action,
+            match_place,
+            OGN = o
+        );
+        err.span_label(mutate_span, format!("cannot {}", action));
+        err.span_label(match_span, format!("value is immutable in match guard"));
+
+        self.cancel_if_wrong_origin(err, o)
+    }
+
     fn cannot_borrow_across_generator_yield(
         self,
         span: Span,
diff --git a/src/test/mir-opt/box_expr.rs b/src/test/mir-opt/box_expr.rs
index f6877d979e0..8390a0d19ae 100644
--- a/src/test/mir-opt/box_expr.rs
+++ b/src/test/mir-opt/box_expr.rs
@@ -63,7 +63,6 @@ impl Drop for S {
 //
 //     bb4: {
 //         StorageDead(_2);
-//         FakeRead(ForLet, _1);
 //         StorageLive(_4);
 //         _4 = move _1;
 //         _3 = const std::mem::drop(move _4) -> [return: bb5, unwind: bb7];
diff --git a/src/test/mir-opt/issue-49232.rs b/src/test/mir-opt/issue-49232.rs
index 3bc735bc6c5..f9024b67063 100644
--- a/src/test/mir-opt/issue-49232.rs
+++ b/src/test/mir-opt/issue-49232.rs
@@ -34,10 +34,9 @@ fn main() {
 //     }
 //     let mut _1: ();
 //     let mut _3: bool;
-//     let mut _4: u8;
-//     let mut _5: !;
-//     let mut _6: ();
-//     let mut _7: &i32;
+//     let mut _4: !;
+//     let mut _5: ();
+//     let mut _6: &i32;
 //     bb0: {
 //         goto -> bb1;
 //     }
@@ -51,7 +50,7 @@ fn main() {
 //         StorageLive(_2);
 //         StorageLive(_3);
 //         _3 = const true;
-//         _4 = discriminant(_3);
+//         FakeRead(ForMatchedPlace, _3);
 //         switchInt(_3) -> [false: bb11, otherwise: bb10];
 //     }
 //     bb4: {
@@ -89,9 +88,9 @@ fn main() {
 //     bb14: {
 //         FakeRead(ForLet, _2);
 //         StorageDead(_3);
-//         StorageLive(_7);
-//         _7 = &_2;
-//         _6 = const std::mem::drop(move _7) -> [return: bb28, unwind: bb4];
+//         StorageLive(_6);
+//         _6 = &_2;
+//         _5 = const std::mem::drop(move _6) -> [return: bb28, unwind: bb4];
 //     }
 //     bb15: {
 //         goto -> bb16;
@@ -129,15 +128,15 @@ fn main() {
 //         goto -> bb2;
 //     }
 //     bb26: {
-//         _5 = ();
+//         _4 = ();
 //         unreachable;
 //     }
 //     bb27: {
-//         StorageDead(_5);
+//         StorageDead(_4);
 //         goto -> bb14;
 //     }
 //     bb28: {
-//         StorageDead(_7);
+//         StorageDead(_6);
 //         _1 = ();
 //         StorageDead(_2);
 //         goto -> bb1;
diff --git a/src/test/mir-opt/match_false_edges.rs b/src/test/mir-opt/match_false_edges.rs
index b9f4c239127..9ccf0371399 100644
--- a/src/test/mir-opt/match_false_edges.rs
+++ b/src/test/mir-opt/match_false_edges.rs
@@ -53,10 +53,11 @@ fn main() {
 //  bb0: {
 //      ...
 //      _2 = std::option::Option<i32>::Some(const 42i32,);
-//      _3 = discriminant(_2);
-//      _4 = &(promoted[1]: std::option::Option<i32>);
-//      _9 = discriminant(_2);
-//      switchInt(move _9) -> [0isize: bb5, 1isize: bb3, otherwise: bb7];
+//      FakeRead(ForMatchedPlace, _2);
+//      _7 = discriminant(_2);
+//      _9 = &shallow (promoted[2]: std::option::Option<i32>);
+//      _10 = &(((promoted[1]: std::option::Option<i32>) as Some).0: i32);
+//      switchInt(move _7) -> [0isize: bb5, 1isize: bb3, otherwise: bb7];
 //  }
 //  bb1: {
 //      resume;
@@ -66,15 +67,18 @@ fn main() {
 //      goto -> bb13;
 //  }
 //  bb3: { // binding3(empty) and arm3
-//      FakeRead(ForMatch, _4);
+//      FakeRead(ForMatchGuard, _9);
+//      FakeRead(ForMatchGuard, _10);
 //      falseEdges -> [real: bb8, imaginary: bb4]; //pre_binding1
 //  }
 //  bb4: {
-//      FakeRead(ForMatch, _4);
+//      FakeRead(ForMatchGuard, _9);
+//      FakeRead(ForMatchGuard, _10);
 //      falseEdges -> [real: bb12, imaginary: bb5]; //pre_binding2
 //  }
 //  bb5: {
-//      FakeRead(ForMatch, _4);
+//      FakeRead(ForMatchGuard, _9);
+//      FakeRead(ForMatchGuard, _10);
 //      falseEdges -> [real: bb2, imaginary: bb6]; //pre_binding3
 //  }
 //  bb6: {
@@ -84,31 +88,31 @@ fn main() {
 //      unreachable;
 //  }
 //  bb8: { // binding1 and guard
-//      StorageLive(_7);
-//      _7 = &(((promoted[0]: std::option::Option<i32>) as Some).0: i32);
-//      StorageLive(_10);
-//      _10 = const guard() -> [return: bb9, unwind: bb1];
+//      StorageLive(_5);
+//      _5 = &(((promoted[0]: std::option::Option<i32>) as Some).0: i32);
+//      StorageLive(_8);
+//      _8 = const guard() -> [return: bb9, unwind: bb1];
 //  }
 //  bb9: {
-//      switchInt(move _10) -> [false: bb10, otherwise: bb11];
+//      switchInt(move _8) -> [false: bb10, otherwise: bb11];
 //  }
 //  bb10: { // to pre_binding2
 //      falseEdges -> [real: bb4, imaginary: bb4];
 //  }
 //  bb11: { // bindingNoLandingPads.before.mir2 and arm2
-//      StorageLive(_5);
-//      _5 = ((_2 as Some).0: i32);
+//      StorageLive(_3);
+//      _3 = ((_2 as Some).0: i32);
 //      StorageLive(_11);
-//      _11 = _5;
+//      _11 = _3;
 //      _1 = (const 1i32, move _11);
 //      StorageDead(_11);
 //      goto -> bb13;
 //  }
 //  bb12: {
-//      StorageLive(_8);
-//      _8 = ((_2 as Some).0: i32);
+//      StorageLive(_6);
+//      _6 = ((_2 as Some).0: i32);
 //      StorageLive(_12);
-//      _12 = _8;
+//      _12 = _6;
 //      _1 = (const 2i32, move_12);
 //      StorageDead(_12);
 //      goto -> bb13;
@@ -123,10 +127,11 @@ fn main() {
 //  bb0: {
 //      ...
 //      _2 = std::option::Option<i32>::Some(const 42i32,);
-//      _3 = discriminant(_2);
-//      _4 = &_2;
-//      _9 = discriminant(_2);
-//      switchInt(move _9) -> [0isize: bb4, 1isize: bb3, otherwise: bb7];
+//      FakeRead(ForMatchedPlace, _2);
+//      _7 = discriminant(_2);
+//      _9 = &shallow _2;
+//      _10 = &((_2 as Some).0: i32);
+//      switchInt(move _7) -> [0isize: bb4, 1isize: bb3, otherwise: bb7];
 //  }
 //  bb1: {
 //      resume;
@@ -136,15 +141,18 @@ fn main() {
 //      goto -> bb13;
 //  }
 //  bb3: {
-//      FakeRead(ForMatch, _4);
+//      FakeRead(ForMatchGuard, _9);
+//      FakeRead(ForMatchGuard, _10);
 //      falseEdges -> [real: bb8, imaginary: bb4]; //pre_binding1
 //  }
 //  bb4: {
-//      FakeRead(ForMatch, _4);
+//      FakeRead(ForMatchGuard, _9);
+//      FakeRead(ForMatchGuard, _10);
 //      falseEdges -> [real: bb2, imaginary: bb5]; //pre_binding2
 //  }
 //  bb5: {
-//      FakeRead(ForMatch, _4);
+//      FakeRead(ForMatchGuard, _9);
+//      FakeRead(ForMatchGuard, _10);
 //      falseEdges -> [real: bb12, imaginary: bb6]; //pre_binding3
 //  }
 //  bb6: {
@@ -154,31 +162,31 @@ fn main() {
 //      unreachable;
 //  }
 //  bb8: { // binding1 and guard
-//      StorageLive(_7);
-//      _7 = &((_2 as Some).0: i32);
-//      StorageLive(_10);
-//      _10 = const guard() -> [return: bb9, unwind: bb1];
+//      StorageLive(_5);
+//      _5 = &((_2 as Some).0: i32);
+//      StorageLive(_8);
+//      _8 = const guard() -> [return: bb9, unwind: bb1];
 //  }
 //  bb9: { // end of guard
-//      switchInt(move _10) -> [false: bb10, otherwise: bb11];
+//      switchInt(move _8) -> [false: bb10, otherwise: bb11];
 //  }
 //  bb10: { // to pre_binding3 (can skip 2 since this is `Some`)
 //      falseEdges -> [real: bb5, imaginary: bb4];
 //  }
 //  bb11: { // arm1
-//      StorageLive(_5);
-//      _5 = ((_2 as Some).0: i32);
+//      StorageLive(_3);
+//      _3 = ((_2 as Some).0: i32);
 //      StorageLive(_11);
-//      _11 = _5;
+//      _11 = _3;
 //      _1 = (const 1i32, move _11);
 //      StorageDead(_11);
 //      goto -> bb13;
 //  }
 //  bb12: { // binding3 and arm3
-//      StorageLive(_8);
-//      _8 = ((_2 as Some).0: i32);
+//      StorageLive(_6);
+//      _6 = ((_2 as Some).0: i32);
 //      StorageLive(_12);
-//      _12 = _8;
+//      _12 = _6;
 //      _1 = (const 2i32, move _12);
 //      StorageDead(_12);
 //      goto -> bb13;
@@ -193,81 +201,86 @@ fn main() {
 // bb0: {
 //     ...
 //     _2 = std::option::Option<i32>::Some(const 1i32,);
-//     _3 = discriminant(_2);
-//     _4 = &_2;
-//     _13 = discriminant(_2);
-//     switchInt(move _13) -> [1isize: bb2, otherwise: bb3];
+//     FakeRead(ForMatchedPlace, _2);
+//     _11 = discriminant(_2);
+//    _16 = &shallow _2;
+//    _17 = &((_2 as Some).0: i32);
+//     switchInt(move _11) -> [1isize: bb2, otherwise: bb3];
 // }
 // bb1: {
 //     resume;
 // }
 // bb2: {
-//     FakeRead(ForMatch, _4);
+//      FakeRead(ForMatchGuard, _16);
+//      FakeRead(ForMatchGuard, _17);
 //     falseEdges -> [real: bb7, imaginary: bb3]; //pre_binding1
 // }
 // bb3: {
-//     FakeRead(ForMatch, _4);
+//      FakeRead(ForMatchGuard, _16);
+//      FakeRead(ForMatchGuard, _17);
 //     falseEdges -> [real: bb11, imaginary: bb4]; //pre_binding2
 // }
 // bb4: {
-//     FakeRead(ForMatch, _4);
+//      FakeRead(ForMatchGuard, _16);
+//      FakeRead(ForMatchGuard, _17);
 //     falseEdges -> [real: bb12, imaginary: bb5]; //pre_binding3
 // }
 // bb5: {
-//     FakeRead(ForMatch, _4);
+//      FakeRead(ForMatchGuard, _16);
+//      FakeRead(ForMatchGuard, _17);
 //     falseEdges -> [real: bb16, imaginary: bb6]; //pre_binding4
 // }
 // bb6: {
 //     unreachable;
 // }
 // bb7: { // binding1: Some(w) if guard()
-//     StorageLive(_7);
-//     _7 = &((_2 as Some).0: i32);
-//     StorageLive(_14);
-//     _14 = const guard() -> [return: bb8, unwind: bb1];
+//     StorageLive(_5);
+//     _5 = &((_2 as Some).0: i32);
+//     StorageLive(_12);
+//     _12 = const guard() -> [return: bb8, unwind: bb1];
 // }
 // bb8: { //end of guard
-//     switchInt(move _14) -> [false: bb9, otherwise: bb10];
+//     switchInt(move _12) -> [false: bb9, otherwise: bb10];
 // }
 // bb9: { // to pre_binding2
 //     falseEdges -> [real: bb3, imaginary: bb3];
 // }
 // bb10: { // set up bindings for arm1
-//     StorageLive(_5);
-//     _5 = ((_2 as Some).0: i32);
+//     StorageLive(_3);
+//     _3 = ((_2 as Some).0: i32);
 //     _1 = const 1i32;
 //     goto -> bb17;
 // }
 // bb11: { // binding2 & arm2
-//     StorageLive(_8);
-//     _8 = _2;
+//     StorageLive(_6);
+//     _6 = _2;
 //     _1 = const 2i32;
 //     goto -> bb17;
 // }
 // bb12: { // binding3: Some(y) if guard2(y)
-//     StorageLive(_11);
-//     _11 = &((_2 as Some).0: i32);
-//     StorageLive(_16);
-//     StorageLive(_17);
-//     _17 = (*_11);
-//     _16 = const guard2(move _17) -> [return: bb13, unwind: bb1];
+//     StorageLive(_9);
+//     _9 = &((_2 as Some).0: i32);
+//     StorageLive(_14);
+//     StorageLive(_15);
+//     _15 = (*_9);
+//     _14 = const guard2(move _15) -> [return: bb13, unwind: bb1];
 // }
 // bb13: { // end of guard2
-//     StorageDead(_17);
-//     switchInt(move _16) -> [false: bb14, otherwise: bb15];
+//     StorageDead(_15);
+//     switchInt(move _14) -> [false: bb14, otherwise: bb15];
 // }
 // bb14: { // to pre_binding4
 //     falseEdges -> [real: bb5, imaginary: bb5];
 // }
 // bb15: { // set up bindings for arm3
-//     StorageLive(_9);
-//     _9 = ((_2 as Some).0: i32);
+//     StorageLive(_7);
+//     _7 = ((_2 as Some).0: i32);
 //     _1 = const 3i32;
 //     goto -> bb17;
 // }
 // bb16: { // binding4 & arm4
-//     StorageLive(_12);
-//     _12 = _2;
+//     StorageLive(_10);
+//     _10 = _2;
 //     _1 = const 4i32;
 //     goto -> bb17;
 // }
diff --git a/src/test/mir-opt/remove_fake_borrows.rs b/src/test/mir-opt/remove_fake_borrows.rs
new file mode 100644
index 00000000000..8411fba02e9
--- /dev/null
+++ b/src/test/mir-opt/remove_fake_borrows.rs
@@ -0,0 +1,122 @@
+// Test that the fake borrows for matches are removed after borrow checking.
+
+// ignore-wasm32-bare
+
+#![feature(nll)]
+
+fn match_guard(x: Option<&&i32>) -> i32 {
+    match x {
+        Some(0) if true => 0,
+        _ => 1,
+    }
+}
+
+fn main() {
+    match_guard(None);
+}
+
+// END RUST SOURCE
+
+// START rustc.match_guard.CleanFakeReadsAndBorrows.before.mir
+// bb0: {
+//     FakeRead(ForMatchedPlace, _1);
+//     _2 = discriminant(_1);
+//     _3 = &shallow _1;
+//     _4 = &shallow ((_1 as Some).0: &'<empty> &'<empty> i32);
+//     _5 = &shallow (*((_1 as Some).0: &'<empty> &'<empty> i32));
+//     _6 = &shallow (*(*((_1 as Some).0: &'<empty> &'<empty> i32)));
+//     switchInt(move _2) -> [1isize: bb6, otherwise: bb4];
+// }
+// bb1: {
+//     _0 = const 0i32;
+//     goto -> bb9;
+// }
+// bb2: {
+//     _0 = const 1i32;
+//     goto -> bb9;
+// }
+// bb3: {
+//     FakeRead(ForMatchGuard, _3);
+//     FakeRead(ForMatchGuard, _4);
+//     FakeRead(ForMatchGuard, _5);
+//     FakeRead(ForMatchGuard, _6);
+//     goto -> bb7;
+// }
+// bb4: {
+//     FakeRead(ForMatchGuard, _3);
+//     FakeRead(ForMatchGuard, _4);
+//     FakeRead(ForMatchGuard, _5);
+//     FakeRead(ForMatchGuard, _6);
+//     goto -> bb2;
+// }
+// bb5: {
+//     unreachable;
+// }
+// bb6: {
+//     switchInt((*(*((_1 as Some).0: &'<empty> &'<empty> i32)))) -> [0i32: bb3, otherwise: bb4];
+// }
+// bb7: {
+//     goto -> bb1;
+// }
+// bb8: {
+//     goto -> bb4;
+// }
+// bb9: {
+//     return;
+// }
+// bb10: {
+//     resume;
+// }
+// END rustc.match_guard.CleanFakeReadsAndBorrows.before.mir
+
+// START rustc.match_guard.CleanFakeReadsAndBorrows.after.mir
+// bb0: {
+//     nop;
+//     _2 = discriminant(_1);
+//     nop;
+//     nop;
+//     nop;
+//     nop;
+//     switchInt(move _2) -> [1isize: bb6, otherwise: bb4];
+// }
+// bb1: {
+//     _0 = const 0i32;
+//     goto -> bb9;
+// }
+// bb2: {
+//     _0 = const 1i32;
+//     goto -> bb9;
+// }
+// bb3: {
+//     nop;
+//     nop;
+//     nop;
+//     nop;
+//     goto -> bb7;
+// }
+// bb4: {
+//     nop;
+//     nop;
+//     nop;
+//     nop;
+//     goto -> bb2;
+// }
+// bb5: {
+//     unreachable;
+// }
+// bb6: {
+//     switchInt((*(*((_1 as Some).0: &'<empty> &'<empty> i32)))) -> [0i32: bb3, otherwise: bb4];
+// }
+// bb7: {
+//     goto -> bb1;
+// }
+// bb8: {
+//     goto -> bb4;
+// }
+// bb9: {
+//     return;
+// }
+// bb10: {
+//     resume;
+// }
+// END rustc.match_guard.CleanFakeReadsAndBorrows.after.mir
diff --git a/src/test/mir-opt/validate_1.rs b/src/test/mir-opt/validate_1.rs
index 3ea8e99e953..882579c5710 100644
--- a/src/test/mir-opt/validate_1.rs
+++ b/src/test/mir-opt/validate_1.rs
@@ -67,7 +67,6 @@ fn main() {
 //         Validate(Suspend(ReScope(Remainder { block: ItemLocalId(25), first_statement_index: 0 })), [(*_2): i32]);
 //         _3 = &ReErased (*_2);
 //         Validate(Acquire, [(*_3): i32/ReScope(Remainder { block: ItemLocalId(25), first_statement_index: 0 }) (imm)]);
-//         FakeRead(ForLet, _3);
 //         _0 = (*_3);
 //         EndRegion(ReScope(Remainder { block: ItemLocalId(25), first_statement_index: 0 }));
 //         StorageDead(_3);
diff --git a/src/test/mir-opt/validate_2.rs b/src/test/mir-opt/validate_2.rs
index 0cb0b7debfa..3776a11b3ab 100644
--- a/src/test/mir-opt/validate_2.rs
+++ b/src/test/mir-opt/validate_2.rs
@@ -28,7 +28,6 @@ fn main() {
 //         Validate(Acquire, [_1: std::boxed::Box<[i32]>]);
 //         StorageDead(_2);
 //         StorageDead(_3);
-//         FakeRead(ForLet, _1);
 //         _0 = ();
 //         Validate(Release, [_1: std::boxed::Box<[i32]>]);
 //         drop(_1) -> [return: bb2, unwind: bb3];
diff --git a/src/test/mir-opt/validate_3.rs b/src/test/mir-opt/validate_3.rs
index 89b67bd34c8..07f5b2aa84b 100644
--- a/src/test/mir-opt/validate_3.rs
+++ b/src/test/mir-opt/validate_3.rs
@@ -47,12 +47,10 @@ fn main() {
 //     bb0: {
 //         StorageLive(_1);
 //         _1 = Test { x: const 0i32 };
-//         FakeRead(ForLet, _1);
 //         StorageLive(_2);
 //         Validate(Suspend(ReScope(Remainder { block: ItemLocalId(20), first_statement_index: 3 })), [_1: Test]);
 //         _2 = &ReErased _1;
 //         Validate(Acquire, [(*_2): Test/ReScope(Remainder { block: ItemLocalId(20), first_statement_index: 3 }) (imm)]);
-//         FakeRead(ForLet, _2);
 //         StorageLive(_4);
 //         StorageLive(_5);
 //         Validate(Suspend(ReScope(Node(ItemLocalId(18)))), [((*_2).0: i32): i32/ReScope(Remainder { block: ItemLocalId(20), first_statement_index: 3 }) (imm)]);
diff --git a/src/test/ui/borrowck/borrowck-anon-fields-struct.nll.stderr b/src/test/ui/borrowck/borrowck-anon-fields-struct.nll.stderr
index 0fe9106249b..963a89ed44d 100644
--- a/src/test/ui/borrowck/borrowck-anon-fields-struct.nll.stderr
+++ b/src/test/ui/borrowck/borrowck-anon-fields-struct.nll.stderr
@@ -1,27 +1,3 @@
-error[E0502]: cannot borrow `y` as immutable because it is also borrowed as mutable
-  --> $DIR/borrowck-anon-fields-struct.rs:23:19
-   |
-LL |         Y(ref mut a, _) => a
-   |           --------- mutable borrow occurs here
-...
-LL |     let b = match y {
-   |                   ^ immutable borrow occurs here
-...
-LL |     *a += 1;
-   |     ------- borrow later used here
-
-error[E0502]: cannot borrow `y` as immutable because it is also borrowed as mutable
-  --> $DIR/borrowck-anon-fields-struct.rs:38:19
-   |
-LL |         Y(ref mut a, _) => a
-   |           --------- mutable borrow occurs here
-...
-LL |     let b = match y {
-   |                   ^ immutable borrow occurs here
-...
-LL |     *a += 1;
-   |     ------- borrow later used here
-
 error[E0499]: cannot borrow `y.0` as mutable more than once at a time
   --> $DIR/borrowck-anon-fields-struct.rs:39:11
    |
@@ -34,7 +10,6 @@ LL |         Y(ref mut b, _) => b //~ ERROR cannot borrow
 LL |     *a += 1;
    |     ------- borrow later used here
 
-error: aborting due to 3 previous errors
+error: aborting due to previous error
 
-Some errors occurred: E0499, E0502.
-For more information about an error, try `rustc --explain E0499`.
+For more information about this error, try `rustc --explain E0499`.
diff --git a/src/test/ui/borrowck/borrowck-anon-fields-tuple.nll.stderr b/src/test/ui/borrowck/borrowck-anon-fields-tuple.nll.stderr
index 015174a9e45..f06822f7bb6 100644
--- a/src/test/ui/borrowck/borrowck-anon-fields-tuple.nll.stderr
+++ b/src/test/ui/borrowck/borrowck-anon-fields-tuple.nll.stderr
@@ -1,27 +1,3 @@
-error[E0502]: cannot borrow `y` as immutable because it is also borrowed as mutable
-  --> $DIR/borrowck-anon-fields-tuple.rs:21:19
-   |
-LL |         (ref mut a, _) => a
-   |          --------- mutable borrow occurs here
-...
-LL |     let b = match y {
-   |                   ^ immutable borrow occurs here
-...
-LL |     *a += 1;
-   |     ------- borrow later used here
-
-error[E0502]: cannot borrow `y` as immutable because it is also borrowed as mutable
-  --> $DIR/borrowck-anon-fields-tuple.rs:36:19
-   |
-LL |         (ref mut a, _) => a
-   |          --------- mutable borrow occurs here
-...
-LL |     let b = match y {
-   |                   ^ immutable borrow occurs here
-...
-LL |     *a += 1;
-   |     ------- borrow later used here
-
 error[E0499]: cannot borrow `y.0` as mutable more than once at a time
   --> $DIR/borrowck-anon-fields-tuple.rs:37:10
    |
@@ -34,7 +10,6 @@ LL |         (ref mut b, _) => b //~ ERROR cannot borrow
 LL |     *a += 1;
    |     ------- borrow later used here
 
-error: aborting due to 3 previous errors
+error: aborting due to previous error
 
-Some errors occurred: E0499, E0502.
-For more information about an error, try `rustc --explain E0499`.
+For more information about this error, try `rustc --explain E0499`.
diff --git a/src/test/ui/borrowck/borrowck-anon-fields-variant.nll.stderr b/src/test/ui/borrowck/borrowck-anon-fields-variant.nll.stderr
index e4b9f2f2329..05197205e81 100644
--- a/src/test/ui/borrowck/borrowck-anon-fields-variant.nll.stderr
+++ b/src/test/ui/borrowck/borrowck-anon-fields-variant.nll.stderr
@@ -1,27 +1,3 @@
-error[E0502]: cannot borrow `y` as immutable because it is also borrowed as mutable
-  --> $DIR/borrowck-anon-fields-variant.rs:26:19
-   |
-LL |       Foo::Y(ref mut a, _) => a,
-   |              --------- mutable borrow occurs here
-...
-LL |     let b = match y {
-   |                   ^ immutable borrow occurs here
-...
-LL |     *a += 1;
-   |     ------- borrow later used here
-
-error[E0502]: cannot borrow `y` as immutable because it is also borrowed as mutable
-  --> $DIR/borrowck-anon-fields-variant.rs:43:19
-   |
-LL |       Foo::Y(ref mut a, _) => a,
-   |              --------- mutable borrow occurs here
-...
-LL |     let b = match y {
-   |                   ^ immutable borrow occurs here
-...
-LL |     *a += 1;
-   |     ------- borrow later used here
-
 error[E0499]: cannot borrow `y.0` as mutable more than once at a time
   --> $DIR/borrowck-anon-fields-variant.rs:44:14
    |
@@ -34,7 +10,6 @@ LL |       Foo::Y(ref mut b, _) => b, //~ ERROR cannot borrow
 LL |     *a += 1;
    |     ------- borrow later used here
 
-error: aborting due to 3 previous errors
+error: aborting due to previous error
 
-Some errors occurred: E0499, E0502.
-For more information about an error, try `rustc --explain E0499`.
+For more information about this error, try `rustc --explain E0499`.
diff --git a/src/test/ui/borrowck/borrowck-borrow-from-owned-ptr.nll.stderr b/src/test/ui/borrowck/borrowck-borrow-from-owned-ptr.nll.stderr
index 08d66eb0344..603f4d63f7f 100644
--- a/src/test/ui/borrowck/borrowck-borrow-from-owned-ptr.nll.stderr
+++ b/src/test/ui/borrowck/borrowck-borrow-from-owned-ptr.nll.stderr
@@ -28,17 +28,6 @@ LL |     let _bar2 = &mut foo.bar1;  //~ ERROR cannot borrow
 LL |     *bar1;
    |     ----- borrow later used here
 
-error[E0502]: cannot borrow `*foo` as immutable because it is also borrowed as mutable
-  --> $DIR/borrowck-borrow-from-owned-ptr.rs:72:11
-   |
-LL |     let bar1 = &mut foo.bar1;
-   |                ------------- mutable borrow occurs here
-LL |     match *foo {
-   |           ^^^^ immutable borrow occurs here
-...
-LL |     *bar1;
-   |     ----- borrow later used here
-
 error[E0499]: cannot borrow `foo.bar1` as mutable more than once at a time
   --> $DIR/borrowck-borrow-from-owned-ptr.rs:73:21
    |
@@ -121,7 +110,7 @@ LL |     let foo = make_foo();
 LL |     let bar1 = &mut foo.bar1; //~ ERROR cannot borrow
    |                ^^^^^^^^^^^^^ cannot borrow as mutable
 
-error: aborting due to 12 previous errors
+error: aborting due to 11 previous errors
 
 Some errors occurred: E0499, E0502, E0596.
 For more information about an error, try `rustc --explain E0499`.
diff --git a/src/test/ui/borrowck/borrowck-borrow-from-stack-variable.nll.stderr b/src/test/ui/borrowck/borrowck-borrow-from-stack-variable.nll.stderr
index 8d1b9ca672a..3a215f2336a 100644
--- a/src/test/ui/borrowck/borrowck-borrow-from-stack-variable.nll.stderr
+++ b/src/test/ui/borrowck/borrowck-borrow-from-stack-variable.nll.stderr
@@ -28,17 +28,6 @@ LL |     let _bar2 = &mut foo.bar1;  //~ ERROR cannot borrow
 LL |     *bar1;
    |     ----- borrow later used here
 
-error[E0502]: cannot borrow `foo` as immutable because it is also borrowed as mutable
-  --> $DIR/borrowck-borrow-from-stack-variable.rs:70:11
-   |
-LL |     let bar1 = &mut foo.bar1;
-   |                ------------- mutable borrow occurs here
-LL |     match foo {
-   |           ^^^ immutable borrow occurs here
-...
-LL |     *bar1;
-   |     ----- borrow later used here
-
 error[E0499]: cannot borrow `foo.bar1` as mutable more than once at a time
   --> $DIR/borrowck-borrow-from-stack-variable.rs:71:21
    |
@@ -121,7 +110,7 @@ LL |     let foo = make_foo();
 LL |     let bar1 = &mut foo.bar1; //~ ERROR cannot borrow
    |                ^^^^^^^^^^^^^ cannot borrow as mutable
 
-error: aborting due to 12 previous errors
+error: aborting due to 11 previous errors
 
 Some errors occurred: E0499, E0502, E0596.
 For more information about an error, try `rustc --explain E0499`.
diff --git a/src/test/ui/borrowck/borrowck-describe-lvalue.ast.nll.stderr b/src/test/ui/borrowck/borrowck-describe-lvalue.ast.nll.stderr
index ae706ef64dd..e417dadf6df 100644
--- a/src/test/ui/borrowck/borrowck-describe-lvalue.ast.nll.stderr
+++ b/src/test/ui/borrowck/borrowck-describe-lvalue.ast.nll.stderr
@@ -72,23 +72,12 @@ LL |              //[mir]~^ ERROR cannot use `h.0` because it was mutably borrow
 LL |         drop(x);
    |              - borrow later used here
 
-error[E0503]: cannot use `e` because it was mutably borrowed
-  --> $DIR/borrowck-describe-lvalue.rs:77:15
-   |
-LL |         let x = e.x();
-   |                 - borrow of `e` occurs here
-LL |         match e { //[mir]~ ERROR cannot use `e` because it was mutably borrowed
-   |               ^ use of borrowed `e`
-...
-LL |         drop(x);
-   |              - borrow later used here
-
 error[E0503]: cannot use `e.0` because it was mutably borrowed
   --> $DIR/borrowck-describe-lvalue.rs:78:20
    |
 LL |         let x = e.x();
    |                 - borrow of `e` occurs here
-LL |         match e { //[mir]~ ERROR cannot use `e` because it was mutably borrowed
+LL |         match e {
 LL |             Baz::X(value) => value
    |                    ^^^^^ use of borrowed `e`
 ...
@@ -139,23 +128,12 @@ LL |              //[mir]~^ ERROR cannot use `h.0` because it was mutably borrow
 LL |         drop(x);
    |              - borrow later used here
 
-error[E0503]: cannot use `*e` because it was mutably borrowed
-  --> $DIR/borrowck-describe-lvalue.rs:120:15
-   |
-LL |         let x = e.x();
-   |                 - borrow of `*e` occurs here
-LL |         match *e { //[mir]~ ERROR cannot use `*e` because it was mutably borrowed
-   |               ^^ use of borrowed `*e`
-...
-LL |         drop(x);
-   |              - borrow later used here
-
 error[E0503]: cannot use `e.0` because it was mutably borrowed
   --> $DIR/borrowck-describe-lvalue.rs:121:20
    |
 LL |         let x = e.x();
    |                 - borrow of `*e` occurs here
-LL |         match *e { //[mir]~ ERROR cannot use `*e` because it was mutably borrowed
+LL |         match *e {
 LL |             Baz::X(value) => value
    |                    ^^^^^ use of borrowed `*e`
 ...
@@ -173,41 +151,18 @@ LL |              //[mir]~^ ERROR cannot use `u.a` because it was mutably borrow
 LL |         drop(x);
    |              - borrow later used here
 
-error[E0503]: cannot use `v` because it was mutably borrowed
-  --> $DIR/borrowck-describe-lvalue.rs:139:15
-   |
-LL |         let x = &mut v;
-   |                 ------ borrow of `v` occurs here
-LL |         match v { //[mir]~ ERROR cannot use `v` because it was mutably borrowed
-   |               ^ use of borrowed `v`
-...
-LL |         drop(x);
-   |              - borrow later used here
-
 error[E0503]: cannot use `v[..]` because it was mutably borrowed
   --> $DIR/borrowck-describe-lvalue.rs:140:15
    |
 LL |         let x = &mut v;
    |                 ------ borrow of `v` occurs here
-LL |         match v { //[mir]~ ERROR cannot use `v` because it was mutably borrowed
+LL |         match v {
 LL |             &[x, _, .., _, _] => println!("{}", x),
    |               ^ use of borrowed `v`
 ...
 LL |         drop(x);
    |              - borrow later used here
 
-error[E0503]: cannot use `v` because it was mutably borrowed
-  --> $DIR/borrowck-describe-lvalue.rs:145:15
-   |
-LL |         let x = &mut v;
-   |                 ------ borrow of `v` occurs here
-...
-LL |         match v { //[mir]~ ERROR cannot use `v` because it was mutably borrowed
-   |               ^ use of borrowed `v`
-...
-LL |         drop(x);
-   |              - borrow later used here
-
 error[E0503]: cannot use `v[..]` because it was mutably borrowed
   --> $DIR/borrowck-describe-lvalue.rs:146:18
    |
@@ -220,18 +175,6 @@ LL |             &[_, x, .., _, _] => println!("{}", x),
 LL |         drop(x);
    |              - borrow later used here
 
-error[E0503]: cannot use `v` because it was mutably borrowed
-  --> $DIR/borrowck-describe-lvalue.rs:151:15
-   |
-LL |         let x = &mut v;
-   |                 ------ borrow of `v` occurs here
-...
-LL |         match v { //[mir]~ ERROR cannot use `v` because it was mutably borrowed
-   |               ^ use of borrowed `v`
-...
-LL |         drop(x);
-   |              - borrow later used here
-
 error[E0503]: cannot use `v[..]` because it was mutably borrowed
   --> $DIR/borrowck-describe-lvalue.rs:152:25
    |
@@ -244,18 +187,6 @@ LL |             &[_, _, .., x, _] => println!("{}", x),
 LL |         drop(x);
    |              - borrow later used here
 
-error[E0503]: cannot use `v` because it was mutably borrowed
-  --> $DIR/borrowck-describe-lvalue.rs:157:15
-   |
-LL |         let x = &mut v;
-   |                 ------ borrow of `v` occurs here
-...
-LL |         match v { //[mir]~ ERROR cannot use `v` because it was mutably borrowed
-   |               ^ use of borrowed `v`
-...
-LL |         drop(x);
-   |              - borrow later used here
-
 error[E0503]: cannot use `v[..]` because it was mutably borrowed
   --> $DIR/borrowck-describe-lvalue.rs:158:28
    |
@@ -268,41 +199,18 @@ LL |             &[_, _, .., _, x] => println!("{}", x),
 LL |         drop(x);
    |              - borrow later used here
 
-error[E0503]: cannot use `v` because it was mutably borrowed
-  --> $DIR/borrowck-describe-lvalue.rs:169:15
-   |
-LL |         let x = &mut v;
-   |                 ------ borrow of `v` occurs here
-LL |         match v { //[mir]~ ERROR cannot use `v` because it was mutably borrowed
-   |               ^ use of borrowed `v`
-...
-LL |         drop(x);
-   |              - borrow later used here
-
 error[E0503]: cannot use `v[..]` because it was mutably borrowed
   --> $DIR/borrowck-describe-lvalue.rs:170:15
    |
 LL |         let x = &mut v;
    |                 ------ borrow of `v` occurs here
-LL |         match v { //[mir]~ ERROR cannot use `v` because it was mutably borrowed
+LL |         match v {
 LL |             &[x..] => println!("{:?}", x),
    |               ^ use of borrowed `v`
 ...
 LL |         drop(x);
    |              - borrow later used here
 
-error[E0503]: cannot use `v` because it was mutably borrowed
-  --> $DIR/borrowck-describe-lvalue.rs:175:15
-   |
-LL |         let x = &mut v;
-   |                 ------ borrow of `v` occurs here
-...
-LL |         match v { //[mir]~ ERROR cannot use `v` because it was mutably borrowed
-   |               ^ use of borrowed `v`
-...
-LL |         drop(x);
-   |              - borrow later used here
-
 error[E0503]: cannot use `v[..]` because it was mutably borrowed
   --> $DIR/borrowck-describe-lvalue.rs:176:18
    |
@@ -315,18 +223,6 @@ LL |             &[_, x..] => println!("{:?}", x),
 LL |         drop(x);
    |              - borrow later used here
 
-error[E0503]: cannot use `v` because it was mutably borrowed
-  --> $DIR/borrowck-describe-lvalue.rs:181:15
-   |
-LL |         let x = &mut v;
-   |                 ------ borrow of `v` occurs here
-...
-LL |         match v { //[mir]~ ERROR cannot use `v` because it was mutably borrowed
-   |               ^ use of borrowed `v`
-...
-LL |         drop(x);
-   |              - borrow later used here
-
 error[E0503]: cannot use `v[..]` because it was mutably borrowed
   --> $DIR/borrowck-describe-lvalue.rs:182:15
    |
@@ -339,18 +235,6 @@ LL |             &[x.., _] => println!("{:?}", x),
 LL |         drop(x);
    |              - borrow later used here
 
-error[E0503]: cannot use `v` because it was mutably borrowed
-  --> $DIR/borrowck-describe-lvalue.rs:187:15
-   |
-LL |         let x = &mut v;
-   |                 ------ borrow of `v` occurs here
-...
-LL |         match v { //[mir]~ ERROR cannot use `v` because it was mutably borrowed
-   |               ^ use of borrowed `v`
-...
-LL |         drop(x);
-   |              - borrow later used here
-
 error[E0503]: cannot use `v[..]` because it was mutably borrowed
   --> $DIR/borrowck-describe-lvalue.rs:188:18
    |
@@ -364,22 +248,11 @@ LL |         drop(x);
    |              - borrow later used here
 
 error[E0503]: cannot use `e` because it was mutably borrowed
-  --> $DIR/borrowck-describe-lvalue.rs:201:15
-   |
-LL |         let x = &mut e;
-   |                 ------ borrow of `e` occurs here
-LL |         match e { //[mir]~ ERROR cannot use `e` because it was mutably borrowed
-   |               ^ use of borrowed `e`
-...
-LL |         drop(x);
-   |              - borrow later used here
-
-error[E0503]: cannot use `e` because it was mutably borrowed
   --> $DIR/borrowck-describe-lvalue.rs:202:13
    |
 LL |         let x = &mut e;
    |                 ------ borrow of `e` occurs here
-LL |         match e { //[mir]~ ERROR cannot use `e` because it was mutably borrowed
+LL |         match e {
 LL |             E::A(ref ax) =>
    |             ^^^^^^^^^^^^ use of borrowed `e`
 ...
@@ -391,7 +264,7 @@ error[E0502]: cannot borrow `e.0` as immutable because it is also borrowed as mu
    |
 LL |         let x = &mut e;
    |                 ------ mutable borrow occurs here
-LL |         match e { //[mir]~ ERROR cannot use `e` because it was mutably borrowed
+LL |         match e {
 LL |             E::A(ref ax) =>
    |                  ^^^^^^ immutable borrow occurs here
 ...
@@ -410,41 +283,18 @@ LL |             E::B { x: ref bx } =>
 LL |         drop(x);
    |              - borrow later used here
 
-error[E0503]: cannot use `s` because it was mutably borrowed
-  --> $DIR/borrowck-describe-lvalue.rs:220:15
-   |
-LL |         let x = &mut s;
-   |                 ------ borrow of `s` occurs here
-LL |         match s { //[mir]~ ERROR cannot use `s` because it was mutably borrowed
-   |               ^ use of borrowed `s`
-...
-LL |         drop(x);
-   |              - borrow later used here
-
 error[E0502]: cannot borrow `s.y.0` as immutable because it is also borrowed as mutable
   --> $DIR/borrowck-describe-lvalue.rs:221:22
    |
 LL |         let x = &mut s;
    |                 ------ mutable borrow occurs here
-LL |         match s { //[mir]~ ERROR cannot use `s` because it was mutably borrowed
+LL |         match s {
 LL |             S  { y: (ref y0, _), .. } =>
    |                      ^^^^^^ immutable borrow occurs here
 ...
 LL |         drop(x);
    |              - borrow later used here
 
-error[E0503]: cannot use `s` because it was mutably borrowed
-  --> $DIR/borrowck-describe-lvalue.rs:227:15
-   |
-LL |         let x = &mut s;
-   |                 ------ borrow of `s` occurs here
-...
-LL |         match s { //[mir]~ ERROR cannot use `s` because it was mutably borrowed
-   |               ^ use of borrowed `s`
-...
-LL |         drop(x);
-   |              - borrow later used here
-
 error[E0502]: cannot borrow `s.x.y` as immutable because it is also borrowed as mutable
   --> $DIR/borrowck-describe-lvalue.rs:228:28
    |
@@ -479,23 +329,12 @@ LL |         v[0].y;
 LL |         drop(x);
    |              - borrow later used here
 
-error[E0503]: cannot use `v` because it was mutably borrowed
-  --> $DIR/borrowck-describe-lvalue.rs:282:15
-   |
-LL |         let x = &mut v;
-   |                 ------ borrow of `v` occurs here
-LL |         match v { //[mir]~ ERROR cannot use `v` because it was mutably borrowed
-   |               ^ use of borrowed `v`
-...
-LL |         drop(x);
-   |              - borrow later used here
-
 error[E0502]: cannot borrow `v[..].x` as immutable because it is also borrowed as mutable
   --> $DIR/borrowck-describe-lvalue.rs:283:24
    |
 LL |         let x = &mut v;
    |                 ------ mutable borrow occurs here
-LL |         match v { //[mir]~ ERROR cannot use `v` because it was mutably borrowed
+LL |         match v {
 LL |             &[_, F {x: ref xf, ..}] => println!("{}", xf),
    |                        ^^^^^^ immutable borrow occurs here
 ...
@@ -534,7 +373,7 @@ LL |                 drop(x); //[ast]~ ERROR use of moved value: `x`
    |
    = note: move occurs because `x` has type `std::vec::Vec<i32>`, which does not implement the `Copy` trait
 
-error: aborting due to 46 previous errors
+error: aborting due to 32 previous errors
 
 Some errors occurred: E0382, E0499, E0502, E0503.
 For more information about an error, try `rustc --explain E0382`.
diff --git a/src/test/ui/borrowck/borrowck-describe-lvalue.ast.stderr b/src/test/ui/borrowck/borrowck-describe-lvalue.ast.stderr
index ca9b2dda8bc..bc6385ffd92 100644
--- a/src/test/ui/borrowck/borrowck-describe-lvalue.ast.stderr
+++ b/src/test/ui/borrowck/borrowck-describe-lvalue.ast.stderr
@@ -27,7 +27,7 @@ error[E0503]: cannot use `e.0` because it was mutably borrowed
    |
 LL |         let x = e.x();
    |                 - borrow of `e` occurs here
-LL |         match e { //[mir]~ ERROR cannot use `e` because it was mutably borrowed
+LL |         match e {
 LL |             Baz::X(value) => value
    |                    ^^^^^ use of borrowed `e`
 
@@ -68,7 +68,7 @@ error[E0503]: cannot use `e.0` because it was mutably borrowed
    |
 LL |         let x = e.x();
    |                 - borrow of `*e` occurs here
-LL |         match *e { //[mir]~ ERROR cannot use `*e` because it was mutably borrowed
+LL |         match *e {
 LL |             Baz::X(value) => value
    |                    ^^^^^ use of borrowed `*e`
 
@@ -85,7 +85,7 @@ error[E0503]: cannot use `v[..]` because it was mutably borrowed
    |
 LL |         let x = &mut v;
    |                      - borrow of `v` occurs here
-LL |         match v { //[mir]~ ERROR cannot use `v` because it was mutably borrowed
+LL |         match v {
 LL |             &[x, _, .., _, _] => println!("{}", x),
    |               ^ use of borrowed `v`
 
@@ -121,7 +121,7 @@ error[E0503]: cannot use `v[..]` because it was mutably borrowed
    |
 LL |         let x = &mut v;
    |                      - borrow of `v` occurs here
-LL |         match v { //[mir]~ ERROR cannot use `v` because it was mutably borrowed
+LL |         match v {
 LL |             &[x..] => println!("{:?}", x),
    |               ^ use of borrowed `v`
 
@@ -157,7 +157,7 @@ error[E0502]: cannot borrow `e.0` as immutable because `e` is also borrowed as m
    |
 LL |         let x = &mut e;
    |                      - mutable borrow occurs here
-LL |         match e { //[mir]~ ERROR cannot use `e` because it was mutably borrowed
+LL |         match e {
 LL |             E::A(ref ax) =>
    |                  ^^^^^^ immutable borrow occurs here
 ...
@@ -181,7 +181,7 @@ error[E0502]: cannot borrow `s.y.0` as immutable because `s` is also borrowed as
    |
 LL |         let x = &mut s;
    |                      - mutable borrow occurs here
-LL |         match s { //[mir]~ ERROR cannot use `s` because it was mutably borrowed
+LL |         match s {
 LL |             S  { y: (ref y0, _), .. } =>
    |                      ^^^^^^ immutable borrow occurs here
 ...
diff --git a/src/test/ui/borrowck/borrowck-describe-lvalue.mir.stderr b/src/test/ui/borrowck/borrowck-describe-lvalue.mir.stderr
index ae706ef64dd..e417dadf6df 100644
--- a/src/test/ui/borrowck/borrowck-describe-lvalue.mir.stderr
+++ b/src/test/ui/borrowck/borrowck-describe-lvalue.mir.stderr
@@ -72,23 +72,12 @@ LL |              //[mir]~^ ERROR cannot use `h.0` because it was mutably borrow
 LL |         drop(x);
    |              - borrow later used here
 
-error[E0503]: cannot use `e` because it was mutably borrowed
-  --> $DIR/borrowck-describe-lvalue.rs:77:15
-   |
-LL |         let x = e.x();
-   |                 - borrow of `e` occurs here
-LL |         match e { //[mir]~ ERROR cannot use `e` because it was mutably borrowed
-   |               ^ use of borrowed `e`
-...
-LL |         drop(x);
-   |              - borrow later used here
-
 error[E0503]: cannot use `e.0` because it was mutably borrowed
   --> $DIR/borrowck-describe-lvalue.rs:78:20
    |
 LL |         let x = e.x();
    |                 - borrow of `e` occurs here
-LL |         match e { //[mir]~ ERROR cannot use `e` because it was mutably borrowed
+LL |         match e {
 LL |             Baz::X(value) => value
    |                    ^^^^^ use of borrowed `e`
 ...
@@ -139,23 +128,12 @@ LL |              //[mir]~^ ERROR cannot use `h.0` because it was mutably borrow
 LL |         drop(x);
    |              - borrow later used here
 
-error[E0503]: cannot use `*e` because it was mutably borrowed
-  --> $DIR/borrowck-describe-lvalue.rs:120:15
-   |
-LL |         let x = e.x();
-   |                 - borrow of `*e` occurs here
-LL |         match *e { //[mir]~ ERROR cannot use `*e` because it was mutably borrowed
-   |               ^^ use of borrowed `*e`
-...
-LL |         drop(x);
-   |              - borrow later used here
-
 error[E0503]: cannot use `e.0` because it was mutably borrowed
   --> $DIR/borrowck-describe-lvalue.rs:121:20
    |
 LL |         let x = e.x();
    |                 - borrow of `*e` occurs here
-LL |         match *e { //[mir]~ ERROR cannot use `*e` because it was mutably borrowed
+LL |         match *e {
 LL |             Baz::X(value) => value
    |                    ^^^^^ use of borrowed `*e`
 ...
@@ -173,41 +151,18 @@ LL |              //[mir]~^ ERROR cannot use `u.a` because it was mutably borrow
 LL |         drop(x);
    |              - borrow later used here
 
-error[E0503]: cannot use `v` because it was mutably borrowed
-  --> $DIR/borrowck-describe-lvalue.rs:139:15
-   |
-LL |         let x = &mut v;
-   |                 ------ borrow of `v` occurs here
-LL |         match v { //[mir]~ ERROR cannot use `v` because it was mutably borrowed
-   |               ^ use of borrowed `v`
-...
-LL |         drop(x);
-   |              - borrow later used here
-
 error[E0503]: cannot use `v[..]` because it was mutably borrowed
   --> $DIR/borrowck-describe-lvalue.rs:140:15
    |
 LL |         let x = &mut v;
    |                 ------ borrow of `v` occurs here
-LL |         match v { //[mir]~ ERROR cannot use `v` because it was mutably borrowed
+LL |         match v {
 LL |             &[x, _, .., _, _] => println!("{}", x),
    |               ^ use of borrowed `v`
 ...
 LL |         drop(x);
    |              - borrow later used here
 
-error[E0503]: cannot use `v` because it was mutably borrowed
-  --> $DIR/borrowck-describe-lvalue.rs:145:15
-   |
-LL |         let x = &mut v;
-   |                 ------ borrow of `v` occurs here
-...
-LL |         match v { //[mir]~ ERROR cannot use `v` because it was mutably borrowed
-   |               ^ use of borrowed `v`
-...
-LL |         drop(x);
-   |              - borrow later used here
-
 error[E0503]: cannot use `v[..]` because it was mutably borrowed
   --> $DIR/borrowck-describe-lvalue.rs:146:18
    |
@@ -220,18 +175,6 @@ LL |             &[_, x, .., _, _] => println!("{}", x),
 LL |         drop(x);
    |              - borrow later used here
 
-error[E0503]: cannot use `v` because it was mutably borrowed
-  --> $DIR/borrowck-describe-lvalue.rs:151:15
-   |
-LL |         let x = &mut v;
-   |                 ------ borrow of `v` occurs here
-...
-LL |         match v { //[mir]~ ERROR cannot use `v` because it was mutably borrowed
-   |               ^ use of borrowed `v`
-...
-LL |         drop(x);
-   |              - borrow later used here
-
 error[E0503]: cannot use `v[..]` because it was mutably borrowed
   --> $DIR/borrowck-describe-lvalue.rs:152:25
    |
@@ -244,18 +187,6 @@ LL |             &[_, _, .., x, _] => println!("{}", x),
 LL |         drop(x);
    |              - borrow later used here
 
-error[E0503]: cannot use `v` because it was mutably borrowed
-  --> $DIR/borrowck-describe-lvalue.rs:157:15
-   |
-LL |         let x = &mut v;
-   |                 ------ borrow of `v` occurs here
-...
-LL |         match v { //[mir]~ ERROR cannot use `v` because it was mutably borrowed
-   |               ^ use of borrowed `v`
-...
-LL |         drop(x);
-   |              - borrow later used here
-
 error[E0503]: cannot use `v[..]` because it was mutably borrowed
   --> $DIR/borrowck-describe-lvalue.rs:158:28
    |
@@ -268,41 +199,18 @@ LL |             &[_, _, .., _, x] => println!("{}", x),
 LL |         drop(x);
    |              - borrow later used here
 
-error[E0503]: cannot use `v` because it was mutably borrowed
-  --> $DIR/borrowck-describe-lvalue.rs:169:15
-   |
-LL |         let x = &mut v;
-   |                 ------ borrow of `v` occurs here
-LL |         match v { //[mir]~ ERROR cannot use `v` because it was mutably borrowed
-   |               ^ use of borrowed `v`
-...
-LL |         drop(x);
-   |              - borrow later used here
-
 error[E0503]: cannot use `v[..]` because it was mutably borrowed
   --> $DIR/borrowck-describe-lvalue.rs:170:15
    |
 LL |         let x = &mut v;
    |                 ------ borrow of `v` occurs here
-LL |         match v { //[mir]~ ERROR cannot use `v` because it was mutably borrowed
+LL |         match v {
 LL |             &[x..] => println!("{:?}", x),
    |               ^ use of borrowed `v`
 ...
 LL |         drop(x);
    |              - borrow later used here
 
-error[E0503]: cannot use `v` because it was mutably borrowed
-  --> $DIR/borrowck-describe-lvalue.rs:175:15
-   |
-LL |         let x = &mut v;
-   |                 ------ borrow of `v` occurs here
-...
-LL |         match v { //[mir]~ ERROR cannot use `v` because it was mutably borrowed
-   |               ^ use of borrowed `v`
-...
-LL |         drop(x);
-   |              - borrow later used here
-
 error[E0503]: cannot use `v[..]` because it was mutably borrowed
   --> $DIR/borrowck-describe-lvalue.rs:176:18
    |
@@ -315,18 +223,6 @@ LL |             &[_, x..] => println!("{:?}", x),
 LL |         drop(x);
    |              - borrow later used here
 
-error[E0503]: cannot use `v` because it was mutably borrowed
-  --> $DIR/borrowck-describe-lvalue.rs:181:15
-   |
-LL |         let x = &mut v;
-   |                 ------ borrow of `v` occurs here
-...
-LL |         match v { //[mir]~ ERROR cannot use `v` because it was mutably borrowed
-   |               ^ use of borrowed `v`
-...
-LL |         drop(x);
-   |              - borrow later used here
-
 error[E0503]: cannot use `v[..]` because it was mutably borrowed
   --> $DIR/borrowck-describe-lvalue.rs:182:15
    |
@@ -339,18 +235,6 @@ LL |             &[x.., _] => println!("{:?}", x),
 LL |         drop(x);
    |              - borrow later used here
 
-error[E0503]: cannot use `v` because it was mutably borrowed
-  --> $DIR/borrowck-describe-lvalue.rs:187:15
-   |
-LL |         let x = &mut v;
-   |                 ------ borrow of `v` occurs here
-...
-LL |         match v { //[mir]~ ERROR cannot use `v` because it was mutably borrowed
-   |               ^ use of borrowed `v`
-...
-LL |         drop(x);
-   |              - borrow later used here
-
 error[E0503]: cannot use `v[..]` because it was mutably borrowed
   --> $DIR/borrowck-describe-lvalue.rs:188:18
    |
@@ -364,22 +248,11 @@ LL |         drop(x);
    |              - borrow later used here
 
 error[E0503]: cannot use `e` because it was mutably borrowed
-  --> $DIR/borrowck-describe-lvalue.rs:201:15
-   |
-LL |         let x = &mut e;
-   |                 ------ borrow of `e` occurs here
-LL |         match e { //[mir]~ ERROR cannot use `e` because it was mutably borrowed
-   |               ^ use of borrowed `e`
-...
-LL |         drop(x);
-   |              - borrow later used here
-
-error[E0503]: cannot use `e` because it was mutably borrowed
   --> $DIR/borrowck-describe-lvalue.rs:202:13
    |
 LL |         let x = &mut e;
    |                 ------ borrow of `e` occurs here
-LL |         match e { //[mir]~ ERROR cannot use `e` because it was mutably borrowed
+LL |         match e {
 LL |             E::A(ref ax) =>
    |             ^^^^^^^^^^^^ use of borrowed `e`
 ...
@@ -391,7 +264,7 @@ error[E0502]: cannot borrow `e.0` as immutable because it is also borrowed as mu
    |
 LL |         let x = &mut e;
    |                 ------ mutable borrow occurs here
-LL |         match e { //[mir]~ ERROR cannot use `e` because it was mutably borrowed
+LL |         match e {
 LL |             E::A(ref ax) =>
    |                  ^^^^^^ immutable borrow occurs here
 ...
@@ -410,41 +283,18 @@ LL |             E::B { x: ref bx } =>
 LL |         drop(x);
    |              - borrow later used here
 
-error[E0503]: cannot use `s` because it was mutably borrowed
-  --> $DIR/borrowck-describe-lvalue.rs:220:15
-   |
-LL |         let x = &mut s;
-   |                 ------ borrow of `s` occurs here
-LL |         match s { //[mir]~ ERROR cannot use `s` because it was mutably borrowed
-   |               ^ use of borrowed `s`
-...
-LL |         drop(x);
-   |              - borrow later used here
-
 error[E0502]: cannot borrow `s.y.0` as immutable because it is also borrowed as mutable
   --> $DIR/borrowck-describe-lvalue.rs:221:22
    |
 LL |         let x = &mut s;
    |                 ------ mutable borrow occurs here
-LL |         match s { //[mir]~ ERROR cannot use `s` because it was mutably borrowed
+LL |         match s {
 LL |             S  { y: (ref y0, _), .. } =>
    |                      ^^^^^^ immutable borrow occurs here
 ...
 LL |         drop(x);
    |              - borrow later used here
 
-error[E0503]: cannot use `s` because it was mutably borrowed
-  --> $DIR/borrowck-describe-lvalue.rs:227:15
-   |
-LL |         let x = &mut s;
-   |                 ------ borrow of `s` occurs here
-...
-LL |         match s { //[mir]~ ERROR cannot use `s` because it was mutably borrowed
-   |               ^ use of borrowed `s`
-...
-LL |         drop(x);
-   |              - borrow later used here
-
 error[E0502]: cannot borrow `s.x.y` as immutable because it is also borrowed as mutable
   --> $DIR/borrowck-describe-lvalue.rs:228:28
    |
@@ -479,23 +329,12 @@ LL |         v[0].y;
 LL |         drop(x);
    |              - borrow later used here
 
-error[E0503]: cannot use `v` because it was mutably borrowed
-  --> $DIR/borrowck-describe-lvalue.rs:282:15
-   |
-LL |         let x = &mut v;
-   |                 ------ borrow of `v` occurs here
-LL |         match v { //[mir]~ ERROR cannot use `v` because it was mutably borrowed
-   |               ^ use of borrowed `v`
-...
-LL |         drop(x);
-   |              - borrow later used here
-
 error[E0502]: cannot borrow `v[..].x` as immutable because it is also borrowed as mutable
   --> $DIR/borrowck-describe-lvalue.rs:283:24
    |
 LL |         let x = &mut v;
    |                 ------ mutable borrow occurs here
-LL |         match v { //[mir]~ ERROR cannot use `v` because it was mutably borrowed
+LL |         match v {
 LL |             &[_, F {x: ref xf, ..}] => println!("{}", xf),
    |                        ^^^^^^ immutable borrow occurs here
 ...
@@ -534,7 +373,7 @@ LL |                 drop(x); //[ast]~ ERROR use of moved value: `x`
    |
    = note: move occurs because `x` has type `std::vec::Vec<i32>`, which does not implement the `Copy` trait
 
-error: aborting due to 46 previous errors
+error: aborting due to 32 previous errors
 
 Some errors occurred: E0382, E0499, E0502, E0503.
 For more information about an error, try `rustc --explain E0382`.
diff --git a/src/test/ui/borrowck/borrowck-describe-lvalue.rs b/src/test/ui/borrowck/borrowck-describe-lvalue.rs
index b821c7cfa34..2ef08e75cfd 100644
--- a/src/test/ui/borrowck/borrowck-describe-lvalue.rs
+++ b/src/test/ui/borrowck/borrowck-describe-lvalue.rs
@@ -74,7 +74,7 @@ fn main() {
     {
         let mut e = Baz::X(2);
         let x = e.x();
-        match e { //[mir]~ ERROR cannot use `e` because it was mutably borrowed
+        match e {
             Baz::X(value) => value
             //[ast]~^ ERROR cannot use `e.0` because it was mutably borrowed
             //[mir]~^^ ERROR cannot use `e.0` because it was mutably borrowed
@@ -117,7 +117,7 @@ fn main() {
     {
         let mut e = Box::new(Baz::X(3));
         let x = e.x();
-        match *e { //[mir]~ ERROR cannot use `*e` because it was mutably borrowed
+        match *e {
             Baz::X(value) => value
             //[ast]~^ ERROR cannot use `e.0` because it was mutably borrowed
             //[mir]~^^ ERROR cannot use `e.0` because it was mutably borrowed
@@ -136,25 +136,25 @@ fn main() {
     {
         let mut v = &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
         let x = &mut v;
-        match v { //[mir]~ ERROR cannot use `v` because it was mutably borrowed
+        match v {
             &[x, _, .., _, _] => println!("{}", x),
                 //[ast]~^ ERROR cannot use `v[..]` because it was mutably borrowed
                 //[mir]~^^ ERROR cannot use `v[..]` because it was mutably borrowed
                             _ => panic!("other case"),
         }
-        match v { //[mir]~ ERROR cannot use `v` because it was mutably borrowed
+        match v {
             &[_, x, .., _, _] => println!("{}", x),
                 //[ast]~^ ERROR cannot use `v[..]` because it was mutably borrowed
                 //[mir]~^^ ERROR cannot use `v[..]` because it was mutably borrowed
                             _ => panic!("other case"),
         }
-        match v { //[mir]~ ERROR cannot use `v` because it was mutably borrowed
+        match v {
             &[_, _, .., x, _] => println!("{}", x),
                 //[ast]~^ ERROR cannot use `v[..]` because it was mutably borrowed
                 //[mir]~^^ ERROR cannot use `v[..]` because it was mutably borrowed
                             _ => panic!("other case"),
         }
-        match v { //[mir]~ ERROR cannot use `v` because it was mutably borrowed
+        match v {
             &[_, _, .., _, x] => println!("{}", x),
                 //[ast]~^ ERROR cannot use `v[..]` because it was mutably borrowed
                 //[mir]~^^ ERROR cannot use `v[..]` because it was mutably borrowed
@@ -166,25 +166,25 @@ fn main() {
     {
         let mut v = &[1, 2, 3, 4, 5];
         let x = &mut v;
-        match v { //[mir]~ ERROR cannot use `v` because it was mutably borrowed
+        match v {
             &[x..] => println!("{:?}", x),
                 //[ast]~^ ERROR cannot use `v[..]` because it was mutably borrowed
                 //[mir]~^^ ERROR cannot use `v[..]` because it was mutably borrowed
             _ => panic!("other case"),
         }
-        match v { //[mir]~ ERROR cannot use `v` because it was mutably borrowed
+        match v {
             &[_, x..] => println!("{:?}", x),
                 //[ast]~^ ERROR cannot use `v[..]` because it was mutably borrowed
                 //[mir]~^^ ERROR cannot use `v[..]` because it was mutably borrowed
             _ => panic!("other case"),
         }
-        match v { //[mir]~ ERROR cannot use `v` because it was mutably borrowed
+        match v {
             &[x.., _] => println!("{:?}", x),
                 //[ast]~^ ERROR cannot use `v[..]` because it was mutably borrowed
                 //[mir]~^^ ERROR cannot use `v[..]` because it was mutably borrowed
             _ => panic!("other case"),
         }
-        match v { //[mir]~ ERROR cannot use `v` because it was mutably borrowed
+        match v {
             &[_, x.., _] => println!("{:?}", x),
                 //[ast]~^ ERROR cannot use `v[..]` because it was mutably borrowed
                 //[mir]~^^ ERROR cannot use `v[..]` because it was mutably borrowed
@@ -198,7 +198,7 @@ fn main() {
 
         let mut e = E::A(3);
         let x = &mut e;
-        match e { //[mir]~ ERROR cannot use `e` because it was mutably borrowed
+        match e {
             E::A(ref ax) =>
                 //[ast]~^ ERROR cannot borrow `e.0` as immutable because `e` is also borrowed as mutable
                 //[mir]~^^ ERROR cannot borrow `e.0` as immutable because it is also borrowed as mutable
@@ -217,14 +217,14 @@ fn main() {
         struct S { x: F, y: (u32, u32), };
         let mut s = S { x: F { x: 1, y: 2}, y: (999, 998) };
         let x = &mut s;
-        match s { //[mir]~ ERROR cannot use `s` because it was mutably borrowed
+        match s {
             S  { y: (ref y0, _), .. } =>
                 //[ast]~^ ERROR cannot borrow `s.y.0` as immutable because `s` is also borrowed as mutable
                 //[mir]~^^ ERROR cannot borrow `s.y.0` as immutable because it is also borrowed as mutable
                 println!("y0: {:?}", y0),
             _ => panic!("other case"),
         }
-        match s { //[mir]~ ERROR cannot use `s` because it was mutably borrowed
+        match s {
             S  { x: F { y: ref x0, .. }, .. } =>
                 //[ast]~^ ERROR cannot borrow `s.x.y` as immutable because `s` is also borrowed as mutable
                 //[mir]~^^ ERROR cannot borrow `s.x.y` as immutable because it is also borrowed as mutable
@@ -279,7 +279,7 @@ fn main() {
         struct F {x: u32, y: u32};
         let mut v = &[F{x: 1, y: 2}, F{x: 3, y: 4}];
         let x = &mut v;
-        match v { //[mir]~ ERROR cannot use `v` because it was mutably borrowed
+        match v {
             &[_, F {x: ref xf, ..}] => println!("{}", xf),
             //[mir]~^ ERROR cannot borrow `v[..].x` as immutable because it is also borrowed as mutable
             // No errors in AST
diff --git a/src/test/ui/borrowck/borrowck-match-already-borrowed.ast.nll.stderr b/src/test/ui/borrowck/borrowck-match-already-borrowed.ast.nll.stderr
index 8dec40520c4..0853018bae9 100644
--- a/src/test/ui/borrowck/borrowck-match-already-borrowed.ast.nll.stderr
+++ b/src/test/ui/borrowck/borrowck-match-already-borrowed.ast.nll.stderr
@@ -1,20 +1,9 @@
 error[E0503]: cannot use `foo` because it was mutably borrowed
-  --> $DIR/borrowck-match-already-borrowed.rs:22:19
-   |
-LL |     let p = &mut foo;
-   |             -------- borrow of `foo` occurs here
-LL |     let _ = match foo { //[mir]~ ERROR [E0503]
-   |                   ^^^ use of borrowed `foo`
-...
-LL |     drop(p);
-   |          - borrow later used here
-
-error[E0503]: cannot use `foo` because it was mutably borrowed
   --> $DIR/borrowck-match-already-borrowed.rs:23:9
    |
 LL |     let p = &mut foo;
    |             -------- borrow of `foo` occurs here
-LL |     let _ = match foo { //[mir]~ ERROR [E0503]
+LL |     let _ = match foo {
 LL |         Foo::B => 1, //[mir]~ ERROR [E0503]
    |         ^^^^^^ use of borrowed `foo`
 ...
@@ -34,22 +23,11 @@ LL |     drop(p);
    |          - borrow later used here
 
 error[E0503]: cannot use `x` because it was mutably borrowed
-  --> $DIR/borrowck-match-already-borrowed.rs:35:19
-   |
-LL |     let r = &mut x;
-   |             ------ borrow of `x` occurs here
-LL |     let _ = match x { //[mir]~ ERROR [E0503]
-   |                   ^ use of borrowed `x`
-...
-LL |     drop(r);
-   |          - borrow later used here
-
-error[E0503]: cannot use `x` because it was mutably borrowed
   --> $DIR/borrowck-match-already-borrowed.rs:36:9
    |
 LL |     let r = &mut x;
    |             ------ borrow of `x` occurs here
-LL |     let _ = match x { //[mir]~ ERROR [E0503]
+LL |     let _ = match x {
 LL |         x => x + 1, //[ast]~ ERROR [E0503]
    |         ^ use of borrowed `x`
 ...
@@ -68,6 +46,6 @@ LL |         y => y + 2, //[ast]~ ERROR [E0503]
 LL |     drop(r);
    |          - borrow later used here
 
-error: aborting due to 6 previous errors
+error: aborting due to 4 previous errors
 
 For more information about this error, try `rustc --explain E0503`.
diff --git a/src/test/ui/borrowck/borrowck-match-already-borrowed.ast.stderr b/src/test/ui/borrowck/borrowck-match-already-borrowed.ast.stderr
index 2e49f90a169..5ba2f26e220 100644
--- a/src/test/ui/borrowck/borrowck-match-already-borrowed.ast.stderr
+++ b/src/test/ui/borrowck/borrowck-match-already-borrowed.ast.stderr
@@ -12,7 +12,7 @@ error[E0503]: cannot use `x` because it was mutably borrowed
    |
 LL |     let r = &mut x;
    |                  - borrow of `x` occurs here
-LL |     let _ = match x { //[mir]~ ERROR [E0503]
+LL |     let _ = match x {
 LL |         x => x + 1, //[ast]~ ERROR [E0503]
    |         ^ use of borrowed `x`
 
diff --git a/src/test/ui/borrowck/borrowck-match-already-borrowed.mir.stderr b/src/test/ui/borrowck/borrowck-match-already-borrowed.mir.stderr
index 8dec40520c4..0853018bae9 100644
--- a/src/test/ui/borrowck/borrowck-match-already-borrowed.mir.stderr
+++ b/src/test/ui/borrowck/borrowck-match-already-borrowed.mir.stderr
@@ -1,20 +1,9 @@
 error[E0503]: cannot use `foo` because it was mutably borrowed
-  --> $DIR/borrowck-match-already-borrowed.rs:22:19
-   |
-LL |     let p = &mut foo;
-   |             -------- borrow of `foo` occurs here
-LL |     let _ = match foo { //[mir]~ ERROR [E0503]
-   |                   ^^^ use of borrowed `foo`
-...
-LL |     drop(p);
-   |          - borrow later used here
-
-error[E0503]: cannot use `foo` because it was mutably borrowed
   --> $DIR/borrowck-match-already-borrowed.rs:23:9
    |
 LL |     let p = &mut foo;
    |             -------- borrow of `foo` occurs here
-LL |     let _ = match foo { //[mir]~ ERROR [E0503]
+LL |     let _ = match foo {
 LL |         Foo::B => 1, //[mir]~ ERROR [E0503]
    |         ^^^^^^ use of borrowed `foo`
 ...
@@ -34,22 +23,11 @@ LL |     drop(p);
    |          - borrow later used here
 
 error[E0503]: cannot use `x` because it was mutably borrowed
-  --> $DIR/borrowck-match-already-borrowed.rs:35:19
-   |
-LL |     let r = &mut x;
-   |             ------ borrow of `x` occurs here
-LL |     let _ = match x { //[mir]~ ERROR [E0503]
-   |                   ^ use of borrowed `x`
-...
-LL |     drop(r);
-   |          - borrow later used here
-
-error[E0503]: cannot use `x` because it was mutably borrowed
   --> $DIR/borrowck-match-already-borrowed.rs:36:9
    |
 LL |     let r = &mut x;
    |             ------ borrow of `x` occurs here
-LL |     let _ = match x { //[mir]~ ERROR [E0503]
+LL |     let _ = match x {
 LL |         x => x + 1, //[ast]~ ERROR [E0503]
    |         ^ use of borrowed `x`
 ...
@@ -68,6 +46,6 @@ LL |         y => y + 2, //[ast]~ ERROR [E0503]
 LL |     drop(r);
    |          - borrow later used here
 
-error: aborting due to 6 previous errors
+error: aborting due to 4 previous errors
 
 For more information about this error, try `rustc --explain E0503`.
diff --git a/src/test/ui/borrowck/borrowck-match-already-borrowed.rs b/src/test/ui/borrowck/borrowck-match-already-borrowed.rs
index c2136e62a7b..cabce08429e 100644
--- a/src/test/ui/borrowck/borrowck-match-already-borrowed.rs
+++ b/src/test/ui/borrowck/borrowck-match-already-borrowed.rs
@@ -19,7 +19,7 @@ enum Foo {
 fn match_enum() {
     let mut foo = Foo::B;
     let p = &mut foo;
-    let _ = match foo { //[mir]~ ERROR [E0503]
+    let _ = match foo {
         Foo::B => 1, //[mir]~ ERROR [E0503]
         _ => 2,
         Foo::A(x) => x //[ast]~ ERROR [E0503]
@@ -32,7 +32,7 @@ fn match_enum() {
 fn main() {
     let mut x = 1;
     let r = &mut x;
-    let _ = match x { //[mir]~ ERROR [E0503]
+    let _ = match x {
         x => x + 1, //[ast]~ ERROR [E0503]
                     //[mir]~^ ERROR [E0503]
         y => y + 2, //[ast]~ ERROR [E0503]
diff --git a/src/test/ui/consts/const-eval/conditional_array_execution.nll.stderr b/src/test/ui/consts/const-eval/conditional_array_execution.nll.stderr
deleted file mode 100644
index 86287e12997..00000000000
--- a/src/test/ui/consts/const-eval/conditional_array_execution.nll.stderr
+++ /dev/null
@@ -1,47 +0,0 @@
-warning: this constant cannot be used
-  --> $DIR/conditional_array_execution.rs:15:1
-   |
-LL | const FOO: u32 = [X - Y, Y - X][(X < Y) as usize];
-   | ^^^^^^^^^^^^^^^^^^-----^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |                   |
-   |                   attempt to subtract with overflow
-   |
-note: lint level defined here
-  --> $DIR/conditional_array_execution.rs:11:9
-   |
-LL | #![warn(const_err)]
-   |         ^^^^^^^^^
-
-error[E0080]: referenced constant has errors
-  --> $DIR/conditional_array_execution.rs:19:14
-   |
-LL | const FOO: u32 = [X - Y, Y - X][(X < Y) as usize];
-   |                   ----- attempt to subtract with overflow
-...
-LL |     println!("{}", FOO);
-   |              ^^^^
-
-error[E0080]: erroneous constant used
-  --> $DIR/conditional_array_execution.rs:19:14
-   |
-LL |     println!("{}", FOO);
-   |              ^^^^  --- referenced constant has errors
-
-error[E0080]: referenced constant has errors
-  --> $DIR/conditional_array_execution.rs:19:20
-   |
-LL | const FOO: u32 = [X - Y, Y - X][(X < Y) as usize];
-   |                   ----- attempt to subtract with overflow
-...
-LL |     println!("{}", FOO);
-   |                    ^^^
-
-error[E0080]: erroneous constant used
-  --> $DIR/conditional_array_execution.rs:19:20
-   |
-LL |     println!("{}", FOO);
-   |                    ^^^ referenced constant has errors
-
-error: aborting due to 4 previous errors
-
-For more information about this error, try `rustc --explain E0080`.
diff --git a/src/test/ui/consts/const-eval/issue-43197.nll.stderr b/src/test/ui/consts/const-eval/issue-43197.nll.stderr
deleted file mode 100644
index 732fe459834..00000000000
--- a/src/test/ui/consts/const-eval/issue-43197.nll.stderr
+++ /dev/null
@@ -1,70 +0,0 @@
-warning: this constant cannot be used
-  --> $DIR/issue-43197.rs:20:5
-   |
-LL |     const X: u32 = 0-1;
-   |     ^^^^^^^^^^^^^^^---^
-   |                    |
-   |                    attempt to subtract with overflow
-   |
-note: lint level defined here
-  --> $DIR/issue-43197.rs:11:9
-   |
-LL | #![warn(const_err)]
-   |         ^^^^^^^^^
-
-warning: this constant cannot be used
-  --> $DIR/issue-43197.rs:22:5
-   |
-LL |     const Y: u32 = foo(0-1);
-   |     ^^^^^^^^^^^^^^^^^^^---^^
-   |                        |
-   |                        attempt to subtract with overflow
-
-error[E0080]: referenced constant has errors
-  --> $DIR/issue-43197.rs:24:14
-   |
-LL |     const X: u32 = 0-1;
-   |                    --- attempt to subtract with overflow
-...
-LL |     println!("{} {}", X, Y);
-   |              ^^^^^^^
-
-error[E0080]: erroneous constant used
-  --> $DIR/issue-43197.rs:24:14
-   |
-LL |     println!("{} {}", X, Y);
-   |              ^^^^^^^  - referenced constant has errors
-
-error[E0080]: referenced constant has errors
-  --> $DIR/issue-43197.rs:24:26
-   |
-LL |     const Y: u32 = foo(0-1);
-   |                        --- attempt to subtract with overflow
-LL |     //~^ WARN this constant cannot be used
-LL |     println!("{} {}", X, Y);
-   |                          ^
-
-error[E0080]: erroneous constant used
-  --> $DIR/issue-43197.rs:24:26
-   |
-LL |     println!("{} {}", X, Y);
-   |                          ^ referenced constant has errors
-
-error[E0080]: referenced constant has errors
-  --> $DIR/issue-43197.rs:24:23
-   |
-LL |     const X: u32 = 0-1;
-   |                    --- attempt to subtract with overflow
-...
-LL |     println!("{} {}", X, Y);
-   |                       ^
-
-error[E0080]: erroneous constant used
-  --> $DIR/issue-43197.rs:24:23
-   |
-LL |     println!("{} {}", X, Y);
-   |                       ^ referenced constant has errors
-
-error: aborting due to 6 previous errors
-
-For more information about this error, try `rustc --explain E0080`.
diff --git a/src/test/ui/consts/const-eval/issue-44578.nll.stderr b/src/test/ui/consts/const-eval/issue-44578.nll.stderr
deleted file mode 100644
index da040747991..00000000000
--- a/src/test/ui/consts/const-eval/issue-44578.nll.stderr
+++ /dev/null
@@ -1,33 +0,0 @@
-error[E0080]: referenced constant has errors
-  --> $DIR/issue-44578.rs:35:14
-   |
-LL |     const AMT: usize = [A::AMT][(A::AMT > B::AMT) as usize];
-   |                        ------------------------------------ index out of bounds: the len is 1 but the index is 1
-...
-LL |     println!("{}", <Bar<u16, u8> as Foo>::AMT);
-   |              ^^^^
-
-error[E0080]: erroneous constant used
-  --> $DIR/issue-44578.rs:35:14
-   |
-LL |     println!("{}", <Bar<u16, u8> as Foo>::AMT);
-   |              ^^^^  -------------------------- referenced constant has errors
-
-error[E0080]: referenced constant has errors
-  --> $DIR/issue-44578.rs:35:20
-   |
-LL |     const AMT: usize = [A::AMT][(A::AMT > B::AMT) as usize];
-   |                        ------------------------------------ index out of bounds: the len is 1 but the index is 1
-...
-LL |     println!("{}", <Bar<u16, u8> as Foo>::AMT);
-   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error[E0080]: erroneous constant used
-  --> $DIR/issue-44578.rs:35:20
-   |
-LL |     println!("{}", <Bar<u16, u8> as Foo>::AMT);
-   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^ referenced constant has errors
-
-error: aborting due to 4 previous errors
-
-For more information about this error, try `rustc --explain E0080`.
diff --git a/src/test/ui/issues/issue-27282-move-match-input-into-guard.rs b/src/test/ui/issues/issue-27282-move-match-input-into-guard.rs
index b3be36e41e6..909c369354b 100644
--- a/src/test/ui/issues/issue-27282-move-match-input-into-guard.rs
+++ b/src/test/ui/issues/issue-27282-move-match-input-into-guard.rs
@@ -24,7 +24,6 @@ fn main() {
     match b {
         &mut false => {},
         _ if { (|| { let bar = b; *bar = false; })();
-                     //~^ ERROR cannot move out of `b` because it is borrowed [E0505]
                      false } => { },
         &mut true => { println!("You might think we should get here"); },
         //~^ ERROR use of moved value: `*b` [E0382]
diff --git a/src/test/ui/issues/issue-27282-move-match-input-into-guard.stderr b/src/test/ui/issues/issue-27282-move-match-input-into-guard.stderr
index 91c51bcd058..0b783e37615 100644
--- a/src/test/ui/issues/issue-27282-move-match-input-into-guard.stderr
+++ b/src/test/ui/issues/issue-27282-move-match-input-into-guard.stderr
@@ -1,29 +1,14 @@
-error[E0505]: cannot move out of `b` because it is borrowed
-  --> $DIR/issue-27282-move-match-input-into-guard.rs:26:17
-   |
-LL |     match b {
-   |           - borrow of `b` occurs here
-LL |         &mut false => {},
-LL |         _ if { (|| { let bar = b; *bar = false; })();
-   |                 ^^             - move occurs due to use in closure
-   |                 |
-   |                 move out of `b` occurs here
-...
-LL |         &mut true => { println!("You might think we should get here"); },
-   |         --------- borrow later used here
-
 error[E0382]: use of moved value: `*b`
-  --> $DIR/issue-27282-move-match-input-into-guard.rs:29:14
+  --> $DIR/issue-27282-move-match-input-into-guard.rs:28:14
    |
 LL |         _ if { (|| { let bar = b; *bar = false; })();
    |                 --             - variable moved due to use in closure
    |                 |
    |                 value moved into closure here
-...
+LL |                      false } => { },
 LL |         &mut true => { println!("You might think we should get here"); },
    |              ^^^^ value used here after move
 
-error: aborting due to 2 previous errors
+error: aborting due to previous error
 
-Some errors occurred: E0382, E0505.
-For more information about an error, try `rustc --explain E0382`.
+For more information about this error, try `rustc --explain E0382`.
diff --git a/src/test/ui/issues/issue-27282-mutate-before-diverging-arm-1.rs b/src/test/ui/issues/issue-27282-mutate-before-diverging-arm-1.rs
index b575f4ebce6..2ebfb995d8c 100644
--- a/src/test/ui/issues/issue-27282-mutate-before-diverging-arm-1.rs
+++ b/src/test/ui/issues/issue-27282-mutate-before-diverging-arm-1.rs
@@ -31,7 +31,7 @@ fn main() {
         &mut Some(&_) if {
             // ForceFnOnce needed to exploit #27282
             (|| { *x = None; drop(force_fn_once); })();
-            //~^ ERROR closure requires unique access to `x` but it is already borrowed [E0500]
+            //~^ ERROR cannot mutably borrow `x` in match guard [E0510]
             false
         } => {}
         &mut Some(&a) if { // this binds to garbage if we've corrupted discriminant
diff --git a/src/test/ui/issues/issue-27282-mutate-before-diverging-arm-1.stderr b/src/test/ui/issues/issue-27282-mutate-before-diverging-arm-1.stderr
index a9d9651fb2a..2ecbb25fd3e 100644
--- a/src/test/ui/issues/issue-27282-mutate-before-diverging-arm-1.stderr
+++ b/src/test/ui/issues/issue-27282-mutate-before-diverging-arm-1.stderr
@@ -1,17 +1,14 @@
-error[E0500]: closure requires unique access to `x` but it is already borrowed
+error[E0510]: cannot mutably borrow `x` in match guard
   --> $DIR/issue-27282-mutate-before-diverging-arm-1.rs:33:14
    |
 LL |     match x {
-   |           - borrow occurs here
+   |           - value is immutable in match guard
 ...
 LL |             (|| { *x = None; drop(force_fn_once); })();
-   |              ^^    - second borrow occurs due to use of `x` in closure
+   |              ^^    - borrow occurs due to use of `x` in closure
    |              |
-   |              closure construction occurs here
-...
-LL |         &mut Some(&a) if { // this binds to garbage if we've corrupted discriminant
-   |         ------------- borrow later used here
+   |              cannot mutably borrow
 
 error: aborting due to previous error
 
-For more information about this error, try `rustc --explain E0500`.
+For more information about this error, try `rustc --explain E0510`.
diff --git a/src/test/ui/issues/issue-27282-mutate-before-diverging-arm-2.rs b/src/test/ui/issues/issue-27282-mutate-before-diverging-arm-2.rs
index 866fed13685..6b50973e04d 100644
--- a/src/test/ui/issues/issue-27282-mutate-before-diverging-arm-2.rs
+++ b/src/test/ui/issues/issue-27282-mutate-before-diverging-arm-2.rs
@@ -36,7 +36,7 @@ fn main() {
             if {
                 // ForceFnOnce needed to exploit #27282
                 (|| { *x = None; drop(force_fn_once); })();
-                //~^ ERROR closure requires unique access to `x` but it is already borrowed [E0500]
+                //~^ ERROR cannot mutably borrow `x` in match guard [E0510]
                 false
             } => {}
 
diff --git a/src/test/ui/issues/issue-27282-mutate-before-diverging-arm-2.stderr b/src/test/ui/issues/issue-27282-mutate-before-diverging-arm-2.stderr
index 582d0fd678c..6feef95300e 100644
--- a/src/test/ui/issues/issue-27282-mutate-before-diverging-arm-2.stderr
+++ b/src/test/ui/issues/issue-27282-mutate-before-diverging-arm-2.stderr
@@ -1,17 +1,14 @@
-error[E0500]: closure requires unique access to `x` but it is already borrowed
+error[E0510]: cannot mutably borrow `x` in match guard
   --> $DIR/issue-27282-mutate-before-diverging-arm-2.rs:38:18
    |
 LL |     match x {
-   |           - borrow occurs here
+   |           - value is immutable in match guard
 ...
 LL |                 (|| { *x = None; drop(force_fn_once); })();
-   |                  ^^    - second borrow occurs due to use of `x` in closure
+   |                  ^^    - borrow occurs due to use of `x` in closure
    |                  |
-   |                  closure construction occurs here
-...
-LL |         &mut Some(&2)
-   |         ------------- borrow later used here
+   |                  cannot mutably borrow
 
 error: aborting due to previous error
 
-For more information about this error, try `rustc --explain E0500`.
+For more information about this error, try `rustc --explain E0510`.
diff --git a/src/test/ui/issues/issue-27282-mutate-before-diverging-arm-3.rs b/src/test/ui/issues/issue-27282-mutate-before-diverging-arm-3.rs
new file mode 100644
index 00000000000..4cf5bcd6b4f
--- /dev/null
+++ b/src/test/ui/issues/issue-27282-mutate-before-diverging-arm-3.rs
@@ -0,0 +1,30 @@
+// This is testing an attempt to corrupt the discriminant of the match
+// arm in a guard, followed by an attempt to continue matching on that
+// corrupted discriminant in the remaining match arms.
+//
+// Basically this is testing that our new NLL feature of emitting a
+// fake read on each match arm is catching cases like this.
+//
+// This case is interesting because a borrow of **x is untracked, because **x is
+// immutable. However, for matches we care that **x refers to the same value
+// until we have chosen a match arm.
+#![feature(nll)]
+struct ForceFnOnce;
+fn main() {
+    let mut x = &mut &Some(&2);
+    let force_fn_once = ForceFnOnce;
+    match **x {
+        None => panic!("unreachable"),
+        Some(&_) if {
+            // ForceFnOnce needed to exploit #27282
+            (|| { *x = &None; drop(force_fn_once); })();
+            //~^ ERROR cannot mutably borrow `x` in match guard [E0510]
+            false
+        } => {}
+        Some(&a) if { // this binds to garbage if we've corrupted discriminant
+            println!("{}", a);
+            panic!()
+        } => {}
+        _ => panic!("unreachable"),
+    }
+}
diff --git a/src/test/ui/issues/issue-27282-mutate-before-diverging-arm-3.stderr b/src/test/ui/issues/issue-27282-mutate-before-diverging-arm-3.stderr
new file mode 100644
index 00000000000..f46a42d7508
--- /dev/null
+++ b/src/test/ui/issues/issue-27282-mutate-before-diverging-arm-3.stderr
@@ -0,0 +1,14 @@
+error[E0510]: cannot mutably borrow `x` in match guard
+  --> $DIR/issue-27282-mutate-before-diverging-arm-3.rs:20:14
+   |
+LL |     match **x {
+   |           --- value is immutable in match guard
+...
+LL |             (|| { *x = &None; drop(force_fn_once); })();
+   |              ^^    - borrow occurs due to use of `x` in closure
+   |              |
+   |              cannot mutably borrow
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0510`.
diff --git a/src/test/ui/nll/borrowed-match-issue-45045.rs b/src/test/ui/nll/borrowed-match-issue-45045.rs
index 4b95bbd5a05..8688bfa86dc 100644
--- a/src/test/ui/nll/borrowed-match-issue-45045.rs
+++ b/src/test/ui/nll/borrowed-match-issue-45045.rs
@@ -21,7 +21,7 @@ fn main() {
     let mut e = Xyz::A;
     let f = &mut e;
     let g = f;
-    match e { //~ cannot use `e` because it was mutably borrowed [E0503]
+    match e {
         Xyz::A => println!("a"),
         //~^ cannot use `e` because it was mutably borrowed [E0503]
         Xyz::B => println!("b"),
diff --git a/src/test/ui/nll/borrowed-match-issue-45045.stderr b/src/test/ui/nll/borrowed-match-issue-45045.stderr
index acbba9ee187..ded773165c6 100644
--- a/src/test/ui/nll/borrowed-match-issue-45045.stderr
+++ b/src/test/ui/nll/borrowed-match-issue-45045.stderr
@@ -1,16 +1,4 @@
 error[E0503]: cannot use `e` because it was mutably borrowed
-  --> $DIR/borrowed-match-issue-45045.rs:24:11
-   |
-LL |     let f = &mut e;
-   |             ------ borrow of `e` occurs here
-LL |     let g = f;
-LL |     match e { //~ cannot use `e` because it was mutably borrowed [E0503]
-   |           ^ use of borrowed `e`
-...
-LL |     *g = Xyz::B;
-   |     ----------- borrow later used here
-
-error[E0503]: cannot use `e` because it was mutably borrowed
   --> $DIR/borrowed-match-issue-45045.rs:25:9
    |
 LL |     let f = &mut e;
@@ -22,6 +10,6 @@ LL |         Xyz::A => println!("a"),
 LL |     *g = Xyz::B;
    |     ----------- borrow later used here
 
-error: aborting due to 2 previous errors
+error: aborting due to previous error
 
 For more information about this error, try `rustc --explain E0503`.
diff --git a/src/test/ui/nll/match-guards-partially-borrow.rs b/src/test/ui/nll/match-guards-partially-borrow.rs
new file mode 100644
index 00000000000..49846f620f0
--- /dev/null
+++ b/src/test/ui/nll/match-guards-partially-borrow.rs
@@ -0,0 +1,164 @@
+// Test that a (partially) mutably borrowed place can be matched on, so long as
+// we don't have to read any values that are mutably borrowed to determine
+// which arm to take.
+//
+// Test that we don't allow mutating the value being matched on in a way that
+// changes which patterns it matches, until we have chosen an arm.
+
+// compile-flags: -Zdisable-ast-check-for-mutation-in-guard
+
+#![feature(nll)]
+
+fn ok_mutation_in_guard(mut q: i32) {
+    match q {
+        // OK, mutation doesn't change which patterns g matches
+        _ if { q = 1; false } => (),
+        _ => (),
+    }
+}
+
+fn ok_indirect_mutation_in_guard(mut p: &bool) {
+    match *p {
+        // OK, mutation doesn't change which patterns s matches
+        _ if {
+            p = &true;
+            false
+        } => (),
+        _ => (),
+    }
+}
+
+fn mutation_invalidates_pattern_in_guard(mut q: bool) {
+    match q {
+        // s doesn't match the pattern with the guard by the end of the guard.
+        false if {
+            q = true; //~ ERROR
+            true
+        } => (),
+        _ => (),
+    }
+}
+
+fn mutation_invalidates_previous_pattern_in_guard(mut r: bool) {
+    match r {
+        // s matches a previous pattern by the end of the guard.
+        true => (),
+        _ if {
+            r = true; //~ ERROR
+            true
+        } => (),
+        _ => (),
+    }
+}
+
+fn match_on_borrowed_early_end(mut s: bool) {
+    let h = &mut s;
+    match s { //~ ERROR
+        // s changes value between the start of the match and when its value is checked.
+        _ if {
+            *h = !*h;
+            false
+        } => (),
+        true => (),
+        false => (),
+    }
+}
+
+fn bad_mutation_in_guard(mut t: bool) {
+    match t {
+        true => (),
+        false if {
+            t = true; //~ ERROR
+            false
+        } => (),
+        false => (),
+    }
+}
+
+fn bad_mutation_in_guard2(mut u: bool) {
+    match u {
+        // Guard changes the value bound in the last pattern.
+        _ => (),
+        _ if {
+            u = true; //~ ERROR
+            false
+        } => (),
+        x => (),
+    }
+}
+
+pub fn bad_mutation_in_guard3(mut x: Option<Option<&i32>>) {
+    // Check that nested patterns are checked.
+    match x {
+        None => (),
+        Some(None) => (),
+        _ if {
+            match x {
+                Some(ref mut r) => *r = None, //~ ERROR
+                _ => return,
+            };
+            false
+        } => (),
+        Some(Some(r)) => println!("{}", r),
+    }
+}
+
+
+fn bad_mutation_in_guard4(mut w: (&mut bool,)) {
+    match w {
+        // Guard changes the value bound in the last pattern.
+        _ => (),
+        _ if {
+            *w.0 = true; //~ ERROR
+            false
+        } => (),
+        x => (),
+    }
+}
+
+fn bad_indirect_mutation_in_guard(mut y: &bool) {
+    match *y {
+        true => (),
+        false if {
+            y = &true; //~ ERROR
+            false
+        } => (),
+        false => (),
+    }
+}
+
+fn bad_indirect_mutation_in_guard2(mut z: &bool) {
+    match z {
+        &true => (),
+        &false if {
+            z = &true; //~ ERROR
+            false
+        } => (),
+        &false => (),
+    }
+}
+
+fn bad_indirect_mutation_in_guard3(mut a: &bool) {
+    // Same as bad_indirect_mutation_in_guard2, but using match ergonomics
+    match a {
+        true => (),
+        false if {
+            a = &true; //~ ERROR
+            false
+        } => (),
+        false => (),
+    }
+}
+
+fn bad_indirect_mutation_in_guard4(mut b: &bool) {
+    match b {
+        &_ => (),
+        &_ if {
+            b = &true; //~ ERROR
+            false
+        } => (),
+        &b => (),
+    }
+}
+
+fn main() {}
diff --git a/src/test/ui/nll/match-guards-partially-borrow.stderr b/src/test/ui/nll/match-guards-partially-borrow.stderr
new file mode 100644
index 00000000000..2cbfeb886b5
--- /dev/null
+++ b/src/test/ui/nll/match-guards-partially-borrow.stderr
@@ -0,0 +1,132 @@
+error[E0510]: cannot assign `q` in match guard
+  --> $DIR/match-guards-partially-borrow.rs:35:13
+   |
+LL |     match q {
+   |           - value is immutable in match guard
+...
+LL |             q = true; //~ ERROR
+   |             ^^^^^^^^ cannot assign
+...
+LL |         _ => (),
+   |         - borrow later used here
+
+error[E0510]: cannot assign `r` in match guard
+  --> $DIR/match-guards-partially-borrow.rs:47:13
+   |
+LL |     match r {
+   |           - value is immutable in match guard
+...
+LL |             r = true; //~ ERROR
+   |             ^^^^^^^^ cannot assign
+...
+LL |         _ => (),
+   |         - borrow later used here
+
+error[E0503]: cannot use `s` because it was mutably borrowed
+  --> $DIR/match-guards-partially-borrow.rs:56:11
+   |
+LL |     let h = &mut s;
+   |             ------ borrow of `s` occurs here
+LL |     match s { //~ ERROR
+   |           ^ use of borrowed `s`
+...
+LL |             *h = !*h;
+   |                   -- borrow later used here
+
+error[E0510]: cannot assign `t` in match guard
+  --> $DIR/match-guards-partially-borrow.rs:71:13
+   |
+LL |     match t {
+   |           - value is immutable in match guard
+...
+LL |             t = true; //~ ERROR
+   |             ^^^^^^^^ cannot assign
+...
+LL |         false => (),
+   |         ----- borrow later used here
+
+error[E0506]: cannot assign to `u` because it is borrowed
+  --> $DIR/match-guards-partially-borrow.rs:83:13
+   |
+LL |     match u {
+   |           - borrow of `u` occurs here
+...
+LL |             u = true; //~ ERROR
+   |             ^^^^^^^^ assignment to borrowed `u` occurs here
+...
+LL |         x => (),
+   |         - borrow later used here
+
+error[E0510]: cannot mutably borrow `x.0` in match guard
+  --> $DIR/match-guards-partially-borrow.rs:97:22
+   |
+LL |     match x {
+   |           - value is immutable in match guard
+...
+LL |                 Some(ref mut r) => *r = None, //~ ERROR
+   |                      ^^^^^^^^^ cannot mutably borrow
+
+error[E0506]: cannot assign to `*w.0` because it is borrowed
+  --> $DIR/match-guards-partially-borrow.rs:112:13
+   |
+LL |     match w {
+   |           - borrow of `*w.0` occurs here
+...
+LL |             *w.0 = true; //~ ERROR
+   |             ^^^^^^^^^^^ assignment to borrowed `*w.0` occurs here
+...
+LL |         x => (),
+   |         - borrow later used here
+
+error[E0510]: cannot assign `y` in match guard
+  --> $DIR/match-guards-partially-borrow.rs:123:13
+   |
+LL |     match *y {
+   |           -- value is immutable in match guard
+...
+LL |             y = &true; //~ ERROR
+   |             ^^^^^^^^^ cannot assign
+...
+LL |         false => (),
+   |         ----- borrow later used here
+
+error[E0510]: cannot assign `z` in match guard
+  --> $DIR/match-guards-partially-borrow.rs:134:13
+   |
+LL |     match z {
+   |           - value is immutable in match guard
+...
+LL |             z = &true; //~ ERROR
+   |             ^^^^^^^^^ cannot assign
+...
+LL |         &false => (),
+   |         ------ borrow later used here
+
+error[E0510]: cannot assign `a` in match guard
+  --> $DIR/match-guards-partially-borrow.rs:146:13
+   |
+LL |     match a {
+   |           - value is immutable in match guard
+...
+LL |             a = &true; //~ ERROR
+   |             ^^^^^^^^^ cannot assign
+...
+LL |         false => (),
+   |         ----- borrow later used here
+
+error[E0510]: cannot assign `b` in match guard
+  --> $DIR/match-guards-partially-borrow.rs:157:13
+   |
+LL |     match b {
+   |           - value is immutable in match guard
+...
+LL |             b = &true; //~ ERROR
+   |             ^^^^^^^^^ cannot assign
+...
+LL |         &b => (),
+   |         -- borrow later used here
+
+error: aborting due to 11 previous errors
+
+Some errors occurred: E0503, E0506, E0510.
+For more information about an error, try `rustc --explain E0503`.
diff --git a/src/test/ui/nll/match-on-borrowed.rs b/src/test/ui/nll/match-on-borrowed.rs
new file mode 100644
index 00000000000..6a8ce03e8fd
--- /dev/null
+++ b/src/test/ui/nll/match-on-borrowed.rs
@@ -0,0 +1,95 @@
+// Test that a (partially) mutably borrowed place can be matched on, so long as
+// we don't have to read any values that are mutably borrowed to determine
+// which arm to take.
+//
+// Test that we don't allow mutating the value being matched on in a way that
+// changes which patterns it matches, until we have chosen an arm.
+
+#![feature(nll)]
+
+struct A(i32, i32);
+
+fn struct_example(mut a: A) {
+    let x = &mut a.0;
+    match a { // OK, no access of borrowed data
+        _ if false => (),
+        A(_, r) => (),
+    }
+    x;
+}
+
+fn indirect_struct_example(mut b: &mut A) {
+    let x = &mut b.0;
+    match *b { // OK, no access of borrowed data
+        _ if false => (),
+        A(_, r) => (),
+    }
+    x;
+}
+
+fn underscore_example(mut c: i32) {
+    let r = &mut c;
+    match c { // OK, no access of borrowed data (or any data at all)
+        _ if false => (),
+        _ => (),
+    }
+    r;
+}
+
+enum E {
+    V(i32, i32),
+    W,
+}
+
+fn enum_example(mut e: E) {
+    let x = match e {
+        E::V(ref mut x, _) => x,
+        E::W => panic!(),
+    };
+    match e { // OK, no access of borrowed data
+        _ if false => (),
+        E::V(_, r) => (),
+        E::W => (),
+    }
+    x;
+}
+
+fn indirect_enum_example(mut f: &mut E) {
+    let x = match *f {
+        E::V(ref mut x, _) => x,
+        E::W => panic!(),
+    };
+    match f { // OK, no access of borrowed data
+        _ if false => (),
+        E::V(_, r) => (),
+        E::W => (),
+    }
+    x;
+}
+
+fn match_on_muatbly_borrowed_ref(mut p: &bool) {
+    let r = &mut p;
+    match *p { // OK, no access at all
+        _ if false => (),
+        _ => (),
+    }
+    r;
+}
+
+fn match_on_borrowed(mut t: bool) {
+    let x = &mut t;
+    match t {
+        true => (), //~ ERROR
+        false => (),
+    }
+    x;
+}
+
+enum Never {}
+
+fn never_init() {
+    let n: Never;
+    match n {} //~ ERROR
+}
+
+fn main() {}
diff --git a/src/test/ui/nll/match-on-borrowed.stderr b/src/test/ui/nll/match-on-borrowed.stderr
new file mode 100644
index 00000000000..cdff29d44b8
--- /dev/null
+++ b/src/test/ui/nll/match-on-borrowed.stderr
@@ -0,0 +1,22 @@
+error[E0503]: cannot use `t` because it was mutably borrowed
+  --> $DIR/match-on-borrowed.rs:82:9
+   |
+LL |     let x = &mut t;
+   |             ------ borrow of `t` occurs here
+LL |     match t {
+LL |         true => (), //~ ERROR
+   |         ^^^^ use of borrowed `t`
+...
+LL |     x;
+   |     - borrow later used here
+
+error[E0381]: use of possibly uninitialized variable: `n`
+  --> $DIR/match-on-borrowed.rs:92:11
+   |
+LL |     match n {} //~ ERROR
+   |           ^ use of possibly uninitialized `n`
+
+error: aborting due to 2 previous errors
+
+Some errors occurred: E0381, E0503.
+For more information about an error, try `rustc --explain E0381`.