Skip to content

Commit

Permalink
Auto merge of rust-lang#127008 - Jules-Bertholet:tc-ergonomics, r=Nad…
Browse files Browse the repository at this point in the history
…rieril

Match ergonomics 2024: Implement TC's match ergonomics proposal

Under gate `ref_pat_eat_one_layer_2024_structural`. Enabling `ref_pat_eat_one_layer_2024` at the same time allows the union of what the individual gates allow. `@traviscross`

r? `@Nadrieril`

cc rust-lang#123076

`@rustbot` label A-edition-2024 A-patterns
  • Loading branch information
bors committed Jul 5, 2024
2 parents d2e6cf7 + e09815f commit 2ad6630
Show file tree
Hide file tree
Showing 12 changed files with 458 additions and 80 deletions.
1 change: 1 addition & 0 deletions compiler/rustc_ast/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -714,6 +714,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 @@ -575,6 +575,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 @@ -2123,7 +2128,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 @@ -2138,32 +2149,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 @@ -2178,8 +2206,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 @@ -1513,6 +1513,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

0 comments on commit 2ad6630

Please sign in to comment.