@@ -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} ;
@@ -169,15 +170,16 @@ enum AdjustMode {
169
170
Pass ,
170
171
}
171
172
172
- /// `ref mut` patterns (explicit or match-ergonomics)
173
- /// are not allowed behind an `&` reference.
173
+ /// `ref mut` bindings (explicit or match-ergonomics) are not allowed behind an `&` reference.
174
+ /// Normally, the borrow checker enforces this, but for (currently experimental) match ergonomics,
175
+ /// we track this when typing patterns for two purposes:
174
176
///
175
- /// This includes explicit `ref mut` behind `&` patterns
176
- /// that match against `&mut` references,
177
- /// where the code would have compiled
178
- /// had the pattern been written as `& mut`.
179
- /// However, the borrow checker will not catch
180
- /// this last case , so we need to throw an error ourselves.
177
+ /// - For RFC 3627's Rule 3, when this would prevent us from binding with `ref mut`, we limit the
178
+ /// default binding mode to be by shared `ref` when it would otherwise be `ref mut`.
179
+ ///
180
+ /// - For RFC 3627's Rule 5, we allow `&` patterns to match against `& mut` references, treating them
181
+ /// as if they were shared references. Since the scrutinee is mutable in this case, the borrow
182
+ /// checker won't catch if we bind with `ref mut` , so we need to throw an error ourselves.
181
183
#[ derive( Clone , Copy , Debug , PartialEq , Eq ) ]
182
184
enum MutblCap {
183
185
/// Mutability restricted to immutable.
@@ -213,7 +215,67 @@ impl MutblCap {
213
215
}
214
216
}
215
217
218
+ /// Variations on RFC 3627's Rule 4: when do reference patterns match against inherited references?
219
+ ///
220
+ /// "Inherited reference" designates the `&`/`&mut` types that arise from using match ergonomics, i.e.
221
+ /// from matching a reference type with a non-reference pattern. E.g. when `Some(x)` matches on
222
+ /// `&mut Option<&T>`, `x` gets type `&mut &T` and the outer `&mut` is considered "inherited".
223
+ #[ derive( Clone , Copy , Debug , PartialEq , Eq ) ]
224
+ enum InheritedRefMatchRule {
225
+ /// Reference patterns consume only the inherited reference if possible, regardless of whether
226
+ /// the underlying type being matched against is a reference type. If there is no inherited
227
+ /// reference, a reference will be consumed from the underlying type.
228
+ EatOuter ,
229
+ /// Reference patterns consume only a reference from the underlying type if possible. If the
230
+ /// underlying type is not a reference type, the inherited reference will be consumed.
231
+ EatInner ,
232
+ /// When the underlying type is a reference type, reference patterns consume both layers of
233
+ /// reference, i.e. they both reset the binding mode and consume the reference type. Reference
234
+ /// patterns are not permitted when there is no underlying reference type, i.e. they can't eat
235
+ /// only an inherited reference. This is the current stable Rust behavior.
236
+ EatBoth ,
237
+ }
238
+
216
239
impl < ' a , ' tcx > FnCtxt < ' a , ' tcx > {
240
+ /// Experimental pattern feature: after matching against a shared reference, do we limit the
241
+ /// default binding mode in subpatterns to be `ref` when it would otherwise be `ref mut`?
242
+ /// This corresponds to Rule 3 of RFC 3627.
243
+ fn downgrade_mut_inside_shared ( & self ) -> bool {
244
+ // NB: RFC 3627 proposes stabilizing Rule 3 in all editions. If we adopt the same behavior
245
+ // across all editions, this may be removed.
246
+ self . tcx . features ( ) . ref_pat_eat_one_layer_2024 ( )
247
+ || self . tcx . features ( ) . ref_pat_eat_one_layer_2024_structural ( )
248
+ }
249
+
250
+ /// Experimental pattern feature: when do reference patterns match against inherited references?
251
+ /// This corresponds to variations on Rule 4 of RFC 3627.
252
+ fn ref_pat_matches_inherited_ref ( & self , edition : Edition ) -> InheritedRefMatchRule {
253
+ // NB: The particular rule used here is likely to differ across editions, so calls to this
254
+ // may need to become edition checks after match ergonomics stabilize.
255
+ if edition. at_least_rust_2024 ( ) {
256
+ if self . tcx . features ( ) . ref_pat_eat_one_layer_2024 ( ) {
257
+ InheritedRefMatchRule :: EatOuter
258
+ } else if self . tcx . features ( ) . ref_pat_eat_one_layer_2024_structural ( ) {
259
+ InheritedRefMatchRule :: EatInner
260
+ } else {
261
+ // Currently, matching against an inherited ref on edition 2024 is an error.
262
+ // Use `EatBoth` as a fallback to be similar to stable Rust.
263
+ InheritedRefMatchRule :: EatBoth
264
+ }
265
+ } else {
266
+ InheritedRefMatchRule :: EatBoth
267
+ }
268
+ }
269
+
270
+ /// Experimental pattern feature: do `&` patterns match against `&mut` references, treating them
271
+ /// as if they were shared references? This corresponds to Rule 5 of RFC 3627.
272
+ fn ref_pat_matches_mut_ref ( & self ) -> bool {
273
+ // NB: RFC 3627 proposes stabilizing Rule 5 in all editions. If we adopt the same behavior
274
+ // across all editions, this may be removed.
275
+ self . tcx . features ( ) . ref_pat_eat_one_layer_2024 ( )
276
+ || self . tcx . features ( ) . ref_pat_eat_one_layer_2024_structural ( )
277
+ }
278
+
217
279
/// Type check the given top level pattern against the `expected` type.
218
280
///
219
281
/// If a `Some(span)` is provided and `origin_expr` holds,
@@ -474,13 +536,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
474
536
} ) ;
475
537
}
476
538
477
- let features = self . tcx . features ( ) ;
478
- if features. ref_pat_eat_one_layer_2024 ( ) || features. ref_pat_eat_one_layer_2024_structural ( )
479
- {
539
+ if self . downgrade_mut_inside_shared ( ) {
480
540
def_br = def_br. cap_ref_mutability ( max_ref_mutbl. as_mutbl ( ) ) ;
481
- if def_br == ByRef :: Yes ( Mutability :: Not ) {
482
- max_ref_mutbl = MutblCap :: Not ;
483
- }
541
+ }
542
+ if def_br == ByRef :: Yes ( Mutability :: Not ) {
543
+ max_ref_mutbl = MutblCap :: Not ;
484
544
}
485
545
486
546
if !pat_adjustments. is_empty ( ) {
@@ -731,6 +791,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
731
791
// Determine the binding mode...
732
792
let bm = match user_bind_annot {
733
793
BindingMode ( ByRef :: No , Mutability :: Mut ) if matches ! ( def_br, ByRef :: Yes ( _) ) => {
794
+ // Only mention the experimental `mut_ref` feature if if we're in edition 2024 and
795
+ // using other experimental matching features compatible with it.
734
796
if pat. span . at_least_rust_2024 ( )
735
797
&& ( self . tcx . features ( ) . ref_pat_eat_one_layer_2024 ( )
736
798
|| self . tcx . features ( ) . ref_pat_eat_one_layer_2024_structural ( ) )
@@ -2228,55 +2290,70 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
2228
2290
mut pat_info : PatInfo < ' _ , ' tcx > ,
2229
2291
) -> Ty < ' tcx > {
2230
2292
let tcx = self . tcx ;
2231
- let features = tcx. features ( ) ;
2232
- let ref_pat_eat_one_layer_2024 = features. ref_pat_eat_one_layer_2024 ( ) ;
2233
- let ref_pat_eat_one_layer_2024_structural =
2234
- features. ref_pat_eat_one_layer_2024_structural ( ) ;
2235
-
2236
- let no_ref_mut_behind_and =
2237
- ref_pat_eat_one_layer_2024 || ref_pat_eat_one_layer_2024_structural;
2238
- let new_match_ergonomics = pat. span . at_least_rust_2024 ( ) && no_ref_mut_behind_and;
2239
2293
2240
2294
let pat_prefix_span =
2241
2295
inner. span . find_ancestor_inside ( pat. span ) . map ( |end| pat. span . until ( end) ) ;
2242
2296
2243
- if no_ref_mut_behind_and {
2244
- if pat_mutbl == Mutability :: Not {
2245
- // Prevent the inner pattern from binding with `ref mut`.
2246
- pat_info . max_ref_mutbl = pat_info . max_ref_mutbl . cap_to_weakly_not ( pat_prefix_span ) ;
2247
- }
2248
- } else {
2249
- pat_info. max_ref_mutbl = MutblCap :: Mut ;
2297
+ let ref_pat_matches_mut_ref = self . ref_pat_matches_mut_ref ( ) ;
2298
+ if ref_pat_matches_mut_ref && pat_mutbl == Mutability :: Not {
2299
+ // If `&` patterns can match against mutable reference types (RFC 3627, Rule 5), we need
2300
+ // to prevent subpatterns from binding with `ref mut`. Subpatterns of a shared reference
2301
+ // pattern should have read-only access to the scrutinee, and the borrow checker won't
2302
+ // catch it in this case.
2303
+ pat_info. max_ref_mutbl = pat_info . max_ref_mutbl . cap_to_weakly_not ( pat_prefix_span ) ;
2250
2304
}
2251
2305
2252
2306
expected = self . try_structurally_resolve_type ( pat. span , expected) ;
2253
- if new_match_ergonomics {
2254
- if let ByRef :: Yes ( inh_mut) = pat_info. binding_mode {
2255
- if !ref_pat_eat_one_layer_2024 && let ty:: Ref ( _, _, r_mutbl) = * expected. kind ( ) {
2256
- // Don't attempt to consume inherited reference
2257
- pat_info. binding_mode = pat_info. binding_mode . cap_ref_mutability ( r_mutbl) ;
2258
- } else {
2307
+ // Determine whether we're consuming an inherited reference and resetting the default
2308
+ // binding mode, based on edition and enabled experimental features.
2309
+ if let ByRef :: Yes ( inh_mut) = pat_info. binding_mode {
2310
+ match self . ref_pat_matches_inherited_ref ( pat. span . edition ( ) ) {
2311
+ InheritedRefMatchRule :: EatOuter => {
2259
2312
// ref pattern attempts to consume inherited reference
2260
2313
if pat_mutbl > inh_mut {
2261
2314
// Tried to match inherited `ref` with `&mut`
2262
- if !ref_pat_eat_one_layer_2024_structural {
2263
- let err_msg = "mismatched types" ;
2264
- let err = if let Some ( span) = pat_prefix_span {
2265
- let mut err = self . dcx ( ) . struct_span_err ( span, err_msg) ;
2266
- err. code ( E0308 ) ;
2267
- err. note ( "cannot match inherited `&` with `&mut` pattern" ) ;
2268
- err. span_suggestion_verbose (
2269
- span,
2270
- "replace this `&mut` pattern with `&`" ,
2271
- "&" ,
2272
- Applicability :: MachineApplicable ,
2273
- ) ;
2274
- err
2275
- } else {
2276
- self . dcx ( ) . struct_span_err ( pat. span , err_msg)
2277
- } ;
2278
- err. emit ( ) ;
2315
+ // NB: This assumes that `&` patterns can match against mutable references
2316
+ // (RFC 3627, Rule 5). If we implement a pattern typing ruleset with Rule 4E
2317
+ // but not Rule 5, we'll need to check that here.
2318
+ debug_assert ! ( ref_pat_matches_mut_ref) ;
2319
+ let err_msg = "mismatched types" ;
2320
+ let err = if let Some ( span) = pat_prefix_span {
2321
+ let mut err = self . dcx ( ) . struct_span_err ( span, err_msg) ;
2322
+ err. code ( E0308 ) ;
2323
+ err. note ( "cannot match inherited `&` with `&mut` pattern" ) ;
2324
+ err. span_suggestion_verbose (
2325
+ span,
2326
+ "replace this `&mut` pattern with `&`" ,
2327
+ "&" ,
2328
+ Applicability :: MachineApplicable ,
2329
+ ) ;
2330
+ err
2331
+ } else {
2332
+ self . dcx ( ) . struct_span_err ( pat. span , err_msg)
2333
+ } ;
2334
+ err. emit ( ) ;
2335
+ }
2279
2336
2337
+ pat_info. binding_mode = ByRef :: No ;
2338
+ self . typeck_results . borrow_mut ( ) . skipped_ref_pats_mut ( ) . insert ( pat. hir_id ) ;
2339
+ self . check_pat ( inner, expected, pat_info) ;
2340
+ return expected;
2341
+ }
2342
+ InheritedRefMatchRule :: EatInner => {
2343
+ if let ty:: Ref ( _, _, r_mutbl) = * expected. kind ( ) {
2344
+ // Match against the reference type; don't consume the inherited ref.
2345
+ pat_info. binding_mode = pat_info. binding_mode . cap_ref_mutability ( r_mutbl) ;
2346
+ } else {
2347
+ // The expected type isn't a reference, so match against the inherited ref.
2348
+ if pat_mutbl > inh_mut {
2349
+ // We can't match an inherited shared reference with `&mut`. This will
2350
+ // be a type error later, since we're matching a reference pattern
2351
+ // against a non-reference type.
2352
+ // NB: This assumes that `&` patterns can match against mutable
2353
+ // references (RFC 3627, Rule 5). If we implement a pattern typing
2354
+ // ruleset with Rule 4 but not Rule 5, we'll need to check that here.
2355
+ debug_assert ! ( ref_pat_matches_mut_ref) ;
2356
+ } else {
2280
2357
pat_info. binding_mode = ByRef :: No ;
2281
2358
self . typeck_results
2282
2359
. borrow_mut ( )
@@ -2285,24 +2362,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
2285
2362
self . check_pat ( inner, expected, pat_info) ;
2286
2363
return expected;
2287
2364
}
2288
- } else {
2289
- pat_info. binding_mode = ByRef :: No ;
2290
- self . typeck_results . borrow_mut ( ) . skipped_ref_pats_mut ( ) . insert ( pat. hir_id ) ;
2291
- self . check_pat ( inner, expected, pat_info) ;
2292
- return expected;
2293
2365
}
2294
2366
}
2295
- }
2296
- } else {
2297
- // Reset binding mode on old editions
2298
- if pat_info. binding_mode != ByRef :: No {
2299
- pat_info. binding_mode = ByRef :: No ;
2300
- self . add_rust_2024_migration_desugared_pat (
2301
- pat_info. top_info . hir_id ,
2302
- pat. span ,
2303
- inner. span ,
2304
- "cannot implicitly match against multiple layers of reference" ,
2305
- )
2367
+ InheritedRefMatchRule :: EatBoth => {
2368
+ // Reset binding mode on old editions
2369
+ pat_info. binding_mode = ByRef :: No ;
2370
+ self . add_rust_2024_migration_desugared_pat (
2371
+ pat_info. top_info . hir_id ,
2372
+ pat. span ,
2373
+ inner. span ,
2374
+ "cannot implicitly match against multiple layers of reference" ,
2375
+ )
2376
+ }
2306
2377
}
2307
2378
}
2308
2379
@@ -2317,10 +2388,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
2317
2388
debug ! ( "check_pat_ref: expected={:?}" , expected) ;
2318
2389
match * expected. kind ( ) {
2319
2390
ty:: Ref ( _, r_ty, r_mutbl)
2320
- if ( no_ref_mut_behind_and && r_mutbl >= pat_mutbl)
2391
+ if ( ref_pat_matches_mut_ref && r_mutbl >= pat_mutbl)
2321
2392
|| r_mutbl == pat_mutbl =>
2322
2393
{
2323
- if no_ref_mut_behind_and && r_mutbl == Mutability :: Not {
2394
+ if r_mutbl == Mutability :: Not {
2324
2395
pat_info. max_ref_mutbl = MutblCap :: Not ;
2325
2396
}
2326
2397
0 commit comments