Skip to content

Commit 0a92d84

Browse files
committed
make [u8] and [u8;N] literal patterns usable in deref patterns
Specifically, this allows byte string literal patterns to be used where a `[u8]` or `[u8;N]` is expected when `deref_patterns` is enabled.
1 parent c41740d commit 0a92d84

File tree

7 files changed

+181
-10
lines changed

7 files changed

+181
-10
lines changed

compiler/rustc_hir_typeck/src/pat.rs

+28-7
Original file line numberDiff line numberDiff line change
@@ -759,19 +759,40 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
759759

760760
// Byte string patterns behave the same way as array patterns
761761
// They can denote both statically and dynamically-sized byte arrays.
762+
// Additionally, when `deref_patterns` is enabled, byte string literal patterns may have
763+
// types `[u8]` or `[u8; N]`, in order to type, e.g., `deref!(b"..."): Vec<u8>`.
762764
let mut pat_ty = ty;
763765
if let hir::PatExprKind::Lit {
764766
lit: Spanned { node: ast::LitKind::ByteStr(..), .. }, ..
765767
} = lt.kind
766768
{
769+
let tcx = self.tcx;
767770
let expected = self.structurally_resolve_type(span, expected);
768-
if let ty::Ref(_, inner_ty, _) = *expected.kind()
769-
&& self.try_structurally_resolve_type(span, inner_ty).is_slice()
770-
{
771-
let tcx = self.tcx;
772-
trace!(?lt.hir_id.local_id, "polymorphic byte string lit");
773-
pat_ty =
774-
Ty::new_imm_ref(tcx, tcx.lifetimes.re_static, Ty::new_slice(tcx, tcx.types.u8));
771+
match *expected.kind() {
772+
// Allow `b"...": &[u8]`
773+
ty::Ref(_, inner_ty, _)
774+
if self.try_structurally_resolve_type(span, inner_ty).is_slice() =>
775+
{
776+
trace!(?lt.hir_id.local_id, "polymorphic byte string lit");
777+
pat_ty = Ty::new_imm_ref(
778+
tcx,
779+
tcx.lifetimes.re_static,
780+
Ty::new_slice(tcx, tcx.types.u8),
781+
);
782+
}
783+
// Allow `b"...": [u8; 3]` for `deref_patterns`
784+
ty::Array(..) if tcx.features().deref_patterns() => {
785+
pat_ty = match *ty.kind() {
786+
ty::Ref(_, inner_ty, _) => inner_ty,
787+
_ => span_bug!(span, "found byte string literal with non-ref type {ty:?}"),
788+
}
789+
}
790+
// Allow `b"...": [u8]` for `deref_patterns`
791+
ty::Slice(..) if tcx.features().deref_patterns() => {
792+
pat_ty = Ty::new_slice(tcx, tcx.types.u8);
793+
}
794+
// Otherwise, `b"...": &[u8; 3]`
795+
_ => {}
775796
}
776797
}
777798

compiler/rustc_mir_build/src/thir/constant.rs

+6-2
Original file line numberDiff line numberDiff line change
@@ -44,12 +44,16 @@ pub(crate) fn lit_to_const<'tcx>(
4444
ty::ValTree::from_raw_bytes(tcx, str_bytes)
4545
}
4646
(ast::LitKind::ByteStr(data, _), ty::Ref(_, inner_ty, _))
47-
if matches!(inner_ty.kind(), ty::Slice(_)) =>
47+
if matches!(inner_ty.kind(), ty::Slice(_) | ty::Array(..)) =>
4848
{
4949
let bytes = data as &[u8];
5050
ty::ValTree::from_raw_bytes(tcx, bytes)
5151
}
52-
(ast::LitKind::ByteStr(data, _), ty::Ref(_, inner_ty, _)) if inner_ty.is_array() => {
52+
(ast::LitKind::ByteStr(data, _), ty::Slice(_) | ty::Array(..))
53+
if tcx.features().deref_patterns() =>
54+
{
55+
// Byte string literal patterns may have type `[u8]` or `[u8; N]` if `deref_patterns` is
56+
// enabled, in order to allow, e.g., `deref!(b"..."): Vec<u8>`.
5357
let bytes = data as &[u8];
5458
ty::ValTree::from_raw_bytes(tcx, bytes)
5559
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
//! Test type errors for byte string literal patterns. `deref_patterns` allows byte string literal
2+
//! patterns to have type `[u8]` or `[u8; N]` when matching on a slice or array; this can affect the
3+
//! "found" type reported in error messages when matching on a slice or array of the wrong type.
4+
5+
#![feature(deref_patterns)]
6+
#![expect(incomplete_features)]
7+
8+
fn main() {
9+
// Baseline 1: under normal circumstances, byte string literal patterns have type `&[u8; N]`,
10+
// the same as byte string literals.
11+
if let b"test" = () {}
12+
//~^ ERROR mismatched types
13+
//~| expected `()`, found `&[u8; 4]`
14+
15+
// Baseline 2: there's a special case for byte string patterns in stable rust, allowing them to
16+
// match on slice references. This affects the error when matching on a non-`&[u8]` slice ref,
17+
// reporting the "found" type as `&[u8]`.
18+
if let b"test" = &[] as &[i8] {}
19+
//~^ ERROR mismatched types
20+
//~| expected `&[i8]`, found `&[u8]`
21+
22+
// Test matching on a non-`[u8]` slice: the pattern has type `[u8]` if a slice is expected.
23+
if let b"test" = *(&[] as &[i8]) {}
24+
//~^ ERROR mismatched types
25+
//~| expected `[i8]`, found `[u8]`
26+
27+
// Test matching on a non-`[u8;4]` array: the pattern has type `[u8;4]` if an array is expected.
28+
if let b"test" = [()] {}
29+
//~^ ERROR mismatched types
30+
//~| expected `[(); 1]`, found `[u8; 4]`
31+
if let b"test" = *b"this array is too long" {}
32+
//~^ ERROR mismatched types
33+
//~| expected an array with a size of 22, found one with a size of 4
34+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
error[E0308]: mismatched types
2+
--> $DIR/byte-string-type-errors.rs:11:12
3+
|
4+
LL | if let b"test" = () {}
5+
| ^^^^^^^ -- this expression has type `()`
6+
| |
7+
| expected `()`, found `&[u8; 4]`
8+
9+
error[E0308]: mismatched types
10+
--> $DIR/byte-string-type-errors.rs:18:12
11+
|
12+
LL | if let b"test" = &[] as &[i8] {}
13+
| ^^^^^^^ ------------ this expression has type `&[i8]`
14+
| |
15+
| expected `&[i8]`, found `&[u8]`
16+
|
17+
= note: expected reference `&[i8]`
18+
found reference `&'static [u8]`
19+
20+
error[E0308]: mismatched types
21+
--> $DIR/byte-string-type-errors.rs:23:12
22+
|
23+
LL | if let b"test" = *(&[] as &[i8]) {}
24+
| ^^^^^^^ --------------- this expression has type `[i8]`
25+
| |
26+
| expected `[i8]`, found `[u8]`
27+
|
28+
= note: expected slice `[i8]`
29+
found slice `[u8]`
30+
31+
error[E0308]: mismatched types
32+
--> $DIR/byte-string-type-errors.rs:28:12
33+
|
34+
LL | if let b"test" = [()] {}
35+
| ^^^^^^^ ---- this expression has type `[(); 1]`
36+
| |
37+
| expected `[(); 1]`, found `[u8; 4]`
38+
|
39+
= note: expected array `[(); 1]`
40+
found array `[u8; 4]`
41+
42+
error[E0308]: mismatched types
43+
--> $DIR/byte-string-type-errors.rs:31:12
44+
|
45+
LL | if let b"test" = *b"this array is too long" {}
46+
| ^^^^^^^ -------------------------- this expression has type `[u8; 22]`
47+
| |
48+
| expected an array with a size of 22, found one with a size of 4
49+
50+
error: aborting due to 5 previous errors
51+
52+
For more information about this error, try `rustc --explain E0308`.

tests/ui/pattern/deref-patterns/needs-gate.rs

+10
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,14 @@ fn main() {
1919
//~^ ERROR: mismatched types
2020
_ => {}
2121
}
22+
match *b"test" {
23+
b"test" => {}
24+
//~^ ERROR: mismatched types
25+
_ => {}
26+
}
27+
match *(b"test" as &[u8]) {
28+
b"test" => {}
29+
//~^ ERROR: mismatched types
30+
_ => {}
31+
}
2232
}

tests/ui/pattern/deref-patterns/needs-gate.stderr

+17-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,23 @@ LL | match *"test" {
3131
LL | "test" => {}
3232
| ^^^^^^ expected `str`, found `&str`
3333

34-
error: aborting due to 3 previous errors
34+
error[E0308]: mismatched types
35+
--> $DIR/needs-gate.rs:23:9
36+
|
37+
LL | match *b"test" {
38+
| -------- this expression has type `[u8; 4]`
39+
LL | b"test" => {}
40+
| ^^^^^^^ expected `[u8; 4]`, found `&[u8; 4]`
41+
42+
error[E0308]: mismatched types
43+
--> $DIR/needs-gate.rs:28:9
44+
|
45+
LL | match *(b"test" as &[u8]) {
46+
| ------------------- this expression has type `[u8]`
47+
LL | b"test" => {}
48+
| ^^^^^^^ expected `[u8]`, found `&[u8; 4]`
49+
50+
error: aborting due to 5 previous errors
3551

3652
Some errors have detailed explanations: E0308, E0658.
3753
For more information about an error, try `rustc --explain E0308`.

tests/ui/pattern/deref-patterns/strings.rs

+34
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,38 @@ fn main() {
2929
s.make_ascii_uppercase();
3030
}
3131
assert_eq!(test, "TEST");
32+
33+
for (test_in, test_expect) in [(b"0", 0), (b"1", 1), (b"2", 2)] {
34+
// Test byte string literal patterns having type `[u8; N]`
35+
let test_actual = match *test_in {
36+
b"0" => 0,
37+
b"1" => 1,
38+
_ => 2,
39+
};
40+
assert_eq!(test_actual, test_expect);
41+
42+
// Test byte string literal patterns having type `[u8]`
43+
let test_actual = match *(test_in as &[u8]) {
44+
b"0" => 0,
45+
b"1" => 1,
46+
_ => 2,
47+
};
48+
assert_eq!(test_actual, test_expect);
49+
50+
// Test byte string literals used as arrays in explicit `deref!(_)` patterns.
51+
let test_actual = match Box::new(*test_in) {
52+
deref!(b"0") => 0,
53+
deref!(b"1") => 1,
54+
_ => 2,
55+
};
56+
assert_eq!(test_actual, test_expect);
57+
58+
// Test byte string literals used as slices in explicit `deref!(_)` patterns.
59+
let test_actual = match test_in.to_vec() {
60+
deref!(b"0") => 0,
61+
deref!(b"1") => 1,
62+
_ => 2,
63+
};
64+
assert_eq!(test_actual, test_expect);
65+
}
3266
}

0 commit comments

Comments
 (0)