Skip to content
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

Match Ergonomics 2024: update edition 2024 behavior of feature gates #135434

Merged
merged 12 commits into from
Jan 30, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 53 additions & 33 deletions compiler/rustc_hir_typeck/src/pat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -243,8 +243,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
fn downgrade_mut_inside_shared(&self) -> bool {
// NB: RFC 3627 proposes stabilizing Rule 3 in all editions. If we adopt the same behavior
// across all editions, this may be removed.
self.tcx.features().ref_pat_eat_one_layer_2024()
|| self.tcx.features().ref_pat_eat_one_layer_2024_structural()
self.tcx.features().ref_pat_eat_one_layer_2024_structural()
}

/// Experimental pattern feature: when do reference patterns match against inherited references?
Expand Down Expand Up @@ -425,7 +424,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
max_ref_mutbl: MutblCap,
) -> (Ty<'tcx>, ByRef, MutblCap) {
#[cfg(debug_assertions)]
if def_br == ByRef::Yes(Mutability::Mut) && max_ref_mutbl != MutblCap::Mut {
if def_br == ByRef::Yes(Mutability::Mut)
&& max_ref_mutbl != MutblCap::Mut
&& self.downgrade_mut_inside_shared()
{
span_bug!(pat.span, "Pattern mutability cap violated!");
}
match adjust_mode {
Expand Down Expand Up @@ -2316,22 +2318,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// (RFC 3627, Rule 5). If we implement a pattern typing ruleset with Rule 4E
// but not Rule 5, we'll need to check that here.
debug_assert!(ref_pat_matches_mut_ref);
let err_msg = "mismatched types";
let err = if let Some(span) = pat_prefix_span {
let mut err = self.dcx().struct_span_err(span, err_msg);
err.code(E0308);
err.note("cannot match inherited `&` with `&mut` pattern");
err.span_suggestion_verbose(
span,
"replace this `&mut` pattern with `&`",
"&",
Applicability::MachineApplicable,
);
err
} else {
self.dcx().struct_span_err(pat.span, err_msg)
};
err.emit();
self.error_inherited_ref_mutability_mismatch(pat, pat_prefix_span);
}

pat_info.binding_mode = ByRef::No;
Expand All @@ -2340,28 +2327,38 @@ 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
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A diagnostics note: currently this means if falling back to matching on the inherited ref fails, the error will be about failing to match an inherited & with a &mut pattern. I think it could use some rewording/explaining in general, but in particular if we adopt fallback-to-outer I think the message should also mention in some way that we first failed to match the inner & with the &mut pattern.

This is also the case for the fallback-to-outer variant of the edition 2021 rules (not in this PR).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you leave a // FIXME(ref_pat_eat_one_layer_2024_structural): explain that there was fallback in the error message?

{
// Match against the reference type; don't consume the inherited ref.
pat_info.binding_mode = pat_info.binding_mode.cap_ref_mutability(r_mutbl);
// 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.
debug_assert!(ref_pat_matches_mut_ref);
// 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.
debug_assert!(self.downgrade_mut_inside_shared());
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`. This will
// be a type error later, since we're matching a reference pattern
// against a non-reference type.
// We can't match an inherited shared reference with `&mut`.
// 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.
debug_assert!(ref_pat_matches_mut_ref);
} else {
pat_info.binding_mode = ByRef::No;
self.typeck_results
.borrow_mut()
.skipped_ref_pats_mut()
.insert(pat.hir_id);
self.check_pat(inner, expected, pat_info);
return expected;
self.error_inherited_ref_mutability_mismatch(pat, pat_prefix_span);
}

pat_info.binding_mode = ByRef::No;
self.typeck_results.borrow_mut().skipped_ref_pats_mut().insert(pat.hir_id);
self.check_pat(inner, expected, pat_info);
return expected;
}
}
InheritedRefMatchRule::EatBoth => {
Expand Down Expand Up @@ -2435,6 +2432,29 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
Ty::new_ref(self.tcx, region, ty, mutbl)
}

fn error_inherited_ref_mutability_mismatch(
&self,
pat: &'tcx Pat<'tcx>,
pat_prefix_span: Option<Span>,
) -> ErrorGuaranteed {
let err_msg = "mismatched types";
let err = if let Some(span) = pat_prefix_span {
let mut err = self.dcx().struct_span_err(span, err_msg);
err.code(E0308);
err.note("cannot match inherited `&` with `&mut` pattern");
err.span_suggestion_verbose(
span,
"replace this `&mut` pattern with `&`",
"&",
Applicability::MachineApplicable,
);
err
} else {
self.dcx().struct_span_err(pat.span, err_msg)
};
err.emit()
}

fn try_resolve_slice_ty_to_array_ty(
&self,
before: &'tcx [Pat<'tcx>],
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# `ref_pat_eat_one_layer_2024_structural`

The tracking issue for this feature is: [#123076]

[#123076]: https://github.com/rust-lang/rust/issues/123076

---

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 alternative experimental match ergonomics, see the feature
[`ref_pat_eat_one_layer_2024`](./ref-pat-eat-one-layer-2024.md).

[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
@@ -0,0 +1,19 @@
# `ref_pat_eat_one_layer_2024`

The tracking issue for this feature is: [#123076]

[#123076]: https://github.com/rust-lang/rust/issues/123076

---

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 alternative experimental match ergonomics, see the feature
[`ref_pat_eat_one_layer_2024_structural`](./ref-pat-eat-one-layer-2024-structural.md).

[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,79 @@
error[E0508]: cannot move out of type `[&mut u32; 1]`, a non-copy array
--> $DIR/borrowck-errors.rs:13: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
|
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
|
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
|
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
|
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:32: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:35: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:40: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:44:20
|
LL | let [&mut x] = &mut [&mut 0];
| - ^^^^^^^^^^^^^ cannot move out of here
| |
| data moved here
| move occurs because `x` has type `&mut i32`, which does not implement the `Copy` trait
|
help: consider borrowing the pattern binding
|
LL | let [&mut ref x] = &mut [&mut 0];
| +++

error: aborting due to 7 previous errors

Some errors have detailed explanations: E0507, E0508, E0596.
For more information about an error, try `rustc --explain E0507`.
Original file line number Diff line number Diff line change
@@ -1,9 +1,27 @@
//@ edition: 2024
//@ revisions: classic structural
//@ revisions: stable2021 classic2024 structural2024
//@[stable2021] 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(classic, feature(ref_pat_eat_one_layer_2024))]
#![cfg_attr(structural, feature(ref_pat_eat_one_layer_2024_structural))]
#![cfg_attr(classic2024, feature(ref_pat_eat_one_layer_2024))]
#![cfg_attr(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.
fn errors_caught_in_hir_typeck_on_stable() {
let [&x] = &[&mut 0];
//[stable2021]~^ mismatched types
//[stable2021]~| types differ in mutability
//[classic2024]~^^^ ERROR: cannot move out of type
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;
}

pub fn main() {
if let Some(&Some(x)) = Some(&Some(&mut 0)) {
Expand All @@ -13,4 +31,18 @@ pub fn main() {

let &ref mut x = &0;
//~^ cannot borrow data in a `&` reference as mutable [E0596]

if let &Some(Some(x)) = &Some(&mut Some(0)) {
//[stable2021,classic2024]~^ ERROR: cannot borrow data in a `&` reference as mutable
let _: &u32 = x;
}

let &[x] = &&mut [0];
//[stable2021,classic2024]~^ ERROR: cannot borrow data in a `&` reference as mutable
let _: &u32 = x;

let [&mut x] = &mut [&mut 0];
//[classic2024]~^ ERROR: cannot move out of type
#[cfg(stable2021)] let _: u32 = x;
#[cfg(structural2024)] let _: &mut u32 = x;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
error[E0308]: mismatched types
--> $DIR/borrowck-errors.rs:13:10
|
LL | let [&x] = &[&mut 0];
| ^^ --------- this expression has type `&[&mut {integer}; 1]`
| |
| types differ in mutability
|
= note: expected mutable reference `&mut {integer}`
found reference `&_`
help: consider removing `&` from the pattern
|
LL - let [&x] = &[&mut 0];
LL + let [x] = &[&mut 0];
|

error[E0308]: mismatched types
--> $DIR/borrowck-errors.rs:19:10
|
LL | let [&x] = &mut [&mut 0];
| ^^ ------------- this expression has type `&mut [&mut {integer}; 1]`
| |
| types differ in mutability
|
= note: expected mutable reference `&mut {integer}`
found reference `&_`
help: consider removing `&` from the pattern
|
LL - let [&x] = &mut [&mut 0];
LL + let [x] = &mut [&mut 0];
|

error[E0507]: cannot move out of a shared reference
--> $DIR/borrowck-errors.rs:27: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:32: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:35: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:40:11
|
LL | let &[x] = &&mut [0];
| ^ cannot borrow as mutable

error: aborting due to 6 previous errors

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

This file was deleted.

Loading
Loading