@@ -28,7 +28,7 @@ use rustc_span::hygiene::DesugaringKind;
28
28
use rustc_span:: source_map:: Spanned ;
29
29
use rustc_span:: { BytePos , DUMMY_SP , Ident , Span , kw, sym} ;
30
30
use rustc_trait_selection:: infer:: InferCtxtExt ;
31
- use rustc_trait_selection:: traits:: { ObligationCause , ObligationCauseCode } ;
31
+ use rustc_trait_selection:: traits:: { ObligationCause , ObligationCauseCode , ObligationCtxt } ;
32
32
use tracing:: { debug, instrument, trace} ;
33
33
use ty:: VariantDef ;
34
34
use ty:: adjustment:: { PatAdjust , PatAdjustment } ;
@@ -403,19 +403,38 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
403
403
let ty = self . check_pat_inner ( pat, opt_path_res, adjust_mode, expected, pat_info) ;
404
404
self . write_ty ( pat. hir_id , ty) ;
405
405
406
- // If we implicitly inserted overloaded dereferences before matching, check the pattern to
407
- // see if the dereferenced types need `DerefMut` bounds.
408
- if let Some ( derefed_tys) = self . typeck_results . borrow ( ) . pat_adjustments ( ) . get ( pat. hir_id )
409
- && derefed_tys. iter ( ) . any ( |adjust| adjust. kind == PatAdjust :: OverloadedDeref )
410
- {
411
- self . register_deref_mut_bounds_if_needed (
412
- pat. span ,
413
- pat,
414
- derefed_tys. iter ( ) . filter_map ( |adjust| match adjust. kind {
415
- PatAdjust :: OverloadedDeref => Some ( adjust. source ) ,
416
- PatAdjust :: BuiltinDeref | PatAdjust :: PinDeref => None ,
417
- } ) ,
418
- ) ;
406
+ // If we implicitly inserted overloaded dereferences and pinned dereferences before matching,
407
+ // check the pattern to see if the dereferenced types need `DerefMut` or `!Unpin` bounds.
408
+ if let Some ( derefed_tys) = self . typeck_results . borrow ( ) . pat_adjustments ( ) . get ( pat. hir_id ) {
409
+ let mut has_overloaded_deref = false ;
410
+ let mut has_pin_deref = false ;
411
+ derefed_tys. iter ( ) . for_each ( |adjust| match adjust. kind {
412
+ PatAdjust :: BuiltinDeref => { }
413
+ PatAdjust :: OverloadedDeref => has_overloaded_deref = true ,
414
+ PatAdjust :: PinDeref => has_pin_deref = true ,
415
+ } ) ;
416
+ if has_overloaded_deref {
417
+ self . register_deref_mut_bounds_if_needed (
418
+ pat. span ,
419
+ pat,
420
+ derefed_tys. iter ( ) . filter_map ( |adjust| match adjust. kind {
421
+ PatAdjust :: OverloadedDeref => Some ( adjust. source ) ,
422
+ PatAdjust :: BuiltinDeref | PatAdjust :: PinDeref => None ,
423
+ } ) ,
424
+ ) ;
425
+ }
426
+ if has_pin_deref {
427
+ self . register_not_unpin_bounds_if_needed (
428
+ pat. span ,
429
+ pat,
430
+ derefed_tys. iter ( ) . filter_map ( |adjust| match adjust. kind {
431
+ PatAdjust :: BuiltinDeref | PatAdjust :: OverloadedDeref => None ,
432
+ PatAdjust :: PinDeref => {
433
+ Some ( adjust. source . pinned_ref ( ) . expect ( "expected pinned reference" ) . 0 )
434
+ }
435
+ } ) ,
436
+ ) ;
437
+ }
419
438
}
420
439
421
440
// (note_1): In most of the cases where (note_1) is referenced
@@ -553,30 +572,34 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
553
572
&& let & ty:: Ref ( _, inner_ty, inner_mutability) = pinned_ty. kind ( ) =>
554
573
{
555
574
debug ! ( "scrutinee ty {expected:?} is a pinned reference, inserting pin deref" ) ;
556
- // Preserve the pinned type. We'll need it later during THIR lowering.
557
- self . typeck_results
558
- . borrow_mut ( )
559
- . pat_adjustments_mut ( )
560
- . entry ( pat. hir_id )
561
- . or_default ( )
562
- . push ( PatAdjustment { kind : PatAdjust :: PinDeref , source : expected } ) ;
563
575
564
576
let binding_mode = adjust_binding_mode ( Pinnedness :: Pinned , inner_mutability) ;
565
577
// If the pinnedness is `Not`, it means the pattern is unpinned
566
578
// and thus requires an `Unpin` bound.
567
579
if binding_mode == ByRef :: Yes ( Pinnedness :: Not , Mutability :: Mut ) {
568
580
self . register_bound (
569
581
inner_ty,
570
- self . tcx . require_lang_item ( hir:: LangItem :: Unpin , Some ( pat. span ) ) ,
582
+ self . tcx . require_lang_item ( hir:: LangItem :: Unpin , pat. span ) ,
571
583
self . misc ( pat. span ) ,
572
- ) ;
584
+ )
573
585
}
586
+ // Once we've checked `pat`, we'll add a `!Unpin` bound if it contains any
587
+ // `ref pin` bindings. See `Self::register_not_unpin_bounds_if_needed`.
588
+
589
+ debug ! ( "default binding mode is now {:?}" , binding_mode) ;
590
+
574
591
// Use the old pat info to keep `current_depth` to its old value.
575
592
let new_pat_info = PatInfo { binding_mode, ..old_pat_info } ;
576
- // Recurse with the new expected type.
577
- // using `break` instead of `return` in case where any shared codes are added
578
- // after the `match pat.kind {}`.
579
- self . check_pat_inner ( pat, opt_path_res, adjust_mode, inner_ty, new_pat_info)
593
+
594
+ self . check_deref_pattern (
595
+ pat,
596
+ opt_path_res,
597
+ adjust_mode,
598
+ expected,
599
+ inner_ty,
600
+ PatAdjust :: PinDeref ,
601
+ new_pat_info,
602
+ )
580
603
}
581
604
// If `deref_patterns` is enabled, peel a smart pointer from the scrutinee type. See the
582
605
// examples in `tests/ui/pattern/deref_patterns/`.
@@ -585,35 +608,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
585
608
&& pat. default_binding_modes
586
609
&& self . should_peel_smart_pointer ( peel_kind, expected) =>
587
610
{
588
- debug ! ( "scrutinee ty {expected:?} is a smart pointer, inserting overloaded deref" ) ;
611
+ debug ! ( "scrutinee ty {expected:?} is a smart pointer, inserting pin deref" ) ;
612
+
589
613
// The scrutinee is a smart pointer; implicitly dereference it. This adds a
590
614
// requirement that `expected: DerefPure`.
591
- let mut inner_ty = self . deref_pat_target ( pat. span , expected) ;
615
+ let inner_ty = self . deref_pat_target ( pat. span , expected) ;
592
616
// Once we've checked `pat`, we'll add a `DerefMut` bound if it contains any
593
617
// `ref mut` bindings. See `Self::register_deref_mut_bounds_if_needed`.
594
618
595
- let mut typeck_results = self . typeck_results . borrow_mut ( ) ;
596
- let mut pat_adjustments_table = typeck_results. pat_adjustments_mut ( ) ;
597
- let pat_adjustments = pat_adjustments_table. entry ( pat. hir_id ) . or_default ( ) ;
598
- // We may reach the recursion limit if a user matches on a type `T` satisfying
599
- // `T: Deref<Target = T>`; error gracefully in this case.
600
- // FIXME(deref_patterns): If `deref_patterns` stabilizes, it may make sense to move
601
- // this check out of this branch. Alternatively, this loop could be implemented with
602
- // autoderef and this check removed. For now though, don't break code compiling on
603
- // stable with lots of `&`s and a low recursion limit, if anyone's done that.
604
- if self . tcx . recursion_limit ( ) . value_within_limit ( pat_adjustments. len ( ) ) {
605
- // Preserve the smart pointer type for THIR lowering and closure upvar analysis.
606
- pat_adjustments
607
- . push ( PatAdjustment { kind : PatAdjust :: OverloadedDeref , source : expected } ) ;
608
- } else {
609
- let guar = report_autoderef_recursion_limit_error ( self . tcx , pat. span , expected) ;
610
- inner_ty = Ty :: new_error ( self . tcx , guar) ;
611
- }
612
- drop ( typeck_results) ;
613
-
614
- // Recurse, using the old pat info to keep `current_depth` to its old value.
615
- // Peeling smart pointers does not update the default binding mode.
616
- self . check_pat_inner ( pat, opt_path_res, adjust_mode, inner_ty, old_pat_info)
619
+ self . check_deref_pattern (
620
+ pat,
621
+ opt_path_res,
622
+ adjust_mode,
623
+ expected,
624
+ inner_ty,
625
+ PatAdjust :: OverloadedDeref ,
626
+ old_pat_info,
627
+ )
617
628
}
618
629
PatKind :: Missing | PatKind :: Wild | PatKind :: Err ( _) => expected,
619
630
// We allow any type here; we ensure that the type is uninhabited during match checking.
@@ -684,6 +695,44 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
684
695
}
685
696
}
686
697
698
+ fn check_deref_pattern (
699
+ & self ,
700
+ pat : & ' tcx Pat < ' tcx > ,
701
+ opt_path_res : Option < Result < ResolvedPat < ' tcx > , ErrorGuaranteed > > ,
702
+ adjust_mode : AdjustMode ,
703
+ expected : Ty < ' tcx > ,
704
+ mut inner_ty : Ty < ' tcx > ,
705
+ pat_adjust_kind : PatAdjust ,
706
+ pat_info : PatInfo < ' tcx > ,
707
+ ) -> Ty < ' tcx > {
708
+ debug_assert ! (
709
+ !matches!( pat_adjust_kind, PatAdjust :: BuiltinDeref ) ,
710
+ "unexpected deref pattern for builtin reference type {expected:?}" ,
711
+ ) ;
712
+
713
+ let mut typeck_results = self . typeck_results . borrow_mut ( ) ;
714
+ let mut pat_adjustments_table = typeck_results. pat_adjustments_mut ( ) ;
715
+ let pat_adjustments = pat_adjustments_table. entry ( pat. hir_id ) . or_default ( ) ;
716
+ // We may reach the recursion limit if a user matches on a type `T` satisfying
717
+ // `T: Deref<Target = T>`; error gracefully in this case.
718
+ // FIXME(deref_patterns): If `deref_patterns` stabilizes, it may make sense to move
719
+ // this check out of this branch. Alternatively, this loop could be implemented with
720
+ // autoderef and this check removed. For now though, don't break code compiling on
721
+ // stable with lots of `&`s and a low recursion limit, if anyone's done that.
722
+ if self . tcx . recursion_limit ( ) . value_within_limit ( pat_adjustments. len ( ) ) {
723
+ // Preserve the smart pointer type for THIR lowering and closure upvar analysis.
724
+ pat_adjustments. push ( PatAdjustment { kind : pat_adjust_kind, source : expected } ) ;
725
+ } else {
726
+ let guar = report_autoderef_recursion_limit_error ( self . tcx , pat. span , expected) ;
727
+ inner_ty = Ty :: new_error ( self . tcx , guar) ;
728
+ }
729
+ drop ( typeck_results) ;
730
+
731
+ // Recurse, using the old pat info to keep `current_depth` to its old value.
732
+ // Peeling smart pointers does not update the default binding mode.
733
+ self . check_pat_inner ( pat, opt_path_res, adjust_mode, inner_ty, pat_info)
734
+ }
735
+
687
736
/// How should the binding mode and expected type be adjusted?
688
737
///
689
738
/// When the pattern contains a path, `opt_path_res` must be `Some(path_res)`.
@@ -1195,7 +1244,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
1195
1244
// Wrapping the type into `Pin` if the binding is like `ref pin const|mut x`
1196
1245
ByRef :: Yes ( Pinnedness :: Pinned , mutbl) => Ty :: new_adt (
1197
1246
self . tcx ,
1198
- self . tcx . adt_def ( self . tcx . require_lang_item ( hir:: LangItem :: Pin , Some ( pat. span ) ) ) ,
1247
+ self . tcx . adt_def ( self . tcx . require_lang_item ( hir:: LangItem :: Pin , pat. span ) ) ,
1199
1248
self . tcx . mk_args ( & [ self . new_ref_ty ( pat. span , mutbl, expected) . into ( ) ] ) ,
1200
1249
) ,
1201
1250
// Otherwise, the type of x is the expected type `T`.
@@ -2638,6 +2687,44 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
2638
2687
}
2639
2688
}
2640
2689
2690
+ /// Check if the interior of a pin pattern (either explicit or implicit) has any `ref pin`
2691
+ /// bindings of non-`Unpin` types, which would require `!Unpin` to be emitted.
2692
+ fn register_not_unpin_bounds_if_needed (
2693
+ & self ,
2694
+ span : Span ,
2695
+ inner : & ' tcx Pat < ' tcx > ,
2696
+ derefed_tys : impl IntoIterator < Item = Ty < ' tcx > > ,
2697
+ ) {
2698
+ // Check if there are subpatterns with `ref pin` binding modes of non-`Unpin` types.
2699
+ let unpin = self . tcx . require_lang_item ( hir:: LangItem :: Unpin , span) ;
2700
+ let cause = self . misc ( span) ;
2701
+ let unpin_obligations = self . probe ( |_| {
2702
+ let ocx = ObligationCtxt :: new ( & self ) ;
2703
+ self . typeck_results . borrow ( ) . pat_walk_ref_pin_binding_of_non_unpin_type ( inner, |ty| {
2704
+ let ty = ocx
2705
+ . normalize ( & cause, self . param_env , ty)
2706
+ . pinned_ref ( )
2707
+ . expect ( "expect pinned reference" )
2708
+ . 0 ;
2709
+ debug ! ( "check if `Unpin` is implemented for `{ty:?}`" ) ;
2710
+ ocx. register_bound ( cause. clone ( ) , self . param_env , ty, unpin) ;
2711
+ } ) ;
2712
+ ocx. select_all_or_error ( )
2713
+ } ) ;
2714
+
2715
+ // If any, the current pattern type should implement `!Unpin`.
2716
+ if !unpin_obligations. is_empty ( ) {
2717
+ for pinned_derefed_ty in derefed_tys {
2718
+ debug ! ( "register `!Unpin` for `{pinned_derefed_ty:?}`" ) ;
2719
+ self . register_negative_bound (
2720
+ pinned_derefed_ty,
2721
+ self . tcx . require_lang_item ( hir:: LangItem :: Unpin , span) ,
2722
+ self . misc ( span) ,
2723
+ ) ;
2724
+ }
2725
+ }
2726
+ }
2727
+
2641
2728
// Precondition: Pat is Ref(inner)
2642
2729
fn check_pat_ref (
2643
2730
& self ,
@@ -2664,7 +2751,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
2664
2751
expected = self . try_structurally_resolve_type ( pat. span , expected) ;
2665
2752
// Determine whether we're consuming an inherited reference and resetting the default
2666
2753
// binding mode, based on edition and enabled experimental features.
2667
- if let ByRef :: Yes ( _, inh_mut) = pat_info. binding_mode {
2754
+ // FIXME(pin_ergonomics): since `&pin` pattern is supported, the condition here
2755
+ // should be adjusted to `pat_pin == inh_pin`
2756
+ if let ByRef :: Yes ( Pinnedness :: Not , inh_mut) = pat_info. binding_mode {
2668
2757
match self . ref_pat_matches_inherited_ref ( pat. span . edition ( ) ) {
2669
2758
InheritedRefMatchRule :: EatOuter => {
2670
2759
// ref pattern attempts to consume inherited reference
@@ -2683,9 +2772,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
2683
2772
return expected;
2684
2773
}
2685
2774
InheritedRefMatchRule :: EatInner => {
2686
- if let ty:: Ref ( _, _, r_mutbl) = * expected. kind ( )
2687
- && pat_mutbl <= r_mutbl
2688
- {
2775
+ let expected_ref_or_pinned_ref = || {
2776
+ if self . tcx . features ( ) . pin_ergonomics ( )
2777
+ && let Some ( ty:: Ref ( _, _, r_mutbl) ) =
2778
+ expected. pinned_ty ( ) . map ( |ty| * ty. kind ( ) )
2779
+ && pat_mutbl <= r_mutbl
2780
+ {
2781
+ return Some ( ( Pinnedness :: Pinned , r_mutbl) ) ;
2782
+ }
2783
+ if let ty:: Ref ( _, _, r_mutbl) = * expected. kind ( )
2784
+ && pat_mutbl <= r_mutbl
2785
+ {
2786
+ return Some ( ( Pinnedness :: Not , r_mutbl) ) ;
2787
+ }
2788
+ None
2789
+ } ;
2790
+ if let Some ( ( _, r_mutbl) ) = expected_ref_or_pinned_ref ( ) {
2689
2791
// Match against the reference type; don't consume the inherited ref.
2690
2792
// NB: The check for compatible pattern and ref type mutability assumes that
2691
2793
// `&` patterns can match against mutable references (RFC 3627, Rule 5). If
0 commit comments