Require target lra in gcc.dg/pr108095.c
[official-gcc.git] / gcc / rust / expand / rust-macro-substitute-ctx.cc
blob9592d2d2a9e178469949dacede177c539e854567
1 #include "rust-macro-substitute-ctx.h"
3 namespace Rust {
5 std::vector<std::unique_ptr<AST::Token>>
6 SubstituteCtx::substitute_metavar (std::unique_ptr<AST::Token> &metavar)
8 auto metavar_name = metavar->get_str ();
10 std::vector<std::unique_ptr<AST::Token>> expanded;
11 auto it = fragments.find (metavar_name);
12 if (it == fragments.end ())
14 // Return a copy of the original token
15 expanded.push_back (metavar->clone_token ());
17 else
19 // If we are expanding a metavar which has a lof of matches, we are
20 // currently expanding a repetition metavar - not a simple metavar. We
21 // need to error out and inform the user.
22 // Associated test case for an example: compile/macro-issue1224.rs
23 if (it->second.get_match_amount () != 1)
25 rust_error_at (metavar->get_locus (),
26 "metavariable is still repeating at this depth");
27 rust_inform (
28 metavar->get_locus (),
29 "you probably forgot the repetition operator: %<%s%s%s%>", "$(",
30 metavar->as_string ().c_str (), ")*");
31 return expanded;
34 // We only care about the vector when expanding repetitions.
35 // Just access the first element of the vector.
36 auto &frag = it->second.get_single_fragment ();
37 for (size_t offs = frag.token_offset_begin; offs < frag.token_offset_end;
38 offs++)
40 auto &tok = input.at (offs);
41 expanded.push_back (tok->clone_token ());
45 return expanded;
48 bool
49 SubstituteCtx::check_repetition_amount (size_t pattern_start,
50 size_t pattern_end,
51 size_t &expected_repetition_amount)
53 bool first_fragment_found = false;
54 bool is_valid = true;
56 for (size_t i = pattern_start; i < pattern_end; i++)
58 if (macro.at (i)->get_id () == DOLLAR_SIGN)
60 auto &frag_token = macro.at (i + 1);
61 if (frag_token->get_id () == IDENTIFIER)
63 auto it = fragments.find (frag_token->get_str ());
64 if (it == fragments.end ())
66 // If the repetition is not anything we know (ie no declared
67 // metavars, or metavars which aren't present in the
68 // fragment), we can just error out. No need to paste the
69 // tokens as if nothing had happened.
70 rust_error_at (frag_token->get_locus (),
71 "metavar %s used in repetition does not exist",
72 frag_token->get_str ().c_str ());
74 is_valid = false;
77 auto &fragment = it->second;
79 size_t repeat_amount = fragment.get_match_amount ();
80 if (!first_fragment_found)
82 first_fragment_found = true;
83 expected_repetition_amount = repeat_amount;
85 else
87 if (repeat_amount != expected_repetition_amount
88 && !fragment.is_single_fragment ())
90 rust_error_at (
91 frag_token->get_locus (),
92 "different amount of matches used in merged "
93 "repetitions: expected %lu, got %lu",
94 (unsigned long) expected_repetition_amount,
95 (unsigned long) repeat_amount);
96 is_valid = false;
103 return is_valid;
106 std::vector<std::unique_ptr<AST::Token>>
107 SubstituteCtx::substitute_repetition (
108 size_t pattern_start, size_t pattern_end,
109 std::unique_ptr<AST::Token> separator_token)
111 rust_assert (pattern_end < macro.size ());
113 size_t repeat_amount = 0;
114 if (!check_repetition_amount (pattern_start, pattern_end, repeat_amount))
115 return {};
117 rust_debug ("repetition amount to use: %lu", (unsigned long) repeat_amount);
118 std::vector<std::unique_ptr<AST::Token>> expanded;
119 std::vector<std::unique_ptr<AST::Token>> new_macro;
121 // We want to generate a "new macro" to substitute with. This new macro
122 // should contain only the tokens inside the pattern
123 for (size_t tok_idx = pattern_start; tok_idx < pattern_end; tok_idx++)
124 new_macro.emplace_back (macro.at (tok_idx)->clone_token ());
126 // Then, we want to create a subset of the matches so that
127 // `substitute_tokens()` can only see one fragment per metavar. Let's say we
128 // have the following user input: (1 145 'h')
129 // on the following match arm: ($($lit:literal)*)
130 // which causes the following matches: { "lit": [1, 145, 'h'] }
132 // The pattern (new_macro) is `$lit:literal`
133 // The first time we expand it, we want $lit to have the following token: 1
134 // The second time, 145
135 // The third and final time, 'h'
137 // In order to do so we must create "sub maps", which only contain parts of
138 // the original matches
139 // sub-maps: [ { "lit": 1 }, { "lit": 145 }, { "lit": 'h' } ]
141 // and give them to `substitute_tokens` one by one.
143 for (size_t i = 0; i < repeat_amount; i++)
145 std::map<std::string, MatchedFragmentContainer> sub_map;
146 for (auto &kv_match : fragments)
148 MatchedFragment sub_fragment;
150 // FIXME: Hack: If a fragment is not repeated, how does it fit in the
151 // submap? Do we really want to expand it? Is this normal behavior?
152 if (kv_match.second.is_single_fragment ())
153 sub_fragment = kv_match.second.get_single_fragment ();
154 else
155 sub_fragment = kv_match.second.get_fragments ()[i];
157 sub_map.insert (
158 {kv_match.first, MatchedFragmentContainer::metavar (sub_fragment)});
161 auto substitute_context = SubstituteCtx (input, new_macro, sub_map);
162 auto new_tokens = substitute_context.substitute_tokens ();
164 // Skip the first repetition, but add the separator to the expanded
165 // tokens if it is present
166 if (i != 0 && separator_token)
167 expanded.emplace_back (separator_token->clone_token ());
169 for (auto &new_token : new_tokens)
170 expanded.emplace_back (new_token->clone_token ());
173 // FIXME: We also need to make sure that all subsequent fragments
174 // contain the same amount of repetitions as the first one
176 return expanded;
179 static bool
180 is_rep_op (std::unique_ptr<AST::Token> &tok)
182 auto id = tok->get_id ();
183 return id == QUESTION_MARK || id == ASTERISK || id == PLUS;
186 std::pair<std::vector<std::unique_ptr<AST::Token>>, size_t>
187 SubstituteCtx::substitute_token (size_t token_idx)
189 auto &token = macro.at (token_idx);
190 switch (token->get_id ())
192 case IDENTIFIER:
193 rust_debug ("expanding metavar: %s", token->get_str ().c_str ());
194 return {substitute_metavar (token), 1};
195 case LEFT_PAREN: {
196 // We need to parse up until the closing delimiter and expand this
197 // fragment->n times.
198 rust_debug ("expanding repetition");
200 // We're in a context where macro repetitions have already been
201 // parsed and validated: This means that
202 // 1/ There will be no delimiters as that is an error
203 // 2/ There are no fragment specifiers anymore, which prevents us
204 // from reusing parser functions.
206 // Repetition patterns are also special in that they cannot contain
207 // "rogue" delimiters: For example, this is invalid, as they are
208 // parsed as MacroMatches and must contain a correct amount of
209 // delimiters.
210 // `$($e:expr ) )`
211 // ^ rogue closing parenthesis
213 // With all of that in mind, we can simply skip ahead from one
214 // parenthesis to the other to find the pattern to expand. Of course,
215 // pairs of delimiters, including parentheses, are allowed.
216 // `$($e:expr ( ) )`
217 // Parentheses are the sole delimiter for which we need a special
218 // behavior since they delimit the repetition pattern
220 size_t pattern_start = token_idx + 1;
221 size_t pattern_end = pattern_start;
222 auto parentheses_stack = 0;
223 for (size_t idx = pattern_start; idx < macro.size (); idx++)
225 if (macro.at (idx)->get_id () == LEFT_PAREN)
227 parentheses_stack++;
229 else if (macro.at (idx)->get_id () == RIGHT_PAREN)
231 if (parentheses_stack == 0)
233 pattern_end = idx;
234 break;
236 parentheses_stack--;
240 // Unreachable case, but let's make sure we don't ever run into it
241 rust_assert (pattern_end != pattern_start);
243 std::unique_ptr<AST::Token> separator_token = nullptr;
244 if (pattern_end + 1 <= macro.size ())
246 auto &post_pattern_token = macro.at (pattern_end + 1);
247 if (!is_rep_op (post_pattern_token))
248 separator_token = post_pattern_token->clone_token ();
251 // Amount of tokens to skip
252 auto to_skip = 0;
253 // Parentheses
254 to_skip += 2;
255 // Repetition operator
256 to_skip += 1;
257 // Separator
258 if (separator_token)
259 to_skip += 1;
261 return {substitute_repetition (pattern_start, pattern_end,
262 std::move (separator_token)),
263 pattern_end - pattern_start + to_skip};
265 // TODO: We need to check if the $ was alone. In that case, do
266 // not error out: Simply act as if there was an empty identifier
267 // with no associated fragment and paste the dollar sign in the
268 // transcription. Unsure how to do that since we always have at
269 // least the closing curly brace after an empty $...
270 default:
271 rust_error_at (token->get_locus (),
272 "unexpected token in macro transcribe: expected "
273 "%<(%> or identifier after %<$%>, got %<%s%>",
274 get_token_description (token->get_id ()));
277 // FIXME: gcc_unreachable() error case?
278 return {std::vector<std::unique_ptr<AST::Token>> (), 0};
281 std::vector<std::unique_ptr<AST::Token>>
282 SubstituteCtx::substitute_tokens ()
284 std::vector<std::unique_ptr<AST::Token>> replaced_tokens;
285 rust_debug ("expanding tokens");
287 for (size_t i = 0; i < macro.size (); i++)
289 auto &tok = macro.at (i);
290 if (tok->get_id () == DOLLAR_SIGN)
292 // Aaaaah, if only we had C++17 :)
293 // auto [expanded, tok_to_skip] = ...
294 auto p = substitute_token (i + 1);
295 auto expanded = std::move (p.first);
296 auto tok_to_skip = p.second;
298 i += tok_to_skip;
300 for (auto &token : expanded)
301 replaced_tokens.emplace_back (token->clone_token ());
303 else
305 replaced_tokens.emplace_back (tok->clone_token ());
309 return replaced_tokens;
312 } // namespace Rust