about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_hir_typeck/src/pat.rs35
-rw-r--r--compiler/rustc_mir_build/src/thir/constant.rs8
-rw-r--r--tests/ui/pattern/deref-patterns/byte-string-type-errors.rs34
-rw-r--r--tests/ui/pattern/deref-patterns/byte-string-type-errors.stderr52
-rw-r--r--tests/ui/pattern/deref-patterns/needs-gate.rs10
-rw-r--r--tests/ui/pattern/deref-patterns/needs-gate.stderr18
-rw-r--r--tests/ui/pattern/deref-patterns/strings.rs34
7 files changed, 181 insertions, 10 deletions
diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs
index bb85396277c..6391e843dc4 100644
--- a/compiler/rustc_hir_typeck/src/pat.rs
+++ b/compiler/rustc_hir_typeck/src/pat.rs
@@ -759,19 +759,40 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
         // Byte string patterns behave the same way as array patterns
         // They can denote both statically and dynamically-sized byte arrays.
+        // Additionally, when `deref_patterns` is enabled, byte string literal patterns may have
+        // types `[u8]` or `[u8; N]`, in order to type, e.g., `deref!(b"..."): Vec<u8>`.
         let mut pat_ty = ty;
         if let hir::PatExprKind::Lit {
             lit: Spanned { node: ast::LitKind::ByteStr(..), .. }, ..
         } = lt.kind
         {
+            let tcx = self.tcx;
             let expected = self.structurally_resolve_type(span, expected);
-            if let ty::Ref(_, inner_ty, _) = *expected.kind()
-                && self.try_structurally_resolve_type(span, inner_ty).is_slice()
-            {
-                let tcx = self.tcx;
-                trace!(?lt.hir_id.local_id, "polymorphic byte string lit");
-                pat_ty =
-                    Ty::new_imm_ref(tcx, tcx.lifetimes.re_static, Ty::new_slice(tcx, tcx.types.u8));
+            match *expected.kind() {
+                // Allow `b"...": &[u8]`
+                ty::Ref(_, inner_ty, _)
+                    if self.try_structurally_resolve_type(span, inner_ty).is_slice() =>
+                {
+                    trace!(?lt.hir_id.local_id, "polymorphic byte string lit");
+                    pat_ty = Ty::new_imm_ref(
+                        tcx,
+                        tcx.lifetimes.re_static,
+                        Ty::new_slice(tcx, tcx.types.u8),
+                    );
+                }
+                // Allow `b"...": [u8; 3]` for `deref_patterns`
+                ty::Array(..) if tcx.features().deref_patterns() => {
+                    pat_ty = match *ty.kind() {
+                        ty::Ref(_, inner_ty, _) => inner_ty,
+                        _ => span_bug!(span, "found byte string literal with non-ref type {ty:?}"),
+                    }
+                }
+                // Allow `b"...": [u8]` for `deref_patterns`
+                ty::Slice(..) if tcx.features().deref_patterns() => {
+                    pat_ty = Ty::new_slice(tcx, tcx.types.u8);
+                }
+                // Otherwise, `b"...": &[u8; 3]`
+                _ => {}
             }
         }
 
diff --git a/compiler/rustc_mir_build/src/thir/constant.rs b/compiler/rustc_mir_build/src/thir/constant.rs
index a5ae74280d0..b4fa55e1c1f 100644
--- a/compiler/rustc_mir_build/src/thir/constant.rs
+++ b/compiler/rustc_mir_build/src/thir/constant.rs
@@ -44,12 +44,16 @@ pub(crate) fn lit_to_const<'tcx>(
             ty::ValTree::from_raw_bytes(tcx, str_bytes)
         }
         (ast::LitKind::ByteStr(data, _), ty::Ref(_, inner_ty, _))
-            if matches!(inner_ty.kind(), ty::Slice(_)) =>
+            if matches!(inner_ty.kind(), ty::Slice(_) | ty::Array(..)) =>
         {
             let bytes = data as &[u8];
             ty::ValTree::from_raw_bytes(tcx, bytes)
         }
