Skip to content

Commit

Permalink
Implement pinned borrows
Browse files Browse the repository at this point in the history
  • Loading branch information
frank-king committed Jan 20, 2025
1 parent 73c0ae6 commit e57789f
Show file tree
Hide file tree
Showing 15 changed files with 334 additions and 10 deletions.
4 changes: 4 additions & 0 deletions compiler/rustc_ast/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -864,6 +864,10 @@ pub enum BorrowKind {
/// The resulting type is either `*const T` or `*mut T`
/// where `T = typeof($expr)`.
Raw,
/// A pinned borrow, `&pin const $expr` or `&pin mut $expr`.
/// The resulting type is either `Pin<&'a T>` or `Pin<&'a mut T>`
/// where `T = typeof($expr)` and `'a` is some lifetime.
Pin,
}

#[derive(Clone, Copy, Debug, PartialEq, Encodable, Decodable, HashStable_Generic)]
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_ast_pretty/src/pprust/state/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,10 @@ impl<'a> State<'a> {
self.word_nbsp("raw");
self.print_mutability(mutability, true);
}
ast::BorrowKind::Pin => {
self.word_nbsp("pin");
self.print_mutability(mutability, true);
}
}
self.print_expr_cond_paren(
expr,
Expand Down
12 changes: 7 additions & 5 deletions compiler/rustc_const_eval/src/check_consts/ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -596,11 +596,13 @@ impl<'tcx> NonConstOp<'tcx> for EscapingMutBorrow {
kind: ccx.const_kind(),
teach: ccx.tcx.sess.teach(E0764),
}),
hir::BorrowKind::Ref => ccx.dcx().create_err(errors::MutableRefEscaping {
span,
kind: ccx.const_kind(),
teach: ccx.tcx.sess.teach(E0764),
}),
hir::BorrowKind::Ref | hir::BorrowKind::Pin => {
ccx.dcx().create_err(errors::MutableRefEscaping {
span,
kind: ccx.const_kind(),
teach: ccx.tcx.sess.teach(E0764),
})
}
}
}
}
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_hir_pretty/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1338,6 +1338,10 @@ impl<'a> State<'a> {
self.word_nbsp("raw");
self.print_mutability(mutability, true);
}
hir::BorrowKind::Pin => {
self.word_nbsp("pin");
self.print_mutability(mutability, true);
}
}
self.print_expr_cond_paren(expr, expr.precedence() < ExprPrecedence::Prefix);
}
Expand Down
5 changes: 5 additions & 0 deletions compiler/rustc_hir_typeck/src/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -690,6 +690,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let region = self.next_region_var(infer::BorrowRegion(expr.span));
Ty::new_ref(self.tcx, region, ty, mutbl)
}
hir::BorrowKind::Pin => {
// See comments in the `hir::BorrowKind::Ref` arm above.
let region = self.next_region_var(infer::BorrowRegion(expr.span));
Ty::new_pinned_ref(self.tcx, region, ty, mutbl)
}
}
}

Expand Down
25 changes: 25 additions & 0 deletions compiler/rustc_mir_build/src/thir/cx/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -474,6 +474,31 @@ impl<'tcx> Cx<'tcx> {
ExprKind::RawBorrow { mutability, arg: self.mirror_expr(arg) }
}

// make `&pin mut $expr` and `&pin const $expr` into `Pin { __pointer: &mut $expr }`
// and `Pin { __pointer: &$expr }`
hir::ExprKind::AddrOf(hir::BorrowKind::Pin, mutbl, arg) => match expr_ty.kind() {
&ty::Adt(adt_def, args)
if tcx.is_lang_item(adt_def.did(), rustc_hir::LangItem::Pin) =>
{
let arg = self.mirror_expr(arg);
let expr = self.thir.exprs.push(Expr {
temp_lifetime: TempLifetime { temp_lifetime, backwards_incompatible },
ty: args.type_at(0),
span: expr.span,
kind: ExprKind::Borrow { borrow_kind: mutbl.to_borrow_kind(), arg },
});
ExprKind::Adt(Box::new(AdtExpr {
adt_def,
variant_index: FIRST_VARIANT,
args,
fields: Box::new([FieldExpr { name: FieldIdx::from(0u32), expr }]),
user_ty: None,
base: AdtExprBase::None,
}))
}
_ => span_bug!(expr.span, "unexpected type for pinned borrow: {:?}", expr_ty),
},

