(root)/
gcc-13.2.0/
gcc/
rust/
expand/
rust-macro-expand.h
       1  // Copyright (C) 2020-2023 Free Software Foundation, Inc.
       2  
       3  // This file is part of GCC.
       4  
       5  // GCC is free software; you can redistribute it and/or modify it under
       6  // the terms of the GNU General Public License as published by the Free
       7  // Software Foundation; either version 3, or (at your option) any later
       8  // version.
       9  
      10  // GCC is distributed in the hope that it will be useful, but WITHOUT ANY
      11  // WARRANTY; without even the implied warranty of MERCHANTABILITY or
      12  // FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
      13  // for more details.
      14  
      15  // You should have received a copy of the GNU General Public License
      16  // along with GCC; see the file COPYING3.  If not see
      17  // <http://www.gnu.org/licenses/>.
      18  
      19  #ifndef RUST_MACRO_EXPAND_H
      20  #define RUST_MACRO_EXPAND_H
      21  
      22  #include "rust-buffered-queue.h"
      23  #include "rust-parse.h"
      24  #include "rust-token.h"
      25  #include "rust-ast.h"
      26  #include "rust-macro.h"
      27  #include "rust-hir-map.h"
      28  #include "rust-early-name-resolver.h"
      29  #include "rust-name-resolver.h"
      30  #include "rust-macro-invoc-lexer.h"
      31  
      32  // Provides objects and method prototypes for macro expansion
      33  
      34  namespace Rust {
      35  // forward decls for AST
      36  namespace AST {
      37  class MacroInvocation;
      38  }
      39  
      40  // Object used to store configuration data for macro expansion.
      41  // NOTE: Keep all these items complying with the latest rustc.
      42  struct ExpansionCfg
      43  {
      44    // features?
      45    // TODO: Add `features' when we have it.
      46    unsigned int recursion_limit = 1024;
      47    bool trace_mac = false;   // trace macro
      48    bool should_test = false; // strip #[test] nodes if false
      49    bool keep_macs = false;   // keep macro definitions
      50    std::string crate_name = "";
      51  };
      52  
      53  struct MatchedFragment
      54  {
      55    std::string fragment_ident;
      56    size_t token_offset_begin;
      57    size_t token_offset_end;
      58  
      59    MatchedFragment (std::string identifier, size_t token_offset_begin,
      60  		   size_t token_offset_end)
      61      : fragment_ident (identifier), token_offset_begin (token_offset_begin),
      62        token_offset_end (token_offset_end)
      63    {}
      64  
      65    /**
      66     * Empty constructor for uninitialized fragments
      67     */
      68    MatchedFragment () : MatchedFragment ("", 0, 0) {}
      69  
      70    std::string as_string () const
      71    {
      72      return fragment_ident + "=" + std::to_string (token_offset_begin) + ":"
      73  	   + std::to_string (token_offset_end);
      74    }
      75  };
      76  
      77  class MatchedFragmentContainer
      78  {
      79  public:
      80    // Does the container refer to a simple metavariable, different from a
      81    // repetition repeated once
      82    enum class Kind
      83    {
      84      MetaVar,
      85      Repetition,
      86    };
      87  
      88    MatchedFragmentContainer (std::vector<MatchedFragment> fragments,
      89  			    Kind kind = Kind::Repetition)
      90      : fragments (fragments), kind (kind)
      91    {}
      92  
      93    /**
      94     * Create a valid fragment matched zero times. This is useful for repetitions
      95     * which allow the absence of a fragment, such as * and ?
      96     */
      97    static MatchedFragmentContainer zero ()
      98    {
      99      return MatchedFragmentContainer ({});
     100    }
     101  
     102    /**
     103     * Create a valid fragment matched one time
     104     */
     105    static MatchedFragmentContainer metavar (MatchedFragment fragment)
     106    {
     107      return MatchedFragmentContainer ({fragment}, Kind::MetaVar);
     108    }
     109  
     110    /**
     111     * Add a matched fragment to the container
     112     */
     113    void add_fragment (MatchedFragment fragment)
     114    {
     115      rust_assert (!is_single_fragment ());
     116  
     117      fragments.emplace_back (fragment);
     118    }
     119  
     120    size_t get_match_amount () const { return fragments.size (); }
     121    const std::vector<MatchedFragment> &get_fragments () const
     122    {
     123      return fragments;
     124    }
     125    // const std::string &get_fragment_name () const { return fragment_name; }
     126  
     127    bool is_single_fragment () const
     128    {
     129      return get_match_amount () == 1 && kind == Kind::MetaVar;
     130    }
     131  
     132    const MatchedFragment get_single_fragment () const
     133    {
     134      rust_assert (is_single_fragment ());
     135  
     136      return fragments[0];
     137    }
     138  
     139    const Kind &get_kind () const { return kind; }
     140  
     141  private:
     142    /**
     143     * Fragments matched `match_amount` times. This can be an empty vector
     144     * in case having zero matches is allowed (i.e ? or * operators)
     145     */
     146    std::vector<MatchedFragment> fragments;
     147    Kind kind;
     148  };
     149  
     150  class SubstitutionScope
     151  {
     152  public:
     153    SubstitutionScope () : stack () {}
     154  
     155    void push () { stack.push_back ({}); }
     156  
     157    std::map<std::string, MatchedFragmentContainer> pop ()
     158    {
     159      auto top = stack.back ();
     160      stack.pop_back ();
     161      return top;
     162    }
     163  
     164    std::map<std::string, MatchedFragmentContainer> &peek ()
     165    {
     166      return stack.back ();
     167    }
     168  
     169    /**
     170     * Insert a new matched metavar into the current substitution map
     171     */
     172    void insert_metavar (MatchedFragment fragment)
     173    {
     174      auto &current_map = stack.back ();
     175      auto it = current_map.find (fragment.fragment_ident);
     176  
     177      if (it == current_map.end ())
     178        current_map.insert ({fragment.fragment_ident,
     179  			   MatchedFragmentContainer::metavar (fragment)});
     180      else
     181        gcc_unreachable ();
     182    }
     183  
     184    /**
     185     * Append a new matched fragment to a repetition into the current substitution
     186     * map
     187     */
     188    void append_fragment (MatchedFragment fragment)
     189    {
     190      auto &current_map = stack.back ();
     191      auto it = current_map.find (fragment.fragment_ident);
     192  
     193      if (it == current_map.end ())
     194        current_map.insert (
     195  	{fragment.fragment_ident, MatchedFragmentContainer ({fragment})});
     196      else
     197        it->second.add_fragment (fragment);
     198    }
     199  
     200    void insert_matches (std::string key, MatchedFragmentContainer matches)
     201    {
     202      auto &current_map = stack.back ();
     203      auto it = current_map.find (key);
     204      rust_assert (it == current_map.end ());
     205  
     206      current_map.insert ({key, matches});
     207    }
     208  
     209  private:
     210    std::vector<std::map<std::string, MatchedFragmentContainer>> stack;
     211  };
     212  
     213  // Object used to store shared data (between functions) for macro expansion.
     214  struct MacroExpander
     215  {
     216    enum ContextType
     217    {
     218      ITEM,
     219      BLOCK,
     220      EXTERN,
     221      TYPE,
     222      TRAIT,
     223      IMPL,
     224      TRAIT_IMPL,
     225    };
     226  
     227    ExpansionCfg cfg;
     228    unsigned int expansion_depth = 0;
     229  
     230    MacroExpander (AST::Crate &crate, ExpansionCfg cfg, Session &session)
     231      : cfg (cfg), crate (crate), session (session),
     232        sub_stack (SubstitutionScope ()),
     233        expanded_fragment (AST::Fragment::create_error ()),
     234        has_changed_flag (false), resolver (Resolver::Resolver::get ()),
     235        mappings (Analysis::Mappings::get ())
     236    {}
     237  
     238    ~MacroExpander () = default;
     239  
     240    // Expands all macros in the crate passed in.
     241    void expand_crate ();
     242  
     243    /**
     244     * Expand the eager invocations contained within a builtin macro invocation.
     245     * Called by `expand_invoc` when expanding builtin invocations.
     246     */
     247    void expand_eager_invocations (AST::MacroInvocation &invoc);
     248  
     249    /* Expands a macro invocation - possibly make both
     250     * have similar duck-typed interface and use templates?*/
     251    // should this be public or private?
     252    void expand_invoc (AST::MacroInvocation &invoc, bool has_semicolon);
     253  
     254    // Expands a single declarative macro.
     255    AST::Fragment expand_decl_macro (Location locus, AST::MacroInvocData &invoc,
     256  				   AST::MacroRulesDefinition &rules_def,
     257  				   bool semicolon);
     258  
     259    void expand_cfg_attrs (AST::AttrVec &attrs);
     260    bool fails_cfg (const AST::AttrVec &attr) const;
     261    bool fails_cfg_with_expand (AST::AttrVec &attrs) const;
     262  
     263    bool depth_exceeds_recursion_limit () const;
     264  
     265    bool try_match_rule (AST::MacroRule &match_rule,
     266  		       AST::DelimTokenTree &invoc_token_tree);
     267  
     268    AST::Fragment transcribe_rule (
     269      AST::MacroRule &match_rule, AST::DelimTokenTree &invoc_token_tree,
     270      std::map<std::string, MatchedFragmentContainer> &matched_fragments,
     271      bool semicolon, ContextType ctx);
     272  
     273    bool match_fragment (Parser<MacroInvocLexer> &parser,
     274  		       AST::MacroMatchFragment &fragment);
     275  
     276    bool match_token (Parser<MacroInvocLexer> &parser, AST::Token &token);
     277  
     278    bool match_repetition (Parser<MacroInvocLexer> &parser,
     279  			 AST::MacroMatchRepetition &rep);
     280  
     281    bool match_matcher (Parser<MacroInvocLexer> &parser,
     282  		      AST::MacroMatcher &matcher, bool in_repetition = false);
     283  
     284    /**
     285     * Match any amount of matches
     286     *
     287     * @param parser Parser to use for matching
     288     * @param rep Repetition to try and match
     289     * @param match_amount Reference in which to store the ammount of succesful
     290     * and valid matches
     291     *
     292     * @param lo_bound Lower bound of the matcher. When specified, the matcher
     293     * will only succeed if it parses at *least* `lo_bound` fragments. If
     294     * unspecified, the matcher could succeed when parsing 0 fragments.
     295     *
     296     * @param hi_bound Higher bound of the matcher. When specified, the matcher
     297     * will only succeed if it parses *less than* `hi_bound` fragments. If
     298     * unspecified, the matcher could succeed when parsing an infinity of
     299     * fragments.
     300     *
     301     * @return true if matching was successful and within the given limits, false
     302     * otherwise
     303     */
     304    bool match_n_matches (Parser<MacroInvocLexer> &parser,
     305  			AST::MacroMatchRepetition &rep, size_t &match_amount,
     306  			size_t lo_bound = 0, size_t hi_bound = 0);
     307  
     308    void push_context (ContextType t) { context.push_back (t); }
     309  
     310    ContextType pop_context ()
     311    {
     312      rust_assert (!context.empty ());
     313  
     314      ContextType t = context.back ();
     315      context.pop_back ();
     316  
     317      return t;
     318    }
     319  
     320    ContextType peek_context () { return context.back (); }
     321  
     322    void set_expanded_fragment (AST::Fragment &&fragment)
     323    {
     324      if (!fragment.is_error ())
     325        has_changed_flag = true;
     326  
     327      expanded_fragment = std::move (fragment);
     328    }
     329  
     330    AST::Fragment take_expanded_fragment ()
     331    {
     332      auto fragment = std::move (expanded_fragment);
     333      expanded_fragment = AST::Fragment::create_error ();
     334  
     335      return fragment;
     336    }
     337  
     338    /**
     339     * Has the MacroExpander expanded a macro since its state was last reset?
     340     */
     341    bool has_changed () const { return has_changed_flag; }
     342  
     343    /**
     344     * Reset the expander's "changed" state. This function should be executed at
     345     * each iteration in a fixed point loop
     346     */
     347    void reset_changed_state () { has_changed_flag = false; }
     348  
     349    AST::MacroRulesDefinition *get_last_definition () { return last_def; }
     350    AST::MacroInvocation *get_last_invocation () { return last_invoc; }
     351  
     352  private:
     353    AST::Crate &crate;
     354    Session &session;
     355    SubstitutionScope sub_stack;
     356    std::vector<ContextType> context;
     357    AST::Fragment expanded_fragment;
     358    bool has_changed_flag;
     359  
     360    AST::MacroRulesDefinition *last_def;
     361    AST::MacroInvocation *last_invoc;
     362  
     363  public:
     364    Resolver::Resolver *resolver;
     365    Analysis::Mappings *mappings;
     366  };
     367  
     368  } // namespace Rust
     369  
     370  #endif