Skip to content

Commit 0422097

Browse files
committed
Support coercion between references and pinned references
1 parent 905b926 commit 0422097

File tree

12 files changed

+640
-3
lines changed

12 files changed

+640
-3
lines changed

compiler/rustc_hir_analysis/src/autoderef.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ pub enum AutoderefKind {
1717
Builtin,
1818
/// A type which must dispatch to a `Deref` implementation.
1919
Overloaded,
20+
/// A pinned reference type, such as `Pin<&T>` and `Pin<&mut T>`.
21+
Pin,
2022
}
2123
struct AutoderefSnapshot<'tcx> {
2224
at_start: bool,

compiler/rustc_hir_typeck/src/autoderef.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use itertools::Itertools;
66
use rustc_hir_analysis::autoderef::{Autoderef, AutoderefKind};
77
use rustc_infer::infer::InferOk;
88
use rustc_infer::traits::PredicateObligations;
9+
use rustc_middle::bug;
910
use rustc_middle::ty::adjustment::{Adjust, Adjustment, DerefAdjustKind, OverloadedDeref};
1011
use rustc_middle::ty::{self, Ty};
1112
use rustc_span::Span;
@@ -62,6 +63,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
6263
})
6364
.unwrap_or(DerefAdjustKind::Builtin)
6465
}
66+
AutoderefKind::Pin => {
67+
bug!("Pin autoderef kind should not be present in the steps")
68+
}
6569
AutoderefKind::Builtin => DerefAdjustKind::Builtin,
6670
})
6771
.zip_eq(targets)

