Skip to content

Commit 39c0425

Browse files
Merge #994 #997
994: Parse macro patterns properly in repetitions r=CohenArthur a=CohenArthur Closes #966 We actually cannot reuse functions from the parser since we're expanding a macro transcriber. This is fine as the "algorithm" is extremely simple 997: macros: Allow any delimiters for invocation r=CohenArthur a=CohenArthur Closes #946 Co-authored-by: Arthur Cohen <arthur.cohen@embecosm.com>
3 parents 865b609 + dc2eab3 + d2a6a5e commit 39c0425

File tree

4 files changed

+87
-29
lines changed

4 files changed

+87
-29
lines changed

gcc/rust/expand/rust-macro-expand.cc

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3493,26 +3493,30 @@ MacroExpander::match_matcher (Parser<MacroInvocLexer> &parser,
34933493
return false;
34943494
}
34953495

3496+
auto delimiter = parser.peek_current_token ();
3497+
34963498
// this is used so we can check that we delimit the stream correctly.
3497-
switch (matcher.get_delim_type ())
3499+
switch (delimiter->get_id ())
34983500
{
3499-
case AST::DelimType::PARENS: {
3501+
case LEFT_PAREN: {
35003502
if (!parser.skip_token (LEFT_PAREN))
35013503
return false;
35023504
}
35033505
break;
35043506

3505-
case AST::DelimType::SQUARE: {
3507+
case LEFT_SQUARE: {
35063508
if (!parser.skip_token (LEFT_SQUARE))
35073509
return false;
35083510
}
35093511
break;
35103512

3511-
case AST::DelimType::CURLY: {
3513+
case LEFT_CURLY: {
35123514
if (!parser.skip_token (LEFT_CURLY))
35133515
return false;
35143516
}
35153517
break;
3518+
default:
3519+
gcc_unreachable ();
35163520
}
35173521

35183522
const MacroInvocLexer &source = parser.get_token_source ();
@@ -3566,25 +3570,27 @@ MacroExpander::match_matcher (Parser<MacroInvocLexer> &parser,
35663570
}
35673571
}
35683572

3569-
switch (matcher.get_delim_type ())
3573+
switch (delimiter->get_id ())
35703574
{
3571-
case AST::DelimType::PARENS: {
3575+
case LEFT_PAREN: {
35723576
if (!parser.skip_token (RIGHT_PAREN))
35733577
return false;
35743578
}
35753579
break;
35763580

3577-
case AST::DelimType::SQUARE: {
3581+
case LEFT_SQUARE: {
35783582
if (!parser.skip_token (RIGHT_SQUARE))
35793583
return false;
35803584
}
35813585
break;
35823586

3583-
case AST::DelimType::CURLY: {
3587+
case LEFT_CURLY: {
35843588
if (!parser.skip_token (RIGHT_CURLY))
35853589
return false;
35863590
}
35873591
break;
3592+
default:
3593+
gcc_unreachable ();
35883594
}
35893595

35903596
return true;

gcc/rust/expand/rust-macro-substitute-ctx.cc

Lines changed: 47 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,6 @@ SubstituteCtx::substitute_repetition (
3939
{
4040
rust_assert (pattern_end < macro.size ());
4141

42-
rust_debug ("pattern start: %lu", pattern_start);
43-
rust_debug ("pattern end: %lu", pattern_end);
44-
4542
std::vector<std::unique_ptr<AST::Token>> expanded;
4643

4744
// Find the first fragment and get the amount of repetitions that we should
@@ -154,19 +151,57 @@ SubstituteCtx::substitute_token (size_t token_idx)
154151
// We need to parse up until the closing delimiter and expand this
155152
// fragment->n times.
156153
rust_debug ("expanding repetition");
157-
std::vector<std::unique_ptr<AST::Token>> repetition_pattern;
154+
155+
// We're in a context where macro repetitions have already been
156+
// parsed and validated: This means that
157+
// 1/ There will be no delimiters as that is an error
158+
// 2/ There are no fragment specifiers anymore, which prevents us
159+
// from reusing parser functions.
160+
//
161+
// Repetition patterns are also special in that they cannot contain
162+
// "rogue" delimiters: For example, this is invalid, as they are
163+
// parsed as MacroMatches and must contain a correct amount of
164+
// delimiters.
165+
// `$($e:expr ) )`
166+
// ^ rogue closing parenthesis
167+
//
168+
// With all of that in mind, we can simply skip ahead from one
169+
// parenthesis to the other to find the pattern to expand. Of course,
170+
// pairs of delimiters, including parentheses, are allowed.
171+
// `$($e:expr ( ) )`
172+
// Parentheses are the sole delimiter for which we need a special
173+
// behavior since they delimit the repetition pattern
174+
158175
size_t pattern_start = token_idx + 1;
159176
size_t pattern_end = pattern_start;
160-
for (; pattern_end < macro.size ()
161-
&& macro.at (pattern_end)->get_id () != RIGHT_PAREN;
162-
pattern_end++)
163-
;
177+
auto parentheses_stack = 0;
178+
for (size_t idx = pattern_start; idx < macro.size (); idx++)
179+
{
180+
if (macro.at (idx)->get_id () == LEFT_PAREN)
181+
{
182+
parentheses_stack++;
183+
}
184+
else if (macro.at (idx)->get_id () == RIGHT_PAREN)
185+
{
186+
if (parentheses_stack == 0)
187+
{
188+
pattern_end = idx;
189+
break;
190+
}
191+
parentheses_stack--;
192+
}
193+
}
194+
195+
// Unreachable case, but let's make sure we don't ever run into it
196+
rust_assert (pattern_end != pattern_start);
164197

165198
std::unique_ptr<AST::Token> separator_token = nullptr;
166-
// FIXME: Can this go out of bounds?
167-
auto &post_pattern_token = macro.at (pattern_end + 1);
168-
if (!is_rep_op (post_pattern_token))
169-
separator_token = post_pattern_token->clone_token ();
199+
if (pattern_end + 1 <= macro.size ())
200+
{
201+
auto &post_pattern_token = macro.at (pattern_end + 1);
202+
if (!is_rep_op (post_pattern_token))
203+
separator_token = post_pattern_token->clone_token ();
204+
}
170205

171206
// Amount of tokens to skip
172207
auto to_skip = 0;
@@ -178,15 +213,6 @@ SubstituteCtx::substitute_token (size_t token_idx)
178213
if (separator_token)
179214
to_skip += 1;
180215

181-
// FIXME: This skips whitespaces... Is that okay??
182-
// FIXME: Is there any existing parsing function that allows us to
183-
// parse a macro pattern?
184-
185-
// FIXME: Add error handling in the case we haven't found a matching
186-
// closing delimiter
187-
188-
// FIXME: We need to parse the repetition token now
189-
190216
return {substitute_repetition (pattern_start, pattern_end,
191217
std::move (separator_token)),
192218
pattern_end - pattern_start + to_skip};

gcc/testsuite/rust/compile/macro10.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// { dg-additional-options "-w" }
2+
macro_rules! foo {
3+
{} => {
4+
15
5+
};
6+
}
7+
8+
fn main() {
9+
let a = foo!();
10+
let b = foo![];
11+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
macro_rules! add_parens {
2+
($($rep:ident ( ) )*) => {
3+
{ 0 $(+ $rep ( ))* }
4+
};
5+
}
6+
7+
fn f() -> i32 {
8+
1
9+
}
10+
11+
fn main() -> i32 {
12+
let a = add_parens!(f() f() f());
13+
14+
a - 3
15+
}

0 commit comments

Comments
 (0)