@@ -21,6 +21,7 @@ use rustc_middle::{bug, span_bug};
2121use rustc_session:: lint:: builtin:: NON_EXHAUSTIVE_OMITTED_PATTERNS ;
2222use rustc_session:: parse:: feature_err;
2323use rustc_span:: edit_distance:: find_best_match_for_name;
24+ use rustc_span:: edition:: Edition ;
2425use rustc_span:: hygiene:: DesugaringKind ;
2526use rustc_span:: source_map:: Spanned ;
2627use rustc_span:: { BytePos , DUMMY_SP , Ident , Span , kw, sym} ;
@@ -213,7 +214,62 @@ impl MutblCap {
213214 }
214215}
215216
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+
216233impl < ' 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+
217273 /// Type check the given top level pattern against the `expected` type.
218274 ///
219275 /// If a `Some(span)` is provided and `origin_expr` holds,
@@ -474,9 +530,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
474530 } ) ;
475531 }
476532
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 ( ) {
480534 def_br = def_br. cap_ref_mutability ( max_ref_mutbl. as_mutbl ( ) ) ;
481535 }
482536 if def_br == ByRef :: Yes ( Mutability :: Not ) {
@@ -708,6 +762,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
708762 // Determine the binding mode...
709763 let bm = match user_bind_annot {
710764 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.
711767 if pat. span . at_least_rust_2024 ( )
712768 && ( self . tcx . features ( ) . ref_pat_eat_one_layer_2024 ( )
713769 || self . tcx . features ( ) . ref_pat_eat_one_layer_2024_structural ( ) )
@@ -2205,51 +2261,68 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
22052261 mut pat_info : PatInfo < ' _ , ' tcx > ,
22062262 ) -> Ty < ' tcx > {
22072263 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;
22162264
22172265 let pat_prefix_span =
22182266 inner. span . find_ancestor_inside ( pat. span ) . map ( |end| pat. span . until ( end) ) ;
22192267
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.
22222274 pat_info. max_ref_mutbl = pat_info. max_ref_mutbl . cap_to_weakly_not ( pat_prefix_span) ;
22232275 }
22242276
22252277 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 => {
22322283 // ref pattern attempts to consume inherited reference
22332284 if pat_mutbl > inh_mut {
22342285 // 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+ }
22522306
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 {
22532326 pat_info. binding_mode = ByRef :: No ;
22542327 self . typeck_results
22552328 . borrow_mut ( )
@@ -2258,24 +2331,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
22582331 self . check_pat ( inner, expected, pat_info) ;
22592332 return expected;
22602333 }
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;
22662334 }
22672335 }
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+ }
22792346 }
22802347 }
22812348
@@ -2290,7 +2357,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
22902357 debug ! ( "check_pat_ref: expected={:?}" , expected) ;
22912358 match * expected. kind ( ) {
22922359 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)
22942361 || r_mutbl == pat_mutbl =>
22952362 {
22962363 if r_mutbl == Mutability :: Not {
0 commit comments