Skip to content

Commit 260b960

Browse files
committed
require T: !Unpin for &pin mut T to be projected to &pin mut T.U where U: ?Unpin
1 parent 833fd1e commit 260b960

25 files changed

+980
-1731
lines changed

compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ use rustc_lint::builtin::SELF_CONSTRUCTOR_FROM_OUTER_ITEM;
2323
use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability};
2424
use rustc_middle::ty::{
2525
self, AdtKind, CanonicalUserType, GenericArgsRef, GenericParamDefKind, IsIdentity,
26-
SizedTraitKind, Ty, TyCtxt, TypeFoldable, TypeVisitable, TypeVisitableExt, UserArgs,
26+
SizedTraitKind, Ty, TyCtxt, TypeFoldable, TypeVisitable, TypeVisitableExt, Upcast, UserArgs,
2727
UserSelfTy,
2828
};
2929
use rustc_middle::{bug, span_bug};
@@ -469,6 +469,29 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
469469
}
470470
}
471471

472+
pub(crate) fn register_negative_bound(
473+
&self,
474+
ty: Ty<'tcx>,
475+
def_id: DefId,
476+
cause: traits::ObligationCause<'tcx>,
477+
) {
478+
if !ty.references_error() {
479+
let trait_ref = ty::TraitRef::new(self.tcx, def_id, [ty]);
480+
let predicate =
481+
ty::TraitPredicate { trait_ref, polarity: ty::PredicatePolarity::Negative }
482+
.upcast(self.tcx);
483+
self.fulfillment_cx.borrow_mut().register_predicate_obligation(
484+
self,
485+
traits::Obligation {
486+
cause,
487+
recursion_depth: 0,
488+
param_env: self.param_env,
489+
predicate,
490+
},
491+
);
492+
}
493+
}
494+
472495
pub(crate) fn lower_ty(&self, hir_ty: &hir::Ty<'tcx>) -> LoweredTy<'tcx> {
473496
let ty = self.lowerer().lower_ty(hir_ty);
474497
self.register_wf_obligation(ty.into(), hir_ty.span, ObligationCauseCode::WellFormed(None));

compiler/rustc_hir_typeck/src/pat.rs

Lines changed: 158 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ use rustc_span::hygiene::DesugaringKind;
2828
use rustc_span::source_map::Spanned;
2929
use rustc_span::{BytePos, DUMMY_SP, Ident, Span, kw, sym};
3030
use rustc_trait_selection::infer::InferCtxtExt;
31-
use rustc_trait_selection::traits::{ObligationCause, ObligationCauseCode};
31+
use rustc_trait_selection::traits::{ObligationCause, ObligationCauseCode, ObligationCtxt};
3232
use tracing::{debug, instrument, trace};
3333
use ty::VariantDef;
3434
use ty::adjustment::{PatAdjust, PatAdjustment};
@@ -403,19 +403,38 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
403403
let ty = self.check_pat_inner(pat, opt_path_res, adjust_mode, expected, pat_info);
404404
self.write_ty(pat.hir_id, ty);
405405

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+
}
419438
}
420439

421440
// (note_1): In most of the cases where (note_1) is referenced
@@ -553,30 +572,34 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
553572
&& let &ty::Ref(_, inner_ty, inner_mutability) = pinned_ty.kind() =>
554573
{
555574
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 });
563575

