Skip to content

Commit 17bb4bb

Browse files
committed
always peel &mut, to allow matching on &mut str
1 parent fe98130 commit 17bb4bb

8 files changed

+153
-6
lines changed

compiler/rustc_hir_typeck/src/pat.rs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -740,10 +740,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
740740
);
741741

742742
// If the pattern has as many or more layers of reference as the expected type, we can match
743-
// without peeling more, *unless* we find a smart pointer that we also need to peel.
744-
// TODO: always peel `&mut`
743+
// without peeling more, unless we find a smart pointer or `&mut` that we also need to peel.
744+
// We don't treat `&` and `&mut` as interchangeable, but by peeling `&mut`s before matching,
745+
// we can still, e.g., match on a `&mut str` with a string literal pattern. This is because
746+
// string literal patterns may be used where `str` is expected.
745747
let mut expected_ref_layers = 0;
746-
while let ty::Ref(_, inner_ty, _) = *expected.kind() {
748+
while let ty::Ref(_, inner_ty, mutbl) = *expected.kind() {
749+
if mutbl.is_mut() {
750+
// Mutable references can't be in the final value of constants, thus they can't be
751+
// at the head of their types, thus we should always peel `&mut`.
752+
return true;
753+
}
747754
expected_ref_layers += 1;
748755
expected = inner_ty;
749756
}

tests/ui/pattern/deref-patterns/byte-string-type-errors.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,4 +33,23 @@ fn main() {
3333
if let b"test" = *b"this array is too long" {}
3434
//~^ ERROR mismatched types
3535
//~| NOTE expected an array with a size of 22, found one with a size of 4
36+
37+
// Test matching on `&mut T`: we peel the `&mut` before applying the usual special cases.
38+
// No special cases apply to `()`, so the "found" type is the type of the literal.
39+
if let b"test" = &mut () {}
40+
//~^ ERROR mismatched types
41+
//~| NOTE expected `()`, found `&[u8; 4]`
42+
43+
// If the pointee is an array or slice, the usual special cases will apply to the "found" type:
44+
if let b"test" = &mut [] as &mut [i8] {}
45+
//~^ ERROR mismatched type
46+
//~| NOTE expected `[i8]`, found `[u8]`
47+
48+
if let b"test" = &mut [()] {}
49+
//~^ ERROR mismatched types
50+
//~| NOTE expected `[(); 1]`, found `[u8; 4]`
51+
52+
if let b"test" = &mut *b"this array is too long" {}
53+
//~^ ERROR mismatched type
54+
//~| NOTE expected an array with a size of 22, found one with a size of 4
3655
}

tests/ui/pattern/deref-patterns/byte-string-type-errors.stderr

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,44 @@ LL | if let b"test" = *b"this array is too long" {}
4747
| |
4848
| expected an array with a size of 22, found one with a size of 4
4949

50-
error: aborting due to 5 previous errors
50+
error[E0308]: mismatched types
51+
--> $DIR/byte-string-type-errors.rs:39:12
52+
|
53+
LL | if let b"test" = &mut () {}
54+
| ^^^^^^^ ------- this expression has type `&mut ()`
55+
| |
56+
| expected `()`, found `&[u8; 4]`
57+
58+
error[E0308]: mismatched types
59+
--> $DIR/byte-string-type-errors.rs:44:12
60+
|
61+
LL | if let b"test" = &mut [] as &mut [i8] {}
62+
| ^^^^^^^ -------------------- this expression has type `&mut [i8]`
63+
| |
64+
| expected `[i8]`, found `[u8]`
65+
|
66+
= note: expected slice `[i8]`
67+
found slice `[u8]`
68+
69+
error[E0308]: mismatched types
70+
--> $DIR/byte-string-type-errors.rs:48:12
71+
|
72+
LL | if let b"test" = &mut [()] {}
73+
| ^^^^^^^ --------- this expression has type `&mut [(); 1]`
74+
| |
75+
| expected `[(); 1]`, found `[u8; 4]`
76+
|
77+
= note: expected array `[(); 1]`
78+
found array `[u8; 4]`
79+
80+
error[E0308]: mismatched types
81+
--> $DIR/byte-string-type-errors.rs:52:12
82+
|
83+
LL | if let b"test" = &mut *b"this array is too long" {}
84+
| ^^^^^^^ ------------------------------- this expression has type `&mut [u8; 22]`
85+
| |
86+
| expected an array with a size of 22, found one with a size of 4
87+
88+
error: aborting due to 9 previous errors
5189

5290
For more information about this error, try `rustc --explain E0308`.

tests/ui/pattern/deref-patterns/const-pats-do-not-mislead-inference.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,4 +45,10 @@ fn main() {
4545
if let b"..." = Box::new(&x) {}
4646
//[stable]~^ ERROR mismatched types
4747
has_type::<[u8; 3]>(x);
48+
49+
// `&` and `&mut` aren't interchangeable: `&mut`s need to be peeled before unifying, like boxes:
50+
let mut x = uninferred();
51+
if let "..." = &mut x {}
52+
//[stable]~^ ERROR mismatched types
53+
has_type::<&str>(x);
4854
}

tests/ui/pattern/deref-patterns/const-pats-do-not-mislead-inference.stable.stderr

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,17 @@ help: consider dereferencing to access the inner value using the Deref trait
3939
LL | if let b"..." = *Box::new(&x) {}
4040
| +
4141

42-
error: aborting due to 3 previous errors
42+
error[E0308]: mismatched types
43+
--> $DIR/const-pats-do-not-mislead-inference.rs:51:12
44+
|
45+
LL | if let "..." = &mut x {}
46+
| ^^^^^ ------ this expression has type `&mut _`
47+
| |
48+
| types differ in mutability
49+
|
50+
= note: expected mutable reference `&mut _`
51+
found reference `&'static str`
52+
53+
error: aborting due to 4 previous errors
4354

4455
For more information about this error, try `rustc --explain E0308`.

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,4 +46,13 @@ fn main() {
4646
//~^ ERROR: mismatched types
4747
_ => {}
4848
}
49+
50+
// `deref_patterns` allows string and byte string patterns to match on mutable references.
51+
// See also `tests/ui/pattern/byte-string-mutability-mismatch.rs`.
52+
if let "str" = &mut *"str".to_string() {}
53+
//~^ ERROR mismatched types
54+
if let b"str" = &mut b"str".clone() {}
55+
//~^ ERROR mismatched types
56+
if let b"str" = &mut b"str".clone()[..] {}
57+
//~^ ERROR mismatched types
4958
}

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

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,40 @@ LL | match "str".to_owned() {
7777
LL | "str" => {}
7878
| ^^^^^ expected `String`, found `&str`
7979

80-
error: aborting due to 8 previous errors
80+
error[E0308]: mismatched types
81+
--> $DIR/needs-gate.rs:52:12
82+
|
83+
LL | if let "str" = &mut *"str".to_string() {}
84+
| ^^^^^ ----------------------- this expression has type `&mut str`
85+
| |
86+
| types differ in mutability
87+
|
88+
= note: expected mutable reference `&mut _`
89+
found reference `&'static _`
90+
91+
error[E0308]: mismatched types
92+
--> $DIR/needs-gate.rs:54:12
93+
|
94+
LL | if let b"str" = &mut b"str".clone() {}
95+
| ^^^^^^ ------------------- this expression has type `&mut [u8; 3]`
96+
| |
97+
| types differ in mutability
98+
|
99+
= note: expected mutable reference `&mut _`
100+
found reference `&'static _`
101+
102+
error[E0308]: mismatched types
103+
--> $DIR/needs-gate.rs:56:12
104+
|
105+
LL | if let b"str" = &mut b"str".clone()[..] {}
106+
| ^^^^^^ ----------------------- this expression has type `&mut [u8]`
107+
| |
108+
| types differ in mutability
109+
|
110+
= note: expected mutable reference `&mut _`
111+
found reference `&'static _`
112+
113+
error: aborting due to 11 previous errors
81114

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

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

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,14 @@ fn main() {
1414
};
1515
assert_eq!(test_actual, test_expect);
1616

17+
// Test matching on `&mut str`.
18+
let test_actual = match &mut *test_in.to_string() {
19+
"zero" => 0,
20+
"one" => 1,
21+
_ => 2,
22+
};
23+
assert_eq!(test_actual, test_expect);
24+
1725
// Test string literals in deref patterns.
1826
let test_actual = match test_in.to_string() {
1927
deref!("zero") => 0,
@@ -55,6 +63,22 @@ fn main() {
5563
};
5664
assert_eq!(test_actual, test_expect);
5765

66+
// Test matching on `&mut [u8; N]`.
67+
let test_actual = match &mut test_in.clone() {
68+
b"0" => 0,
69+
b"1" => 1,
70+
_ => 2,
71+
};
72+
assert_eq!(test_actual, test_expect);
73+
74+
// Test matching on `&mut [u8]`.
75+
let test_actual = match &mut test_in.clone()[..] {
76+
b"0" => 0,
77+
b"1" => 1,
78+
_ => 2,
79+
};
80+
assert_eq!(test_actual, test_expect);
81+
5882
// Test byte string literals used as arrays in deref patterns.
5983
let test_actual = match Box::new(*test_in) {
6084
deref!(b"0") => 0,

0 commit comments

Comments
 (0)