]> git.ipfire.org Git - thirdparty/gcc.git/blob - gcc/rust/expand/rust-macro-expand.h
Update copyright years.
[thirdparty/gcc.git] / 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-name-resolver.h"
29 #include "rust-macro-invoc-lexer.h"
30
31 // Provides objects and method prototypes for macro expansion
32
33 namespace Rust {
34 // forward decls for AST
35 namespace AST {
36 class MacroInvocation;
37 }
38
39 // Object used to store configuration data for macro expansion.
40 // NOTE: Keep all these items complying with the latest rustc.
41 struct ExpansionCfg
42 {
43 // features?
44 // TODO: Add `features' when we have it.
45 unsigned int recursion_limit = 1024;
46 bool trace_mac = false; // trace macro
47 bool should_test = false; // strip #[test] nodes if false
48 bool keep_macs = false; // keep macro definitions
49 std::string crate_name = "";
50 };
51
52 struct MatchedFragment
53 {
54 std::string fragment_ident;
55 size_t token_offset_begin;
56 size_t token_offset_end;
57
58 MatchedFragment (std::string identifier, size_t token_offset_begin,
59 size_t token_offset_end)
60 : fragment_ident (identifier), token_offset_begin (token_offset_begin),
61 token_offset_end (token_offset_end)
62 {}
63
64 /**
65 * Empty constructor for uninitialized fragments
66 */
67 MatchedFragment () : MatchedFragment ("", 0, 0) {}
68
69 std::string as_string () const
70 {
71 return fragment_ident + "=" + std::to_string (token_offset_begin) + ":"
72 + std::to_string (token_offset_end);
73 }
74 };
75
76 class MatchedFragmentContainer
77 {
78 public:
79 // Does the container refer to a simple metavariable, different from a
80 // repetition repeated once
81 enum class Kind
82 {
83 MetaVar,
84 Repetition,
85 };
86
87 MatchedFragmentContainer (std::vector<MatchedFragment> fragments,
88 Kind kind = Kind::Repetition)
89 : fragments (fragments), kind (kind)
90 {}
91
92 /**
93 * Create a valid fragment matched zero times. This is useful for repetitions
94 * which allow the absence of a fragment, such as * and ?
95 */
96 static MatchedFragmentContainer zero ()
97 {
98 return MatchedFragmentContainer ({});
99 }
100
101 /**
102 * Create a valid fragment matched one time
103 */
104 static MatchedFragmentContainer metavar (MatchedFragment fragment)
105 {
106 return MatchedFragmentContainer ({fragment}, Kind::MetaVar);
107 }
108
109 /**
110 * Add a matched fragment to the container
111 */
112 void add_fragment (MatchedFragment fragment)
113 {
114 rust_assert (!is_single_fragment ());
115
116 fragments.emplace_back (fragment);
117 }
118
119 size_t get_match_amount () const { return fragments.size (); }
120 const std::vector<MatchedFragment> &get_fragments () const
121 {
122 return fragments;
123 }
124 // const std::string &get_fragment_name () const { return fragment_name; }
125
126 bool is_single_fragment () const
127 {
128 return get_match_amount () == 1 && kind == Kind::MetaVar;
129 }
130
131 const MatchedFragment get_single_fragment () const
132 {
133 rust_assert (is_single_fragment ());
134
135 return fragments[0];
136 }
137
138 const Kind &get_kind () const { return kind; }
139
140 private:
141 /**
142 * Fragments matched `match_amount` times. This can be an empty vector
143 * in case having zero matches is allowed (i.e ? or * operators)
144 */
145 std::vector<MatchedFragment> fragments;
146 Kind kind;
147 };
148
149 class SubstitutionScope
150 {
151 public:
152 SubstitutionScope () : stack () {}
153
154 void push () { stack.push_back ({}); }
155
156 std::map<std::string, MatchedFragmentContainer> pop ()
157 {
158 auto top = stack.back ();
159 stack.pop_back ();
160 return top;
161 }
162
163 std::map<std::string, MatchedFragmentContainer> &peek ()
164 {
165 return stack.back ();
166 }
167
168 /**
169 * Insert a new matched metavar into the current substitution map
170 */
171 void insert_metavar (MatchedFragment fragment)
172 {
173 auto &current_map = stack.back ();
174 auto it = current_map.find (fragment.fragment_ident);
175
176 if (it == current_map.end ())
177 current_map.insert ({fragment.fragment_ident,
178 MatchedFragmentContainer::metavar (fragment)});
179 else
180 gcc_unreachable ();
181 }
182
183 /**
184 * Append a new matched fragment to a repetition into the current substitution
185 * map
186 */
187 void append_fragment (MatchedFragment fragment)
188 {
189 auto &current_map = stack.back ();
190 auto it = current_map.find (fragment.fragment_ident);
191
192 if (it == current_map.end ())
193 current_map.insert (
194 {fragment.fragment_ident, MatchedFragmentContainer ({fragment})});
195 else
196 it->second.add_fragment (fragment);
197 }
198
199 void insert_matches (std::string key, MatchedFragmentContainer matches)
200 {
201 auto &current_map = stack.back ();
202 auto it = current_map.find (key);
203 rust_assert (it == current_map.end ());
204
205 current_map.insert ({key, matches});
206 }
207
208 private:
209 std::vector<std::map<std::string, MatchedFragmentContainer>> stack;
210 };
211
212 // Object used to store shared data (between functions) for macro expansion.
213 struct MacroExpander
214 {
215 enum ContextType
216 {
217 ITEM,
218 BLOCK,
219 EXTERN,
220 TYPE,
221 TRAIT,
222 IMPL,
223 TRAIT_IMPL,
224 };
225
226 ExpansionCfg cfg;
227 unsigned int expansion_depth = 0;
228
229 MacroExpander (AST::Crate &crate, ExpansionCfg cfg, Session &session)
230 : cfg (cfg), crate (crate), session (session),
231 sub_stack (SubstitutionScope ()),
232 expanded_fragment (AST::ASTFragment::create_error ()),
233 resolver (Resolver::Resolver::get ()),
234 mappings (Analysis::Mappings::get ())
235 {}
236
237 ~MacroExpander () = default;
238
239 // Expands all macros in the crate passed in.
240 void expand_crate ();
241
242 /* Expands a macro invocation - possibly make both
243 * have similar duck-typed interface and use templates?*/
244 // should this be public or private?
245 void expand_invoc (AST::MacroInvocation &invoc, bool has_semicolon);
246
247 // Expands a single declarative macro.
248 AST::ASTFragment expand_decl_macro (Location locus,
249 AST::MacroInvocData &invoc,
250 AST::MacroRulesDefinition &rules_def,
251 bool semicolon);
252
253 void expand_cfg_attrs (AST::AttrVec &attrs);
254 bool fails_cfg (const AST::AttrVec &attr) const;
255 bool fails_cfg_with_expand (AST::AttrVec &attrs) const;
256
257 bool depth_exceeds_recursion_limit () const;
258
259 bool try_match_rule (AST::MacroRule &match_rule,
260 AST::DelimTokenTree &invoc_token_tree);
261
262 AST::ASTFragment transcribe_rule (
263 AST::MacroRule &match_rule, AST::DelimTokenTree &invoc_token_tree,
264 std::map<std::string, MatchedFragmentContainer> &matched_fragments,
265 bool semicolon, ContextType ctx);
266
267 bool match_fragment (Parser<MacroInvocLexer> &parser,
268 AST::MacroMatchFragment &fragment);
269
270 bool match_token (Parser<MacroInvocLexer> &parser, AST::Token &token);
271
272 bool match_repetition (Parser<MacroInvocLexer> &parser,
273 AST::MacroMatchRepetition &rep);
274
275 bool match_matcher (Parser<MacroInvocLexer> &parser,
276 AST::MacroMatcher &matcher);
277
278 /**
279 * Match any amount of matches
280 *
281 * @param parser Parser to use for matching
282 * @param rep Repetition to try and match
283 * @param match_amount Reference in which to store the ammount of succesful
284 * and valid matches
285 *
286 * @param lo_bound Lower bound of the matcher. When specified, the matcher
287 * will only succeed if it parses at *least* `lo_bound` fragments. If
288 * unspecified, the matcher could succeed when parsing 0 fragments.
289 *
290 * @param hi_bound Higher bound of the matcher. When specified, the matcher
291 * will only succeed if it parses *less than* `hi_bound` fragments. If
292 * unspecified, the matcher could succeed when parsing an infinity of
293 * fragments.
294 *
295 * @return true if matching was successful and within the given limits, false
296 * otherwise
297 */
298 bool match_n_matches (Parser<MacroInvocLexer> &parser,
299 AST::MacroMatchRepetition &rep, size_t &match_amount,
300 size_t lo_bound = 0, size_t hi_bound = 0);
301
302 void push_context (ContextType t) { context.push_back (t); }
303
304 ContextType pop_context ()
305 {
306 rust_assert (!context.empty ());
307
308 ContextType t = context.back ();
309 context.pop_back ();
310
311 return t;
312 }
313
314 ContextType peek_context () { return context.back (); }
315
316 void set_expanded_fragment (AST::ASTFragment &&fragment)
317 {
318 expanded_fragment = std::move (fragment);
319 }
320
321 AST::ASTFragment take_expanded_fragment (AST::ASTVisitor &vis)
322 {
323 AST::ASTFragment old_fragment = std::move (expanded_fragment);
324 auto accumulator = std::vector<AST::SingleASTNode> ();
325 expanded_fragment = AST::ASTFragment::create_error ();
326
327 for (auto &node : old_fragment.get_nodes ())
328 {
329 expansion_depth++;
330 node.accept_vis (vis);
331 // we'll decide the next move according to the outcome of the macro
332 // expansion
333 if (expanded_fragment.is_error ())
334 accumulator.push_back (node); // if expansion fails, there might be a
335 // non-macro expression we need to keep
336 else
337 {
338 // if expansion succeeded, then we need to merge the fragment with
339 // the contents in the accumulator, so that our final expansion
340 // result will contain non-macro nodes as it should
341 auto new_nodes = expanded_fragment.get_nodes ();
342 std::move (new_nodes.begin (), new_nodes.end (),
343 std::back_inserter (accumulator));
344 expanded_fragment = AST::ASTFragment (accumulator);
345 }
346 expansion_depth--;
347 }
348
349 return old_fragment;
350 }
351
352 private:
353 AST::Crate &crate;
354 Session &session;
355 SubstitutionScope sub_stack;
356 std::vector<ContextType> context;
357 AST::ASTFragment expanded_fragment;
358
359 public:
360 Resolver::Resolver *resolver;
361 Analysis::Mappings *mappings;
362 };
363
364 } // namespace Rust
365
366 #endif