@@ -161,15 +161,28 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
161
161
/// Mode for adjusting the expected type and binding mode.
162
162
#[ derive( Clone , Copy , Debug , PartialEq , Eq ) ]
163
163
enum AdjustMode {
164
- /// Peel off all immediate reference types.
165
- Peel ,
164
+ /// Peel off all immediate reference types. If the `deref_patterns` feature is enabled, this
165
+ /// also peels library-defined smart pointer ADTs.
166
+ Peel { kind : PeelKind } ,
166
167
/// Reset binding mode to the initial mode.
167
168
/// Used for destructuring assignment, where we don't want any match ergonomics.
168
169
Reset ,
169
170
/// Pass on the input binding mode and expected type.
170
171
Pass ,
171
172
}
172
173
174
+ /// Restrictions on what types to peel when adjusting the expected type and binding mode.
175
+ #[ derive( Clone , Copy , Debug , PartialEq , Eq ) ]
176
+ enum PeelKind {
177
+ /// Only peel reference types. This is used for explicit deref patterns.
178
+ RefOnly ,
179
+ /// If `deref_patterns` is enabled, this also peels library-defined smart pointer ADTs, except
180
+ /// for `until_adt` if the pattern is a constructor. Otherwise this is the same as `RefOnly`.
181
+ /// See [`ResolvedPat`] for more information.
182
+ // TODO: add `ResolvedPat` and `until_adt`.
183
+ Overloaded ,
184
+ }
185
+
173
186
/// `ref mut` bindings (explicit or match-ergonomics) are not allowed behind an `&` reference.
174
187
/// Normally, the borrow checker enforces this, but for (currently experimental) match ergonomics,
175
188
/// we track this when typing patterns for two purposes:
@@ -457,7 +470,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
457
470
match adjust_mode {
458
471
AdjustMode :: Pass => ( expected, def_br, max_ref_mutbl) ,
459
472
AdjustMode :: Reset => ( expected, ByRef :: No , MutblCap :: Mut ) ,
460
- AdjustMode :: Peel => self . peel_off_references ( pat, expected, def_br, max_ref_mutbl) ,
473
+ AdjustMode :: Peel { kind } => {
474
+ self . peel_off_references ( pat, kind, expected, def_br, max_ref_mutbl)
475
+ }
461
476
}
462
477
}
463
478
@@ -476,12 +491,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
476
491
PatKind :: Struct ( ..)
477
492
| PatKind :: TupleStruct ( ..)
478
493
| PatKind :: Tuple ( ..)
479
- | PatKind :: Box ( _)
480
- | PatKind :: Deref ( _)
481
494
| PatKind :: Range ( ..)
482
- | PatKind :: Slice ( ..) => AdjustMode :: Peel ,
495
+ | PatKind :: Slice ( ..) => AdjustMode :: Peel { kind : PeelKind :: Overloaded } ,
496
+ // When checking an explicit deref pattern, only peel reference types.
497
+ // FIXME(deref_patterns): If box patterns and deref patterns need to coexist, box
498
+ // patterns may want `PeelKind::Overloaded`, stopping on encountering a box.
499
+ | PatKind :: Box ( _)
500
+ | PatKind :: Deref ( _) => AdjustMode :: Peel { kind : PeelKind :: RefOnly } ,
483
501
// A never pattern behaves somewhat like a literal or unit variant.
484
- PatKind :: Never => AdjustMode :: Peel ,
502
+ PatKind :: Never => AdjustMode :: Peel { kind : PeelKind :: Overloaded } ,
485
503
PatKind :: Expr ( PatExpr { kind : PatExprKind :: Path ( _) , .. } ) => match opt_path_res. unwrap ( ) {
486
504
// These constants can be of a reference type, e.g. `const X: &u8 = &0;`.
487
505
// Peeling the reference types too early will cause type checking failures.
@@ -491,7 +509,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
491
509
// could successfully compile. The former being `Self` requires a unit struct.
492
510
// In either case, and unlike constants, the pattern itself cannot be
493
511
// a reference type wherefore peeling doesn't give up any expressiveness.
494
- _ => AdjustMode :: Peel ,
512
+ _ => AdjustMode :: Peel { kind : PeelKind :: Overloaded } ,
495
513
} ,
496
514
497
515
// String and byte-string literals result in types `&str` and `&[u8]` respectively.
@@ -501,7 +519,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
501
519
// Call `resolve_vars_if_possible` here for inline const blocks.
502
520
PatKind :: Expr ( lt) => match self . resolve_vars_if_possible ( self . check_pat_expr_unadjusted ( lt) ) . kind ( ) {
503
521
ty:: Ref ( ..) => AdjustMode :: Pass ,
504
- _ => AdjustMode :: Peel ,
522
+ _ => AdjustMode :: Peel { kind : PeelKind :: Overloaded } ,
505
523
} ,
506
524
507
525
// Ref patterns are complicated, we handle them in `check_pat_ref`.
@@ -526,42 +544,83 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
526
544
527
545
/// Peel off as many immediately nested `& mut?` from the expected type as possible
528
546
/// and return the new expected type and binding default binding mode.
547
+ /// If the `deref_patterns` feature is enabled, also peel library-defined pointer-like types.
529
548
/// The adjustments vector, if non-empty is stored in a table.
530
549
fn peel_off_references (
531
550
& self ,
532
551
pat : & ' tcx Pat < ' tcx > ,
552
+ peel_kind : PeelKind ,
533
553
expected : Ty < ' tcx > ,
534
554
mut def_br : ByRef ,
535
555
mut max_ref_mutbl : MutblCap ,
536
556
) -> ( Ty < ' tcx > , ByRef , MutblCap ) {
557
+ let deref_patterns = self . tcx . features ( ) . deref_patterns ( ) ;
537
558
let mut expected = self . try_structurally_resolve_type ( pat. span , expected) ;
538
559
// Peel off as many `&` or `&mut` from the scrutinee type as possible. For example,
539
560
// for `match &&&mut Some(5)` the loop runs three times, aborting when it reaches
540
561
// the `Some(5)` which is not of type Ref.
541
562
//
542
563
// For each ampersand peeled off, update the binding mode and push the original
543
564
// type into the adjustments vector.
565
+ // When peeling a library-defined type, the default binding mode is not updated.
544
566
//
545
- // See the examples in `ui/match-defbm*.rs`.
567
+ // See the examples in `tests/ui/rfcs/rfc-2005-default-binding-mode/` and
568
+ // `tests/ui/pattern/deref-patterns/`.
546
569
let mut pat_adjustments = vec ! [ ] ;
547
- while let ty:: Ref ( _, inner_ty, inner_mutability) = * expected. kind ( ) {
548
- debug ! ( "inspecting {:?}" , expected) ;
570
+ loop {
571
+ // TODO: check # of iterations against tcx's recursion limit, so we don't loop until OOM
572
+ // if someone tries matching on a type with a cyclic `Deref` impl.
573
+ let inner_ty = if let ty:: Ref ( _, inner_ty, inner_mutability) = * expected. kind ( ) {
574
+ def_br = ByRef :: Yes ( match def_br {
575
+ // If default binding mode is by value, make it `ref` or `ref mut`
576
+ // (depending on whether we observe `&` or `&mut`).
577
+ ByRef :: No |
578
+ // When `ref mut`, stay a `ref mut` (on `&mut`) or downgrade to `ref` (on `&`).
579
+ ByRef :: Yes ( Mutability :: Mut ) => inner_mutability,
580
+ // Once a `ref`, always a `ref`.
581
+ // This is because a `& &mut` cannot mutate the underlying value.
582
+ ByRef :: Yes ( Mutability :: Not ) => Mutability :: Not ,
583
+ } ) ;
584
+ inner_ty
585
+ } else if deref_patterns
586
+ && peel_kind == PeelKind :: Overloaded
587
+ // For simplicity, only apply overloaded derefs if `expected` is a known ADT.
588
+ // FIXME(deref_patterns): we'll get better diagnostics for users trying to
589
+ // implicitly deref generics if we allow them here, but primitives, tuples, and
590
+ // inference vars definitely should be stopped. Figure out what makes most sense.
591
+ // TODO: stop peeling if the pattern is a constructor for the scrutinee type
592
+ && expected. is_adt ( )
593
+ {
594
+ // At this point, the pattern isn't able to match `expected` without peeling. Check
595
+ // that it implements `Deref` before assuming it's a smart pointer, to get a normal
596
+ // type error instead of a missing impl error if not. This only checks for `Deref`,
597
+ // not `DerefPure`: we require that too, but we want a trait error if it's missing.
598
+ if let Some ( deref_trait) = self . tcx . lang_items ( ) . deref_trait ( )
599
+ && self
600
+ . type_implements_trait ( deref_trait, [ expected] , self . param_env )
601
+ . may_apply ( )
602
+ {
603
+ // The scrutinee is a smart pointer; implicitly dereference it. This adds a
604
+ // requirement that `expected: DerefPure`.
605
+ self . deref_pat_target ( pat. span , expected)
606
+ // Once we've checked `pat`, we'll add a `DerefMut` bound if it contains any
607
+ // `ref mut` bindings. TODO: implement that, then reference here.
608
+ } else {
609
+ // Bail, so we get a normal type error.
610
+ break ;
611
+ }
612
+ } else {
613
+ // The scrutinee is a non-reference type and either `deref_patterns` isn't enabled
614
+ // or `pat` could possibly already match it; stop implicitly dereferencing.
615
+ break ;
616
+ } ;
549
617
550
- debug ! ( "current discriminant is Ref, inserting implicit deref" ) ;
618
+ debug ! ( "inspecting {:?}" , expected) ;
619
+ debug ! ( "current discriminant can be dereferenced, inserting implicit deref" ) ;
551
620
// Preserve the reference type. We'll need it later during THIR lowering.
552
621
pat_adjustments. push ( expected) ;
553
622
554
623
expected = self . try_structurally_resolve_type ( pat. span , inner_ty) ;
555
- def_br = ByRef :: Yes ( match def_br {
556
- // If default binding mode is by value, make it `ref` or `ref mut`
557
- // (depending on whether we observe `&` or `&mut`).
558
- ByRef :: No |
559
- // When `ref mut`, stay a `ref mut` (on `&mut`) or downgrade to `ref` (on `&`).
560
- ByRef :: Yes ( Mutability :: Mut ) => inner_mutability,
561
- // Once a `ref`, always a `ref`.
562
- // This is because a `& &mut` cannot mutate the underlying value.
563
- ByRef :: Yes ( Mutability :: Not ) => Mutability :: Not ,
564
- } ) ;
565
624
}
566
625
567
626
if self . downgrade_mut_inside_shared ( ) {
@@ -2282,38 +2341,43 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
2282
2341
expected : Ty < ' tcx > ,
2283
2342
pat_info : PatInfo < ' tcx > ,
2284
2343
) -> Ty < ' tcx > {
2285
- let tcx = self . tcx ;
2286
- // Register a `DerefPure` bound, which is required by all `deref!()` pats.
2287
- self . register_bound (
2288
- expected,
2289
- tcx. require_lang_item ( hir:: LangItem :: DerefPure , Some ( span) ) ,
2290
- self . misc ( span) ,
2291
- ) ;
2292
- // <expected as Deref>::Target
2293
- let ty = Ty :: new_projection (
2294
- tcx,
2295
- tcx. require_lang_item ( hir:: LangItem :: DerefTarget , Some ( span) ) ,
2296
- [ expected] ,
2297
- ) ;
2298
- let ty = self . normalize ( span, ty) ;
2299
- let ty = self . try_structurally_resolve_type ( span, ty) ;
2300
- self . check_pat ( inner, ty, pat_info) ;
2344
+ let target_ty = self . deref_pat_target ( span, expected) ;
2345
+ self . check_pat ( inner, target_ty, pat_info) ;
2301
2346
2302
2347
// Check if the pattern has any `ref mut` bindings, which would require
2303
2348
// `DerefMut` to be emitted in MIR building instead of just `Deref`.
2304
2349
// We do this *after* checking the inner pattern, since we want to make
2305
2350
// sure to apply any match-ergonomics adjustments.
2351
+ // TODO: move this to a separate definition to share it with implicit deref pats
2306
2352
if self . typeck_results . borrow ( ) . pat_has_ref_mut_binding ( inner) {
2307
2353
self . register_bound (
2308
2354
expected,
2309
- tcx. require_lang_item ( hir:: LangItem :: DerefMut , Some ( span) ) ,
2355
+ self . tcx . require_lang_item ( hir:: LangItem :: DerefMut , Some ( span) ) ,
2310
2356
self . misc ( span) ,
2311
2357
) ;
2312
2358
}
2313
2359
2314
2360
expected
2315
2361
}
2316
2362
2363
+ fn deref_pat_target ( & self , span : Span , source_ty : Ty < ' tcx > ) -> Ty < ' tcx > {
2364
+ // Register a `DerefPure` bound, which is required by all `deref!()` pats.
2365
+ let tcx = self . tcx ;
2366
+ self . register_bound (
2367
+ source_ty,
2368
+ tcx. require_lang_item ( hir:: LangItem :: DerefPure , Some ( span) ) ,
2369
+ self . misc ( span) ,
2370
+ ) ;
2371
+ // The expected type for the deref pat's inner pattern is `<expected as Deref>::Target`.
2372
+ let target_ty = Ty :: new_projection (
2373
+ tcx,
2374
+ tcx. require_lang_item ( hir:: LangItem :: DerefTarget , Some ( span) ) ,
2375
+ [ source_ty] ,
2376
+ ) ;
2377
+ let target_ty = self . normalize ( span, target_ty) ;
2378
+ self . try_structurally_resolve_type ( span, target_ty)
2379
+ }
2380
+
2317
2381
// Precondition: Pat is Ref(inner)
2318
2382
fn check_pat_ref (
2319
2383
& self ,
0 commit comments