Skip to content

Commit 8aa00d4

Browse files
authored
Unrolled build for rust-lang#130526
Rollup merge of rust-lang#130526 - eholk:pin-reborrow, r=compiler-errors 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). This PR makes the following example compile: ```rust #![feature(pin_ergonomics)] fn foo(_: Pin<&mut Foo>) { } fn bar(mut x: Pin<&mut Foo>) { foo(x); foo(x); } ``` Previously, you would have had to write `bar` as: ```rust fn bar(mut x: Pin<&mut Foo>) { foo(x.as_mut()); foo(x); } ``` Tracking: - rust-lang#130494 r? `@compiler-errors`
2 parents 5ba6db1 + a18800f commit 8aa00d4

17 files changed

+316
-3
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_typeck/src/coercion.rs

+62
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+
&& self.tcx.is_lang_item(pin.did(), hir::LangItem::Pin) =>
220+
{
221+
return self.coerce_pin(a, b);
222+
}
217223
_ => {}
218224
}
219225

@@ -774,6 +780,62 @@ 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 extract_pin_mut = |ty: Ty<'tcx>| {
800+
// Get the T out of Pin<T>
801+
let (pin, ty) = match ty.kind() {
802+
ty::Adt(pin, args) if self.tcx.is_lang_item(pin.did(), hir::LangItem::Pin) => {
803+
(*pin, args[0].expect_ty())
804+
}
805+
_ => {
806+
debug!("can't reborrow {:?} as pinned", ty);
807+
return Err(TypeError::Mismatch);
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, mutbl) => Ok((pin, *region, *ty, *mutbl)),
813+
_ => {
814+
debug!("can't reborrow pin of inner type {:?}", ty);
815+
Err(TypeError::Mismatch)
816+
}
817+
}
818+
};
819+
820+
let (pin, a_region, a_ty, mut_a) = extract_pin_mut(a)?;
821+
let (_, b_region, _b_ty, mut_b) = extract_pin_mut(b)?;
822+
823+
coerce_mutbls(mut_a, mut_b)?;
824+
825+
// update a with b's mutability since we'll be coercing mutability
826+
let a = Ty::new_adt(
827+
self.tcx,
828+
pin,
829+
self.tcx.mk_args(&[Ty::new_ref(self.tcx, a_region, a_ty, mut_b).into()]),
830+
);
831+
832+
// To complete the reborrow, we need to make sure we can unify the inner types, and if so we
833+
// add the adjustments.
834+
self.unify_and(a, b, |_inner_ty| {
835+
vec![Adjustment { kind: Adjust::ReborrowPin(b_region, mut_b), target: b }]
836+
})
837+
}
838+
777839
fn coerce_from_safe_fn<F, G>(
778840
&self,
779841
a: Ty<'tcx>,

compiler/rustc_hir_typeck/src/expr_use_visitor.rs

+11
Original file line numberDiff line numberDiff line change
@@ -780,6 +780,16 @@ 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(_, mutbl) => {
785+
// Reborrowing a Pin is like a combinations of a deref and a borrow, so we do
786+
// both.
787+
let bk = match mutbl {
788+
ty::Mutability::Not => ty::BorrowKind::ImmBorrow,
789+
ty::Mutability::Mut => ty::BorrowKind::MutBorrow,
790+
};
791+
self.delegate.borrow_mut().borrow(&place_with_id, place_with_id.hir_id, bk);
792+
}
783793
}
784794
place_with_id = self.cat_expr_adjusted(expr, place_with_id, adjustment)?;
785795
}
@@ -1284,6 +1294,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
12841294
adjustment::Adjust::NeverToAny
12851295
| adjustment::Adjust::Pointer(_)
12861296
| adjustment::Adjust::Borrow(_)
1297+
| adjustment::Adjust::ReborrowPin(..)
12871298
| adjustment::Adjust::DynStar => {
12881299
// Result is an rvalue.
12891300
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 pinned reference and reborrow as a `Pin<&mut T>` or `Pin<&T>`.
109+
ReborrowPin(ty::Region<'tcx>, hir::Mutability),
107110
}
108111

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

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

+63-1
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,67 @@ 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(region, mutbl) => {
151+
debug!("apply ReborrowPin adjustment");
152+
// Rewrite `$expr` as `Pin { __pointer: &(mut)? *($expr).__pointer }`
153+
154+
// We'll need these types later on
155+
let pin_ty_args = match expr.ty.kind() {
156+
ty::Adt(_, args) => args,
157+
_ => bug!("ReborrowPin with non-Pin type"),
158+
};
159+
let pin_ty = pin_ty_args.iter().next().unwrap().expect_ty();
160+
let ptr_target_ty = match pin_ty.kind() {
161+
ty::Ref(_, ty, _) => *ty,
162+
_ => bug!("ReborrowPin with non-Ref type"),
163+
};
164+
165+
// pointer = ($expr).__pointer
166+
let pointer_target = ExprKind::Field {
167+
lhs: self.thir.exprs.push(expr),
168+
variant_index: FIRST_VARIANT,
169+
name: FieldIdx::from(0u32),
170+
};
171+
let arg = Expr { temp_lifetime, ty: pin_ty, span, kind: pointer_target };
172+
let arg = self.thir.exprs.push(arg);
173+
174+
// arg = *pointer
175+
let expr = ExprKind::Deref { arg };
176+
let arg = self.thir.exprs.push(Expr {
177+
temp_lifetime,
178+
ty: ptr_target_ty,
179+
span,
180+
kind: expr,
181+
});
182+
183+
// expr = &mut target
184+
let borrow_kind = match mutbl {
185+
hir::Mutability::Mut => BorrowKind::Mut { kind: mir::MutBorrowKind::Default },
186+
hir::Mutability::Not => BorrowKind::Shared,
187+
};
188+
let new_pin_target = Ty::new_ref(self.tcx, region, ptr_target_ty, mutbl);
189+
let expr = self.thir.exprs.push(Expr {
190+
temp_lifetime,
191+
ty: new_pin_target,
192+
span,
193+
kind: ExprKind::Borrow { borrow_kind, arg },
194+
});
195+
196+
// kind = Pin { __pointer: pointer }
197+
let pin_did = self.tcx.require_lang_item(rustc_hir::LangItem::Pin, Some(span));
198+
let args = self.tcx.mk_args(&[new_pin_target.into()]);
199+
let kind = ExprKind::Adt(Box::new(AdtExpr {
200+
adt_def: self.tcx.adt_def(pin_did),
201+
variant_index: FIRST_VARIANT,
202+
args,
203+
fields: Box::new([FieldExpr { name: FieldIdx::from(0u32), expr }]),
204+
user_ty: None,
205+
base: None,
206+
}));
207+
208+
debug!(?kind);
209+
kind
210+
}
149211
};
150212

