Skip to content

Commit

Permalink
Implement &pin const self and &pin mut self sugars
Browse files Browse the repository at this point in the history
  • Loading branch information
frank-king committed Jan 19, 2025
1 parent 73c0ae6 commit 07f960b
Show file tree
Hide file tree
Showing 7 changed files with 156 additions and 0 deletions.
13 changes: 13 additions & 0 deletions compiler/rustc_ast/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2530,6 +2530,8 @@ pub enum SelfKind {
Value(Mutability),
/// `&'lt self`, `&'lt mut self`
Region(Option<Lifetime>, Mutability),
/// `&'lt pin const self`, `&'lt pin mut self`
Pinned(Option<Lifetime>, Mutability),
/// `self: TYPE`, `mut self: TYPE`
Explicit(P<Ty>, Mutability),
}
Expand All @@ -2539,6 +2541,8 @@ impl SelfKind {
match self {
SelfKind::Region(None, mutbl) => mutbl.ref_prefix_str().to_string(),
SelfKind::Region(Some(lt), mutbl) => format!("&{lt} {}", mutbl.prefix_str()),
SelfKind::Pinned(None, mutbl) => format!("&pin {}", mutbl.ptr_str()),
SelfKind::Pinned(Some(lt), mutbl) => format!("&{lt} pin {}", mutbl.ptr_str()),
SelfKind::Value(_) | SelfKind::Explicit(_, _) => {
unreachable!("if we had an explicit self, we wouldn't be here")
}
Expand Down Expand Up @@ -2601,6 +2605,15 @@ impl Param {
tokens: None,
}),
),
SelfKind::Pinned(lt, mutbl) => (
mutbl,
P(Ty {
id: DUMMY_NODE_ID,
kind: TyKind::PinnedRef(lt, MutTy { ty: infer_ty, mutbl }),
span,
tokens: None,
}),
),
};
Param {
attrs,
Expand Down
7 changes: 7 additions & 0 deletions compiler/rustc_ast_pretty/src/pprust/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1756,6 +1756,13 @@ impl<'a> State<'a> {
self.print_mutability(*m, false);
self.word("self")
}
SelfKind::Pinned(lt, m) => {
self.word("&");
self.word("pin");
self.print_opt_lifetime(lt);
self.print_mutability(*m, true);
self.word("self")
}
SelfKind::Explicit(typ, m) => {
self.print_mutability(*m, false);
self.word("self");
Expand Down
45 changes: 45 additions & 0 deletions compiler/rustc_parse/src/parser/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2957,9 +2957,20 @@ impl<'a> Parser<'a> {
this.is_keyword_ahead(n, &[kw::SelfLower])
&& this.look_ahead(n + 1, |t| t != &token::PathSep)
};
// Is `pin const self` `n` tokens ahead?
let is_isolated_pin_const_self = |this: &Self, n| {
this.look_ahead(n, |token| token.is_ident_named(sym::pin))
&& this.is_keyword_ahead(n + 1, &[kw::Const])
&& is_isolated_self(this, n + 2)
};
// Is `mut self` `n` tokens ahead?
let is_isolated_mut_self =
|this: &Self, n| this.is_keyword_ahead(n, &[kw::Mut]) && is_isolated_self(this, n + 1);
// Is `pin mut self` `n` tokens ahead?
let is_isolated_pin_mut_self = |this: &Self, n| {
this.look_ahead(n, |token| token.is_ident_named(sym::pin))
&& is_isolated_mut_self(this, n + 1)
};
// Parse `self` or `self: TYPE`. We already know the current token is `self`.
let parse_self_possibly_typed = |this: &mut Self, m| {
let eself_ident = expect_self_ident(this);
Expand Down Expand Up @@ -3019,6 +3030,20 @@ impl<'a> Parser<'a> {
self.bump();
self.bump();
SelfKind::Region(None, Mutability::Mut)
} else if is_isolated_pin_const_self(self, 1) {
// `&pin const self`
self.bump(); // &
self.psess.gated_spans.gate(sym::pin_ergonomics, self.token.span);
self.bump(); // pin
self.bump(); // const
SelfKind::Pinned(None, Mutability::Not)
} else if is_isolated_pin_mut_self(self, 1) {
// `&pin mut self`
self.bump(); // &
self.psess.gated_spans.gate(sym::pin_ergonomics, self.token.span);
self.bump(); // pin
self.bump(); // mut
SelfKind::Pinned(None, Mutability::Mut)
} else if self.look_ahead(1, |t| t.is_lifetime()) && is_isolated_self(self, 2) {
// `&'lt self`
self.bump();
Expand All @@ -3030,6 +3055,26 @@ impl<'a> Parser<'a> {
let lt = self.expect_lifetime();
self.bump();
SelfKind::Region(Some(lt), Mutability::Mut)
} else if self.look_ahead(1, |t| t.is_lifetime())
&& is_isolated_pin_const_self(self, 2)
{
// `&'lt pin const self`
self.bump(); // &
let lt = self.expect_lifetime();
self.psess.gated_spans.gate(sym::pin_ergonomics, self.token.span);
self.bump(); // pin
self.bump(); // const
SelfKind::Pinned(Some(lt), Mutability::Not)
} else if self.look_ahead(1, |t| t.is_lifetime())
&& is_isolated_pin_mut_self(self, 2)
{
// `&'lt pin mut self`
self.bump(); // &
let lt = self.expect_lifetime();
self.psess.gated_spans.gate(sym::pin_ergonomics, self.token.span);
self.bump(); // pin
self.bump(); // mut
SelfKind::Pinned(Some(lt), Mutability::Mut)
} else {
// `&not_self`
return Ok(None);
Expand Down
27 changes: 27 additions & 0 deletions src/tools/rustfmt/src/items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2395,6 +2395,33 @@ fn rewrite_explicit_self(
)?),
}
}
ast::SelfKind::Pinned(lt, m) => {
let mut_str = m.ptr_str();
match lt {
Some(ref l) => {
let lifetime_str = l.rewrite_result(
context,
Shape::legacy(context.config.max_width(), Indent::empty()),
)?;
Ok(combine_strs_with_missing_comments(
context,
param_attrs,
&format!("&{lifetime_str} pin {mut_str} self"),
span,
shape,
!has_multiple_attr_lines,
)?)
}
None => Ok(combine_strs_with_missing_comments(
context,
param_attrs,
&format!("&pin {mut_str} self"),
span,
shape,
!has_multiple_attr_lines,
)?),
}
}
ast::SelfKind::Explicit(ref ty, mutability) => {
let type_str = ty.rewrite_result(
context,
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) {}

struct Foo;

impl Foo {
fn f(&pin const self) {}
fn g<'a>(& 'a pin const self) {}
fn h<'a>(& 'a pin
mut self) {}
fn i(&pin mut self) {}
}
9 changes: 9 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,12 @@ 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) {}

