From 17bb4bbc86c2078e8ca09e2fdf6fd380094be6d6 Mon Sep 17 00:00:00 2001 From: dianne Date: Mon, 5 May 2025 03:40:37 -0700 Subject: [PATCH] always peel `&mut`, to allow matching on `&mut str` --- compiler/rustc_hir_typeck/src/pat.rs | 13 ++++-- .../deref-patterns/byte-string-type-errors.rs | 19 +++++++++ .../byte-string-type-errors.stderr | 40 ++++++++++++++++++- .../const-pats-do-not-mislead-inference.rs | 6 +++ ...ats-do-not-mislead-inference.stable.stderr | 13 +++++- tests/ui/pattern/deref-patterns/needs-gate.rs | 9 +++++ .../pattern/deref-patterns/needs-gate.stderr | 35 +++++++++++++++- tests/ui/pattern/deref-patterns/strings.rs | 24 +++++++++++ 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,