From f3a88c04ec57e6e08419f9dfac7dc7ac4e8547a2 Mon Sep 17 00:00:00 2001 From: Frank King Date: Sun, 19 Jan 2025 23:30:58 +0800 Subject: [PATCH] Implement `&pin const self` and `&pin mut self` sugars --- compiler/rustc_ast/src/ast.rs | 21 +++++++-- compiler/rustc_ast_pretty/src/pprust/state.rs | 7 +++ compiler/rustc_parse/src/parser/item.rs | 45 +++++++++++++++++++ src/tools/rustfmt/src/items.rs | 27 +++++++++++ src/tools/rustfmt/tests/source/pin_sugar.rs | 10 +++++ src/tools/rustfmt/tests/target/pin_sugar.rs | 9 ++++ .../async-await/pin-ergonomics/sugar-self.rs | 45 +++++++++++++++++++ .../feature-gate-pin_ergonomics.rs | 6 ++- .../feature-gate-pin_ergonomics.stderr | 38 ++++++++++++---- 9 files changed, 195 insertions(+), 13 deletions(-) create mode 100644 tests/ui/async-await/pin-ergonomics/sugar-self.rs diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 8e73df63ef54f..dc9f556e9893e 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -2530,6 +2530,8 @@ pub enum SelfKind { Value(Mutability), /// `&'lt self`, `&'lt mut self` Region(Option, Mutability), + /// `&'lt pin const self`, `&'lt pin mut self` + Pinned(Option, Mutability), /// `self: TYPE`, `mut self: TYPE` Explicit(P, Mutability), } @@ -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") } @@ -2555,11 +2559,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), @@ -2601,6 +2607,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, diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index 9b958ed6b0d6b..cf221f2127019 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -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"); diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index f3e56be9f6e8b..bf687f21a24d2 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -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); @@ -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(); @@ -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 { // `¬_self` return Ok(None); diff --git a/src/tools/rustfmt/src/items.rs b/src/tools/rustfmt/src/items.rs index e7d0fba048b43..a218845a567b2 100644 --- a/src/tools/rustfmt/src/items.rs +++ b/src/tools/rustfmt/src/items.rs @@ -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, diff --git a/src/tools/rustfmt/tests/source/pin_sugar.rs b/src/tools/rustfmt/tests/source/pin_sugar.rs index 0eb3c0770c482..370dfbc196aea 100644 --- a/src/tools/rustfmt/tests/source/pin_sugar.rs +++ b/src/tools/rustfmt/tests/source/pin_sugar.rs @@ -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) {} +} diff --git a/src/tools/rustfmt/tests/target/pin_sugar.rs b/src/tools/rustfmt/tests/target/pin_sugar.rs index c9fa883e238fd..7d04efb1b3266 100644 --- a/src/tools/rustfmt/tests/target/pin_sugar.rs +++ b/src/tools/rustfmt/tests/target/pin_sugar.rs @@ -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) {} +} diff --git a/tests/ui/async-await/pin-ergonomics/sugar-self.rs b/tests/ui/async-await/pin-ergonomics/sugar-self.rs new file mode 100644 index 0000000000000..95b45480b3a5e --- /dev/null +++ b/tests/ui/async-await/pin-ergonomics/sugar-self.rs @@ -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() {} diff --git a/tests/ui/feature-gates/feature-gate-pin_ergonomics.rs b/tests/ui/feature-gates/feature-gate-pin_ergonomics.rs index 4624faf1e53cf..b73c2bb394a65 100644 --- a/tests/ui/feature-gates/feature-gate-pin_ergonomics.rs +++ b/tests/ui/feature-gates/feature-gate-pin_ergonomics.rs @@ -7,9 +7,13 @@ struct Foo; impl Foo { fn foo(self: Pin<&mut Self>) { } + fn foo_sugar(&pin mut self) {} //~ ERROR pinned reference syntax is experimental + fn foo_sugar_const(&pin const self) {} //~ ERROR pinned reference syntax is experimental } -fn foo(x: Pin<&mut Foo>) { +fn foo(mut x: Pin<&mut Foo>) { + Foo::foo_sugar(x.as_mut()); + Foo::foo_sugar_const(x.as_ref()); let _y: &pin mut Foo = x; //~ ERROR pinned reference syntax is experimental } diff --git a/tests/ui/feature-gates/feature-gate-pin_ergonomics.stderr b/tests/ui/feature-gates/feature-gate-pin_ergonomics.stderr index dd93a7be1ada1..f63abb0c1ae3d 100644 --- a/tests/ui/feature-gates/feature-gate-pin_ergonomics.stderr +++ b/tests/ui/feature-gates/feature-gate-pin_ergonomics.stderr @@ -1,5 +1,25 @@ error[E0658]: pinned reference syntax is experimental - --> $DIR/feature-gate-pin_ergonomics.rs:13:14 + --> $DIR/feature-gate-pin_ergonomics.rs:10:19 + | +LL | fn foo_sugar(&pin mut self) {} + | ^^^ + | + = note: see issue #130494 for more information + = help: add `#![feature(pin_ergonomics)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: pinned reference syntax is experimental + --> $DIR/feature-gate-pin_ergonomics.rs:11:25 + | +LL | fn foo_sugar_const(&pin const self) {} + | ^^^ + | + = note: see issue #130494 for more information + = help: add `#![feature(pin_ergonomics)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: pinned reference syntax is experimental + --> $DIR/feature-gate-pin_ergonomics.rs:17:14 | LL | let _y: &pin mut Foo = x; | ^^^ @@ -9,7 +29,7 @@ LL | let _y: &pin mut Foo = x; = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: pinned reference syntax is experimental - --> $DIR/feature-gate-pin_ergonomics.rs:16:18 + --> $DIR/feature-gate-pin_ergonomics.rs:20:18 | LL | fn foo_sugar(_: &pin mut Foo) {} | ^^^ @@ -19,7 +39,7 @@ LL | fn foo_sugar(_: &pin mut Foo) {} = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: pinned reference syntax is experimental - --> $DIR/feature-gate-pin_ergonomics.rs:28:18 + --> $DIR/feature-gate-pin_ergonomics.rs:32:18 | LL | fn baz_sugar(_: &pin const Foo) {} | ^^^ @@ -29,7 +49,7 @@ LL | fn baz_sugar(_: &pin const Foo) {} = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0382]: use of moved value: `x` - --> $DIR/feature-gate-pin_ergonomics.rs:20:9 + --> $DIR/feature-gate-pin_ergonomics.rs:24:9 | LL | fn bar(x: Pin<&mut Foo>) { | - move occurs because `x` has type `Pin<&mut Foo>`, which does not implement the `Copy` trait @@ -39,15 +59,15 @@ LL | foo(x); | ^ value used here after move | note: consider changing this parameter type in function `foo` to borrow instead if owning the value isn't necessary - --> $DIR/feature-gate-pin_ergonomics.rs:12:11 + --> $DIR/feature-gate-pin_ergonomics.rs:14:15 | -LL | fn foo(x: Pin<&mut Foo>) { - | --- ^^^^^^^^^^^^^ this parameter takes ownership of the value +LL | fn foo(mut x: Pin<&mut Foo>) { + | --- ^^^^^^^^^^^^^ this parameter takes ownership of the value | | | in this function error[E0382]: use of moved value: `x` - --> $DIR/feature-gate-pin_ergonomics.rs:25:5 + --> $DIR/feature-gate-pin_ergonomics.rs:29:5 | LL | fn baz(mut x: Pin<&mut Foo>) { | ----- move occurs because `x` has type `Pin<&mut Foo>`, which does not implement the `Copy` trait @@ -66,7 +86,7 @@ help: consider reborrowing the `Pin` instead of moving it LL | x.as_mut().foo(); | +++++++++ -error: aborting due to 5 previous errors +error: aborting due to 7 previous errors Some errors have detailed explanations: E0382, E0658. For more information about an error, try `rustc --explain E0382`.