hir::ExprKind::Block(blk, _) => ExprKind::Block { block: self.mirror_block(blk) },

hir::ExprKind::Assign(lhs, rhs, _) => {
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_parse/src/parser/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -871,6 +871,10 @@ impl<'a> Parser<'a> {
assert!(found_raw);
let mutability = self.parse_const_or_mut().unwrap();
(ast::BorrowKind::Raw, mutability)
} else if let Some((ast::Pinnedness::Pinned, mutbl)) = self.parse_pin_and_mut() {
// `pin [ const | mut ]`.
// `pin` has been gated in `self.parse_pin_and_mut()` so we don't need to gate it here.
(ast::BorrowKind::Pin, mutbl)
} else {
// `mut?`
(ast::BorrowKind::Ref, self.parse_mutability())
Expand Down
2 changes: 2 additions & 0 deletions src/tools/rustfmt/src/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2285,8 +2285,10 @@ fn rewrite_expr_addrof(
let operator_str = match (mutability, borrow_kind) {
(ast::Mutability::Not, ast::BorrowKind::Ref) => "&",
(ast::Mutability::Not, ast::BorrowKind::Raw) => "&raw const ",
(ast::Mutability::Not, ast::BorrowKind::Pin) => "&pin const ",
(ast::Mutability::Mut, ast::BorrowKind::Ref) => "&mut ",
(ast::Mutability::Mut, ast::BorrowKind::Raw) => "&raw mut ",
(ast::Mutability::Mut, ast::BorrowKind::Pin) => "&pin mut ",
};
rewrite_unary_prefix(context, operator_str, expr, shape)
}
Expand Down
10 changes: 10 additions & 0 deletions src/tools/rustfmt/tests/source/pin_sugar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,13 @@ fn g<'a>(x: & 'a pin const i32) {}
fn h<'a>(x: & 'a pin
mut i32) {}
fn i(x: &pin mut i32) {}

fn borrows() {
let mut foo = 0_i32;
let x: Pin<&mut _> = & pin
mut foo;

let x: Pin<&_> = &
pin const
foo;
}
7 changes: 7 additions & 0 deletions src/tools/rustfmt/tests/target/pin_sugar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,10 @@ fn f(x: &pin const i32) {}
fn g<'a>(x: &'a pin const i32) {}
fn h<'a>(x: &'a pin mut i32) {}
fn i(x: &pin mut i32) {}

fn borrows() {
let mut foo = 0_i32;
let x: Pin<&mut _> = &pin mut foo;

let x: Pin<&_> = &pin const foo;
}
59 changes: 59 additions & 0 deletions tests/ui/async-await/pin-ergonomics/borrow-mut-xor-share.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
#![feature(pin_ergonomics)]
#![allow(dead_code, incomplete_features)]

// Makes sure `&pin mut place` and `&pin const place` cannot violate the mut-xor-share rules.

use std::pin::Pin;

struct Foo;

fn foo_mut(_: &mut Foo) {
}

fn foo_ref(_: &Foo) {
}

fn foo_pin_mut(_: Pin<&mut Foo>) {
}

fn foo_pin_ref(_: Pin<&Foo>) {
}

