Skip to content

Commit ff5f300

Browse files
Merge #1051
1051: macros: Add remaining restrictions for follow-set restrictions r=CohenArthur a=CohenArthur Adds the remaining restrictions for follow-set ambiguities in macros. This means adding the remaining allowed tokens for all fragment specifiers with follow-up restrictions, as well as handling allowed fragment specifiers in certain cases. For example, :vis specifiers can sometimes be followed by fragments, if they have the :ident, :ty or :path specifier. Likewise for :path and :ty which can be followed by a :block. Finally, we also allow *any* fragment after a matcher: Since the matcher is delimiter by parentheses, brackets or curlies, anything is allowed afterwards. Some edge cases or allowed tokens that we cannot handle yet remain, for which FIXMEs exist. I'll open up corresponding issues. Addresses #947 Co-authored-by: Arthur Cohen <arthur.cohen@embecosm.com>
2 parents 90f938c + 6821a64 commit ff5f300

File tree

7 files changed

+120
-27
lines changed

7 files changed

+120
-27
lines changed

gcc/rust/ast/rust-macro.h

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -128,19 +128,29 @@ class MacroFragSpec
128128
}
129129
}
130130

131-
bool has_follow_set_restrictions ()
131+
bool has_follow_set_restrictions () const
132132
{
133133
switch (kind)
134134
{
135135
case EXPR:
136136
case STMT:
137-
// FIXME: Add the following cases once we can handle them properly
138-
// in `is_match_compatible()`
139-
// case PAT:
140-
// // case PAT_PARAM: FIXME: Doesn't <metavar>:pat_param exist?
141-
// case PATH:
142-
// case TY:
143-
// case VIS:
137+
case PAT:
138+
case PATH:
139+
case TY:
140+
case VIS:
141+
return true;
142+
default:
143+
return false;
144+
}
145+
}
146+
147+
bool has_follow_set_fragment_restrictions () const
148+
{
149+
switch (kind)
150+
{
151+
case PAT:
152+
case TY:
153+
case VIS:
144154
return true;
145155
default:
146156
return false;
@@ -188,7 +198,7 @@ class MacroMatchFragment : public MacroMatch
188198
}
189199

190200
Identifier get_ident () const { return ident; }
191-
MacroFragSpec get_frag_spec () const { return frag_spec; }
201+
const MacroFragSpec &get_frag_spec () const { return frag_spec; }
192202

193203
protected:
194204
/* Use covariance to implement clone function as returning this object rather

gcc/rust/lex/rust-token.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,7 @@ enum PrimitiveCoreType
165165
RS_TOKEN_KEYWORD (CONST, "const") \
166166
RS_TOKEN_KEYWORD (CONTINUE, "continue") \
167167
RS_TOKEN_KEYWORD (CRATE, "crate") \
168+
/* FIXME: Do we need to add $crate (DOLLAR_CRATE) as a reserved kw? */ \
168169
RS_TOKEN_KEYWORD (DO, "do") /* unused */ \
169170
RS_TOKEN_KEYWORD (DYN, "dyn") \
170171
RS_TOKEN_KEYWORD (ELSE, "else") \

gcc/rust/parse/rust-parse.cc

Lines changed: 82 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -93,15 +93,83 @@ extract_module_path (const AST::AttrVec &inner_attrs,
9393
return path;
9494
}
9595

96+
template <typename T>
97+
static bool
98+
contains (std::vector<T> &vec, T elm)
99+
{
100+
return std::find (vec.begin (), vec.end (), elm) != vec.end ();
101+
}
102+
103+
static bool
104+
peculiar_fragment_match_compatible_fragment (
105+
const AST::MacroFragSpec &last_spec, const AST::MacroFragSpec &spec,
106+
Location match_locus)
107+
{
108+
static std::unordered_map<AST::MacroFragSpec::Kind,
109+
std::vector<AST::MacroFragSpec::Kind>>
110+
fragment_follow_set
111+
= {{AST::MacroFragSpec::PATH, {AST::MacroFragSpec::BLOCK}},
112+
{AST::MacroFragSpec::TY, {AST::MacroFragSpec::BLOCK}},
113+
{AST::MacroFragSpec::VIS,
114+
{AST::MacroFragSpec::IDENT, AST::MacroFragSpec::TY,
115+
AST::MacroFragSpec::PATH}}};
116+
117+
auto is_valid
118+
= contains (fragment_follow_set[last_spec.get_kind ()], spec.get_kind ());
119+
120+
if (!is_valid)
121+
rust_error_at (
122+
match_locus,
123+
"fragment specifier %<%s%> is not allowed after %<%s%> fragments",
124+
spec.as_string ().c_str (), last_spec.as_string ().c_str ());
125+
126+
return is_valid;
127+
}
128+
96129
static bool
97130
peculiar_fragment_match_compatible (AST::MacroMatchFragment &last_match,
98131
AST::MacroMatch &match)
99132
{
100133
static std::unordered_map<AST::MacroFragSpec::Kind, std::vector<TokenId>>
101-
follow_set = {
102-
{AST::MacroFragSpec::EXPR, {MATCH_ARROW, COMMA, SEMICOLON}},
103-
{AST::MacroFragSpec::STMT, {MATCH_ARROW, COMMA, SEMICOLON}},
104-
};
134+
follow_set
135+
= {{AST::MacroFragSpec::EXPR, {MATCH_ARROW, COMMA, SEMICOLON}},
136+
{AST::MacroFragSpec::STMT, {MATCH_ARROW, COMMA, SEMICOLON}},
137+
{AST::MacroFragSpec::PAT, {MATCH_ARROW, COMMA, EQUAL, PIPE, IF, IN}},
138+
{AST::MacroFragSpec::PATH,
139+
{MATCH_ARROW, COMMA, EQUAL, PIPE, SEMICOLON, COLON, RIGHT_ANGLE,
140+
RIGHT_SHIFT, LEFT_SQUARE, LEFT_CURLY, AS, WHERE}},
141+
{AST::MacroFragSpec::TY,
142+
{MATCH_ARROW, COMMA, EQUAL, PIPE, SEMICOLON, COLON, RIGHT_ANGLE,
143+
RIGHT_SHIFT, LEFT_SQUARE, LEFT_CURLY, AS, WHERE}},
144+
{AST::MacroFragSpec::VIS,
145+
{
146+
COMMA,
147+
IDENTIFIER /* FIXME: Other than `priv` */,
148+
LEFT_PAREN,
149+
LEFT_SQUARE,
150+
EXCLAM,
151+
ASTERISK,
152+
AMP,
153+
LOGICAL_AND,
154+
QUESTION_MARK,
155+
LIFETIME,
156+
LEFT_ANGLE,
157+
LEFT_SHIFT,
158+
SUPER,
159+
SELF,
160+
SELF_ALIAS,
161+
EXTERN_TOK,
162+
CRATE,
163+
UNDERSCORE,
164+
FOR,
165+
IMPL,
166+
FN_TOK,
167+
UNSAFE,
168+
TYPEOF,
169+
DYN
170+
// FIXME: Add Non kw identifiers
171+
// FIXME: Add $crate as valid
172+
}}};
105173

106174
Location error_locus = match.get_match_locus ();
107175

@@ -117,9 +185,7 @@ peculiar_fragment_match_compatible (AST::MacroMatchFragment &last_match,
117185
auto tok = static_cast<AST::Token *> (&match);
118186
auto &allowed_toks
119187
= follow_set[last_match.get_frag_spec ().get_kind ()];
120-
auto is_valid = std::find (allowed_toks.begin (), allowed_toks.end (),
121-
tok->get_id ())
122-
!= allowed_toks.end ();
188+
auto is_valid = contains (allowed_toks, tok->get_id ());
123189
if (!is_valid)
124190
// FIXME: Add hint about allowed fragments
125191
rust_error_at (tok->get_match_locus (),
@@ -143,10 +209,17 @@ peculiar_fragment_match_compatible (AST::MacroMatchFragment &last_match,
143209
error_locus = matches.front ()->get_match_locus ();
144210
break;
145211
}
146-
default:
212+
case AST::MacroMatch::Fragment: {
213+
auto last_spec = last_match.get_frag_spec ();
214+
auto fragment = static_cast<AST::MacroMatchFragment *> (&match);
215+
if (last_spec.has_follow_set_fragment_restrictions ())
216+
return peculiar_fragment_match_compatible_fragment (
217+
last_spec, fragment->get_frag_spec (), match.get_match_locus ());
218+
}
147219
break;
148220
}
149221

222+
// FIXME: Improve error message
150223
rust_error_at (error_locus, "fragment not allowed after %<%s%> fragment",
151224
last_match.get_frag_spec ().as_string ().c_str ());
152225

@@ -213,16 +286,7 @@ is_match_compatible (AST::MacroMatch &last_match, AST::MacroMatch &match)
213286
return true;
214287
break;
215288
}
216-
case AST::MacroMatch::Matcher: {
217-
// Likewise for another matcher
218-
auto matcher = static_cast<AST::MacroMatcher *> (&last_match);
219-
new_last = get_back_ptr (matcher->get_matches ());
220-
// If there are no matches in the matcher, then it can be followed by
221-
// anything
222-
if (!new_last)
223-
return true;
224-
break;
225-
}
289+
case AST::MacroMatch::Matcher:
226290
case AST::MacroMatch::Tok:
227291
return true;
228292
}

gcc/testsuite/rust/compile/macro33.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
macro_rules! forbidden_frag {
2+
($t:ty $not_block:ident) => {{}}; // { dg-error "fragment specifier .ident. is not allowed after .ty. fragments" }
3+
// { dg-error "required first macro rule in macro rules definition could not be parsed" "" { target *-*-* } .-1 }
4+
// { dg-error "failed to parse item in crate" "" { target *-*-* } .-2 }
5+
}

gcc/testsuite/rust/compile/macro34.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
macro_rules! allowed_after_expr_matcher {
2+
(($t:expr) bok) => {{}}; // follow-set restrictions do not apply after a matcher, but they do apply inside the matcher
3+
}

gcc/testsuite/rust/compile/macro35.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
macro_rules! inside_matcher {
2+
(($e:expr tok) tok) => {{}}; // { dg-error "token .tok. is not allowed after .expr. fragment" }
3+
// { dg-error "failed to parse macro matcher" "" { target *-*-* } .-1 }
4+
// { dg-error "failed to parse macro match" "" { target *-*-* } .-2 }
5+
// { dg-error "required first macro rule" "" { target *-*-* } .-3 }
6+
// { dg-error "failed to parse item in crate" "" { target *-*-* } .-4 }
7+
}

gcc/testsuite/rust/compile/macro36.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
macro_rules! ty_allowed {
2+
($t:ty $b:block) => {{}};
3+
}

0 commit comments

Comments
 (0)