compiler/rustc_hir_typeck/src/coercion.rs

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,12 +269,21 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
269269
return self.coerce_to_raw_ptr(a, b, b_mutbl);
270270
}
271271
ty::Ref(r_b, _, mutbl_b) => {
272+
if self.tcx.features().pin_ergonomics()
273+
&& a.pinned_ty().is_some_and(|ty| ty.is_ref())
274+
&& let Ok(coerce) = self.commit_if_ok(|_| self.coerce_maybe_pinned_ref(a, b))
275+
{
276+
return Ok(coerce);
277+
}
272278
return self.coerce_to_ref(a, b, r_b, mutbl_b);
273279
}
274280
ty::Adt(pin, _)
275281
if self.tcx.features().pin_ergonomics()
276282
&& self.tcx.is_lang_item(pin.did(), hir::LangItem::Pin) =>
277283
{
284+
if a.is_ref() && b.pinned_ty().is_some_and(|ty| ty.is_ref()) {
285+
return self.coerce_maybe_pinned_ref(a, b);
286+
}
278287
let pin_coerce = self.commit_if_ok(|_| self.coerce_to_pin_ref(a, b));
279288
if pin_coerce.is_ok() {
280289
return pin_coerce;
@@ -847,6 +856,62 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
847856
self.unify_and(a, b, [], Adjust::ReborrowPin(mut_b), ForceLeakCheck::No)
848857
}
849858

859+
/// Coerce pinned reference to regular reference or vice versa
860+
///
861+
/// - `Pin<&mut T>` <-> `&mut T` when `T: Unpin`
862+
/// - `Pin<&T>` <-> `&T` when `T: Unpin`
863+
/// - `Pin<&mut T>` <-> `Pin<&T>` when `T: Unpin`
864+
#[instrument(skip(self), level = "trace")]
865+
fn coerce_maybe_pinned_ref(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx> {
866+
let span = self.cause.span;
867+
let Some((a_ty, a_pinnedness, a_mutbl, a_region)) = a.maybe_pinned_ref() else {
868+
span_bug!(span, "expect pinned reference or reference, found {:?}", a);
869+
};
870+
let Some((_b_ty, b_pinnedness, b_mutbl, _b_region)) = b.maybe_pinned_ref() else {
871+
span_bug!(span, "expect pinned reference or reference, found {:?}", b);
872+
};
873+
use ty::Pinnedness::*;
874+
if a_pinnedness == b_pinnedness {
875+
span_bug!(span, "expect different pinnedness, found {:?} and {:?}", a, b);
876+
}
877+
878+
coerce_mutbls(a_mutbl, b_mutbl)?;
879+
880+
let (deref, borrow) = match (a_pinnedness, b_pinnedness) {
881+
(Not, Not) | (Pinned, Pinned) => {
882+
span_bug!(span, "expect different pinnedness, found {:?} and {:?}", a, b)
883+
}
884+
(Pinned, Not) => {
885+
let mutbl = AutoBorrowMutability::new(b_mutbl, AllowTwoPhase::Yes);
886+
(DerefAdjustKind::Pin, AutoBorrow::Ref(mutbl))
887+
}
888+
(Not, Pinned) => (DerefAdjustKind::Builtin, AutoBorrow::Pin(b_mutbl)),
889+
};
890+
let mut coerce = self.unify_and(
891+
// update a with b's pinnedness and mutability since we'll be coercing pinnedness and mutability
892+
match b_pinnedness {
893+
Pinned => Ty::new_pinned_ref(self.tcx, a_region, a_ty, b_mutbl),
894+
Not => Ty::new_ref(self.tcx, a_region, a_ty, b_mutbl),
895+
},
896+
b,
897+
[Adjustment { kind: Adjust::Deref(deref), target: a_ty }],
898+
Adjust::Borrow(borrow),
899+
ForceLeakCheck::No,
900+
)?;
901+
902+
// Create an obligation for `a_ty: Unpin`.
903+
let cause =
904+
self.cause(self.cause.span, ObligationCauseCode::Coercion { source: a, target: b });
905+
let pred = ty::TraitRef::new(
906+
self.tcx,
907+
self.tcx.require_lang_item(hir::LangItem::Unpin, self.cause.span),
908+
[a_ty],
909+
);
910+
let obligation = Obligation::new(self.tcx, cause, self.fcx.param_env, pred);
911+
coerce.obligations.push(obligation);
912+
Ok(coerce)
913+
}
914+
850915
fn coerce_from_fn_pointer(
851916
&self,
852917
a: Ty<'tcx>,

compiler/rustc_hir_typeck/src/expr_use_visitor.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -734,7 +734,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
734734
self.consume_or_copy(&place_with_id, place_with_id.hir_id);
735735
}
736736

737-
adjustment::Adjust::Deref(DerefAdjustKind::Builtin) => {}
737+
adjustment::Adjust::Deref(DerefAdjustKind::Builtin | DerefAdjustKind::Pin) => {}
738738

739739
// Autoderefs for overloaded Deref calls in fact reference
740740
// their receiver. That is, if we have `(*x)` where `x`
@@ -798,6 +798,16 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
798798
ty::BorrowKind::from_mutbl(m),
799799
);
800800
}
801+
802+
adjustment::AutoBorrow::Pin(m) => {
803+
debug!("walk_autoref: expr.hir_id={} base_place={:?}", expr.hir_id, base_place);
804+
805+
self.delegate.borrow_mut().borrow(
806+
base_place,
807+
base_place.hir_id,
808+
ty::BorrowKind::from_mutbl(m),
809+
);
810+
}
801811
}
802812
}
803813

compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
279279
Adjust::Deref(DerefAdjustKind::Builtin) => {
280280
// FIXME(const_trait_impl): We *could* enforce `&T: [const] Deref` here.
281281
}
282+
Adjust::Deref(DerefAdjustKind::Pin) => {
283+
// FIXME(const_trait_impl): We *could* enforce `Pin<&T>: [const] Deref` here.
284+
}
282285
Adjust::Pointer(_pointer_coercion) => {
283286
// FIXME(const_trait_impl): We should probably enforce these.
284287
}

compiler/rustc_lint/src/autorefs.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ fn has_implicit_borrow(Adjustment { kind, .. }: &Adjustment<'_>) -> Option<(Muta
174174
Adjust::NeverToAny
175175
| Adjust::Pointer(..)
176176
| Adjust::ReborrowPin(..)
177-
| Adjust::Deref(DerefAdjustKind::Builtin)
178-
| Adjust::Borrow(AutoBorrow::RawPtr(..)) => None,
177+
| Adjust::Deref(DerefAdjustKind::Builtin | DerefAdjustKind::Pin)
178+
| Adjust::Borrow(AutoBorrow::RawPtr(..) | AutoBorrow::Pin(..)) => None,
179179
}
180180
}

