diff --git a/gcc/rust/expand/rust-macro-expand.cc b/gcc/rust/expand/rust-macro-expand.cc index fab7f56de313..4c6e897cd64b 100644 --- a/gcc/rust/expand/rust-macro-expand.cc +++ b/gcc/rust/expand/rust-macro-expand.cc @@ -3190,7 +3190,7 @@ MacroExpander::expand_decl_macro (Location invoc_locus, // find matching arm AST::MacroRule *matched_rule = nullptr; - std::map> matched_fragments; + std::map matched_fragments; for (auto &rule : rules_def.get_rules ()) { sub_stack.push (); @@ -3199,9 +3199,10 @@ MacroExpander::expand_decl_macro (Location invoc_locus, if (did_match_rule) { - for (auto &kv : matched_fragments) - rust_debug ("[fragment]: %s (%ld)", kv.first.c_str (), - kv.second.size ()); + // Debugging + // for (auto &kv : matched_fragments) + // rust_debug ("[fragment]: %s (%ld)", kv.first.c_str (), + // kv.second.get_fragments ().size ()); matched_rule = &rule; break; @@ -3825,7 +3826,10 @@ MacroExpander::match_repetition (Parser &parser, rust_debug_loc (rep.get_match_locus (), "%s matched %lu times", res ? "successfully" : "unsuccessfully", match_amount); - // We can now set the amount to each fragment we matched in the substack + // We have to handle zero fragments differently: They will not have been + // "matched" but they are still valid and should be inserted as a special + // case. So we go through the stack map, and for every fragment which doesn't + // exist, insert a zero-matched fragment. auto &stack_map = sub_stack.peek (); for (auto &match : rep.get_matches ()) { @@ -3835,20 +3839,9 @@ MacroExpander::match_repetition (Parser &parser, auto fragment = static_cast (match.get ()); auto it = stack_map.find (fragment->get_ident ()); - // If we can't find the fragment, but the result was valid, then - // it's a zero-matched fragment and we can insert it if (it == stack_map.end ()) - { - sub_stack.insert_fragment ( - MatchedFragment::zero (fragment->get_ident ())); - } - else - { - // We can just set the repetition amount on the first match - // FIXME: Make this more ergonomic and similar to what we fetch - // in `substitute_repetition` - it->second[0].set_match_amount (match_amount); - } + sub_stack.insert_matches (fragment->get_ident (), + MatchedFragmentContainer::zero ()); } } @@ -3925,7 +3918,7 @@ transcribe_expression (Parser &parser) AST::ASTFragment MacroExpander::transcribe_rule ( AST::MacroRule &match_rule, AST::DelimTokenTree &invoc_token_tree, - std::map> &matched_fragments, + std::map &matched_fragments, bool semicolon, ContextType ctx) { // we can manipulate the token tree to substitute the dollar identifiers so diff --git a/gcc/rust/expand/rust-macro-expand.h b/gcc/rust/expand/rust-macro-expand.h index 3433e646664f..4efcd7bab989 100644 --- a/gcc/rust/expand/rust-macro-expand.h +++ b/gcc/rust/expand/rust-macro-expand.h @@ -54,32 +54,78 @@ struct MatchedFragment std::string fragment_ident; size_t token_offset_begin; size_t token_offset_end; - size_t match_amount; MatchedFragment (std::string identifier, size_t token_offset_begin, - size_t token_offset_end, size_t match_amount = 1) + size_t token_offset_end) : fragment_ident (identifier), token_offset_begin (token_offset_begin), - token_offset_end (token_offset_end), match_amount (match_amount) + token_offset_end (token_offset_end) + {} + + /** + * Empty constructor for uninitialized fragments + */ + MatchedFragment () : MatchedFragment ("", 0, 0) {} + + std::string as_string () const + { + return fragment_ident + "=" + std::to_string (token_offset_begin) + ":" + + std::to_string (token_offset_end); + } +}; + +class MatchedFragmentContainer +{ +public: + MatchedFragmentContainer (std::vector fragments) + : fragments (fragments) {} /** * Create a valid fragment matched zero times. This is useful for repetitions * which allow the absence of a fragment, such as * and ? */ - static MatchedFragment zero (std::string identifier) + static MatchedFragmentContainer zero () { - // We don't need offsets since there is "no match" - return MatchedFragment (identifier, 0, 0, 0); + return MatchedFragmentContainer ({}); } - std::string as_string () const + /** + * Create a valid fragment matched one time + */ + static MatchedFragmentContainer one (MatchedFragment fragment) { - return fragment_ident + "=" + std::to_string (token_offset_begin) + ":" - + std::to_string (token_offset_end) + " (matched " - + std::to_string (match_amount) + " times)"; + return MatchedFragmentContainer ({fragment}); } - void set_match_amount (size_t new_amount) { match_amount = new_amount; } + /** + * Add a matched fragment to the container + */ + void add_fragment (MatchedFragment fragment) + { + fragments.emplace_back (fragment); + } + + size_t get_match_amount () const { return fragments.size (); } + const std::vector &get_fragments () const + { + return fragments; + } + // const std::string &get_fragment_name () const { return fragment_name; } + + bool is_single_fragment () const { return get_match_amount () == 1; } + const MatchedFragment get_single_fragment () const + { + rust_assert (get_match_amount () == 1); + + return fragments[0]; + } + +private: + /** + * Fragments matched `match_amount` times. This can be an empty vector + * in case having zero matches is allowed (i.e ? or * operators) + */ + std::vector fragments; }; class SubstitutionScope @@ -89,18 +135,21 @@ class SubstitutionScope void push () { stack.push_back ({}); } - std::map> pop () + std::map pop () { auto top = stack.back (); stack.pop_back (); return top; } - std::map> &peek () + std::map &peek () { return stack.back (); } + /** + * Insert a new matched fragment into the current substitution map + */ void insert_fragment (MatchedFragment fragment) { auto ¤t_map = stack.back (); @@ -108,19 +157,27 @@ class SubstitutionScope if (it == current_map.end ()) { - auto new_frags = std::vector (); - new_frags.emplace_back (fragment); - current_map.insert ({fragment.fragment_ident, new_frags}); + current_map.insert ( + {fragment.fragment_ident, MatchedFragmentContainer::one (fragment)}); } else { auto &frags = it->second; - frags.emplace_back (fragment); + frags.add_fragment (fragment); } } + void insert_matches (std::string key, MatchedFragmentContainer matches) + { + auto ¤t_map = stack.back (); + auto it = current_map.find (key); + rust_assert (it == current_map.end ()); + + current_map.insert ({key, matches}); + } + private: - std::vector>> stack; + std::vector> stack; }; // Object used to store shared data (between functions) for macro expansion. @@ -176,7 +233,7 @@ struct MacroExpander AST::ASTFragment transcribe_rule ( AST::MacroRule &match_rule, AST::DelimTokenTree &invoc_token_tree, - std::map> &matched_fragments, + std::map &matched_fragments, bool semicolon, ContextType ctx); bool match_fragment (Parser &parser, diff --git a/gcc/rust/expand/rust-macro-substitute-ctx.cc b/gcc/rust/expand/rust-macro-substitute-ctx.cc index 61ab626f101e..392a9fe42f1d 100644 --- a/gcc/rust/expand/rust-macro-substitute-ctx.cc +++ b/gcc/rust/expand/rust-macro-substitute-ctx.cc @@ -16,11 +16,9 @@ SubstituteCtx::substitute_metavar (std::unique_ptr &metavar) } else { - // Replace // We only care about the vector when expanding repetitions. // Just access the first element of the vector. - // FIXME: Clean this up so it makes more sense - auto &frag = it->second[0]; + auto &frag = it->second.get_single_fragment (); for (size_t offs = frag.token_offset_begin; offs < frag.token_offset_end; offs++) { @@ -66,7 +64,7 @@ SubstituteCtx::substitute_repetition ( } // FIXME: Refactor, ugly - repeat_amount = it->second[0].match_amount; + repeat_amount = it->second.get_match_amount (); } } } @@ -98,19 +96,20 @@ SubstituteCtx::substitute_repetition ( for (size_t i = 0; i < repeat_amount; i++) { - std::map> sub_map; + std::map sub_map; for (auto &kv_match : fragments) { - std::vector sub_vec; + MatchedFragment sub_fragment; // FIXME: Hack: If a fragment is not repeated, how does it fit in the // submap? Do we really want to expand it? Is this normal behavior? - if (kv_match.second.size () == 1) - sub_vec.emplace_back (kv_match.second[0]); + if (kv_match.second.is_single_fragment ()) + sub_fragment = kv_match.second.get_single_fragment (); else - sub_vec.emplace_back (kv_match.second[i]); + sub_fragment = kv_match.second.get_fragments ()[i]; - sub_map.insert ({kv_match.first, sub_vec}); + sub_map.insert ( + {kv_match.first, MatchedFragmentContainer::one (sub_fragment)}); } auto substitute_context = SubstituteCtx (input, new_macro, sub_map); diff --git a/gcc/rust/expand/rust-macro-substitute-ctx.h b/gcc/rust/expand/rust-macro-substitute-ctx.h index ed83926c32ce..e89f9d788a1e 100644 --- a/gcc/rust/expand/rust-macro-substitute-ctx.h +++ b/gcc/rust/expand/rust-macro-substitute-ctx.h @@ -24,12 +24,12 @@ class SubstituteCtx { std::vector> &input; std::vector> ¯o; - std::map> &fragments; + std::map &fragments; public: SubstituteCtx (std::vector> &input, std::vector> ¯o, - std::map> &fragments) + std::map &fragments) : input (input), macro (macro), fragments (fragments) {}