Skip to content

Commit

Permalink
Rollup merge of rust-lang#136093 - dianne:match-2024-for-edition-2021…
Browse files Browse the repository at this point in the history
…, r=Nadrieril

Match Ergonomics 2024: update old-edition behavior of feature gates

This updates the behavior of the feature gates `ref_pat_eat_one_layer_2024_structural` and `ref_pat_eat_one_layer_2024` in Editions 2021 and earlier to correspond to the left and right typing rules compared [here](https://nadrieril.github.io/typing-rust-patterns/?opts1=AQEBAQIBAQEBAAAAAAAAAAAAAAAAAAA%3D&style=UserVisible&compare=true&opts2=AQEBAQIBAQABAAAAAQEBAAEBAAABAAA%3D&mode=rules), respectively. Compared to the `stable_rust` rules:
- they both allow reference patterns to match a lone inherited ref,
- they both allow `&` patterns to eat `&mut` reference types (and lone `&mut` inherited refs) as if they're shared,
- they both allow `&mut` patterns to eat `&` reference types when there's a `&mut` inherited reference to also eat,
- and the left ruleset has RFC 3627's Rule 3: after encountering a shared reference type in the scrutinee, the default binding mode will be treated as by-shared-ref when it would otherwise be by-mutable-ref.

I think there's already tests for all of those typing rules, so I've added revisions to use the existing tests with the new rulesets. Additionally, I've added a few tests to make sure we handle mixed-edition patterns appropriately, and I've added references to the unstable book.

Relevant tracking issue: rust-lang#123076

r? ``@ghost``
  • Loading branch information
matthiaskrgr authored Feb 19, 2025
2 parents 7b7b1d4 + 0a15bfb commit 659838e
Show file tree
Hide file tree
Showing 45 changed files with 1,866 additions and 523 deletions.
67 changes: 60 additions & 7 deletions compiler/rustc_hir_typeck/src/pat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -230,10 +230,19 @@ enum InheritedRefMatchRule {
/// underlying type is not a reference type, the inherited reference will be consumed.
EatInner,
/// When the underlying type is a reference type, reference patterns consume both layers of
/// reference, i.e. they both reset the binding mode and consume the reference type. Reference
/// patterns are not permitted when there is no underlying reference type, i.e. they can't eat
/// only an inherited reference. This is the current stable Rust behavior.
EatBoth,
/// reference, i.e. they both reset the binding mode and consume the reference type.
EatBoth {
/// If `true`, an inherited reference will be considered when determining whether a reference
/// pattern matches a given type:
/// - If the underlying type is not a reference, a reference pattern may eat the inherited reference;
/// - If the underlying type is a reference, a reference pattern matches if it can eat either one
/// of the underlying and inherited references. E.g. a `&mut` pattern is allowed if either the
/// underlying type is `&mut` or the inherited reference is `&mut`.
/// If `false`, a reference pattern is only matched against the underlying type.
/// This is `false` for stable Rust and `true` for both the `ref_pat_eat_one_layer_2024` and
/// `ref_pat_eat_one_layer_2024_structural` feature gates.
consider_inherited_ref: bool,
},
}

impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
Expand All @@ -259,10 +268,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
} else {
// Currently, matching against an inherited ref on edition 2024 is an error.
// Use `EatBoth` as a fallback to be similar to stable Rust.
InheritedRefMatchRule::EatBoth
InheritedRefMatchRule::EatBoth { consider_inherited_ref: false }
}
} else {
InheritedRefMatchRule::EatBoth
InheritedRefMatchRule::EatBoth {
consider_inherited_ref: self.tcx.features().ref_pat_eat_one_layer_2024()
|| self.tcx.features().ref_pat_eat_one_layer_2024_structural(),
}
}
}

