@@ -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