aboutsummaryrefslogtreecommitdiff
path: root/gcc/rust/expand/rust-macro-expand.cc
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/rust/expand/rust-macro-expand.cc')
-rw-r--r--gcc/rust/expand/rust-macro-expand.cc146
1 files changed, 142 insertions, 4 deletions
diff --git a/gcc/rust/expand/rust-macro-expand.cc b/gcc/rust/expand/rust-macro-expand.cc
index 36b3195..597cb6a 100644
--- a/gcc/rust/expand/rust-macro-expand.cc
+++ b/gcc/rust/expand/rust-macro-expand.cc
@@ -111,6 +111,122 @@ MacroExpander::expand_decl_macro (Location invoc_locus,
}
void
+MacroExpander::expand_eager_invocations (AST::MacroInvocation &invoc)
+{
+ if (invoc.get_pending_eager_invocations ().empty ())
+ return;
+
+ // We have to basically create a new delimited token tree which contains the
+ // result of one step of expansion. In the case of builtin macros called with
+ // other macro invocations, such as `concat!("h", 'a', a!())`, we need to
+ // expand `a!()` before expanding the concat macro.
+ // This will, ideally, give us a new token tree containing the various
+ // existing tokens + the result of the expansion of a!().
+ // To do this, we "parse" the given token tree to find anything that "looks
+ // like a macro invocation". Then, we get the corresponding macro invocation
+ // from the `pending_eager_invocations` vector and expand it.
+ // Because the `pending_eager_invocations` vector is created in the same order
+ // that the DelimTokenTree is parsed, we know that the first macro invocation
+ // within the DelimTokenTree corresponds to the first element in
+ // `pending_eager_invocations`. The idea is thus to:
+ // 1. Find a macro invocation in the token tree, noting the index of the start
+ // token and of the end token
+ // 2. Get its associated invocation in `pending_eager_invocations`
+ // 3. Expand that element
+ // 4. Get the token tree associated with that AST fragment
+ // 5. Replace the original tokens corresponding to the invocation with the new
+ // tokens from the fragment
+ // pseudo-code:
+ //
+ // i = 0;
+ // for tok in dtt:
+ // if tok is identifier && tok->next() is !:
+ // start = index(tok);
+ // l_delim = tok->next()->next();
+ // tok = skip_until_r_delim();
+ // end = index(tok);
+ //
+ // new_tt = expand_eager_invoc(eagers[i++]);
+ // old_tt[start..end] = new_tt;
+
+ auto dtt = invoc.get_invoc_data ().get_delim_tok_tree ();
+ auto stream = dtt.to_token_stream ();
+ std::vector<std::unique_ptr<AST::TokenTree>> new_stream;
+ size_t current_pending = 0;
+
+ // we need to create a clone of the delimited token tree as the lexer
+ // expects ownership of the tokens
+ std::vector<std::unique_ptr<Rust::AST::Token>> dtt_clone;
+ for (auto &tok : stream)
+ dtt_clone.emplace_back (tok->clone_token ());
+
+ MacroInvocLexer lex (std::move (dtt_clone));
+ Parser<MacroInvocLexer> parser (lex);
+
+ // we want to build a substitution map - basically, associating a `start` and
+ // `end` index for each of the pending macro invocations
+ std::map<std::pair<size_t, size_t>, std::unique_ptr<AST::MacroInvocation> &>
+ substitution_map;
+
+ for (size_t i = 0; i < stream.size (); i++)
+ {
+ // FIXME: Can't these offsets be figure out when we actually parse the
+ // pending_eager_invocation in the first place?
+ auto invocation = parser.parse_macro_invocation ({});
+
+ // if we've managed to parse a macro invocation, we look at the current
+ // offset and store them in the substitution map. Otherwise, we skip one
+ // token and try parsing again
+ if (invocation)
+ substitution_map.insert (
+ {{i, parser.get_token_source ().get_offs ()},
+ invoc.get_pending_eager_invocations ()[current_pending++]});
+ else
+ parser.skip_token (stream[i]->get_id ());
+ }
+
+ size_t current_idx = 0;
+ for (auto kv : substitution_map)
+ {
+ auto &to_expand = kv.second;
+ expand_invoc (*to_expand, false);
+
+ auto fragment = take_expanded_fragment ();
+ auto &new_tokens = fragment.get_tokens ();
+
+ auto start = kv.first.first;
+ auto end = kv.first.second;
+
+ // We're now going to re-add the tokens to the invocation's token tree.
+ // 1. Basically, what we want to do is insert all tokens up until the
+ // beginning of the macro invocation (start).
+ // 2. Then, we'll insert all of the tokens resulting from the macro
+ // expansion: These are in `new_tokens`.
+ // 3. Finally, we'll do that again from
+ // the end of macro and go back to 1.
+
+ for (size_t i = current_idx; i < start; i++)
+ new_stream.emplace_back (stream[i]->clone_token ());
+
+ for (auto &tok : new_tokens)
+ new_stream.emplace_back (tok->clone_token ());
+
+ current_idx = end;
+ }
+
+ // Once all of that is done, we copy the last remaining tokens from the
+ // original stream
+ for (size_t i = current_idx; i < stream.size (); i++)
+ new_stream.emplace_back (stream[i]->clone_token ());
+
+ auto new_dtt
+ = AST::DelimTokenTree (dtt.get_delim_type (), std::move (new_stream));
+
+ invoc.get_pending_eager_invocations ().clear ();
+ invoc.get_invoc_data ().set_delim_tok_tree (new_dtt);
+}
+
+void
MacroExpander::expand_invoc (AST::MacroInvocation &invoc, bool has_semicolon)
{
if (depth_exceeds_recursion_limit ())
@@ -119,6 +235,9 @@ MacroExpander::expand_invoc (AST::MacroInvocation &invoc, bool has_semicolon)
return;
}
+ if (invoc.get_kind () == AST::MacroInvocation::InvocKind::Builtin)
+ expand_eager_invocations (invoc);
+
AST::MacroInvocData &invoc_data = invoc.get_invoc_data ();
// ??
@@ -151,6 +270,11 @@ MacroExpander::expand_invoc (AST::MacroInvocation &invoc, bool has_semicolon)
if (!ok)
return;
+ // We store the last expanded invocation and macro definition for error
+ // reporting in case the recursion limit is reached
+ last_invoc = &invoc;
+ last_def = rules_def;
+
if (rules_def->is_builtin ())
fragment
= rules_def->get_builtin_transcriber () (invoc.get_locus (), invoc_data);
@@ -292,7 +416,7 @@ MacroExpander::expand_crate ()
// mark for stripping if required
item->accept_vis (attr_visitor);
- auto fragment = take_expanded_fragment (attr_visitor);
+ auto fragment = take_expanded_fragment ();
if (fragment.should_expand ())
{
// Remove the current expanded invocation
@@ -711,6 +835,9 @@ static AST::Fragment
parse_many (Parser<MacroInvocLexer> &parser, TokenId &delimiter,
std::function<AST::SingleASTNode ()> parse_fn)
{
+ auto &lexer = parser.get_token_source ();
+ auto start = lexer.get_offs ();
+
std::vector<AST::SingleASTNode> nodes;
while (true)
{
@@ -728,8 +855,9 @@ parse_many (Parser<MacroInvocLexer> &parser, TokenId &delimiter,
nodes.emplace_back (std::move (node));
}
+ auto end = lexer.get_offs ();
- return AST::Fragment::complete (std::move (nodes));
+ return AST::Fragment (std::move (nodes), lexer.get_token_slice (start, end));
}
/**
@@ -838,11 +966,16 @@ transcribe_many_stmts (Parser<MacroInvocLexer> &parser, TokenId &delimiter)
static AST::Fragment
transcribe_expression (Parser<MacroInvocLexer> &parser)
{
+ auto &lexer = parser.get_token_source ();
+ auto start = lexer.get_offs ();
+
auto expr = parser.parse_expr ();
if (expr == nullptr)
return AST::Fragment::create_error ();
- return AST::Fragment::complete ({std::move (expr)});
+ auto end = lexer.get_offs ();
+
+ return AST::Fragment ({std::move (expr)}, lexer.get_token_slice (start, end));
}
/**
@@ -853,11 +986,16 @@ transcribe_expression (Parser<MacroInvocLexer> &parser)
static AST::Fragment
transcribe_type (Parser<MacroInvocLexer> &parser)
{
+ auto &lexer = parser.get_token_source ();
+ auto start = lexer.get_offs ();
+
auto type = parser.parse_type (true);
for (auto err : parser.get_errors ())
err.emit_error ();
- return AST::Fragment::complete ({std::move (type)});
+ auto end = lexer.get_offs ();
+
+ return AST::Fragment ({std::move (type)}, lexer.get_token_slice (start, end));
}
static AST::Fragment