Line data Source code
1 : // Copyright (C) 2020-2026 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 : // #include "rust-session-manager.h"
19 :
20 : #ifndef RUST_SESSION_MANAGER_H
21 : #define RUST_SESSION_MANAGER_H
22 :
23 : #include "rust-linemap.h"
24 : #include "rust-backend.h"
25 : #include "rust-hir-map.h"
26 : #include "safe-ctype.h"
27 : #include "rust-name-resolution-context.h"
28 :
29 : #include "config.h"
30 : #include "rust-system.h"
31 : #include "coretypes.h"
32 : #include "options.h"
33 :
34 : #include "optional.h"
35 :
36 : namespace Rust {
37 : // parser forward decl
38 : template <typename ManagedTokenSource> class Parser;
39 : class Lexer;
40 : // crate forward decl
41 : namespace AST {
42 : struct Crate;
43 : }
44 : // crate forward decl
45 : namespace HIR {
46 : class Crate;
47 : }
48 :
49 : /* Data related to target, most useful for conditional compilation and
50 : * whatever. */
51 : struct TargetOptions
52 : {
53 : /* TODO: maybe make private and access through helpers to allow changes to
54 : * impl */
55 : std::unordered_map<std::string, std::unordered_set<tl::optional<std::string>>>
56 : features;
57 :
58 : enum class CrateType
59 : {
60 : BIN = 0,
61 : LIB,
62 : RLIB,
63 : DYLIB,
64 : CDYLIB,
65 : STATICLIB,
66 : PROC_MACRO
67 : } crate_type
68 : = CrateType::BIN;
69 :
70 : public:
71 21 : void set_crate_type (int raw_type)
72 : {
73 21 : crate_type = static_cast<CrateType> (raw_type);
74 : }
75 :
76 : const CrateType &get_crate_type () const { return crate_type; }
77 :
78 : // Returns whether a key is defined in the feature set.
79 2834 : bool has_key (std::string key) const
80 : {
81 2834 : auto it = features.find (key);
82 2834 : return it != features.end ()
83 199 : && it->second.find (tl::nullopt) != it->second.end ();
84 : }
85 :
86 : // Returns whether a key exists with the given value in the feature set.
87 14636 : bool has_key_value_pair (std::string key, std::string value) const
88 : {
89 14636 : auto it = features.find (key);
90 14636 : if (it != features.end ())
91 : {
92 14494 : auto set = it->second;
93 28988 : auto it2 = set.find (value);
94 14494 : if (it2 != set.end ())
95 8384 : return true;
96 14494 : }
97 : return false;
98 : }
99 :
100 : /* Returns the singular value from the key, or if the key has multiple, an
101 : * empty string. */
102 : std::string get_singular_value (std::string key) const
103 : {
104 : auto it = features.find (key);
105 : if (it != features.end ())
106 : {
107 : auto set = it->second;
108 : if (set.size () == 1 && set.begin ()->has_value ())
109 : return set.begin ()->value ();
110 : }
111 : return "";
112 : }
113 :
114 : /* Returns all values associated with a key (including none), or an empty
115 : * set if no key is found. */
116 : std::unordered_set<std::string> get_values_for_key (std::string key) const
117 : {
118 : std::unordered_set<std::string> ret;
119 :
120 : auto it = features.find (key);
121 : if (it == features.end ())
122 : return {};
123 :
124 : for (auto &val : it->second)
125 : if (val.has_value ())
126 : ret.insert (val.value ());
127 :
128 : return ret;
129 : }
130 :
131 : /* Inserts a key (no value) into the feature set. This will do nothing if
132 : * the key already exists. This returns whether the insertion was successful
133 : * (i.e. whether key already existed). */
134 66 : bool insert_key (std::string key)
135 : {
136 66 : auto it = features.find (key);
137 :
138 66 : if (it == features.end ())
139 66 : it
140 66 : = features
141 66 : .insert (
142 132 : std::make_pair (std::move (key),
143 132 : std::unordered_set<tl::optional<std::string>> ()))
144 : .first;
145 :
146 66 : return it->second.insert (tl::nullopt).second;
147 : }
148 :
149 : // Inserts a key-value pair into the feature set.
150 54829 : void insert_key_value_pair (std::string key, std::string value)
151 : {
152 54829 : auto it = features.find (key);
153 :
154 54829 : if (it == features.end ())
155 39880 : it
156 39880 : = features
157 39880 : .insert (
158 79760 : std::make_pair (std::move (key),
159 79760 : std::unordered_set<tl::optional<std::string>> ()))
160 : .first;
161 :
162 54829 : it->second.insert (std::move (value));
163 54829 : }
164 :
165 : // Dump all target options to stderr.
166 : void dump_target_options () const;
167 :
168 : /* Creates derived values and implicit enables after all target info is
169 : * added (e.g. "unix"). */
170 : void init_derived_values ();
171 :
172 : /* Enables all requirements for the feature given, and will enable feature
173 : * itself if not enabled. */
174 : void enable_implicit_feature_reqs (std::string feature);
175 :
176 : /* According to reference, Rust uses either multi-map key-values or just
177 : * values (although values may be aliases for a key-value value). This seems
178 : * like overkill. Thus, depending on whether the attributes used in cfg are
179 : * fixed or not, I think I'll either put each non-multimap "key-value" as a
180 : * separate field and have the multimap "key-values" in a regular map for
181 : * that one key, or actually use a multimap.
182 : *
183 : * rustc itself uses a set of key-value tuples where the second tuple
184 : * element is optional. This gets rid of the requirement to make a
185 : * multi-map, I guess, but seems like it might make search slow (unless all
186 : * "is defined"-only ones have empty string as second element). */
187 : /* cfg attributes:
188 : * - target_arch: single value
189 : * - target_feature: multiple values possible
190 : * - target_os: single value
191 : * - target_family: single value (or no value?)
192 : * - unix: set when target_family = "unix"
193 : * - windows: set when target_family = "windows"
194 : * - if these are just syntactic sugar, then maybe have a separate set or
195 : * map for this kind of stuff
196 : * - target_env: set when needed for disambiguation about ABI - usually
197 : * empty string for GNU, complicated
198 : * - seems to be a single value (if any)
199 : * - target_endian: single value; "little" or "big"
200 : * - target_pointer_width: single value, "32" for 32-bit pointers, etc.
201 : * - target_vendor, single value
202 : * - test: set when testing is being done
203 : * - again, seems similar to a "is defined" rather than "is equal to" like
204 : * unix
205 : * - debug_assertions: seems to "is defined"
206 : * - proc_macro: no idea, bad docs. seems to be boolean, so maybe "is
207 : * defined"
208 : */
209 : };
210 :
211 : // Defines compiler options (e.g. dump, etc.).
212 : struct CompileOptions
213 : {
214 : enum DumpOption
215 : {
216 : LEXER_DUMP,
217 : AST_DUMP_PRETTY,
218 : REGISTER_PLUGINS_DUMP,
219 : INJECTION_DUMP,
220 : EXPANSION_DUMP,
221 : RESOLUTION_DUMP,
222 : TARGET_OPTION_DUMP,
223 : HIR_DUMP,
224 : HIR_DUMP_PRETTY,
225 : BIR_DUMP,
226 : INTERNAL_DUMP,
227 : };
228 :
229 : std::set<DumpOption> dump_options;
230 :
231 : /* List of node that is not print during the dump of the ast with internal
232 : * comment */
233 : std::set<std::string> excluded_node;
234 :
235 : /* configuration options - actually useful for conditional compilation and
236 : * whatever data related to target arch, features, os, family, env, endian,
237 : * pointer width, vendor */
238 : TargetOptions target_data;
239 : std::string crate_name;
240 : bool crate_name_set_manually = false;
241 : bool enable_test = false;
242 : bool debug_assertions = false;
243 : std::string metadata_output_path;
244 :
245 : int compat_version = 49;
246 :
247 : /** Structure containing additional attributes to be injected within the
248 : * compiled crate from the command line instead of the source code.
249 : *
250 : * To make things a bit easier with rustc, we're trying to use the same
251 : * format accepted by -Zcrate-attr. This means the CLI accepts the text
252 : * content of the attribute and not text of the whole attribute itself.
253 : */
254 3 : struct CliAttributeContent
255 : {
256 1 : CliAttributeContent (std::string content, location_t locus)
257 2 : : content (content), locus (locus)
258 : {}
259 : /* Content of the attribute*/
260 : std::string content;
261 : location_t locus;
262 : };
263 : std::vector<CliAttributeContent> addional_attributes;
264 :
265 : enum class Edition
266 : {
267 : E2015 = 0,
268 : E2018,
269 : E2021,
270 : } edition
271 : = Edition::E2015;
272 :
273 : enum class CompileStep
274 : {
275 : Ast,
276 : AttributeCheck,
277 : Expansion,
278 : ASTValidation,
279 : FeatureGating,
280 : NameResolution,
281 : Lowering,
282 : TypeCheck,
283 : Privacy,
284 : Unsafety,
285 : Const,
286 : BorrowCheck,
287 : Compilation,
288 : End,
289 : } compile_until
290 : = CompileStep::End;
291 :
292 : enum class PanicStrategy
293 : {
294 : Unwind,
295 : Abort,
296 : } panic_strategy
297 : = PanicStrategy::Unwind;
298 :
299 48318 : bool dump_option_enabled (DumpOption option) const
300 : {
301 38834 : return dump_options.find (option) != dump_options.end ();
302 : }
303 :
304 1 : void enable_dump_option (DumpOption option) { dump_options.insert (option); }
305 :
306 0 : void enable_all_dump_options ()
307 : {
308 0 : enable_dump_option (DumpOption::LEXER_DUMP);
309 0 : enable_dump_option (DumpOption::AST_DUMP_PRETTY);
310 0 : enable_dump_option (DumpOption::REGISTER_PLUGINS_DUMP);
311 0 : enable_dump_option (DumpOption::INJECTION_DUMP);
312 0 : enable_dump_option (DumpOption::EXPANSION_DUMP);
313 0 : enable_dump_option (DumpOption::RESOLUTION_DUMP);
314 0 : enable_dump_option (DumpOption::TARGET_OPTION_DUMP);
315 0 : enable_dump_option (DumpOption::HIR_DUMP);
316 0 : enable_dump_option (DumpOption::HIR_DUMP_PRETTY);
317 0 : enable_dump_option (DumpOption::BIR_DUMP);
318 0 : enable_dump_option (DumpOption::INTERNAL_DUMP);
319 0 : }
320 :
321 0 : void add_excluded (std::string node)
322 : {
323 0 : rust_assert (!node.empty ());
324 0 : excluded_node.insert (node);
325 0 : }
326 :
327 0 : const std::set<std::string> get_excluded () const { return excluded_node; }
328 :
329 4983 : void set_crate_name (std::string name)
330 : {
331 4983 : rust_assert (!name.empty ());
332 :
333 4983 : crate_name = std::move (name);
334 4983 : }
335 :
336 9832 : const std::string &get_crate_name () const
337 : {
338 9832 : rust_assert (!crate_name.empty ());
339 9832 : return crate_name;
340 : }
341 :
342 11 : void set_edition (int raw_edition)
343 : {
344 11 : edition = static_cast<Edition> (raw_edition);
345 : }
346 :
347 : const Edition &get_edition () const { return edition; }
348 :
349 40 : void set_compat_version (const char *version)
350 : {
351 40 : if (!strcmp (version, "max"))
352 : {
353 0 : compat_version = INT_MAX;
354 0 : return;
355 : }
356 :
357 40 : long res;
358 40 : char *end_ptr;
359 40 : if (version[0] != '1' || version[1] != '.' || version[2] == '\0'
360 40 : || (res = strtol (version + 2, &end_ptr, 10), *end_ptr) || res < 0
361 80 : || res > INT_MAX)
362 : {
363 0 : rust_error_at (UNKNOWN_LOCATION,
364 : "compat version must be of form 1.x or be \"max\"");
365 0 : return;
366 : }
367 :
368 40 : compat_version = res;
369 : }
370 :
371 302155 : int get_compat_version () const { return compat_version; }
372 :
373 21 : void set_crate_type (int raw_type) { target_data.set_crate_type (raw_type); }
374 :
375 18188 : bool is_proc_macro () const
376 : {
377 18188 : return target_data.get_crate_type ()
378 : == TargetOptions::CrateType::PROC_MACRO;
379 : }
380 :
381 49 : void set_compile_step (int raw_step)
382 : {
383 49 : compile_until = static_cast<CompileStep> (raw_step);
384 : }
385 :
386 : const CompileStep &get_compile_until () const { return compile_until; }
387 :
388 0 : void set_panic_strategy (int strategy)
389 : {
390 0 : panic_strategy = static_cast<PanicStrategy> (strategy);
391 : }
392 :
393 : const PanicStrategy &get_panic_strategy () const { return panic_strategy; }
394 :
395 0 : void set_metadata_output (const std::string &path)
396 : {
397 0 : metadata_output_path = path;
398 : }
399 :
400 0 : const std::string &get_metadata_output () const
401 : {
402 0 : return metadata_output_path;
403 : }
404 :
405 4305 : bool metadata_output_path_set () const
406 : {
407 4305 : return !metadata_output_path.empty ();
408 : }
409 : };
410 :
411 : /* Defines a compiler session. This is for a single compiler invocation, so
412 : * potentially includes parsing multiple crates. */
413 : struct Session
414 : {
415 : CompileOptions options;
416 : /* This should really be in a per-crate storage area but it is wiped with
417 : * every file so eh. */
418 : std::string injected_crate_name;
419 : std::map<std::string, std::string> extern_crates;
420 :
421 : /* extra files get included during late stages of compilation (e.g. macro
422 : * expansion) */
423 : std::vector<std::string> extra_files;
424 :
425 : // backend linemap
426 : Linemap *linemap;
427 :
428 : // mappings
429 : Analysis::Mappings &mappings;
430 :
431 : public:
432 : /* Get a reference to the static session instance */
433 : static Session &get_instance ();
434 :
435 4984 : ~Session () = default;
436 :
437 : /* This initializes the compiler session. Corresponds to langhook
438 : * grs_langhook_init(). Note that this is called after option handling. */
439 : void init ();
440 :
441 : // delete those constructors so we don't access the singleton in any
442 : // other way than via `get_instance()`
443 : Session (Session const &) = delete;
444 : void operator= (Session const &) = delete;
445 :
446 : bool handle_option (enum opt_code code, const char *arg, HOST_WIDE_INT value,
447 : int kind, location_t loc,
448 : const struct cl_option_handlers *handlers);
449 : void handle_input_files (int num_files, const char **files);
450 : void init_options ();
451 : void handle_crate_name (const char *filename, const AST::Crate &parsed_crate);
452 :
453 : /* This function saves the filename data into the session manager using the
454 : * `move` semantics, and returns a C-style string referencing the input
455 : * std::string */
456 5 : inline const char *include_extra_file (std::string filename)
457 : {
458 5 : extra_files.push_back (std::move (filename));
459 5 : return extra_files.back ().c_str ();
460 : }
461 :
462 72 : struct LoadedCrate
463 : {
464 24 : LoadedCrate (std::string name, NodeId node_id,
465 : Resolver2_0::NameResolutionContext ctx)
466 24 : : name (std::move (name)), node_id (node_id), ctx (std::move (ctx))
467 : {}
468 :
469 : LoadedCrate (const LoadedCrate &) = delete;
470 : LoadedCrate &operator= (const LoadedCrate &) = delete;
471 48 : LoadedCrate (LoadedCrate &&other) = default;
472 :
473 : std::string name;
474 : NodeId node_id;
475 : Resolver2_0::NameResolutionContext ctx;
476 : };
477 :
478 : struct LoadingError
479 : {
480 : public:
481 : enum class Kind
482 : {
483 : ALREADY_LOADED,
484 : FAILED_TO_LOCATE,
485 : COLLISION,
486 : } kind;
487 :
488 : static LoadingError make_already_loaded (NodeId node_id)
489 : {
490 : return LoadingError{Kind::ALREADY_LOADED, node_id};
491 : }
492 :
493 : static LoadingError make_failed_to_locate ()
494 : {
495 : return LoadingError{Kind::FAILED_TO_LOCATE, UNKNOWN_NODEID};
496 : }
497 :
498 : static LoadingError make_collision ()
499 : {
500 : return LoadingError{Kind::COLLISION, UNKNOWN_NODEID};
501 : }
502 : NodeId node_id;
503 : };
504 :
505 : tl::expected<LoadedCrate, LoadingError>
506 : load_extern_crate (const std::string &crate_name, location_t locus);
507 :
508 302155 : int get_compat_version () const { return options.get_compat_version (); }
509 :
510 148664 : bool should_support_offset_of () const { return get_compat_version () >= 71; }
511 :
512 148646 : bool should_support_cfg_select () const
513 : {
514 148646 : return get_compat_version () >= 90;
515 : }
516 :
517 : private:
518 4984 : Session () : mappings (Analysis::Mappings::get ()) {}
519 : void compile_crate (const char *filename);
520 : bool enable_dump (std::string arg);
521 :
522 : void dump_lex (Parser<Lexer> &parser) const;
523 : void dump_ast_pretty (AST::Crate &crate, bool expanded = false) const;
524 : void dump_ast_pretty_internal (AST::Crate &crate) const;
525 : void dump_name_resolution (Resolver2_0::NameResolutionContext &ctx) const;
526 : void dump_hir (HIR::Crate &crate) const;
527 : void dump_hir_pretty (HIR::Crate &crate) const;
528 :
529 : void handle_excluded_node (std::string arg);
530 :
531 : // pipeline stages - TODO maybe move?
532 : /* Register plugins pipeline stage. TODO maybe move to another object?
533 : * Currently dummy stage. In future will handle attribute injection
534 : * (top-level inner attribute creation from command line arguments), setting
535 : * options maybe, registering lints maybe, loading plugins maybe. */
536 : void register_plugins (AST::Crate &crate);
537 :
538 : /* Injection pipeline stage. TODO maybe move to another object? Maybe have
539 : * some lint checks (in future, obviously), register builtin macros, crate
540 : * injection. */
541 : void injection (AST::Crate &crate, AST::AttrVec cli_attributes);
542 :
543 : /* Expansion pipeline stage. TODO maybe move to another object? Expands all
544 : * macros, maybe build test harness in future, AST validation, maybe create
545 : * macro crate (if not rustdoc).*/
546 : void expansion (AST::Crate &crate, Resolver2_0::NameResolutionContext &ctx);
547 :
548 : // handle cfg_option
549 : bool handle_cfg_option (std::string &data);
550 :
551 : bool handle_extern_option (std::string &data);
552 : };
553 :
554 : } // namespace Rust
555 :
556 : #if CHECKING_P
557 : namespace selftest {
558 : extern void rust_crate_name_validation_test (void);
559 : }
560 : #endif // CHECKING_P
561 :
562 : #endif
|