Skip to content

Commit 2ef8503

Browse files
committed
require T: !Unpin for &pin mut T to be projected to &pin mut T.U where U: ?Unpin
1 parent 201f85f commit 2ef8503

24 files changed

+1000
-1703
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};
@@ -464,6 +464,29 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
464464
}
465465
}
466466

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

compiler/rustc_hir_typeck/src/pat.rs

Lines changed: 156 additions & 54 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};
@@ -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,13 +572,6 @@ 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
@@ -569,14 +581,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
569581
inner_ty,
570582
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.
@@ -692,6 +703,44 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
692703
}
693704
}
694705

706+
fn check_deref_pattern(
707+
&self,
708+
pat: &'tcx Pat<'tcx>,
709+
opt_path_res: Option<Result<ResolvedPat<'tcx>, ErrorGuaranteed>>,
710+
adjust_mode: AdjustMode,
711+
expected: Ty<'tcx>,
712+
mut inner_ty: Ty<'tcx>,
713+
pat_adjust_kind: PatAdjust,
714+
pat_info: PatInfo<'tcx>,
715+
) -> Ty<'tcx> {
716+
debug_assert!(
717+
!matches!(pat_adjust_kind, PatAdjust::BuiltinDeref),
718+
"unexpected deref pattern for builtin reference type {expected:?}",
719+
);
720+
721+
let mut typeck_results = self.typeck_results.borrow_mut();
722+
let mut pat_adjustments_table = typeck_results.pat_adjustments_mut();
723+
let pat_adjustments = pat_adjustments_table.entry(pat.hir_id).or_default();
724+
// We may reach the recursion limit if a user matches on a type `T` satisfying
725+
// `T: Deref<Target = T>`; error gracefully in this case.
726+
// FIXME(deref_patterns): If `deref_patterns` stabilizes, it may make sense to move
727+
// this check out of this branch. Alternatively, this loop could be implemented with
728+
// autoderef and this check removed. For now though, don't break code compiling on
729+
// stable with lots of `&`s and a low recursion limit, if anyone's done that.
730+
if self.tcx.recursion_limit().value_within_limit(pat_adjustments.len()) {
731+
// Preserve the smart pointer type for THIR lowering and closure upvar analysis.
732+
pat_adjustments.push(PatAdjustment { kind: pat_adjust_kind, source: expected });
733+
} else {
734+
let guar = report_autoderef_recursion_limit_error(self.tcx, pat.span, expected);
735+
inner_ty = Ty::new_error(self.tcx, guar);
736+
}
737+
drop(typeck_results);
738+
739+
// Recurse, using the old pat info to keep `current_depth` to its old value.
740+
// Peeling smart pointers does not update the default binding mode.
741+
self.check_pat_inner(pat, opt_path_res, adjust_mode, inner_ty, pat_info)
742+
}
743+
695744
/// How should the binding mode and expected type be adjusted?
696745
///
697746
/// When the pattern contains a path, `opt_path_res` must be `Some(path_res)`.
@@ -2630,6 +2679,44 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
26302679
}
26312680
}
26322681

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

1333+
pub fn pinned_ref(self) -> Option<(Ty<'tcx>, ty::Mutability)> {
1334+
if let Adt(def, args) = self.kind()
1335+
&& def.is_pin()
1336+
&& let &ty::Ref(_, ty, mutbl) = args.type_at(0).kind()
1337+
{
1338+
return Some((ty, mutbl));
1339+
}
1340+
None
1341+
}
1342+
13331343
/// Panics if called on any type other than `Box<T>`.
13341344
pub fn expect_boxed_ty(self) -> Ty<'tcx> {
13351345
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
@@ -493,6 +493,32 @@ impl<'tcx> TypeckResults<'tcx> {
493493
has_ref_mut
494494
}
495495

496+
/// Visits the pattern recursively whether it contains a `ref pin` binding
497+
/// of non-`Unpin` type in it.
498+
///
499+
/// This is used to determined whether a `&pin` pattern should emit a `!Unpin`
500+
/// call for its pattern scrutinee.
501+
///
502+
/// This is computed from the typeck results since we want to make
503+
/// sure to apply any match-ergonomics adjustments, which we cannot
504+
/// determine from the HIR alone.
505+
pub fn pat_walk_ref_pin_binding_of_non_unpin_type<'a>(
506+
&self,
507+
pat: &hir::Pat<'_>,
508+
mut ty_visitor: impl FnMut(Ty<'tcx>) + 'a,
509+
) {
510+
pat.walk(|pat| {
511+
if let hir::PatKind::Binding(_, id, _, _) = pat.kind
512+
&& let Some(BindingMode(ByRef::Yes(Pinnedness::Pinned, _), _)) =
513+
self.pat_binding_modes().get(id)
514+
{
515+
let ty = self.pat_ty(pat);
516+
ty_visitor(ty);
517+
}
518+
true
519+
});
520+
}
521+
496522
/// How should a deref pattern find the place for its inner pattern to match on?
497523
///
498524
/// In most cases, if the pattern recursively contains a `ref mut` binding, we find the inner

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,9 @@ use rustc_middle::middle::region;
1919
use rustc_middle::mir::*;
2020
use rustc_middle::thir::{self, *};
2121
use rustc_middle::ty::{self, CanonicalUserTypeAnnotation, Ty, ValTree, ValTreeKind};
22+
use rustc_middle::{bug, span_bug};
2223
use rustc_pattern_analysis::constructor::RangeEnd;
2324
use rustc_pattern_analysis::rustc::{DeconstructedPat, RustcPatCtxt};
24-
use rustc_middle::{bug, span_bug};
2525
use rustc_span::{BytePos, Pos, Span, Symbol, sym};
2626
use tracing::{debug, instrument};
2727

compiler/rustc_pattern_analysis/src/constructor.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -981,7 +981,7 @@ pub enum ConstructorSet<Cx: PatCx> {
981981
/// This type has the following list of constructors. If `variants` is empty and
982982
/// `non_exhaustive` is false, don't use this; use `NoConstructors` instead.
983983
Variants { variants: IndexVec<Cx::VariantIdx, VariantVisibility>, non_exhaustive: bool },
984-
/// The type is `&T`
984+
/// The type is `&T`.
985985
Ref,
986986
/// The type is a union.
987987
Union,

0 commit comments

Comments
 (0)