Skip to content

Commit a7e7234

Browse files
Merge #1055
1055: Allow keeping list of last matches to check against r=CohenArthur a=CohenArthur When trying to figure out if a match can follow another, we must figure out whether or not that match is in the follow-set of the other. If that match is zeroable (i.e a repetition using the * or ? kleene operators), then we must be able to check the match after them: should our current match not be present, the match after must be part of the follow-set. This commits allows us to performs such checks properly and to "look past" zeroable matches. This is not done with any lookahead, simply by keeping a list of pointers to possible previous matches and checking all of them for ambiguities. Addresses #947 Closes #947 Co-authored-by: Arthur Cohen <arthur.cohen@embecosm.com>
2 parents 89ad4f2 + 7ea3548 commit a7e7234

File tree

6 files changed

+65
-19
lines changed

6 files changed

+65
-19
lines changed

gcc/rust/ast/rust-macro.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,10 @@ class MacroMatchRepetition : public MacroMatch
291291
MacroRepOp get_op () const { return op; }
292292
const std::unique_ptr<MacroRepSep> &get_sep () const { return sep; }
293293
std::vector<std::unique_ptr<MacroMatch> > &get_matches () { return matches; }
294+
const std::vector<std::unique_ptr<MacroMatch> > &get_matches () const
295+
{
296+
return matches;
297+
}
294298

295299
protected:
296300
/* Use covariance to implement clone function as returning this object rather
@@ -366,6 +370,10 @@ class MacroMatcher : public MacroMatch
366370

367371
DelimType get_delim_type () const { return delim_type; }
368372
std::vector<std::unique_ptr<MacroMatch> > &get_matches () { return matches; }
373+
const std::vector<std::unique_ptr<MacroMatch> > &get_matches () const
374+
{
375+
return matches;
376+
}
369377

370378
protected:
371379
/* Use covariance to implement clone function as returning this object rather

gcc/rust/parse/rust-parse-impl.h

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1750,6 +1750,10 @@ Parser<ManagedTokenSource>::parse_macro_matcher ()
17501750

17511751
// parse actual macro matches
17521752
std::vector<std::unique_ptr<AST::MacroMatch>> matches;
1753+
// Set of possible preceding macro matches to make sure follow-set
1754+
// restrictions are respected.
1755+
// TODO: Consider using std::reference_wrapper instead of raw pointers?
1756+
std::vector<const AST::MacroMatch *> last_matches;
17531757

17541758
t = lexer.peek_token ();
17551759
// parse token trees until the initial delimiter token is found again
@@ -1770,9 +1774,30 @@ Parser<ManagedTokenSource>::parse_macro_matcher ()
17701774

17711775
if (matches.size () > 0)
17721776
{
1773-
auto &last_match = matches.back ();
1774-
if (!is_match_compatible (*last_match, *match))
1775-
return AST::MacroMatcher::create_error (match->get_match_locus ());
1777+
const auto *last_match = matches.back ().get ();
1778+
1779+
// We want to check if we are dealing with a zeroable repetition
1780+
bool zeroable = false;
1781+
if (last_match->get_macro_match_type ()
1782+
== AST::MacroMatch::MacroMatchType::Repetition)
1783+
{
1784+
auto repetition
1785+
= static_cast<const AST::MacroMatchRepetition *> (last_match);
1786+
1787+
if (repetition->get_op ()
1788+
!= AST::MacroMatchRepetition::MacroRepOp::ONE_OR_MORE)
1789+
zeroable = true;
1790+
}
1791+
1792+
if (!zeroable)
1793+
last_matches.clear ();
1794+
1795+
last_matches.emplace_back (last_match);
1796+
1797+
for (auto last : last_matches)
1798+
if (!is_match_compatible (*last, *match))
1799+
return AST::MacroMatcher::create_error (
1800+
match->get_match_locus ());
17761801
}
17771802

17781803
matches.push_back (std::move (match));

gcc/rust/parse/rust-parse.cc

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -105,8 +105,8 @@ contains (std::vector<T> &vec, T elm)
105105
*/
106106

107107
template <typename T>
108-
static T *
109-
get_back_ptr (std::vector<std::unique_ptr<T>> &values)
108+
static const T *
109+
get_back_ptr (const std::vector<std::unique_ptr<T>> &values)
110110
{
111111
if (values.empty ())
112112
return nullptr;
@@ -115,8 +115,8 @@ get_back_ptr (std::vector<std::unique_ptr<T>> &values)
115115
}
116116

