@@ -177,16 +177,20 @@ enum PeelKind {
177177 /// Only peel reference types. This is used for explicit `deref!(_)` patterns, which dereference
178178 /// any number of `&`/`&mut` references, plus a single smart pointer.
179179 ExplicitDerefPat ,
180- /// Implicitly peel any number of references, and if `deref_patterns` is enabled, smart pointer
181- /// ADTs. In order to peel only as much as necessary for the pattern to match, the `until_adt`
182- /// field contains the ADT def that the pattern is a constructor for, if applicable, so that we
183- /// don't peel it. See [`ResolvedPat`] for more information.
184- Implicit { until_adt : Option < DefId > } ,
180+ /// Implicitly peel references, and if `deref_patterns` is enabled, smart pointer ADTs.
181+ Implicit {
182+ /// The ADT the pattern is a constructor for, if applicable, so that we don't peel it. See
183+ /// [`ResolvedPat`] for more information.
184+ until_adt : Option < DefId > ,
185+ /// The number of references at the head of the pattern's type, so we can leave that many
186+ /// untouched. This is `1` for string literals, and `0` for most patterns.
187+ pat_ref_layers : usize ,
188+ } ,
185189}
186190
187191impl AdjustMode {
188192 const fn peel_until_adt ( opt_adt_def : Option < DefId > ) -> AdjustMode {
189- AdjustMode :: Peel { kind : PeelKind :: Implicit { until_adt : opt_adt_def } }
193+ AdjustMode :: Peel { kind : PeelKind :: Implicit { until_adt : opt_adt_def, pat_ref_layers : 0 } }
190194 }
191195 const fn peel_all ( ) -> AdjustMode {
192196 AdjustMode :: peel_until_adt ( None )
@@ -488,9 +492,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
488492 match pat. kind {
489493 // Peel off a `&` or `&mut` from the scrutinee type. See the examples in
490494 // `tests/ui/rfcs/rfc-2005-default-binding-mode`.
491- _ if let AdjustMode :: Peel { .. } = adjust_mode
495+ _ if let AdjustMode :: Peel { kind : peel_kind } = adjust_mode
492496 && pat. default_binding_modes
493- && let ty:: Ref ( _, inner_ty, inner_mutability) = * expected. kind ( ) =>
497+ && let ty:: Ref ( _, inner_ty, inner_mutability) = * expected. kind ( )
498+ && self . should_peel_ref ( peel_kind, expected) =>
494499 {
495500 debug ! ( "inspecting {:?}" , expected) ;
496501
@@ -665,21 +670,32 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
665670
666671 // String and byte-string literals result in types `&str` and `&[u8]` respectively.
667672 // All other literals result in non-reference types.
668- // As a result, we allow `if let 0 = &&0 {}` but not `if let "foo" = &&"foo" {}`.
669- //
670- // Call `resolve_vars_if_possible` here for inline const blocks.
671- PatKind :: Expr ( lt) => match self . resolve_vars_if_possible ( self . check_pat_expr_unadjusted ( lt) ) . kind ( ) {
672- ty:: Ref ( ..) => AdjustMode :: Pass ,
673- _ => {
674- // Path patterns have already been handled, and inline const blocks currently
675- // aren't possible to write, so any handling for them would be untested.
676- if cfg ! ( debug_assertions)
677- && self . tcx . features ( ) . deref_patterns ( )
678- && !matches ! ( lt. kind, PatExprKind :: Lit { .. } )
679- {
680- span_bug ! ( lt. span, "FIXME(deref_patterns): adjust mode unimplemented for {:?}" , lt. kind) ;
673+ // As a result, we allow `if let 0 = &&0 {}` but not `if let "foo" = &&"foo" {}` unless
674+ // `deref_patterns` is enabled.
675+ PatKind :: Expr ( lt) => {
676+ // Path patterns have already been handled, and inline const blocks currently
677+ // aren't possible to write, so any handling for them would be untested.
678+ if cfg ! ( debug_assertions)
679+ && self . tcx . features ( ) . deref_patterns ( )
680+ && !matches ! ( lt. kind, PatExprKind :: Lit { .. } )
681+ {
682+ span_bug ! ( lt. span, "FIXME(deref_patterns): adjust mode unimplemented for {:?}" , lt. kind) ;
683+ }
684+ // Call `resolve_vars_if_possible` here for inline const blocks.
685+ let lit_ty = self . resolve_vars_if_possible ( self . check_pat_expr_unadjusted ( lt) ) ;
686+ // If `deref_patterns` is enabled, allow `if let "foo" = &&"foo" {}`.
687+ if self . tcx . features ( ) . deref_patterns ( ) {
688+ let mut peeled_ty = lit_ty;
689+ let mut pat_ref_layers = 0 ;
690+ while let ty:: Ref ( _, inner_ty, mutbl) = * peeled_ty. kind ( ) {
691+ // We rely on references at the head of constants being immutable.
692+ debug_assert ! ( mutbl. is_not( ) ) ;
693+ pat_ref_layers += 1 ;
694+ peeled_ty = inner_ty;
681695 }
682- AdjustMode :: peel_all ( )
696+ AdjustMode :: Peel { kind : PeelKind :: Implicit { until_adt : None , pat_ref_layers } }
697+ } else {
698+ if lit_ty. is_ref ( ) { AdjustMode :: Pass } else { AdjustMode :: peel_all ( ) }
683699 }
684700 } ,
685701
@@ -705,6 +721,35 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
705721 }
706722 }
707723
724+ /// Assuming `expected` is a reference type, determine whether to peel it before matching.
725+ fn should_peel_ref ( & self , peel_kind : PeelKind , mut expected : Ty < ' tcx > ) -> bool {
726+ debug_assert ! ( expected. is_ref( ) ) ;
727+ let pat_ref_layers = match peel_kind {
728+ PeelKind :: ExplicitDerefPat => 0 ,
729+ PeelKind :: Implicit { pat_ref_layers, .. } => pat_ref_layers,
730+ } ;
731+
732+ // Most patterns don't have reference types, so we'll want to peel all references from the
733+ // scrutinee before matching. To optimize for the common case, return early.
734+ if pat_ref_layers == 0 {
735+ return true ;
736+ }
737+ debug_assert ! (
738+ self . tcx. features( ) . deref_patterns( ) ,
739+ "Peeling for patterns with reference types is gated by `deref_patterns`."
740+ ) ;
741+
742+ // If the pattern has as many or more layers of reference as the expected type, we can match
743+ // without peeling more, *unless* we find a smart pointer that we also need to peel.
744+ // TODO: always peel `&mut`
745+ let mut expected_ref_layers = 0 ;
746+ while let ty:: Ref ( _, inner_ty, _) = * expected. kind ( ) {
747+ expected_ref_layers += 1 ;
748+ expected = inner_ty;
749+ }
750+ pat_ref_layers < expected_ref_layers || self . should_peel_smart_pointer ( peel_kind, expected)
751+ }
752+
708753 /// Determine whether `expected` is a smart pointer type that should be peeled before matching.
709754 fn should_peel_smart_pointer ( & self , peel_kind : PeelKind , expected : Ty < ' tcx > ) -> bool {
710755 // Explicit `deref!(_)` patterns match against smart pointers; don't peel in that case.
0 commit comments