about summary refs log tree commit diff
diff options
context:
space:
mode:
authorJules Bertholet <julesbertholet@quoi.xyz>2024-04-30 16:14:20 -0400
committerJules Bertholet <julesbertholet@quoi.xyz>2024-05-10 13:47:34 -0400
commit4f76f1069af505ce72535bda8d10e44dd81e65a5 (patch)
tree377a68ca51620c465e5351439ce81e0b94cc9691
parent66f877007de6d575357ce8a0a85743f6cce3c06d (diff)
downloadrust-4f76f1069af505ce72535bda8d10e44dd81e65a5.tar.gz
rust-4f76f1069af505ce72535bda8d10e44dd81e65a5.zip
Match ergonomics 2024: let `&` patterns eat `&mut`
-rw-r--r--compiler/rustc_hir_typeck/src/pat.rs184
-rw-r--r--tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024.rs6
-rw-r--r--tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail.rs16
-rw-r--r--tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail.stderr68
-rw-r--r--tests/ui/match/ref_pat_everywhere-fail.rs (renamed from tests/ui/match/ref_pat_everywhere-mutability-mismatch.rs)6
-rw-r--r--tests/ui/match/ref_pat_everywhere-fail.stderr38
-rw-r--r--tests/ui/match/ref_pat_everywhere-mutability-mismatch.stderr44
-rw-r--r--tests/ui/match/ref_pat_everywhere.rs6
8 files changed, 247 insertions, 121 deletions
diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs
index fe2deee378e..99653814d6f 100644
--- a/compiler/rustc_hir_typeck/src/pat.rs
+++ b/compiler/rustc_hir_typeck/src/pat.rs
@@ -78,7 +78,7 @@ struct TopInfo<'tcx> {
 #[derive(Copy, Clone)]
 struct PatInfo<'tcx, 'a> {
     binding_mode: ByRef,
-    max_ref_mutbl: Mutability,
+    max_ref_mutbl: MutblCap,
     top_info: TopInfo<'tcx>,
     decl_origin: Option<DeclOrigin<'a>>,
 
@@ -124,6 +124,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
 }
 
 /// Mode for adjusting the expected type and binding mode.
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
 enum AdjustMode {
     /// Peel off all immediate reference types.
     Peel,
@@ -135,11 +136,44 @@ enum AdjustMode {
     /// and if the old biding mode was by-reference
     /// with mutability matching the pattern,
     /// mark the pattern as having consumed this reference.
-    ResetAndConsumeRef(Mutability),
+    ///
+    /// `Span` is that of the inside of the reference pattern
+    ResetAndConsumeRef(Mutability, Span),
     /// Pass on the input binding mode and expected type.
     Pass,
 }
 
+/// `ref mut` patterns (explicit or match-ergonomics)
+/// are not allowed behind an `&` reference.
+///
+/// This includes explicit `ref mut` behind `&` patterns
+/// that match against `&mut` references,
+/// where the code would have compiled
+/// had the pattern been written as `&mut`.
+/// However, the borrow checker will not catch
+/// this last case, so we need to throw an error ourselves.
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+enum MutblCap {
+    /// Mutability restricted to immutable;
+    /// contained span, if present, should be shown in diagnostics as the reason.
+    Not(Option<Span>),
+    /// No restriction on mutability
+    Mut,
+}
+
+impl MutblCap {
+    fn cap_mutbl_to_not(self, span: Option<Span>) -> Self {
+        if self == MutblCap::Mut { MutblCap::Not(span) } else { self }
+    }
+
+    fn as_mutbl(self) -> Mutability {
+        match self {
+            MutblCap::Not(_) => Mutability::Not,
+            MutblCap::Mut => Mutability::Mut,
+        }
+    }
+}
+
 impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     /// Type check the given top level pattern against the `expected` type.
     ///
@@ -160,7 +194,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         let info = TopInfo { expected, origin_expr, span };
         let pat_info = PatInfo {
             binding_mode: ByRef::No,
-            max_ref_mutbl: Mutability::Mut,
+            max_ref_mutbl: MutblCap::Mut,
             top_info: info,
             decl_origin,
             current_depth: 0,
@@ -201,8 +235,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             PatKind::Never => expected,
             PatKind::Lit(lt) => self.check_pat_lit(pat.span, lt, expected, ti),
             PatKind::Range(lhs, rhs, _) => self.check_pat_range(pat.span, lhs, rhs, expected, ti),
-            PatKind::Binding(ba, var_id, _, sub) => {
-                self.check_pat_ident(pat, ba, var_id, sub, expected, pat_info)
+            PatKind::Binding(ba, var_id, ident, sub) => {
+                self.check_pat_ident(pat, ba, var_id, ident, sub, expected, pat_info)
             }
             PatKind::TupleStruct(ref qpath, subpats, ddpos) => {
                 self.check_pat_tuple_struct(pat, qpath, subpats, ddpos, expected, pat_info)
@@ -294,20 +328,29 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         expected: Ty<'tcx>,
         def_br: ByRef,
         adjust_mode: AdjustMode,
-        max_ref_mutbl: Mutability,
-    ) -> (Ty<'tcx>, ByRef, Mutability, bool) {
-        if let ByRef::Yes(mutbl) = def_br {
-            debug_assert!(mutbl <= max_ref_mutbl);
+        max_ref_mutbl: MutblCap,
+    ) -> (Ty<'tcx>, ByRef, MutblCap, bool) {
+        if let ByRef::Yes(Mutability::Mut) = def_br {
+            debug_assert!(max_ref_mutbl == MutblCap::Mut);
         }
         match adjust_mode {
             AdjustMode::Pass => (expected, def_br, max_ref_mutbl, false),
-            AdjustMode::Reset => (expected, ByRef::No, Mutability::Mut, false),
-            AdjustMode::ResetAndConsumeRef(ref_pat_mutbl) => {
-                let mutbls_match = def_br == ByRef::Yes(ref_pat_mutbl);
+            AdjustMode::Reset => (expected, ByRef::No, MutblCap::Mut, false),
+            AdjustMode::ResetAndConsumeRef(ref_pat_mutbl, inner_span) => {
+                // `&` pattern eats `&mut`
+                let mutbls_match =
+                    if let ByRef::Yes(def_mut) = def_br { ref_pat_mutbl <= def_mut } else { false };
+
                 if pat.span.at_least_rust_2024() && self.tcx.features().ref_pat_eat_one_layer_2024 {
+                    let max_ref_mutbl = if ref_pat_mutbl == Mutability::Not {
+                        max_ref_mutbl.cap_mutbl_to_not(Some(pat.span.until(inner_span)))
+                    } else {
+                        max_ref_mutbl
+                    };
+
                     if mutbls_match {
                         debug!("consuming inherited reference");
-                        (expected, ByRef::No, cmp::min(max_ref_mutbl, ref_pat_mutbl), true)
+                        (expected, ByRef::No, max_ref_mutbl, true)
                     } else {
                         let (new_ty, new_bm, max_ref_mutbl) = if ref_pat_mutbl == Mutability::Mut {
                             self.peel_off_references(
@@ -318,7 +361,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                                 max_ref_mutbl,
                             )
                         } else {
-                            (expected, def_br.cap_ref_mutability(Mutability::Not), Mutability::Not)
+                            (expected, def_br.cap_ref_mutability(Mutability::Not), max_ref_mutbl)
                         };
                         (new_ty, new_bm, max_ref_mutbl, false)
                     }
@@ -385,7 +428,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             // ```
             //
             // See issue #46688.
-            PatKind::Ref(_, mutbl) => AdjustMode::ResetAndConsumeRef(*mutbl),
+            PatKind::Ref(inner, mutbl) => AdjustMode::ResetAndConsumeRef(*mutbl, inner.span),
             // A `_` pattern works with any expected type, so there's no need to do anything.
             PatKind::Wild
             // A malformed pattern doesn't have an expected type, so let's just accept any type.
@@ -411,8 +454,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         expected: Ty<'tcx>,
         mut def_br: ByRef,
         max_peelable_mutability: Mutability,
-        mut max_ref_mutability: Mutability,
-    ) -> (Ty<'tcx>, ByRef, Mutability) {
+        mut max_ref_mutability: MutblCap,
+    ) -> (Ty<'tcx>, ByRef, MutblCap) {
         let mut expected = self.try_structurally_resolve_type(pat.span, expected);
         // Peel off as many `&` or `&mut` from the scrutinee type as possible. For example,
         // for `match &&&mut Some(5)` the loop runs three times, aborting when it reaches
@@ -446,9 +489,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         }
 
         if pat.span.at_least_rust_2024() && self.tcx.features().ref_pat_eat_one_layer_2024 {
-            def_br = def_br.cap_ref_mutability(max_ref_mutability);
+            def_br = def_br.cap_ref_mutability(max_ref_mutability.as_mutbl());
             if def_br == ByRef::Yes(Mutability::Not) {
-                max_ref_mutability = Mutability::Not;
+                max_ref_mutability = max_ref_mutability.cap_mutbl_to_not(None);
             }
         }
 
@@ -665,8 +708,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     fn check_pat_ident(
         &self,
         pat: &'tcx Pat<'tcx>,
-        ba: BindingMode,
+        explicit_ba: BindingMode,
         var_id: HirId,
+        ident: Ident,
         sub: Option<&'tcx Pat<'tcx>>,
         expected: Ty<'tcx>,
         pat_info: PatInfo<'tcx, '_>,
@@ -674,7 +718,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         let PatInfo { binding_mode: def_br, top_info: ti, .. } = pat_info;
 
         // Determine the binding mode...
-        let bm = match ba {
+        let bm = match explicit_ba {
             BindingMode(ByRef::No, Mutability::Mut)
                 if !(pat.span.at_least_rust_2024()
                     && self.tcx.features().mut_preserve_binding_mode_2024)
@@ -690,8 +734,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 BindingMode(ByRef::No, Mutability::Mut)
             }
             BindingMode(ByRef::No, mutbl) => BindingMode(def_br, mutbl),
-            BindingMode(ByRef::Yes(_), _) => ba,
+            BindingMode(ByRef::Yes(_), _) => explicit_ba,
         };
+
+        if bm.0 == ByRef::Yes(Mutability::Mut)
+            && let MutblCap::Not(Some(and_pat_span)) = pat_info.max_ref_mutbl
+        {
+            let mut err = struct_span_code_err!(
+                self.tcx.dcx(),
+                ident.span,
+                E0596,
+                "cannot bind with `ref mut` behind an `&` pattern"
+            );
+            err.span_help(and_pat_span, "change this `&` pattern to an `&mut`");
+            err.emit();
+        }
+
         // ...and store it in a side table:
         self.typeck_results.borrow_mut().pat_binding_modes_mut().insert(pat.hir_id, bm);
 
@@ -717,7 +775,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         // If there are multiple arms, make sure they all agree on
         // what the type of the binding `x` ought to be.
         if var_id != pat.hir_id {
-            self.check_binding_alt_eq_ty(ba, pat.span, var_id, local_ty, ti);
+            self.check_binding_alt_eq_ty(explicit_ba, pat.span, var_id, local_ty, ti);
         }
 
         if let Some(p) = sub {
@@ -2117,7 +2175,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         } else {
             let tcx = self.tcx;
             let expected = self.shallow_resolve(expected);
-            let (ref_ty, inner_ty) = match self.check_dereferenceable(pat.span, expected, inner) {
+            let (ref_ty, inner_ty, pat_info) = match self
+                .check_dereferenceable(pat.span, expected, inner)
+            {
                 Ok(()) => {
                     // `demand::subtype` would be good enough, but using `eqtype` turns
                     // out to be equally general. See (note_1) for details.
@@ -2127,42 +2187,62 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     // the bad interactions of the given hack detailed in (note_1).
                     debug!("check_pat_ref: expected={:?}", expected);
                     match *expected.kind() {
-                        ty::Ref(_, r_ty, r_mutbl) if r_mutbl == mutbl => (expected, r_ty),
+                        ty::Ref(_, r_ty, r_mutbl) if r_mutbl == mutbl => (expected, r_ty, pat_info),
+
+                        // `&` pattern eats `&mut` reference
+                        ty::Ref(_, r_ty, Mutability::Mut)
+                            if mutbl == Mutability::Not
+                                && ((pat.span.at_least_rust_2024()
+                                    && self.tcx.features().ref_pat_eat_one_layer_2024)
+                                    || self.tcx.features().ref_pat_everywhere) =>
+                        {
+                            (
+                                expected,
+                                r_ty,
+                                PatInfo {
+                                    max_ref_mutbl: pat_info
+                                        .max_ref_mutbl
+                                        .cap_mutbl_to_not(Some(pat.span.until(inner.span))),
+                                    ..pat_info
+                                },
+                            )
+                        }
+
+                        _ if consumed_inherited_ref && self.tcx.features().ref_pat_everywhere => {
+                            // We already matched against a match-ergonmics inserted reference,
+                            // so we don't need to match against a reference from the original type.
+                            // Save this infor for use in lowering later
+                            self.typeck_results
+                                .borrow_mut()
+                                .skipped_ref_pats_mut()
+                                .insert(pat.hir_id);
+                            (expected, expected, pat_info)
+                        }
+
                         _ => {
-                            if consumed_inherited_ref && self.tcx.features().ref_pat_everywhere {
-                                // We already matched against a match-ergonmics inserted reference,
-                                // so we don't need to match against a reference from the original type.
-                                // Save this infor for use in lowering later
-                                self.typeck_results
-                                    .borrow_mut()
-                                    .skipped_ref_pats_mut()
-                                    .insert(pat.hir_id);
-                                (expected, expected)
-                            } else {
-                                let inner_ty = self.next_ty_var(inner.span);
-                                let ref_ty = self.new_ref_ty(pat.span, mutbl, inner_ty);
-                                debug!("check_pat_ref: demanding {:?} = {:?}", expected, ref_ty);
-                                let err = self.demand_eqtype_pat_diag(
-                                    pat.span,
-                                    expected,
-                                    ref_ty,
-                                    pat_info.top_info,
-                                );
+                            let inner_ty = self.next_ty_var(inner.span);
+                            let ref_ty = self.new_ref_ty(pat.span, mutbl, inner_ty);
+                            debug!("check_pat_ref: demanding {:?} = {:?}", expected, ref_ty);
+                            let err = self.demand_eqtype_pat_diag(
+                                pat.span,
+                                expected,
+                                ref_ty,
+                                pat_info.top_info,
+                            );
 
-                                // Look for a case like `fn foo(&foo: u32)` and suggest
-                                // `fn foo(foo: &u32)`
-                                if let Some(mut err) = err {
-                                    self.borrow_pat_suggestion(&mut err, pat);
-                                    err.emit();
-                                }
-                                (ref_ty, inner_ty)
+                            // Look for a case like `fn foo(&foo: u32)` and suggest
+                            // `fn foo(foo: &u32)`
+                            if let Some(mut err) = err {
+                                self.borrow_pat_suggestion(&mut err, pat);
+                                err.emit();
                             }
+                            (ref_ty, inner_ty, pat_info)
                         }
                     }
                 }
                 Err(guar) => {
                     let err = Ty::new_error(tcx, guar);
-                    (err, err)
+                    (err, err, pat_info)
                 }
             };
             self.check_pat(inner, inner_ty, pat_info);
diff --git a/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024.rs b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024.rs
index f1ac3e340e9..62e4f82a3ff 100644
--- a/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024.rs
+++ b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024.rs
@@ -53,6 +53,12 @@ pub fn main() {
     if let Some(&Some(Some(&x))) = &Some(Some(&mut Some(0))) {
         let _: u32 = x;
     }
+    if let Some(&Some(&x)) = Some(&Some(&mut 0)) {
+        let _: u32 = x;
+    }
+    if let Some(&Some(x)) = &mut Some(Some(0)) {
+        let _: u32 = x;
+    }
 
     let &mut x = &&mut 0;
     let _: &u32 = x;
diff --git a/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail.rs b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail.rs
index ec091bb1746..61e61719458 100644
--- a/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail.rs
+++ b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail.rs
@@ -14,16 +14,24 @@ pub fn main() {
         let _: &mut u32 = x;
         //~^ ERROR: mismatched types
     }
-    if let Some(&Some(&_)) = Some(&Some(&mut 0)) {
-        //~^ ERROR: mismatched types
-    }
     if let Some(&Some(&mut _)) = &mut Some(&Some(0)) {
         //~^ ERROR: mismatched types
     }
     if let Some(&Some(Some((&mut _)))) = &Some(Some(&mut Some(0))) {
         //~^ ERROR: mismatched types
     }
-
+    if let Some(&mut Some(x)) = &Some(Some(0)) {
+        //~^ ERROR: mismatched types
+    }
+    if let Some(&Some(ref mut x)) = &mut Some(Some(0)) {
+        //~^ ERROR: cannot bind with `ref mut` behind an `&` pattern
+    }
+    if let &Some(Some(ref mut x)) = &mut Some(Some(0)) {
+        //~^ ERROR: cannot bind with `ref mut` behind an `&` pattern
+    }
+    if let Some(&mut Some(x)) = &Some(Some(0)) {
+        //~^ ERROR: mismatched types
+    }
 
     let &mut _= &&0;
     //~^ ERROR: mismatched types
diff --git a/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail.stderr b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail.stderr
index be71ee606c7..2aa2e2851ce 100644
--- a/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail.stderr
+++ b/tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024_fail.stderr
@@ -34,17 +34,6 @@ LL |         let _: &mut u32 = x;
 error[E0308]: mismatched types
   --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:17:23
    |
-LL |     if let Some(&Some(&_)) = Some(&Some(&mut 0)) {
-   |                       ^^     ------------------- this expression has type `Option<&Option<&mut {integer}>>`
-   |                       |
-   |                       types differ in mutability
-   |
-   = note: expected mutable reference `&mut {integer}`
-                      found reference `&_`
-
-error[E0308]: mismatched types
-  --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:20:23
-   |
 LL |     if let Some(&Some(&mut _)) = &mut Some(&Some(0)) {
    |                       ^^^^^^     ------------------- this expression has type `&mut Option<&Option<{integer}>>`
    |                       |
@@ -54,7 +43,7 @@ LL |     if let Some(&Some(&mut _)) = &mut Some(&Some(0)) {
            found mutable reference `&mut _`
 
 error[E0308]: mismatched types
-  --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:23:29
+  --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:20:29
    |
 LL |     if let Some(&Some(Some((&mut _)))) = &Some(Some(&mut Some(0))) {
    |                             ^^^^^^       ------------------------- this expression has type `&Option<Option<&mut Option<{integer}>>>`
@@ -65,7 +54,53 @@ LL |     if let Some(&Some(Some((&mut _)))) = &Some(Some(&mut Some(0))) {
            found mutable reference `&mut _`
 
 error[E0308]: mismatched types
-  --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:28:9
+  --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:23:17
+   |
+LL |     if let Some(&mut Some(x)) = &Some(Some(0)) {
+   |                 ^^^^^^^^^^^^    -------------- this expression has type `&Option<Option<{integer}>>`
+   |                 |
+   |                 expected `Option<{integer}>`, found `&mut _`
+   |
+   = note:           expected enum `Option<{integer}>`
+           found mutable reference `&mut _`
+
+error[E0596]: cannot bind with `ref mut` behind an `&` pattern
+  --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:26:31
+   |
+LL |     if let Some(&Some(ref mut x)) = &mut Some(Some(0)) {
+   |                               ^
+   |
+help: change this `&` pattern to an `&mut`
+  --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:26:17
+   |
+LL |     if let Some(&Some(ref mut x)) = &mut Some(Some(0)) {
+   |                 ^
+
+error[E0596]: cannot bind with `ref mut` behind an `&` pattern
+  --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:29:31
+   |
+LL |     if let &Some(Some(ref mut x)) = &mut Some(Some(0)) {
+   |                               ^
+   |
+help: change this `&` pattern to an `&mut`
+  --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:29:12
+   |
+LL |     if let &Some(Some(ref mut x)) = &mut Some(Some(0)) {
+   |            ^
+
+error[E0308]: mismatched types
+  --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:32:17
+   |
+LL |     if let Some(&mut Some(x)) = &Some(Some(0)) {
+   |                 ^^^^^^^^^^^^    -------------- this expression has type `&Option<Option<{integer}>>`
+   |                 |
+   |                 expected `Option<{integer}>`, found `&mut _`
+   |
+   = note:           expected enum `Option<{integer}>`
+           found mutable reference `&mut _`
+
+error[E0308]: mismatched types
+  --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:36:9
    |
 LL |     let &mut _= &&0;
    |         ^^^^^^  --- this expression has type `&&{integer}`
@@ -76,7 +111,7 @@ LL |     let &mut _= &&0;
            found mutable reference `&mut _`
 
 error[E0308]: mismatched types
-  --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:31:9
+  --> $DIR/ref_pat_eat_one_layer_2024_fail.rs:39:9
    |
 LL |     let &mut _ = &&&&&&&&&&&&&&&&&&&&&&&&&&&&0;
    |         ^^^^^^   ----------------------------- this expression has type `&&&&&&&&&&&&&&&&&&&&&&&&&&&&{integer}`
@@ -86,6 +121,7 @@ LL |     let &mut _ = &&&&&&&&&&&&&&&&&&&&&&&&&&&&0;
    = note:           expected type `{integer}`
            found mutable reference `&mut _`
 
-error: aborting due to 8 previous errors
+error: aborting due to 11 previous errors
 
-For more information about this error, try `rustc --explain E0308`.
+Some errors have detailed explanations: E0308, E0596.
+For more information about an error, try `rustc --explain E0308`.
diff --git a/tests/ui/match/ref_pat_everywhere-mutability-mismatch.rs b/tests/ui/match/ref_pat_everywhere-fail.rs
index 9dd7a7893ec..d1b1c04730d 100644
--- a/tests/ui/match/ref_pat_everywhere-mutability-mismatch.rs
+++ b/tests/ui/match/ref_pat_everywhere-fail.rs
@@ -5,11 +5,7 @@ pub fn main() {
         //~^ ERROR: mismatched types [E0308]
         let _: u32 = x;
     }
-    if let &Some(x) = &mut Some(0) {
-        //~^ ERROR: mismatched types [E0308]
-        let _: u32 = x;
-    }
-    if let Some(&x) = &mut Some(0) {
+    if let Some(&mut x) = Some(&0) {
         //~^ ERROR: mismatched types [E0308]
         let _: u32 = x;
     }
diff --git a/tests/ui/match/ref_pat_everywhere-fail.stderr b/tests/ui/match/ref_pat_everywhere-fail.stderr
new file mode 100644
index 00000000000..25a01129f4a
--- /dev/null
+++ b/tests/ui/match/ref_pat_everywhere-fail.stderr
@@ -0,0 +1,38 @@
+error[E0308]: mismatched types
+  --> $DIR/ref_pat_everywhere-fail.rs:4:17
+   |
+LL |     if let Some(&x) = Some(0) {
+   |                 ^^    ------- this expression has type `Option<{integer}>`
+   |                 |
+   |                 expected integer, found `&_`
+   |
+   = note:   expected type `{integer}`
+           found reference `&_`
+help: consider removing `&` from the pattern
+   |
+LL |     if let Some(x) = Some(0) {
+   |                 ~
+
+error[E0308]: mismatched types
+  --> $DIR/ref_pat_everywhere-fail.rs:8:17
+   |
+LL |     if let Some(&mut x) = Some(&0) {
+   |                 ^^^^^^    -------- this expression has type `Option<&{integer}>`
+   |                 |
+   |                 types differ in mutability
+   |
+   = note:      expected reference `&{integer}`
+           found mutable reference `&mut _`
+note: to declare a mutable binding use: `mut x`
+  --> $DIR/ref_pat_everywhere-fail.rs:8:17
+   |
+LL |     if let Some(&mut x) = Some(&0) {
+   |                 ^^^^^^
+help: consider removing `&mut` from the pattern
+   |
+LL |     if let Some(x) = Some(&0) {
+   |                 ~
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/tests/ui/match/ref_pat_everywhere-mutability-mismatch.stderr b/tests/ui/match/ref_pat_everywhere-mutability-mismatch.stderr
deleted file mode 100644
index d512ea5f957..00000000000
--- a/tests/ui/match/ref_pat_everywhere-mutability-mismatch.stderr
+++ /dev/null
@@ -1,44 +0,0 @@
-error[E0308]: mismatched types
-  --> $DIR/ref_pat_everywhere-mutability-mismatch.rs:4:17
-   |
-LL |     if let Some(&x) = Some(0) {
-   |                 ^^    ------- this expression has type `Option<{integer}>`
-   |                 |
-   |                 expected integer, found `&_`
-   |
-   = note:   expected type `{integer}`
-           found reference `&_`
-help: consider removing `&` from the pattern
-   |
-LL |     if let Some(x) = Some(0) {
-   |                 ~
-
-error[E0308]: mismatched types
-  --> $DIR/ref_pat_everywhere-mutability-mismatch.rs:8:12
-   |
-LL |     if let &Some(x) = &mut Some(0) {
-   |            ^^^^^^^^   ------------ this expression has type `&mut Option<{integer}>`
-   |            |
-   |            types differ in mutability
-   |
-   = note: expected mutable reference `&mut Option<{integer}>`
-                      found reference `&_`
-
-error[E0308]: mismatched types
-  --> $DIR/ref_pat_everywhere-mutability-mismatch.rs:12:17
-   |
-LL |     if let Some(&x) = &mut Some(0) {
-   |                 ^^    ------------ this expression has type `&mut Option<{integer}>`
-   |                 |
-   |                 expected integer, found `&_`
-   |
-   = note:   expected type `{integer}`
-           found reference `&_`
-help: consider removing `&` from the pattern
-   |
-LL |     if let Some(x) = &mut Some(0) {
-   |                 ~
-
-error: aborting due to 3 previous errors
-
-For more information about this error, try `rustc --explain E0308`.
diff --git a/tests/ui/match/ref_pat_everywhere.rs b/tests/ui/match/ref_pat_everywhere.rs
index b3daca48409..9a79c548475 100644
--- a/tests/ui/match/ref_pat_everywhere.rs
+++ b/tests/ui/match/ref_pat_everywhere.rs
@@ -15,4 +15,10 @@ pub fn main() {
     if let Some(Some(&x)) = &Some(&mut Some(0)) {
         let _: u32 = x;
     }
+    if let &Some(x) = &mut Some(0) {
+        let _: u32 = x;
+    }
+    if let Some(&x) = &mut Some(0) {
+        let _: u32 = x;
+    }
 }