151213
Expr { temp_lifetime, ty: adjustment.target, span, kind }
@@ -1014,7 +1076,7 @@ impl<'tcx> Cx<'tcx> {
10141076

10151077
// Reconstruct the output assuming it's a reference with the
10161078
// same region and mutability as the receiver. This holds for
1017-
// `Deref(Mut)::Deref(_mut)` and `Index(Mut)::index(_mut)`.
1079+
// `Deref(Mut)::deref(_mut)` and `Index(Mut)::index(_mut)`.
10181080
let ty::Ref(region, _, mutbl) = *self.thir[args[0]].ty.kind() else {
10191081
span_bug!(span, "overloaded_place: receiver is not a reference");
10201082
};

compiler/rustc_span/src/symbol.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1453,6 +1453,7 @@ symbols! {
14531453
pic,
14541454
pie,
14551455
pin,
1456+
pin_ergonomics,
14561457
platform_intrinsics,
14571458
plugin,
14581459
plugin_registrar,
+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
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 foo_const(_: Pin<&Foo>) {
19+
}
20+
21+
fn bar(x: Pin<&mut Foo>) {
22+
foo(x);
23+
foo(x); // for this to work we need to automatically reborrow,
24+
// as if the user had written `foo(x.as_mut())`.
25+
26+
Foo::baz(x);
27+
Foo::baz(x);
28+
29+
foo_const(x); // make sure we can reborrow &mut as &.
30+
31+
let x: Pin<&Foo> = Pin::new(&Foo);
32+
33+
foo_const(x); // make sure reborrowing from & to & works.
34+
}
35+
36+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#![feature(pin_ergonomics)]
2+
#![allow(dead_code, incomplete_features)]
3+
4+
// make sure we can't accidentally reborrow Pin<&T> as Pin<&mut T>
5+
6+
use std::pin::Pin;
7+
8+
struct Foo;
9+
10+
fn foo(_: Pin<&mut Foo>) {
11+
}
12+
13+
fn bar(x: Pin<&Foo>) {
14+
foo(x); //~ ERROR mismatched types
15+
//| ERROR types differ in mutability
16+
}
17+
18+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
error[E0308]: mismatched types
2+
--> $DIR/pin-reborrow-const-as-mut.rs:14:9
3+
|
4+
LL | foo(x);
5+
| --- ^ types differ in mutability
6+
| |
7+
| arguments to this function are incorrect
8+
|
9+
= note: expected struct `Pin<&mut Foo>`
10+
found struct `Pin<&Foo>`
11+
note: function defined here
12+
--> $DIR/pin-reborrow-const-as-mut.rs:10:4
13+
|
14+
LL | fn foo(_: Pin<&mut Foo>) {
15+
| ^^^ ----------------
16+
17+
error: aborting due to 1 previous error
18+
19+
For more information about this error, try `rustc --explain E0308`.
+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#![feature(pin_ergonomics)]
2+
#![allow(dead_code, incomplete_features)]
3+
4+
// Make sure with pin reborrowing that we can only get one mutable reborrow of a pinned reference.
5+
6+
use std::pin::{pin, Pin};
7+
8+
fn twice(_: Pin<&mut i32>, _: Pin<&mut i32>) {}
9+
10+
fn main() {
11+
let x = pin!(42);
12+
twice(x, x); //~ ERROR cannot borrow
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
error[E0499]: cannot borrow `*x.__pointer` as mutable more than once at a time
2+
--> $DIR/pin-reborrow-once.rs:12:14
3+
|
4+
LL | twice(x, x);
5+
| ----- - ^ second mutable borrow occurs here
6+
| | |
7+
| | first mutable borrow occurs here
8+
| first borrow later used by call
9+
10+
error: aborting due to 1 previous error
11+
12+
For more information about this error, try `rustc --explain E0499`.
+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,14 @@
1+
//@check-pass
2+
3+
#![feature(pin_ergonomics)]
4+
#![allow(dead_code, incomplete_features)]
5+
6+
use std::pin::Pin;
7+
8+
fn shorter<'b, T: 'b>(_: Pin<&'b mut T>) {}
9+
10+
fn test<'a: 'b, 'b, T: 'a>(x: Pin<&'a mut T>) {
11+
shorter::<'b>(x);
12+
}
13+
14+
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() {}

0 commit comments

Comments
 (0)