Skip to content
Draft
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
2 changes: 2 additions & 0 deletions compiler/rustc_expand/src/mbe/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,8 @@ impl BestFailure {
impl<'dcx, 'matcher> Tracker<'matcher> for CollectTrackerAndEmitter<'dcx, 'matcher> {
type Failure = (Token, u32, &'static str);

const NEEDS_TRACKING: bool = true;

fn build_failure(tok: Token, position: u32, msg: &'static str) -> Self::Failure {
(tok, position, msg)
}
Expand Down
66 changes: 66 additions & 0 deletions compiler/rustc_expand/src/mbe/macro_parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -447,6 +447,49 @@ pub(crate) struct TtParser {
empty_matches: Rc<Vec<NamedMatch>>,
}


/// Matches a run of literal-token (and delimiter-marker) matcher locations
/// directly against the input, advancing `mp` and the parser past every
/// matched token. Stops at input Eof or at the first location the queue
/// machinery must handle; a literal mismatch is a definitive failure of the
/// arm, returned as `Err`. Callers ensure `mp` is the only live position and
/// that the tracker does not need to observe individual locations.
fn match_literal_run<'matcher, T: Tracker<'matcher>>(
mp: &mut MatcherPos,
parser: &mut Cow<'_, Parser<'_>>,
matcher: &'matcher [MatcherLoc],
) -> Result<(), NamedParseResult<T::Failure>> {
loop {
if parser.token == token::Eof {
return Ok(());
}
match &matcher[mp.idx] {
MatcherLoc::Token { token: t } => {
// Doc comments in the matcher are skipped, see `parse_tt_inner`.
if matches!(t, Token { kind: DocComment(..), .. }) {
mp.idx += 1;
} else if token_name_eq(t, &parser.token) {
mp.idx += 1;
parser.to_mut().bump();
} else {
// The only position failed on a literal token, so the arm
// cannot match.
return Err(Failure(T::build_failure(
parser.token,
parser.approx_token_stream_pos(),
"no rules expected this token in macro call",
)));
}
}
MatcherLoc::Delimited => {
// Entering the delimiter is trivial.
mp.idx += 1;
}
_ => return Ok(()),
}
}
}

impl TtParser {
pub(super) fn new(macro_name: Ident) -> TtParser {
TtParser {
Expand Down Expand Up @@ -635,6 +678,15 @@ impl TtParser {
self.cur_mps.clear();
self.cur_mps.push(MatcherPos { idx: 0, matches: Rc::clone(&self.empty_matches) });

// Match any leading run of literal-token locations directly; matchers
// that fail on a leading literal (the common case when a macro tries
// its rules in order) never enter the queue machinery at all.
if !T::NEEDS_TRACKING
&& let Err(failure) = match_literal_run::<T>(&mut self.cur_mps[0], parser, matcher)
{
return failure;
}

loop {
self.next_mps.clear();
self.bb_mps.clear();
Expand Down Expand Up @@ -667,6 +719,20 @@ impl TtParser {
));
}

(1, 0) if !T::NEEDS_TRACKING => {
// A single next position: advance past the matched token and, if
// the position sits at a run of literal-token (and delimiter-
// marker) locations, match the whole run directly against the
// input without going through the queue machinery. Literal-heavy
// matchers spend most of their steps in this state.
let mut mp = self.next_mps.pop().unwrap();
parser.to_mut().bump();
if let Err(failure) = match_literal_run::<T>(&mut mp, parser, matcher) {
return failure;
}
self.cur_mps.push(mp);
}

(_, 0) => {
// Dump all possible `next_mps` into `cur_mps` for the next iteration. Then
// process the next token.
Expand Down
7 changes: 7 additions & 0 deletions compiler/rustc_expand/src/mbe/macro_rules.rs
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,11 @@ pub(super) trait Tracker<'matcher> {
/// The contents of `ParseResult::Failure`.
type Failure;

/// Whether this tracker needs to observe every matcher location via
/// `before_match_loc`. When `false`, the matcher may take fast paths that
/// skip those callbacks.
const NEEDS_TRACKING: bool;

/// Arm failed to match. If the token is `token::Eof`, it indicates an unexpected
/// end of macro invocation. Otherwise, it indicates that no rules expected the given token.
/// The usize is the approximate position of the token in the input token stream.
Expand Down Expand Up @@ -361,6 +366,8 @@ pub(super) struct NoopTracker;
impl<'matcher> Tracker<'matcher> for NoopTracker {
type Failure = ();

const NEEDS_TRACKING: bool = false;

fn build_failure(_tok: Token, _position: u32, _msg: &'static str) -> Self::Failure {}

fn description() -> &'static str {
Expand Down
17 changes: 14 additions & 3 deletions compiler/rustc_expand/src/mbe/transcribe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,10 @@ impl<'psess> TranscrCtx<'psess, '_> {
struct Marker {
expand_id: LocalExpnId,
transparency: Transparency,
/// One-entry cache in front of `cache`: virtually all tokens in a macro
/// body share one syntax context, so this hits on a plain compare without
/// hashing.
last: Option<(SyntaxContext, SyntaxContext)>,
cache: FxHashMap<SyntaxContext, SyntaxContext>,
}

Expand All @@ -96,10 +100,17 @@ impl Marker {
// it's some advanced case with macro-generated macros. So if we cache the marked version
// of that context once, we'll typically have a 100% cache hit rate after that.
*span = span.map_ctxt(|ctxt| {
*self
if let Some((from, to)) = self.last
&& from == ctxt
{
return to;
}
let to = *self
.cache
.entry(ctxt)
.or_insert_with(|| ctxt.apply_mark(self.expand_id.to_expn_id(), self.transparency))
.or_insert_with(|| ctxt.apply_mark(self.expand_id.to_expn_id(), self.transparency));
self.last = Some((ctxt, to));
to
});
}
}
Expand Down Expand Up @@ -370,7 +381,7 @@ pub(super) fn transcribe<'a>(
let mut tscx = TranscrCtx {
psess,
interp,
marker: Marker { expand_id, transparency, cache: Default::default() },
marker: Marker { expand_id, transparency, last: None, cache: Default::default() },
repeats: Vec::new(),
lookup_cache: SmallVec::new(),
stack: smallvec![ExecFrame::Root { segs: &template.segs, idx: 0 }],
Expand Down
Loading