Skip to content

Commit cf51ed1

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 cf51ed1

21 files changed

+710
-1725
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: 131 additions & 52 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,36 @@ 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 => Some(adjust.source.peel_pin_refs()),
433+
}),
434+
);
435+
}
419436
}
420437

421438
// (note_1): In most of the cases where (note_1) is referenced
@@ -553,30 +570,34 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
553570
&& let &ty::Ref(_, inner_ty, inner_mutability) = pinned_ty.kind() =>
554571
{
555572
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 });
563573

564574
let binding_mode = adjust_binding_mode(Pinnedness::Pinned, inner_mutability);
565575
// If the pinnedness is `Not`, it means the pattern is unpinned
566576
// and thus requires an `Unpin` bound.
567577
if binding_mode == ByRef::Yes(Pinnedness::Not, Mutability::Mut) {
568578
self.register_bound(
569579
inner_ty,
570-
self.tcx.require_lang_item(hir::LangItem::Unpin, Some(pat.span)),
580+
self.tcx.require_lang_item(hir::LangItem::Unpin, pat.span),
571581
self.misc(pat.span),
572-
);
582+
)
573583
}
584+
// Once we've checked `pat`, we'll add a `!Unpin` bound if it contains any
585+
// `ref pin` bindings. See `Self::register_not_unpin_bounds_if_needed`.
586+
587+
debug!("default binding mode is now {:?}", binding_mode);
588+
574589
// Use the old pat info to keep `current_depth` to its old value.
575590
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)
591+
592+
self.check_deref_pattern(
593+
pat,
594+
opt_path_res,
595+
adjust_mode,
596+
expected,
597+
inner_ty,
598+
PatAdjust::PinDeref,
599+
new_pat_info,
600+
)
580601
}
581602
// If `deref_patterns` is enabled, peel a smart pointer from the scrutinee type. See the
582603
// examples in `tests/ui/pattern/deref_patterns/`.
@@ -585,35 +606,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
585606
&& pat.default_binding_modes
586607
&& self.should_peel_smart_pointer(peel_kind, expected) =>
587608
{
588-
debug!("scrutinee ty {expected:?} is a smart pointer, inserting overloaded deref");
609+
debug!("scrutinee ty {expected:?} is a smart pointer, inserting pin deref");
610+
589611
// The scrutinee is a smart pointer; implicitly dereference it. This adds a
590612
// requirement that `expected: DerefPure`.
591-
let mut inner_ty = self.deref_pat_target(pat.span, expected);
613+
let inner_ty = self.deref_pat_target(pat.span, expected);
592614
// Once we've checked `pat`, we'll add a `DerefMut` bound if it contains any
593615
// `ref mut` bindings. See `Self::register_deref_mut_bounds_if_needed`.
594616

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)
617+
self.check_deref_pattern(
618+
pat,
619+
opt_path_res,
620+
adjust_mode,
621+
expected,
622+
inner_ty,
623+
PatAdjust::OverloadedDeref,
624+
old_pat_info,
625+
)
617626
}
618627
PatKind::Missing | PatKind::Wild | PatKind::Err(_) => expected,
619628
// We allow any type here; we ensure that the type is uninhabited during match checking.
@@ -684,6 +693,44 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
684693
}
685694
}
686695

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

