From 19eb307741e2dbf5fb3b8cb74142cc55fac6c786 Mon Sep 17 00:00:00 2001 From: Eric Holk Date: Fri, 20 Sep 2024 13:50:01 -0700 Subject: [PATCH] Handle incorrect and ambiguous use of pin sugar better --- compiler/rustc_parse/src/parser/ty.rs | 42 +++++++++---------- tests/ui/async-await/pin-sugar-ambiguity.rs | 15 +++++++ tests/ui/async-await/pin-sugar-no-const.rs | 8 ++++ .../ui/async-await/pin-sugar-no-const.stderr | 15 +++++++ .../{pin-reborrow-sugar.rs => pin-sugar.rs} | 0 .../feature-gate-pin_ergonomics.rs | 5 ++- 6 files changed, 60 insertions(+), 25 deletions(-) create mode 100644 tests/ui/async-await/pin-sugar-ambiguity.rs create mode 100644 tests/ui/async-await/pin-sugar-no-const.rs create mode 100644 tests/ui/async-await/pin-sugar-no-const.stderr rename tests/ui/async-await/{pin-reborrow-sugar.rs => pin-sugar.rs} (100%) diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index 1c70f4726f1fc..0d649d196a8e7 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -530,7 +530,10 @@ impl<'a> Parser<'a> { fn parse_borrowed_pointee(&mut self) -> PResult<'a, TyKind> { let and_span = self.prev_token.span; let mut opt_lifetime = self.check_lifetime().then(|| self.expect_lifetime()); - let (pinned, mut mutbl) = self.parse_pin_and_mut(); + let (pinned, mut mutbl) = match self.parse_pin_and_mut() { + Some(pin_mut) => pin_mut, + None => (Pinnedness::Not, self.parse_mutability()), + }; if self.token.is_lifetime() && mutbl == Mutability::Mut && opt_lifetime.is_none() { // A lifetime is invalid here: it would be part of a bare trait bound, which requires // it to be followed by a plus, but we disallow plus in the pointee type. @@ -572,31 +575,24 @@ impl<'a> Parser<'a> { /// Parses `pin` and `mut` annotations on references. /// /// It must be either `pin const` or `pin mut`. - pub(crate) fn parse_pin_and_mut(&mut self) -> (Pinnedness, Mutability) { - let pinned = if self.eat(&TokenKind::Ident(sym::pin, IdentIsRaw::No)) { - Pinnedness::Pinned - } else { - Pinnedness::Not - }; - - if pinned == Pinnedness::Pinned { - self.psess.gated_spans.gate(sym::pin_ergonomics, self.prev_token.span); - } - - match pinned { - Pinnedness::Pinned => { - if self.eat_keyword(kw::Const) { - (pinned, Mutability::Not) - } else if self.eat_keyword(kw::Mut) { - (pinned, Mutability::Mut) + pub(crate) fn parse_pin_and_mut(&mut self) -> Option<(Pinnedness, Mutability)> { + if self.token.is_ident_named(sym::pin) { + let result = self.look_ahead(1, |token| { + if token.is_keyword(kw::Const) { + Some((Pinnedness::Pinned, Mutability::Not)) + } else if token.is_keyword(kw::Mut) { + Some((Pinnedness::Pinned, Mutability::Mut)) } else { - // FIXME: Emit an error here - - // self.dcx().emit_err(); - (pinned, Mutability::Not) + None } + }); + if result.is_some() { + self.bump(); + self.bump(); } - Pinnedness::Not => (pinned, self.parse_mutability()), + result + } else { + None } } diff --git a/tests/ui/async-await/pin-sugar-ambiguity.rs b/tests/ui/async-await/pin-sugar-ambiguity.rs new file mode 100644 index 0000000000000..d183000931ec1 --- /dev/null +++ b/tests/ui/async-await/pin-sugar-ambiguity.rs @@ -0,0 +1,15 @@ +//@ check-pass +#![feature(pin_ergonomics)] +#![allow(dead_code, incomplete_features)] + +// Handle the case where there's ambiguity between pin as a contextual keyword and pin as a path. + +struct Foo; + +mod pin { + pub struct Foo; +} + +fn main() { + let _x: &pin ::Foo = &pin::Foo; +} diff --git a/tests/ui/async-await/pin-sugar-no-const.rs b/tests/ui/async-await/pin-sugar-no-const.rs new file mode 100644 index 0000000000000..dd6456b603481 --- /dev/null +++ b/tests/ui/async-await/pin-sugar-no-const.rs @@ -0,0 +1,8 @@ +#![feature(pin_ergonomics)] +#![allow(incomplete_features)] + +// Makes sure we don't accidentally accept `&pin Foo` without the `const` keyword. + +fn main() { + let _x: &pin i32 = todo!(); //~ ERROR found `i32` +} diff --git a/tests/ui/async-await/pin-sugar-no-const.stderr b/tests/ui/async-await/pin-sugar-no-const.stderr new file mode 100644 index 0000000000000..5f01156c1f0a4 --- /dev/null +++ b/tests/ui/async-await/pin-sugar-no-const.stderr @@ -0,0 +1,15 @@ +error: expected one of `!`, `(`, `::`, `;`, `<`, or `=`, found `i32` + --> $DIR/pin-sugar-no-const.rs:7:18 + | +LL | let _x: &pin i32 = todo!(); + | - ^^^ expected one of `!`, `(`, `::`, `;`, `<`, or `=` + | | + | while parsing the type for `_x` + | +help: there is a keyword `in` with a similar name + | +LL | let _x: &in i32 = todo!(); + | ~~ + +error: aborting due to 1 previous error + diff --git a/tests/ui/async-await/pin-reborrow-sugar.rs b/tests/ui/async-await/pin-sugar.rs similarity index 100% rename from tests/ui/async-await/pin-reborrow-sugar.rs rename to tests/ui/async-await/pin-sugar.rs diff --git a/tests/ui/feature-gates/feature-gate-pin_ergonomics.rs b/tests/ui/feature-gates/feature-gate-pin_ergonomics.rs index d694531d53abe..aba73c53932ba 100644 --- a/tests/ui/feature-gates/feature-gate-pin_ergonomics.rs +++ b/tests/ui/feature-gates/feature-gate-pin_ergonomics.rs @@ -4,12 +4,13 @@ use std::pin::Pin; struct Foo; -fn foo(_: Pin<&mut Foo>) { -} +fn foo(_: &pin mut Foo) {} fn bar(mut x: Pin<&mut Foo>) { foo(x); foo(x); //~ ERROR use of moved value: `x` } +fn baz(_: &pin Foo) {} + fn main() {}