diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index 31732882f7e86..2b098d8fdbbbf 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -83,20 +83,22 @@ enum AllowCVariadic { /// Determine if the given token can begin a bound assuming it follows Rust 2015 identifier `dyn`. /// /// In Rust 2015, `dyn` is a contextual keyword, not a full one. -fn can_begin_dyn_bound_in_edition_2015(t: Token) -> bool { - if t.is_path_start() { - // In `dyn::x`, `dyn` and `dyn<::Y>`, `dyn` should (continue to) denote a regular path - // segment for backward compatibility. We make an exception for `dyn(X)` which used to be - // interpreted as a path with parenthesized generic arguments which can be semantically - // well-formed (consider: `use std::ops::Fn as dyn;`). Instead, we treat it as a trait - // object type whose first bound is parenthesized. - return t != token::PathSep && t != token::Lt && t != token::Shl; - } +fn can_begin_dyn_bound_in_rust_2015(t: Token) -> bool { + // In `dyn::x`, `dyn` and `dyn<::Y>`, `dyn` should (continue to) denote a regular path + // segment for backward compatibility. We make an exception for `dyn(X)` which used to be + // interpreted as a path with parenthesized generic arguments which can be semantically + // well-formed (consider: `use std::ops::Fn as dyn;`). Instead, we treat it as a trait + // object type whose first bound is parenthesized. // Contrary to `Parser::can_begin_bound`, `!`, `const`, `[` and `async` are deliberately not // part of this list to contain the number of potential regressions esp. in MBE code. // `const` and `[` would regress UI test `macro-dyn-const-2015.rs` and // `!` would regress `dyn!(...)` macro calls in Rust 2015 for example. + + if t.is_path_start() { + return t != token::PathSep && t != token::Lt && t != token::Shl; + } + t == token::OpenParen || t == token::Question || t.is_lifetime() || t.is_keyword(kw::For) } @@ -347,9 +349,10 @@ impl<'a> Parser<'a> { } else { // Try to recover `for<'a> dyn Trait` or `for<'a> impl Trait`. if self.may_recover() - && (self.eat_keyword_noexpect(kw::Impl) || self.eat_keyword_noexpect(kw::Dyn)) + && (self.token.is_keyword(kw::Impl) || self.can_begin_dyn_ty()) { - let kw = self.prev_token.ident().unwrap().0; + self.bump(); + let (kw, _) = self.prev_token.ident().unwrap(); let removal_span = kw.span.with_hi(self.token.span.lo()); let path = self.parse_path(PathStyle::Type)?; let parse_plus = allow_plus == AllowPlus::Yes && self.check_plus(); @@ -397,7 +400,7 @@ impl<'a> Parser<'a> { } } else if self.eat_keyword(exp!(Impl)) { self.parse_impl_ty(&mut impl_dyn_multi)? - } else if self.is_explicit_dyn_type() { + } else if self.can_begin_dyn_ty() { self.parse_dyn_ty(&mut impl_dyn_multi)? } else if self.eat_lt() { // Qualified path @@ -1000,16 +1003,14 @@ impl<'a> Parser<'a> { Ok(GenericBound::Use(args, lo.to(self.prev_token.span))) } - /// Is a `dyn B0 + ... + Bn` type allowed here? - fn is_explicit_dyn_type(&mut self) -> bool { - self.check_keyword(exp!(Dyn)) + /// Can the current token begin a `dyn`-prefixed trait object type? + fn can_begin_dyn_ty(&mut self) -> bool { + self.token.is_keyword(kw::Dyn) && (self.token_uninterpolated_span().at_least_rust_2018() - || self.look_ahead(1, |&t| can_begin_dyn_bound_in_edition_2015(t))) + || self.look_ahead(1, |&t| can_begin_dyn_bound_in_rust_2015(t))) } - /// Parses a `dyn B0 + ... + Bn` type. - /// - /// Note that this does *not* parse bare trait objects. + /// Parse a `dyn`-prefixed trait object type. fn parse_dyn_ty(&mut self, impl_dyn_multi: &mut bool) -> PResult<'a, TyKind> { self.bump(); // `dyn` @@ -1067,8 +1068,8 @@ impl<'a> Parser<'a> { && (self.token.can_begin_type() || (self.token.is_reserved_ident() && !self.token.is_keyword(kw::Where)))) { - if self.token.is_keyword(kw::Dyn) && self.token.span.edition().at_least_rust_2018() { - // Account for `&dyn Trait + dyn Other`. + // Account for `&dyn Trait + dyn Other`. + if self.can_begin_dyn_ty() { self.bump(); self.dcx().emit_err(InvalidDynKeyword { span: self.prev_token.span, diff --git a/tests/ui/parser/dyn-2015-identifier.fail.stderr b/tests/ui/parser/dyn-2015-identifier.fail.stderr index da82a9f2d067f..43ca1a2ffd996 100644 --- a/tests/ui/parser/dyn-2015-identifier.fail.stderr +++ b/tests/ui/parser/dyn-2015-identifier.fail.stderr @@ -52,6 +52,12 @@ error[E0405]: cannot find trait `dyn` in this scope LL | type A4 = dyn + dyn; | ^^^ not found in this scope +error[E0405]: cannot find trait `dyn` in this scope + --> $DIR/dyn-2015-identifier.rs:33:17 + | +LL | type A5 = for<> dyn; + | ^^^ not found in this scope + warning: trait objects without an explicit `dyn` are deprecated --> $DIR/dyn-2015-identifier.rs:24:11 | @@ -66,6 +72,19 @@ help: if this is a dyn-compatible trait, use `dyn` LL | type A4 = dyn dyn + dyn; | +++ +warning: trait objects without an explicit `dyn` are deprecated + --> $DIR/dyn-2015-identifier.rs:33:11 + | +LL | type A5 = for<> dyn; + | ^^^^^^^^^ + | + = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021! + = note: for more information, see +help: if this is a dyn-compatible trait, use `dyn` + | +LL | type A5 = dyn for<> dyn; + | +++ + error[E0433]: cannot find module or crate `dyn` in this scope --> $DIR/dyn-2015-identifier.rs:10:11 | @@ -74,7 +93,7 @@ LL | type A1 = dyn::dyn; | = help: you might be missing a crate named `dyn` -error: aborting due to 10 previous errors; 1 warning emitted +error: aborting due to 11 previous errors; 2 warnings emitted Some errors have detailed explanations: E0405, E0425, E0433. For more information about an error, try `rustc --explain E0405`. diff --git a/tests/ui/parser/dyn-2015-identifier.rs b/tests/ui/parser/dyn-2015-identifier.rs index 3bac8d2b631dc..53a09c8895a69 100644 --- a/tests/ui/parser/dyn-2015-identifier.rs +++ b/tests/ui/parser/dyn-2015-identifier.rs @@ -26,3 +26,11 @@ type A4 = dyn + dyn; //[fail]~| ERROR cannot find trait `dyn` in this scope //[fail]~| WARN trait objects without an explicit `dyn` are deprecated //[fail]~| WARN this is accepted in the current edition + +// The `for<…> dyn …` -> `dyn for<…> …` recovery code used to incorrectly treat `dyn` as a keyword +// in Rust 2015 even it's not followed by a token in the "trigger set". +// What's more, this also used to ICE for a period of time (see also #118564). +type A5 = for<> dyn; +//[fail]~^ ERROR cannot find trait `dyn` in this scope +//[fail]~| WARN trait objects without an explicit `dyn` are deprecated +//[fail]~| WARN this is accepted in the current edition diff --git a/tests/ui/parser/recover-hrtb-before-dyn-impl-kw.rs b/tests/ui/parser/recover-hrtb-before-dyn-impl-kw.rs index b78832bbe3dc0..56eb102b225ee 100644 --- a/tests/ui/parser/recover-hrtb-before-dyn-impl-kw.rs +++ b/tests/ui/parser/recover-hrtb-before-dyn-impl-kw.rs @@ -8,8 +8,4 @@ fn test(_: &for<'a> dyn Trait) {} fn test2(_: for<'a> impl Trait) {} //~^ ERROR `for<...>` expected after `impl`, not before -// Issue #118564 -type A2 = dyn dyn>; -//~^ ERROR expected identifier, found `>` - fn main() {} diff --git a/tests/ui/parser/recover-hrtb-before-dyn-impl-kw.stderr b/tests/ui/parser/recover-hrtb-before-dyn-impl-kw.stderr index 3745cf8b07790..e1032b8fdf232 100644 --- a/tests/ui/parser/recover-hrtb-before-dyn-impl-kw.stderr +++ b/tests/ui/parser/recover-hrtb-before-dyn-impl-kw.stderr @@ -22,11 +22,5 @@ LL - fn test2(_: for<'a> impl Trait) {} LL + fn test2(_: impl for<'a> Trait) {} | -error: expected identifier, found `>` - --> $DIR/recover-hrtb-before-dyn-impl-kw.rs:12:24 - | -LL | type A2 = dyn dyn>; - | ^ expected identifier - -error: aborting due to 3 previous errors +error: aborting due to 2 previous errors