gccrs: Implement declarative macro 2.0 parser

gcc/rust/ChangeLog:

	* ast/rust-ast-full-decls.h (class MacroItem): Remove forward declaration.
	* ast/rust-ast-full-test.cc (MacroRulesDefinition):
	Rework MacroRulesDefinition class
	* ast/rust-ast.h (class MacroItem): Remove abstract class.
	* ast/rust-item.h (class MacroItem): Remove forward declaration.
	* ast/rust-macro.h (class MacroItem): Likewise.
	(class MacroRulesDefinition): Add MacroKind enum.
	(class MacroInvocation): Fix inheritance.
	* lex/rust-token.h: Token "macro" is now used.
	* parse/rust-parse-impl.h (Parser::parse_item): Add handling for MACRO.
	(Parser::parse_vis_item): Call into parse_decl_macro_def.
	(Parser::parse_macro_item): Delete function.
	(Parser::parse_macro_rules_def): Return MBE macros only.
	(Parser::parse_decl_macro_def): New function.
	(Parser::parse_stmt): Handle MACRO token.
	(Parser::parse_stmt_or_expr_without_block): Call into parse_macro_rules_def.
	* parse/rust-parse.h: Declare new function.

gcc/testsuite/ChangeLog:

	* rust/compile/decl_macro1.rs: New test.
	* rust/compile/decl_macro2.rs: New test.
	* rust/compile/decl_macro3.rs: New test.
	* rust/compile/decl_macro4.rs: New test.
	* rust/compile/decl_macro5.rs: New test.
	* rust/compile/decl_macro6.rs: New test.
	* rust/compile/decl_macro7.rs: New test.
	* rust/execute/torture/decl_macro1.rs: New test.
	* rust/execute/torture/decl_macro2.rs: New test.
	* rust/execute/torture/decl_macro3.rs: New test.
	* rust/execute/torture/decl_macro4.rs: New test.

Signed-off-by: Raiki Tamura <tamaron1203@gmail.com>
This commit is contained in:
Raiki Tamura
2022-12-19 01:48:54 +09:00
committed by Arthur Cohen
parent ef792b9658
commit e1394230b3
19 changed files with 301 additions and 80 deletions

View File

@@ -51,7 +51,6 @@ class Lifetime;
class GenericParam;
class LifetimeParam;
class ConstGenericParam;
class MacroItem;
class TraitItem;
class InherentImplItem;
class TraitImplItem;

View File

@@ -1284,6 +1284,7 @@ MacroRulesDefinition::as_string () const
// get outer attrs
str += append_attributes (outer_attrs, OUTER);
// TODO: deal with macro_2_0
str += "macro_rules!";
str += rule_name;

View File

@@ -1350,11 +1350,6 @@ protected:
}
};
// A macro item AST node - abstract base class
class MacroItem : public Item
{
};
// Item used in trait declarations - abstract base class
class TraitItem
{

View File

@@ -4391,8 +4391,6 @@ protected:
}
};
// Replaced with forward decls - defined in "rust-macro.h"
class MacroItem;
class MacroRulesDefinition;
} // namespace AST
} // namespace Rust

View File

