Cache pretty-print/retokenize result to avoid compile time blowup

Fixes #79242

If a `macro_rules!` recursively builds up a nested nonterminal
(passing it to a proc-macro at each step), we will end up repeatedly
pretty-printing/retokenizing the same nonterminals. Unfortunately, the
'probable equality' check we do has a non-trivial cost, which leads to a
blowup in compilation time.

As a workaround, we cache the result of the 'probable equality' check,
which eliminates the compilation time blowup for the linked issue. This
commit only touches a single file (other than adding tests), so it
should be easy to backport.

The proper solution is to remove the pretty-print/retokenize hack
entirely. However, this will almost certainly break a large number of
crates that were relying on hygiene bugs created by using the reparsed
`TokenStream`. As a result, we will definitely not want to backport
such a change.
This commit is contained in:
Aaron Hill 2020-11-23 00:32:51 -05:00
parent a0d664bae6
commit 6e466efa11
No known key found for this signature in database
GPG key ID: B4087E510E98B164
3 changed files with 85 additions and 3 deletions

View file

@ -0,0 +1,16 @@
// force-host
// no-prefer-dynamic
#![crate_type = "proc-macro"]
extern crate proc_macro;
use proc_macro::TokenStream;
#[proc_macro]
pub fn dummy(input: TokenStream) -> TokenStream {
// Iterate to force internal conversion of nonterminals
// to `proc_macro` structs
for _ in input {}
TokenStream::new()
}

View file

@ -0,0 +1,34 @@
// check-pass
// aux-build:issue-79242.rs
// Regression test for issue #79242
// Tests that compilation time doesn't blow up for a proc-macro
// invocation with deeply nested nonterminals
#![allow(unused)]
extern crate issue_79242;
macro_rules! declare_nats {
($prev:ty) => {};
($prev:ty, $n:literal$(, $tail:literal)*) => {
issue_79242::dummy! {
$prev
}
declare_nats!(Option<$prev>$(, $tail)*);
};
(0, $($n:literal),+) => {
pub struct N0;
declare_nats!(N0, $($n),+);
};
}
declare_nats! {
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28
}
fn main() {}