Skip to content

Commit 500d786

Browse files
committed
Support coercion between references and pinned references
1 parent 0208ee0 commit 500d786

File tree

31 files changed

+750
-91
lines changed

31 files changed

+750
-91
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: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ 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::ty::adjustment::{Adjust, Adjustment, OverloadedDeref};
9+
use rustc_middle::bug;
10+
use rustc_middle::ty::adjustment::{Adjust, Adjustment, DerefAdjustKind, OverloadedDeref};
1011
use rustc_middle::ty::{self, Ty};
1112
use rustc_span::Span;
1213

@@ -45,22 +46,27 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
4546
steps.iter().skip(1).map(|&(ty, _)| ty).chain(iter::once(autoderef.final_ty()));
4647
let steps: Vec<_> = steps
4748
.iter()
48-
.map(|&(source, kind)| {
49-
if let AutoderefKind::Overloaded = kind {
50-
self.try_overloaded_deref(autoderef.span(), source).and_then(
51-
|InferOk { value: method, obligations: o }| {
49+
.map(|&(source, kind)| match kind {
50+
AutoderefKind::Overloaded => {
51+
self.try_overloaded_deref(autoderef.span(), source)
52+
.and_then(|InferOk { value: method, obligations: o }| {
5253
obligations.extend(o);
5354
// FIXME: we should assert the sig is &T here... there's no reason for this to be fallible.
5455
if let ty::Ref(_, _, mutbl) = *method.sig.output().kind() {
55-
Some(OverloadedDeref { mutbl, span: autoderef.span() })
56+
Some(DerefAdjustKind::Overloaded(OverloadedDeref {
57+
mutbl,
58+
span: autoderef.span(),
59+
}))
5660
} else {
5761
None
5862
}
59-
},
60-
)
61-
} else {
62-
None
63+
})
64+
.unwrap_or(DerefAdjustKind::Builtin)
6365
}
66+
AutoderefKind::Pin => {
67+
bug!("Pin autoderef kind should not be present in the steps")
68+
}
69+
AutoderefKind::Builtin => DerefAdjustKind::Builtin,
6470
})
6571
.zip_eq(targets)
6672
.map(|(autoderef, target)| Adjustment { kind: Adjust::Deref(autoderef), target })

compiler/rustc_hir_typeck/src/coercion.rs

Lines changed: 70 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,8 @@ use rustc_infer::traits::{
5050
};
5151
use rustc_middle::span_bug;
5252
use rustc_middle::ty::adjustment::{
53-
Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, PointerCoercion,
53+
Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, DerefAdjustKind,
54+
PointerCoercion,
5455
};
5556
use rustc_middle::ty::error::TypeError;
5657
use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt};
@@ -268,12 +269,21 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
268269
return self.coerce_to_raw_ptr(a, b, b_mutbl);
269270
}
270271
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+
}
271278
return self.coerce_to_ref(a, b, r_b, mutbl_b);
272279
}
273280
ty::Adt(pin, _)
274281
if self.tcx.features().pin_ergonomics()
275282
&& self.tcx.is_lang_item(pin.did(), hir::LangItem::Pin) =>
276283
{
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+
}
277287
let pin_coerce = self.commit_if_ok(|_| self.coerce_to_pin_ref(a, b));
278288
if pin_coerce.is_ok() {
279289
return pin_coerce;
@@ -595,7 +605,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
595605
let mutbl = AutoBorrowMutability::new(mutbl_b, AllowTwoPhase::No);
596606

597607
Some((
598-
Adjustment { kind: Adjust::Deref(None), target: ty_a },
608+
Adjustment { kind: Adjust::Deref(DerefAdjustKind::Builtin), target: ty_a },
599609
Adjustment {
600610
kind: Adjust::Borrow(AutoBorrow::Ref(mutbl)),
601611
target: Ty::new_ref(self.tcx, r_borrow, ty_a, mutbl_b),
@@ -606,7 +616,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
606616
coerce_mutbls(mt_a, mt_b)?;
607617

608618
Some((
609-
Adjustment { kind: Adjust::Deref(None), target: ty_a },
619+
Adjustment { kind: Adjust::Deref(DerefAdjustKind::Builtin), target: ty_a },
610620
Adjustment {
611621
kind: Adjust::Borrow(AutoBorrow::RawPtr(mt_b)),
612622
target: Ty::new_ptr(self.tcx, ty_a, mt_b),
@@ -846,6 +856,62 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
846856
self.unify_and(a, b, [], Adjust::ReborrowPin(mut_b), ForceLeakCheck::No)
847857
}
848858

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+
849915
fn coerce_from_fn_pointer(
850916
&self,
851917
a: Ty<'tcx>,
@@ -936,7 +1002,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
9361002
self.unify_and(
9371003
a_raw,
9381004
b,
939-
[Adjustment { kind: Adjust::Deref(None), target: mt_a.ty }],
1005+
[Adjustment { kind: Adjust::Deref(DerefAdjustKind::Builtin), target: mt_a.ty }],
9401006
Adjust::Borrow(AutoBorrow::RawPtr(mutbl_b)),
9411007
ForceLeakCheck::No,
9421008
)

compiler/rustc_hir_typeck/src/expr_use_visitor.rs

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ use rustc_middle::hir::place::ProjectionKind;
2323
// Export these here so that Clippy can use them.
2424
pub use rustc_middle::hir::place::{Place, PlaceBase, PlaceWithHirId, Projection};
2525
use rustc_middle::mir::FakeReadCause;
26+
use rustc_middle::ty::adjustment::DerefAdjustKind;
2627
use rustc_middle::ty::{
2728
self, BorrowKind, Ty, TyCtxt, TypeFoldable, TypeVisitableExt as _, adjustment,
2829
};
@@ -829,14 +830,14 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
829830
self.consume_or_copy(&place_with_id, place_with_id.hir_id);
830831
}
831832

832-
adjustment::Adjust::Deref(None) => {}
833+
adjustment::Adjust::Deref(DerefAdjustKind::Builtin | DerefAdjustKind::Pin) => {}
833834

834835
// Autoderefs for overloaded Deref calls in fact reference
835836
// their receiver. That is, if we have `(*x)` where `x`
836837
// is of type `Rc<T>`, then this in fact is equivalent to
837838
// `x.deref()`. Since `deref()` is declared with `&self`,
838839
// this is an autoref of `x`.
839-
adjustment::Adjust::Deref(Some(ref deref)) => {
840+
adjustment::Adjust::Deref(DerefAdjustKind::Overloaded(deref)) => {
840841
let bk = ty::BorrowKind::from_mutbl(deref.mutbl);
841842
self.delegate.borrow_mut().borrow(&place_with_id, place_with_id.hir_id, bk);
842843
}
@@ -893,6 +894,16 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
893894
ty::BorrowKind::from_mutbl(m),
894895
);
895896
}
897+
898+
adjustment::AutoBorrow::Pin(m) => {
899+
debug!("walk_autoref: expr.hir_id={} base_place={:?}", expr.hir_id, base_place);
900+
901+
self.delegate.borrow_mut().borrow(
902+
base_place,
903+
base_place.hir_id,
904+
ty::BorrowKind::from_mutbl(m),
905+
);
906+
}
896907
}
897908
}
898909

@@ -1325,9 +1336,9 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
13251336
{
13261337
let target = self.cx.resolve_vars_if_possible(adjustment.target);
13271338
match adjustment.kind {
1328-
adjustment::Adjust::Deref(overloaded) => {
1339+
adjustment::Adjust::Deref(deref_kind) => {
13291340
// Equivalent to *expr or something similar.
1330-
let base = if let Some(deref) = overloaded {
1341+
let base = if let DerefAdjustKind::Overloaded(deref) = deref_kind {
13311342
let ref_ty = Ty::new_ref(
13321343
self.cx.tcx(),
13331344
self.cx.tcx().lifetimes.re_erased,

compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@ use rustc_hir_analysis::hir_ty_lowering::{
2020
use rustc_infer::infer::canonical::{Canonical, OriginalQueryValues, QueryResponse};
2121
use rustc_infer::infer::{DefineOpaqueTypes, InferResult};
2222
use rustc_lint::builtin::SELF_CONSTRUCTOR_FROM_OUTER_ITEM;
23-
use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability};
23+
use rustc_middle::ty::adjustment::{
24+
Adjust, Adjustment, AutoBorrow, AutoBorrowMutability, DerefAdjustKind,
25+
};
2426
use rustc_middle::ty::{
2527
self, AdtKind, CanonicalUserType, GenericArgsRef, GenericParamDefKind, IsIdentity,
2628
SizedTraitKind, Ty, TyCtxt, TypeFoldable, TypeVisitable, TypeVisitableExt, UserArgs,
@@ -266,17 +268,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
266268
debug!("apply_adjustments: adding `{:?}` as diverging type var", a.target);
267269
}
268270
}
269-
Adjust::Deref(Some(overloaded_deref)) => {
271+
Adjust::Deref(DerefAdjustKind::Overloaded(overloaded_deref)) => {
270272
self.enforce_context_effects(
271273
None,
272274
expr.span,
273275
overloaded_deref.method_call(self.tcx),
274276
self.tcx.mk_args(&[expr_ty.into()]),
275277
);
276278
}
277-
Adjust::Deref(None) => {
279+
Adjust::Deref(DerefAdjustKind::Builtin) => {
278280
// FIXME(const_trait_impl): We *could* enforce `&T: [const] Deref` here.
279281
}
282+
Adjust::Deref(DerefAdjustKind::Pin) => {
283+
// FIXME(const_trait_impl): We *could* enforce `Pin<&T>: [const] Deref` here.
284+
}
280285
Adjust::Pointer(_pointer_coercion) => {
281286
// FIXME(const_trait_impl): We should probably enforce these.
282287
}

compiler/rustc_hir_typeck/src/pat.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2857,7 +2857,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
28572857
// the bad interactions of the given hack detailed in (note_1).
28582858
debug!("check_pat_ref: expected={:?}", expected);
28592859
match expected.maybe_pinned_ref() {
2860-
Some((r_ty, r_pinned, r_mutbl))
2860+
Some((r_ty, r_pinned, r_mutbl, _))
28612861
if ((ref_pat_matches_mut_ref && r_mutbl >= pat_mutbl)
28622862
|| r_mutbl == pat_mutbl)
28632863
&& pat_pinned == r_pinned =>

compiler/rustc_hir_typeck/src/place_op.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ use rustc_infer::infer::InferOk;
44
use rustc_infer::traits::{Obligation, ObligationCauseCode};
55
use rustc_middle::span_bug;
66
use rustc_middle::ty::adjustment::{
7-
Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, OverloadedDeref,
8-
PointerCoercion,
7+
Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, DerefAdjustKind,
8+
OverloadedDeref, PointerCoercion,
99
};
1010
use rustc_middle::ty::{self, Ty};
1111
use rustc_span::{Span, sym};
@@ -298,7 +298,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
298298
self.typeck_results.borrow_mut().adjustments_mut().remove(expr.hir_id);
299299
if let Some(mut adjustments) = previous_adjustments {
300300
for adjustment in &mut adjustments {
301-
if let Adjust::Deref(Some(ref mut deref)) = adjustment.kind
301+
if let Adjust::Deref(DerefAdjustKind::Overloaded(ref mut deref)) =
302+
adjustment.kind
302303
&& let Some(ok) = self.try_mutable_overloaded_place_op(
303304
expr.span,
304305
source,

compiler/rustc_lint/src/autorefs.rs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
use rustc_ast::{BorrowKind, UnOp};
22
use rustc_hir::{Expr, ExprKind, Mutability};
3-
use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, OverloadedDeref};
3+
use rustc_middle::ty::adjustment::{
4+
Adjust, Adjustment, AutoBorrow, DerefAdjustKind, OverloadedDeref,
5+
};
46
use rustc_session::{declare_lint, declare_lint_pass};
57
use rustc_span::sym;
68

@@ -165,12 +167,14 @@ fn peel_derefs_adjustments<'a>(mut adjs: &'a [Adjustment<'a>]) -> &'a [Adjustmen
165167
/// an implicit borrow (or has an implicit borrow via an overloaded deref).
166168
fn has_implicit_borrow(Adjustment { kind, .. }: &Adjustment<'_>) -> Option<(Mutability, bool)> {
167169
match kind {
168-
&Adjust::Deref(Some(OverloadedDeref { mutbl, .. })) => Some((mutbl, true)),
170+
&Adjust::Deref(DerefAdjustKind::Overloaded(OverloadedDeref { mutbl, .. })) => {
171+
Some((mutbl, true))
172+
}
169173
&Adjust::Borrow(AutoBorrow::Ref(mutbl)) => Some((mutbl.into(), false)),
170174
Adjust::NeverToAny
171175
| Adjust::Pointer(..)
172176
| Adjust::ReborrowPin(..)
173-
| Adjust::Deref(None)
174-
| Adjust::Borrow(AutoBorrow::RawPtr(..)) => None,
177+
| Adjust::Deref(DerefAdjustKind::Builtin | DerefAdjustKind::Pin)
178+
| Adjust::Borrow(AutoBorrow::RawPtr(..) | AutoBorrow::Pin(..)) => None,
175179
}
176180
}

compiler/rustc_lint/src/noop_method_call.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use rustc_hir::def::DefKind;
22
use rustc_hir::{Expr, ExprKind};
33
use rustc_middle::ty;
4-
use rustc_middle::ty::adjustment::Adjust;
4+
use rustc_middle::ty::adjustment::{Adjust, DerefAdjustKind};
55
use rustc_session::{declare_lint, declare_lint_pass};
66
use rustc_span::sym;
77

@@ -114,7 +114,10 @@ impl<'tcx> LateLintPass<'tcx> for NoopMethodCall {
114114

115115
// If there is any user defined auto-deref step, then we don't want to warn.
116116
// https://github.com/rust-lang/rust-clippy/issues/9272
117-
if arg_adjustments.iter().any(|adj| matches!(adj.kind, Adjust::Deref(Some(_)))) {
117+
if arg_adjustments
118+
.iter()
119+
.any(|adj| matches!(adj.kind, Adjust::Deref(DerefAdjustKind::Overloaded(_))))
120+
{
118121
return;
119122
}
120123

compiler/rustc_middle/src/ty/adjustment.rs

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,17 +97,37 @@ pub enum Adjust {
9797
NeverToAny,
9898

9999
/// Dereference once, producing a place.
100-
Deref(Option<OverloadedDeref>),
100+
Deref(DerefAdjustKind),
101101

102102
/// Take the address and produce either a `&` or `*` pointer.
103103
Borrow(AutoBorrow),
104104

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

112+
#[derive(Copy, Clone, Debug, TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable)]
113+
pub enum DerefAdjustKind {
114+
Builtin,
115+
Overloaded(OverloadedDeref),
116+
Pin,
117+
}
118+
119+
impl DerefAdjustKind {
120+
pub fn is_builtin(&self) -> bool {
121+
matches!(self, DerefAdjustKind::Builtin)
122+
}
123+
pub fn is_overloaded(&self) -> bool {
124+
matches!(self, DerefAdjustKind::Overloaded(_))
125+
}
126+
pub fn is_pin(&self) -> bool {
127+
matches!(self, DerefAdjustKind::Pin)
128+
}
129+
}
130+
111131
/// An overloaded autoderef step, representing a `Deref(Mut)::deref(_mut)`
112132
/// call, with the signature `&'a T -> &'a U` or `&'a mut T -> &'a mut U`.
113133
/// The target type is `U` in both cases, with the region and mutability
@@ -190,6 +210,9 @@ pub enum AutoBorrow {
190210

191211
/// Converts from T to *T.
192212
RawPtr(hir::Mutability),
213+
214+
/// Converts from T to Pin<&T>.
215+
Pin(hir::Mutability),
193216
}
194217

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

0 commit comments

Comments
 (0)