-        (ast::LitKind::ByteStr(data, _), ty::Ref(_, inner_ty, _)) if inner_ty.is_array() => {
+        (ast::LitKind::ByteStr(data, _), ty::Slice(_) | ty::Array(..))
+            if tcx.features().deref_patterns() =>
+        {
+            // Byte string literal patterns may have type `[u8]` or `[u8; N]` if `deref_patterns` is
+            // enabled, in order to allow, e.g., `deref!(b"..."): Vec<u8>`.
             let bytes = data as &[u8];
             ty::ValTree::from_raw_bytes(tcx, bytes)
         }
diff --git a/tests/ui/pattern/deref-patterns/byte-string-type-errors.rs b/tests/ui/pattern/deref-patterns/byte-string-type-errors.rs
new file mode 100644
index 00000000000..29a33e3e2c3
--- /dev/null
+++ b/tests/ui/pattern/deref-patterns/byte-string-type-errors.rs
@@ -0,0 +1,34 @@
+//! Test type errors for byte string literal patterns. `deref_patterns` allows byte string literal
+//! patterns to have type `[u8]` or `[u8; N]` when matching on a slice or array; this can affect the
+//! "found" type reported in error messages when matching on a slice or array of the wrong type.
+
+#![feature(deref_patterns)]
+#![expect(incomplete_features)]
+
+fn main() {
+    // Baseline 1: under normal circumstances, byte string literal patterns have type `&[u8; N]`,
+    // the same as byte string literals.
+    if let b"test" = () {}
+    //~^ ERROR mismatched types
+    //~| expected `()`, found `&[u8; 4]`
+
+    // Baseline 2: there's a special case for byte string patterns in stable rust, allowing them to
+    // match on slice references. This affects the error when matching on a non-`&[u8]` slice ref,
+    // reporting the "found" type as `&[u8]`.
+    if let b"test" = &[] as &[i8] {}
+    //~^ ERROR mismatched types
+    //~| expected `&[i8]`, found `&[u8]`
+
+    // Test matching on a non-`[u8]` slice: the pattern has type `[u8]` if a slice is expected.
+    if let b"test" = *(&[] as &[i8]) {}
+    //~^ ERROR mismatched types
+    //~| expected `[i8]`, found `[u8]`
+
+    // Test matching on a non-`[u8;4]` array: the pattern has type `[u8;4]` if an array is expected.
+    if let b"test" = [()] {}
+    //~^ ERROR mismatched types
+    //~| expected `[(); 1]`, found `[u8; 4]`
+    if let b"test" = *b"this array is too long" {}
+    //~^ ERROR mismatched types
+    //~| 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
new file mode 100644
index 00000000000..d29a5b59252
--- /dev/null
+++ b/tests/ui/pattern/deref-patterns/byte-string-type-errors.stderr
@@ -0,0 +1,52 @@
+error[E0308]: mismatched types
+  --> $DIR/byte-string-type-errors.rs:11:12
+   |
+LL |     if let b"test" = () {}
+   |            ^^^^^^^   -- this expression has type `()`
+   |            |
+   |            expected `()`, found `&[u8; 4]`
+
+error[E0308]: mismatched types
+  --> $DIR/byte-string-type-errors.rs:18:12
+   |
+LL |     if let b"test" = &[] as &[i8] {}
+   |            ^^^^^^^   ------------ this expression has type `&[i8]`
+   |            |
+   |            expected `&[i8]`, found `&[u8]`
+   |
+   = note: expected reference `&[i8]`
+              found reference `&'static [u8]`
+
+error[E0308]: mismatched types
+  --> $DIR/byte-string-type-errors.rs:23:12
+   |
+LL |     if let b"test" = *(&[] as &[i8]) {}
+   |            ^^^^^^^   --------------- this expression has type `[i8]`
+   |            |
+   |            expected `[i8]`, found `[u8]`
+   |
+   = note: expected slice `[i8]`
+              found slice `[u8]`
+
+error[E0308]: mismatched types
+  --> $DIR/byte-string-type-errors.rs:28:12
+   |
+LL |     if let b"test" = [()] {}
+   |            ^^^^^^^   ---- this expression has type `[(); 1]`
+   |            |
+   |            expected `[(); 1]`, found `[u8; 4]`
+   |
+   = note: expected array `[(); 1]`
+              found array `[u8; 4]`
+
+error[E0308]: mismatched types
+  --> $DIR/byte-string-type-errors.rs:31:12
+   |
+LL |     if let b"test" = *b"this array is too long" {}
+   |            ^^^^^^^   -------------------------- this expression has type `[u8; 22]`
+   |            |
+   |            expected an array with a size of 22, found one with a size of 4
+
+error: aborting due to 5 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 a26a4fedc5c..7944744ee83 100644
--- a/tests/ui/pattern/deref-patterns/needs-gate.rs
+++ b/tests/ui/pattern/deref-patterns/needs-gate.rs
@@ -19,4 +19,14 @@ fn main() {
         //~^ ERROR: mismatched types
         _ => {}
     }
+    match *b"test" {
+        b"test" => {}
+        //~^ ERROR: mismatched types
+        _ => {}
+    }
+    match *(b"test" as &[u8]) {
+        b"test" => {}
+        //~^ ERROR: mismatched types
+        _ => {}
+    }
 }
diff --git a/tests/ui/pattern/deref-patterns/needs-gate.stderr b/tests/ui/pattern/deref-patterns/needs-gate.stderr
index b1ae03753fe..e886ca98055 100644
--- a/tests/ui/pattern/deref-patterns/needs-gate.stderr
+++ b/tests/ui/pattern/deref-patterns/needs-gate.stderr
@@ -31,7 +31,23 @@ LL |     match *"test" {
 LL |         "test" => {}
    |         ^^^^^^ expected `str`, found `&str`
 
-error: aborting due to 3 previous errors
+error[E0308]: mismatched types
+  --> $DIR/needs-gate.rs:23:9
+   |
+LL |     match *b"test" {
+   |           -------- this expression has type `[u8; 4]`
+LL |         b"test" => {}
+   |         ^^^^^^^ expected `[u8; 4]`, found `&[u8; 4]`
+
+error[E0308]: mismatched types
+  --> $DIR/needs-gate.rs:28:9
+   |
+LL |     match *(b"test" as &[u8]) {
+   |           ------------------- this expression has type `[u8]`
+LL |         b"test" => {}
+   |         ^^^^^^^ expected `[u8]`, found `&[u8; 4]`
+
+error: aborting due to 5 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 b55451e3181..536e943b3f6 100644
--- a/tests/ui/pattern/deref-patterns/strings.rs
+++ b/tests/ui/pattern/deref-patterns/strings.rs
@@ -29,4 +29,38 @@ fn main() {
         s.make_ascii_uppercase();
     }
     assert_eq!(test, "TEST");
+
+    for (test_in, test_expect) in [(b"0", 0), (b"1", 1), (b"2", 2)] {
+        // Test byte string literal patterns having type `[u8; N]`
+        let test_actual = match *test_in {
+            b"0" => 0,
+            b"1" => 1,
+            _ => 2,
+        };
+        assert_eq!(test_actual, test_expect);
+
+        // Test byte string literal patterns having type `[u8]`
+        let test_actual = match *(test_in as &[u8]) {
+            b"0" => 0,
+            b"1" => 1,
+            _ => 2,
+        };
+        assert_eq!(test_actual, test_expect);
+
+        // Test byte string literals used as arrays in explicit `deref!(_)` patterns.
+        let test_actual = match Box::new(*test_in) {
+            deref!(b"0") => 0,
+            deref!(b"1") => 1,
+            _ => 2,
+        };
+        assert_eq!(test_actual, test_expect);
+
+        // Test byte string literals used as slices in explicit `deref!(_)` patterns.
+        let test_actual = match test_in.to_vec() {
+            deref!(b"0") => 0,
+            deref!(b"1") => 1,
+            _ => 2,
+        };
+        assert_eq!(test_actual, test_expect);
+    }
 }