Skip to content

Commit 35ca685

Browse files
committed
macros: Add base functions to check for follow-set ambiguities
Rust does not allow for all macro fragments to be followed by any kind of tokens: We must check tokens following those fragments that might contain restrictions and make sure that they are allowed, conforming to the Macro Follow-Set Ambiguity specification Co-authored-by: philberty <philip.herron@embecosm.com> macro-frag-spec: Transform enum into a class This allows us to add methods on the fragment specifier, which are needed to make sure that follow-set ambiguities are respected tests: Add tests for forbidden follow-up tokens This also fix a test that was previously accepted but invalid: rustc also rejected it
1 parent cc6e405 commit 35ca685

File tree

12 files changed

+336
-100
lines changed

12 files changed

+336
-100
lines changed

gcc/rust/ast/rust-ast-full-test.cc

Lines changed: 1 addition & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -76,45 +76,6 @@ get_string_in_delims (std::string str_input, DelimType delim_type)
7676
gcc_unreachable ();
7777
}
7878

79-
// Converts a frag spec enum item to a string form.
80-
std::string
81-
frag_spec_to_str (MacroFragSpec frag_spec)
82-
{
83-
switch (frag_spec)
84-
{
85-
case BLOCK:
86-
return "block";
87-
case EXPR:
88-
return "expr";
89-
case IDENT:
90-
return "ident";
91-
case ITEM:
92-
return "item";
93-
case LIFETIME:
94-
return "lifetime";
95-
case LITERAL:
96-
return "literal";
97-
case META:
98-
return "meta";
99-
case PAT:
100-
return "pat";
101-
case PATH:
102-
return "path";
103-
case STMT:
104-
return "stmt";
105-
case TT:
106-
return "tt";
107-
case TY:
108-
return "ty";
109-
case VIS:
110-
return "vis";
111-
case INVALID:
112-
return "INVALID_FRAG_SPEC";
113-
default:
114-
return "ERROR_MARK_STRING - unknown frag spec";
115-
}
116-
}
117-
11879
enum AttrMode
11980
{
12081
OUTER,
@@ -2396,7 +2357,7 @@ LifetimeParam::as_string () const
23962357
std::string
23972358
MacroMatchFragment::as_string () const
23982359
{
2399-
return "$" + ident + ": " + frag_spec_to_str (frag_spec);
2360+
return "$" + ident + ": " + frag_spec.as_string ();
24002361
}
24012362

24022363
std::string

gcc/rust/ast/rust-macro.h

Lines changed: 127 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -28,60 +28,128 @@ namespace AST {
2828
// Decls as definitions moved to rust-ast.h
2929
class MacroItem;
3030

31-
enum MacroFragSpec
31+
class MacroFragSpec
3232
{
33-
BLOCK,
34-
EXPR,
35-
IDENT,
36-
ITEM,
37-
LIFETIME,
38-
LITERAL,
39-
META,
40-
PAT,
41-
PATH,
42-
STMT,
43-
TT,
44-
TY,
45-
VIS,
46-
INVALID // not really a specifier, but used to mark invalid one passed in
47-
};
33+
public:
34+
enum Kind
35+
{
36+
BLOCK,
37+
EXPR,
38+
IDENT,
39+
ITEM,
40+
LIFETIME,
41+
LITERAL,
42+
META,
43+
PAT,
44+
PATH,
45+
STMT,
46+
TT,
47+
TY,
48+
VIS,
49+
INVALID // not really a specifier, but used to mark invalid one passed in
50+
};
4851

49-
inline MacroFragSpec
50-
get_frag_spec_from_str (std::string str)
51-
{
52-
if (str == "block")
53-
return BLOCK;
54-
else if (str == "expr")
55-
return EXPR;
56-
else if (str == "ident")
57-
return IDENT;
58-
else if (str == "item")
59-
return ITEM;
60-
else if (str == "lifetime")
61-
return LIFETIME;
62-
else if (str == "literal")
63-
return LITERAL;
64-
else if (str == "meta")
65-
return META;
66-
else if (str == "pat")
67-
return PAT;
68-
else if (str == "path")
69-
return PATH;
70-
else if (str == "stmt")
71-
return STMT;
72-
else if (str == "tt")
73-
return TT;
74-
else if (str == "ty")
75-
return TY;
76-
else if (str == "vis")
77-
return VIS;
78-
else
79-
{
80-
// error_at("invalid string '%s' used as fragment specifier",
81-
// str->c_str());
82-
return INVALID;
83-
}
84-
}
52+
MacroFragSpec (Kind kind) : kind (kind) {}
53+
54+
static MacroFragSpec get_frag_spec_from_str (const std::string &str)
55+
{
56+
if (str == "block")
57+
return MacroFragSpec (BLOCK);
58+
else if (str == "expr")
59+
return MacroFragSpec (EXPR);
60+
else if (str == "ident")
61+
return MacroFragSpec (IDENT);
62+
else if (str == "item")
63+
return MacroFragSpec (ITEM);
64+
else if (str == "lifetime")
65+
return MacroFragSpec (LIFETIME);
66+
else if (str == "literal")
67+
return MacroFragSpec (LITERAL);
68+
else if (str == "meta")
69+
return MacroFragSpec (META);
70+
else if (str == "pat" || str == "pat_param")
71+
return MacroFragSpec (PAT);
72+
else if (str == "path")
73+
return MacroFragSpec (PATH);
74+
else if (str == "stmt")
75+
return MacroFragSpec (STMT);
76+
else if (str == "tt")
77+
return MacroFragSpec (TT);
78+
else if (str == "ty")
79+
return MacroFragSpec (TY);
80+
else if (str == "vis")
81+
return MacroFragSpec (VIS);
82+
else
83+
{
84+
// error_at("invalid string '%s' used as fragment specifier",
85+
// str->c_str()));
86+
return MacroFragSpec (INVALID);
87+
}
88+
}
89+
90+
Kind get_kind () const { return kind; }
91+
bool is_error () const { return kind == Kind::INVALID; }
92+
93+
// Converts a frag spec enum item to a string form.
94+
std::string as_string () const
95+
{
96+
switch (kind)
97+
{
98+
case BLOCK:
99+
return "block";
100+
case EXPR:
101+
return "expr";
102+
case IDENT:
103+
return "ident";
104+
case ITEM:
105+
return "item";
106+
case LIFETIME:
107+
return "lifetime";
108+
case LITERAL:
109+
return "literal";
110+
case META:
111+
return "meta";
112+
case PAT:
113+
return "pat";
114+
case PATH:
115+
return "path";
116+
case STMT:
117+
return "stmt";
118+
case TT:
119+
return "tt";
120+
case TY:
121+
return "ty";
122+
case VIS:
123+
return "vis";
124+
case INVALID:
125+
return "INVALID_FRAG_SPEC";
126+
default:
127+
return "ERROR_MARK_STRING - unknown frag spec";
128+
}
129+
}
130+
131+
bool has_follow_set_restrictions ()
132+
{
133+
switch (kind)
134+
{
135+
case EXPR:
136+
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:
144+
return true;
145+
default:
146+
return false;
147+
}
148+
}
149+
150+
private:
151+
Kind kind;
152+
};
85153

86154
// A macro match that has an identifier and fragment spec
87155
class MacroMatchFragment : public MacroMatch
@@ -96,12 +164,17 @@ class MacroMatchFragment : public MacroMatch
96164
{}
97165

98166
// Returns whether macro match fragment is in an error state.
99-
bool is_error () const { return frag_spec == INVALID; }
167+
bool is_error () const
168+
{
169+
return frag_spec.get_kind () == MacroFragSpec::INVALID;
170+
}
100171

101172
// Creates an error state macro match fragment.
102173
static MacroMatchFragment create_error (Location locus)
103174
{
104-
return MacroMatchFragment (std::string (""), INVALID, locus);
175+
return MacroMatchFragment (std::string (""),
176+
MacroFragSpec (MacroFragSpec::Kind::INVALID),
177+
locus);
105178
}
106179

107180
std::string as_string () const override;

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -439,7 +439,7 @@ bool
439439
MacroExpander::match_fragment (Parser<MacroInvocLexer> &parser,
440440
AST::MacroMatchFragment &fragment)
441441
{
442-
switch (fragment.get_frag_spec ())
442+
switch (fragment.get_frag_spec ().get_kind ())
443443
{
444444
case AST::MacroFragSpec::EXPR:
445445
parser.parse_expr ();

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

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ along with GCC; see the file COPYING3. If not see
2222

2323
#include "rust-diagnostics.h"
2424
#include "util/rust-make-unique.h"
25+
#include <algorithm>
2526

2627
namespace Rust {
2728
// Left binding powers of operations.
@@ -1767,6 +1768,13 @@ Parser<ManagedTokenSource>::parse_macro_matcher ()
17671768
return AST::MacroMatcher::create_error (t->get_locus ());
17681769
}
17691770

1771+
if (matches.size () > 0)
1772+
{
1773+
auto &last_match = matches.back ();
1774+
if (!is_match_compatible (*last_match, *match))
1775+
return AST::MacroMatcher::create_error (match->get_match_locus ());
1776+
}
1777+
17701778
matches.push_back (std::move (match));
17711779

17721780
// DEBUG
@@ -1955,8 +1963,9 @@ Parser<ManagedTokenSource>::parse_macro_match_fragment ()
19551963
if (t == nullptr)
19561964
return nullptr;
19571965

1958-
AST::MacroFragSpec frag = AST::get_frag_spec_from_str (t->get_str ());
1959-
if (frag == AST::INVALID)
1966+
AST::MacroFragSpec frag
1967+
= AST::MacroFragSpec::get_frag_spec_from_str (t->get_str ());
1968+
if (frag.is_error ())
19601969
{
19611970
Error error (t->get_locus (),
19621971
"invalid fragment specifier %qs in fragment macro match",

0 commit comments

Comments
 (0)