Expand Down Expand Up @@ -2371,6 +2383,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// NB: This assumes that `&` patterns can match against mutable
// references (RFC 3627, Rule 5). If we implement a pattern typing
// ruleset with Rule 4 but not Rule 5, we'll need to check that here.
// FIXME(ref_pat_eat_one_layer_2024_structural): If we already tried
// matching the real reference, the error message should explain that
// falling back to the inherited reference didn't work. This should be
// the same error as the old-Edition version below.
debug_assert!(ref_pat_matches_mut_ref);
self.error_inherited_ref_mutability_mismatch(pat, pat_prefix_span);
}
Expand All @@ -2381,9 +2397,46 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
return expected;
}
}
InheritedRefMatchRule::EatBoth => {
InheritedRefMatchRule::EatBoth { consider_inherited_ref: true } => {
// Reset binding mode on old editions
pat_info.binding_mode = ByRef::No;

if let ty::Ref(_, inner_ty, _) = *expected.kind() {
// Consume both the inherited and inner references.
if pat_mutbl.is_mut() && inh_mut.is_mut() {
// As a special case, a `&mut` reference pattern will be able to match
// against a reference type of any mutability if the inherited ref is
// mutable. Since this allows us to match against a shared reference
// type, we refer to this as "falling back" to matching the inherited
// reference, though we consume the real reference as well. We handle
// this here to avoid adding this case to the common logic below.
self.check_pat(inner, inner_ty, pat_info);
return expected;
} else {
// Otherwise, use the common logic below for matching the inner
// reference type.
// FIXME(ref_pat_eat_one_layer_2024_structural): If this results in a
// mutability mismatch, the error message should explain that falling
// back to the inherited reference didn't work. This should be the same
// error as the Edition 2024 version above.
}
} else {
// The expected type isn't a reference type, so only match against the
// inherited reference.
if pat_mutbl > inh_mut {
// We can't match a lone inherited shared reference with `&mut`.
self.error_inherited_ref_mutability_mismatch(pat, pat_prefix_span);
}

self.typeck_results.borrow_mut().skipped_ref_pats_mut().insert(pat.hir_id);
self.check_pat(inner, expected, pat_info);
return expected;
}
}
InheritedRefMatchRule::EatBoth { consider_inherited_ref: false } => {
// Reset binding mode on stable Rust. This will be a type error below if
// `expected` is not a reference type.
pat_info.binding_mode = ByRef::No;
self.add_rust_2024_migration_desugared_pat(
pat_info.top_info.hir_id,
pat,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,11 @@ This feature is incomplete and not yet intended for general use.

This implements experimental, Edition-dependent match ergonomics under consideration for inclusion
in Rust.
For more information, see the corresponding typing rules for [Editions 2024 and later].
On earlier Editions, the current behavior is unspecified.
For more information, see the corresponding typing rules for [Editions 2021 and earlier] and for
[Editions 2024 and later].

For alternative experimental match ergonomics, see the feature
[`ref_pat_eat_one_layer_2024`](./ref-pat-eat-one-layer-2024.md).

[Editions 2021 and earlier]: https://nadrieril.github.io/typing-rust-patterns/?compare=false&opts1=AQEBAQIBAQEBAAAAAAAAAAAAAAAAAAA%3D&mode=rules&do_cmp=false
[Editions 2024 and later]: https://nadrieril.github.io/typing-rust-patterns/?compare=false&opts1=AQEBAgEBAQEBAgIAAAAAAAAAAAAAAAA%3D&mode=rules&do_cmp=false
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,11 @@ This feature is incomplete and not yet intended for general use.

This implements experimental, Edition-dependent match ergonomics under consideration for inclusion
in Rust.
For more information, see the corresponding typing rules for [Editions 2024 and later].
On earlier Editions, the current behavior is unspecified.
For more information, see the corresponding typing rules for [Editions 2021 and earlier] and for
[Editions 2024 and later].

For alternative experimental match ergonomics, see the feature
[`ref_pat_eat_one_layer_2024_structural`](./ref-pat-eat-one-layer-2024-structural.md).

[Editions 2021 and earlier]: https://nadrieril.github.io/typing-rust-patterns/?compare=false&opts1=AQEBAQIBAQABAAAAAQEBAAEBAAABAAA%3D&mode=rules&do_cmp=false
[Editions 2024 and later]: https://nadrieril.github.io/typing-rust-patterns/?compare=false&opts1=AQEBAAABAQABAgIAAQEBAAEBAAABAAA%3D&mode=rules&do_cmp=false
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
//@[classic2021] edition: 2024
//@[structural2021] edition: 2024
//@[classic2024] edition: 2021
//@[structural2024] edition: 2021
//! This contains macros in an edition *different* to the one used in `../mixed-editions.rs`, in
//! order to test typing mixed-edition patterns.
#[macro_export]
macro_rules! match_ctor {
($p:pat) => {
[$p]
};
}

#[macro_export]
macro_rules! match_ref {
($p:pat) => {
&$p
};
}

#[macro_export]
macro_rules! bind {
($i:ident) => {
$i
}
}

#[macro_export]
macro_rules! bind_ref {
($i:ident) => {
ref $i
}
}

#[macro_export]
macro_rules! bind_mut {
($i:ident) => {
mut $i
}
}

#[macro_export]
macro_rules! bind_ref_mut {
($i:ident) => {
ref mut $i
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
error[E0507]: cannot move out of a shared reference
--> $DIR/borrowck-errors.rs:31:29
|
LL | if let Some(&Some(x)) = Some(&Some(&mut 0)) {
| - ^^^^^^^^^^^^^^^^^^^
| |
| data moved here
| move occurs because `x` has type `&mut u32`, which does not implement the `Copy` trait
|
help: consider removing the borrow
|
LL - if let Some(&Some(x)) = Some(&Some(&mut 0)) {
LL + if let Some(Some(x)) = Some(&Some(&mut 0)) {
|

error[E0596]: cannot borrow data in a `&` reference as mutable
--> $DIR/borrowck-errors.rs:36:10
|
LL | let &ref mut x = &0;
| ^^^^^^^^^ cannot borrow as mutable

error[E0596]: cannot borrow data in a `&` reference as mutable
--> $DIR/borrowck-errors.rs:41:23
|
LL | if let &Some(Some(x)) = &Some(&mut Some(0)) {
| ^ cannot borrow as mutable

error[E0596]: cannot borrow data in a `&` reference as mutable
--> $DIR/borrowck-errors.rs:46:11
|
LL | let &[x] = &&mut [0];
| ^ cannot borrow as mutable

error: aborting due to 4 previous errors

Some errors have detailed explanations: E0507, E0596.
For more information about an error, try `rustc --explain E0507`.
Original file line number Diff line number Diff line change
@@ -1,33 +1,33 @@
error[E0508]: cannot move out of type `[&mut u32; 1]`, a non-copy array
--> $DIR/borrowck-errors.rs:13:16
error[E0508]: cannot move out of type `[&mut i32; 1]`, a non-copy array
--> $DIR/borrowck-errors.rs:15:16
|
LL | let [&x] = &[&mut 0];
| - ^^^^^^^^^ cannot move out of here
| |
| data moved here
| move occurs because `x` has type `&mut u32`, which does not implement the `Copy` trait
| move occurs because `x` has type `&mut i32`, which does not implement the `Copy` trait
|
help: consider borrowing the pattern binding
|
LL | let [&ref x] = &[&mut 0];
| +++

error[E0508]: cannot move out of type `[&mut u32; 1]`, a non-copy array
--> $DIR/borrowck-errors.rs:19:16
error[E0508]: cannot move out of type `[&mut i32; 1]`, a non-copy array
--> $DIR/borrowck-errors.rs:22:16
|
LL | let [&x] = &mut [&mut 0];
| - ^^^^^^^^^^^^^ cannot move out of here
| |
| data moved here
| move occurs because `x` has type `&mut u32`, which does not implement the `Copy` trait
| move occurs because `x` has type `&mut i32`, which does not implement the `Copy` trait
|
help: consider borrowing the pattern binding
|
LL | let [&ref x] = &mut [&mut 0];
| +++

error[E0507]: cannot move out of a shared reference
--> $DIR/borrowck-errors.rs:27:29
--> $DIR/borrowck-errors.rs:31:29
|
LL | if let Some(&Some(x)) = Some(&Some(&mut 0)) {
| - ^^^^^^^^^^^^^^^^^^^
Expand All @@ -42,25 +42,25 @@ LL + if let Some(Some(x)) = Some(&Some(&mut 0)) {
|

error[E0596]: cannot borrow data in a `&` reference as mutable
--> $DIR/borrowck-errors.rs:32:10
--> $DIR/borrowck-errors.rs:36:10
|
LL | let &ref mut x = &0;
| ^^^^^^^^^ cannot borrow as mutable

error[E0596]: cannot borrow data in a `&` reference as mutable
--> $DIR/borrowck-errors.rs:37:23
--> $DIR/borrowck-errors.rs:41:23
|
LL | if let &Some(Some(x)) = &Some(&mut Some(0)) {
| ^ cannot borrow as mutable

error[E0596]: cannot borrow data in a `&` reference as mutable
--> $DIR/borrowck-errors.rs:42:11
--> $DIR/borrowck-errors.rs:46:11
|
LL | let &[x] = &&mut [0];
| ^ cannot borrow as mutable

error[E0508]: cannot move out of type `[&mut i32; 1]`, a non-copy array
--> $DIR/borrowck-errors.rs:46:20
--> $DIR/borrowck-errors.rs:50:20
|
LL | let [&mut x] = &mut [&mut 0];
| - ^^^^^^^^^^^^^ cannot move out of here
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
//@ revisions: stable2021 classic2024 structural2024
//@ revisions: stable2021 classic2021 structural2021 classic2024 structural2024
//@[stable2021] edition: 2021
//@[classic2021] edition: 2021
//@[structural2021] edition: 2021
//@[classic2024] edition: 2024
//@[structural2024] edition: 2024
//! Tests for pattern errors not handled by the pattern typing rules, but by borrowck.
#![allow(incomplete_features)]
#![cfg_attr(classic2024, feature(ref_pat_eat_one_layer_2024))]
#![cfg_attr(structural2024, feature(ref_pat_eat_one_layer_2024_structural))]
#![cfg_attr(any(classic2021, classic2024), feature(ref_pat_eat_one_layer_2024))]
#![cfg_attr(any(structural2021, structural2024), feature(ref_pat_eat_one_layer_2024_structural))]

/// These patterns additionally use `&` to match a `&mut` reference type, which causes compilation
/// to fail in HIR typeck on stable. As such, they need to be separate from the other tests.
Expand All @@ -14,13 +16,15 @@ fn errors_caught_in_hir_typeck_on_stable() {
//[stable2021]~^ mismatched types
//[stable2021]~| types differ in mutability
//[classic2024]~^^^ ERROR: cannot move out of type
let _: &u32 = x;
#[cfg(any(classic2021, structural2021))] let _: u32 = x;
#[cfg(structural2024)] let _: &u32 = x;

let [&x] = &mut [&mut 0];
//[stable2021]~^ mismatched types
//[stable2021]~| types differ in mutability
//[classic2024]~^^^ ERROR: cannot move out of type
let _: &u32 = x;
#[cfg(any(classic2021, structural2021))] let _: u32 = x;
#[cfg(structural2024)] let _: &u32 = x;
}

pub fn main() {
Expand All @@ -35,16 +39,16 @@ pub fn main() {
// For 2021 edition, this is also a regression test for #136223
// since the maximum mutability is downgraded during the pattern check process.
if let &Some(Some(x)) = &Some(&mut Some(0)) {
//[stable2021,classic2024]~^ ERROR: cannot borrow data in a `&` reference as mutable
let _: &u32 = x;
//[stable2021,classic2021,classic2024]~^ ERROR: cannot borrow data in a `&` reference as mutable
#[cfg(any(structural2021, structural2024))] let _: &u32 = x;
}

let &[x] = &&mut [0];
//[stable2021,classic2024]~^ ERROR: cannot borrow data in a `&` reference as mutable
let _: &u32 = x;
//[stable2021,classic2021,classic2024]~^ ERROR: cannot borrow data in a `&` reference as mutable
#[cfg(any(structural2021, structural2024))] let _: &u32 = x;

let [&mut x] = &mut [&mut 0];
//[classic2024]~^ ERROR: cannot move out of type
#[cfg(stable2021)] let _: u32 = x;
#[cfg(any(stable2021, classic2021, structural2021))] let _: u32 = x;
#[cfg(structural2024)] let _: &mut u32 = x;
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
error[E0308]: mismatched types
--> $DIR/borrowck-errors.rs:13:10
--> $DIR/borrowck-errors.rs:15:10
|
LL | let [&x] = &[&mut 0];
| ^^ --------- this expression has type `&[&mut {integer}; 1]`
Expand All @@ -15,7 +15,7 @@ LL + let [x] = &[&mut 0];
|

error[E0308]: mismatched types
--> $DIR/borrowck-errors.rs:19:10
--> $DIR/borrowck-errors.rs:22:10
|
LL | let [&x] = &mut [&mut 0];
| ^^ ------------- this expression has type `&mut [&mut {integer}; 1]`
Expand All @@ -31,7 +31,7 @@ LL + let [x] = &mut [&mut 0];
|

error[E0507]: cannot move out of a shared reference
--> $DIR/borrowck-errors.rs:27:29
--> $DIR/borrowck-errors.rs:31:29
|
LL | if let Some(&Some(x)) = Some(&Some(&mut 0)) {
| - ^^^^^^^^^^^^^^^^^^^
Expand All @@ -46,19 +46,19 @@ LL + if let Some(Some(x)) = Some(&Some(&mut 0)) {
|

error[E0596]: cannot borrow data in a `&` reference as mutable
--> $DIR/borrowck-errors.rs:32:10
--> $DIR/borrowck-errors.rs:36:10
|
LL | let &ref mut x = &0;
| ^^^^^^^^^ cannot borrow as mutable

error[E0596]: cannot borrow data in a `&` reference as mutable
--> $DIR/borrowck-errors.rs:37:23
--> $DIR/borrowck-errors.rs:41:23
|
LL | if let &Some(Some(x)) = &Some(&mut Some(0)) {
| ^ cannot borrow as mutable

error[E0596]: cannot borrow data in a `&` reference as mutable
--> $DIR/borrowck-errors.rs:42:11
--> $DIR/borrowck-errors.rs:46:11
|
LL | let &[x] = &&mut [0];
| ^ cannot borrow as mutable
Expand Down
Loading

0 comments on commit 659838e

Please sign in to comment.