about summary refs log tree commit diff
diff options
context:
space:
mode:
authordianne <diannes.gm@gmail.com>2025-05-05 03:40:37 -0700
committerdianne <diannes.gm@gmail.com>2025-05-05 04:29:33 -0700
commit17bb4bbc86c2078e8ca09e2fdf6fd380094be6d6 (patch)
treeed4816e5c6f9c8bcdbcc5aa55e053e9e0d28df5e
parentfe98130e0ff87ef16170b4f03afd5c7bbb7da573 (diff)
downloadrust-17bb4bbc86c2078e8ca09e2fdf6fd380094be6d6.tar.gz
rust-17bb4bbc86c2078e8ca09e2fdf6fd380094be6d6.zip
always peel `&mut`, to allow matching on `&mut str`
-rw-r--r--compiler/rustc_hir_typeck/src/pat.rs13
-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.rs6
-rw-r--r--tests/ui/pattern/deref-patterns/const-pats-do-not-mislead-inference.stable.stderr13
-rw-r--r--tests/ui/pattern/deref-patterns/needs-gate.rs9
-rw-r--r--tests/ui/pattern/deref-patterns/needs-gate.stderr35
-rw-r--r--tests/ui/pattern/deref-patterns/strings.rs24
8 files changed, 153 insertions, 6 deletions
diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs
index c96ad0749a1..f9502153afd 100644
--- a/compiler/rustc_hir_typeck/src/pat.rs
+++ b/compiler/rustc_hir_typeck/src/pat.rs
@@ -740,10 +740,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         );
 
         // 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 that we also need to peel.
-        // TODO: always peel `&mut`
+        // 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, _) = *expected.kind() {
+        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;
         }
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
index 437c2ef7c65..3a2531f4b95 100644
--- 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
@@ -45,4 +45,10 @@ fn main() {
     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
index 9dcb9131184..61079718c5d 100644
--- 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
@@ -39,6 +39,17 @@ help: consider dereferencing to access the inner value using the Deref trait
 LL |     if let b"..." = *Box::new(&x) {}
    |                     +
 
-error: aborting due to 3 previous errors
+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 5dcf69a92d3..953051f7b04 100644
--- a/tests/ui/pattern/deref-patterns/needs-gate.rs
+++ b/tests/ui/pattern/deref-patterns/needs-gate.rs
@@ -46,4 +46,13 @@ fn main() {
         //~^ 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 55e1fa826e8..3d938a7e23f 100644
--- a/tests/ui/pattern/deref-patterns/needs-gate.stderr
+++ b/tests/ui/pattern/deref-patterns/needs-gate.stderr
@@ -77,7 +77,40 @@ LL |     match "str".to_owned() {
 LL |         "str" => {}
    |         ^^^^^ expected `String`, found `&str`
 
-error: aborting due to 8 previous errors
+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 7d571c81e91..fac15a9aee3 100644
--- a/tests/ui/pattern/deref-patterns/strings.rs
+++ b/tests/ui/pattern/deref-patterns/strings.rs
@@ -14,6 +14,14 @@ fn main() {
         };
         assert_eq!(test_actual, test_expect);
 
+        // 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,
@@ -55,6 +63,22 @@ fn main() {
         };
         assert_eq!(test_actual, test_expect);
 
+        // 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,