Skip to content

Commit e48bce4

Browse files
Merge #1027 #1032
1027: parser: Allow parsing stmts without closing semicolon r=CohenArthur a=CohenArthur In certain cases such as macro matching or macro expansion, it is important to allow the parser to return a valid statement even if no closing semicolon is given. This commit adds an optional parameter to the concerned functions to allow a lack of semicolon those special cases Closes #1011 Closes #1010 1032: Add AST kind information r=CohenArthur a=CohenArthur Closes #1001 This PR adds a base for adding node information to our AST types. It can be used when requiring to differentiate between multiple kinds of nodes, while not necessarily wanting to do a full static cast. This will open up a lot of cleanup issues and good first issues for Project Pineapple Co-authored-by: Arthur Cohen <[email protected]>
3 parents 3ada3d8 + 313e989 + 1e87392 commit e48bce4

14 files changed

+167
-62
lines changed

gcc/rust/ast/rust-ast.h

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,28 @@ namespace AST {
3636
class ASTVisitor;
3737
using AttrVec = std::vector<Attribute>;
3838

39+
// The available kinds of AST Nodes
40+
enum Kind
41+
{
42+
UNKNOWN,
43+
MACRO_RULES_DEFINITION,
44+
MACRO_INVOCATION,
45+
};
46+
47+
// Abstract base class for all AST elements
48+
class Node
49+
{
50+
public:
51+
/**
52+
* Get the kind of Node this is. This is used to differentiate various AST
53+
* elements with very little overhead when extracting the derived type through
54+
* static casting is not necessary.
55+
*/
56+
// FIXME: Mark this as `= 0` in the future to make sure every node implements
57+
// it
58+
virtual Kind get_ast_kind () const { return Kind::UNKNOWN; }
59+
};
60+
3961
// Delimiter types - used in macros and whatever.
4062
enum DelimType
4163
{
@@ -814,7 +836,7 @@ class MetaListNameValueStr;
814836

815837
/* Base statement abstract class. Note that most "statements" are not allowed in
816838
* top-level module scope - only a subclass of statements called "items" are. */
817-
class Stmt
839+
class Stmt : public Node
818840
{
819841
public:
820842
// Unique pointer custom clone function
@@ -880,7 +902,7 @@ class Item : public Stmt
880902
class ExprWithoutBlock;
881903

882904
// Base expression AST node - abstract
883-
class Expr
905+
class Expr : public Node
884906
{
885907
public:
886908
// Unique pointer custom clone function
@@ -1053,7 +1075,7 @@ class Pattern
10531075
class TraitBound;
10541076

10551077
// Base class for types as represented in AST - abstract
1056-
class Type
1078+
class Type : public Node
10571079
{
10581080
public:
10591081
// Unique pointer custom clone function

gcc/rust/ast/rust-macro.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -441,6 +441,8 @@ class MacroRulesDefinition : public MacroItem
441441
is_builtin_rule = true;
442442
}
443443

444+
Kind get_ast_kind () const override { return Kind::MACRO_RULES_DEFINITION; }
445+
444446
protected:
445447
/* Use covariance to implement clone function as returning this object rather
446448
* than base */
@@ -505,6 +507,8 @@ class MacroInvocation : public TypeNoBounds,
505507
return ExprWithoutBlock::get_node_id ();
506508
}
507509

510+
Kind get_ast_kind () const override { return Kind::MACRO_INVOCATION; }
511+
508512
NodeId get_macro_node_id () const { return node_id; }
509513

510514
MacroInvocData &get_invoc_data () { return invoc_data; }

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

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -478,7 +478,7 @@ MacroExpander::match_fragment (Parser<MacroInvocLexer> &parser,
478478
break;
479479

480480
case AST::MacroFragSpec::STMT:
481-
parser.parse_stmt ();
481+
parser.parse_stmt (/* allow_no_semi */ true);
482482
break;
483483

484484
case AST::MacroFragSpec::LIFETIME:
@@ -505,6 +505,9 @@ MacroExpander::match_fragment (Parser<MacroInvocLexer> &parser,
505505
return false;
506506
}
507507

508+
for (const auto &error : parser.get_errors ())
509+
error.emit_error ();
510+
508511
// it matches if the parser did not produce errors trying to parse that type
509512
// of item
510513
return !parser.has_errors ();
@@ -824,7 +827,7 @@ transcribe_many_stmts (Parser<MacroInvocLexer> &parser, TokenId &delimiter)
824827
// transcriber is an expression, but since the macro call is followed by
825828
// a semicolon, it's a valid ExprStmt
826829
return parse_many (parser, delimiter, [&parser] () {
827-
auto stmt = parser.parse_stmt ();
830+
auto stmt = parser.parse_stmt (/* allow_no_semi */ true);
828831
return AST::SingleASTNode (std::move (stmt));
829832
});
830833
}

gcc/rust/hir/rust-ast-lower-expr.h

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -100,14 +100,6 @@ class ASTLoweringExpr : public ASTLoweringBase
100100
return resolver.translated;
101101
}
102102

103-
void visit (AST::MacroInvocation &expr) override
104-
{
105-
rust_fatal_error (
106-
expr.get_locus (),
107-
"macro expansion failed: No macro invocation should get lowered to HIR "
108-
"as they should disappear during expansion");
109-
}
110-
111103
void visit (AST::TupleIndexExpr &expr) override
112104
{
113105
HIR::Expr *tuple_expr

gcc/rust/hir/rust-ast-lower-implitem.h

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -52,14 +52,6 @@ class ASTLowerImplItem : public ASTLoweringBase
5252
return resolver.translated;
5353
}
5454

55-
void visit (AST::MacroInvocation &invoc) override
56-
{
57-
rust_fatal_error (
58-
invoc.get_locus (),
59-
"macro expansion failed: No macro invocation should get lowered to HIR "
60-
"as they should disappear during expansion");
61-
}
62-
6355
void visit (AST::TypeAlias &alias) override
6456
{
6557
std::vector<std::unique_ptr<HIR::WhereClauseItem> > where_clause_items;
@@ -316,14 +308,6 @@ class ASTLowerTraitItem : public ASTLoweringBase
316308
return resolver.translated;
317309
}
318310

319-
void visit (AST::MacroInvocation &invoc) override
320-
{
321-
rust_fatal_error (
322-
invoc.get_locus (),
323-
"macro expansion failed: No macro invocation should get lowered to HIR "
324-
"as they should disappear during expansion");
325-
}
326-
327311
void visit (AST::TraitItemFunc &func) override
328312
{
329313
AST::TraitFunctionDecl &ref = func.get_trait_function_decl ();

gcc/rust/hir/rust-ast-lower-item.h

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -51,14 +51,6 @@ class ASTLoweringItem : public ASTLoweringBase
5151
return resolver.translated;
5252
}
5353

54-
void visit (AST::MacroInvocation &invoc) override
55-
{
56-
rust_fatal_error (
57-
invoc.get_locus (),
58-
"macro expansion failed: No macro invocation should get lowered to HIR "
59-
"as they should disappear during expansion");
60-
}
61-
6254
void visit (AST::Module &module) override
6355
{
6456
auto crate_num = mappings->get_current_crate ();

gcc/rust/hir/rust-ast-lower-stmt.h

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -45,14 +45,6 @@ class ASTLoweringStmt : public ASTLoweringBase
4545
return resolver.translated;
4646
}
4747

48-
void visit (AST::MacroInvocation &invoc) override
49-
{
50-
rust_fatal_error (
51-
invoc.get_locus (),
52-
"macro expansion failed: No macro invocation should get lowered to HIR "
53-
"as they should disappear during expansion");
54-
}
55-
5648
void visit (AST::ExprStmtWithBlock &stmt) override
5749
{
5850
HIR::ExprWithBlock *expr

gcc/rust/hir/rust-ast-lower.cc

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,16 @@ ASTLoweringBlock::visit (AST::BlockExpr &expr)
6767

6868
for (auto &s : expr.get_statements ())
6969
{
70+
if (s->get_ast_kind () == AST::Kind::MACRO_RULES_DEFINITION)
71+
continue;
72+
73+
if (s->get_ast_kind () == AST::Kind::MACRO_INVOCATION)
74+
rust_fatal_error (
75+
s->get_locus (),
76+
"macro invocations should not get lowered to HIR - At "
77+
"this point in "
78+
"the pipeline, they should all have been expanded");
79+
7080
if (block_did_terminate)
7181
rust_warning_at (s->get_locus (), 0, "unreachable statement");
7282

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

Lines changed: 31 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6034,9 +6034,10 @@ Parser<ManagedTokenSource>::parse_named_function_param (
60346034
// Parses a statement (will further disambiguate any statement).
60356035
template <typename ManagedTokenSource>
60366036
std::unique_ptr<AST::Stmt>
6037-
Parser<ManagedTokenSource>::parse_stmt ()
6037+
Parser<ManagedTokenSource>::parse_stmt (bool allow_no_semi)
60386038
{
60396039
// quick exit for empty statement
6040+
// FIXME: Can we have empty statements without semicolons? Just nothing?
60406041
const_TokenPtr t = lexer.peek_token ();
60416042
if (t->get_id () == SEMICOLON)
60426043
{
@@ -6058,7 +6059,7 @@ Parser<ManagedTokenSource>::parse_stmt ()
60586059
{
60596060
case LET:
60606061
// let statement
6061-
return parse_let_stmt (std::move (outer_attrs));
6062+
return parse_let_stmt (std::move (outer_attrs), allow_no_semi);
60626063
case PUB:
60636064
case MOD:
60646065
case EXTERN_TOK:
@@ -6113,15 +6114,16 @@ Parser<ManagedTokenSource>::parse_stmt ()
61136114
// TODO: find out how to disable gcc "implicit fallthrough" warning
61146115
default:
61156116
// fallback: expression statement
6116-
return parse_expr_stmt (std::move (outer_attrs));
6117+
return parse_expr_stmt (std::move (outer_attrs), allow_no_semi);
61176118
break;
61186119
}
61196120
}
61206121

61216122
// Parses a let statement.
61226123
template <typename ManagedTokenSource>
61236124
std::unique_ptr<AST::LetStmt>
6124-
Parser<ManagedTokenSource>::parse_let_stmt (AST::AttrVec outer_attrs)
6125+
Parser<ManagedTokenSource>::parse_let_stmt (AST::AttrVec outer_attrs,
6126+
bool allow_no_semi)
61256127
{
61266128
Location locus = lexer.peek_token ()->get_locus ();
61276129
skip_token (LET);
@@ -6176,12 +6178,12 @@ Parser<ManagedTokenSource>::parse_let_stmt (AST::AttrVec outer_attrs)
61766178
}
61776179
}
61786180

6179-
if (!skip_token (SEMICOLON))
6181+
if (!maybe_skip_token (SEMICOLON) && !allow_no_semi)
61806182
{
61816183
// skip after somewhere
61826184
return nullptr;
6183-
/* TODO: how wise is it to ditch a mostly-valid let statement just because
6184-
* a semicolon is missing? */
6185+
/* TODO: how wise is it to ditch a mostly-valid let statement just
6186+
* because a semicolon is missing? */
61856187
}
61866188

61876189
return std::unique_ptr<AST::LetStmt> (
@@ -7016,7 +7018,8 @@ Parser<ManagedTokenSource>::parse_method ()
70167018
* block statement). */
70177019
template <typename ManagedTokenSource>
70187020
std::unique_ptr<AST::ExprStmt>
7019-
Parser<ManagedTokenSource>::parse_expr_stmt (AST::AttrVec outer_attrs)
7021+
Parser<ManagedTokenSource>::parse_expr_stmt (AST::AttrVec outer_attrs,
7022+
bool allow_no_semi)
70207023
{
70217024
/* potential thoughts - define new virtual method "has_block()" on expr. parse
70227025
* expr and then determine whether semicolon is needed as a result of this
@@ -7055,7 +7058,8 @@ Parser<ManagedTokenSource>::parse_expr_stmt (AST::AttrVec outer_attrs)
70557058
}
70567059
else
70577060
{
7058-
return parse_expr_stmt_without_block (std::move (outer_attrs));
7061+
return parse_expr_stmt_without_block (std::move (outer_attrs),
7062+
allow_no_semi);
70597063
}
70607064
}
70617065
case UNSAFE: {
@@ -7068,15 +7072,17 @@ Parser<ManagedTokenSource>::parse_expr_stmt (AST::AttrVec outer_attrs)
70687072
}
70697073
else
70707074
{
7071-
return parse_expr_stmt_without_block (std::move (outer_attrs));
7075+
return parse_expr_stmt_without_block (std::move (outer_attrs),
7076+
allow_no_semi);
70727077
}
70737078
}
70747079
default:
70757080
// not a parse expr with block, so must be expr without block
70767081
/* TODO: if possible, be more selective about possible expr without block
70777082
* initial tokens in order to prevent more syntactical errors at parse
70787083
* time. */
7079-
return parse_expr_stmt_without_block (std::move (outer_attrs));
7084+
return parse_expr_stmt_without_block (std::move (outer_attrs),
7085+
allow_no_semi);
70807086
}
70817087
}
70827088

@@ -7192,7 +7198,7 @@ Parser<ManagedTokenSource>::parse_expr_stmt_with_block (
71927198
template <typename ManagedTokenSource>
71937199
std::unique_ptr<AST::ExprStmtWithoutBlock>
71947200
Parser<ManagedTokenSource>::parse_expr_stmt_without_block (
7195-
AST::AttrVec outer_attrs)
7201+
AST::AttrVec outer_attrs, bool allow_no_semi)
71967202
{
71977203
/* TODO: maybe move more logic for expr without block in here for better error
71987204
* handling */
@@ -7217,7 +7223,7 @@ Parser<ManagedTokenSource>::parse_expr_stmt_without_block (
72177223
}
72187224

72197225
// skip semicolon at end that is required
7220-
if (!skip_token (SEMICOLON))
7226+
if (!maybe_skip_token (SEMICOLON) && !allow_no_semi)
72217227
{
72227228
// skip somewhere?
72237229
return nullptr;
@@ -12219,6 +12225,18 @@ Parser<ManagedTokenSource>::skip_token (TokenId token_id)
1221912225
return expect_token (token_id) != const_TokenPtr ();
1222012226
}
1222112227

12228+
/* Checks if current token has inputted id - skips it and returns true if so,
12229+
* returns false otherwise without diagnosing an error */
12230+
template <typename ManagedTokenSource>
12231+
bool
12232+
Parser<ManagedTokenSource>::maybe_skip_token (TokenId token_id)
12233+
{
12234+
if (lexer.peek_token ()->get_id () != token_id)
12235+
return false;
12236+
else
12237+
return skip_token (token_id);
12238+
}
12239+
1222212240
/* Checks the current token - if id is same as expected, skips and returns it,
1222312241
* otherwise diagnoses error and returns null. */
1222412242
template <typename ManagedTokenSource>

0 commit comments

Comments
 (0)