@@ -161,15 +161,28 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
161161/// Mode for adjusting the expected type and binding mode.
162162#[ derive( Clone , Copy , Debug , PartialEq , Eq ) ]
163163enum 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 } ,
166167 /// Reset binding mode to the initial mode.
167168 /// Used for destructuring assignment, where we don't want any match ergonomics.
168169 Reset ,
169170 /// Pass on the input binding mode and expected type.
170171 Pass ,
171172}
172173
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+
173186/// `ref mut` bindings (explicit or match-ergonomics) are not allowed behind an `&` reference.
174187/// Normally, the borrow checker enforces this, but for (currently experimental) match ergonomics,
175188/// we track this when typing patterns for two purposes:
@@ -457,7 +470,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
457470 match adjust_mode {
458471 AdjustMode :: Pass => ( expected, def_br, max_ref_mutbl) ,
459472 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+ }
461476 }
462477 }
463478
@@ -476,12 +491,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
476491 PatKind :: Struct ( ..)
477492 | PatKind :: TupleStruct ( ..)
478493 | PatKind :: Tuple ( ..)
479- | PatKind :: Box ( _)
480- | PatKind :: Deref ( _)
481494 | 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 } ,
483501 // A never pattern behaves somewhat like a literal or unit variant.
484- PatKind :: Never => AdjustMode :: Peel ,
502+ PatKind :: Never => AdjustMode :: Peel { kind : PeelKind :: Overloaded } ,
485503 PatKind :: Expr ( PatExpr { kind : PatExprKind :: Path ( _) , .. } ) => match opt_path_res. unwrap ( ) {
486504 // These constants can be of a reference type, e.g. `const X: &u8 = &0;`.
487505 // Peeling the reference types too early will cause type checking failures.
@@ -491,7 +509,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
491509 // could successfully compile. The former being `Self` requires a unit struct.
492510 // In either case, and unlike constants, the pattern itself cannot be
493511 // a reference type wherefore peeling doesn't give up any expressiveness.
494- _ => AdjustMode :: Peel ,
512+ _ => AdjustMode :: Peel { kind : PeelKind :: Overloaded } ,
495513 } ,
496514
497515 // String and byte-string literals result in types `&str` and `&[u8]` respectively.
@@ -501,7 +519,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
501519 // Call `resolve_vars_if_possible` here for inline const blocks.
502520 PatKind :: Expr ( lt) => match self . resolve_vars_if_possible ( self . check_pat_expr_unadjusted ( lt) ) . kind ( ) {
503521 ty:: Ref ( ..) => AdjustMode :: Pass ,
504- _ => AdjustMode :: Peel ,
522+ _ => AdjustMode :: Peel { kind : PeelKind :: Overloaded } ,
505523 } ,
506524
507525 // Ref patterns are complicated, we handle them in `check_pat_ref`.
@@ -526,42 +544,83 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
526544
527545 /// Peel off as many immediately nested `& mut?` from the expected type as possible
528546 /// 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.
529548 /// The adjustments vector, if non-empty is stored in a table.
530549 fn peel_off_references (
531550 & self ,
532551 pat : & ' tcx Pat < ' tcx > ,
552+ peel_kind : PeelKind ,
533553 expected : Ty < ' tcx > ,
534554 mut def_br : ByRef ,
535555 mut max_ref_mutbl : MutblCap ,
536556 ) -> ( Ty < ' tcx > , ByRef , MutblCap ) {
557+ let deref_patterns = self . tcx . features ( ) . deref_patterns ( ) ;
537558 let mut expected = self . try_structurally_resolve_type ( pat. span , expected) ;
538559 // Peel off as many `&` or `&mut` from the scrutinee type as possible. For example,
539560 // for `match &&&mut Some(5)` the loop runs three times, aborting when it reaches
540561 // the `Some(5)` which is not of type Ref.
541562 //
542563 // For each ampersand peeled off, update the binding mode and push the original
543564 // type into the adjustments vector.
565+ // When peeling a library-defined type, the default binding mode is not updated.
544566 //
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/`.
546569 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+ } ;
549617
550- debug ! ( "current discriminant is Ref, inserting implicit deref" ) ;
618+ debug ! ( "inspecting {:?}" , expected) ;
619+ debug ! ( "current discriminant can be dereferenced, inserting implicit deref" ) ;
551620 // Preserve the reference type. We'll need it later during THIR lowering.
552621 pat_adjustments. push ( expected) ;
553622
554623 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- } ) ;
565624 }
566625
567626 if self . downgrade_mut_inside_shared ( ) {
@@ -2287,38 +2346,43 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
22872346 expected : Ty < ' tcx > ,
22882347 pat_info : PatInfo < ' tcx > ,
22892348 ) -> Ty < ' tcx > {
2290- let tcx = self . tcx ;
2291- // Register a `DerefPure` bound, which is required by all `deref!()` pats.
2292- self . register_bound (
2293- expected,
2294- tcx. require_lang_item ( hir:: LangItem :: DerefPure , Some ( span) ) ,
2295- self . misc ( span) ,
2296- ) ;
2297- // <expected as Deref>::Target
2298- let ty = Ty :: new_projection (
2299- tcx,
2300- tcx. require_lang_item ( hir:: LangItem :: DerefTarget , Some ( span) ) ,
2301- [ expected] ,
2302- ) ;
2303- let ty = self . normalize ( span, ty) ;
2304- let ty = self . try_structurally_resolve_type ( span, ty) ;
2305- self . check_pat ( inner, ty, pat_info) ;
2349+ let target_ty = self . deref_pat_target ( span, expected) ;
2350+ self . check_pat ( inner, target_ty, pat_info) ;
23062351
23072352 // Check if the pattern has any `ref mut` bindings, which would require
23082353 // `DerefMut` to be emitted in MIR building instead of just `Deref`.
23092354 // We do this *after* checking the inner pattern, since we want to make
23102355 // sure to apply any match-ergonomics adjustments.
2356+ // TODO: move this to a separate definition to share it with implicit deref pats
23112357 if self . typeck_results . borrow ( ) . pat_has_ref_mut_binding ( inner) {
23122358 self . register_bound (
23132359 expected,
2314- tcx. require_lang_item ( hir:: LangItem :: DerefMut , Some ( span) ) ,
2360+ self . tcx . require_lang_item ( hir:: LangItem :: DerefMut , Some ( span) ) ,
23152361 self . misc ( span) ,
23162362 ) ;
23172363 }
23182364
23192365 expected
23202366 }
23212367
2368+ fn deref_pat_target ( & self , span : Span , source_ty : Ty < ' tcx > ) -> Ty < ' tcx > {
2369+ // Register a `DerefPure` bound, which is required by all `deref!()` pats.
2370+ let tcx = self . tcx ;
2371+ self . register_bound (
2372+ source_ty,
2373+ tcx. require_lang_item ( hir:: LangItem :: DerefPure , Some ( span) ) ,
2374+ self . misc ( span) ,
2375+ ) ;
2376+ // The expected type for the deref pat's inner pattern is `<expected as Deref>::Target`.
2377+ let target_ty = Ty :: new_projection (
2378+ tcx,
2379+ tcx. require_lang_item ( hir:: LangItem :: DerefTarget , Some ( span) ) ,
2380+ [ source_ty] ,
2381+ ) ;
2382+ let target_ty = self . normalize ( span, target_ty) ;
2383+ self . try_structurally_resolve_type ( span, target_ty)
2384+ }
2385+
23222386 // Precondition: Pat is Ref(inner)
23232387 fn check_pat_ref (
23242388 & self ,
0 commit comments