compiler/rustc_middle/src/ty/adjustment.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,13 +105,15 @@ pub enum Adjust {
105105
Pointer(PointerCoercion),
106106

107107
/// Take a pinned reference and reborrow as a `Pin<&mut T>` or `Pin<&T>`.
108+
// FIXME(pin_ergonomics): This can be replaced with a `Deref(Pin)` followed by a `Borrow(Pin)`
108109
ReborrowPin(hir::Mutability),
109110
}
110111

111112
#[derive(Copy, Clone, Debug, TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable)]
112113
pub enum DerefAdjustKind {
113114
Builtin,
114115
Overloaded(OverloadedDeref),
116+
Pin,
115117
}
116118

117119
/// An overloaded autoderef step, representing a `Deref(Mut)::deref(_mut)`
@@ -196,6 +198,9 @@ pub enum AutoBorrow {
196198

197199
/// Converts from T to *T.
198200
RawPtr(hir::Mutability),
201+
202+
/// Converts from T to Pin<&T>.
203+
Pin(hir::Mutability),
199204
}
200205

201206
/// Information for `CoerceUnsized` impls, storing information we

compiler/rustc_mir_build/src/thir/cx/expr.rs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,19 @@ impl<'tcx> ThirBuildCx<'tcx> {
144144
adjust_span(&mut expr);
145145
ExprKind::Deref { arg: self.thir.exprs.push(expr) }
146146
}
147+
Adjust::Deref(DerefAdjustKind::Pin) => {
148+
adjust_span(&mut expr);
149+
// pointer = ($expr).pointer
150+
let pin_ty = expr.ty.pinned_ty().expect("Deref(Pin) with non-Pin type");
151+
let pointer_target = ExprKind::Field {
152+
lhs: self.thir.exprs.push(expr),
153+
variant_index: FIRST_VARIANT,
154+
name: FieldIdx::ZERO,
155+
};
156+
let expr = Expr { temp_scope_id, ty: pin_ty, span, kind: pointer_target };
157+
// expr = *pointer
158+
ExprKind::Deref { arg: self.thir.exprs.push(expr) }
159+
}
147160
Adjust::Deref(DerefAdjustKind::Overloaded(deref)) => {
148161
// We don't need to do call adjust_span here since
149162
// deref coercions always start with a built-in deref.
@@ -178,6 +191,37 @@ impl<'tcx> ThirBuildCx<'tcx> {
178191
Adjust::Borrow(AutoBorrow::RawPtr(mutability)) => {
179192
ExprKind::RawBorrow { mutability, arg: self.thir.exprs.push(expr) }
180193
}
194+
Adjust::Borrow(AutoBorrow::Pin(mutbl)) => {
195+
// expr = &pin (mut|const|) arget
196+
let borrow_kind = match mutbl {
197+
hir::Mutability::Mut => BorrowKind::Mut { kind: mir::MutBorrowKind::Default },
198+
hir::Mutability::Not => BorrowKind::Shared,
199+
};
200+
let new_pin_target =
201+
Ty::new_ref(self.tcx, self.tcx.lifetimes.re_erased, expr.ty, mutbl);
202+
let arg = self.thir.exprs.push(expr);
203+
let expr = self.thir.exprs.push(Expr {
204+
temp_scope_id,
205+
ty: new_pin_target,
206+
span,
207+
kind: ExprKind::Borrow { borrow_kind, arg },
208+
});
209+
210+
// kind = Pin { pointer }
211+
let pin_did = self.tcx.require_lang_item(rustc_hir::LangItem::Pin, span);
212+
let args = self.tcx.mk_args(&[new_pin_target.into()]);
213+
let kind = ExprKind::Adt(Box::new(AdtExpr {
214+
adt_def: self.tcx.adt_def(pin_did),
215+
variant_index: FIRST_VARIANT,
216+
args,
217+
fields: Box::new([FieldExpr { name: FieldIdx::ZERO, expr }]),
218+
user_ty: None,
219+
base: AdtExprBase::None,
220+
}));
221+
222+
debug!(?kind);
223+
kind
224+
}
181225
Adjust::ReborrowPin(mutbl) => {
182226
debug!("apply ReborrowPin adjustment");
183227
// Rewrite `$expr` as `Pin { __pointer: &(mut)? *($expr).__pointer }`

0 commit comments

Comments
 (0)