Skip to content

Commit a4a5e79

Browse files
committed
Auto merge of rust-lang#95159 - nnethercote:TtParser, r=petrochenkov
Introduce `TtParser` These commits make a number of changes to declarative macro expansion, resulting in code that is shorter, simpler, and faster. Best reviewed one commit at a time. r? `@petrochenkov`
2 parents 5f37001 + 31df680 commit a4a5e79

File tree

7 files changed

+421
-511
lines changed

7 files changed

+421
-511
lines changed

compiler/rustc_expand/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#![feature(associated_type_bounds)]
22
#![feature(associated_type_defaults)]
3+
#![feature(box_syntax)]
34
#![feature(crate_visibility_modifier)]
45
#![feature(decl_macro)]
56
#![feature(if_let_guard)]

compiler/rustc_expand/src/mbe.rs

+41-47
Original file line numberDiff line numberDiff line change
@@ -17,23 +17,48 @@ use rustc_data_structures::sync::Lrc;
1717
use rustc_span::symbol::Ident;
1818
use rustc_span::Span;
1919

20-
/// Contains the sub-token-trees of a "delimited" token tree, such as the contents of `(`. Note
21-
/// that the delimiter itself might be `NoDelim`.
20+
/// Contains the sub-token-trees of a "delimited" token tree such as `(a b c)`. The delimiter itself
21+
/// might be `NoDelim`.
2222
#[derive(Clone, PartialEq, Encodable, Decodable, Debug)]
2323
struct Delimited {
2424
delim: token::DelimToken,
25-
tts: Vec<TokenTree>,
25+
/// Note: This contains the opening and closing delimiters tokens (e.g. `(` and `)`). Note that
26+
/// these could be `NoDelim`. These token kinds must match `delim`, and the methods below
27+
/// debug_assert this.
28+
all_tts: Vec<TokenTree>,
2629
}
2730