2688+
/// Check if the interior of a pin pattern (either explicit or implicit) has any `ref pin`
2689+
/// bindings of non-`Unpin` types, which would require `!Unpin` to be emitted.
2690+
fn register_not_unpin_bounds_if_needed(
2691+
&self,
2692+
span: Span,
2693+
inner: &'tcx Pat<'tcx>,
2694+
derefed_tys: impl IntoIterator<Item = Ty<'tcx>>,
2695+
) {
2696+
// Check if there are subpatterns with `ref pin` binding modes of non-`Unpin` types.
2697+
let unpin = self.tcx.require_lang_item(hir::LangItem::Unpin, span);
2698+
let cause = self.misc(span);
2699+
let unpin_obligations = self.probe(|_| {
2700+
let obligation_ctxt = ObligationCtxt::new(&self);
2701+
self.typeck_results.borrow().pat_walk_ref_pin_binding_of_non_unpin_type(inner, |ty| {
2702+
let ty = obligation_ctxt.normalize(&cause, self.param_env, ty).peel_pin_refs();
2703+
obligation_ctxt.register_bound(cause.clone(), self.param_env, ty, unpin);
2704+
});
2705+
obligation_ctxt.select_all_or_error()
2706+
});
2707+
2708+
// If any, the current pattern type should implement `!Unpin`.
2709+
if !unpin_obligations.is_empty() {
2710+
for pinned_derefed_ty in derefed_tys {
2711+
self.register_negative_bound(
2712+
pinned_derefed_ty,
2713+
self.tcx.require_lang_item(hir::LangItem::Unpin, span),
2714+
self.misc(span),
2715+
);
2716+
}
2717+
}
2718+
}
2719+
26412720
// Precondition: Pat is Ref(inner)
26422721
fn check_pat_ref(
26432722
&self,

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

compiler/rustc_middle/src/ty/util.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1463,6 +1463,31 @@ impl<'tcx> Ty<'tcx> {
14631463
ty
14641464
}
14651465

1466+
/// Peel off all (maybe-pinned) reference types in this type until there are none left.
1467+
///
1468+
/// This method is idempotent, i.e. `ty.peel_pinned_refs().peel_pinned_refs() == ty.peel_pinned_refs()`.
1469+
///
1470+
/// # Examples
1471+
///
1472+
/// - `u8` -> `u8`
1473+
/// - `&'a mut u8` -> `u8`
1474+
/// - `&'a pin mut u8` -> `u8`
1475+
/// - `&'a &'b u8` -> `u8`
1476+
/// - `&'a pin const &'b u8` -> `u8`
1477+
/// - `&'a *const &'b u8 -> *const &'b u8`
1478+
/// - `&'a pin const *const &'b u8 -> *const &'b u8`
1479+
pub fn peel_pin_refs(self) -> Ty<'tcx> {
1480+
let mut ty = self;
1481+
loop {
1482+
ty = match ty.kind() {
1483+
ty::Ref(_, inner_ty, _) => *inner_ty,
1484+
ty::Adt(adt, args) if adt.is_pin() => args.type_at(0),
1485+
_ => break,
1486+
};
1487+
}
1488+
ty
1489+
}
1490+
14661491
// FIXME(compiler-errors): Think about removing this.
14671492
#[inline]
14681493
pub fn outer_exclusive_binder(self) -> ty::DebruijnIndex {

compiler/rustc_mir_build/src/builder/matches/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,9 @@ use rustc_middle::middle::region;
1818
use rustc_middle::mir::{self, *};
1919
use rustc_middle::thir::{self, *};
2020
use rustc_middle::ty::{self, CanonicalUserTypeAnnotation, Ty, ValTree, ValTreeKind};
21+
use rustc_middle::{bug, span_bug};
2122
use rustc_pattern_analysis::constructor::RangeEnd;
2223
use rustc_pattern_analysis::rustc::{DeconstructedPat, RustcPatCtxt};
23-
use rustc_middle::{bug, span_bug};
2424
use rustc_span::{BytePos, Pos, Span, Symbol, sym};
2525
use tracing::{debug, instrument};
2626

@@ -2811,7 +2811,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
28112811
self.cfg.push_assign(block, source_info, pinned_temp, borrow);
28122812
Rvalue::Aggregate(
28132813
Box::new(AggregateKind::Adt(
2814-
self.tcx.require_lang_item(LangItem::Pin, Some(source_info.span)),
2814+
self.tcx.require_lang_item(LangItem::Pin, source_info.span),
28152815
FIRST_VARIANT,
28162816
self.tcx.mk_args(&[pinned_ty.into()]),
28172817
None,

compiler/rustc_pattern_analysis/src/rustc.rs

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -226,11 +226,6 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> {
226226
// The only legal patterns of type `Box` (outside `std`) are `_` and box
227227
// patterns. If we're here we can assume this is a box pattern.
228228
reveal_and_alloc(cx, once(args.type_at(0)))
229-
} else if adt.is_pin()
230-
&& let ty::Ref(_, rty, _) = args.type_at(0).kind()
231-
&& self.tcx.features().pin_ergonomics()
232-
{
233-
reveal_and_alloc(cx, once(*rty))
234229
} else {
235230
let variant =
236231
&adt.variant(RustcPatCtxt::variant_index_for_adt(&ctor, *adt));

0 commit comments

Comments
 (0)