@@ -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
@@ -531,24 +536,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
531
536
// If `deref_patterns` is enabled, peel a smart pointer from the scrutinee type. See the
532
537
// examples in `tests/ui/pattern/deref_patterns/`.
533
538
_ if self . tcx . features ( ) . deref_patterns ( )
534
- && let AdjustMode :: Peel { kind : PeelKind :: Implicit { until_adt } } = adjust_mode
539
+ && let AdjustMode :: Peel { kind : peel_kind } = adjust_mode
535
540
&& pat. default_binding_modes
536
- // For simplicity, only apply overloaded derefs if `expected` is a known ADT.
537
- // FIXME(deref_patterns): we'll get better diagnostics for users trying to
538
- // implicitly deref generics if we allow them here, but primitives, tuples, and
539
- // inference vars definitely should be stopped. Figure out what makes most sense.
540
- && let ty:: Adt ( scrutinee_adt, _) = * expected. kind ( )
541
- // Don't peel if the pattern type already matches the scrutinee. E.g., stop here if
542
- // matching on a `Cow<'a, T>` scrutinee with a `Cow::Owned(_)` pattern.
543
- && until_adt != Some ( scrutinee_adt. did ( ) )
544
- // At this point, the pattern isn't able to match `expected` without peeling. Check
545
- // that it implements `Deref` before assuming it's a smart pointer, to get a normal
546
- // type error instead of a missing impl error if not. This only checks for `Deref`,
547
- // not `DerefPure`: we require that too, but we want a trait error if it's missing.
548
- && let Some ( deref_trait) = self . tcx . lang_items ( ) . deref_trait ( )
549
- && self
550
- . type_implements_trait ( deref_trait, [ expected] , self . param_env )
551
- . may_apply ( ) =>
541
+ && self . should_peel_smart_pointer ( peel_kind, expected) =>
552
542
{
553
543
debug ! ( "scrutinee ty {expected:?} is a smart pointer, inserting overloaded deref" ) ;
554
544
// The scrutinee is a smart pointer; implicitly dereference it. This adds a
@@ -680,21 +670,32 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
680
670
681
671
// String and byte-string literals result in types `&str` and `&[u8]` respectively.
682
672
// All other literals result in non-reference types.
683
- // As a result, we allow `if let 0 = &&0 {}` but not `if let "foo" = &&"foo" {}`.
684
- //
685
- // Call `resolve_vars_if_possible` here for inline const blocks.
686
- PatKind :: Expr ( lt) => match self . resolve_vars_if_possible ( self . check_pat_expr_unadjusted ( lt) ) . kind ( ) {
687
- ty:: Ref ( ..) => AdjustMode :: Pass ,
688
- _ => {
689
- // Path patterns have already been handled, and inline const blocks currently
690
- // aren't possible to write, so any handling for them would be untested.
691
- if cfg ! ( debug_assertions)
692
- && self . tcx . features ( ) . deref_patterns ( )
693
- && !matches ! ( lt. kind, PatExprKind :: Lit { .. } )
694
- {
695
- 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;
696
695
}
697
- 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 ( ) }
698
699
}
699
700
} ,
700
701
@@ -720,6 +721,67 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
720
721
}
721
722
}
722
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 or `&mut` that we also need to peel.
744
+ // We don't treat `&` and `&mut` as interchangeable, but by peeling `&mut`s before matching,
745
+ // we can still, e.g., match on a `&mut str` with a string literal pattern. This is because
746
+ // string literal patterns may be used where `str` is expected.
747
+ let mut expected_ref_layers = 0 ;
748
+ while let ty:: Ref ( _, inner_ty, mutbl) = * expected. kind ( ) {
749
+ if mutbl. is_mut ( ) {
750
+ // Mutable references can't be in the final value of constants, thus they can't be
751
+ // at the head of their types, thus we should always peel `&mut`.
752
+ return true ;
753
+ }
754
+ expected_ref_layers += 1 ;
755
+ expected = inner_ty;
756
+ }
757
+ pat_ref_layers < expected_ref_layers || self . should_peel_smart_pointer ( peel_kind, expected)
758
+ }
759
+
760
+ /// Determine whether `expected` is a smart pointer type that should be peeled before matching.
761
+ fn should_peel_smart_pointer ( & self , peel_kind : PeelKind , expected : Ty < ' tcx > ) -> bool {
762
+ // Explicit `deref!(_)` patterns match against smart pointers; don't peel in that case.
763
+ if let PeelKind :: Implicit { until_adt, .. } = peel_kind
764
+ // For simplicity, only apply overloaded derefs if `expected` is a known ADT.
765
+ // FIXME(deref_patterns): we'll get better diagnostics for users trying to
766
+ // implicitly deref generics if we allow them here, but primitives, tuples, and
767
+ // inference vars definitely should be stopped. Figure out what makes most sense.
768
+ && let ty:: Adt ( scrutinee_adt, _) = * expected. kind ( )
769
+ // Don't peel if the pattern type already matches the scrutinee. E.g., stop here if
770
+ // matching on a `Cow<'a, T>` scrutinee with a `Cow::Owned(_)` pattern.
771
+ && until_adt != Some ( scrutinee_adt. did ( ) )
772
+ // At this point, the pattern isn't able to match `expected` without peeling. Check
773
+ // that it implements `Deref` before assuming it's a smart pointer, to get a normal
774
+ // type error instead of a missing impl error if not. This only checks for `Deref`,
775
+ // not `DerefPure`: we require that too, but we want a trait error if it's missing.
776
+ && let Some ( deref_trait) = self . tcx . lang_items ( ) . deref_trait ( )
777
+ && self . type_implements_trait ( deref_trait, [ expected] , self . param_env ) . may_apply ( )
778
+ {
779
+ true
780
+ } else {
781
+ false
782
+ }
783
+ }
784
+
723
785
fn check_pat_expr_unadjusted ( & self , lt : & ' tcx hir:: PatExpr < ' tcx > ) -> Ty < ' tcx > {
724
786
let ty = match & lt. kind {
725
787
rustc_hir:: PatExprKind :: Lit { lit, negated } => {
0 commit comments