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.
This commit is contained in:
dianne 2025-04-15 23:35:52 -07:00
parent 61840254c5
commit 32503440cd
7 changed files with 181 additions and 10 deletions

View file

@ -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]`
_ => {}
}
}

View file

@ -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)
}

View file

@ -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
}

View file

@ -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`.

View file

@ -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
_ => {}
}
}

View file

@ -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`.

View file

@ -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);
}
}