Skip to content

Commit 03f7970

Browse files
committed
require T: !Unpin for &pin mut T to be projected to &pin mut T.U where U: ?Unpin
1 parent 296a363 commit 03f7970

25 files changed

+1015
-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};
@@ -463,6 +463,29 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
463463
}
464464
}
465465

466+
pub(crate) fn register_negative_bound(
467+
&self,
468+
ty: Ty<'tcx>,
469+
def_id: DefId,
470+
cause: traits::ObligationCause<'tcx>,
471+
) {
472+
if !ty.references_error() {
473+
let trait_ref = ty::TraitRef::new(self.tcx, def_id, [ty]);
474+
let predicate =
475+
ty::TraitPredicate { trait_ref, polarity: ty::PredicatePolarity::Negative }
476+
.upcast(self.tcx);
477+
self.fulfillment_cx.borrow_mut().register_predicate_obligation(
478+
self,
479+
traits::Obligation {
480+
cause,
481+
recursion_depth: 0,
482+
param_env: self.param_env,
483+
predicate,
484+
},
485+
);
486+
}
487+
}
488+
466489
pub(crate) fn lower_ty(&self, hir_ty: &hir::Ty<'tcx>) -> LoweredTy<'tcx> {
467490
let ty = self.lowerer().lower_ty(hir_ty);
468491
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
@@ -27,7 +27,7 @@ use rustc_span::edition::Edition;
2727
use rustc_span::source_map::Spanned;
2828
use rustc_span::{BytePos, DUMMY_SP, Ident, Span, kw, sym};
2929
use rustc_trait_selection::infer::InferCtxtExt;
30-
use rustc_trait_selection::traits::{ObligationCause, ObligationCauseCode};
30+
use rustc_trait_selection::traits::{ObligationCause, ObligationCauseCode, ObligationCtxt};
3131
use tracing::{debug, instrument, trace};
3232
use ty::VariantDef;
3333
use ty::adjustment::{PatAdjust, PatAdjustment};
@@ -402,19 +402,38 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
402402
let ty = self.check_pat_inner(pat, opt_path_res, adjust_mode, expected, pat_info);
403403
self.write_ty(pat.hir_id, ty);
404404

405-
// If we implicitly inserted overloaded dereferences before matching, check the pattern to
406-
// see if the dereferenced types need `DerefMut` bounds.
407-
if let Some(derefed_tys) = self.typeck_results.borrow().pat_adjustments().get(pat.hir_id)
408-
&& derefed_tys.iter().any(|adjust| adjust.kind == PatAdjust::OverloadedDeref)
409-
{
410-
self.register_deref_mut_bounds_if_needed(
411-
pat.span,
412-
pat,
413-
derefed_tys.iter().filter_map(|adjust| match adjust.kind {
414-
PatAdjust::OverloadedDeref => Some(adjust.source),
415-
PatAdjust::BuiltinDeref | PatAdjust::PinDeref => None,
416-
}),
417-
);
405+
// If we implicitly inserted overloaded dereferences and pinned dereferences before matching,
406+
// check the pattern to see if the dereferenced types need `DerefMut` or `!Unpin` bounds.
407+
if let Some(derefed_tys) = self.typeck_results.borrow().pat_adjustments().get(pat.hir_id) {
408+
let mut has_overloaded_deref = false;
409+
let mut has_pin_deref = false;
410+
derefed_tys.iter().for_each(|adjust| match adjust.kind {
411+
PatAdjust::BuiltinDeref => {}
412+
PatAdjust::OverloadedDeref => has_overloaded_deref = true,
413+
PatAdjust::PinDeref => has_pin_deref = true,
414+
});
415+
if has_overloaded_deref {
416+
self.register_deref_mut_bounds_if_needed(
417+
pat.span,
418+
pat,
419+
derefed_tys.iter().filter_map(|adjust| match adjust.kind {
420+
PatAdjust::OverloadedDeref => Some(adjust.source),
421+
PatAdjust::BuiltinDeref | PatAdjust::PinDeref => None,
422+
}),
423+
);
424+
}
425+
if has_pin_deref {
426+
self.register_not_unpin_bounds_if_needed(
427+
pat.span,
428+
pat,
429+
derefed_tys.iter().filter_map(|adjust| match adjust.kind {
430+
PatAdjust::BuiltinDeref | PatAdjust::OverloadedDeref => None,
431+
PatAdjust::PinDeref => {
432+
Some(adjust.source.pinned_ref().expect("expected pinned reference").0)
433+
}
434+
}),
435+
);
436+
}
418437
}
419438

420439
// (note_1): In most of the cases where (note_1) is referenced
@@ -552,30 +571,34 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
552571
&& let &ty::Ref(_, inner_ty, inner_mutability) = pinned_ty.kind() =>
553572
{
554573
debug!("scrutinee ty {expected:?} is a pinned reference, inserting pin deref");
555-
// Preserve the pinned type. We'll need it later during THIR lowering.
556-
self.typeck_results
557-
.borrow_mut()
558-
.pat_adjustments_mut()
559-
.entry(pat.hir_id)
560-
.or_default()
561-
.push(PatAdjustment { kind: PatAdjust::PinDeref, source: expected });
562574

563575
let binding_mode = adjust_binding_mode(Pinnedness::Pinned, inner_mutability);
564576
// If the pinnedness is `Not`, it means the pattern is unpinned
565577
// and thus requires an `Unpin` bound.
566578
if binding_mode == ByRef::Yes(Pinnedness::Not, Mutability::Mut) {
567579
self.register_bound(
568580
inner_ty,
569-
self.tcx.require_lang_item(hir::LangItem::Unpin, Some(pat.span)),
581+
self.tcx.require_lang_item(hir::LangItem::Unpin, pat.span),
570582
self.misc(pat.span),
571-
);
583+
)
572584
}
585+
// Once we've checked `pat`, we'll add a `!Unpin` bound if it contains any
586+
// `ref pin` bindings. See `Self::register_not_unpin_bounds_if_needed`.
587+
588+
debug!("default binding mode is now {:?}", binding_mode);
589+
573590
// Use the old pat info to keep `current_depth` to its old value.
574591
let new_pat_info = PatInfo { binding_mode, ..old_pat_info };
575-
// Recurse with the new expected type.
576-
// using `break` instead of `return` in case where any shared codes are added
577-
// after the `match pat.kind {}`.
578-
self.check_pat_inner(pat, opt_path_res, adjust_mode, inner_ty, new_pat_info)
592+
593+
self.check_deref_pattern(
594+
pat,
595+
opt_path_res,
596+
adjust_mode,
597+
expected,
598+
inner_ty,
599+
PatAdjust::PinDeref,
600+
new_pat_info,
601+
)
579602
}
580603
// If `deref_patterns` is enabled, peel a smart pointer from the scrutinee type. See the
581604
// examples in `tests/ui/pattern/deref_patterns/`.
@@ -584,35 +607,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
584607
&& pat.default_binding_modes
585608
&& self.should_peel_smart_pointer(peel_kind, expected) =>
586609
{
587-
debug!("scrutinee ty {expected:?} is a smart pointer, inserting overloaded deref");
610+
debug!("scrutinee ty {expected:?} is a smart pointer, inserting pin deref");
611+
588612
// The scrutinee is a smart pointer; implicitly dereference it. This adds a
589613
// requirement that `expected: DerefPure`.
590-
let mut inner_ty = self.deref_pat_target(pat.span, expected);
614+
let inner_ty = self.deref_pat_target(pat.span, expected);
591615
// Once we've checked `pat`, we'll add a `DerefMut` bound if it contains any
592616
// `ref mut` bindings. See `Self::register_deref_mut_bounds_if_needed`.
593617

594-
let mut typeck_results = self.typeck_results.borrow_mut();
595-
let mut pat_adjustments_table = typeck_results.pat_adjustments_mut();
596-
let pat_adjustments = pat_adjustments_table.entry(pat.hir_id).or_default();
597-
// We may reach the recursion limit if a user matches on a type `T` satisfying
598-
// `T: Deref<Target = T>`; error gracefully in this case.
599-
// FIXME(deref_patterns): If `deref_patterns` stabilizes, it may make sense to move
600-
// this check out of this branch. Alternatively, this loop could be implemented with
601-
// autoderef and this check removed. For now though, don't break code compiling on
602-
// stable with lots of `&`s and a low recursion limit, if anyone's done that.
603-
if self.tcx.recursion_limit().value_within_limit(pat_adjustments.len()) {
604-
// Preserve the smart pointer type for THIR lowering and closure upvar analysis.
605-
pat_adjustments
606-
.push(PatAdjustment { kind: PatAdjust::OverloadedDeref, source: expected });
607-
} else {
608-
let guar = report_autoderef_recursion_limit_error(self.tcx, pat.span, expected);
609-
inner_ty = Ty::new_error(self.tcx, guar);
610-
}
611-
drop(typeck_results);
612-
613-
// Recurse, using the old pat info to keep `current_depth` to its old value.
614-
// Peeling smart pointers does not update the default binding mode.
615-
self.check_pat_inner(pat, opt_path_res, adjust_mode, inner_ty, old_pat_info)
618+
self.check_deref_pattern(
619+
pat,
620+
opt_path_res,
621+
adjust_mode,
622+
expected,
623+
inner_ty,
624+
PatAdjust::OverloadedDeref,
625+
old_pat_info,
626+
)
616627
}
617628
PatKind::Missing | PatKind::Wild | PatKind::Err(_) => expected,
618629
// We allow any type here; we ensure that the type is uninhabited during match checking.
@@ -683,6 +694,44 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
683694
}
684695
}
685696

