1
Fork 0

Speed up Parser::expected_token_types.

The parser pushes a `TokenType` to `Parser::expected_token_types` on
every call to the various `check`/`eat` methods, and clears it on every
call to `bump`. Some of those `TokenType` values are full tokens that
require cloning and dropping. This is a *lot* of work for something
that is only used in error messages and it accounts for a significant
fraction of parsing execution time.

This commit overhauls `TokenType` so that `Parser::expected_token_types`
can be implemented as a bitset. This requires changing `TokenType` to a
C-style parameterless enum, and adding `TokenTypeSet` which uses a
`u128` for the bits. (The new `TokenType` has 105 variants.)

The new types `ExpTokenPair` and `ExpKeywordPair` are now arguments to
the `check`/`eat` methods. This is for maximum speed. The elements in
the pairs are always statically known; e.g. a
`token::BinOp(token::Star)` is always paired with a `TokenType::Star`.
So we now compute `TokenType`s in advance and pass them in to
`check`/`eat` rather than the current approach of constructing them on
insertion into `expected_token_types`.

Values of these pair types can be produced by the new `exp!` macro,
which is used at every `check`/`eat` call site. The macro is for
convenience, allowing any pair to be generated from a single identifier.

The ident/keyword filtering in `expected_one_of_not_found` is no longer
necessary. It was there to account for some sloppiness in
`TokenKind`/`TokenType` comparisons.

The existing `TokenType` is moved to a new file `token_type.rs`, and all
its new infrastructure is added to that file. There is more boilerplate
code than I would like, but I can't see how to make it shorter.
This commit is contained in:
Nicholas Nethercote 2024-12-04 15:55:06 +11:00
parent d5370d981f
commit b9bf0b4b10
22 changed files with 1357 additions and 793 deletions

View file

@ -17,7 +17,7 @@ use super::ty::{AllowPlus, RecoverQPath, RecoverReturnSign};
use super::{Parser, Restrictions, TokenType};
use crate::errors::{PathSingleColon, PathTripleColon};
use crate::parser::{CommaRecoveryMode, RecoverColon, RecoverComma};
use crate::{errors, maybe_whole};
use crate::{errors, exp, maybe_whole};
/// Specifies how to parse a path.
#[derive(Copy, Clone, PartialEq)]
@ -80,7 +80,7 @@ impl<'a> Parser<'a> {
// above). `path_span` has the span of that path, or an empty
// span in the case of something like `<T>::Bar`.
let (mut path, path_span);
if self.eat_keyword(kw::As) {
if self.eat_keyword(exp!(As)) {
let path_lo = self.token.span;
path = self.parse_path(PathStyle::Type)?;
path_span = path_lo.to(self.prev_token.span);
@ -90,7 +90,7 @@ impl<'a> Parser<'a> {
}
// See doc comment for `unmatched_angle_bracket_count`.
self.expect(&token::Gt)?;
self.expect(exp!(Gt))?;
if self.unmatched_angle_bracket_count > 0 {
self.unmatched_angle_bracket_count -= 1;
debug!("parse_qpath: (decrement) count={:?}", self.unmatched_angle_bracket_count);
@ -98,7 +98,7 @@ impl<'a> Parser<'a> {
let is_import_coupler = self.is_import_coupler();
if !is_import_coupler && !self.recover_colon_before_qpath_proj() {
self.expect(&token::PathSep)?;
self.expect(exp!(PathSep))?;
}
let qself = P(QSelf { ty, path_span, position: path.segments.len() });
@ -242,7 +242,7 @@ impl<'a> Parser<'a> {
// `PathStyle::Expr` is only provided at the root invocation and never in
// `parse_path_segment` to recurse and therefore can be checked to maintain
// this invariant.
self.check_trailing_angle_brackets(&segment, &[&token::PathSep]);
self.check_trailing_angle_brackets(&segment, &[exp!(PathSep)]);
}
segments.push(segment);
@ -275,7 +275,7 @@ impl<'a> Parser<'a> {
/// Eat `::` or, potentially, `:::`.
#[must_use]
pub(super) fn eat_path_sep(&mut self) -> bool {
let result = self.eat(&token::PathSep);
let result = self.eat(exp!(PathSep));
if result && self.may_recover() {
if self.eat_noexpect(&token::Colon) {
self.dcx().emit_err(PathTripleColon { span: self.prev_token.span });
@ -300,10 +300,8 @@ impl<'a> Parser<'a> {
)
};
let check_args_start = |this: &mut Self| {
this.expected_token_types.extend_from_slice(&[
TokenType::Token(token::Lt),
TokenType::Token(token::OpenDelim(Delimiter::Parenthesis)),
]);
this.expected_token_types.insert(TokenType::Lt);
this.expected_token_types.insert(TokenType::OpenParen);
is_args_start(&this.token)
};
@ -367,7 +365,7 @@ impl<'a> Parser<'a> {
{
self.bump(); // (
self.bump(); // ..
self.expect(&token::CloseDelim(Delimiter::Parenthesis))?;
self.expect(exp!(CloseParen))?;
let span = lo.to(self.prev_token.span);
self.psess.gated_spans.gate(sym::return_type_notation, span);
@ -661,12 +659,12 @@ impl<'a> Parser<'a> {
let mut args = ThinVec::new();
while let Some(arg) = self.parse_angle_arg(ty_generics)? {
args.push(arg);
if !self.eat(&token::Comma) {
if !self.eat(exp!(Comma)) {
if self.check_noexpect(&TokenKind::Semi)
&& self.look_ahead(1, |t| t.is_ident() || t.is_lifetime())
{
// Add `>` to the list of expected tokens.
self.check(&token::Gt);
self.check(exp!(Gt));
// Handle `,` to `;` substitution
let mut err = self.unexpected().unwrap_err();
self.bump();
@ -705,7 +703,7 @@ impl<'a> Parser<'a> {
// is present and then use that info to push the other token onto the tokens list
let separated =
self.check_noexpect(&token::Colon) || self.check_noexpect(&token::Eq);
if separated && (self.check(&token::Colon) | self.check(&token::Eq)) {
if separated && (self.check(exp!(Colon)) | self.check(exp!(Eq))) {
let arg_span = arg.span();
let (binder, ident, gen_args) = match self.get_ident_from_generic_arg(&arg) {
Ok(ident_gen_args) => ident_gen_args,
@ -720,9 +718,9 @@ impl<'a> Parser<'a> {
"`for<...>` is not allowed on associated type bounds",
));
}
let kind = if self.eat(&token::Colon) {
let kind = if self.eat(exp!(Colon)) {
AssocItemConstraintKind::Bound { bounds: self.parse_generic_bounds()? }
} else if self.eat(&token::Eq) {
} else if self.eat(exp!(Eq)) {
self.parse_assoc_equality_term(
ident,
gen_args.as_ref(),
@ -743,8 +741,8 @@ impl<'a> Parser<'a> {
if self.prev_token.is_ident()
&& (self.token.is_ident() || self.look_ahead(1, |token| token.is_ident()))
{
self.check(&token::Colon);
self.check(&token::Eq);
self.check(exp!(Colon));
self.check(exp!(Eq));
}
Ok(Some(AngleBracketedArg::Arg(arg)))
}