diff options
Diffstat (limited to 'gcc')
31 files changed, 1383 insertions, 269 deletions
diff --git a/gcc/rust/ast/rust-ast-full-test.cc b/gcc/rust/ast/rust-ast-full-test.cc index 68f6f8c..3a1e295 100644 --- a/gcc/rust/ast/rust-ast-full-test.cc +++ b/gcc/rust/ast/rust-ast-full-test.cc @@ -4387,13 +4387,6 @@ std::vector<std::unique_ptr<Token> > DelimTokenTree::to_token_stream () const { std::vector<std::unique_ptr<Token> > tokens; - - // simulate presence of delimiters - const_TokenPtr left_paren - = Rust::Token::make (LEFT_PAREN, Linemap::unknown_location ()); - tokens.push_back ( - std::unique_ptr<Token> (new Token (std::move (left_paren)))); - for (const auto &tree : token_trees) { std::vector<std::unique_ptr<Token> > stream = tree->to_token_stream (); @@ -4402,13 +4395,7 @@ DelimTokenTree::to_token_stream () const std::make_move_iterator (stream.end ())); } - const_TokenPtr right_paren - = Rust::Token::make (RIGHT_PAREN, Linemap::unknown_location ()); - tokens.push_back ( - std::unique_ptr<Token> (new Token (std::move (right_paren)))); - tokens.shrink_to_fit (); - return tokens; } diff --git a/gcc/rust/ast/rust-ast.h b/gcc/rust/ast/rust-ast.h index ba973f1..e72937e 100644 --- a/gcc/rust/ast/rust-ast.h +++ b/gcc/rust/ast/rust-ast.h @@ -22,9 +22,6 @@ #include "rust-system.h" #include "rust-hir-map.h" - -// gccrs imports -// required for AST::Token #include "rust-token.h" #include "rust-location.h" @@ -32,7 +29,6 @@ namespace Rust { // TODO: remove typedefs and make actual types for these typedef std::string Identifier; typedef int TupleIndex; - struct Session; namespace AST { @@ -48,34 +44,6 @@ enum DelimType CURLY }; -// Base AST node object - TODO is this really required or useful? Where to draw -// line? -/*class Node { - public: - // Gets node's Location. - Location get_locus() const { - return loc; - } - - // Sets node's Location. - void set_locus(Location loc_) { - loc = loc_; - } - - // Get node output as a string. Pure virtual. - virtual std::string as_string() const = 0; - - virtual ~Node() {} - - // TODO: constructor including Location? Make all derived classes have -Location? - - private: - // The node's location. - Location loc; -};*/ -// decided to not have node as a "node" would never need to be stored - // forward decl for use in token tree method class Token; @@ -108,6 +76,14 @@ protected: class MacroMatch { public: + enum MacroMatchType + { + Fragment, + Repetition, + Matcher, + Tok + }; + virtual ~MacroMatch () {} virtual std::string as_string () const = 0; @@ -121,6 +97,8 @@ public: virtual void accept_vis (ASTVisitor &vis) = 0; + virtual MacroMatchType get_macro_match_type () const = 0; + protected: // pure virtual clone implementation virtual MacroMatch *clone_macro_match_impl () const = 0; @@ -234,6 +212,11 @@ public: // Get a new token pointer copy. const_TokenPtr get_tok_ptr () const { return tok_ref; } + MacroMatchType get_macro_match_type () const override + { + return MacroMatchType::Tok; + } + protected: // No virtual for now as not polymorphic but can be in future /*virtual*/ Token *clone_token_impl () const { return new Token (*this); } @@ -788,6 +771,13 @@ public: { return AttrInput::AttrInputType::TOKEN_TREE; } + + std::vector<std::unique_ptr<TokenTree> > &get_token_trees () + { + return token_trees; + } + + DelimType get_delim_type () const { return delim_type; } }; /* Forward decl - definition moved to rust-expr.h as it requires LiteralExpr to @@ -1485,6 +1475,160 @@ public: } }; +class SingleASTNode +{ +public: + enum NodeType + { + EXPRESSION, + ITEM, + STMT, + }; + + SingleASTNode (std::unique_ptr<Expr> expr) + : type (EXPRESSION), expr (std::move (expr)), item (nullptr), stmt (nullptr) + {} + + SingleASTNode (std::unique_ptr<Item> item) + : type (ITEM), expr (nullptr), item (std::move (item)), stmt (nullptr) + {} + + SingleASTNode (std::unique_ptr<Stmt> stmt) + : type (STMT), expr (nullptr), item (nullptr), stmt (std::move (stmt)) + {} + + SingleASTNode (SingleASTNode const &other) + { + type = other.type; + switch (type) + { + case EXPRESSION: + expr = other.expr->clone_expr (); + break; + + case ITEM: + item = other.item->clone_item (); + break; + + case STMT: + stmt = other.stmt->clone_stmt (); + break; + } + } + + SingleASTNode operator= (SingleASTNode const &other) + { + type = other.type; + switch (type) + { + case EXPRESSION: + expr = other.expr->clone_expr (); + break; + + case ITEM: + item = other.item->clone_item (); + break; + + case STMT: + stmt = other.stmt->clone_stmt (); + break; + } + return *this; + } + + SingleASTNode (SingleASTNode &&other) = default; + SingleASTNode &operator= (SingleASTNode &&other) = default; + + std::unique_ptr<Expr> &get_expr () + { + rust_assert (type == EXPRESSION); + return expr; + } + + std::unique_ptr<Item> &get_item () + { + rust_assert (type == ITEM); + return item; + } + + std::unique_ptr<Stmt> &get_stmt () + { + rust_assert (type == STMT); + return stmt; + } + + void accept_vis (ASTVisitor &vis) + { + switch (type) + { + case EXPRESSION: + expr->accept_vis (vis); + break; + + case ITEM: + item->accept_vis (vis); + break; + + case STMT: + stmt->accept_vis (vis); + break; + } + } + +private: + NodeType type; + + // FIXME make this a union + std::unique_ptr<Expr> expr; + std::unique_ptr<Item> item; + std::unique_ptr<Stmt> stmt; +}; + +/* Basically, a "fragment" that can be incorporated into the AST, created as + * a result of macro expansion. Really annoying to work with due to the fact + * that macros can really expand to anything. As such, horrible representation + * at the moment. */ +class ASTFragment +{ +private: + /* basic idea: essentially, a vector of tagged unions of different AST node + * types. Now, this could actually be stored without a tagged union if the + * different AST node types had a unified parent, but that would create + * issues with the diamond problem or significant performance penalties. So + * a tagged union had to be used instead. A vector is used to represent the + * ability for a macro to expand to two statements, for instance. */ + + std::vector<SingleASTNode> nodes; + +public: + ASTFragment (std::vector<SingleASTNode> nodes) : nodes (std::move (nodes)) {} + + ASTFragment (ASTFragment const &other) + { + nodes.clear (); + nodes.reserve (other.nodes.size ()); + for (auto &n : other.nodes) + { + nodes.push_back (n); + } + } + + ASTFragment &operator= (ASTFragment const &other) + { + nodes.clear (); + nodes.reserve (other.nodes.size ()); + for (auto &n : other.nodes) + { + nodes.push_back (n); + } + return *this; + } + + static ASTFragment create_empty () { return ASTFragment ({}); } + + std::vector<SingleASTNode> &get_nodes () { return nodes; } +}; + /* A macro invocation item (or statement) AST node (i.e. semi-coloned macro * invocation) */ class MacroInvocationSemi : public MacroItem, @@ -1496,6 +1640,10 @@ class MacroInvocationSemi : public MacroItem, std::vector<Attribute> outer_attrs; MacroInvocData invoc_data; Location locus; + NodeId node_id; + + // this is the expanded macro + ASTFragment fragment; public: std::string as_string () const override; @@ -1503,7 +1651,9 @@ public: MacroInvocationSemi (MacroInvocData invoc_data, std::vector<Attribute> outer_attrs, Location locus) : outer_attrs (std::move (outer_attrs)), - invoc_data (std::move (invoc_data)), locus (locus) + invoc_data (std::move (invoc_data)), locus (locus), + node_id (Analysis::Mappings::get ()->get_next_node_id ()), + fragment (ASTFragment::create_empty ()) {} void accept_vis (ASTVisitor &vis) override; @@ -1527,6 +1677,14 @@ public: Location get_locus () const override final { return locus; } + MacroInvocData &get_invoc_data () { return invoc_data; } + + ASTFragment &get_fragment () { return fragment; } + + void set_fragment (ASTFragment &&f) { fragment = std::move (f); } + + NodeId get_macro_node_id () const { return node_id; } + protected: MacroInvocationSemi *clone_macro_invocation_semi_impl () const { diff --git a/gcc/rust/ast/rust-macro.h b/gcc/rust/ast/rust-macro.h index e0524c6..b5370d8 100644 --- a/gcc/rust/ast/rust-macro.h +++ b/gcc/rust/ast/rust-macro.h @@ -24,6 +24,7 @@ namespace Rust { namespace AST { + // Decls as definitions moved to rust-ast.h class MacroItem; class MacroInvocationSemi; @@ -109,6 +110,14 @@ public: void accept_vis (ASTVisitor &vis) override; + MacroMatchType get_macro_match_type () const override + { + return MacroMatchType::Fragment; + } + + Identifier get_ident () const { return ident; } + MacroFragSpec get_frag_spec () const { return frag_spec; } + protected: /* Use covariance to implement clone function as returning this object rather * than base */ @@ -192,6 +201,11 @@ public: void accept_vis (ASTVisitor &vis) override; + MacroMatchType get_macro_match_type () const override + { + return MacroMatchType::Repetition; + } + protected: /* Use covariance to implement clone function as returning this object rather * than base */ @@ -259,6 +273,14 @@ public: void accept_vis (ASTVisitor &vis) override; + MacroMatchType get_macro_match_type () const override + { + return MacroMatchType::Matcher; + } + + DelimType get_delim_type () const { return delim_type; } + std::vector<std::unique_ptr<MacroMatch> > &get_matches () { return matches; } + protected: /* Use covariance to implement clone function as returning this object rather * than base */ @@ -288,6 +310,8 @@ public: std::string as_string () const { return token_tree.as_string (); } Location get_locus () const { return locus; } + + DelimTokenTree &get_token_tree () { return token_tree; } }; // A macro rule? Matcher and transcriber pair? @@ -319,6 +343,9 @@ public: Location get_locus () const { return locus; } std::string as_string () const; + + MacroMatcher &get_matcher () { return matcher; } + MacroTranscriber &get_transcriber () { return transcriber; } }; // A macro rules definition item AST node @@ -365,6 +392,11 @@ public: Location get_locus () const override final { return locus; } + Identifier get_rule_name () const { return rule_name; } + + std::vector<MacroRule> &get_rules () { return rules; } + const std::vector<MacroRule> &get_rules () const { return rules; } + protected: /* Use covariance to implement clone function as returning this object rather * than base */ @@ -384,13 +416,17 @@ class MacroInvocation : public TypeNoBounds, MacroInvocData invoc_data; Location locus; + // this is the expanded macro + ASTFragment fragment; + public: std::string as_string () const override; MacroInvocation (MacroInvocData invoc_data, std::vector<Attribute> outer_attrs, Location locus) : outer_attrs (std::move (outer_attrs)), - invoc_data (std::move (invoc_data)), locus (locus) + invoc_data (std::move (invoc_data)), locus (locus), + fragment (ASTFragment::create_empty ()) {} Location get_locus () const override final { return locus; } @@ -417,6 +453,12 @@ public: return ExprWithoutBlock::get_node_id (); } + MacroInvocData &get_invoc_data () { return invoc_data; } + + ASTFragment &get_fragment () { return fragment; } + + void set_fragment (ASTFragment &&f) { fragment = std::move (f); } + protected: /* Use covariance to implement clone function as returning this object rather * than base */ @@ -651,62 +693,6 @@ protected: } }; -/* Should be a tagged union to save space but implemented as struct due to - * technical difficulties. TODO: fix - * Basically, a single AST node used inside an AST fragment. */ -struct SingleASTNode -{ - std::unique_ptr<Expr> expr; - std::unique_ptr<Stmt> stmt; - std::unique_ptr<Item> item; - std::unique_ptr<Type> type; - std::unique_ptr<Pattern> pattern; - std::unique_ptr<TraitItem> trait_item; - std::unique_ptr<InherentImplItem> inherent_impl_item; - std::unique_ptr<TraitImplItem> trait_impl_item; - std::unique_ptr<ExternalItem> external_item; - - SingleASTNode (std::unique_ptr<Expr> expr) : expr (std::move (expr)) {} - SingleASTNode (std::unique_ptr<Stmt> stmt) : stmt (std::move (stmt)) {} - SingleASTNode (std::unique_ptr<Item> item) : item (std::move (item)) {} - SingleASTNode (std::unique_ptr<Type> type) : type (std::move (type)) {} - SingleASTNode (std::unique_ptr<Pattern> pattern) - : pattern (std::move (pattern)) - {} - SingleASTNode (std::unique_ptr<TraitItem> trait_item) - : trait_item (std::move (trait_item)) - {} - SingleASTNode (std::unique_ptr<InherentImplItem> inherent_impl_item) - : inherent_impl_item (std::move (inherent_impl_item)) - {} - SingleASTNode (std::unique_ptr<TraitImplItem> trait_impl_item) - : trait_impl_item (std::move (trait_impl_item)) - {} - SingleASTNode (std::unique_ptr<ExternalItem> external_item) - : external_item (std::move (external_item)) - {} -}; - -/* Basically, a "fragment" that can be incorporated into the AST, created as - * a result of macro expansion. Really annoying to work with due to the fact - * that macros can really expand to anything. As such, horrible representation - * at the moment. */ -struct ASTFragment -{ -private: - /* basic idea: essentially, a vector of tagged unions of different AST node - * types. Now, this could actually be stored without a tagged union if the - * different AST node types had a unified parent, but that would create - * issues with the diamond problem or significant performance penalties. So - * a tagged union had to be used instead. A vector is used to represent the - * ability for a macro to expand to two statements, for instance. */ - - std::vector<SingleASTNode> nodes; - -public: - ASTFragment (std::vector<SingleASTNode> nodes) : nodes (std::move (nodes)) {} -}; - // Object that parses macros from a token stream. /* TODO: would "AttributeParser" be a better name? MetaItems are only for * attributes, I believe */ diff --git a/gcc/rust/expand/rust-macro-expand.cc b/gcc/rust/expand/rust-macro-expand.cc index 4a096c3..dcfec7c 100644 --- a/gcc/rust/expand/rust-macro-expand.cc +++ b/gcc/rust/expand/rust-macro-expand.cc @@ -20,6 +20,7 @@ #include "rust-ast-full.h" #include "rust-ast-visitor.h" #include "rust-diagnostics.h" +#include "rust-parse.h" namespace Rust { // Visitor used to expand attributes. @@ -323,6 +324,13 @@ public: // I don't think any macro token trees can be stripped in any way // TODO: maybe have cfg! macro stripping behaviour here? + + expander.expand_invoc_semi (macro_invoc); + + // we need to visit the expanded fragments since it may need cfg expansion + // and it may be recursive + for (auto &node : macro_invoc.get_fragment ().get_nodes ()) + node.accept_vis (*this); } void visit (AST::PathInExpression &path) override @@ -1033,13 +1041,17 @@ public: "cannot strip expression in this position - outer " "attributes not allowed"); } + void visit (AST::BlockExpr &expr) override { + expander.push_context (MacroExpander::BLOCK); + // initial strip test based on outer attrs expander.expand_cfg_attrs (expr.get_outer_attrs ()); if (expander.fails_cfg_with_expand (expr.get_outer_attrs ())) { expr.mark_for_strip (); + expander.pop_context (); return; } @@ -1049,6 +1061,7 @@ public: if (expander.fails_cfg_with_expand (expr.get_inner_attrs ())) { expr.mark_for_strip (); + expander.pop_context (); return; } @@ -1065,7 +1078,9 @@ public: if (tail_expr->is_marked_for_strip ()) expr.strip_tail_expr (); } + expander.pop_context (); } + void visit (AST::ClosureExprInnerTyped &expr) override { // initial strip test based on outer attrs @@ -2509,9 +2524,20 @@ public: } // I don't think any macro rules can be stripped in any way + + auto path = Resolver::CanonicalPath::new_seg (rules_def.get_node_id (), + rules_def.get_rule_name ()); + expander.resolver->get_macro_scope ().insert (path, + rules_def.get_node_id (), + rules_def.get_locus ()); + expander.mappings->insert_macro_def (&rules_def); } + void visit (AST::MacroInvocation ¯o_invoc) override { + // FIXME + // we probably need another recurision check here + // initial strip test based on outer attrs expander.expand_cfg_attrs (macro_invoc.get_outer_attrs ()); if (expander.fails_cfg_with_expand (macro_invoc.get_outer_attrs ())) @@ -2521,9 +2547,14 @@ public: } // I don't think any macro token trees can be stripped in any way + expander.expand_invoc (macro_invoc); - // TODO: maybe have stripping behaviour for the cfg! macro here? + // we need to visit the expanded fragments since it may need cfg expansion + // and it may be recursive + for (auto &node : macro_invoc.get_fragment ().get_nodes ()) + node.accept_vis (*this); } + void visit (AST::MetaItemPath &) override {} void visit (AST::MetaItemSeq &) override {} void visit (AST::MetaWord &) override {} @@ -3011,7 +3042,7 @@ MacroExpander::parse_macro_to_meta_item (AST::MacroInvocData &invoc) } else { - std::vector<std::unique_ptr<AST::MetaItemInner> > meta_items ( + std::vector<std::unique_ptr<AST::MetaItemInner>> meta_items ( std::move (converted_input->get_items ())); invoc.set_meta_item_output (std::move (meta_items)); } @@ -3038,10 +3069,11 @@ MacroExpander::expand_cfg_macro (AST::MacroInvocData &invoc) return AST::Literal ("false", AST::Literal::BOOL, CORETYPE_BOOL); } -#if 0 AST::ASTFragment -MacroExpander::expand_decl_macro (AST::MacroInvocData &invoc, - AST::MacroRulesDefinition &rules_def) +MacroExpander::expand_decl_macro (Location invoc_locus, + AST::MacroInvocData &invoc, + AST::MacroRulesDefinition &rules_def, + bool semicolon) { // ensure that both invocation and rules are in a valid state rust_assert (!invoc.is_marked_for_strip ()); @@ -3081,49 +3113,123 @@ MacroExpander::expand_decl_macro (AST::MacroInvocData &invoc, * TokenTree). This will prevent re-conversion of Tokens between each type * all the time, while still allowing the heterogenous storage of token trees. */ + + AST::DelimTokenTree &invoc_token_tree = invoc.get_delim_tok_tree (); + + // find matching arm + AST::MacroRule *matched_rule = nullptr; + std::map<std::string, MatchedFragment> matched_fragments; + for (auto &rule : rules_def.get_rules ()) + { + sub_stack.push (); + bool did_match_rule = try_match_rule (rule, invoc_token_tree); + matched_fragments = sub_stack.pop (); + + if (did_match_rule) + { + matched_rule = &rule; + break; + } + } + + if (matched_rule == nullptr) + { + RichLocation r (invoc_locus); + r.add_range (rules_def.get_locus ()); + rust_error_at (r, "Failed to match any rule within macro"); + return AST::ASTFragment::create_empty (); + } + + return transcribe_rule (*matched_rule, invoc_token_tree, matched_fragments, + semicolon, peek_context ()); +} + +void +MacroExpander::expand_invoc (AST::MacroInvocation &invoc) +{ + if (depth_exceeds_recursion_limit ()) + { + rust_error_at (invoc.get_locus (), "reached recursion limit"); + return; + } + + AST::MacroInvocData &invoc_data = invoc.get_invoc_data (); + + // ?? + // switch on type of macro: + // - '!' syntax macro (inner switch) + // - procedural macro - "A token-based function-like macro" + // - 'macro_rules' (by example/pattern-match) macro? or not? "an + // AST-based function-like macro" + // - else is unreachable + // - attribute syntax macro (inner switch) + // - procedural macro attribute syntax - "A token-based attribute + // macro" + // - legacy macro attribute syntax? - "an AST-based attribute macro" + // - non-macro attribute: mark known + // - else is unreachable + // - derive macro (inner switch) + // - derive or legacy derive - "token-based" vs "AST-based" + // - else is unreachable + // - derive container macro - unreachable + + // lookup the rules for this macro + NodeId resolved_node = UNKNOWN_NODEID; + bool found = resolver->get_macro_scope ().lookup ( + Resolver::CanonicalPath::new_seg (invoc.get_pattern_node_id (), + invoc_data.get_path ().as_string ()), + &resolved_node); + if (!found) + { + rust_error_at (invoc.get_locus (), "unknown macro"); + return; + } + + // lookup the rules + AST::MacroRulesDefinition *rules_def = nullptr; + bool ok = mappings->lookup_macro_def (resolved_node, &rules_def); + rust_assert (ok); + + auto fragment + = expand_decl_macro (invoc.get_locus (), invoc_data, *rules_def, false); + + // lets attach this fragment to the invocation + invoc.set_fragment (std::move (fragment)); } -#endif void -MacroExpander::expand_invoc (std::unique_ptr<AST::MacroInvocation> &invoc) +MacroExpander::expand_invoc_semi (AST::MacroInvocationSemi &invoc) { - /* if current expansion depth > recursion limit, create an error (maybe fatal - * error) and return */ - - /* switch on type of macro: - - '!' syntax macro (inner switch) - - procedural macro - "A token-based function-like macro" - - 'macro_rules' (by example/pattern-match) macro? or not? "an - AST-based function-like macro" - - else is unreachable - - attribute syntax macro (inner switch) - - procedural macro attribute syntax - "A token-based attribute macro" - - legacy macro attribute syntax? - "an AST-based attribute macro" - - non-macro attribute: mark known - - else is unreachable - - derive macro (inner switch) - - derive or legacy derive - "token-based" vs "AST-based" - - else is unreachable - - derive container macro - unreachable*/ - -#if 0 - // macro_rules macro test code - auto rule_def = find_rules_def(invoc->get_path()); - if (rule_def != nullptr) { - ASTFrag expanded = expand_decl_macro(invoc, rule_def); - /* could make this a data structure containing vectors of exprs, patterns and types (for regular), - * and then stmts and items (for semi). Except what about having an expr, then a type? Hmm. Might - * have to do the "unified base type" thing OR just have a simulated union, and then have AST frag - * be a vector of these simulated unions. */ - - // how would errors be signalled? null fragment? something else? - // what about error vs just not having stuff in rules definition yet? - - /* replace macro invocation with ast frag. actually, don't have any context here. maybe attach ast - * frag to macro invocation, and then have a method above get it? Or just return the ast frag from - * this method. */ - } -#endif + if (depth_exceeds_recursion_limit ()) + { + rust_error_at (invoc.get_locus (), "reached recursion limit"); + return; + } + + AST::MacroInvocData &invoc_data = invoc.get_invoc_data (); + + // lookup the rules for this macro + NodeId resolved_node = UNKNOWN_NODEID; + bool found = resolver->get_macro_scope ().lookup ( + Resolver::CanonicalPath::new_seg (invoc.get_macro_node_id (), + invoc_data.get_path ().as_string ()), + &resolved_node); + if (!found) + { + rust_error_at (invoc.get_locus (), "unknown macro"); + return; + } + + // lookup the rules + AST::MacroRulesDefinition *rules_def = nullptr; + bool ok = mappings->lookup_macro_def (resolved_node, &rules_def); + rust_assert (ok); + + auto fragment + = expand_decl_macro (invoc.get_locus (), invoc_data, *rules_def, true); + + // lets attach this fragment to the invocation + invoc.set_fragment (std::move (fragment)); } /* Determines whether any cfg predicate is false and hence item with attributes @@ -3225,6 +3331,9 @@ MacroExpander::expand_cfg_attrs (AST::AttrVec &attrs) void MacroExpander::expand_crate () { + NodeId scope_node_id = crate.get_node_id (); + resolver->get_macro_scope ().push (scope_node_id); + /* fill macro/decorator map from init list? not sure where init list comes * from? */ @@ -3242,6 +3351,8 @@ MacroExpander::expand_crate () } // expand module attributes? + push_context (ITEM); + // expand attributes recursively and strip items if required AttrVisitor attr_visitor (*this); auto &items = crate.items; @@ -3258,6 +3369,8 @@ MacroExpander::expand_crate () ++it; } + pop_context (); + // TODO: should recursive attribute and macro expansion be done in the same // transversal? Or in separate ones like currently? @@ -3267,4 +3380,435 @@ MacroExpander::expand_crate () // extract exported macros? } + +bool +MacroExpander::depth_exceeds_recursion_limit () const +{ + return expansion_depth >= cfg.recursion_limit; +} + +bool +MacroExpander::try_match_rule (AST::MacroRule &match_rule, + AST::DelimTokenTree &invoc_token_tree) +{ + MacroInvocLexer lex (invoc_token_tree.to_token_stream ()); + Parser<MacroInvocLexer> parser (std::move (lex)); + + AST::MacroMatcher &matcher = match_rule.get_matcher (); + + expansion_depth++; + if (!match_matcher (parser, matcher)) + { + expansion_depth--; + return false; + } + expansion_depth--; + + bool used_all_input_tokens = parser.skip_token (END_OF_FILE); + return used_all_input_tokens; +} + +bool +MacroExpander::match_fragment (Parser<MacroInvocLexer> &parser, + AST::MacroMatchFragment &fragment) +{ + switch (fragment.get_frag_spec ()) + { + case AST::MacroFragSpec::EXPR: + parser.parse_expr (); + break; + + case AST::MacroFragSpec::BLOCK: + parser.parse_block_expr (); + break; + + case AST::MacroFragSpec::IDENT: + parser.parse_identifier_pattern (); + break; + + case AST::MacroFragSpec::LITERAL: + parser.parse_literal_expr (); + break; + + case AST::MacroFragSpec::ITEM: + parser.parse_item (false); + break; + + case AST::MacroFragSpec::TY: + parser.parse_type (); + break; + + case AST::MacroFragSpec::PAT: + parser.parse_pattern (); + break; + + case AST::MacroFragSpec::PATH: + parser.parse_path_in_expression (); + break; + + case AST::MacroFragSpec::VIS: + parser.parse_visibility (); + break; + + case AST::MacroFragSpec::STMT: + parser.parse_stmt (); + break; + + case AST::MacroFragSpec::LIFETIME: + parser.parse_lifetime_params (); + break; + + // is meta attributes? + case AST::MacroFragSpec::META: + // parser.parse_inner_attribute ? + // parser.parse_outer_attribute ? + // parser.parse_attribute_body ? + // parser.parse_doc_comment ? + gcc_unreachable (); + break; + + // what is TT? + case AST::MacroFragSpec::TT: + // parser.parse_token_tree() ? + gcc_unreachable (); + break; + + // i guess we just ignore invalid and just error out + case AST::MacroFragSpec::INVALID: + return false; + } + + // it matches if the parser did not produce errors trying to parse that type + // of item + return !parser.has_errors (); +} + +bool +MacroExpander::match_matcher (Parser<MacroInvocLexer> &parser, + AST::MacroMatcher &matcher) +{ + if (depth_exceeds_recursion_limit ()) + { + rust_error_at (matcher.get_match_locus (), "reached recursion limit"); + return false; + } + + // this is used so we can check that we delimit the stream correctly. + switch (matcher.get_delim_type ()) + { + case AST::DelimType::PARENS: { + if (!parser.skip_token (LEFT_PAREN)) + return false; + } + break; + + case AST::DelimType::SQUARE: { + if (!parser.skip_token (LEFT_SQUARE)) + return false; + } + break; + + case AST::DelimType::CURLY: { + if (!parser.skip_token (LEFT_CURLY)) + return false; + } + break; + } + + const MacroInvocLexer &source = parser.get_token_source (); + + for (auto &match : matcher.get_matches ()) + { + size_t offs_begin = source.get_offs (); + switch (match->get_macro_match_type ()) + { + case AST::MacroMatch::MacroMatchType::Fragment: { + AST::MacroMatchFragment *fragment + = static_cast<AST::MacroMatchFragment *> (match.get ()); + if (!match_fragment (parser, *fragment)) + return false; + + // matched fragment get the offset in the token stream + size_t offs_end = source.get_offs (); + sub_stack.peek ().insert ( + {fragment->get_ident (), + {fragment->get_ident (), offs_begin, offs_end}}); + } + break; + + case AST::MacroMatch::MacroMatchType::Tok: { + AST::Token *tok = static_cast<AST::Token *> (match.get ()); + if (!match_token (parser, *tok)) + return false; + } + break; + + case AST::MacroMatch::MacroMatchType::Repetition: { + AST::MacroMatchRepetition *rep + = static_cast<AST::MacroMatchRepetition *> (match.get ()); + if (!match_repetition (parser, *rep)) + return false; + } + break; + + case AST::MacroMatch::MacroMatchType::Matcher: { + AST::MacroMatcher *m + = static_cast<AST::MacroMatcher *> (match.get ()); + expansion_depth++; + if (!match_matcher (parser, *m)) + { + expansion_depth--; + return false; + } + expansion_depth--; + } + break; + } + } + + switch (matcher.get_delim_type ()) + { + case AST::DelimType::PARENS: { + if (!parser.skip_token (RIGHT_PAREN)) + return false; + } + break; + + case AST::DelimType::SQUARE: { + if (!parser.skip_token (RIGHT_SQUARE)) + return false; + } + break; + + case AST::DelimType::CURLY: { + if (!parser.skip_token (RIGHT_CURLY)) + return false; + } + break; + } + + return true; +} + +bool +MacroExpander::match_token (Parser<MacroInvocLexer> &parser, AST::Token &token) +{ + // FIXME this needs to actually match the content and the type + return parser.skip_token (token.get_id ()); +} + +bool +MacroExpander::match_repetition (Parser<MacroInvocLexer> &parser, + AST::MacroMatchRepetition &rep) +{ + // TODO + gcc_unreachable (); + return false; +} + +AST::ASTFragment +MacroExpander::transcribe_rule ( + AST::MacroRule &match_rule, AST::DelimTokenTree &invoc_token_tree, + std::map<std::string, MatchedFragment> &matched_fragments, bool semicolon, + ContextType ctx) +{ + // we can manipulate the token tree to substitute the dollar identifiers so + // that when we call parse its already substituted for us + AST::MacroTranscriber &transcriber = match_rule.get_transcriber (); + AST::DelimTokenTree &transcribe_tree = transcriber.get_token_tree (); + + auto invoc_stream = invoc_token_tree.to_token_stream (); + auto macro_rule_tokens = transcribe_tree.to_token_stream (); + + std::vector<std::unique_ptr<AST::Token>> substituted_tokens + = substitute_tokens (invoc_stream, macro_rule_tokens, matched_fragments); + + // // handy for debugging + // for (auto &tok : substituted_tokens) + // { + // rust_debug ("tok: [%s]", tok->as_string ().c_str ()); + // } + + // parse it to an ASTFragment + MacroInvocLexer lex (std::move (substituted_tokens)); + Parser<MacroInvocLexer> parser (std::move (lex)); + + // this is used so we can check that we delimit the stream correctly. + switch (transcribe_tree.get_delim_type ()) + { + case AST::DelimType::PARENS: + rust_assert (parser.skip_token (LEFT_PAREN)); + break; + + case AST::DelimType::CURLY: + rust_assert (parser.skip_token (LEFT_CURLY)); + break; + + case AST::DelimType::SQUARE: + rust_assert (parser.skip_token (LEFT_SQUARE)); + break; + } + + // see https://github.com/Rust-GCC/gccrs/issues/22 + // TL;DR: + // - Treat all macro invocations with parentheses, (), or square brackets, + // [], as expressions. + // - If the macro invocation has curly brackets, {}, it may be parsed as a + // statement depending on the context. + // - If the macro invocation has a semicolon at the end, it must be parsed + // as a statement (either via ExpressionStatement or + // MacroInvocationWithSemi) + + // parse the item + std::vector<AST::SingleASTNode> nodes; + switch (invoc_token_tree.get_delim_type ()) + { + case AST::DelimType::PARENS: + case AST::DelimType::SQUARE: { + switch (ctx) + { + case ContextType::ITEM: { + auto item = parser.parse_item (true); + if (item != nullptr && !parser.has_errors ()) + { + rust_debug ("HELLO WORLD: [%s]", item->as_string ().c_str ()); + nodes.push_back (std::move (item)); + } + } + break; + + case ContextType::BLOCK: { + auto expr = parser.parse_expr (); + if (expr != nullptr && !parser.has_errors ()) + nodes.push_back (std::move (expr)); + } + break; + } + } + break; + + case AST::DelimType::CURLY: { + switch (ctx) + { + case ContextType::ITEM: { + auto item = parser.parse_item (true); + if (item != nullptr && !parser.has_errors ()) + nodes.push_back (std::move (item)); + } + break; + + case ContextType::BLOCK: { + auto stmt = parser.parse_stmt (); + if (stmt != nullptr && !parser.has_errors ()) + nodes.push_back (std::move (stmt)); + } + break; + } + } + break; + } + + // emit any errors + if (parser.has_errors ()) + { + for (auto &err : parser.get_errors ()) + { + rust_error_at (err.locus, "%s", err.message.c_str ()); + } + return AST::ASTFragment::create_empty (); + } + + // are all the tokens used? + bool did_delimit = false; + switch (transcribe_tree.get_delim_type ()) + { + case AST::DelimType::PARENS: + did_delimit = parser.skip_token (RIGHT_PAREN); + break; + case AST::DelimType::SQUARE: + did_delimit = parser.skip_token (RIGHT_SQUARE); + break; + case AST::DelimType::CURLY: + did_delimit = parser.skip_token (RIGHT_CURLY); + break; + } + + bool reached_end_of_stream = did_delimit && parser.skip_token (END_OF_FILE); + if (!reached_end_of_stream) + { + const_TokenPtr current_token = parser.peek_current_token (); + rust_error_at (current_token->get_locus (), + "tokens here and after are unparsed"); + } + + return AST::ASTFragment (std::move (nodes)); +} + +std::vector<std::unique_ptr<AST::Token>> +MacroExpander::substitute_tokens ( + std::vector<std::unique_ptr<AST::Token>> &input, + std::vector<std::unique_ptr<AST::Token>> ¯o, + std::map<std::string, MatchedFragment> &fragments) +{ + std::vector<std::unique_ptr<AST::Token>> replaced_tokens; + + for (size_t i = 0; i < macro.size (); i++) + { + auto &tok = macro.at (i); + if (tok->get_id () == DOLLAR_SIGN) + { + std::vector<std::unique_ptr<AST::Token>> parsed_toks; + + std::string ident; + for (size_t offs = i; i < macro.size (); offs++) + { + auto &tok = macro.at (offs); + if (tok->get_id () == DOLLAR_SIGN && offs == i) + { + parsed_toks.push_back (tok->clone_token ()); + } + else if (tok->get_id () == IDENTIFIER) + { + rust_assert (tok->as_string ().size () == 1); + ident.push_back (tok->as_string ().at (0)); + parsed_toks.push_back (tok->clone_token ()); + } + else + { + break; + } + } + + // lookup the ident + auto it = fragments.find (ident); + if (it == fragments.end ()) + { + // just leave the tokens in + for (auto &tok : parsed_toks) + { + replaced_tokens.push_back (tok->clone_token ()); + } + } + else + { + // replace + MatchedFragment &frag = it->second; + for (size_t offs = frag.token_offset_begin; + offs < frag.token_offset_end; offs++) + { + auto &tok = input.at (offs); + replaced_tokens.push_back (tok->clone_token ()); + } + } + i += parsed_toks.size () - 1; + } + else + { + replaced_tokens.push_back (tok->clone_token ()); + } + } + + return replaced_tokens; +} + } // namespace Rust diff --git a/gcc/rust/expand/rust-macro-expand.h b/gcc/rust/expand/rust-macro-expand.h index cd9165a..d8a2d50 100644 --- a/gcc/rust/expand/rust-macro-expand.h +++ b/gcc/rust/expand/rust-macro-expand.h @@ -19,8 +19,13 @@ #ifndef RUST_MACRO_EXPAND_H #define RUST_MACRO_EXPAND_H +#include "rust-buffered-queue.h" +#include "rust-parse.h" +#include "rust-token.h" #include "rust-ast.h" #include "rust-macro.h" +#include "rust-hir-map.h" +#include "rust-name-resolver.h" // Provides objects and method prototypes for macro expansion @@ -43,14 +48,101 @@ struct ExpansionCfg std::string crate_name = ""; }; +class MacroInvocLexer +{ +public: + MacroInvocLexer (std::vector<std::unique_ptr<AST::Token>> stream) + : offs (0), token_stream (std::move (stream)) + {} + + // Returns token n tokens ahead of current position. + const_TokenPtr peek_token (int n) + { + if ((offs + n) >= token_stream.size ()) + return Token::make (END_OF_FILE, Location ()); + + return token_stream.at (offs + n)->get_tok_ptr (); + } + // Peeks the current token. + const_TokenPtr peek_token () { return peek_token (0); } + + // Advances current token to n + 1 tokens ahead of current position. + void skip_token (int n) { offs += (n + 1); } + + // Skips the current token. + void skip_token () { skip_token (0); } + + // Splits the current token into two. Intended for use with nested generics + // closes (i.e. T<U<X>> where >> is wrongly lexed as one token). Note that + // this will only work with "simple" tokens like punctuation. + void split_current_token (TokenId /*new_left*/, TokenId /*new_right*/) + { + // FIXME + gcc_unreachable (); + } + + std::string get_filename () const + { + gcc_unreachable (); + return "FIXME"; + } + + size_t get_offs () const { return offs; } + +private: + size_t offs; + std::vector<std::unique_ptr<AST::Token>> token_stream; +}; + +struct MatchedFragment +{ + std::string fragment_ident; + size_t token_offset_begin; + size_t token_offset_end; + + std::string as_string () const + { + return fragment_ident + "=" + std::to_string (token_offset_begin) + ":" + + std::to_string (token_offset_end); + } +}; + +class SubstitutionScope +{ +public: + SubstitutionScope () : stack () {} + + void push () { stack.push_back ({}); } + + std::map<std::string, MatchedFragment> pop () + { + auto top = stack.back (); + stack.pop_back (); + return top; + } + + std::map<std::string, MatchedFragment> &peek () { return stack.back (); } + +private: + std::vector<std::map<std::string, MatchedFragment>> stack; +}; + // Object used to store shared data (between functions) for macro expansion. struct MacroExpander { + enum ContextType + { + ITEM, + BLOCK, + }; + ExpansionCfg cfg; unsigned int expansion_depth = 0; MacroExpander (AST::Crate &crate, ExpansionCfg cfg, Session &session) - : cfg (cfg), crate (crate), session (session) + : cfg (cfg), crate (crate), session (session), + sub_stack (SubstitutionScope ()), resolver (Resolver::Resolver::get ()), + mappings (Analysis::Mappings::get ()) {} ~MacroExpander () = default; @@ -61,11 +153,14 @@ struct MacroExpander /* Expands a macro invocation (not macro invocation semi) - possibly make both * have similar duck-typed interface and use templates?*/ // should this be public or private? - void expand_invoc (std::unique_ptr<AST::MacroInvocation> &invoc); + void expand_invoc (AST::MacroInvocation &invoc); + void expand_invoc_semi (AST::MacroInvocationSemi &invoc); // Expands a single declarative macro. - AST::ASTFragment expand_decl_macro (AST::MacroInvocData &invoc, - AST::MacroRulesDefinition &rules_def); + AST::ASTFragment expand_decl_macro (Location locus, + AST::MacroInvocData &invoc, + AST::MacroRulesDefinition &rules_def, + bool semicolon); void expand_cfg_attrs (AST::AttrVec &attrs); bool fails_cfg (const AST::AttrVec &attr) const; @@ -76,10 +171,55 @@ struct MacroExpander // Get the literal representation of a cfg! macro. AST::Literal expand_cfg_macro (AST::MacroInvocData &invoc); + bool depth_exceeds_recursion_limit () const; + + bool try_match_rule (AST::MacroRule &match_rule, + AST::DelimTokenTree &invoc_token_tree); + + AST::ASTFragment + transcribe_rule (AST::MacroRule &match_rule, + AST::DelimTokenTree &invoc_token_tree, + std::map<std::string, MatchedFragment> &matched_fragments, + bool semicolon, ContextType ctx); + + bool match_fragment (Parser<MacroInvocLexer> &parser, + AST::MacroMatchFragment &fragment); + + bool match_token (Parser<MacroInvocLexer> &parser, AST::Token &token); + + bool match_repetition (Parser<MacroInvocLexer> &parser, + AST::MacroMatchRepetition &rep); + + bool match_matcher (Parser<MacroInvocLexer> &parser, + AST::MacroMatcher &matcher); + + static std::vector<std::unique_ptr<AST::Token>> + substitute_tokens (std::vector<std::unique_ptr<AST::Token>> &input, + std::vector<std::unique_ptr<AST::Token>> ¯o, + std::map<std::string, MatchedFragment> &fragments); + + void push_context (ContextType t) { context.push_back (t); } + + ContextType pop_context () + { + ContextType t = context.back (); + context.pop_back (); + return t; + } + + ContextType peek_context () { return context.back (); } + private: AST::Crate &crate; Session &session; + SubstitutionScope sub_stack; + std::vector<ContextType> context; + +public: + Resolver::Resolver *resolver; + Analysis::Mappings *mappings; }; + } // namespace Rust #endif diff --git a/gcc/rust/hir/rust-ast-lower-expr.h b/gcc/rust/hir/rust-ast-lower-expr.h index 1cc3f1d..a8048bb 100644 --- a/gcc/rust/hir/rust-ast-lower-expr.h +++ b/gcc/rust/hir/rust-ast-lower-expr.h @@ -100,6 +100,16 @@ public: return resolver.translated; } + void visit (AST::MacroInvocation &expr) override + { + AST::ASTFragment &fragment = expr.get_fragment (); + + // FIXME + // this assertion might go away, maybe on failure's to expand a macro? + rust_assert (!fragment.get_nodes ().empty ()); + fragment.get_nodes ().at (0).accept_vis (*this); + } + void visit (AST::TupleIndexExpr &expr) override { HIR::Expr *tuple_expr diff --git a/gcc/rust/hir/rust-ast-lower-implitem.h b/gcc/rust/hir/rust-ast-lower-implitem.h index c9dfd1b..3d10b70 100644 --- a/gcc/rust/hir/rust-ast-lower-implitem.h +++ b/gcc/rust/hir/rust-ast-lower-implitem.h @@ -52,6 +52,16 @@ public: return resolver.translated; } + void visit (AST::MacroInvocationSemi &invoc) override + { + AST::ASTFragment &fragment = invoc.get_fragment (); + + // FIXME + // this assertion might go away, maybe on failure's to expand a macro? + rust_assert (!fragment.get_nodes ().empty ()); + fragment.get_nodes ().at (0).accept_vis (*this); + } + void visit (AST::TypeAlias &alias) override { std::vector<std::unique_ptr<HIR::WhereClauseItem> > where_clause_items; @@ -308,6 +318,16 @@ public: return resolver.translated; } + void visit (AST::MacroInvocationSemi &invoc) override + { + AST::ASTFragment &fragment = invoc.get_fragment (); + + // FIXME + // this assertion might go away, maybe on failure's to expand a macro? + rust_assert (!fragment.get_nodes ().empty ()); + fragment.get_nodes ().at (0).accept_vis (*this); + } + void visit (AST::TraitItemFunc &func) override { AST::TraitFunctionDecl &ref = func.get_trait_function_decl (); diff --git a/gcc/rust/hir/rust-ast-lower-item.h b/gcc/rust/hir/rust-ast-lower-item.h index d4b5910..30bd896 100644 --- a/gcc/rust/hir/rust-ast-lower-item.h +++ b/gcc/rust/hir/rust-ast-lower-item.h @@ -47,6 +47,16 @@ public: return resolver.translated; } + void visit (AST::MacroInvocationSemi &invoc) override + { + AST::ASTFragment &fragment = invoc.get_fragment (); + + // FIXME + // this assertion might go away, maybe on failure's to expand a macro? + rust_assert (!fragment.get_nodes ().empty ()); + fragment.get_nodes ().at (0).accept_vis (*this); + } + void visit (AST::Module &module) override { auto crate_num = mappings->get_current_crate (); diff --git a/gcc/rust/hir/rust-ast-lower-stmt.h b/gcc/rust/hir/rust-ast-lower-stmt.h index ede72bd..b25246a 100644 --- a/gcc/rust/hir/rust-ast-lower-stmt.h +++ b/gcc/rust/hir/rust-ast-lower-stmt.h @@ -45,6 +45,16 @@ public: return resolver.translated; } + void visit (AST::MacroInvocationSemi &invoc) override + { + AST::ASTFragment &fragment = invoc.get_fragment (); + + // FIXME + // this assertion might go away, maybe on failure's to expand a macro? + rust_assert (!fragment.get_nodes ().empty ()); + fragment.get_nodes ().at (0).accept_vis (*this); + } + void visit (AST::ExprStmtWithBlock &stmt) override { HIR::ExprWithBlock *expr diff --git a/gcc/rust/lex/rust-lex.h b/gcc/rust/lex/rust-lex.h index 4b2feae..0ae07fe 100644 --- a/gcc/rust/lex/rust-lex.h +++ b/gcc/rust/lex/rust-lex.h @@ -1,3 +1,21 @@ +// Copyright (C) 2020-2022 Free Software Foundation, Inc. + +// This file is part of GCC. + +// GCC is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3, or (at your option) any later +// version. + +// GCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. + +// You should have received a copy of the GNU General Public License +// along with GCC; see the file COPYING3. If not see +// <http://www.gnu.org/licenses/>. + #ifndef RUST_LEX_H #define RUST_LEX_H @@ -198,6 +216,7 @@ private: // Token stream queue. buffered_queue<std::shared_ptr<Token>, TokenSource> token_queue; }; + } // namespace Rust #endif diff --git a/gcc/rust/parse/rust-parse-impl.h b/gcc/rust/parse/rust-parse-impl.h index 7483818..784e6d1 100644 --- a/gcc/rust/parse/rust-parse-impl.h +++ b/gcc/rust/parse/rust-parse-impl.h @@ -905,6 +905,9 @@ Parser<ManagedTokenSource>::parse_delim_token_tree () // parse actual token tree vector - 0 or more std::vector<std::unique_ptr<AST::TokenTree>> token_trees_in_tree; + auto delim_open + = std::unique_ptr<AST::Token> (new AST::Token (std::move (t))); + token_trees_in_tree.push_back (std::move (delim_open)); // repeat loop until finding the matching delimiter t = lexer.peek_token (); @@ -929,6 +932,9 @@ Parser<ManagedTokenSource>::parse_delim_token_tree () // lexer.skip_token(); t = lexer.peek_token (); } + auto delim_close + = std::unique_ptr<AST::Token> (new AST::Token (std::move (t))); + token_trees_in_tree.push_back (std::move (delim_close)); AST::DelimTokenTree token_tree (delim_type, std::move (token_trees_in_tree), initial_loc); @@ -1565,6 +1571,9 @@ Parser<ManagedTokenSource>::parse_macro_invocation_semi ( // parse actual token trees std::vector<std::unique_ptr<AST::TokenTree>> token_trees; + auto delim_open + = std::unique_ptr<AST::Token> (new AST::Token (std::move (t))); + token_trees.push_back (std::move (delim_open)); t = lexer.peek_token (); // parse token trees until the initial delimiter token is found again @@ -1587,6 +1596,9 @@ Parser<ManagedTokenSource>::parse_macro_invocation_semi ( t = lexer.peek_token (); } + auto delim_close + = std::unique_ptr<AST::Token> (new AST::Token (std::move (t))); + token_trees.push_back (std::move (delim_close)); AST::DelimTokenTree delim_tok_tree (delim_type, std::move (token_trees), tok_tree_locus); @@ -1605,6 +1617,7 @@ Parser<ManagedTokenSource>::parse_macro_invocation_semi ( if (!skip_token (SEMICOLON)) { // as this is the end, allow recovery (probably) - may change + return std::unique_ptr<AST::MacroInvocationSemi> ( new AST::MacroInvocationSemi (std::move (invoc_data), std::move (outer_attrs), @@ -11755,6 +11768,9 @@ Parser<ManagedTokenSource>::parse_path_based_stmt_or_expr ( // parse actual token trees std::vector<std::unique_ptr<AST::TokenTree>> token_trees; + auto delim_open + = std::unique_ptr<AST::Token> (new AST::Token (std::move (t3))); + token_trees.push_back (std::move (delim_open)); t3 = lexer.peek_token (); // parse token trees until the initial delimiter token is found again @@ -11779,6 +11795,10 @@ Parser<ManagedTokenSource>::parse_path_based_stmt_or_expr ( t3 = lexer.peek_token (); } + auto delim_close + = std::unique_ptr<AST::Token> (new AST::Token (std::move (t3))); + token_trees.push_back (std::move (delim_close)); + // parse end delimiters t3 = lexer.peek_token (); if (token_id_matches_delims (t3->get_id (), type)) @@ -12070,6 +12090,9 @@ Parser<ManagedTokenSource>::parse_macro_invocation_maybe_semi ( // parse actual token trees std::vector<std::unique_ptr<AST::TokenTree>> token_trees; + auto delim_open + = std::unique_ptr<AST::Token> (new AST::Token (std::move (t3))); + token_trees.push_back (std::move (delim_open)); t3 = lexer.peek_token (); // parse token trees until the initial delimiter token is found again @@ -12092,6 +12115,9 @@ Parser<ManagedTokenSource>::parse_macro_invocation_maybe_semi ( t3 = lexer.peek_token (); } + auto delim_close + = std::unique_ptr<AST::Token> (new AST::Token (std::move (t3))); + token_trees.push_back (std::move (delim_close)); // parse end delimiters t3 = lexer.peek_token (); diff --git a/gcc/rust/parse/rust-parse.cc b/gcc/rust/parse/rust-parse.cc index f2c1301..f995e4b 100644 --- a/gcc/rust/parse/rust-parse.cc +++ b/gcc/rust/parse/rust-parse.cc @@ -18,32 +18,6 @@ along with GCC; see the file COPYING3. If not see #include "rust-linemap.h" #include "rust-diagnostics.h" -#if 0 -#include "config.h" -#include "system.h" -#include "coretypes.h" -#include "target.h" -#include "tree.h" -#include "tree-iterator.h" -#include "input.h" -#include "diagnostic.h" -#include "stringpool.h" -#include "cgraph.h" -#include "gimplify.h" -#include "gimple-expr.h" -#include "convert.h" -#include "print-tree.h" -#include "stor-layout.h" -#include "fold-const.h" -/* order: config, system, coretypes, target, tree, tree-iterator, input, diagnostic, stringpool, - * cgraph, gimplify, gimple-expr, convert, print-tree, stor-layout, fold-const */ -// probably don't need all these -#endif -// maybe put these back in if compiling no longer works - -/* TODO: move non-essential stuff back here from rust-parse-impl.h after - * confirming that it works */ - namespace Rust { std::string diff --git a/gcc/rust/parse/rust-parse.h b/gcc/rust/parse/rust-parse.h index acab7ff..5ee7b4e 100644 --- a/gcc/rust/parse/rust-parse.h +++ b/gcc/rust/parse/rust-parse.h @@ -86,6 +86,29 @@ struct ParseRestrictions // TODO: if updated to C++20, ManagedTokenSource would be useful as a concept template <typename ManagedTokenSource> class Parser { +public: + bool skip_token (TokenId t); + + std::unique_ptr<AST::Expr> + parse_expr (AST::AttrVec outer_attrs = AST::AttrVec (), + ParseRestrictions restrictions = ParseRestrictions ()); + + std::unique_ptr<AST::LiteralExpr> parse_literal_expr (AST::AttrVec outer_attrs + = AST::AttrVec ()); + + std::unique_ptr<AST::BlockExpr> + parse_block_expr (AST::AttrVec outer_attrs = AST::AttrVec (), + Location pratt_parsed_loc = Linemap::unknown_location ()); + + std::unique_ptr<AST::Item> parse_item (bool called_from_statement); + std::unique_ptr<AST::Pattern> parse_pattern (); + std::unique_ptr<AST::Stmt> parse_stmt (); + std::unique_ptr<AST::Type> parse_type (); + AST::PathInExpression parse_path_in_expression (); + std::vector<std::unique_ptr<AST::LifetimeParam> > parse_lifetime_params (); + AST::Visibility parse_visibility (); + std::unique_ptr<AST::IdentifierPattern> parse_identifier_pattern (); + private: void skip_after_semicolon (); void skip_after_end (); @@ -93,7 +116,6 @@ private: void skip_after_next_block (); void skip_after_end_attribute (); - bool skip_token (TokenId t); const_TokenPtr expect_token (TokenId t); void unexpected_token (const_TokenPtr t); bool skip_generics_right_angle (); @@ -118,7 +140,6 @@ private: AST::GenericArgs parse_path_generic_args (); AST::GenericArgsBinding parse_generic_args_binding (); AST::TypePathFunction parse_type_path_function (); - AST::PathInExpression parse_path_in_expression (); AST::PathExprSegment parse_path_expr_segment (); AST::QualifiedPathInExpression // When given a pratt_parsed_loc, use it as the location of the @@ -147,10 +168,8 @@ private: std::unique_ptr<AST::MacroMatchRepetition> parse_macro_match_repetition (); // Top-level item-related - std::unique_ptr<AST::Item> parse_item (bool called_from_statement); std::unique_ptr<AST::VisItem> parse_vis_item (AST::AttrVec outer_attrs); std::unique_ptr<AST::MacroItem> parse_macro_item (AST::AttrVec outer_attrs); - AST::Visibility parse_visibility (); // VisItem subclass-related std::unique_ptr<AST::Module> parse_module (AST::Visibility vis, @@ -169,7 +188,7 @@ private: template <typename EndTokenPred> std::vector<std::unique_ptr<AST::GenericParam> > parse_generic_params (EndTokenPred is_end_token); - std::vector<std::unique_ptr<AST::LifetimeParam> > parse_lifetime_params (); + template <typename EndTokenPred> std::vector<std::unique_ptr<AST::LifetimeParam> > parse_lifetime_params (EndTokenPred is_end_token); @@ -260,9 +279,6 @@ private: // Expression-related (Pratt parsed) std::unique_ptr<AST::Expr> - parse_expr (AST::AttrVec outer_attrs = AST::AttrVec (), - ParseRestrictions restrictions = ParseRestrictions ()); - std::unique_ptr<AST::Expr> parse_expr (int right_binding_power, AST::AttrVec outer_attrs = AST::AttrVec (), ParseRestrictions restrictions = ParseRestrictions ()); @@ -478,9 +494,6 @@ private: // When given a pratt_parsed_loc, use it as the location of the // first token parsed in the expression (the parsing of that first // token should be skipped). - std::unique_ptr<AST::BlockExpr> - parse_block_expr (AST::AttrVec outer_attrs = AST::AttrVec (), - Location pratt_parsed_loc = Linemap::unknown_location ()); std::unique_ptr<AST::IfExpr> parse_if_expr (AST::AttrVec outer_attrs = AST::AttrVec (), Location pratt_parsed_loc = Linemap::unknown_location ()); @@ -518,8 +531,7 @@ private: std::unique_ptr<AST::ClosureExpr> parse_closure_expr (AST::AttrVec outer_attrs = AST::AttrVec ()); AST::ClosureParam parse_closure_param (); - std::unique_ptr<AST::LiteralExpr> parse_literal_expr (AST::AttrVec outer_attrs - = AST::AttrVec ()); + // When given a pratt_parsed_loc, use it as the location of the // first token parsed in the expression (the parsing of that first // token should be skipped). @@ -548,7 +560,6 @@ private: bool will_be_expr_with_block (); // Type-related - std::unique_ptr<AST::Type> parse_type (); std::unique_ptr<AST::TypeNoBounds> parse_type_no_bounds (); std::unique_ptr<AST::TypeNoBounds> parse_slice_or_array_type (); std::unique_ptr<AST::RawPointerType> parse_raw_pointer_type (); @@ -561,7 +572,6 @@ private: AST::MaybeNamedParam parse_maybe_named_param (AST::AttrVec outer_attrs); // Statement-related - std::unique_ptr<AST::Stmt> parse_stmt (); std::unique_ptr<AST::LetStmt> parse_let_stmt (AST::AttrVec outer_attrs); std::unique_ptr<AST::ExprStmt> parse_expr_stmt (AST::AttrVec outer_attrs); std::unique_ptr<AST::ExprStmtWithBlock> @@ -574,13 +584,11 @@ private: ExprOrStmt parse_path_based_stmt_or_expr (AST::AttrVec outer_attrs); // Pattern-related - std::unique_ptr<AST::Pattern> parse_pattern (); std::unique_ptr<AST::Pattern> parse_literal_or_range_pattern (); std::unique_ptr<AST::RangePatternBound> parse_range_pattern_bound (); std::unique_ptr<AST::ReferencePattern> parse_reference_pattern (); std::unique_ptr<AST::Pattern> parse_grouped_or_tuple_pattern (); std::unique_ptr<AST::SlicePattern> parse_slice_pattern (); - std::unique_ptr<AST::IdentifierPattern> parse_identifier_pattern (); std::unique_ptr<AST::Pattern> parse_ident_leading_pattern (); std::unique_ptr<AST::TupleStructItems> parse_tuple_struct_items (); AST::StructPatternElements parse_struct_pattern_elems (); @@ -617,6 +625,10 @@ public: // Get a reference to the list of errors encountered std::vector<Error> &get_errors () { return error_table; } + const ManagedTokenSource &get_token_source () const { return lexer; } + + const_TokenPtr peek_current_token () { return lexer.peek_token (0); } + private: // The token source (usually lexer) associated with the parser. ManagedTokenSource lexer; diff --git a/gcc/rust/resolve/rust-ast-resolve-expr.h b/gcc/rust/resolve/rust-ast-resolve-expr.h index b7b8646..bb1cbb0 100644 --- a/gcc/rust/resolve/rust-ast-resolve-expr.h +++ b/gcc/rust/resolve/rust-ast-resolve-expr.h @@ -70,6 +70,13 @@ public: expr->accept_vis (resolver); }; + void visit (AST::MacroInvocation &expr) override + { + AST::ASTFragment &fragment = expr.get_fragment (); + for (auto &node : fragment.get_nodes ()) + node.accept_vis (*this); + } + void visit (AST::TupleIndexExpr &expr) override { resolve_expr (expr.get_tuple_expr ().get (), expr.get_node_id ()); diff --git a/gcc/rust/resolve/rust-ast-resolve-implitem.h b/gcc/rust/resolve/rust-ast-resolve-implitem.h index f17b222..ce7234c 100644 --- a/gcc/rust/resolve/rust-ast-resolve-implitem.h +++ b/gcc/rust/resolve/rust-ast-resolve-implitem.h @@ -49,6 +49,13 @@ public: item->accept_vis (resolver); } + void visit (AST::MacroInvocationSemi &invoc) override + { + AST::ASTFragment &fragment = invoc.get_fragment (); + for (auto &node : fragment.get_nodes ()) + node.accept_vis (*this); + } + void visit (AST::TypeAlias &type) override { auto decl @@ -137,6 +144,13 @@ public: item->accept_vis (resolver); }; + void visit (AST::MacroInvocationSemi &invoc) override + { + AST::ASTFragment &fragment = invoc.get_fragment (); + for (auto &node : fragment.get_nodes ()) + node.accept_vis (*this); + } + void visit (AST::TraitItemFunc &function) override { auto decl = ResolveTraitItemFunctionToCanonicalPath::resolve (function); @@ -240,6 +254,13 @@ public: item->accept_vis (resolver); }; + void visit (AST::MacroInvocationSemi &invoc) override + { + AST::ASTFragment &fragment = invoc.get_fragment (); + for (auto &node : fragment.get_nodes ()) + node.accept_vis (*this); + } + void visit (AST::ExternalFunctionItem &function) override { auto decl = CanonicalPath::new_seg (function.get_node_id (), diff --git a/gcc/rust/resolve/rust-ast-resolve-item.h b/gcc/rust/resolve/rust-ast-resolve-item.h index 5d32c00..48f93e5 100644 --- a/gcc/rust/resolve/rust-ast-resolve-item.h +++ b/gcc/rust/resolve/rust-ast-resolve-item.h @@ -45,6 +45,13 @@ public: item->accept_vis (resolver); }; + void visit (AST::MacroInvocationSemi &invoc) override + { + AST::ASTFragment &fragment = invoc.get_fragment (); + for (auto &node : fragment.get_nodes ()) + node.accept_vis (*this); + } + void visit (AST::TraitItemType &type) override { auto decl = ResolveTraitItemTypeToCanonicalPath::resolve (type); @@ -227,6 +234,13 @@ public: item->accept_vis (resolver); }; + void visit (AST::MacroInvocationSemi &invoc) override + { + AST::ASTFragment &fragment = invoc.get_fragment (); + for (auto &node : fragment.get_nodes ()) + node.accept_vis (*this); + } + void visit (AST::TypeAlias &alias) override { auto talias = CanonicalPath::new_seg (alias.get_node_id (), diff --git a/gcc/rust/resolve/rust-ast-resolve-stmt.h b/gcc/rust/resolve/rust-ast-resolve-stmt.h index 3afed53..7521739 100644 --- a/gcc/rust/resolve/rust-ast-resolve-stmt.h +++ b/gcc/rust/resolve/rust-ast-resolve-stmt.h @@ -44,6 +44,14 @@ public: stmt->accept_vis (resolver); }; + void visit (AST::MacroInvocationSemi &invoc) override + { + AST::ASTFragment &fragment = invoc.get_fragment (); + + for (auto &node : fragment.get_nodes ()) + node.accept_vis (*this); + } + void visit (AST::ExprStmtWithBlock &stmt) override { ResolveExpr::go (stmt.get_expr ().get (), stmt.get_node_id (), prefix, diff --git a/gcc/rust/resolve/rust-ast-resolve-toplevel.h b/gcc/rust/resolve/rust-ast-resolve-toplevel.h index 7aba67f..39d6818 100644 --- a/gcc/rust/resolve/rust-ast-resolve-toplevel.h +++ b/gcc/rust/resolve/rust-ast-resolve-toplevel.h @@ -43,6 +43,13 @@ public: item->accept_vis (resolver); }; + void visit (AST::MacroInvocationSemi &invoc) override + { + AST::ASTFragment &fragment = invoc.get_fragment (); + for (auto &node : fragment.get_nodes ()) + node.accept_vis (*this); + } + void visit (AST::Module &module) override { auto mod diff --git a/gcc/rust/resolve/rust-ast-resolve.cc b/gcc/rust/resolve/rust-ast-resolve.cc index 8aef313..5ac076a 100644 --- a/gcc/rust/resolve/rust-ast-resolve.cc +++ b/gcc/rust/resolve/rust-ast-resolve.cc @@ -54,6 +54,7 @@ Resolver::Resolver () name_scope (Scope (mappings->get_current_crate ())), type_scope (Scope (mappings->get_current_crate ())), label_scope (Scope (mappings->get_current_crate ())), + macro_scope (Scope (mappings->get_current_crate ())), global_type_node_id (UNKNOWN_NODEID), unit_ty_node_id (UNKNOWN_NODEID) { generate_builtins (); @@ -93,6 +94,13 @@ Resolver::push_new_label_rib (Rib *r) label_ribs[r->get_node_id ()] = r; } +void +Resolver::push_new_macro_rib (Rib *r) +{ + rust_assert (label_ribs.find (r->get_node_id ()) == label_ribs.end ()); + macro_ribs[r->get_node_id ()] = r; +} + bool Resolver::find_name_rib (NodeId id, Rib **rib) { @@ -115,6 +123,17 @@ Resolver::find_type_rib (NodeId id, Rib **rib) return true; } +bool +Resolver::find_macro_rib (NodeId id, Rib **rib) +{ + auto it = macro_ribs.find (id); + if (it == macro_ribs.end ()) + return false; + + *rib = it->second; + return true; +} + void Resolver::insert_builtin_types (Rib *r) { @@ -281,6 +300,27 @@ Resolver::lookup_resolved_label (NodeId refId, NodeId *defId) return true; } +void +Resolver::insert_resolved_macro (NodeId refId, NodeId defId) +{ + auto it = resolved_macros.find (refId); + rust_assert (it == resolved_macros.end ()); + + resolved_labels[refId] = defId; + get_label_scope ().append_reference_for_def (refId, defId); +} + +bool +Resolver::lookup_resolved_macro (NodeId refId, NodeId *defId) +{ + auto it = resolved_macros.find (refId); + if (it == resolved_macros.end ()) + return false; + + *defId = it->second; + return true; +} + // NameResolution NameResolution * diff --git a/gcc/rust/resolve/rust-name-resolver.h b/gcc/rust/resolve/rust-name-resolver.h index 199b0f9..2084480 100644 --- a/gcc/rust/resolve/rust-name-resolver.h +++ b/gcc/rust/resolve/rust-name-resolver.h @@ -274,10 +274,12 @@ public: void push_new_name_rib (Rib *r); void push_new_type_rib (Rib *r); void push_new_label_rib (Rib *r); + void push_new_macro_rib (Rib *r); bool find_name_rib (NodeId id, Rib **rib); bool find_type_rib (NodeId id, Rib **rib); bool find_label_rib (NodeId id, Rib **rib); + bool find_macro_rib (NodeId id, Rib **rib); void insert_new_definition (NodeId id, Definition def); bool lookup_definition (NodeId id, Definition *def); @@ -291,10 +293,14 @@ public: void insert_resolved_label (NodeId refId, NodeId defId); bool lookup_resolved_label (NodeId refId, NodeId *defId); + void insert_resolved_macro (NodeId refId, NodeId defId); + bool lookup_resolved_macro (NodeId refId, NodeId *defId); + // proxy for scoping Scope &get_name_scope () { return name_scope; } Scope &get_type_scope () { return type_scope; } Scope &get_label_scope () { return label_scope; } + Scope &get_macro_scope () { return macro_scope; } NodeId get_global_type_node_id () { return global_type_node_id; } @@ -371,6 +377,7 @@ private: Scope name_scope; Scope type_scope; Scope label_scope; + Scope macro_scope; NodeId global_type_node_id; NodeId unit_ty_node_id; @@ -379,6 +386,7 @@ private: std::map<NodeId, Rib *> name_ribs; std::map<NodeId, Rib *> type_ribs; std::map<NodeId, Rib *> label_ribs; + std::map<NodeId, Rib *> macro_ribs; // map any Node to its Definition // ie any name or type usage @@ -395,6 +403,7 @@ private: std::map<NodeId, NodeId> resolved_names; std::map<NodeId, NodeId> resolved_types; std::map<NodeId, NodeId> resolved_labels; + std::map<NodeId, NodeId> resolved_macros; // map of resolved names mutability flag std::map<NodeId, bool> decl_mutability; diff --git a/gcc/rust/typecheck/rust-tycheck-dump.h b/gcc/rust/typecheck/rust-tycheck-dump.h index 0bcc6c8..53daa42 100644 --- a/gcc/rust/typecheck/rust-tycheck-dump.h +++ b/gcc/rust/typecheck/rust-tycheck-dump.h @@ -57,6 +57,12 @@ public: += indent () + "union " + type_string (union_decl.get_mappings ()) + "\n"; } + void visit (HIR::TupleStruct &struct_decl) override + { + dump += indent () + "struct" + type_string (struct_decl.get_mappings ()) + + "\n"; + } + void visit (HIR::ImplBlock &impl_block) override { dump += indent () + "impl " diff --git a/gcc/rust/util/rust-hir-map.cc b/gcc/rust/util/rust-hir-map.cc index 04db333..1348e29 100644 --- a/gcc/rust/util/rust-hir-map.cc +++ b/gcc/rust/util/rust-hir-map.cc @@ -738,5 +738,25 @@ Mappings::iterate_trait_items ( } } +void +Mappings::insert_macro_def (AST::MacroRulesDefinition *macro) +{ + auto it = macroMappings.find (macro->get_node_id ()); + rust_assert (it == macroMappings.end ()); + + macroMappings[macro->get_node_id ()] = macro; +} + +bool +Mappings::lookup_macro_def (NodeId id, AST::MacroRulesDefinition **def) +{ + auto it = macroMappings.find (id); + if (it == macroMappings.end ()) + return false; + + *def = it->second; + return true; +} + } // namespace Analysis } // namespace Rust diff --git a/gcc/rust/util/rust-hir-map.h b/gcc/rust/util/rust-hir-map.h index 8781629..799351b 100644 --- a/gcc/rust/util/rust-hir-map.h +++ b/gcc/rust/util/rust-hir-map.h @@ -559,6 +559,10 @@ public: return true; } + void insert_macro_def (AST::MacroRulesDefinition *macro); + + bool lookup_macro_def (NodeId id, AST::MacroRulesDefinition **def); + private: Mappings (); @@ -612,6 +616,9 @@ private: // all hirid nodes std::map<CrateNum, std::set<HirId>> hirNodesWithinCrate; + // macros + std::map<NodeId, AST::MacroRulesDefinition *> macroMappings; + // crate names std::map<CrateNum, std::string> crate_names; }; diff --git a/gcc/testsuite/rust/execute/torture/macros1.rs b/gcc/testsuite/rust/execute/torture/macros1.rs new file mode 100644 index 0000000..652d2d8 --- /dev/null +++ b/gcc/testsuite/rust/execute/torture/macros1.rs @@ -0,0 +1,13 @@ +macro_rules! add { + ($a:expr,$b:expr) => { + $a + $b + }; +} + +fn test() -> i32 { + add!(1 + 2, 3) +} + +fn main() -> i32 { + test() - 6 +} diff --git a/gcc/testsuite/rust/execute/torture/macros2.rs b/gcc/testsuite/rust/execute/torture/macros2.rs new file mode 100644 index 0000000..0116bd1 --- /dev/null +++ b/gcc/testsuite/rust/execute/torture/macros2.rs @@ -0,0 +1,40 @@ +// { dg-output "arg\narg\narg\n" } +extern "C" { + fn printf(s: *const i8, ...); +} + +fn f() { + unsafe { + let r_s = "arg\n\0"; + let s_p = r_s as *const str; + let c_p = s_p as *const i8; + + printf(c_p); + } +} + +macro_rules! kw0 { + (keyword) => { + f() + }; +} + +macro_rules! kw1 { + (fn) => { + f() + }; +} + +macro_rules! kw2 { + (kw0 kw1 kw3) => { + f() + }; +} + +fn main() -> i32 { + kw0!(keyword); + kw1!(fn); + kw2!(kw0 kw1 kw3); + + 0 +} diff --git a/gcc/testsuite/rust/execute/torture/macros3.rs b/gcc/testsuite/rust/execute/torture/macros3.rs new file mode 100644 index 0000000..c1f5a53 --- /dev/null +++ b/gcc/testsuite/rust/execute/torture/macros3.rs @@ -0,0 +1,61 @@ +// { dg-output "invok\ninvok\ninvok\ninvok\ninvok\n" } +extern "C" { + fn printf(s: *const i8, ...); +} + +fn f() { + unsafe { + let r_s = "invok\n\0"; + let s_p = r_s as *const str; + let c_p = s_p as *const i8; + + printf(c_p); + } +} + +macro_rules! invocation0 { + (valid) => { + f() + }; + () => {}; +} + +macro_rules! invocation1 { + (valid) => {}; + () => { + f() + }; +} + +macro_rules! invocation2 { + (valid) => { + f() + }; + (invalid) => {}; +} + +macro_rules! invocation3 { + (this is a valid invocation) => { + f() + }; + (not this one) => {}; +} + +macro_rules! invocation4 { + (fn f() {}) => { + f() + }; + (not a keyword) => {}; +} + +fn main() -> i32 { + invocation0!(valid); + invocation1!(); + invocation2!(valid); + invocation3!(this is a valid invocation); + invocation4!( + fn f() {} + ); + + 0 +} diff --git a/gcc/testsuite/rust/execute/torture/macros4.rs b/gcc/testsuite/rust/execute/torture/macros4.rs new file mode 100644 index 0000000..3303bfa --- /dev/null +++ b/gcc/testsuite/rust/execute/torture/macros4.rs @@ -0,0 +1,15 @@ +macro_rules! add { + ($a:expr,$b:expr) => { + $a + $b + }; + ($a:expr) => { + $a + }; +} + +fn main() -> i32 { + let mut x = add!(1); + x += add!(2, 3); + + x - 6 +} diff --git a/gcc/testsuite/rust/execute/torture/macros5.rs b/gcc/testsuite/rust/execute/torture/macros5.rs new file mode 100644 index 0000000..8226654 --- /dev/null +++ b/gcc/testsuite/rust/execute/torture/macros5.rs @@ -0,0 +1,13 @@ +macro_rules! add { + ($a:expr,$b:expr) => {{ + $a + $b + }}; +} + +fn test() -> i32 { + add!(1, 2) +} + +fn main() -> i32 { + test() - 3 +} diff --git a/gcc/testsuite/rust/execute/torture/macros6.rs b/gcc/testsuite/rust/execute/torture/macros6.rs new file mode 100644 index 0000000..652a765 --- /dev/null +++ b/gcc/testsuite/rust/execute/torture/macros6.rs @@ -0,0 +1,12 @@ +macro_rules! Test { + ($a:ident, $b:ty) => { + struct $a($b); + }; +} + +Test!(Foo, i32); + +fn main() -> i32 { + let a = Foo(123); + a.0 - 123 +} diff --git a/gcc/testsuite/rust/execute/xfail/macro2.rs b/gcc/testsuite/rust/execute/xfail/macro2.rs deleted file mode 100644 index 49bd6a8..0000000 --- a/gcc/testsuite/rust/execute/xfail/macro2.rs +++ /dev/null @@ -1,30 +0,0 @@ -// { dg-output "arg\narg\n" } -extern "C" { - fn printf(s: *const i8, ...); -} - -fn f() { - let r_s = "arg\n\0"; - let s_p = r_s as *const str; - let c_p = s_p as *const i8; - - printf(c_p); -} - -macro_rules! kw0 { - (keyword) => { f() }; -} - -macro_rules! kw1 { - (fn) => { f() }; -} - -macro_rules! kw2 { - (kw0 kw1 kw3) => { f() }; -} - -fn main() { - kw0!(keyword); - kw1!(fn); - kw2!(kw0 kw1 kw3); -} diff --git a/gcc/testsuite/rust/execute/xfail/macro3.rs b/gcc/testsuite/rust/execute/xfail/macro3.rs deleted file mode 100644 index 0d99d716..0000000 --- a/gcc/testsuite/rust/execute/xfail/macro3.rs +++ /dev/null @@ -1,45 +0,0 @@ -// { dg-output "invok\ninvok\ninvok\ninvok\ninvok\n" } -extern "C" { - fn printf(s: *const i8, ...); -} - -fn f() { - let r_s = "invok\n\0"; - let s_p = r_s as *const str; - let c_p = s_p as *const i8; - - printf(c_p); -} - -macro_rules! invocation0 { - (valid) => { f() }; - () => { }; -} - -macro_rules! invocation1 { - (valid) => { }; - () => { f() }; -} - -macro_rules! invocation2 { - (valid) => { f() }; - (invalid) => { }; -} - -macro_rules! invocation3 { - (this is a valid invocation) => { f() }; - (not this one) => { }; -} - -macro_rules! invocation4 { - (fn f() {}) => { f() }; - (not a keyword) => { }; -} - -fn main() { - invocation0!(valid); - invocation1!(); - invocation2!(valid); - invocation3!(this is a valid invocation); - invocation4!(fn f() {}); -} |