117117
template <typename T>
118-
static T *
119-
get_front_ptr (std::vector<std::unique_ptr<T>> &values)
118+
static const T *
119+
get_front_ptr (const std::vector<std::unique_ptr<T>> &values)
120120
{
121121
if (values.empty ())
122122
return nullptr;
@@ -151,8 +151,8 @@ peculiar_fragment_match_compatible_fragment (
151151
}
152152

153153
static bool
154-
peculiar_fragment_match_compatible (AST::MacroMatchFragment &last_match,
155-
AST::MacroMatch &match)
154+
peculiar_fragment_match_compatible (const AST::MacroMatchFragment &last_match,
155+
const AST::MacroMatch &match)
156156
{
157157
static std::unordered_map<AST::MacroFragSpec::Kind, std::vector<TokenId>>
158158
follow_set
@@ -208,7 +208,7 @@ peculiar_fragment_match_compatible (AST::MacroMatchFragment &last_match,
208208
switch (match.get_macro_match_type ())
209209
{
210210
case AST::MacroMatch::Tok: {
211-
auto tok = static_cast<AST::Token *> (&match);
211+
auto tok = static_cast<const AST::Token *> (&match);
212212
if (contains (allowed_toks, tok->get_id ()))
213213
return true;
214214
kind_str = "token `"
@@ -218,15 +218,16 @@ peculiar_fragment_match_compatible (AST::MacroMatchFragment &last_match,
218218
}
219219
break;
220220
case AST::MacroMatch::Repetition: {
221-
auto repetition = static_cast<AST::MacroMatchRepetition *> (&match);
221+
auto repetition
222+
= static_cast<const AST::MacroMatchRepetition *> (&match);
222223
auto &matches = repetition->get_matches ();
223224
auto first_frag = get_front_ptr (matches);
224225
if (first_frag)
225226
return peculiar_fragment_match_compatible (last_match, *first_frag);
226227
break;
227228
}
228229
case AST::MacroMatch::Matcher: {
229-
auto matcher = static_cast<AST::MacroMatcher *> (&match);
230+
auto matcher = static_cast<const AST::MacroMatcher *> (&match);
230231
auto first_token = matcher->get_delim_type ();
231232
TokenId delim_id;
232233
switch (first_token)
@@ -250,7 +251,7 @@ peculiar_fragment_match_compatible (AST::MacroMatchFragment &last_match,
250251
}
251252
case AST::MacroMatch::Fragment: {
252253
auto last_spec = last_match.get_frag_spec ();
253-
auto fragment = static_cast<AST::MacroMatchFragment *> (&match);
254+
auto fragment = static_cast<const AST::MacroMatchFragment *> (&match);
254255
if (last_spec.has_follow_set_fragment_restrictions ())
255256
return peculiar_fragment_match_compatible_fragment (
256257
last_spec, fragment->get_frag_spec (), match.get_match_locus ());
@@ -273,9 +274,10 @@ peculiar_fragment_match_compatible (AST::MacroMatchFragment &last_match,
273274
}
274275

275276
bool
276-
is_match_compatible (AST::MacroMatch &last_match, AST::MacroMatch &match)
277+
is_match_compatible (const AST::MacroMatch &last_match,
278+
const AST::MacroMatch &match)
277279
{
278-
AST::MacroMatch *new_last = nullptr;
280+
const AST::MacroMatch *new_last = nullptr;
279281

280282
// We want to "extract" the concerning matches. In cases such as matchers and
281283
// repetitions, we actually store multiple matchers, but are only concerned
@@ -290,7 +292,8 @@ is_match_compatible (AST::MacroMatch &last_match, AST::MacroMatch &match)
290292
// last match (or its actual last component), and it is a fragment, it
291293
// may contain some follow up restrictions.
292294
case AST::MacroMatch::Fragment: {
293-
auto fragment = static_cast<AST::MacroMatchFragment *> (&last_match);
295+
auto fragment
296+
= static_cast<const AST::MacroMatchFragment *> (&last_match);
294297
if (fragment->get_frag_spec ().has_follow_set_restrictions ())
295298
return peculiar_fragment_match_compatible (*fragment, match);
296299
else
@@ -300,7 +303,7 @@ is_match_compatible (AST::MacroMatch &last_match, AST::MacroMatch &match)
300303
// A repetition on the left hand side means we want to make sure the
301304
// last match of the repetition is compatible with the new match
302305
auto repetition
303-
= static_cast<AST::MacroMatchRepetition *> (&last_match);
306+
= static_cast<const AST::MacroMatchRepetition *> (&last_match);
304307
new_last = get_back_ptr (repetition->get_matches ());
305308
// If there are no matches in the matcher, then it can be followed by
306309
// anything

gcc/rust/parse/rust-parse.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -714,8 +714,8 @@ extract_module_path (const AST::AttrVec &inner_attrs,
714714
* @return true if the follow-up is valid, false otherwise
715715
*/
716716
bool
717-
is_match_compatible (AST::MacroMatch &last_match,
718-
AST::MacroMatch &current_match);
717+
is_match_compatible (const AST::MacroMatch &last_match,
718+
const AST::MacroMatch &current_match);
719719
} // namespace Rust
720720

721721
// as now template, include implementations of all methods

gcc/testsuite/rust/compile/macro37.rs

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

gcc/testsuite/rust/compile/macro38.rs

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

0 commit comments

Comments
 (0)