697+
fn check_deref_pattern(
698+
&self,
699+
pat: &'tcx Pat<'tcx>,
700+
opt_path_res: Option<Result<ResolvedPat<'tcx>, ErrorGuaranteed>>,
701+
adjust_mode: AdjustMode,
702+
expected: Ty<'tcx>,
703+
mut inner_ty: Ty<'tcx>,
704+
pat_adjust_kind: PatAdjust,
705+
pat_info: PatInfo<'tcx>,
706+
) -> Ty<'tcx> {
707+
debug_assert!(
708+
!matches!(pat_adjust_kind, PatAdjust::BuiltinDeref),
709+
"unexpected deref pattern for builtin reference type {expected:?}",
710+
);
711+
712+
let mut typeck_results = self.typeck_results.borrow_mut();
713+
let mut pat_adjustments_table = typeck_results.pat_adjustments_mut();
714+
let pat_adjustments = pat_adjustments_table.entry(pat.hir_id).or_default();
715+
// We may reach the recursion limit if a user matches on a type `T` satisfying
716+
// `T: Deref<Target = T>`; error gracefully in this case.
717+
// FIXME(deref_patterns): If `deref_patterns` stabilizes, it may make sense to move
718+
// this check out of this branch. Alternatively, this loop could be implemented with
719+
// autoderef and this check removed. For now though, don't break code compiling on
720+
// stable with lots of `&`s and a low recursion limit, if anyone's done that.
721+
if self.tcx.recursion_limit().value_within_limit(pat_adjustments.len()) {
722+
// Preserve the smart pointer type for THIR lowering and closure upvar analysis.
723+
pat_adjustments.push(PatAdjustment { kind: pat_adjust_kind, source: expected });
724+
} else {
725+
let guar = report_autoderef_recursion_limit_error(self.tcx, pat.span, expected);
726+
inner_ty = Ty::new_error(self.tcx, guar);
727+
}
728+
drop(typeck_results);
729+
730+
// Recurse, using the old pat info to keep `current_depth` to its old value.
731+
// Peeling smart pointers does not update the default binding mode.
732+
self.check_pat_inner(pat, opt_path_res, adjust_mode, inner_ty, pat_info)
733+
}
734+
686735
/// How should the binding mode and expected type be adjusted?
687736
///
688737
/// When the pattern contains a path, `opt_path_res` must be `Some(path_res)`.
@@ -1185,7 +1234,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
11851234
// Wrapping the type into `Pin` if the binding is like `ref pin const|mut x`
11861235
ByRef::Yes(Pinnedness::Pinned, mutbl) => Ty::new_adt(
11871236
self.tcx,
1188-
self.tcx.adt_def(self.tcx.require_lang_item(hir::LangItem::Pin, Some(pat.span))),
1237+
self.tcx.adt_def(self.tcx.require_lang_item(hir::LangItem::Pin, pat.span)),
11891238
self.tcx.mk_args(&[self.new_ref_ty(pat.span, mutbl, expected).into()]),
11901239
),
11911240
// Otherwise, the type of x is the expected type `T`.
@@ -2628,6 +2677,44 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
26282677
}
26292678
}
26302679

