@@ -177,16 +177,20 @@ enum PeelKind {
177
177
/// Only peel reference types. This is used for explicit `deref!(_)` patterns, which dereference
178
178
/// any number of `&`/`&mut` references, plus a single smart pointer.
179
179
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
+ } ,
185
189
}
186
190
187
191
impl AdjustMode {
188
192
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 } }
190
194
}
191
195
const fn peel_all ( ) -> AdjustMode {
192
196
AdjustMode :: peel_until_adt ( None )
@@ -488,9 +492,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
488
492
match pat. kind {
489
493
// Peel off a `&` or `&mut` from the scrutinee type. See the examples in
490
494
// `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
492
496
&& 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) =>
494
499
{
495
500
debug ! ( "inspecting {:?}" , expected) ;
496
501
@@ -665,21 +670,32 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
665
670
666
671
// String and byte-string literals result in types `&str` and `&[u8]` respectively.
667
672
// 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;
681
695
}
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 ( ) }
683
699
}
684
700
} ,
685
701
@@ -705,6 +721,35 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
705
721
}
706
722
}
707
723
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
+
708
753
/// Determine whether `expected` is a smart pointer type that should be peeled before matching.
709
754
fn should_peel_smart_pointer ( & self , peel_kind : PeelKind , expected : Ty < ' tcx > ) -> bool {
710
755
// Explicit `deref!(_)` patterns match against smart pointers; don't peel in that case.
0 commit comments