fn bar() {
let foo = Foo;
foo_pin_mut(&pin mut foo); //~ ERROR cannot borrow `foo` as mutable, as it is not declared as mutable

let mut foo = Foo;
let x = &pin mut foo;
foo_pin_ref(&pin const foo); //~ ERROR cannot borrow `foo` as immutable because it is also borrowed as mutable
foo_pin_mut(&pin mut foo); //~ ERROR cannot borrow `foo` as mutable more than once at a time
foo_ref(&foo); //~ ERROR cannot borrow `foo` as immutable because it is also borrowed as mutable
foo_mut(&mut foo); //~ ERROR cannot borrow `foo` as mutable more than once at a time

foo_pin_mut(x);

let mut foo = Foo;
let x = &pin const foo;
foo_pin_ref(&pin const foo); // ok
foo_pin_mut(&pin mut foo); //~ ERROR cannot borrow `foo` as mutable because it is also borrowed as immutable
foo_ref(&foo); // ok
foo_mut(&mut foo); //~ ERROR cannot borrow `foo` as mutable because it is also borrowed as immutable

foo_pin_ref(x);

let mut foo = Foo;
let x = &mut foo;
foo_pin_ref(&pin const foo); //~ ERROR cannot borrow `foo` as immutable because it is also borrowed as mutable
foo_pin_mut(&pin mut foo); //~ ERROR cannot borrow `foo` as mutable more than once at a time

foo_mut(x);

let mut foo = Foo;
let x = &foo;
foo_pin_ref(&pin const foo); // ok
foo_pin_mut(&pin mut foo); //~ ERROR cannot borrow `foo` as mutable because it is also borrowed as immutable

foo_ref(x);
}

fn main() {}
121 changes: 121 additions & 0 deletions tests/ui/async-await/pin-ergonomics/borrow-mut-xor-share.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
error[E0596]: cannot borrow `foo` as mutable, as it is not declared as mutable
--> $DIR/borrow-mut-xor-share.rs:24:17
|
LL | foo_pin_mut(&pin mut foo);
| ^^^^^^^^^^^^ cannot borrow as mutable
|
help: consider changing this to be mutable
|
LL | let mut foo = Foo;
| +++

error[E0502]: cannot borrow `foo` as immutable because it is also borrowed as mutable
--> $DIR/borrow-mut-xor-share.rs:28:17
|
LL | let x = &pin mut foo;
| ------------ mutable borrow occurs here
LL | foo_pin_ref(&pin const foo);
| ^^^^^^^^^^^^^^ immutable borrow occurs here
...
LL | foo_pin_mut(x);
| - mutable borrow later used here

error[E0499]: cannot borrow `foo` as mutable more than once at a time
--> $DIR/borrow-mut-xor-share.rs:29:17
|
LL | let x = &pin mut foo;
| ------------ first mutable borrow occurs here
LL | foo_pin_ref(&pin const foo);
LL | foo_pin_mut(&pin mut foo);
| ^^^^^^^^^^^^ second mutable borrow occurs here
...
LL | foo_pin_mut(x);
| - first borrow later used here

error[E0502]: cannot borrow `foo` as immutable because it is also borrowed as mutable
--> $DIR/borrow-mut-xor-share.rs:30:13
|
LL | let x = &pin mut foo;
| ------------ mutable borrow occurs here
...
LL | foo_ref(&foo);
| ^^^^ immutable borrow occurs here
...
LL | foo_pin_mut(x);
| - mutable borrow later used here

error[E0499]: cannot borrow `foo` as mutable more than once at a time
--> $DIR/borrow-mut-xor-share.rs:31:13
|
LL | let x = &pin mut foo;
| ------------ first mutable borrow occurs here
...
LL | foo_mut(&mut foo);
| ^^^^^^^^ second mutable borrow occurs here
LL |
LL | foo_pin_mut(x);
| - first borrow later used here

error[E0502]: cannot borrow `foo` as mutable because it is also borrowed as immutable
--> $DIR/borrow-mut-xor-share.rs:38:17
|
LL | let x = &pin const foo;
| -------------- immutable borrow occurs here
LL | foo_pin_ref(&pin const foo); // ok
LL | foo_pin_mut(&pin mut foo);
| ^^^^^^^^^^^^ mutable borrow occurs here
...
LL | foo_pin_ref(x);
| - immutable borrow later used here

