always peel &mut, to allow matching on &mut str

This commit is contained in:
dianne 2025-05-05 03:40:37 -07:00
parent fe98130e0f
commit 17bb4bbc86
8 changed files with 153 additions and 6 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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