2680+
/// Check if the interior of a pin pattern (either explicit or implicit) has any `ref pin`
2681+
/// bindings of non-`Unpin` types, which would require `!Unpin` to be emitted.
2682+
fn register_not_unpin_bounds_if_needed(
2683+
&self,
2684+
span: Span,
2685+
inner: &'tcx Pat<'tcx>,
2686+
derefed_tys: impl IntoIterator<Item = Ty<'tcx>>,
2687+
) {
2688+
// Check if there are subpatterns with `ref pin` binding modes of non-`Unpin` types.
2689+
let unpin = self.tcx.require_lang_item(hir::LangItem::Unpin, span);
2690+
let cause = self.misc(span);
2691+
let unpin_obligations = self.probe(|_| {
2692+
let ocx = ObligationCtxt::new(&self);
2693+
self.typeck_results.borrow().pat_walk_ref_pin_binding_of_non_unpin_type(inner, |ty| {
2694+
let ty = ocx
2695+
.normalize(&cause, self.param_env, ty)
2696+
.pinned_ref()
2697+
.expect("expect pinned reference")
2698+
.0;
2699+
debug!("check if `Unpin` is implemented for `{ty:?}`");
2700+
ocx.register_bound(cause.clone(), self.param_env, ty, unpin);
2701+
});
2702+
ocx.select_all_or_error()
2703+
});
2704+
2705+
// If any, the current pattern type should implement `!Unpin`.
2706+
if !unpin_obligations.is_empty() {
2707+
for pinned_derefed_ty in derefed_tys {
2708+
debug!("register `!Unpin` for `{pinned_derefed_ty:?}`");
2709+
self.register_negative_bound(
2710+
pinned_derefed_ty,
2711+
self.tcx.require_lang_item(hir::LangItem::Unpin, span),
2712+
self.misc(span),
2713+
);
2714+
}
2715+
}
2716+
}
2717+
26312718
// Precondition: Pat is Ref(inner)
26322719
fn check_pat_ref(
26332720
&self,
@@ -2654,7 +2741,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
26542741
expected = self.try_structurally_resolve_type(pat.span, expected);
26552742
// Determine whether we're consuming an inherited reference and resetting the default
26562743
// binding mode, based on edition and enabled experimental features.
2657-
if let ByRef::Yes(_, inh_mut) = pat_info.binding_mode {
2744+
// FIXME(pin_ergonomics): since `&pin` pattern is supported, the condition here
2745+
// should be adjusted to `pat_pin == inh_pin`
2746+
if let ByRef::Yes(Pinnedness::Not, inh_mut) = pat_info.binding_mode {
26582747
match self.ref_pat_matches_inherited_ref(pat.span.edition()) {
26592748
InheritedRefMatchRule::EatOuter => {
26602749
// ref pattern attempts to consume inherited reference
@@ -2673,9 +2762,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
26732762
return expected;
26742763
}
26752764
InheritedRefMatchRule::EatInner => {
2676-
if let ty::Ref(_, _, r_mutbl) = *expected.kind()
2677-
&& pat_mutbl <= r_mutbl
2678-
{
2765+
let expected_ref_or_pinned_ref = || {
2766+
if self.tcx.features().pin_ergonomics()
2767+
&& let Some(ty::Ref(_, _, r_mutbl)) =
2768+
expected.pinned_ty().map(|ty| *ty.kind())
2769+
&& pat_mutbl <= r_mutbl
2770+
{
2771+
return Some((Pinnedness::Pinned, r_mutbl));
2772+
}
2773+
if let ty::Ref(_, _, r_mutbl) = *expected.kind()
2774+
&& pat_mutbl <= r_mutbl
2775+
{
2776+
return Some((Pinnedness::Not, r_mutbl));
2777+
}
2778+
None
2779+
};
2780+
if let Some((_, r_mutbl)) = expected_ref_or_pinned_ref() {
26792781
// Match against the reference type; don't consume the inherited ref.
26802782
// NB: The check for compatible pattern and ref type mutability assumes that
26812783
// `&` 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)