@@ -21,6 +21,7 @@ use rustc_middle::{bug, span_bug};
21
21
use rustc_session:: lint:: builtin:: NON_EXHAUSTIVE_OMITTED_PATTERNS ;
22
22
use rustc_session:: parse:: feature_err;
23
23
use rustc_span:: edit_distance:: find_best_match_for_name;
24
+ use rustc_span:: edition:: Edition ;
24
25
use rustc_span:: hygiene:: DesugaringKind ;
25
26
use rustc_span:: source_map:: Spanned ;
26
27
use rustc_span:: { BytePos , DUMMY_SP , Ident , Span , kw, sym} ;
@@ -213,7 +214,62 @@ impl MutblCap {
213
214
}
214
215
}
215
216
217
+ /// Variations on RFC 3627's Rule 4: when do reference patterns match against inherited references?
218
+ #[ derive( Clone , Copy , Debug , PartialEq , Eq ) ]
219
+ enum InheritedRefMatchRule {
220
+ /// Reference patterns try to consume the inherited reference first.
221
+ /// This assumes reference patterns can always match against an inherited reference.
222
+ EatOuter ,
223
+ /// Reference patterns consume inherited references if matching against a non-reference type.
224
+ /// This assumes reference patterns can always match against an inherited reference.
225
+ EatInner ,
226
+ /// Reference patterns consume both layers of reference.
227
+ /// Currently, this assumes the stable Rust behavior of not allowing reference patterns to eat
228
+ /// an inherited reference alone. This will need an additional field or variant to represent the
229
+ /// planned edition <= 2021 behavior of experimental match ergonomics, which does allow that.
230
+ EatBoth ,
231
+ }
232
+
216
233
impl < ' a , ' tcx > FnCtxt < ' a , ' tcx > {
234
+ /// Experimental pattern feature: after matching against a shared reference, do we limit the
235
+ /// default binding mode in subpatterns to be `ref` when it would otherwise be `ref mut`?
236
+ /// This corresponds to Rule 3 of RFC 3627.
237
+ fn downgrade_mut_inside_shared ( & self ) -> bool {
238
+ // NB: RFC 3627 proposes stabilizing Rule 3 in all editions. If we adopt the same behavior
239
+ // across all editions, this may be removed.
240
+ self . tcx . features ( ) . ref_pat_eat_one_layer_2024 ( )
241
+ || self . tcx . features ( ) . ref_pat_eat_one_layer_2024_structural ( )
242
+ }
243
+
244
+ /// Experimental pattern feature: when do reference patterns match against inherited references?
245
+ /// This corresponds to variations on Rule 4 of RFC 3627.
246
+ fn ref_pat_matches_inherited_ref ( & self , edition : Edition ) -> InheritedRefMatchRule {
247
+ // NB: The particular rule used here is likely to differ across editions, so calls to this
248
+ // may need to become edition checks after match ergonomics stabilize.
249
+ if edition. at_least_rust_2024 ( ) {
250
+ if self . tcx . features ( ) . ref_pat_eat_one_layer_2024 ( ) {
251
+ InheritedRefMatchRule :: EatOuter
252
+ } else if self . tcx . features ( ) . ref_pat_eat_one_layer_2024_structural ( ) {
253
+ InheritedRefMatchRule :: EatInner
254
+ } else {
255
+ // Currently, matching against an inherited ref on edition 2024 is an error.
256
+ // Use `EatBoth` as a fallback to be similar to stable Rust.
257
+ InheritedRefMatchRule :: EatBoth
258
+ }
259
+ } else {
260
+ InheritedRefMatchRule :: EatBoth
261
+ }
262
+ }
263
+
264
+ /// Experimental pattern feature: do `&` patterns match against `&mut` references, treating them
265
+ /// as if they were shared references? This corresponds to Rule 5 of RFC 3627.
266
+ fn ref_pat_matches_mut_ref ( & self ) -> bool {
267
+ // NB: RFC 3627 proposes stabilizing Rule 5 in all editions. If we adopt the same behavior
268
+ // across all editions, this may be removed.
269
+ self . tcx . features ( ) . ref_pat_eat_one_layer_2024 ( )
270
+ || self . tcx . features ( ) . ref_pat_eat_one_layer_2024_structural ( )
271
+ }
272
+
217
273
/// Type check the given top level pattern against the `expected` type.
218
274
///
219
275
/// If a `Some(span)` is provided and `origin_expr` holds,
@@ -474,9 +530,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
474
530
} ) ;
475
531
}
476
532
477
- let features = self . tcx . features ( ) ;
478
- if features. ref_pat_eat_one_layer_2024 ( ) || features. ref_pat_eat_one_layer_2024_structural ( )
479
- {
533
+ if self . downgrade_mut_inside_shared ( ) {
480
534
def_br = def_br. cap_ref_mutability ( max_ref_mutbl. as_mutbl ( ) ) ;
481
535
}
482
536
if def_br == ByRef :: Yes ( Mutability :: Not ) {
@@ -708,6 +762,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
708
762
// Determine the binding mode...
709
763
let bm = match user_bind_annot {
710
764
BindingMode ( ByRef :: No , Mutability :: Mut ) if matches ! ( def_br, ByRef :: Yes ( _) ) => {
765
+ // Only mention the experimental `mut_ref` feature if if we're in edition 2024 and
766
+ // using other experimental matching features compatible with it.
711
767
if pat. span . at_least_rust_2024 ( )
712
768
&& ( self . tcx . features ( ) . ref_pat_eat_one_layer_2024 ( )
713
769
|| self . tcx . features ( ) . ref_pat_eat_one_layer_2024_structural ( ) )
@@ -2205,51 +2261,68 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
2205
2261
mut pat_info : PatInfo < ' _ , ' tcx > ,
2206
2262
) -> Ty < ' tcx > {
2207
2263
let tcx = self . tcx ;
2208
- let features = tcx. features ( ) ;
2209
- let ref_pat_eat_one_layer_2024 = features. ref_pat_eat_one_layer_2024 ( ) ;
2210
- let ref_pat_eat_one_layer_2024_structural =
2211
- features. ref_pat_eat_one_layer_2024_structural ( ) ;
2212
-
2213
- let no_ref_mut_behind_and =
2214
- ref_pat_eat_one_layer_2024 || ref_pat_eat_one_layer_2024_structural;
2215
- let new_match_ergonomics = pat. span . at_least_rust_2024 ( ) && no_ref_mut_behind_and;
2216
2264
2217
2265
let pat_prefix_span =
2218
2266
inner. span . find_ancestor_inside ( pat. span ) . map ( |end| pat. span . until ( end) ) ;
2219
2267
2220
- if no_ref_mut_behind_and && pat_mutbl == Mutability :: Not {
2221
- // Prevent the inner pattern from binding with `ref mut`.
2268
+ let ref_pat_matches_mut_ref = self . ref_pat_matches_mut_ref ( ) ;
2269
+ if ref_pat_matches_mut_ref && pat_mutbl == Mutability :: Not {
2270
+ // If `&` patterns can match against mutable reference types (RFC 3627, Rule 5), we need
2271
+ // to prevent subpatterns from binding with `ref mut`. Subpatterns of a shared reference
2272
+ // pattern should have read-only access to the scrutinee, and the borrow checker won't
2273
+ // catch it in this case.
2222
2274
pat_info. max_ref_mutbl = pat_info. max_ref_mutbl . cap_to_weakly_not ( pat_prefix_span) ;
2223
2275
}
2224
2276
2225
2277
expected = self . try_structurally_resolve_type ( pat. span , expected) ;
2226
- if new_match_ergonomics {
2227
- if let ByRef :: Yes ( inh_mut) = pat_info. binding_mode {
2228
- if !ref_pat_eat_one_layer_2024 && let ty:: Ref ( _, _, r_mutbl) = * expected. kind ( ) {
2229
- // Don't attempt to consume inherited reference
2230
- pat_info. binding_mode = pat_info. binding_mode . cap_ref_mutability ( r_mutbl) ;
2231
- } else {
2278
+ // Determine whether we're consuming an inherited reference and resetting the default
2279
+ // binding mode, based on edition and enabled experimental features.
2280
+ if let ByRef :: Yes ( inh_mut) = pat_info. binding_mode {
2281
+ match self . ref_pat_matches_inherited_ref ( pat. span . edition ( ) ) {
2282
+ InheritedRefMatchRule :: EatOuter => {
2232
2283
// ref pattern attempts to consume inherited reference
2233
2284
if pat_mutbl > inh_mut {
2234
2285
// Tried to match inherited `ref` with `&mut`
2235
- if !ref_pat_eat_one_layer_2024_structural {
2236
- let err_msg = "mismatched types" ;
2237
- let err = if let Some ( span) = pat_prefix_span {
2238
- let mut err = self . dcx ( ) . struct_span_err ( span, err_msg) ;
2239
- err. code ( E0308 ) ;
2240
- err. note ( "cannot match inherited `&` with `&mut` pattern" ) ;
2241
- err. span_suggestion_verbose (
2242
- span,
2243
- "replace this `&mut` pattern with `&`" ,
2244
- "&" ,
2245
- Applicability :: MachineApplicable ,
2246
- ) ;
2247
- err
2248
- } else {
2249
- self . dcx ( ) . struct_span_err ( pat. span , err_msg)
2250
- } ;
2251
- err. emit ( ) ;
2286
+ // NB: This assumes that `&` patterns can match against mutable references
2287
+ // (RFC 3627, Rule 5). If we implement a pattern typing ruleset with Rule 4E
2288
+ // but not Rule 5, we'll need to check that here.
2289
+ let err_msg = "mismatched types" ;
2290
+ let err = if let Some ( span) = pat_prefix_span {
2291
+ let mut err = self . dcx ( ) . struct_span_err ( span, err_msg) ;
2292
+ err. code ( E0308 ) ;
2293
+ err. note ( "cannot match inherited `&` with `&mut` pattern" ) ;
2294
+ err. span_suggestion_verbose (
2295
+ span,
2296
+ "replace this `&mut` pattern with `&`" ,
2297
+ "&" ,
2298
+ Applicability :: MachineApplicable ,
2299
+ ) ;
2300
+ err
2301
+ } else {
2302
+ self . dcx ( ) . struct_span_err ( pat. span , err_msg)
2303
+ } ;
2304
+ err. emit ( ) ;
2305
+ }
2252
2306
2307
+ pat_info. binding_mode = ByRef :: No ;
2308
+ self . typeck_results . borrow_mut ( ) . skipped_ref_pats_mut ( ) . insert ( pat. hir_id ) ;
2309
+ self . check_pat ( inner, expected, pat_info) ;
2310
+ return expected;
2311
+ }
2312
+ InheritedRefMatchRule :: EatInner => {
2313
+ if let ty:: Ref ( _, _, r_mutbl) = * expected. kind ( ) {
2314
+ // Match against the reference type; don't consume the inherited ref.
2315
+ pat_info. binding_mode = pat_info. binding_mode . cap_ref_mutability ( r_mutbl) ;
2316
+ } else {
2317
+ // The expected type isn't a reference, so match against the inherited ref.
2318
+ if pat_mutbl > inh_mut {
2319
+ // We can't match an inherited shared reference with `&mut`. This will
2320
+ // be a type error later, since we're matching a reference pattern
2321
+ // against a non-reference type.
2322
+ // NB: This assumes that `&` patterns can match against mutable
2323
+ // references (RFC 3627, Rule 5). If we implement a pattern typing
2324
+ // ruleset with Rule 4 but not Rule 5, we'll need to check that here.
2325
+ } else {
2253
2326
pat_info. binding_mode = ByRef :: No ;
2254
2327
self . typeck_results
2255
2328
. borrow_mut ( )
@@ -2258,24 +2331,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
2258
2331
self . check_pat ( inner, expected, pat_info) ;
2259
2332
return expected;
2260
2333
}
2261
- } else {
2262
- pat_info. binding_mode = ByRef :: No ;
2263
- self . typeck_results . borrow_mut ( ) . skipped_ref_pats_mut ( ) . insert ( pat. hir_id ) ;
2264
- self . check_pat ( inner, expected, pat_info) ;
2265
- return expected;
2266
2334
}
2267
2335
}
2268
- }
2269
- } else {
2270
- // Reset binding mode on old editions
2271
- if pat_info. binding_mode != ByRef :: No {
2272
- pat_info. binding_mode = ByRef :: No ;
2273
- self . add_rust_2024_migration_desugared_pat (
2274
- pat_info. top_info . hir_id ,
2275
- pat. span ,
2276
- inner. span ,
2277
- "cannot implicitly match against multiple layers of reference" ,
2278
- )
2336
+ InheritedRefMatchRule :: EatBoth => {
2337
+ // Reset binding mode on old editions
2338
+ pat_info. binding_mode = ByRef :: No ;
2339
+ self . add_rust_2024_migration_desugared_pat (
2340
+ pat_info. top_info . hir_id ,
2341
+ pat. span ,
2342
+ inner. span ,
2343
+ "cannot implicitly match against multiple layers of reference" ,
2344
+ )
2345
+ }
2279
2346
}
2280
2347
}
2281
2348
@@ -2290,7 +2357,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
2290
2357
debug ! ( "check_pat_ref: expected={:?}" , expected) ;
2291
2358
match * expected. kind ( ) {
2292
2359
ty:: Ref ( _, r_ty, r_mutbl)
2293
- if ( no_ref_mut_behind_and && r_mutbl >= pat_mutbl)
2360
+ if ( ref_pat_matches_mut_ref && r_mutbl >= pat_mutbl)
2294
2361
|| r_mutbl == pat_mutbl =>
2295
2362
{
2296
2363
if r_mutbl == Mutability :: Not {
0 commit comments