From a935481ae9052d42c39af1d71c8e8e64750c9e02 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 21 Oct 2016 10:31:53 +1100 Subject: [PATCH 1/3] Avoid an unnecessary clone in `macro_parser::parse`. This avoids ~800,000 allocations in html5ever. --- src/libsyntax/ext/tt/macro_parser.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libsyntax/ext/tt/macro_parser.rs b/src/libsyntax/ext/tt/macro_parser.rs index ef2e466a04398..a664e115ce4b4 100644 --- a/src/libsyntax/ext/tt/macro_parser.rs +++ b/src/libsyntax/ext/tt/macro_parser.rs @@ -425,8 +425,8 @@ pub fn parse(sess: &ParseSess, cur_eis.push(ei); } TokenTree::Token(_, ref t) => { - let mut ei_t = ei.clone(); if token_name_eq(t,&tok) { + let mut ei_t = ei.clone(); ei_t.idx += 1; next_eis.push(ei_t); } From e382267cfb9133ef12d59b66a2935ee45b546a61 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 21 Oct 2016 10:42:30 +1100 Subject: [PATCH 2/3] Avoid an unnecessary clone in `generic_extensions`. This avoids ~800,000 allocations in html5ever. --- src/libsyntax/ext/tt/macro_rules.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libsyntax/ext/tt/macro_rules.rs b/src/libsyntax/ext/tt/macro_rules.rs index a74d335d6046d..c585c66f9a228 100644 --- a/src/libsyntax/ext/tt/macro_rules.rs +++ b/src/libsyntax/ext/tt/macro_rules.rs @@ -139,9 +139,9 @@ fn generic_extension<'cx>(cx: &'cx ExtCtxt, macro_ident: name }) } - Failure(sp, ref msg) => if sp.lo >= best_fail_spot.lo { + Failure(sp, msg) => if sp.lo >= best_fail_spot.lo { best_fail_spot = sp; - best_fail_msg = (*msg).clone(); + best_fail_msg = msg; }, Error(err_sp, ref msg) => { cx.span_fatal(err_sp.substitute_dummy(sp), &msg[..]) From b817cf8b5730912c558aff811cd34fc3d3fa8637 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 21 Oct 2016 12:01:06 +1100 Subject: [PATCH 3/3] Replace the `String` in `ParseResult::Failure` with `Token`. This lets us delay creation of failure messages until they are needed, which avoids ~1.6M allocations in html5ever. --- src/libsyntax/ext/tt/macro_parser.rs | 19 +++++++++++++------ src/libsyntax/ext/tt/macro_rules.rs | 19 ++++++++++++------- .../auxiliary/procedural_mbe_matching.rs | 8 ++++++-- 3 files changed, 31 insertions(+), 15 deletions(-) diff --git a/src/libsyntax/ext/tt/macro_parser.rs b/src/libsyntax/ext/tt/macro_parser.rs index a664e115ce4b4..74def68b18504 100644 --- a/src/libsyntax/ext/tt/macro_parser.rs +++ b/src/libsyntax/ext/tt/macro_parser.rs @@ -251,14 +251,22 @@ pub fn nameize(p_s: &ParseSess, ms: &[TokenTree], res: &[Rc]) pub enum ParseResult { Success(T), - /// Arm failed to match - Failure(syntax_pos::Span, String), + /// Arm failed to match. If the second parameter is `token::Eof`, it + /// indicates an unexpected end of macro invocation. Otherwise, it + /// indicates that no rules expected the given token. + Failure(syntax_pos::Span, Token), /// Fatal error (malformed macro?). Abort compilation. Error(syntax_pos::Span, String) } +pub fn parse_failure_msg(tok: Token) -> String { + match tok { + token::Eof => "unexpected end of macro invocation".to_string(), + _ => format!("no rules expected the token `{}`", pprust::token_to_string(&tok)), + } +} + pub type NamedParseResult = ParseResult>>; -pub type PositionalParseResult = ParseResult>>; /// Perform a token equality check, ignoring syntax context (that is, an /// unhygienic comparison) @@ -446,7 +454,7 @@ pub fn parse(sess: &ParseSess, } else if eof_eis.len() > 1 { return Error(sp, "ambiguity: multiple successful parses".to_string()); } else { - return Failure(sp, "unexpected end of macro invocation".to_string()); + return Failure(sp, token::Eof); } } else { if (!bb_eis.is_empty() && !next_eis.is_empty()) @@ -467,8 +475,7 @@ pub fn parse(sess: &ParseSess, } )) } else if bb_eis.is_empty() && next_eis.is_empty() { - return Failure(sp, format!("no rules expected the token `{}`", - pprust::token_to_string(&tok))); + return Failure(sp, tok); } else if !next_eis.is_empty() { /* Now process the next token */ while !next_eis.is_empty() { diff --git a/src/libsyntax/ext/tt/macro_rules.rs b/src/libsyntax/ext/tt/macro_rules.rs index c585c66f9a228..06d8dc74788e9 100644 --- a/src/libsyntax/ext/tt/macro_rules.rs +++ b/src/libsyntax/ext/tt/macro_rules.rs @@ -16,7 +16,7 @@ use ext::expand::{Expansion, ExpansionKind}; use ext::placeholders; use ext::tt::macro_parser::{Success, Error, Failure}; use ext::tt::macro_parser::{MatchedSeq, MatchedNonterminal}; -use ext::tt::macro_parser::parse; +use ext::tt::macro_parser::{parse, parse_failure_msg}; use parse::ParseSess; use parse::lexer::new_tt_reader; use parse::parser::{Parser, Restrictions}; @@ -100,7 +100,7 @@ fn generic_extension<'cx>(cx: &'cx ExtCtxt, // Which arm's failure should we report? (the one furthest along) let mut best_fail_spot = DUMMY_SP; - let mut best_fail_msg = "internal error: ran no matchers".to_string(); + let mut best_fail_tok = None; for (i, lhs) in lhses.iter().enumerate() { // try each arm's matchers let lhs_tt = match *lhs { @@ -139,9 +139,9 @@ fn generic_extension<'cx>(cx: &'cx ExtCtxt, macro_ident: name }) } - Failure(sp, msg) => if sp.lo >= best_fail_spot.lo { + Failure(sp, tok) => if sp.lo >= best_fail_spot.lo { best_fail_spot = sp; - best_fail_msg = msg; + best_fail_tok = Some(tok); }, Error(err_sp, ref msg) => { cx.span_fatal(err_sp.substitute_dummy(sp), &msg[..]) @@ -149,7 +149,8 @@ fn generic_extension<'cx>(cx: &'cx ExtCtxt, } } - cx.span_fatal(best_fail_spot.substitute_dummy(sp), &best_fail_msg[..]); + let best_fail_msg = parse_failure_msg(best_fail_tok.expect("ran no matchers")); + cx.span_fatal(best_fail_spot.substitute_dummy(sp), &best_fail_msg); } pub struct MacroRulesExpander; @@ -227,8 +228,12 @@ pub fn compile(sess: &ParseSess, def: &ast::MacroDef) -> SyntaxExtension { let argument_map = match parse(sess, &Vec::new(), arg_reader, &argument_gram) { Success(m) => m, - Failure(sp, str) | Error(sp, str) => { - panic!(sess.span_diagnostic.span_fatal(sp.substitute_dummy(def.span), &str)); + Failure(sp, tok) => { + let s = parse_failure_msg(tok); + panic!(sess.span_diagnostic.span_fatal(sp.substitute_dummy(def.span), &s)); + } + Error(sp, s) => { + panic!(sess.span_diagnostic.span_fatal(sp.substitute_dummy(def.span), &s)); } }; diff --git a/src/test/run-pass-fulldeps/auxiliary/procedural_mbe_matching.rs b/src/test/run-pass-fulldeps/auxiliary/procedural_mbe_matching.rs index 2b50c4fe11e93..5229d42f1fdd4 100644 --- a/src/test/run-pass-fulldeps/auxiliary/procedural_mbe_matching.rs +++ b/src/test/run-pass-fulldeps/auxiliary/procedural_mbe_matching.rs @@ -25,6 +25,7 @@ use syntax::ext::base::{ExtCtxt, MacResult, DummyResult, MacEager}; use syntax::ext::build::AstBuilder; use syntax::ext::tt::macro_parser::{MatchedSeq, MatchedNonterminal}; use syntax::ext::tt::macro_parser::{Success, Failure, Error}; +use syntax::ext::tt::macro_parser::parse_failure_msg; use syntax::ptr::P; use syntax_pos::Span; use rustc_plugin::Registry; @@ -58,8 +59,11 @@ fn expand_mbe_matches(cx: &mut ExtCtxt, sp: Span, args: &[TokenTree]) _ => unreachable!() } } - Failure(_, s) | Error(_, s) => { - panic!("expected Success, but got Error/Failure: {}", s); + Failure(_, tok) => { + panic!("expected Success, but got Failure: {}", parse_failure_msg(tok)); + } + Error(_, s) => { + panic!("expected Success, but got Error: {}", s); } };