Skip to content

Commit

Permalink
"structural" ruleset: match against the inherited ref when a referenc…
Browse files Browse the repository at this point in the history
…e pattern doesn't match the mutability of an inner reference

This is the `Deref(EatInner, FallbackToOuter)` rule in Typing Rust Patterns.
  • Loading branch information
dianne committed Jan 13, 2025
1 parent 4a5d475 commit 2fd80ef
Show file tree
Hide file tree
Showing 5 changed files with 71 additions and 64 deletions.
12 changes: 10 additions & 2 deletions compiler/rustc_hir_typeck/src/pat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2294,15 +2294,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
return expected;
}
InheritedRefMatchRule::EatInner => {
if let ty::Ref(_, _, r_mutbl) = *expected.kind() {
if let ty::Ref(_, _, r_mutbl) = *expected.kind()
&& pat_mutbl <= r_mutbl
{
// Match against the reference type; don't consume the inherited ref.
// NB: The check for compatible pattern and ref type mutability assumes that
// `&` patterns can match against mutable references (RFC 3627, Rule 5). If
// we implement a pattern typing ruleset with Rule 4 (including the fallback
// to matching the inherited ref when the inner ref can't match) but not
// Rule 5, we'll need to check that here.
// NB: For RFC 3627's Rule 3, we limit the default binding mode's ref
// mutability to `pat_info.max_ref_mutbl`. If we implement a pattern typing
// ruleset with Rule 4 but not Rule 3, we'll need to check that here.
let mutbl_cap = cmp::min(r_mutbl, pat_info.max_ref_mutbl.as_mutbl());
pat_info.binding_mode = pat_info.binding_mode.cap_ref_mutability(mutbl_cap);
} else {
// The expected type isn't a reference, so match against the inherited ref.
// The reference pattern can't match against the expected type, so try
// matching against the inherited ref instead.
if pat_mutbl > inh_mut {
// We can't match an inherited shared reference with `&mut`.
// NB: This assumes that `&` patterns can match against mutable
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
error[E0308]: mismatched types
--> $DIR/pattern-errors.rs:15:17
--> $DIR/pattern-errors.rs:10:17
|
LL | if let Some(&mut x) = &Some(&mut 0) {
| ^^^^^
Expand All @@ -11,7 +11,7 @@ LL | if let Some(&x) = &Some(&mut 0) {
| ~

error[E0308]: mismatched types
--> $DIR/pattern-errors.rs:19:17
--> $DIR/pattern-errors.rs:14:17
|
LL | if let Some(&mut Some(&x)) = &Some(&mut Some(0)) {
| ^^^^^
Expand All @@ -23,7 +23,7 @@ LL | if let Some(&Some(&x)) = &Some(&mut Some(0)) {
| ~

error[E0308]: mismatched types
--> $DIR/pattern-errors.rs:23:22
--> $DIR/pattern-errors.rs:18:22
|
LL | if let Some(Some(&mut x)) = &Some(Some(&mut 0)) {
| ^^^^^
Expand All @@ -35,7 +35,7 @@ LL | if let Some(Some(&x)) = &Some(Some(&mut 0)) {
| ~

error[E0308]: mismatched types
--> $DIR/pattern-errors.rs:28:17
--> $DIR/pattern-errors.rs:23:17
|
LL | if let Some(&mut Some(&_)) = &Some(&Some(0)) {
| ^^^^^
Expand All @@ -47,7 +47,7 @@ LL | if let Some(&Some(&_)) = &Some(&Some(0)) {
| ~

error[E0308]: mismatched types
--> $DIR/pattern-errors.rs:34:23
--> $DIR/pattern-errors.rs:29:23
|
LL | if let Some(&Some(&mut _)) = &mut Some(&Some(0)) {
| ^^^^^
Expand All @@ -59,7 +59,7 @@ LL | if let Some(&Some(&_)) = &mut Some(&Some(0)) {
| ~

error[E0308]: mismatched types
--> $DIR/pattern-errors.rs:40:17
--> $DIR/pattern-errors.rs:35:17
|
LL | if let Some(&mut Some(x)) = &Some(Some(0)) {
| ^^^^^
Expand All @@ -71,7 +71,7 @@ LL | if let Some(&Some(x)) = &Some(Some(0)) {
| ~

error[E0308]: mismatched types
--> $DIR/pattern-errors.rs:43:17
--> $DIR/pattern-errors.rs:38:17
|
LL | if let Some(&mut Some(x)) = &Some(Some(0)) {
| ^^^^^
Expand All @@ -83,7 +83,7 @@ LL | if let Some(&Some(x)) = &Some(Some(0)) {
| ~

error[E0308]: mismatched types
--> $DIR/pattern-errors.rs:123:10
--> $DIR/pattern-errors.rs:118:10
|
LL | let [&mut x] = &[&mut 0];
| ^^^^^
Expand All @@ -95,7 +95,7 @@ LL | let [&x] = &[&mut 0];
| ~

error[E0308]: mismatched types
--> $DIR/pattern-errors.rs:128:10
--> $DIR/pattern-errors.rs:123:10
|
LL | let [&mut &x] = &[&mut 0];
| ^^^^^
Expand All @@ -107,7 +107,7 @@ LL | let [&&x] = &[&mut 0];
| ~

error[E0308]: mismatched types
--> $DIR/pattern-errors.rs:133:10
--> $DIR/pattern-errors.rs:128:10
|
LL | let [&mut &ref x] = &[&mut 0];
| ^^^^^
Expand All @@ -119,7 +119,7 @@ LL | let [&&ref x] = &[&mut 0];
| ~

error[E0308]: mismatched types
--> $DIR/pattern-errors.rs:138:10
--> $DIR/pattern-errors.rs:133:10
|
LL | let [&mut &(mut x)] = &[&mut 0];
| ^^^^^
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,6 @@
#![cfg_attr(structural, feature(ref_pat_eat_one_layer_2024_structural))]

pub fn main() {
if let Some(&mut x) = &mut Some(&0) {
//[structural]~^ ERROR: mismatched types
let _: &u32 = x;
}

if let Some(&mut x) = &Some(&mut 0) {
//[classic]~^ ERROR: mismatched types
let _: &u32 = x;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,36 +1,17 @@
error[E0308]: mismatched types
--> $DIR/pattern-errors.rs:10:17
--> $DIR/pattern-errors.rs:23:17
|
LL | if let Some(&mut x) = &mut Some(&0) {
| ^^^^^^ ------------- this expression has type `&mut Option<&{integer}>`
| |
| types differ in mutability
|
= note: expected reference `&{integer}`
found mutable reference `&mut _`
note: to declare a mutable binding use: `mut x`
--> $DIR/pattern-errors.rs:10:17
LL | if let Some(&mut Some(&_)) = &Some(&Some(0)) {
| ^^^^^
|
LL | if let Some(&mut x) = &mut Some(&0) {
| ^^^^^^
help: consider removing `&mut` from the pattern
= note: cannot match inherited `&` with `&mut` pattern
help: replace this `&mut` pattern with `&`
|
LL | if let Some(x) = &mut Some(&0) {
LL | if let Some(&Some(&_)) = &Some(&Some(0)) {
| ~

error[E0308]: mismatched types
--> $DIR/pattern-errors.rs:28:17
|
LL | if let Some(&mut Some(&_)) = &Some(&Some(0)) {
| ^^^^^^^^^^^^^ --------------- this expression has type `&Option<&Option<{integer}>>`
| |
| types differ in mutability
|
= note: expected reference `&Option<{integer}>`
found mutable reference `&mut _`

error[E0308]: mismatched types
--> $DIR/pattern-errors.rs:31:23
--> $DIR/pattern-errors.rs:26:23
|
LL | if let Some(&Some(&mut _)) = &Some(&mut Some(0)) {
| ^^^^^
Expand All @@ -42,7 +23,7 @@ LL | if let Some(&Some(&_)) = &Some(&mut Some(0)) {
| ~

error[E0308]: mismatched types
--> $DIR/pattern-errors.rs:34:23
--> $DIR/pattern-errors.rs:29:23
|
LL | if let Some(&Some(&mut _)) = &mut Some(&Some(0)) {
| ^^^^^
Expand All @@ -54,7 +35,7 @@ LL | if let Some(&Some(&_)) = &mut Some(&Some(0)) {
| ~

error[E0308]: mismatched types
--> $DIR/pattern-errors.rs:37:29
--> $DIR/pattern-errors.rs:32:29
|
LL | if let Some(&Some(Some((&mut _)))) = &Some(Some(&mut Some(0))) {
| ^^^^^
Expand All @@ -66,7 +47,7 @@ LL | if let Some(&Some(Some((&_)))) = &Some(Some(&mut Some(0))) {
| ~

error[E0308]: mismatched types
--> $DIR/pattern-errors.rs:40:17
--> $DIR/pattern-errors.rs:35:17
|
LL | if let Some(&mut Some(x)) = &Some(Some(0)) {
| ^^^^^
Expand All @@ -78,7 +59,7 @@ LL | if let Some(&Some(x)) = &Some(Some(0)) {
| ~

error[E0308]: mismatched types
--> $DIR/pattern-errors.rs:43:17
--> $DIR/pattern-errors.rs:38:17
|
LL | if let Some(&mut Some(x)) = &Some(Some(0)) {
| ^^^^^
Expand All @@ -90,7 +71,7 @@ LL | if let Some(&Some(x)) = &Some(Some(0)) {
| ~

error[E0308]: mismatched types
--> $DIR/pattern-errors.rs:49:11
--> $DIR/pattern-errors.rs:44:11
|
LL | let &[&mut x] = &&mut [0];
| ^^^^^
Expand All @@ -102,7 +83,7 @@ LL | let &[&x] = &&mut [0];
| ~

error[E0308]: mismatched types
--> $DIR/pattern-errors.rs:54:11
--> $DIR/pattern-errors.rs:49:11
|
LL | let &[&mut x] = &mut &mut [0];
| ^^^^^
Expand All @@ -114,7 +95,7 @@ LL | let &[&x] = &mut &mut [0];
| ~

error[E0308]: mismatched types
--> $DIR/pattern-errors.rs:59:11
--> $DIR/pattern-errors.rs:54:11
|
LL | let &[&mut ref x] = &&mut [0];
| ^^^^^
Expand All @@ -126,7 +107,7 @@ LL | let &[&ref x] = &&mut [0];
| ~

error[E0308]: mismatched types
--> $DIR/pattern-errors.rs:64:11
--> $DIR/pattern-errors.rs:59:11
|
LL | let &[&mut ref x] = &mut &mut [0];
| ^^^^^
Expand All @@ -138,7 +119,7 @@ LL | let &[&ref x] = &mut &mut [0];
| ~

error[E0308]: mismatched types
--> $DIR/pattern-errors.rs:69:11
--> $DIR/pattern-errors.rs:64:11
|
LL | let &[&mut mut x] = &&mut [0];
| ^^^^^
Expand All @@ -150,7 +131,7 @@ LL | let &[&mut x] = &&mut [0];
| ~

error[E0308]: mismatched types
--> $DIR/pattern-errors.rs:74:11
--> $DIR/pattern-errors.rs:69:11
|
LL | let &[&mut mut x] = &mut &mut [0];
| ^^^^^
Expand All @@ -162,7 +143,7 @@ LL | let &[&mut x] = &mut &mut [0];
| ~

error[E0658]: binding cannot be both mutable and by-reference
--> $DIR/pattern-errors.rs:81:12
--> $DIR/pattern-errors.rs:76:12
|
LL | let [&(mut x)] = &[&0];
| ^^^^
Expand All @@ -172,7 +153,7 @@ LL | let [&(mut x)] = &[&0];
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date

error[E0658]: binding cannot be both mutable and by-reference
--> $DIR/pattern-errors.rs:85:12
--> $DIR/pattern-errors.rs:80:12
|
LL | let [&(mut x)] = &mut [&0];
| ^^^^
Expand All @@ -182,7 +163,7 @@ LL | let [&(mut x)] = &mut [&0];
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date

error[E0308]: mismatched types
--> $DIR/pattern-errors.rs:91:11
--> $DIR/pattern-errors.rs:86:11
|
LL | let [&&mut x] = &[&mut 0];
| ^^^^^
Expand All @@ -194,7 +175,7 @@ LL | let [&&x] = &[&mut 0];
| ~

error[E0308]: mismatched types
--> $DIR/pattern-errors.rs:96:11
--> $DIR/pattern-errors.rs:91:11
|
LL | let [&&mut x] = &mut [&mut 0];
| ^^^^^
Expand All @@ -206,7 +187,7 @@ LL | let [&&x] = &mut [&mut 0];
| ~

error[E0308]: mismatched types
--> $DIR/pattern-errors.rs:101:11
--> $DIR/pattern-errors.rs:96:11
|
LL | let [&&mut ref x] = &[&mut 0];
| ^^^^^
Expand All @@ -218,7 +199,7 @@ LL | let [&&ref x] = &[&mut 0];
| ~

error[E0308]: mismatched types
--> $DIR/pattern-errors.rs:106:11
--> $DIR/pattern-errors.rs:101:11
|
LL | let [&&mut ref x] = &mut [&mut 0];
| ^^^^^
Expand All @@ -230,7 +211,7 @@ LL | let [&&ref x] = &mut [&mut 0];
| ~

error[E0308]: mismatched types
--> $DIR/pattern-errors.rs:111:11
--> $DIR/pattern-errors.rs:106:11
|
LL | let [&&mut mut x] = &[&mut 0];
| ^^^^^
Expand All @@ -242,7 +223,7 @@ LL | let [&&mut x] = &[&mut 0];
| ~

error[E0308]: mismatched types
--> $DIR/pattern-errors.rs:116:11
--> $DIR/pattern-errors.rs:111:11
|
LL | let [&&mut mut x] = &mut [&mut 0];
| ^^^^^
Expand All @@ -253,7 +234,7 @@ help: replace this `&mut` pattern with `&`
LL | let [&&mut x] = &mut [&mut 0];
| ~

error: aborting due to 21 previous errors
error: aborting due to 20 previous errors

Some errors have detailed explanations: E0308, E0658.
For more information about an error, try `rustc --explain E0308`.
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
//@ run-pass
//! Test cases for well-typed patterns in edition 2024. These are in their own file to ensure we
//! pass both HIR typeck and MIR borrowck, as we may skip the latter if grouped with failing tests.
#![allow(incomplete_features)]
#![allow(incomplete_features, unused_mut)]
#![cfg_attr(classic, feature(ref_pat_eat_one_layer_2024))]
#![cfg_attr(structural, feature(ref_pat_eat_one_layer_2024_structural))]

Expand Down Expand Up @@ -53,4 +53,27 @@ pub fn main() {
if let Some(&Some(x)) = &mut Some(Some(0)) {
let _: u32 = x;
}

// Tests for eat-inner rulesets matching on the outer reference if matching on the inner
// reference causes a mutability mismatch, i.e. `Deref(EatInner, FallbackToOuter)`:
let [&mut x] = &mut [&0];
let _: &u32 = x;

let [&mut ref x] = &mut [&0];
let _: &&u32 = x;

let [&mut ref mut x] = &mut [&0];
let _: &mut &u32 = x;

let [&mut mut x] = &mut [&0];
let _: &u32 = x;

let [&mut &x] = &mut [&0];
let _: u32 = x;

let [&mut &ref x] = &mut [&0];
let _: &u32 = x;

let [&mut &(mut x)] = &mut [&0];
let _: u32 = x;
}

0 comments on commit 2fd80ef

Please sign in to comment.