Skip to content

Commit 6821a64

Browse files
committed
macros: Add remaining restrictions for follow-set restrictions
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.
1 parent eef1ee2 commit 6821a64

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)