about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_hir_typeck/src/pat.rs140
-rw-r--r--src/doc/unstable-book/src/language-features/deref-patterns.md31
-rw-r--r--tests/ui/pattern/deref-patterns/byte-string-type-errors.rs19
-rw-r--r--tests/ui/pattern/deref-patterns/byte-string-type-errors.stderr40
-rw-r--r--tests/ui/pattern/deref-patterns/const-pats-do-not-mislead-inference.rs54
-rw-r--r--tests/ui/pattern/deref-patterns/const-pats-do-not-mislead-inference.stable.stderr55
-rw-r--r--tests/ui/pattern/deref-patterns/needs-gate.rs26
-rw-r--r--tests/ui/pattern/deref-patterns/needs-gate.stderr65
-rw-r--r--tests/ui/pattern/deref-patterns/strings.rs44
-rw-r--r--tests/ui/pattern/deref-patterns/typeck_fail.rs13
-rw-r--r--tests/ui/pattern/deref-patterns/typeck_fail.stderr23
11 files changed, 422 insertions, 88 deletions
diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs
index 7520782930a..f9502153afd 100644
--- a/compiler/rustc_hir_typeck/src/pat.rs
+++ b/compiler/rustc_hir_typeck/src/pat.rs
@@ -177,16 +177,20 @@ enum PeelKind {
     /// Only peel reference types. This is used for explicit `deref!(_)` patterns, which dereference
     /// any number of `&`/`&mut` references, plus a single smart pointer.
     ExplicitDerefPat,
-    /// Implicitly peel any number of references, and if `deref_patterns` is enabled, smart pointer
-    /// ADTs. In order to peel only as much as necessary for the pattern to match, the `until_adt`
-    /// field contains the ADT def that the pattern is a constructor for, if applicable, so that we
-    /// don't peel it. See [`ResolvedPat`] for more information.
-    Implicit { until_adt: Option<DefId> },
+    /// Implicitly peel references, and if `deref_patterns` is enabled, smart pointer ADTs.
+    Implicit {
+        /// The ADT the pattern is a constructor for, if applicable, so that we don't peel it. See
+        /// [`ResolvedPat`] for more information.
+        until_adt: Option<DefId>,
+        /// The number of references at the head of the pattern's type, so we can leave that many
+        /// untouched. This is `1` for string literals, and `0` for most patterns.
+        pat_ref_layers: usize,
+    },
 }
 
 impl AdjustMode {
     const fn peel_until_adt(opt_adt_def: Option<DefId>) -> AdjustMode {
-        AdjustMode::Peel { kind: PeelKind::Implicit { until_adt: opt_adt_def } }
+        AdjustMode::Peel { kind: PeelKind::Implicit { until_adt: opt_adt_def, pat_ref_layers: 0 } }
     }
     const fn peel_all() -> AdjustMode {
         AdjustMode::peel_until_adt(None)
@@ -488,9 +492,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         match pat.kind {
             // Peel off a `&` or `&mut` from the scrutinee type. See the examples in
             // `tests/ui/rfcs/rfc-2005-default-binding-mode`.
-            _ if let AdjustMode::Peel { .. } = adjust_mode
+            _ if let AdjustMode::Peel { kind: peel_kind } = adjust_mode
                 && pat.default_binding_modes
-                && let ty::Ref(_, inner_ty, inner_mutability) = *expected.kind() =>
+                && let ty::Ref(_, inner_ty, inner_mutability) = *expected.kind()
+                && self.should_peel_ref(peel_kind, expected) =>
             {
                 debug!("inspecting {:?}", expected);
 
@@ -531,24 +536,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             // If `deref_patterns` is enabled, peel a smart pointer from the scrutinee type. See the
             // examples in `tests/ui/pattern/deref_patterns/`.
             _ if self.tcx.features().deref_patterns()
-                && let AdjustMode::Peel { kind: PeelKind::Implicit { until_adt } } = adjust_mode
+                && let AdjustMode::Peel { kind: peel_kind } = adjust_mode
                 && pat.default_binding_modes
-                // For simplicity, only apply overloaded derefs if `expected` is a known ADT.
-                // FIXME(deref_patterns): we'll get better diagnostics for users trying to
-                // implicitly deref generics if we allow them here, but primitives, tuples, and
-                // inference vars definitely should be stopped. Figure out what makes most sense.
-                && let ty::Adt(scrutinee_adt, _) = *expected.kind()
-                // Don't peel if the pattern type already matches the scrutinee. E.g., stop here if
-                // matching on a `Cow<'a, T>` scrutinee with a `Cow::Owned(_)` pattern.
-                && until_adt != Some(scrutinee_adt.did())
-                // At this point, the pattern isn't able to match `expected` without peeling. Check
-                // that it implements `Deref` before assuming it's a smart pointer, to get a normal
-                // type error instead of a missing impl error if not. This only checks for `Deref`,
-                // not `DerefPure`: we require that too, but we want a trait error if it's missing.
-                && let Some(deref_trait) = self.tcx.lang_items().deref_trait()
-                && self
-                    .type_implements_trait(deref_trait, [expected], self.param_env)
-                    .may_apply() =>
+                && self.should_peel_smart_pointer(peel_kind, expected) =>
             {
                 debug!("scrutinee ty {expected:?} is a smart pointer, inserting overloaded deref");
                 // The scrutinee is a smart pointer; implicitly dereference it. This adds a
@@ -680,21 +670,32 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
             // String and byte-string literals result in types `&str` and `&[u8]` respectively.
             // All other literals result in non-reference types.
-            // As a result, we allow `if let 0 = &&0 {}` but not `if let "foo" = &&"foo" {}`.
-            //
-            // Call `resolve_vars_if_possible` here for inline const blocks.
-            PatKind::Expr(lt) => match self.resolve_vars_if_possible(self.check_pat_expr_unadjusted(lt)).kind() {
-                ty::Ref(..) => AdjustMode::Pass,
-                _ => {
-                    // Path patterns have already been handled, and inline const blocks currently
-                    // aren't possible to write, so any handling for them would be untested.
-                    if cfg!(debug_assertions)
-                        && self.tcx.features().deref_patterns()
-                        && !matches!(lt.kind, PatExprKind::Lit { .. })
-                    {
-                        span_bug!(lt.span, "FIXME(deref_patterns): adjust mode unimplemented for {:?}", lt.kind);
+            // As a result, we allow `if let 0 = &&0 {}` but not `if let "foo" = &&"foo" {}` unless
+            // `deref_patterns` is enabled.
+            PatKind::Expr(lt) => {
+                // Path patterns have already been handled, and inline const blocks currently
+                // aren't possible to write, so any handling for them would be untested.
+                if cfg!(debug_assertions)
+                    && self.tcx.features().deref_patterns()
+                    && !matches!(lt.kind, PatExprKind::Lit { .. })
+                {
+                    span_bug!(lt.span, "FIXME(deref_patterns): adjust mode unimplemented for {:?}", lt.kind);
+                }
+                // Call `resolve_vars_if_possible` here for inline const blocks.
+                let lit_ty = self.resolve_vars_if_possible(self.check_pat_expr_unadjusted(lt));
+                // If `deref_patterns` is enabled, allow `if let "foo" = &&"foo" {}`.
+                if self.tcx.features().deref_patterns() {
+                    let mut peeled_ty = lit_ty;
+                    let mut pat_ref_layers = 0;
+                    while let ty::Ref(_, inner_ty, mutbl) = *peeled_ty.kind() {
+                        // We rely on references at the head of constants being immutable.
+                        debug_assert!(mutbl.is_not());
+                        pat_ref_layers += 1;
+                        peeled_ty = inner_ty;
                     }
-                    AdjustMode::peel_all()
+                    AdjustMode::Peel { kind: PeelKind::Implicit { until_adt: None, pat_ref_layers } }
+                } else {
+                    if lit_ty.is_ref() { AdjustMode::Pass } else { AdjustMode::peel_all() }
                 }
             },
 
@@ -720,6 +721,67 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         }
     }
 
+    /// Assuming `expected` is a reference type, determine whether to peel it before matching.
+    fn should_peel_ref(&self, peel_kind: PeelKind, mut expected: Ty<'tcx>) -> bool {
+        debug_assert!(expected.is_ref());
+        let pat_ref_layers = match peel_kind {
+            PeelKind::ExplicitDerefPat => 0,
+            PeelKind::Implicit { pat_ref_layers, .. } => pat_ref_layers,
+        };
+
+        // Most patterns don't have reference types, so we'll want to peel all references from the
+        // scrutinee before matching. To optimize for the common case, return early.
+        if pat_ref_layers == 0 {
+            return true;
+        }
+        debug_assert!(
+            self.tcx.features().deref_patterns(),
+            "Peeling for patterns with reference types is gated by `deref_patterns`."
+        );
+
+        // If the pattern has as many or more layers of reference as the expected type, we can match
+        // without peeling more, unless we find a smart pointer or `&mut` that we also need to peel.
+        // We don't treat `&` and `&mut` as interchangeable, but by peeling `&mut`s before matching,
+        // we can still, e.g., match on a `&mut str` with a string literal pattern. This is because
+        // string literal patterns may be used where `str` is expected.
+        let mut expected_ref_layers = 0;
+        while let ty::Ref(_, inner_ty, mutbl) = *expected.kind() {
+            if mutbl.is_mut() {
+                // Mutable references can't be in the final value of constants, thus they can't be
+                // at the head of their types, thus we should always peel `&mut`.
+                return true;
+            }
+            expected_ref_layers += 1;
+            expected = inner_ty;
+        }
+        pat_ref_layers < expected_ref_layers || self.should_peel_smart_pointer(peel_kind, expected)
+    }
+
+    /// Determine whether `expected` is a smart pointer type that should be peeled before matching.
+    fn should_peel_smart_pointer(&self, peel_kind: PeelKind, expected: Ty<'tcx>) -> bool {
+        // Explicit `deref!(_)` patterns match against smart pointers; don't peel in that case.
+        if let PeelKind::Implicit { until_adt, .. } = peel_kind
+            // For simplicity, only apply overloaded derefs if `expected` is a known ADT.
+            // FIXME(deref_patterns): we'll get better diagnostics for users trying to
+            // implicitly deref generics if we allow them here, but primitives, tuples, and
+            // inference vars definitely should be stopped. Figure out what makes most sense.
+            && let ty::Adt(scrutinee_adt, _) = *expected.kind()
+            // Don't peel if the pattern type already matches the scrutinee. E.g., stop here if
+            // matching on a `Cow<'a, T>` scrutinee with a `Cow::Owned(_)` pattern.
+            && until_adt != Some(scrutinee_adt.did())
+            // At this point, the pattern isn't able to match `expected` without peeling. Check
+            // that it implements `Deref` before assuming it's a smart pointer, to get a normal
+            // type error instead of a missing impl error if not. This only checks for `Deref`,
+            // not `DerefPure`: we require that too, but we want a trait error if it's missing.
+            && let Some(deref_trait) = self.tcx.lang_items().deref_trait()
+            && self.type_implements_trait(deref_trait, [expected], self.param_env).may_apply()
+        {
+            true
+        } else {
+            false
+        }
+    }
+
     fn check_pat_expr_unadjusted(&self, lt: &'tcx hir::PatExpr<'tcx>) -> Ty<'tcx> {
         let ty = match &lt.kind {
             rustc_hir::PatExprKind::Lit { lit, negated } => {
diff --git a/src/doc/unstable-book/src/language-features/deref-patterns.md b/src/doc/unstable-book/src/language-features/deref-patterns.md
index 0cc7106da48..fb6df290cc1 100644
--- a/src/doc/unstable-book/src/language-features/deref-patterns.md
+++ b/src/doc/unstable-book/src/language-features/deref-patterns.md
@@ -65,15 +65,26 @@ let deref!(x) = Box::new(NoCopy) else { unreachable!() };
 drop::<NoCopy>(x);
 ```
 
-Additionally, when `deref_patterns` is enabled, string literal patterns may be written where `str`
-is expected. Likewise, byte string literal patterns may be written where `[u8]` or `[u8; _]` is
-expected. This lets them be used in `deref!(_)` patterns:
+Additionally, `deref_patterns` implements changes to string and byte string literal patterns,
+allowing then to be used in deref patterns:
 
 ```rust
 # #![feature(deref_patterns)]
 # #![allow(incomplete_features)]
-match ("test".to_string(), b"test".to_vec()) {
-    (deref!("test"), deref!(b"test")) => {}
+match ("test".to_string(), Box::from("test"), b"test".to_vec()) {
+    ("test", "test", b"test") => {}
+    _ => panic!(),
+}
+
+// This works through multiple layers of reference and smart pointer:
+match (&Box::new(&"test".to_string()), &&&"test") {
+    ("test", "test") => {}
+    _ => panic!(),
+}
+
+// `deref!("...")` syntax may also be used:
+match "test".to_string() {
+    deref!("test") => {}
     _ => panic!(),
 }
 
@@ -82,10 +93,16 @@ match *"test" {
     "test" => {}
     _ => panic!(),
 }
+match *b"test" {
+    b"test" => {}
+    _ => panic!(),
+}
+match *(b"test" as &[u8]) {
+    b"test" => {}
+    _ => panic!(),
+}
 ```
 
-Implicit deref pattern syntax is not yet supported for string or byte string literals.
-
 [`box_patterns`]: ./box-patterns.md
 [`string_deref_patterns`]: ./string-deref-patterns.md
 [smart pointers in the standard library]: https://doc.rust-lang.org/std/ops/trait.DerefPure.html#implementors
diff --git a/tests/ui/pattern/deref-patterns/byte-string-type-errors.rs b/tests/ui/pattern/deref-patterns/byte-string-type-errors.rs
index 64acc4748af..fdcc6cb4611 100644
--- a/tests/ui/pattern/deref-patterns/byte-string-type-errors.rs
+++ b/tests/ui/pattern/deref-patterns/byte-string-type-errors.rs
@@ -33,4 +33,23 @@ fn main() {
     if let b"test" = *b"this array is too long" {}
     //~^ ERROR mismatched types
     //~| NOTE expected an array with a size of 22, found one with a size of 4
+
+    // Test matching on `&mut T`: we peel the `&mut` before applying the usual special cases.
+    // No special cases apply to `()`, so the "found" type is the type of the literal.
+    if let b"test" = &mut () {}
+    //~^ ERROR mismatched types
+    //~| NOTE expected `()`, found `&[u8; 4]`
+
+    // If the pointee is an array or slice, the usual special cases will apply to the "found" type:
+    if let b"test" = &mut [] as &mut [i8] {}
+    //~^ ERROR mismatched type
+    //~| NOTE expected `[i8]`, found `[u8]`
+
+    if let b"test" = &mut [()] {}
+    //~^ ERROR mismatched types
+    //~| NOTE expected `[(); 1]`, found `[u8; 4]`
+
+    if let b"test" = &mut *b"this array is too long" {}
+    //~^ ERROR mismatched type
+    //~| NOTE expected an array with a size of 22, found one with a size of 4
 }
diff --git a/tests/ui/pattern/deref-patterns/byte-string-type-errors.stderr b/tests/ui/pattern/deref-patterns/byte-string-type-errors.stderr
index 0317b7209e1..046682004be 100644
--- a/tests/ui/pattern/deref-patterns/byte-string-type-errors.stderr
+++ b/tests/ui/pattern/deref-patterns/byte-string-type-errors.stderr
@@ -47,6 +47,44 @@ LL |     if let b"test" = *b"this array is too long" {}
    |            |
    |            expected an array with a size of 22, found one with a size of 4
 
-error: aborting due to 5 previous errors
+error[E0308]: mismatched types
+  --> $DIR/byte-string-type-errors.rs:39:12
+   |
+LL |     if let b"test" = &mut () {}
+   |            ^^^^^^^   ------- this expression has type `&mut ()`
+   |            |
+   |            expected `()`, found `&[u8; 4]`
+
+error[E0308]: mismatched types
+  --> $DIR/byte-string-type-errors.rs:44:12
+   |
+LL |     if let b"test" = &mut [] as &mut [i8] {}
+   |            ^^^^^^^   -------------------- this expression has type `&mut [i8]`
+   |            |
+   |            expected `[i8]`, found `[u8]`
+   |
+   = note: expected slice `[i8]`
+              found slice `[u8]`
+
+error[E0308]: mismatched types
+  --> $DIR/byte-string-type-errors.rs:48:12
+   |
+LL |     if let b"test" = &mut [()] {}
+   |            ^^^^^^^   --------- this expression has type `&mut [(); 1]`
+   |            |
+   |            expected `[(); 1]`, found `[u8; 4]`
+   |
+   = note: expected array `[(); 1]`
+              found array `[u8; 4]`
+
+error[E0308]: mismatched types
+  --> $DIR/byte-string-type-errors.rs:52:12
+   |
+LL |     if let b"test" = &mut *b"this array is too long" {}
+   |            ^^^^^^^   ------------------------------- this expression has type `&mut [u8; 22]`
+   |            |
+   |            expected an array with a size of 22, found one with a size of 4
+
+error: aborting due to 9 previous errors
 
 For more information about this error, try `rustc --explain E0308`.
diff --git a/tests/ui/pattern/deref-patterns/const-pats-do-not-mislead-inference.rs b/tests/ui/pattern/deref-patterns/const-pats-do-not-mislead-inference.rs
new file mode 100644
index 00000000000..3a2531f4b95
--- /dev/null
+++ b/tests/ui/pattern/deref-patterns/const-pats-do-not-mislead-inference.rs
@@ -0,0 +1,54 @@
+//@ revisions: stable deref_patterns
+//@[deref_patterns] check-pass
+//! `deref_patterns` allows string and byte string literal patterns to implicitly peel references
+//! and smart pointers from the scrutinee before matching. Since strings and byte strings themselves
+//! have reference types, we need to make sure we don't peel too much. By leaving the type of the
+//! match scrutinee partially uninferred, these tests make sure we only peel as much as needed in
+//! order to match. In particular, when peeling isn't needed, the results should be the same was
+//! we'd get without `deref_patterns` enabled.
+
+#![cfg_attr(deref_patterns, feature(deref_patterns))]
+#![cfg_attr(deref_patterns, expect(incomplete_features))]
+
+fn uninferred<T>() -> T { unimplemented!() }
+
+// Assert type equality without allowing coercions.
+trait Is<T> {}
+impl<T> Is<T> for T {}
+fn has_type<T>(_: impl Is<T>) {}
+
+fn main() {
+    // We don't need to peel anything to unify the type of `x` with `&str`, so `x: &str`.
+    let x = uninferred();
+    if let "..." = x {}
+    has_type::<&str>(x);
+
+    // We don't need to peel anything to unify the type of `&x` with `&[u8; 3]`, so `x: [u8; 3]`.
+    let x = uninferred();
+    if let b"..." = &x {}
+    has_type::<[u8; 3]>(x);
+
+    // Peeling a single `&` lets us unify the type of `&x` with `&[u8; 3]`, giving `x: [u8; 3]`.
+    let x = uninferred();
+    if let b"..." = &&x {}
+    //[stable]~^ ERROR: mismatched types
+    has_type::<[u8; 3]>(x);
+
+    // We have to peel both the `&` and the box before unifying the type of `x` with `&str`.
+    let x = uninferred();
+    if let "..." = &Box::new(x) {}
+    //[stable]~^ ERROR mismatched types
+    has_type::<&str>(x);
+
+    // After peeling the box, we can unify the type of `&x` with `&[u8; 3]`, giving `x: [u8; 3]`.
+    let x = uninferred();
+    if let b"..." = Box::new(&x) {}
+    //[stable]~^ ERROR mismatched types
+    has_type::<[u8; 3]>(x);
+
+    // `&` and `&mut` aren't interchangeable: `&mut`s need to be peeled before unifying, like boxes:
+    let mut x = uninferred();
+    if let "..." = &mut x {}
+    //[stable]~^ ERROR mismatched types
+    has_type::<&str>(x);
+}
diff --git a/tests/ui/pattern/deref-patterns/const-pats-do-not-mislead-inference.stable.stderr b/tests/ui/pattern/deref-patterns/const-pats-do-not-mislead-inference.stable.stderr
new file mode 100644
index 00000000000..61079718c5d
--- /dev/null
+++ b/tests/ui/pattern/deref-patterns/const-pats-do-not-mislead-inference.stable.stderr
@@ -0,0 +1,55 @@
+error[E0308]: mismatched types
+  --> $DIR/const-pats-do-not-mislead-inference.rs:33:12
+   |
+LL |     if let b"..." = &&x {}
+   |            ^^^^^^   --- this expression has type `&&_`
+   |            |
+   |            expected `&&_`, found `&[u8; 3]`
+   |
+   = note: expected reference `&&_`
+              found reference `&'static [u8; 3]`
+
+error[E0308]: mismatched types
+  --> $DIR/const-pats-do-not-mislead-inference.rs:39:12
+   |
+LL |     if let "..." = &Box::new(x) {}
+   |            ^^^^^   ------------ this expression has type `&Box<_>`
+   |            |
+   |            expected `&Box<_>`, found `&str`
+   |
+   = note: expected reference `&Box<_>`
+              found reference `&'static str`
+help: consider dereferencing to access the inner value using the Deref trait
+   |
+LL |     if let "..." = &*Box::new(x) {}
+   |                     +
+
+error[E0308]: mismatched types
+  --> $DIR/const-pats-do-not-mislead-inference.rs:45:12
+   |
+LL |     if let b"..." = Box::new(&x) {}
+   |            ^^^^^^   ------------ this expression has type `Box<&_>`
+   |            |
+   |            expected `Box<&_>`, found `&[u8; 3]`
+   |
+   = note: expected struct `Box<&_>`
+           found reference `&'static [u8; 3]`
+help: consider dereferencing to access the inner value using the Deref trait
+   |
+LL |     if let b"..." = *Box::new(&x) {}
+   |                     +
+
+error[E0308]: mismatched types
+  --> $DIR/const-pats-do-not-mislead-inference.rs:51:12
+   |
+LL |     if let "..." = &mut x {}
+   |            ^^^^^   ------ this expression has type `&mut _`
+   |            |
+   |            types differ in mutability
+   |
+   = note: expected mutable reference `&mut _`
+                      found reference `&'static str`
+
+error: aborting due to 4 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/tests/ui/pattern/deref-patterns/needs-gate.rs b/tests/ui/pattern/deref-patterns/needs-gate.rs
index 7944744ee83..953051f7b04 100644
--- a/tests/ui/pattern/deref-patterns/needs-gate.rs
+++ b/tests/ui/pattern/deref-patterns/needs-gate.rs
@@ -29,4 +29,30 @@ fn main() {
         //~^ ERROR: mismatched types
         _ => {}
     }
+
+    // `deref_patterns` allows string and byte string patterns to implicitly peel references.
+    match &"str" {
+        "str" => {}
+        //~^ ERROR: mismatched types
+        _ => {}
+    }
+    match &b"str" {
+        b"str" => {}
+        //~^ ERROR: mismatched types
+        _ => {}
+    }
+    match "str".to_owned() {
+        "str" => {}
+        //~^ ERROR: mismatched types
+        _ => {}
+    }
+
+    // `deref_patterns` allows string and byte string patterns to match on mutable references.
+    // See also `tests/ui/pattern/byte-string-mutability-mismatch.rs`.
+    if let "str" = &mut *"str".to_string() {}
+    //~^ ERROR mismatched types
+    if let b"str" = &mut b"str".clone() {}
+    //~^ ERROR mismatched types
+    if let b"str" = &mut b"str".clone()[..] {}
+    //~^ ERROR mismatched types
 }
diff --git a/tests/ui/pattern/deref-patterns/needs-gate.stderr b/tests/ui/pattern/deref-patterns/needs-gate.stderr
index e886ca98055..3d938a7e23f 100644
--- a/tests/ui/pattern/deref-patterns/needs-gate.stderr
+++ b/tests/ui/pattern/deref-patterns/needs-gate.stderr
@@ -47,7 +47,70 @@ LL |     match *(b"test" as &[u8]) {
 LL |         b"test" => {}
    |         ^^^^^^^ expected `[u8]`, found `&[u8; 4]`
 
-error: aborting due to 5 previous errors
+error[E0308]: mismatched types
+  --> $DIR/needs-gate.rs:35:9
+   |
+LL |     match &"str" {
+   |           ------ this expression has type `&&str`
+LL |         "str" => {}
+   |         ^^^^^ expected `&&str`, found `&str`
+   |
+   = note: expected reference `&&_`
+              found reference `&'static _`
+
+error[E0308]: mismatched types
+  --> $DIR/needs-gate.rs:40:9
+   |
+LL |     match &b"str" {
+   |           ------- this expression has type `&&[u8; 3]`
+LL |         b"str" => {}
+   |         ^^^^^^ expected `&&[u8; 3]`, found `&[u8; 3]`
+   |
+   = note: expected reference `&&_`
+              found reference `&'static _`
+
+error[E0308]: mismatched types
+  --> $DIR/needs-gate.rs:45:9
+   |
+LL |     match "str".to_owned() {
+   |           ---------------- this expression has type `String`
+LL |         "str" => {}
+   |         ^^^^^ expected `String`, found `&str`
+
+error[E0308]: mismatched types
+  --> $DIR/needs-gate.rs:52:12
+   |
+LL |     if let "str" = &mut *"str".to_string() {}
+   |            ^^^^^   ----------------------- this expression has type `&mut str`
+   |            |
+   |            types differ in mutability
+   |
+   = note: expected mutable reference `&mut _`
+                      found reference `&'static _`
+
+error[E0308]: mismatched types
+  --> $DIR/needs-gate.rs:54:12
+   |
+LL |     if let b"str" = &mut b"str".clone() {}
+   |            ^^^^^^   ------------------- this expression has type `&mut [u8; 3]`
+   |            |
+   |            types differ in mutability
+   |
+   = note: expected mutable reference `&mut _`
+                      found reference `&'static _`
+
+error[E0308]: mismatched types
+  --> $DIR/needs-gate.rs:56:12
+   |
+LL |     if let b"str" = &mut b"str".clone()[..] {}
+   |            ^^^^^^   ----------------------- this expression has type `&mut [u8]`
+   |            |
+   |            types differ in mutability
+   |
+   = note: expected mutable reference `&mut _`
+                      found reference `&'static _`
+
+error: aborting due to 11 previous errors
 
 Some errors have detailed explanations: E0308, E0658.
 For more information about an error, try `rustc --explain E0308`.
diff --git a/tests/ui/pattern/deref-patterns/strings.rs b/tests/ui/pattern/deref-patterns/strings.rs
index 536e943b3f6..fac15a9aee3 100644
--- a/tests/ui/pattern/deref-patterns/strings.rs
+++ b/tests/ui/pattern/deref-patterns/strings.rs
@@ -14,10 +14,26 @@ fn main() {
         };
         assert_eq!(test_actual, test_expect);
 
-        // Test string literals in explicit `deref!(_)` patterns.
+        // Test matching on `&mut str`.
+        let test_actual = match &mut *test_in.to_string() {
+            "zero" => 0,
+            "one" => 1,
+            _ => 2,
+        };
+        assert_eq!(test_actual, test_expect);
+
+        // Test string literals in deref patterns.
         let test_actual = match test_in.to_string() {
             deref!("zero") => 0,
-            deref!("one") => 1,
+            "one" => 1,
+            _ => 2,
+        };
+        assert_eq!(test_actual, test_expect);
+
+        // Test peeling references in addition to smart pointers.
+        let test_actual = match &test_in.to_string() {
+            deref!("zero") => 0,
+            "one" => 1,
             _ => 2,
         };
         assert_eq!(test_actual, test_expect);
@@ -47,18 +63,34 @@ fn main() {
         };
         assert_eq!(test_actual, test_expect);
 
-        // Test byte string literals used as arrays in explicit `deref!(_)` patterns.
+        // Test matching on `&mut [u8; N]`.
+        let test_actual = match &mut test_in.clone() {
+            b"0" => 0,
+            b"1" => 1,
+            _ => 2,
+        };
+        assert_eq!(test_actual, test_expect);
+
+        // Test matching on `&mut [u8]`.
+        let test_actual = match &mut test_in.clone()[..] {
+            b"0" => 0,
+            b"1" => 1,
+            _ => 2,
+        };
+        assert_eq!(test_actual, test_expect);
+
+        // Test byte string literals used as arrays in deref patterns.
         let test_actual = match Box::new(*test_in) {
             deref!(b"0") => 0,
-            deref!(b"1") => 1,
+            b"1" => 1,
             _ => 2,
         };
         assert_eq!(test_actual, test_expect);
 
-        // Test byte string literals used as slices in explicit `deref!(_)` patterns.
+        // Test byte string literals used as slices in deref patterns.
         let test_actual = match test_in.to_vec() {
             deref!(b"0") => 0,
-            deref!(b"1") => 1,
+            b"1" => 1,
             _ => 2,
         };
         assert_eq!(test_actual, test_expect);
diff --git a/tests/ui/pattern/deref-patterns/typeck_fail.rs b/tests/ui/pattern/deref-patterns/typeck_fail.rs
index 52d84f7a34d..6ae87bb7bc3 100644
--- a/tests/ui/pattern/deref-patterns/typeck_fail.rs
+++ b/tests/ui/pattern/deref-patterns/typeck_fail.rs
@@ -2,19 +2,6 @@
 #![allow(incomplete_features)]
 
 fn main() {
-    // FIXME(deref_patterns): fails to typecheck because string literal patterns don't peel
-    // references from the scrutinee.
-    match "foo".to_string() {
-        "foo" => {}
-        //~^ ERROR: mismatched types
-        _ => {}
-    }
-    match &"foo".to_string() {
-        "foo" => {}
-        //~^ ERROR: mismatched types
-        _ => {}
-    }
-
     // Make sure we don't try implicitly dereferncing any ADT.
     match Some(0) {
         Ok(0) => {}
diff --git a/tests/ui/pattern/deref-patterns/typeck_fail.stderr b/tests/ui/pattern/deref-patterns/typeck_fail.stderr
index e87528c1c51..fc29caac563 100644
--- a/tests/ui/pattern/deref-patterns/typeck_fail.stderr
+++ b/tests/ui/pattern/deref-patterns/typeck_fail.stderr
@@ -1,24 +1,5 @@
 error[E0308]: mismatched types
-  --> $DIR/typeck_fail.rs:8:9
-   |
-LL |     match "foo".to_string() {
-   |           ----------------- this expression has type `String`
-LL |         "foo" => {}
-   |         ^^^^^ expected `String`, found `&str`
-
-error[E0308]: mismatched types
-  --> $DIR/typeck_fail.rs:13:9
-   |
-LL |     match &"foo".to_string() {
-   |           ------------------ this expression has type `&String`
-LL |         "foo" => {}
-   |         ^^^^^ expected `&String`, found `&str`
-   |
-   = note: expected reference `&String`
-              found reference `&'static str`
-
-error[E0308]: mismatched types
-  --> $DIR/typeck_fail.rs:20:9
+  --> $DIR/typeck_fail.rs:7:9
    |
 LL |     match Some(0) {
    |           ------- this expression has type `Option<{integer}>`
@@ -28,6 +9,6 @@ LL |         Ok(0) => {}
    = note: expected enum `Option<{integer}>`
               found enum `Result<_, _>`
 
-error: aborting due to 3 previous errors
+error: aborting due to 1 previous error
 
 For more information about this error, try `rustc --explain E0308`.