Skip to content

Commit 7b7992f

Browse files
committed
Begin experimental support for pin reborrowing
This commit adds basic support for reborrowing `Pin` types in argument position. At the moment it only supports reborrowing `Pin<&mut T>` as `Pin<&mut T>` by inserting a call to `Pin::as_mut()`, and only in argument position (not as the receiver in a method call).
1 parent 13b5a4e commit 7b7992f

File tree

14 files changed

+217
-2
lines changed

14 files changed

+217
-2
lines changed

compiler/rustc_borrowck/messages.ftl

+1-1
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,7 @@ borrowck_simd_intrinsic_arg_const =
207207
*[other] {$arg}th
208208
} argument of `{$intrinsic}` is required to be a `const` item
209209
210-
borrowck_suggest_create_freash_reborrow =
210+
borrowck_suggest_create_fresh_reborrow =
211211
consider reborrowing the `Pin` instead of moving it
212212
213213
borrowck_suggest_iterate_over_slice =

compiler/rustc_borrowck/src/session_diagnostics.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -415,7 +415,7 @@ pub(crate) enum CaptureReasonSuggest<'tcx> {
415415
span: Span,
416416
},
417417
#[suggestion(
418-
borrowck_suggest_create_freash_reborrow,
418+
borrowck_suggest_create_fresh_reborrow,
419419
applicability = "maybe-incorrect",
420420
code = ".as_mut()",
421421
style = "verbose"

compiler/rustc_feature/src/unstable.rs

+2
Original file line numberDiff line numberDiff line change
@@ -558,6 +558,8 @@ declare_features! (
558558
(unstable, optimize_attribute, "1.34.0", Some(54882)),
559559
/// Allows specifying nop padding on functions for dynamic patching.
560560
(unstable, patchable_function_entry, "1.81.0", Some(123115)),
561+
/// Experimental features that make `Pin` more ergonomic.
562+
(incomplete, pin_ergonomics, "CURRENT_RUSTC_VERSION", Some(130494)),
561563
/// Allows postfix match `expr.match { ... }`
562564
(unstable, postfix_match, "1.79.0", Some(121618)),
563565
/// Allows macro attributes on expressions, statements and non-inline modules.

compiler/rustc_hir/src/lang_items.rs

+1
Original file line numberDiff line numberDiff line change
@@ -395,6 +395,7 @@ language_item_table! {
395395
IteratorNext, sym::next, next_fn, Target::Method(MethodKind::Trait { body: false}), GenericRequirement::None;
396396

397397
PinNewUnchecked, sym::new_unchecked, new_unchecked_fn, Target::Method(MethodKind::Inherent), GenericRequirement::None;
398+
PinAsMut, sym::pin_as_mut, as_mut_fn, Target::Method(MethodKind::Inherent), GenericRequirement::None;
398399

399400
RangeFrom, sym::RangeFrom, range_from_struct, Target::Struct, GenericRequirement::None;
400401
RangeFull, sym::RangeFull, range_full_struct, Target::Struct, GenericRequirement::None;

compiler/rustc_hir_typeck/src/coercion.rs

+59
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,12 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
214214
ty::Dynamic(predicates, region, ty::DynStar) if self.tcx.features().dyn_star => {
215215
return self.coerce_dyn_star(a, b, predicates, region);
216216
}
217+
ty::Adt(pin, _)
218+
if self.tcx.features().pin_ergonomics
219+
&& pin.did() == self.tcx.lang_items().pin_type().unwrap() =>
220+
{
221+
return self.coerce_pin(a, b);
222+
}
217223
_ => {}
218224
}
219225

@@ -774,6 +780,59 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
774780
})
775781
}
776782

