diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs index e0d7c780c14b6..ef0404d0a8fbe 100644 --- a/compiler/rustc_expand/src/mbe/macro_rules.rs +++ b/compiler/rustc_expand/src/mbe/macro_rules.rs @@ -42,7 +42,7 @@ use crate::expand::{AstFragment, AstFragmentKind, ensure_complete_parse, parse_a use crate::mbe::macro_check::check_meta_variables; use crate::mbe::macro_parser::{Error, ErrorReported, Failure, MatcherLoc, Success, TtParser}; use crate::mbe::quoted::{RulePart, parse_one_tt}; -use crate::mbe::transcribe::transcribe; +use crate::mbe::transcribe::{MacroRhs, transcribe}; use crate::mbe::{self, KleeneOp}; pub(crate) struct ParserAnyMacro<'a, 'b> { @@ -148,7 +148,7 @@ impl<'a, 'b> ParserAnyMacro<'a, 'b> { pub(crate) enum MacroRule { /// A function-style rule, for use with `m!()` - Func { lhs: Vec, lhs_span: Span, rhs: mbe::TokenTree }, + Func { lhs: Vec, lhs_span: Span, rhs: MacroRhs }, /// An attr rule, for use with `#[m]` Attr { unsafe_rule: bool, @@ -156,10 +156,10 @@ pub(crate) enum MacroRule { args_span: Span, body: Vec, body_span: Span, - rhs: mbe::TokenTree, + rhs: MacroRhs, }, /// A derive rule, for use with `#[m]` - Derive { body: Vec, body_span: Span, rhs: mbe::TokenTree }, + Derive { body: Vec, body_span: Span, rhs: MacroRhs }, } pub struct MacroRulesMacroExpander { @@ -183,7 +183,7 @@ impl MacroRulesMacroExpander { } MacroRule::Derive { body_span, ref rhs, .. } => (MultiSpan::from_span(body_span), rhs), }; - if has_compile_error_macro(rhs) { None } else { Some((&self.name, span)) } + if has_compile_error_macro(&rhs.tt) { None } else { Some((&self.name, span)) } } pub fn kinds(&self) -> MacroKinds { @@ -220,12 +220,9 @@ impl MacroRulesMacroExpander { let MacroRule::Derive { rhs, .. } = rule else { panic!("try_match_macro_derive returned non-derive rule"); }; - let mbe::TokenTree::Delimited(rhs_span, _, rhs) = rhs else { - cx.dcx().span_bug(sp, "malformed macro derive rhs"); - }; let id = cx.current_expansion.id; - let tts = transcribe(psess, &named_matches, rhs, *rhs_span, self.transparency, id) + let tts = transcribe(psess, &named_matches, rhs, self.transparency, id) .map_err(|e| e.emit())? .to_token_stream(); @@ -399,14 +396,11 @@ fn expand_macro<'cx, 'a: 'cx>( let MacroRule::Func { lhs, rhs, .. } = rule else { panic!("try_match_macro returned non-func rule"); }; - let mbe::TokenTree::Delimited(rhs_span, _, rhs) = rhs else { - cx.dcx().span_bug(sp, "malformed macro rhs"); - }; - let arm_span = rhs_span.entire(); + let arm_span = rhs.tt.span(); // rhs has holes ( `$id` and `$(...)` that need filled) let id = cx.current_expansion.id; - let flat = match transcribe(psess, &named_matches, rhs, *rhs_span, transparency, id) { + let flat = match transcribe(psess, &named_matches, rhs, transparency, id) { Ok(flat) => flat, Err(err) => { let guar = err.emit(); @@ -484,9 +478,6 @@ fn expand_macro_attr( let MacroRule::Attr { rhs, unsafe_rule, .. } = rule else { panic!("try_macro_match_attr returned non-attr rule"); }; - let mbe::TokenTree::Delimited(rhs_span, _, rhs) = rhs else { - cx.dcx().span_bug(sp, "malformed macro rhs"); - }; match (safety, unsafe_rule) { (Safety::Default, false) | (Safety::Unsafe(_), true) => {} @@ -502,7 +493,7 @@ fn expand_macro_attr( } let id = cx.current_expansion.id; - let tts = transcribe(psess, &named_matches, rhs, *rhs_span, transparency, id) + let tts = transcribe(psess, &named_matches, rhs, transparency, id) .map_err(|e| e.emit())? .to_token_stream(); @@ -838,11 +829,12 @@ pub fn compile_declarative_macro( }; let args = mbe::macro_parser::compute_locs(&delimited.tts); let body_span = lhs_span; + let rhs = MacroRhs::new(rhs); rules.push(MacroRule::Attr { unsafe_rule, args, args_span, body: lhs, body_span, rhs }); } else if is_derive { - rules.push(MacroRule::Derive { body: lhs, body_span: lhs_span, rhs }); + rules.push(MacroRule::Derive { body: lhs, body_span: lhs_span, rhs: MacroRhs::new(rhs) }); } else { - rules.push(MacroRule::Func { lhs, lhs_span, rhs }); + rules.push(MacroRule::Func { lhs, lhs_span, rhs: MacroRhs::new(rhs) }); } if p.token == token::Eof { break; diff --git a/compiler/rustc_expand/src/mbe/metavar_expr.rs b/compiler/rustc_expand/src/mbe/metavar_expr.rs index a02b84204cb39..3d399ba2fb0b5 100644 --- a/compiler/rustc_expand/src/mbe/metavar_expr.rs +++ b/compiler/rustc_expand/src/mbe/metavar_expr.rs @@ -13,7 +13,7 @@ pub(crate) const RAW_IDENT_ERR: &str = "`${concat(..)}` currently does not suppo pub(crate) const UNSUPPORTED_CONCAT_ELEM_ERR: &str = "expected identifier or string literal"; /// A meta-variable expression, for expansions based on properties of meta-variables. -#[derive(Debug, PartialEq, Encodable, Decodable)] +#[derive(Clone, Debug, PartialEq, Encodable, Decodable)] pub(crate) enum MetaVarExpr { /// Unification of two or more identifiers. Concat(Box<[MetaVarExprConcatElem]>), @@ -157,7 +157,7 @@ fn iter_span(iter: &TokenStreamIter<'_>) -> Option { /// Indicates what is placed in a `concat` parameter. For example, literals /// (`${concat("foo", "bar")}`) or adhoc identifiers (`${concat(foo, bar)}`). -#[derive(Debug, Decodable, Encodable, PartialEq)] +#[derive(Clone, Debug, Decodable, Encodable, PartialEq)] pub(crate) enum MetaVarExprConcatElem { /// Identifier WITHOUT a preceding dollar sign, which means that this identifier should be /// interpreted as a literal. diff --git a/compiler/rustc_expand/src/mbe/transcribe.rs b/compiler/rustc_expand/src/mbe/transcribe.rs index f7ed65de9a488..a8a49dff88dcb 100644 --- a/compiler/rustc_expand/src/mbe/transcribe.rs +++ b/compiler/rustc_expand/src/mbe/transcribe.rs @@ -1,9 +1,13 @@ +use std::mem; +use std::sync::OnceLock; + use rustc_ast::token::{ self, Delimiter, IdentIsRaw, InvisibleOrigin, Lit, LitKind, MetaVarKind, Token, TokenKind, }; use rustc_ast::tokenstream::{ - DelimSpacing, DelimSpan, FlatSink, FlatTokenCursor, FlatTt, Spacing, TokenStream, TokenTree, + DelimSpacing, DelimSpan, FlatEntry, FlatSink, FlatTokenCursor, FlatTt, Spacing, TokenStream, + TokenTree, }; use rustc_ast::{ExprKind, StmtKind, TyKind, UnOp}; use rustc_data_structures::fx::FxHashMap; @@ -41,10 +45,10 @@ struct TranscrCtx<'psess, 'itp> { /// The stack of things yet to be completely expanded. /// - /// We descend into the RHS (`src`), expanding things as we go. This stack contains the things - /// we have yet to expand/are still expanding. We start the stack off with the whole RHS. The - /// choice of spacing values doesn't matter. - stack: SmallVec<[Frame<'itp>; 1]>, + /// We descend into the compiled template of the RHS, expanding things as + /// we go. This stack contains the things we have yet to expand/are still + /// expanding. We start the stack off with the whole template. + stack: SmallVec<[ExecFrame<'itp>; 1]>, /// A stack of where we are in the repeat expansion. /// @@ -100,44 +104,229 @@ impl Marker { } } -/// An iterator over the token trees in a delimited token tree (`{ ... }`) or a sequence (`$(...)`). -struct Frame<'a> { - tts: &'a [mbe::TokenTree], - idx: usize, - kind: FrameKind, +/// A macro rule RHS: the parsed tree plus its transcription template, +/// compiled on first expansion so definitions that are never invoked don't +/// pay for compilation. +pub(crate) struct MacroRhs { + pub(crate) tt: mbe::TokenTree, + template: OnceLock