Skip to content

Commit 29a89ae

Browse files
committed
Support coercion between references and pinned references
1 parent 140044c commit 29a89ae

File tree

18 files changed

+703
-55
lines changed

18 files changed

+703
-55
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: 69 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, GenericArgsRef, Ty, TyCtxt, TypeVisitableExt};
@@ -231,12 +232,21 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
231232
return self.coerce_raw_ptr(a, b, b_mutbl);
232233
}
233234
ty::Ref(r_b, _, mutbl_b) => {
235+
if self.tcx.features().pin_ergonomics()
236+
&& a.pinned_ty().is_some_and(|ty| ty.is_ref())
237+
&& let Ok(coerce) = self.commit_if_ok(|_| self.coerce_pin_or_ref(a, b))
238+
{
239+
return Ok(coerce);
240+
}
234241
return self.coerce_borrowed_pointer(a, b, r_b, mutbl_b);
235242
}
236243
ty::Adt(pin, _)
237244
if self.tcx.features().pin_ergonomics()
238245
&& self.tcx.is_lang_item(pin.did(), hir::LangItem::Pin) =>
239246
{
247+
if a.is_ref() && b.pinned_ty().is_some_and(|ty| ty.is_ref()) {
248+
return self.coerce_pin_or_ref(a, b);
249+
}
240250
let pin_coerce = self.commit_if_ok(|_| self.coerce_pin_ref(a, b));
241251
if pin_coerce.is_ok() {
242252
return pin_coerce;
@@ -606,7 +616,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
606616
let mutbl = AutoBorrowMutability::new(mutbl_b, AllowTwoPhase::No);
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::Ref(mutbl)),
612622
target: Ty::new_ref(self.tcx, r_borrow, ty_a, mutbl_b),
@@ -617,7 +627,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
617627
coerce_mutbls(mt_a, mt_b)?;
618628

619629
Some((
620-
Adjustment { kind: Adjust::Deref(None), target: ty_a },
630+
Adjustment { kind: Adjust::Deref(DerefAdjustKind::Builtin), target: ty_a },
621631
Adjustment {
622632
kind: Adjust::Borrow(AutoBorrow::RawPtr(mt_b)),
623633
target: Ty::new_ptr(self.tcx, ty_a, mt_b),
@@ -856,6 +866,61 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
856866
self.unify_and(a, b, [], Adjust::ReborrowPin(mut_b))
857867
}
858868

869+
/// Coerce pinned reference to regular reference or vice versa
870+
///
871+
/// - `Pin<&mut T>` <-> `&mut T` when `T: Unpin`
872+
/// - `Pin<&T>` <-> `&T` when `T: Unpin`
873+
/// - `Pin<&mut T>` <-> `Pin<&T>` when `T: Unpin`
874+
#[instrument(skip(self), level = "trace")]
875+
fn coerce_pin_or_ref(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx> {
876+
let span = self.cause.span;
877+
let Some((a_ty, a_pinnedness, a_mutbl, a_region)) = a.maybe_pinned_ref() else {
878+
span_bug!(span, "expect pinned reference or reference, found {:?}", a);
879+
};
880+
let Some((_b_ty, b_pinnedness, b_mutbl, _b_region)) = b.maybe_pinned_ref() else {
881+
span_bug!(span, "expect pinned reference or reference, found {:?}", b);
882+
};
883+
use ty::Pinnedness::*;
884+
if a_pinnedness == b_pinnedness {
885+
span_bug!(span, "expect different pinnedness, found {:?} and {:?}", a, b);
886+
}
887+
888+
coerce_mutbls(a_mutbl, b_mutbl)?;
889+
890+
let (deref, borrow) = match (a_pinnedness, b_pinnedness) {
891+
(Not, Not) | (Pinned, Pinned) => {
892+
span_bug!(span, "expect different pinnedness, found {:?} and {:?}", a, b)
893+
}
894+
(Pinned, Not) => {
895+
let mutbl = AutoBorrowMutability::new(b_mutbl, AllowTwoPhase::Yes);
896+
(DerefAdjustKind::Pin, AutoBorrow::Ref(mutbl))
897+
}
898+
(Not, Pinned) => (DerefAdjustKind::Builtin, AutoBorrow::Pin(b_mutbl)),
899+
};
900+
let mut coerce = self.unify_and(
901+
// update a with b's pinnedness and mutability since we'll be coercing pinnedness and mutability
902+
match b_pinnedness {
903+
Pinned => Ty::new_pinned_ref(self.tcx, a_region, a_ty, b_mutbl),
904+
Not => Ty::new_ref(self.tcx, a_region, a_ty, b_mutbl),
905+
},
906+
b,
907+
[Adjustment { kind: Adjust::Deref(deref), target: a_ty }],
908+
Adjust::Borrow(borrow),
909+
)?;
910+
911+
// Create an obligation for `a_ty: Unpin`.
912+
let cause =
913+
self.cause(self.cause.span, ObligationCauseCode::Coercion { source: a, target: b });
914+
let pred = ty::TraitRef::new(
915+
self.tcx,
916+
self.tcx.require_lang_item(hir::LangItem::Unpin, self.cause.span),
917+
[a_ty],
918+
);
919+
let obligation = Obligation::new(self.tcx, cause, self.fcx.param_env, pred);
920+
coerce.obligations.push(obligation);
921+
Ok(coerce)
922+
}
923+
859924
fn coerce_from_safe_fn(
860925
&self,
861926
fn_ty_a: ty::PolyFnSig<'tcx>,
@@ -1039,7 +1104,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
10391104
self.unify_and(
10401105
a_raw,
10411106
b,
1042-
[Adjustment { kind: Adjust::Deref(None), target: mt_a.ty }],
1107+
[Adjustment { kind: Adjust::Deref(DerefAdjustKind::Builtin), target: mt_a.ty }],
10431108
Adjust::Borrow(AutoBorrow::RawPtr(mutbl_b)),
10441109
)
10451110
} else if mt_a.mutbl != mutbl_b {

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: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,17 +96,25 @@ pub enum Adjust {
9696
NeverToAny,
9797

9898
/// Dereference once, producing a place.
99-
Deref(Option<OverloadedDeref>),
99+
Deref(DerefAdjustKind),
100100

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

104104
Pointer(PointerCoercion),
105105

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

111+
#[derive(Copy, Clone, Debug, TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable)]
112+
pub enum DerefAdjustKind {
113+
Builtin,
114+
Overloaded(OverloadedDeref),
115+
Pin,
116+
}
117+
110118
/// An overloaded autoderef step, representing a `Deref(Mut)::deref(_mut)`
111119
/// call, with the signature `&'a T -> &'a U` or `&'a mut T -> &'a mut U`.
112120
/// The target type is `U` in both cases, with the region and mutability
@@ -189,6 +197,9 @@ pub enum AutoBorrow {
189197

190198
/// Converts from T to *T.
191199
RawPtr(hir::Mutability),
200+
201+
/// Converts from T to Pin<&T>.
202+
Pin(hir::Mutability),
192203
}
193204

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

0 commit comments

Comments
 (0)