2831
impl Delimited {
29-
/// Returns a `self::TokenTree` with a `Span` corresponding to the opening delimiter.
30-
fn open_tt(&self, span: DelimSpan) -> TokenTree {
31-
TokenTree::token(token::OpenDelim(self.delim), span.open)
32+
/// Returns a `self::TokenTree` with a `Span` corresponding to the opening delimiter. Panics if
33+
/// the delimiter is `NoDelim`.
34+
fn open_tt(&self) -> &TokenTree {
35+
let tt = self.all_tts.first().unwrap();
36+
debug_assert!(matches!(
37+
tt,
38+
&TokenTree::Token(token::Token { kind: token::OpenDelim(d), .. }) if d == self.delim
39+
));
40+
tt
41+
}
42+
43+
/// Returns a `self::TokenTree` with a `Span` corresponding to the closing delimiter. Panics if
44+
/// the delimeter is `NoDelim`.
45+
fn close_tt(&self) -> &TokenTree {
46+
let tt = self.all_tts.last().unwrap();
47+
debug_assert!(matches!(
48+
tt,
49+
&TokenTree::Token(token::Token { kind: token::CloseDelim(d), .. }) if d == self.delim
50+
));
51+
tt
3252
}
3353

34-
/// Returns a `self::TokenTree` with a `Span` corresponding to the closing delimiter.
35-
fn close_tt(&self, span: DelimSpan) -> TokenTree {
36-
TokenTree::token(token::CloseDelim(self.delim), span.close)
54+
/// Returns the tts excluding the outer delimiters.
55+
///
56+
/// FIXME: #67062 has details about why this is sub-optimal.
57+
fn inner_tts(&self) -> &[TokenTree] {
58+
// These functions are called for the assertions within them.
59+
let _open_tt = self.open_tt();
60+
let _close_tt = self.close_tt();
61+
&self.all_tts[1..self.all_tts.len() - 1]
3762
}
3863
}
3964

@@ -73,35 +98,24 @@ enum KleeneOp {
7398
ZeroOrOne,
7499
}
75100

76-
/// Similar to `tokenstream::TokenTree`, except that `$i`, `$i:ident`, `$(...)`,
77-
/// and `${...}` are "first-class" token trees. Useful for parsing macros.
101+
/// Similar to `tokenstream::TokenTree`, except that `Sequence`, `MetaVar`, `MetaVarDecl`, and
102+
/// `MetaVarExpr` are "first-class" token trees. Useful for parsing macros.
78103
#[derive(Debug, Clone, PartialEq, Encodable, Decodable)]
79104
enum TokenTree {
80105
Token(Token),
106+
/// A delimited sequence, e.g. `($e:expr)` (RHS) or `{ $e }` (LHS).
81107
Delimited(DelimSpan, Lrc<Delimited>),
82-
/// A kleene-style repetition sequence
108+
/// A kleene-style repetition sequence, e.g. `$($e:expr)*` (RHS) or `$($e),*` (LHS).
83109
Sequence(DelimSpan, Lrc<SequenceRepetition>),
84-
/// e.g., `$var`
110+
/// e.g., `$var`.
85111
MetaVar(Span, Ident),
86-
/// e.g., `$var:expr`. This is only used in the left hand side of MBE macros.
112+
/// e.g., `$var:expr`. Only appears on the LHS.
87113
MetaVarDecl(Span, Ident /* name to bind */, Option<NonterminalKind>),
88-
/// A meta-variable expression inside `${...}`
114+
/// A meta-variable expression inside `${...}`.
89115
MetaVarExpr(DelimSpan, MetaVarExpr),
90116
}
91117

92118
impl TokenTree {
93-
/// Return the number of tokens in the tree.
94-
fn len(&self) -> usize {
95-
match *self {
96-
TokenTree::Delimited(_, ref delimed) => match delimed.delim {
97-
token::NoDelim => delimed.tts.len(),
98-
_ => delimed.tts.len() + 2,
99-
},
100-
TokenTree::Sequence(_, ref seq) => seq.tts.len(),
101-
_ => 0,
102-
}
103-
}
104-
105119
/// Returns `true` if the given token tree is delimited.
106120
fn is_delimited(&self) -> bool {
107121
matches!(*self, TokenTree::Delimited(..))
@@ -115,26 +129,6 @@ impl TokenTree {
115129
}
116130
}
117131

118-
/// Gets the `index`-th sub-token-tree. This only makes sense for delimited trees and sequences.
119-
fn get_tt(&self, index: usize) -> TokenTree {
120-
match (self, index) {
121-
(&TokenTree::Delimited(_, ref delimed), _) if delimed.delim == token::NoDelim => {
122-
delimed.tts[index].clone()
123-
}
124-
(&TokenTree::Delimited(span, ref delimed), _) => {
125-
if index == 0 {
126-
return delimed.open_tt(span);
127-
}
128-
if index == delimed.tts.len() + 1 {
129-
return delimed.close_tt(span);
130-
}
131-
delimed.tts[index - 1].clone()
132-
}
133-
(&TokenTree::Sequence(_, ref seq), _) => seq.tts[index].clone(),
134-
_ => panic!("Cannot expand a token tree"),
135-
}
136-
}
137-
138132
/// Retrieves the `TokenTree`'s span.
139133
fn span(&self) -> Span {
140134
match *self {

compiler/rustc_expand/src/mbe/macro_check.rs

+11-5
Original file line numberDiff line numberDiff line change
@@ -281,7 +281,7 @@ fn check_binders(
281281
// `MetaVarExpr` can not appear in the LHS of a macro arm
282282
TokenTree::MetaVarExpr(..) => {}
283283
TokenTree::Delimited(_, ref del) => {
284-
for tt in &del.tts {
284+
for tt in del.inner_tts() {
285285
check_binders(sess, node_id, tt, macros, binders, ops, valid);
286286
}
287287
}
@@ -344,7 +344,7 @@ fn check_occurrences(
344344
check_ops_is_prefix(sess, node_id, macros, binders, ops, dl.entire(), name);
345345
}
346346
TokenTree::Delimited(_, ref del) => {
347-
check_nested_occurrences(sess, node_id, &del.tts, macros, binders, ops, valid);
347+
check_nested_occurrences(sess, node_id, del.inner_tts(), macros, binders, ops, valid);
348348
}
349349
TokenTree::Sequence(_, ref seq) => {
350350
let ops = ops.push(seq.kleene);
@@ -431,14 +431,20 @@ fn check_nested_occurrences(
431431
{
432432
let macro_rules = state == NestedMacroState::MacroRulesNotName;
433433
state = NestedMacroState::Empty;
434-
let rest =
435-
check_nested_macro(sess, node_id, macro_rules, &del.tts, &nested_macros, valid);
434+
let rest = check_nested_macro(
435+
sess,
436+
node_id,
437+
macro_rules,
438+
del.inner_tts(),
439+
&nested_macros,
440+
valid,
441+
);
436442
// If we did not check the whole macro definition, then check the rest as if outside
437443
// the macro definition.
438444
check_nested_occurrences(
439445
sess,
440446
node_id,
441-
&del.tts[rest..],
447+
&del.inner_tts()[rest..],
442448
macros,
443449
binders,
444450
ops,

0 commit comments

Comments
 (0)