Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement &pin const self and &pin mut self sugars #135733

Merged
merged 3 commits into from
Mar 7, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 18 additions & 3 deletions compiler/rustc_ast/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2641,6 +2641,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 @@ -2650,6 +2652,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 All @@ -2666,11 +2670,13 @@ impl Param {
if ident.name == kw::SelfLower {
return match self.ty.kind {
TyKind::ImplicitSelf => Some(respan(self.pat.span, SelfKind::Value(mutbl))),
TyKind::Ref(lt, MutTy { ref ty, mutbl })
| TyKind::PinnedRef(lt, MutTy { ref ty, mutbl })
TyKind::Ref(lt, MutTy { ref ty, mutbl }) if ty.kind.is_implicit_self() => {
Some(respan(self.pat.span, SelfKind::Region(lt, mutbl)))
}
TyKind::PinnedRef(lt, MutTy { ref ty, mutbl })
if ty.kind.is_implicit_self() =>
{
Some(respan(self.pat.span, SelfKind::Region(lt, mutbl)))
Some(respan(self.pat.span, SelfKind::Pinned(lt, mutbl)))
}
_ => Some(respan(
self.pat.span.to(self.ty.span),
Expand Down Expand Up @@ -2712,6 +2718,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 @@ -1783,6 +1783,13 @@ impl<'a> State<'a> {
self.print_mutability(*m, false);
self.word("self")
}
SelfKind::Pinned(lt, m) => {
self.word("&");
self.print_opt_lifetime(lt);
self.word("pin ");
self.print_mutability(*m, true);
self.word("self")
}
SelfKind::Explicit(typ, m) => {
self.print_mutability(*m, false);
self.word("self");
Expand Down
62 changes: 42 additions & 20 deletions compiler/rustc_parse/src/parser/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2954,14 +2954,27 @@ impl<'a> Parser<'a> {
}
_ => unreachable!(),
};
// is lifetime `n` tokens ahead?
let is_lifetime = |this: &Self, n| this.look_ahead(n, |t| t.is_lifetime());
// Is `self` `n` tokens ahead?
let is_isolated_self = |this: &Self, n| {
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 @@ -3012,26 +3025,35 @@ impl<'a> Parser<'a> {
let eself_lo = self.token.span;
let (eself, eself_ident, eself_hi) = match self.token.uninterpolate().kind {
token::And => {
let eself = if is_isolated_self(self, 1) {
// `&self`
self.bump();
SelfKind::Region(None, Mutability::Not)
} else if is_isolated_mut_self(self, 1) {
// `&mut self`
self.bump();
self.bump();
SelfKind::Region(None, Mutability::Mut)
} else if self.look_ahead(1, |t| t.is_lifetime()) && is_isolated_self(self, 2) {
// `&'lt self`
self.bump();
let lt = self.expect_lifetime();
SelfKind::Region(Some(lt), Mutability::Not)
} else if self.look_ahead(1, |t| t.is_lifetime()) && is_isolated_mut_self(self, 2) {
// `&'lt mut self`
self.bump();
let lt = self.expect_lifetime();
self.bump();
SelfKind::Region(Some(lt), Mutability::Mut)
let has_lifetime = is_lifetime(self, 1);
let skip_lifetime_count = has_lifetime as usize;
let eself = if is_isolated_self(self, skip_lifetime_count + 1) {
// `&{'lt} self`
self.bump(); // &
let lifetime = has_lifetime.then(|| self.expect_lifetime());
SelfKind::Region(lifetime, Mutability::Not)
} else if is_isolated_mut_self(self, skip_lifetime_count + 1) {
// `&{'lt} mut self`
self.bump(); // &
let lifetime = has_lifetime.then(|| self.expect_lifetime());
self.bump(); // mut
SelfKind::Region(lifetime, Mutability::Mut)
} else if is_isolated_pin_const_self(self, skip_lifetime_count + 1) {
// `&{'lt} pin const self`
self.bump(); // &
let lifetime = has_lifetime.then(|| self.expect_lifetime());
self.psess.gated_spans.gate(sym::pin_ergonomics, self.token.span);
self.bump(); // pin
self.bump(); // const
SelfKind::Pinned(lifetime, Mutability::Not)
} else if is_isolated_pin_mut_self(self, skip_lifetime_count + 1) {
// `&{'lt} pin mut self`
self.bump(); // &
let lifetime = has_lifetime.then(|| self.expect_lifetime());
self.psess.gated_spans.gate(sym::pin_ergonomics, self.token.span);
self.bump(); // pin
self.bump(); // mut
SelfKind::Pinned(lifetime, Mutability::Mut)
} else {
// `&not_self`
return Ok(None);
Expand Down
77 changes: 34 additions & 43 deletions src/tools/rustfmt/src/items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2359,6 +2359,21 @@ impl Rewrite for ast::Param {
}
}

fn rewrite_opt_lifetime(
context: &RewriteContext<'_>,
lifetime: Option<ast::Lifetime>,
) -> RewriteResult {
let Some(l) = lifetime else {
return Ok(String::new());
};
let mut result = l.rewrite_result(
context,
Shape::legacy(context.config.max_width(), Indent::empty()),
)?;
result.push(' ');
Ok(result)
}

fn rewrite_explicit_self(
context: &RewriteContext<'_>,
explicit_self: &ast::ExplicitSelf,
Expand All @@ -2367,58 +2382,34 @@ fn rewrite_explicit_self(
shape: Shape,
has_multiple_attr_lines: bool,
) -> RewriteResult {
match explicit_self.node {
let self_str = match explicit_self.node {
ast::SelfKind::Region(lt, m) => {
let mut_str = format_mutability(m);
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} {mut_str}self"),
span,
shape,
!has_multiple_attr_lines,
)?)
}
None => Ok(combine_strs_with_missing_comments(
context,
param_attrs,
&format!("&{mut_str}self"),
span,
shape,
!has_multiple_attr_lines,
)?),
}
let lifetime_str = rewrite_opt_lifetime(context, lt)?;
format!("&{lifetime_str}{mut_str}self")
}
ast::SelfKind::Pinned(lt, m) => {
let mut_str = m.ptr_str();
let lifetime_str = rewrite_opt_lifetime(context, lt)?;
format!("&{lifetime_str}pin {mut_str} self")
}
ast::SelfKind::Explicit(ref ty, mutability) => {
let type_str = ty.rewrite_result(
context,
Shape::legacy(context.config.max_width(), Indent::empty()),
)?;

Ok(combine_strs_with_missing_comments(
context,
param_attrs,
&format!("{}self: {}", format_mutability(mutability), type_str),
span,
shape,
!has_multiple_attr_lines,
)?)
format!("{}self: {}", format_mutability(mutability), type_str)
}
ast::SelfKind::Value(mutability) => Ok(combine_strs_with_missing_comments(
context,
param_attrs,
&format!("{}self", format_mutability(mutability)),
span,
shape,
!has_multiple_attr_lines,
)?),
}
ast::SelfKind::Value(mutability) => format!("{}self", format_mutability(mutability)),
};
Ok(combine_strs_with_missing_comments(
context,
param_attrs,
&self_str,
span,
shape,
!has_multiple_attr_lines,
)?)
}

pub(crate) fn span_lo_for_param(param: &ast::Param) -> BytePos {
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(&pin const self) {}
fn g<'a>(&'a pin const self) {}
fn h<'a>(&'a pin mut self) {}
fn i(&pin mut self) {}
}
24 changes: 24 additions & 0 deletions tests/pretty/pin-ergonomics.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
//@ pp-exact

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

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_lt<'a>(_: &'a pin mut Foo) {}

fn foo_const(_: &pin const Foo) {}
fn foo_const_lt(_: &'_ pin const Foo) {}

fn main() {}
46 changes: 46 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,46 @@
//@ 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(_: &pin const Foo) {}

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

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() {}
Loading
Loading