@@ -23,13 +23,11 @@
#include "rust-ast.h"
#include "rust-ast-fragment.h"
#include "rust-location.h"
#include "rust-item.h"
#include "rust-make-unique.h"
namespace Rust {
namespace AST {
// Decls as definitions moved to rust-ast.h
class MacroItem;
class MacroFragSpec
{
public:
@@ -446,8 +444,18 @@ public:
};
// A macro rules definition item AST node
class MacroRulesDefinition : public MacroItem
class MacroRulesDefinition : public VisItem
{
public:
enum MacroKind
{
// Macro by Example (legacy macro rules)
MBE,
// Declarative macros 2.0
DeclMacro,
};
private:
std::vector<Attribute> outer_attrs;
Identifier rule_name;
// MacroRulesDef rules_def;
@@ -460,6 +468,7 @@ class MacroRulesDefinition : public MacroItem
std::function<Fragment (Location, MacroInvocData &)> associated_transcriber;
// Since we can't compare std::functions, we need to use an extra boolean
bool is_builtin_rule;
MacroKind kind;
/**
* Default function to use as an associated transcriber. This function should
@@ -479,27 +488,51 @@ class MacroRulesDefinition : public MacroItem
* I am not aware of the implications of this decision. The rustc spec does
* mention that using the same parser for macro definitions and invocations
* is "extremely self-referential and non-intuitive". */
public:
std::string as_string () const override;
MacroRulesDefinition (Identifier rule_name, DelimType delim_type,
std::vector<MacroRule> rules,
std::vector<Attribute> outer_attrs, Location locus)
: outer_attrs (std::move (outer_attrs)), rule_name (std::move (rule_name)),
std::vector<Attribute> outer_attrs, Location locus,
MacroKind kind, Visibility vis)
: VisItem (std::move (vis), outer_attrs),
outer_attrs (std::move (outer_attrs)), rule_name (std::move (rule_name)),
delim_type (delim_type), rules (std::move (rules)), locus (locus),
associated_transcriber (dummy_builtin), is_builtin_rule (false)
associated_transcriber (dummy_builtin), is_builtin_rule (false),
kind (kind)
{}
MacroRulesDefinition (
Identifier builtin_name, DelimType delim_type,
std::function<Fragment (Location, MacroInvocData &)> associated_transcriber)
: outer_attrs (std::vector<Attribute> ()), rule_name (builtin_name),
std::function<Fragment (Location, MacroInvocData &)> associated_transcriber,
MacroKind kind, Visibility vis)
: VisItem (std::move (vis), std::vector<Attribute> ()),
outer_attrs (std::vector<Attribute> ()), rule_name (builtin_name),
delim_type (delim_type), rules (std::vector<MacroRule> ()),
locus (Location ()), associated_transcriber (associated_transcriber),
is_builtin_rule (true)
is_builtin_rule (true), kind (kind)
{}
public:
std::string as_string () const override;
static std::unique_ptr<MacroRulesDefinition>
mbe (Identifier rule_name, DelimType delim_type, std::vector<MacroRule> rules,
std::vector<Attribute> outer_attrs, Location locus)
{
return Rust::make_unique<MacroRulesDefinition> (
MacroRulesDefinition (rule_name, delim_type, rules, outer_attrs, locus,
AST::MacroRulesDefinition::MacroKind::MBE,
AST::Visibility::create_error ()));
}
static std::unique_ptr<MacroRulesDefinition>
decl_macro (Identifier rule_name, std::vector<MacroRule> rules,
std::vector<Attribute> outer_attrs, Location locus,
Visibility vis)
{
return Rust::make_unique<MacroRulesDefinition> (MacroRulesDefinition (
rule_name, AST::DelimType::CURLY, rules, outer_attrs, locus,
AST::MacroRulesDefinition::MacroKind::DeclMacro, vis));
}
void accept_vis (ASTVisitor &vis) override;
// Invalid if rule name is empty, so base stripping on that.
@@ -549,7 +582,7 @@ protected:
* compile time */
class MacroInvocation : public TypeNoBounds,
public Pattern,
public MacroItem,
public Item,
public TraitItem,
public TraitImplItem,
public InherentImplItem,

View File

@@ -171,7 +171,7 @@ enum PrimitiveCoreType
RS_TOKEN_KEYWORD (IN, "in") \
RS_TOKEN_KEYWORD (LET, "let") \
RS_TOKEN_KEYWORD (LOOP, "loop") \
RS_TOKEN_KEYWORD (MACRO, "macro") /* unused */ \
RS_TOKEN_KEYWORD (MACRO, "macro") \
RS_TOKEN_KEYWORD (MATCH_TOK, "match") \
RS_TOKEN_KEYWORD (MOD, "mod") \
RS_TOKEN_KEYWORD (MOVE, "move") \

View File

@@ -1032,11 +1032,6 @@ Parser<ManagedTokenSource>::parse_item (bool called_from_statement)
// parse outer attributes for item
AST::AttrVec outer_attrs = parse_outer_attributes ();
// TODO: decide how to deal with VisItem vs MacroItem dichotomy
/* best current solution: catch all keywords that would imply a VisItem in a
* switch and have MacroItem as a last resort */
const_TokenPtr t = lexer.peek_token ();
switch (t->get_id ())
@@ -1064,6 +1059,7 @@ Parser<ManagedTokenSource>::parse_item (bool called_from_statement)
case STATIC_TOK:
case TRAIT:
case IMPL:
case MACRO:
/* TODO: implement union keyword but not really because of
* context-dependence crappy hack way to parse a union written below to
* separate it from the good code. */
@@ -1078,7 +1074,7 @@ Parser<ManagedTokenSource>::parse_item (bool called_from_statement)
case CRATE:
case DOLLAR_SIGN:
// almost certainly macro invocation semi
return parse_macro_item (std::move (outer_attrs));
return parse_macro_invocation_semi (std::move (outer_attrs));
break;
// crappy hack to do union "keyword"
case IDENTIFIER:
@@ -1092,19 +1088,18 @@ Parser<ManagedTokenSource>::parse_item (bool called_from_statement)
else if (t->get_str () == "macro_rules")
{
// macro_rules! macro item
return parse_macro_item (std::move (outer_attrs));
return parse_macro_rules_def (std::move (outer_attrs));
}
else if (lexer.peek_token (1)->get_id () == SCOPE_RESOLUTION
|| lexer.peek_token (1)->get_id () == EXCLAM)
{
/* path (probably) or macro invocation, so probably a macro invocation
* semi */
return parse_macro_item (std::move (outer_attrs));
return parse_macro_invocation_semi (std::move (outer_attrs));
}
gcc_fallthrough ();
default:
// otherwise unrecognised
// return parse_macro_item(std::move(outer_attrs));
add_error (Error (t->get_locus (),
"unrecognised token %qs for start of %s",
t->get_token_description (),
@@ -1335,6 +1330,8 @@ Parser<ManagedTokenSource>::parse_vis_item (AST::AttrVec outer_attrs)
lexer.skip_token (1); // TODO: is this right thing to do?
return nullptr;
}
case MACRO:
return parse_decl_macro_def (std::move (vis), std::move (outer_attrs));
default:
// otherwise vis item clearly doesn't exist, which is not an error
// has a catch-all post-switch return to allow other breaks to occur
@@ -1343,42 +1340,6 @@ Parser<ManagedTokenSource>::parse_vis_item (AST::AttrVec outer_attrs)
return nullptr;
}
// Parses a MacroItem (either a MacroInvocationSemi or MacroRulesDefinition).
template <typename ManagedTokenSource>
std::unique_ptr<AST::MacroItem>
Parser<ManagedTokenSource>::parse_macro_item (AST::AttrVec outer_attrs)
{
const_TokenPtr t = lexer.peek_token ();
/* dodgy way of detecting macro due to weird context-dependence thing.
* probably can be improved */
// TODO: ensure that string compare works properly
if (t->get_id () == IDENTIFIER && t->get_str () == "macro_rules")
{
return parse_macro_rules_def (std::move (outer_attrs));
}
else
{
// DEBUG: TODO: remove
rust_debug (
"DEBUG - parse_macro_item called and token is not macro_rules");
if (t->get_id () == IDENTIFIER)
{
rust_debug ("just add to last error: token is not macro_rules and is "
"instead '%s'",
t->get_str ().c_str ());
}
else
{
rust_debug ("just add to last error: token is not macro_rules and is "
"not an identifier either - it is '%s'",
t->get_token_description ());
}
return parse_macro_invocation_semi (std::move (outer_attrs));
}
}
// Parses a macro rules definition syntax extension whatever thing.
template <typename ManagedTokenSource>
std::unique_ptr<AST::MacroRulesDefinition>
@@ -1512,16 +1473,16 @@ Parser<ManagedTokenSource>::parse_macro_rules_def (AST::AttrVec outer_attrs)
{
// as this is the end, allow recovery (probably) - may change
return std::unique_ptr<AST::MacroRulesDefinition> (
new AST::MacroRulesDefinition (
AST::MacroRulesDefinition::mbe (
std::move (rule_name), delim_type, std::move (macro_rules),
std::move (outer_attrs), macro_locus));
}
}
return std::unique_ptr<AST::MacroRulesDefinition> (
new AST::MacroRulesDefinition (std::move (rule_name), delim_type,
std::move (macro_rules),
std::move (outer_attrs), macro_locus));
AST::MacroRulesDefinition::mbe (std::move (rule_name), delim_type,
std::move (macro_rules),
std::move (outer_attrs), macro_locus));
}
else
{
@@ -1541,6 +1502,165 @@ Parser<ManagedTokenSource>::parse_macro_rules_def (AST::AttrVec outer_attrs)
}
}
// Parses a declarative macro 2.0 definition.
template <typename ManagedTokenSource>
std::unique_ptr<AST::MacroRulesDefinition>
Parser<ManagedTokenSource>::parse_decl_macro_def (AST::Visibility vis,
AST::AttrVec outer_attrs)
{
// ensure that first token is identifier saying "macro"
const_TokenPtr t = lexer.peek_token ();
if (t->get_id () != MACRO)
{
Error error (
t->get_locus (),
"declarative macro definition does not start with %<macro%>");
add_error (std::move (error));
// skip after somewhere?
return nullptr;
}
lexer.skip_token ();
Location macro_locus = t->get_locus ();
// parse macro name
const_TokenPtr ident_tok = expect_token (IDENTIFIER);
if (ident_tok == nullptr)
{
return nullptr;
}
Identifier rule_name = ident_tok->get_str ();
t = lexer.peek_token ();
if (t->get_id () == LEFT_PAREN)
{
// single definiton of macro rule
// e.g. `macro foo($e:expr) {}`
// parse macro matcher
Location locus = lexer.peek_token ()->get_locus ();
AST::MacroMatcher matcher = parse_macro_matcher ();
if (matcher.is_error ())
return nullptr;
// check delimiter of macro matcher
if (matcher.get_delim_type () != AST::DelimType::PARENS)
{
Error error (locus, "only parenthesis can be used for a macro "
"matcher in declarative macro definition");
add_error (std::move (error));
return nullptr;
}
Location transcriber_loc = lexer.peek_token ()->get_locus ();
AST::DelimTokenTree delim_tok_tree = parse_delim_token_tree ();
AST::MacroTranscriber transcriber (delim_tok_tree, transcriber_loc);
if (transcriber.get_token_tree ().get_delim_type ()
!= AST::DelimType::CURLY)
{
Error error (transcriber_loc,
"only braces can be used for a macro transcriber "
"in declarative macro definition");
add_error (std::move (error));
return nullptr;
}
AST::MacroRule macro_rule
= AST::MacroRule (std::move (matcher), std::move (transcriber), locus);
std::vector<AST::MacroRule> macro_rules;
macro_rules.push_back (macro_rule);
return std::unique_ptr<AST::MacroRulesDefinition> (
AST::MacroRulesDefinition::decl_macro (std::move (rule_name),
macro_rules,
std::move (outer_attrs),
macro_locus, vis));
}
else if (t->get_id () == LEFT_CURLY)
{
// multiple definitions of macro rule separated by comma
// e.g. `macro foo { () => {}, ($e:expr) => {}, }`
// parse left curly
const_TokenPtr left_curly = expect_token (LEFT_CURLY);
if (left_curly == nullptr)
{
return nullptr;
}
// parse actual macro rules
std::vector<AST::MacroRule> macro_rules;
// must be at least one macro rule, so parse it
AST::MacroRule initial_rule = parse_macro_rule ();
if (initial_rule.is_error ())
{
Error error (
lexer.peek_token ()->get_locus (),
"required first macro rule in declarative macro definition "
"could not be parsed");
add_error (std::move (error));
// skip after somewhere?
return nullptr;
}
macro_rules.push_back (std::move (initial_rule));
t = lexer.peek_token ();
// parse macro rules
while (t->get_id () == COMMA)
{
// skip comma
lexer.skip_token ();
// don't parse if end of macro rules
if (token_id_matches_delims (lexer.peek_token ()->get_id (),
AST::CURLY))
{
break;
}
// try to parse next rule
AST::MacroRule rule = parse_macro_rule ();
if (rule.is_error ())
{
Error error (
lexer.peek_token ()->get_locus (),
"failed to parse macro rule in declarative macro definition");
add_error (std::move (error));
return nullptr;
}
macro_rules.push_back (std::move (rule));
t = lexer.peek_token ();
}
// parse right curly
const_TokenPtr right_curly = expect_token (RIGHT_CURLY);
if (right_curly == nullptr)
{
return nullptr;
}
return std::unique_ptr<AST::MacroRulesDefinition> (
AST::MacroRulesDefinition::decl_macro (std::move (rule_name),
std::move (macro_rules),
std::move (outer_attrs),
macro_locus, vis));
}
else
{
add_error (Error (t->get_locus (),
"unexpected token %qs - expecting delimiters "
"(for a declarative macro definiton)",
t->get_token_description ()));
return nullptr;
}
}
// Parses a semi-coloned (except for full block) macro invocation item.
template <typename ManagedTokenSource>
std::unique_ptr<AST::MacroInvocation>
@@ -6004,6 +6124,7 @@ Parser<ManagedTokenSource>::parse_stmt (ParseRestrictions restrictions)
case STATIC_TOK:
case TRAIT:
case IMPL:
case MACRO:
/* TODO: implement union keyword but not really because of
* context-dependence crappy hack way to parse a union written below to
* separate it from the good code. */
@@ -6019,7 +6140,7 @@ Parser<ManagedTokenSource>::parse_stmt (ParseRestrictions restrictions)
case CRATE:
case DOLLAR_SIGN:
// almost certainly macro invocation semi
return parse_macro_item (std::move (outer_attrs));
return parse_macro_invocation_semi (std::move (outer_attrs));
break;
// crappy hack to do union "keyword"
case IDENTIFIER:
@@ -6032,7 +6153,7 @@ Parser<ManagedTokenSource>::parse_stmt (ParseRestrictions restrictions)
else if (t->get_str () == "macro_rules")
{
// macro_rules! macro item
return parse_macro_item (std::move (outer_attrs));
return parse_macro_rules_def (std::move (outer_attrs));
}
else if (lexer.peek_token (1)->get_id () == SCOPE_RESOLUTION
|| lexer.peek_token (1)->get_id () == EXCLAM)
@@ -6040,7 +6161,7 @@ Parser<ManagedTokenSource>::parse_stmt (ParseRestrictions restrictions)
// FIXME: ensure doesn't take any expressions by mistake
/* path (probably) or macro invocation, so probably a macro
* invocation semi */
return parse_macro_item (std::move (outer_attrs));
return parse_macro_invocation_semi (std::move (outer_attrs));
}
gcc_fallthrough ();
// TODO: find out how to disable gcc "implicit fallthrough" warning
@@ -11711,8 +11832,8 @@ Parser<ManagedTokenSource>::parse_stmt_or_expr_without_block ()
else if (t->get_str () == "macro_rules")
{
// macro_rules! macro item
std::unique_ptr<AST::MacroItem> item (
parse_macro_item (std::move (outer_attrs)));
std::unique_ptr<AST::Item> item (
parse_macro_rules_def (std::move (outer_attrs)));
return ExprOrStmt (std::move (item));
}
else if (lexer.peek_token (1)->get_id () == SCOPE_RESOLUTION

View File

@@ -197,6 +197,8 @@ private:
AST::DelimTokenTree parse_delim_token_tree ();
std::unique_ptr<AST::MacroRulesDefinition>
parse_macro_rules_def (AST::AttrVec outer_attrs);
std::unique_ptr<AST::MacroRulesDefinition>
parse_decl_macro_def (AST::Visibility vis, AST::AttrVec outer_attrs);
std::unique_ptr<AST::MacroInvocation>
parse_macro_invocation_semi (AST::AttrVec outer_attrs);
std::unique_ptr<AST::MacroInvocation>
@@ -209,7 +211,6 @@ private:
// Top-level item-related
std::unique_ptr<AST::VisItem> parse_vis_item (AST::AttrVec outer_attrs);
std::unique_ptr<AST::MacroItem> parse_macro_item (AST::AttrVec outer_attrs);
// VisItem subclass-related
std::unique_ptr<AST::Module> parse_module (AST::Visibility vis,

View File

@@ -0,0 +1,2 @@
#![feature(decl_macro)]
macro m() {}

View File

@@ -0,0 +1,2 @@
#![feature(decl_macro)]
pub macro m($e: expr) { $e + $e }

View File

@@ -0,0 +1,4 @@
#![feature(decl_macro)]
macro foo {
() => {}
}

View File

@@ -0,0 +1,5 @@
#![feature(decl_macro)]
macro foo {
() => { 0 },
($n: expr) => { $n + 1 },
}

View File

@@ -0,0 +1,5 @@
#![feature(decl_macro)]
macro foo {
() => [],
($h: expr, $(t: expr),*) => ($h),
}

View File

@@ -0,0 +1,5 @@
#![feature(decl_macro)]
macro m {}
// { dg-error "unexpected token .\}. - expecting delimiters .for a macro matcher." "" { target *-*-* } .-1 }
// { dg-error "required first macro rule in declarative macro definition could not be parsed" "" { target *-*-* } .-2 }
// { dg-error "failed to parse item in crate" "" { target *-*-* } .-3 }

View File

@@ -0,0 +1,4 @@
#![feature(decl_macro)]
pub macro hello() [ "Hello" ]
// { dg-error "only braces can be used for a macro transcriber in declarative macro definition" "" { target *-*-* } .-1 }
// { dg-error "failed to parse item in crate" }

View File

@@ -0,0 +1,8 @@
#![feature(decl_macro)]
macro one() {
1
}
fn main() -> i32 {
one!() - 1
}

View File

@@ -0,0 +1,8 @@
#![feature(decl_macro)]
macro one {
() => { 1 }
}
fn main() -> i32 {
one!() - 1
}

View File

@@ -0,0 +1,15 @@
#![feature(decl_macro)]
macro add {
($e:expr) => {
$e
},
($h:expr, $($t:expr),*) => {
$h + add!($($t),*)
},
}
fn main() -> i32 {
let a = add!(1, 2, 3);
a - 6
}

View File

@@ -0,0 +1,15 @@
#![feature(decl_macro)]
pub macro add {
($e:expr) => {
$e
},
($h:expr, $($t:expr),*) => {
$h + add!($($t),*)
},
}
fn main() -> i32 {
let a = add!(1, 2, 3);
a - 6
}