struct Foo;

impl Foo {
fn f(&self) {}
fn g<'a>(&'a self) {}
fn h<'a>(&'a mut self) {}
fn i(&mut self) {}
}
45 changes: 45 additions & 0 deletions tests/ui/async-await/pin-ergonomics/sugar-self.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
//@ check-pass

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

// Makes sure we can handle `&pin mut self` and `&pin const self` as sugar for
// `self: Pin<&mut Self>` and `self: Pin<&Self>`.

use std::pin::Pin;

struct Foo;

impl Foo {
fn baz(&pin mut self) {}

fn baz_const(&pin const self) {}

fn baz_lt<'a>(&'a pin mut self) {}

fn baz_const_lt(&'_ pin const self) {}
}

fn foo(_: &pin mut Foo) {}

fn foo_const(x: &pin const Foo) {}

fn bar(x: &pin mut Foo) {
foo(x);
foo(x); // for this to work we need to automatically reborrow,
// as if the user had written `foo(x.as_mut())`.

Foo::baz(x);
Foo::baz(x);

// make sure we can reborrow &mut as &.
foo_const(x);
Foo::baz_const(x);

let x: &pin const _ = Pin::new(&Foo);

foo_const(x); // make sure reborrowing from & to & works.
foo_const(x);
}

fn main() {}

0 comments on commit 07f960b

Please sign in to comment.