Skip to content

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

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Feb 20, 2025
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
Loading