564576
let binding_mode = adjust_binding_mode(Pinnedness::Pinned, inner_mutability);
565577
// If the pinnedness is `Not`, it means the pattern is unpinned
566578
// and thus requires an `Unpin` bound.
567579
if binding_mode == ByRef::Yes(Pinnedness::Not, Mutability::Mut) {
568580
self.register_bound(
569581
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),
571583
self.misc(pat.span),
572-
);
584+
)
573585
}
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+
574591
// Use the old pat info to keep `current_depth` to its old value.
575592
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+
)
580603
}
581604
// If `deref_patterns` is enabled, peel a smart pointer from the scrutinee type. See the
582605
// examples in `tests/ui/pattern/deref_patterns/`.
@@ -585,35 +608,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
585608
&& pat.default_binding_modes
586609
&& self.should_peel_smart_pointer(peel_kind, expected) =>
587610
{
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+
589613
// The scrutinee is a smart pointer; implicitly dereference it. This adds a
590614
// 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);
592616
// Once we've checked `pat`, we'll add a `DerefMut` bound if it contains any
593617
// `ref mut` bindings. See `Self::register_deref_mut_bounds_if_needed`.
594618

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+
)
617628
}
618629
PatKind::Missing | PatKind::Wild | PatKind::Err(_) => expected,
619630
// 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> {
684695
}
685696
}
686697

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+
687736
/// How should the binding mode and expected type be adjusted?
688737
///
689738
/// When the pattern contains a path, `opt_path_res` must be `Some(path_res)`.
@@ -1195,7 +1244,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
11951244
// Wrapping the type into `Pin` if the binding is like `ref pin const|mut x`
11961245
ByRef::Yes(Pinnedness::Pinned, mutbl) => Ty::new_adt(
11971246
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)),
11991248
self.tcx.mk_args(&[self.new_ref_ty(pat.span, mutbl, expected).into()]),
12001249
),
12011250
// Otherwise, the type of x is the expected type `T`.
@@ -2638,6 +2687,44 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
26382687
}
26392688
}
26402689

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+
26412728
// Precondition: Pat is Ref(inner)
26422729
fn check_pat_ref(
26432730
&self,
@@ -2664,7 +2751,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
26642751
expected = self.try_structurally_resolve_type(pat.span, expected);
26652752
// Determine whether we're consuming an inherited reference and resetting the default
26662753
// 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 {
26682757
match self.ref_pat_matches_inherited_ref(pat.span.edition()) {
26692758
InheritedRefMatchRule::EatOuter => {
26702759
// ref pattern attempts to consume inherited reference
@@ -2683,9 +2772,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
26832772
return expected;
26842773
}
26852774
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() {
26892791
// Match against the reference type; don't consume the inherited ref.
26902792
// NB: The check for compatible pattern and ref type mutability assumes that
26912793
// `&` patterns can match against mutable references (RFC 3627, Rule 5). If

compiler/rustc_middle/src/ty/sty.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1293,6 +1293,16 @@ impl<'tcx> Ty<'tcx> {
12931293
}
12941294
}
12951295

1296+
pub fn pinned_ref(self) -> Option<(Ty<'tcx>, ty::Mutability)> {
1297+
if let Adt(def, args) = self.kind()
1298+
&& def.is_pin()
1299+
&& let &ty::Ref(_, ty, mutbl) = args.type_at(0).kind()
1300+
{
1301+
return Some((ty, mutbl));
1302+
}
1303+
None
1304+
}
1305+
12961306
/// Panics if called on any type other than `Box<T>`.
12971307
pub fn expect_boxed_ty(self) -> Ty<'tcx> {
12981308
self.boxed_ty()

compiler/rustc_middle/src/ty/typeck_results.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -476,6 +476,32 @@ impl<'tcx> TypeckResults<'tcx> {
476476
has_ref_mut
477477
}
478478

479+
/// Visits the pattern recursively whether it contains a `ref pin` binding
480+
/// of non-`Unpin` type in it.
481+
///
482+
/// This is used to determined whether a `&pin` pattern should emit a `!Unpin`
483+
/// call for its pattern scrutinee.
484+
///
485+
/// This is computed from the typeck results since we want to make
486+
/// sure to apply any match-ergonomics adjustments, which we cannot
487+
/// determine from the HIR alone.
488+
pub fn pat_walk_ref_pin_binding_of_non_unpin_type<'a>(
489+
&self,
490+
pat: &hir::Pat<'_>,
491+
mut ty_visitor: impl FnMut(Ty<'tcx>) + 'a,
492+
) {
493+
pat.walk(|pat| {
494+
if let hir::PatKind::Binding(_, id, _, _) = pat.kind
495+
&& let Some(BindingMode(ByRef::Yes(Pinnedness::Pinned, _), _)) =
496+
self.pat_binding_modes().get(id)
497+
{
498+
let ty = self.pat_ty(pat);
499+
ty_visitor(ty);
500+
}
501+
true
502+
});
503+
}
504+
479505
/// How should a deref pattern find the place for its inner pattern to match on?
480506
///
481507
/// In most cases, if the pattern recursively contains a `ref mut` binding, we find the inner

0 commit comments

Comments
 (0)