783+
/// Applies reborrowing for `Pin`
784+
///
785+
/// We currently only support reborrowing `Pin<&mut T>` as `Pin<&mut T>`. This is accomplished
786+
/// by inserting a call to `Pin::as_mut` during MIR building.
787+
///
788+
/// In the future we might want to support other reborrowing coercions, such as:
789+
/// - `Pin<&mut T>` as `Pin<&T>`
790+
/// - `Pin<&T>` as `Pin<&T>`
791+
/// - `Pin<Box<T>>` as `Pin<&T>`
792+
/// - `Pin<Box<T>>` as `Pin<&mut T>`
793+
#[instrument(skip(self), level = "trace")]
794+
fn coerce_pin(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx> {
795+
// We need to make sure the two types are compatible for coercion.
796+
// Then we will build a ReborrowPin adjustment and return that as an InferOk.
797+
798+
// Right now we can only reborrow if this is a `Pin<&mut T>`.
799+
let can_reborrow = |ty: Ty<'tcx>| {
800+
// Get the T out of Pin<T>
801+
let ty = match ty.kind() {
802+
ty::Adt(pin, args) if pin.did() == self.tcx.lang_items().pin_type().unwrap() => {
803+
args[0].expect_ty()
804+
}
805+
_ => {
806+
debug!("can't reborrow {:?} as pinned", ty);
807+
return None;
808+
}
809+
};
810+
// Make sure the T is something we understand (just `&mut U` for now)
811+
match ty.kind() {
812+
ty::Ref(region, ty, ty::Mutability::Mut) => Some((*region, *ty)),
813+
_ => {
814+
debug!("can't reborrow pin of inner type {:?}", ty);
815+
None
816+
}
817+
}
818+
};
819+
820+
let (_, _a_ty) = can_reborrow(a).ok_or(TypeError::Mismatch)?;
821+
let (b_region, _b_ty) = can_reborrow(b).ok_or(TypeError::Mismatch)?;
822+
823+
// To complete the reborrow, we need to make sure we can unify the inner types, and if so we
824+
// add the adjustments.
825+
self.unify_and(a, b, |_inner_ty| {
826+
vec![Adjustment {
827+
kind: Adjust::ReborrowPin(AutoBorrow::Ref(
828+
b_region,
829+
AutoBorrowMutability::Mut { allow_two_phase_borrow: AllowTwoPhase::No },
830+
)),
831+
target: b,
832+
}]
833+
})
834+
}
835+
777836
fn coerce_from_safe_fn<F, G>(
778837
&self,
779838
a: Ty<'tcx>,

compiler/rustc_hir_typeck/src/expr_use_visitor.rs

+15
Original file line numberDiff line numberDiff line change
@@ -780,6 +780,20 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
780780
adjustment::Adjust::Borrow(ref autoref) => {
781781
self.walk_autoref(expr, &place_with_id, autoref);
782782
}
783+
784+
adjustment::Adjust::ReborrowPin(ref autoref) => {
785+
// Reborrowing a Pin is like a combinations of a deref and a borrow, so we do
786+
// both.
787+
let bk = match autoref {
788+
adjustment::AutoBorrow::Ref(_, m) => {
789+
ty::BorrowKind::from_mutbl((*m).into())
790+
}
791+
adjustment::AutoBorrow::RawPtr(m) => ty::BorrowKind::from_mutbl(*m),
792+
};
793+
self.delegate.borrow_mut().borrow(&place_with_id, place_with_id.hir_id, bk);
794+
795+
self.walk_autoref(expr, &place_with_id, autoref);
796+
}
783797
}
784798
place_with_id = self.cat_expr_adjusted(expr, place_with_id, adjustment)?;
785799
}
@@ -1284,6 +1298,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
12841298
adjustment::Adjust::NeverToAny
12851299
| adjustment::Adjust::Pointer(_)
12861300
| adjustment::Adjust::Borrow(_)
1301+
| adjustment::Adjust::ReborrowPin(_)
12871302
| adjustment::Adjust::DynStar => {
12881303
// Result is an rvalue.
12891304
Ok(self.cat_rvalue(expr.hir_id, target))

compiler/rustc_middle/src/ty/adjustment.rs

+3
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,9 @@ pub enum Adjust<'tcx> {
104104

105105
/// Cast into a dyn* object.
106106
DynStar,
107+
108+
/// Take a Pin<Ptr> and call either as_mut() or as_ref() to get a Pin<&mut T> or Pin<&T>.
109+
ReborrowPin(AutoBorrow<'tcx>),
107110
}
108111

109112
/// An overloaded autoderef step, representing a `Deref(Mut)::deref(_mut)`

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

+45
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ impl<'tcx> Cx<'tcx> {
7474
self.thir.exprs.push(expr)
7575
}
7676

77+
#[instrument(level = "trace", skip(self, expr, span))]
7778
fn apply_adjustment(
7879
&mut self,
7980
hir_expr: &'tcx hir::Expr<'tcx>,
@@ -146,6 +147,50 @@ impl<'tcx> Cx<'tcx> {
146147
ExprKind::RawBorrow { mutability, arg: self.thir.exprs.push(expr) }
147148
}
148149
Adjust::DynStar => ExprKind::Cast { source: self.thir.exprs.push(expr) },
150+
Adjust::ReborrowPin(AutoBorrow::Ref(region, m)) => {
151+
debug!("apply ReborrowPin adjustment");
152+
match m {
153+
AutoBorrowMutability::Mut { .. } => {
154+
// Rewrite `$expr` as `Pin::as_mut(&mut $expr)`
155+
let as_mut_method =
156+
self.tcx().require_lang_item(rustc_hir::LangItem::PinAsMut, Some(span));
157+
let pin_ty_args = match expr.ty.kind() {
158+
ty::Adt(_, args) => args,
159+
_ => bug!("ReborrowPin with non-Pin type"),
160+
};
161+
let as_mut_ty =
162+
Ty::new_fn_def(self.tcx, as_mut_method, pin_ty_args.into_iter());
163+
164+
let ty = Ty::new_ref(self.tcx, region, expr.ty, ty::Mutability::Mut);
165+
let arg = ExprKind::Borrow {
166+
borrow_kind: BorrowKind::Mut { kind: mir::MutBorrowKind::Default },
167+
arg: self.thir.exprs.push(expr),
168+
};
169+
debug!(?arg, "borrow arg");
170+
let arg = self.thir.exprs.push(Expr { temp_lifetime, ty, span, kind: arg });
171+
172+
let kind = ExprKind::Call {
173+
ty: as_mut_ty,
174+
fun: self.thir.exprs.push(Expr {
175+
temp_lifetime,
176+
ty: as_mut_ty,
177+
span,
178+
kind: ExprKind::ZstLiteral { user_ty: None },
179+
}),
180+
args: Box::new([arg]),
181+
from_hir_call: true,
182+
fn_span: span,
183+
};
184+
debug!(?kind);
185+
kind
186+
}
187+
AutoBorrowMutability::Not => {
188+
// FIXME: We need to call Pin::as_ref on the expression
189+
bug!("ReborrowPin with shared reference is not implemented yet")
190+
}
191+
}
192+
}
193+
Adjust::ReborrowPin(AutoBorrow::RawPtr(_)) => bug!("ReborrowPin with raw pointer"),
149194
};
150195

151196
Expr { temp_lifetime, ty: adjustment.target, span, kind }

compiler/rustc_span/src/symbol.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1418,6 +1418,8 @@ symbols! {
14181418
pic,
14191419
pie,
14201420
pin,
1421+
pin_as_mut,
1422+
pin_ergonomics,
14211423
platform_intrinsics,
14221424
plugin,
14231425
plugin_registrar,

library/core/src/pin.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1408,6 +1408,7 @@ impl<Ptr: DerefMut> Pin<Ptr> {
14081408
/// }
14091409
/// }
14101410
/// ```
1411+
#[cfg_attr(not(bootstrap), lang = "pin_as_mut")]
14111412
#[stable(feature = "pin", since = "1.33.0")]
14121413
#[inline(always)]
14131414
pub fn as_mut(&mut self) -> Pin<&mut Ptr::Target> {
+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
//@ check-pass
2+
3+
#![feature(pin_ergonomics)]
4+
#![allow(dead_code, incomplete_features)]
5+
6+
use std::pin::Pin;
7+
8+
struct Foo;
9+
10+
impl Foo {
11+
fn baz(self: Pin<&mut Self>) {
12+
}
13+
}
14+
15+
fn foo(_: Pin<&mut Foo>) {
16+
}
17+
18+
fn bar(mut x: Pin<&mut Foo>) {
19+
foo(x);
20+
foo(x); // for this to work we need to automatically reborrow,
21+
// as if the user had written `foo(x.as_mut())`.
22+
23+
Foo::baz(x);
24+
Foo::baz(x);
25+
}
26+
27+
fn main() {}
+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
//@ check-pass
2+
//@ignore-test
3+
4+
// Currently ignored due to self reborrowing not being implemented for Pin
5+
6+
#![feature(pin_ergonomics)]
7+
#![allow(incomplete_features)]
8+
9+
use std::pin::Pin;
10+
11+
struct Foo;
12+
13+
impl Foo {
14+
fn foo(self: Pin<&mut Self>) {
15+
}
16+
}
17+
18+
fn bar(x: Pin<&mut Foo>) {
19+
x.foo();
20+
x.foo(); // for this to work we need to automatically reborrow,
21+
// as if the user had written `x.as_mut().foo()`.
22+
}
23+
24+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#![allow(dead_code, incomplete_features)]
2+
3+
use std::pin::Pin;
4+
5+
struct Foo;
6+
7+
fn foo(_: Pin<&mut Foo>) {
8+
}
9+
10+
fn bar(mut x: Pin<&mut Foo>) {
11+
foo(x);
12+
foo(x); //~ ERROR use of moved value: `x`
13+
}
14+
15+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
error[E0382]: use of moved value: `x`
2+
--> $DIR/feature-gate-pin_ergonomics.rs:12:9
3+
|
4+
LL | fn bar(mut x: Pin<&mut Foo>) {
5+
| ----- move occurs because `x` has type `Pin<&mut Foo>`, which does not implement the `Copy` trait
6+
LL | foo(x);
7+
| - value moved here
8+
LL | foo(x);
9+
| ^ value used here after move
10+
|
11+
note: consider changing this parameter type in function `foo` to borrow instead if owning the value isn't necessary
12+
--> $DIR/feature-gate-pin_ergonomics.rs:7:11
13+
|
14+
LL | fn foo(_: Pin<&mut Foo>) {
15+
| --- ^^^^^^^^^^^^^ this parameter takes ownership of the value
16+
| |
17+
| in this function
18+
19+
error: aborting due to 1 previous error
20+
21+
For more information about this error, try `rustc --explain E0382`.

0 commit comments

Comments
 (0)