Skip to content

Commit 2ad6630

Browse files
committed
Auto merge of rust-lang#127008 - Jules-Bertholet:tc-ergonomics, r=Nadrieril
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
2 parents d2e6cf7 + e09815f commit 2ad6630

12 files changed

+458
-80
lines changed

compiler/rustc_ast/src/ast.rs

+1
Original file line numberDiff line numberDiff line change
@@ -714,6 +714,7 @@ pub enum ByRef {
714714
}
715715

716716
impl ByRef {
717+
#[must_use]
717718
pub fn cap_ref_mutability(mut self, mutbl: Mutability) -> Self {
718719
if let ByRef::Yes(old_mutbl) = &mut self {
719720
*old_mutbl = cmp::min(*old_mutbl, mutbl);

compiler/rustc_feature/src/unstable.rs

+2
Original file line numberDiff line numberDiff line change
@@ -575,6 +575,8 @@ declare_features! (
575575
(unstable, raw_ref_op, "1.41.0", Some(64490)),
576576
/// Makes `&` and `&mut` patterns eat only one layer of references in Rust 2024.
577577
(incomplete, ref_pat_eat_one_layer_2024, "1.79.0", Some(123076)),
578+
/// Makes `&` and `&mut` patterns eat only one layer of references in Rust 2024—structural variant
579+
(incomplete, ref_pat_eat_one_layer_2024_structural, "CURRENT_RUSTC_VERSION", Some(123076)),
578580
/// Allows using the `#[register_tool]` attribute.
579581
(unstable, register_tool, "1.41.0", Some(66079)),
580582
/// Allows the `#[repr(i128)]` attribute for enums.

compiler/rustc_hir_typeck/src/pat.rs

+55-29
Original file line numberDiff line numberDiff line change
@@ -328,8 +328,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
328328
adjust_mode: AdjustMode,
329329
max_ref_mutbl: MutblCap,
330330
) -> (Ty<'tcx>, ByRef, MutblCap) {
331-
if let ByRef::Yes(Mutability::Mut) = def_br {
332-
debug_assert!(max_ref_mutbl == MutblCap::Mut);
331+
#[cfg(debug_assertions)]
332+
if def_br == ByRef::Yes(Mutability::Mut) && max_ref_mutbl != MutblCap::Mut {
333+
span_bug!(pat.span, "Pattern mutability cap violated!");
333334
}
334335
match adjust_mode {
335336
AdjustMode::Pass => (expected, def_br, max_ref_mutbl),
@@ -437,7 +438,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
437438
});
438439
}
439440

440-
if self.tcx.features().ref_pat_eat_one_layer_2024 {
441+
let features = self.tcx.features();
442+
if features.ref_pat_eat_one_layer_2024 || features.ref_pat_eat_one_layer_2024_structural {
441443
def_br = def_br.cap_ref_mutability(max_ref_mutbl.as_mutbl());
442444
if def_br == ByRef::Yes(Mutability::Not) {
443445
max_ref_mutbl = MutblCap::Not;
@@ -669,7 +671,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
669671
// Determine the binding mode...
670672
let bm = match user_bind_annot {
671673
BindingMode(ByRef::No, Mutability::Mut) if matches!(def_br, ByRef::Yes(_)) => {
672-
if pat.span.at_least_rust_2024() && self.tcx.features().ref_pat_eat_one_layer_2024 {
674+
if pat.span.at_least_rust_2024()
675+
&& (self.tcx.features().ref_pat_eat_one_layer_2024
676+
|| self.tcx.features().ref_pat_eat_one_layer_2024_structural)
677+
{
673678
if !self.tcx.features().mut_ref {
674679
feature_err(
675680
&self.tcx.sess,
@@ -2123,7 +2128,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
21232128
mut expected: Ty<'tcx>,
21242129
mut pat_info: PatInfo<'tcx, '_>,
21252130
) -> Ty<'tcx> {
2126-
let no_ref_mut_behind_and = self.tcx.features().ref_pat_eat_one_layer_2024;
2131+
let tcx = self.tcx;
2132+
let features = tcx.features();
2133+
let ref_pat_eat_one_layer_2024 = features.ref_pat_eat_one_layer_2024;
2134+
let ref_pat_eat_one_layer_2024_structural = features.ref_pat_eat_one_layer_2024_structural;
2135+
2136+
let no_ref_mut_behind_and =
2137+
ref_pat_eat_one_layer_2024 || ref_pat_eat_one_layer_2024_structural;
21272138
let new_match_ergonomics = pat.span.at_least_rust_2024() && no_ref_mut_behind_and;
21282139

21292140
let pat_prefix_span =
@@ -2138,32 +2149,49 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
21382149
pat_info.max_ref_mutbl = MutblCap::Mut;
21392150
}
21402151

2152+
expected = self.try_structurally_resolve_type(pat.span, expected);
21412153
if new_match_ergonomics {
21422154
if let ByRef::Yes(inh_mut) = pat_info.binding_mode {
2143-
// ref pattern consumes inherited reference
2144-
2145-
if pat_mutbl > inh_mut {
2146-
// Tried to match inherited `ref` with `&mut`, which is an error
2147-
let err_msg = "cannot match inherited `&` with `&mut` pattern";
2148-
let err = if let Some(span) = pat_prefix_span {
2149-
let mut err = self.dcx().struct_span_err(span, err_msg);
2150-
err.span_suggestion_verbose(
2151-
span,
2152-
"replace this `&mut` pattern with `&`",
2153-
"&",
2154-
Applicability::MachineApplicable,
2155-
);
2156-
err
2155+
if !ref_pat_eat_one_layer_2024 && let ty::Ref(_, _, r_mutbl) = *expected.kind() {
2156+
// Don't attempt to consume inherited reference
2157+
pat_info.binding_mode = pat_info.binding_mode.cap_ref_mutability(r_mutbl);
2158+
} else {
2159+
// ref pattern attempts to consume inherited reference
2160+
if pat_mutbl > inh_mut {
2161+
// Tried to match inherited `ref` with `&mut`
2162+
if !ref_pat_eat_one_layer_2024_structural {
2163+
let err_msg = "mismatched types";
2164+
let err = if let Some(span) = pat_prefix_span {
2165+
let mut err = self.dcx().struct_span_err(span, err_msg);
2166+
err.code(E0308);
2167+
err.note("cannot match inherited `&` with `&mut` pattern");
2168+
err.span_suggestion_verbose(
2169+
span,
2170+
"replace this `&mut` pattern with `&`",
2171+
"&",
2172+
Applicability::MachineApplicable,
2173+
);
2174+
err
2175+
} else {
2176+
self.dcx().struct_span_err(pat.span, err_msg)
2177+
};
2178+
err.emit();
2179+
2180+
pat_info.binding_mode = ByRef::No;
2181+
self.typeck_results
2182+
.borrow_mut()
2183+
.skipped_ref_pats_mut()
2184+
.insert(pat.hir_id);
2185+
self.check_pat(inner, expected, pat_info);
2186+
return expected;
2187+
}
21572188
} else {
2158-
self.dcx().struct_span_err(pat.span, err_msg)
2159-
};
2160-
err.emit();
2189+
pat_info.binding_mode = ByRef::No;
2190+
self.typeck_results.borrow_mut().skipped_ref_pats_mut().insert(pat.hir_id);
2191+
self.check_pat(inner, expected, pat_info);
2192+
return expected;
2193+
}
21612194
}
2162-
2163-
pat_info.binding_mode = ByRef::No;
2164-
self.typeck_results.borrow_mut().skipped_ref_pats_mut().insert(pat.hir_id);
2165-
self.check_pat(inner, expected, pat_info);
2166-
return expected;
21672195
}
21682196
} else {
21692197
// Reset binding mode on old editions
@@ -2178,8 +2206,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
21782206
}
21792207
}
21802208

2181-
let tcx = self.tcx;
2182-
expected = self.try_structurally_resolve_type(pat.span, expected);
21832209
let (ref_ty, inner_ty) = match self.check_dereferenceable(pat.span, expected, inner) {
21842210
Ok(()) => {
21852211
// `demand::subtype` would be good enough, but using `eqtype` turns

compiler/rustc_span/src/symbol.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1513,6 +1513,7 @@ symbols! {
15131513
recursion_limit,
15141514
reexport_test_harness_main,
15151515
ref_pat_eat_one_layer_2024,
1516+
ref_pat_eat_one_layer_2024_structural,
15161517
ref_pat_everywhere,
15171518
ref_unwind_safe_trait,
15181519
reference,

src/tools/tidy/src/features.rs

+7-7
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ pub fn check(
112112
let file = entry.path();
113113
let filename = file.file_name().unwrap().to_string_lossy();
114114
let filen_underscore = filename.replace('-', "_").replace(".rs", "");
115-
let filename_is_gate_test = test_filen_gate(&filen_underscore, &mut features);
115+
let filename_gate = test_filen_gate(&filen_underscore, &mut features);
116116

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

262-
fn test_filen_gate(filen_underscore: &str, features: &mut Features) -> bool {
262+
fn test_filen_gate<'f>(filen_underscore: &'f str, features: &mut Features) -> Option<&'f str> {
263263
let prefix = "feature_gate_";
264-
if filen_underscore.starts_with(prefix) {
264+
if let Some(suffix) = filen_underscore.strip_prefix(prefix) {
265265
for (n, f) in features.iter_mut() {
266266
// Equivalent to filen_underscore == format!("feature_gate_{n}")
267-
if &filen_underscore[prefix.len()..] == n {
267+
if suffix == n {
268268
f.has_gate_test = true;
269-
return true;
269+
return Some(suffix);
270270
}
271271
}
272272
}
273-
false
273+
None
274274
}
275275

276276
pub fn collect_lang_features(base_compiler_path: &Path, bad: &mut bool) -> Features {

tests/ui/match/ref_pat_eat_one_layer_2024/feature-gate-ref_pat_eat_one_layer_2024.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
//@ edition: 2024
22
//@ compile-flags: -Zunstable-options
3+
// gate-test-ref_pat_eat_one_layer_2024_structural
34

45
pub fn main() {
56
if let Some(Some(&x)) = &Some(&Some(0)) {

tests/ui/match/ref_pat_eat_one_layer_2024/feature-gate-ref_pat_eat_one_layer_2024.stderr

+10-10
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error[E0308]: mismatched types
2-
--> $DIR/feature-gate-ref_pat_eat_one_layer_2024.rs:5:22
2+
--> $DIR/feature-gate-ref_pat_eat_one_layer_2024.rs:6:22
33
|
44
LL | if let Some(Some(&x)) = &Some(&Some(0)) {
55
| ^^ --------------- this expression has type `&Option<&Option<{integer}>>`
@@ -14,7 +14,7 @@ LL | if let Some(Some(x)) = &Some(&Some(0)) {
1414
| ~
1515

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

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

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

5656
error[E0308]: mismatched types
57-
--> $DIR/feature-gate-ref_pat_eat_one_layer_2024.rs:21:22
57+
--> $DIR/feature-gate-ref_pat_eat_one_layer_2024.rs:22:22
5858
|
5959
LL | if let Some(Some(&mut x)) = &mut Some(&mut Some(0)) {
6060
| ^^^^^^ ----------------------- this expression has type `&mut Option<&mut Option<{integer}>>`
@@ -64,7 +64,7 @@ LL | if let Some(Some(&mut x)) = &mut Some(&mut Some(0)) {
6464
= note: expected type `{integer}`
6565
found mutable reference `&mut _`
6666
note: to declare a mutable binding use: `mut x`
67-
--> $DIR/feature-gate-ref_pat_eat_one_layer_2024.rs:21:22
67+
--> $DIR/feature-gate-ref_pat_eat_one_layer_2024.rs:22:22
6868
|
6969
LL | if let Some(Some(&mut x)) = &mut Some(&mut Some(0)) {
7070
| ^^^^^^
@@ -74,7 +74,7 @@ LL | if let Some(Some(x)) = &mut Some(&mut Some(0)) {
7474
| ~
7575

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

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

106106
error[E0308]: mismatched types
107-
--> $DIR/feature-gate-ref_pat_eat_one_layer_2024.rs:33:23
107+
--> $DIR/feature-gate-ref_pat_eat_one_layer_2024.rs:34:23
108108
|
109109
LL | if let Some(&Some(&mut x)) = &mut Some(&Some(0)) {
110110
| ^^^^^^ ------------------- this expression has type `&mut Option<&Option<{integer}>>`
@@ -114,7 +114,7 @@ LL | if let Some(&Some(&mut x)) = &mut Some(&Some(0)) {
114114
= note: expected type `{integer}`
115115
found mutable reference `&mut _`
116116
note: to declare a mutable binding use: `mut x`
117-
--> $DIR/feature-gate-ref_pat_eat_one_layer_2024.rs:33:23
117+
--> $DIR/feature-gate-ref_pat_eat_one_layer_2024.rs:34:23
118118
|
119119
LL | if let Some(&Some(&mut x)) = &mut Some(&Some(0)) {
120120
| ^^^^^^

tests/ui/match/ref_pat_eat_one_layer_2024/ref_pat_eat_one_layer_2024.rs

+11-1
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
//@ run-pass
22
//@ edition: 2024
33
//@ compile-flags: -Zunstable-options
4+
//@ revisions: classic structural both
45
#![allow(incomplete_features)]
5-
#![feature(ref_pat_eat_one_layer_2024)]
6+
#![cfg_attr(any(classic, both), feature(ref_pat_eat_one_layer_2024))]
7+
#![cfg_attr(any(structural, both), feature(ref_pat_eat_one_layer_2024_structural))]
68

79
pub fn main() {
810
if let Some(Some(&x)) = &Some(&Some(0)) {
@@ -53,4 +55,12 @@ pub fn main() {
5355
if let Some(&Some(x)) = &mut Some(Some(0)) {
5456
let _: u32 = x;
5557
}
58+
#[cfg(any(classic, both))]
59+
if let Some(&mut x) = &mut Some(&0) {
60+
let _: &u32 = x;
61+
}
62+
#[cfg(any(structural, both))]
63+
if let Some(&mut x) = &Some(&mut 0) {
64+
let _: &u32 = x;
65+
}
5666
}

0 commit comments

Comments
 (0)