error[E0502]: cannot borrow `foo` as mutable because it is also borrowed as immutable
--> $DIR/borrow-mut-xor-share.rs:40:13
|
LL | let x = &pin const foo;
| -------------- immutable borrow occurs here
...
LL | foo_mut(&mut foo);
| ^^^^^^^^ mutable borrow occurs here
LL |
LL | foo_pin_ref(x);
| - immutable borrow later used here

error[E0502]: cannot borrow `foo` as immutable because it is also borrowed as mutable
--> $DIR/borrow-mut-xor-share.rs:46:17
|
LL | let x = &mut foo;
| -------- mutable borrow occurs here
LL | foo_pin_ref(&pin const foo);
| ^^^^^^^^^^^^^^ immutable borrow occurs here
...
LL | foo_mut(x);
| - mutable borrow later used here

error[E0499]: cannot borrow `foo` as mutable more than once at a time
--> $DIR/borrow-mut-xor-share.rs:47:17
|
LL | let x = &mut foo;
| -------- first mutable borrow occurs here
LL | foo_pin_ref(&pin const foo);
LL | foo_pin_mut(&pin mut foo);
| ^^^^^^^^^^^^ second mutable borrow occurs here
LL |
LL | foo_mut(x);
| - first borrow later used here

error[E0502]: cannot borrow `foo` as mutable because it is also borrowed as immutable
--> $DIR/borrow-mut-xor-share.rs:54:17
|
LL | let x = &foo;
| ---- immutable borrow occurs here
LL | foo_pin_ref(&pin const foo); // ok
LL | foo_pin_mut(&pin mut foo);
| ^^^^^^^^^^^^ mutable borrow occurs here
LL |
LL | foo_ref(x);
| - immutable borrow later used here

error: aborting due to 10 previous errors

Some errors have detailed explanations: E0499, E0502, E0596.
For more information about an error, try `rustc --explain E0499`.
31 changes: 31 additions & 0 deletions tests/ui/async-await/pin-ergonomics/borrow.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
//@ check-pass

#![feature(pin_ergonomics)]
#![allow(dead_code, incomplete_features)]

// Makes sure we can handle `&pin mut place` and `&pin const place` as sugar for
// `unsafe { Pin::new_unchecked(&mut place) }` and `Pin::new(&place)`.

use std::pin::Pin;

struct Foo;

fn foo(_: Pin<&mut Foo>) {
}

fn foo_const(_: Pin<&Foo>) {
}

fn bar() {
let mut x: Pin<&mut _> = &pin mut Foo;
foo(x.as_mut());
foo(x.as_mut());
foo_const(x);

let x: Pin<&_> = &pin const Foo;

foo_const(x);
foo_const(x);
}

fn main() {}
16 changes: 16 additions & 0 deletions tests/ui/feature-gates/feature-gate-pin_ergonomics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ fn foo(x: Pin<&mut Foo>) {
let _y: &pin mut Foo = x; //~ ERROR pinned reference syntax is experimental
}

fn foo_const(x: Pin<&Foo>) {
let _y: &pin const Foo = x; //~ ERROR pinned reference syntax is experimental
}

fn foo_sugar(_: &pin mut Foo) {} //~ ERROR pinned reference syntax is experimental

fn bar(x: Pin<&mut Foo>) {
Expand All @@ -27,4 +31,16 @@ fn baz(mut x: Pin<&mut Foo>) {

fn baz_sugar(_: &pin const Foo) {} //~ ERROR pinned reference syntax is experimental

fn borrows() {
let mut x: Pin<&mut _> = &pin mut Foo; //~ ERROR pinned reference syntax is experimental
foo(x.as_mut());
foo(x.as_mut());
foo_const(x.as_ref());

let x: Pin<&_> = &pin const Foo; //~ ERROR pinned reference syntax is experimental

foo_const(x);
foo_const(x);
}

fn main() {}
Loading

0 comments on commit e57789f

Please sign in to comment.