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: Implement TC's match ergonomics proposal #127008

Merged
merged 3 commits into from
Jul 5, 2024
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
1 change: 1 addition & 0 deletions compiler/rustc_ast/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -711,6 +711,7 @@ pub enum ByRef {
}

impl ByRef {
#[must_use]
pub fn cap_ref_mutability(mut self, mutbl: Mutability) -> Self {
if let ByRef::Yes(old_mutbl) = &mut self {
*old_mutbl = cmp::min(*old_mutbl, mutbl);
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_feature/src/unstable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -573,6 +573,8 @@ declare_features! (
(unstable, raw_ref_op, "1.41.0", Some(64490)),
/// Makes `&` and `&mut` patterns eat only one layer of references in Rust 2024.
(incomplete, ref_pat_eat_one_layer_2024, "1.79.0", Some(123076)),
/// Makes `&` and `&mut` patterns eat only one layer of references in Rust 2024—structural variant
(incomplete, ref_pat_eat_one_layer_2024_structural, "CURRENT_RUSTC_VERSION", Some(123076)),
/// Allows using the `#[register_tool]` attribute.
(unstable, register_tool, "1.41.0", Some(66079)),
/// Allows the `#[repr(i128)]` attribute for enums.
Expand Down
84 changes: 55 additions & 29 deletions compiler/rustc_hir_typeck/src/pat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -328,8 +328,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
adjust_mode: AdjustMode,
max_ref_mutbl: MutblCap,
) -> (Ty<'tcx>, ByRef, MutblCap) {
if let ByRef::Yes(Mutability::Mut) = def_br {
debug_assert!(max_ref_mutbl == MutblCap::Mut);
#[cfg(debug_assertions)]
if def_br == ByRef::Yes(Mutability::Mut) && max_ref_mutbl != MutblCap::Mut {
span_bug!(pat.span, "Pattern mutability cap violated!");
}
match adjust_mode {
AdjustMode::Pass => (expected, def_br, max_ref_mutbl),
Expand Down Expand Up @@ -437,7 +438,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
});
}

if self.tcx.features().ref_pat_eat_one_layer_2024 {
let features = self.tcx.features();
if features.ref_pat_eat_one_layer_2024 || features.ref_pat_eat_one_layer_2024_structural {
def_br = def_br.cap_ref_mutability(max_ref_mutbl.as_mutbl());
if def_br == ByRef::Yes(Mutability::Not) {
max_ref_mutbl = MutblCap::Not;
Expand Down Expand Up @@ -669,7 +671,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// Determine the binding mode...
let bm = match user_bind_annot {
BindingMode(ByRef::No, Mutability::Mut) if matches!(def_br, ByRef::Yes(_)) => {
if pat.span.at_least_rust_2024() && self.tcx.features().ref_pat_eat_one_layer_2024 {
if pat.span.at_least_rust_2024()
&& (self.tcx.features().ref_pat_eat_one_layer_2024
|| self.tcx.features().ref_pat_eat_one_layer_2024_structural)
{
if !self.tcx.features().mut_ref {
feature_err(
&self.tcx.sess,
Expand Down Expand Up @@ -2122,7 +2127,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
mut expected: Ty<'tcx>,
mut pat_info: PatInfo<'tcx, '_>,
) -> Ty<'tcx> {
let no_ref_mut_behind_and = self.tcx.features().ref_pat_eat_one_layer_2024;
let tcx = self.tcx;
let features = tcx.features();
let ref_pat_eat_one_layer_2024 = features.ref_pat_eat_one_layer_2024;
let ref_pat_eat_one_layer_2024_structural = features.ref_pat_eat_one_layer_2024_structural;

let no_ref_mut_behind_and =
ref_pat_eat_one_layer_2024 || ref_pat_eat_one_layer_2024_structural;
let new_match_ergonomics = pat.span.at_least_rust_2024() && no_ref_mut_behind_and;

let pat_prefix_span =
Expand All @@ -2137,32 +2148,49 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
pat_info.max_ref_mutbl = MutblCap::Mut;
}

expected = self.try_structurally_resolve_type(pat.span, expected);
if new_match_ergonomics {
if let ByRef::Yes(inh_mut) = pat_info.binding_mode {
// ref pattern consumes inherited reference

if pat_mutbl > inh_mut {
// Tried to match inherited `ref` with `&mut`, which is an error
let err_msg = "cannot match inherited `&` with `&mut` pattern";
let err = if let Some(span) = pat_prefix_span {
let mut err = self.dcx().struct_span_err(span, err_msg);
err.span_suggestion_verbose(
span,
"replace this `&mut` pattern with `&`",
"&",
Applicability::MachineApplicable,
);
err
if !ref_pat_eat_one_layer_2024 && let ty::Ref(_, _, r_mutbl) = *expected.kind() {
// Don't attempt to consume inherited reference
pat_info.binding_mode = pat_info.binding_mode.cap_ref_mutability(r_mutbl);
} else {
// ref pattern attempts to consume inherited reference
if pat_mutbl > inh_mut {
// Tried to match inherited `ref` with `&mut`
if !ref_pat_eat_one_layer_2024_structural {
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();

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;
}
} else {
self.dcx().struct_span_err(pat.span, err_msg)
};
err.emit();
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;
}
}

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;
}
} else {
// Reset binding mode on old editions
Expand All @@ -2177,8 +2205,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}

let tcx = self.tcx;
expected = self.try_structurally_resolve_type(pat.span, expected);
let (ref_ty, inner_ty) = match self.check_dereferenceable(pat.span, expected, inner) {
Ok(()) => {
// `demand::subtype` would be good enough, but using `eqtype` turns
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1502,6 +1502,7 @@ symbols! {
recursion_limit,
reexport_test_harness_main,
ref_pat_eat_one_layer_2024,
ref_pat_eat_one_layer_2024_structural,
ref_pat_everywhere,
ref_unwind_safe_trait,
reference,
Expand Down
14 changes: 7 additions & 7 deletions src/tools/tidy/src/features.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ pub fn check(
let file = entry.path();
let filename = file.file_name().unwrap().to_string_lossy();
let filen_underscore = filename.replace('-', "_").replace(".rs", "");
let filename_is_gate_test = test_filen_gate(&filen_underscore, &mut features);
let filename_gate = test_filen_gate(&filen_underscore, &mut features);

for (i, line) in contents.lines().enumerate() {
let mut err = |msg: &str| {
Expand All @@ -128,7 +128,7 @@ pub fn check(
};
match features.get_mut(feature_name) {
Some(f) => {
if filename_is_gate_test {
if filename_gate == Some(feature_name) {
err(&format!(
"The file is already marked as gate test \
through its name, no need for a \
Expand Down Expand Up @@ -259,18 +259,18 @@ fn find_attr_val<'a>(line: &'a str, attr: &str) -> Option<&'a str> {
r.captures(line).and_then(|c| c.get(1)).map(|m| m.as_str())
}

fn test_filen_gate(filen_underscore: &str, features: &mut Features) -> bool {
fn test_filen_gate<'f>(filen_underscore: &'f str, features: &mut Features) -> Option<&'f str> {
let prefix = "feature_gate_";
if filen_underscore.starts_with(prefix) {
if let Some(suffix) = filen_underscore.strip_prefix(prefix) {
for (n, f) in features.iter_mut() {
// Equivalent to filen_underscore == format!("feature_gate_{n}")
if &filen_underscore[prefix.len()..] == n {
if suffix == n {
f.has_gate_test = true;
return true;
return Some(suffix);
}
}
}
false
None
}

pub fn collect_lang_features(base_compiler_path: &Path, bad: &mut bool) -> Features {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//@ edition: 2024
//@ compile-flags: -Zunstable-options
// gate-test-ref_pat_eat_one_layer_2024_structural

pub fn main() {
if let Some(Some(&x)) = &Some(&Some(0)) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
error[E0308]: mismatched types
--> $DIR/feature-gate-ref_pat_eat_one_layer_2024.rs:5:22
--> $DIR/feature-gate-ref_pat_eat_one_layer_2024.rs:6:22
|
LL | if let Some(Some(&x)) = &Some(&Some(0)) {
| ^^ --------------- this expression has type `&Option<&Option<{integer}>>`
Expand All @@ -14,7 +14,7 @@ LL | if let Some(Some(x)) = &Some(&Some(0)) {
| ~

error[E0308]: mismatched types
--> $DIR/feature-gate-ref_pat_eat_one_layer_2024.rs:10:23
--> $DIR/feature-gate-ref_pat_eat_one_layer_2024.rs:11:23
|
LL | let _: &u32 = x;
| ---- ^ expected `&u32`, found integer
Expand All @@ -27,7 +27,7 @@ LL | let _: &u32 = &x;
| +

error[E0308]: mismatched types
--> $DIR/feature-gate-ref_pat_eat_one_layer_2024.rs:13:23
--> $DIR/feature-gate-ref_pat_eat_one_layer_2024.rs:14:23
|
LL | if let Some(Some(&&x)) = &Some(Some(&0)) {
| ^^ --------------- this expression has type `&Option<Option<&{integer}>>`
Expand All @@ -43,7 +43,7 @@ LL + if let Some(Some(&x)) = &Some(Some(&0)) {
|

error[E0308]: mismatched types
--> $DIR/feature-gate-ref_pat_eat_one_layer_2024.rs:17:17
--> $DIR/feature-gate-ref_pat_eat_one_layer_2024.rs:18:17
|
LL | if let Some(&Some(x)) = &Some(Some(0)) {
| ^^^^^^^^ -------------- this expression has type `&Option<Option<{integer}>>`
Expand All @@ -54,7 +54,7 @@ LL | if let Some(&Some(x)) = &Some(Some(0)) {
found reference `&_`

error[E0308]: mismatched types
--> $DIR/feature-gate-ref_pat_eat_one_layer_2024.rs:21:22
--> $DIR/feature-gate-ref_pat_eat_one_layer_2024.rs:22:22
|
LL | if let Some(Some(&mut x)) = &mut Some(&mut Some(0)) {
| ^^^^^^ ----------------------- this expression has type `&mut Option<&mut Option<{integer}>>`
Expand All @@ -64,7 +64,7 @@ LL | if let Some(Some(&mut x)) = &mut Some(&mut Some(0)) {
= note: expected type `{integer}`
found mutable reference `&mut _`
note: to declare a mutable binding use: `mut x`
--> $DIR/feature-gate-ref_pat_eat_one_layer_2024.rs:21:22
--> $DIR/feature-gate-ref_pat_eat_one_layer_2024.rs:22:22
|
LL | if let Some(Some(&mut x)) = &mut Some(&mut Some(0)) {
| ^^^^^^
Expand All @@ -74,7 +74,7 @@ LL | if let Some(Some(x)) = &mut Some(&mut Some(0)) {
| ~

error[E0308]: mismatched types
--> $DIR/feature-gate-ref_pat_eat_one_layer_2024.rs:25:22
--> $DIR/feature-gate-ref_pat_eat_one_layer_2024.rs:26:22
|
LL | if let Some(Some(&x)) = &Some(&Some(0)) {
| ^^ --------------- this expression has type `&Option<&Option<{integer}>>`
Expand All @@ -89,7 +89,7 @@ LL | if let Some(Some(x)) = &Some(&Some(0)) {
| ~

error[E0308]: mismatched types
--> $DIR/feature-gate-ref_pat_eat_one_layer_2024.rs:29:27
--> $DIR/feature-gate-ref_pat_eat_one_layer_2024.rs:30:27
|
LL | if let Some(&mut Some(&x)) = &Some(&mut Some(0)) {
| ^^ ------------------- this expression has type `&Option<&mut Option<{integer}>>`
Expand All @@ -104,7 +104,7 @@ LL | if let Some(&mut Some(x)) = &Some(&mut Some(0)) {
| ~

error[E0308]: mismatched types
--> $DIR/feature-gate-ref_pat_eat_one_layer_2024.rs:33:23
--> $DIR/feature-gate-ref_pat_eat_one_layer_2024.rs:34:23
|
LL | if let Some(&Some(&mut x)) = &mut Some(&Some(0)) {
| ^^^^^^ ------------------- this expression has type `&mut Option<&Option<{integer}>>`
Expand All @@ -114,7 +114,7 @@ LL | if let Some(&Some(&mut x)) = &mut Some(&Some(0)) {
= note: expected type `{integer}`
found mutable reference `&mut _`
note: to declare a mutable binding use: `mut x`
--> $DIR/feature-gate-ref_pat_eat_one_layer_2024.rs:33:23
--> $DIR/feature-gate-ref_pat_eat_one_layer_2024.rs:34:23
|
LL | if let Some(&Some(&mut x)) = &mut Some(&Some(0)) {
| ^^^^^^
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
//@ run-pass
//@ edition: 2024
//@ compile-flags: -Zunstable-options
//@ revisions: classic structural both
#![allow(incomplete_features)]
#![feature(ref_pat_eat_one_layer_2024)]
#![cfg_attr(any(classic, both), feature(ref_pat_eat_one_layer_2024))]
#![cfg_attr(any(structural, both), feature(ref_pat_eat_one_layer_2024_structural))]

pub fn main() {
if let Some(Some(&x)) = &Some(&Some(0)) {
Expand Down Expand Up @@ -53,4 +55,12 @@ pub fn main() {
if let Some(&Some(x)) = &mut Some(Some(0)) {
let _: u32 = x;
}
#[cfg(any(classic, both))]
if let Some(&mut x) = &mut Some(&0) {
let _: &u32 = x;
}
#[cfg(any(structural, both))]
if let Some(&mut x) = &Some(&mut 0) {
let _: &u32 = x;
}
}
Loading
Loading