new file mode 100644
@@ -0,0 +1,452 @@
+// Copyright (C) 2020-2022 Free Software Foundation, Inc.
+
+// This file is part of GCC.
+
+// GCC is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3, or (at your option) any later
+// version.
+
+// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+// for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with GCC; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+#include "rust-diagnostics.h"
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "target.h"
+#include "tree.h"
+#include "gimple-expr.h"
+#include "diagnostic.h"
+#include "opts.h"
+#include "fold-const.h"
+#include "gimplify.h"
+#include "stor-layout.h"
+#include "debug.h"
+#include "convert.h"
+#include "langhooks.h"
+#include "langhooks-def.h"
+
+#include "selftest.h"
+#include "rust-cfg-parser.h"
+#include "rust-privacy-ctx.h"
+#include "rust-ast-resolve-item.h"
+#include "rust-optional.h"
+
+#include <mpfr.h>
+// note: header files must be in this order or else forward declarations don't
+// work properly. Kinda dumb system, but have to live with it. clang-format
+// seems to mess it up
+/* Order: config, system, coretypes, target, tree, gimple-expr, diagnostic,
+ * opts, fold-const, gimplify, stor-layout, debug, convert, langhooks,
+ * langhooks-def */
+
+// FIXME: test saving intellisense
+#include "options.h"
+
+// version check to stop compiling if c++ isn't c++11 or higher
+#if __cplusplus < 201103
+#error \
+ "GCC Rust frontend requires C++11 or higher. You can compile the g++ frontend first and then compile the Rust frontend using that."
+#endif
+// TODO: is this best way to do it? Is it allowed? (should be)
+
+/* General TODOs:
+ * - convert all copies of expensive-to-copy (deep copy) AST objects into
+ * moves, if possible. Don't remove clone functionality - it may be required for
+ * e.g. HIR conversion.
+ */
+
+#include "rust-system.h"
+#include "rust-session-manager.h"
+
+// Language-dependent contents of a type. GTY() mark used for garbage collector.
+struct GTY (()) lang_type
+{
+};
+
+// Language-dependent contents of a decl.
+struct GTY (()) lang_decl
+{
+};
+
+// Language-dependent contents of an identifier. This must include a
+// tree_identifier.
+struct GTY (()) lang_identifier
+{
+ struct tree_identifier common;
+};
+
+// The resulting tree type.
+union GTY ((
+ desc ("TREE_CODE (&%h.generic) == IDENTIFIER_NODE"),
+ chain_next (
+ "CODE_CONTAINS_STRUCT (TREE_CODE (&%h.generic), "
+ "TS_COMMON) ? ((union lang_tree_node *) TREE_CHAIN (&%h.generic)) : NULL")))
+ lang_tree_node
+{
+ union tree_node GTY ((tag ("0"), desc ("tree_node_structure (&%h)"))) generic;
+ struct lang_identifier GTY ((tag ("1"))) identifier;
+};
+
+// We don't use language_function.
+struct GTY (()) language_function
+{
+};
+
+// has to be in same compilation unit as session, so here for now
+void
+rust_add_target_info (const char *key, const char *value)
+{
+ sorry ("TODO");
+
+ Rust::Session::get_instance ().options.target_data.insert_key_value_pair (
+ key, value);
+}
+
+/* Language hooks. */
+
+/* Initial lang hook called (possibly), used for initialisation.
+ * Must call build_common_tree_nodes, set_sizetype, build_common_tree_nodes_2,
+ * and build_common_builtin_nodes, as well as set global variable
+ * void_list_node. Apparently called after option handling? */
+static bool
+grs_langhook_init (void)
+{
+ /* Something to do with this:
+ This allows the code in d-builtins.cc to not have to worry about
+ converting (C signed char *) to (D char *) for string arguments of
+ built-in functions. The parameter (signed_char = false) specifies
+ whether char is signed. */
+ build_common_tree_nodes (false);
+
+ // Creates a new TREE_LIST node with purpose NULL_TREE and value
+ // void_type_node
+ void_list_node = build_tree_list (NULL_TREE, void_type_node);
+
+ // Builds built-ins for middle-end after all front-end built-ins are already
+ // instantiated
+ build_common_builtin_nodes ();
+
+ mpfr_set_default_prec (128);
+
+ using_eh_for_cleanups ();
+
+ // initialise compiler session
+ Rust::Session::get_instance ().init ();
+
+ return true;
+}
+
+/* The option mask (something to do with options for specific frontends or
+ * something). */
+static unsigned int
+grs_langhook_option_lang_mask (void)
+{
+ return CL_Rust;
+}
+
+/* Initialize the options structure. */
+static void
+grs_langhook_init_options_struct (struct gcc_options *opts)
+{
+ /* Operations are always wrapping in Rust, even on signed integer. This is
+ * useful for the low level wrapping_{add, sub, mul} intrinsics, not for
+ * regular arithmetic operations which are checked for overflow anyway using
+ * builtins */
+ opts->x_flag_wrapv = 1;
+
+ // nothing yet - used by frontends to change specific options for the language
+ Rust::Session::get_instance ().init_options ();
+}
+
+/* Main entry point for front-end, apparently. Finds input file names in global
+ * vars in_fnames and num_in_fnames. From this, frontend can take over and do
+ * actual parsing and initial compilation. This function must create a complete
+ * parse tree in a global var, and then return.
+ *
+ * Some consider this the "start of compilation". */
+static void
+grs_langhook_parse_file (void)
+{
+ rust_debug ("Preparing to parse files. ");
+
+ Rust::Session::get_instance ().handle_input_files (num_in_fnames, in_fnames);
+}
+
+/* Seems to get the exact type for a specific type - e.g. for scalar float with
+ * 32-bit bitsize, it returns float, and for 64-bit bitsize, it returns double.
+ * Used to map RTL nodes to machine modes or something like that. */
+static tree
+grs_langhook_type_for_mode (machine_mode mode, int unsignedp)
+{
+ // TODO: change all this later to match rustc types
+ if (mode == TYPE_MODE (float_type_node))
+ return float_type_node;
+
+ if (mode == TYPE_MODE (double_type_node))
+ return double_type_node;
+
+ if (mode == TYPE_MODE (intQI_type_node)) // quarter integer mode - single byte
+ // treated as integer
+ return unsignedp ? unsigned_intQI_type_node : intQI_type_node;
+ if (mode
+ == TYPE_MODE (intHI_type_node)) // half integer mode - two-byte integer
+ return unsignedp ? unsigned_intHI_type_node : intHI_type_node;
+ if (mode
+ == TYPE_MODE (intSI_type_node)) // single integer mode - four-byte integer
+ return unsignedp ? unsigned_intSI_type_node : intSI_type_node;
+ if (mode
+ == TYPE_MODE (
+ intDI_type_node)) // double integer mode - eight-byte integer
+ return unsignedp ? unsigned_intDI_type_node : intDI_type_node;
+ if (mode
+ == TYPE_MODE (intTI_type_node)) // tetra integer mode - 16-byte integer
+ return unsignedp ? unsigned_intTI_type_node : intTI_type_node;
+
+ if (mode == TYPE_MODE (integer_type_node))
+ return unsignedp ? unsigned_type_node : integer_type_node;
+
+ if (mode == TYPE_MODE (long_integer_type_node))
+ return unsignedp ? long_unsigned_type_node : long_integer_type_node;
+
+ if (mode == TYPE_MODE (long_long_integer_type_node))
+ return unsignedp ? long_long_unsigned_type_node
+ : long_long_integer_type_node;
+
+ if (COMPLEX_MODE_P (mode))
+ {
+ if (mode == TYPE_MODE (complex_float_type_node))
+ return complex_float_type_node;
+ if (mode == TYPE_MODE (complex_double_type_node))
+ return complex_double_type_node;
+ if (mode == TYPE_MODE (complex_long_double_type_node))
+ return complex_long_double_type_node;
+ if (mode == TYPE_MODE (complex_integer_type_node) && !unsignedp)
+ return complex_integer_type_node;
+ }
+ /* gcc_unreachable */
+ return NULL;
+}
+
+// Record a builtin function. We just ignore builtin functions.
+static tree
+grs_langhook_builtin_function (tree decl ATTRIBUTE_UNUSED)
+{
+ return decl;
+}
+
+/* Return true if we are in the global binding level (which is never,
+ * apparently). */
+static bool
+grs_langhook_global_bindings_p (void)
+{
+ // return current_function_decl == NULL_TREE;
+ // gcc_unreachable();
+ // return true;
+ return false;
+}
+
+/* Push a declaration into the current binding level. We can't
+ usefully implement this since we don't want to convert from tree
+ back to one of our internal data structures. I think the only way
+ this is used is to record a decl which is to be returned by
+ getdecls, and we could implement it for that purpose if
+ necessary. */
+static tree
+grs_langhook_pushdecl (tree decl ATTRIBUTE_UNUSED)
+{
+ gcc_unreachable ();
+ return NULL;
+}
+
+/* This hook is used to get the current list of declarations as trees.
+ We don't support that; instead we use the write_globals hook. This
+ can't simply crash because it is called by -gstabs. */
+static tree
+grs_langhook_getdecls (void)
+{
+ // gcc_unreachable();
+ return NULL;
+}
+
+// Handle Rust-specific options. Return false if nothing happened.
+static bool
+grs_langhook_handle_option (
+ size_t scode, const char *arg, HOST_WIDE_INT value, int kind ATTRIBUTE_UNUSED,
+ location_t loc ATTRIBUTE_UNUSED,
+ const struct cl_option_handlers *handlers ATTRIBUTE_UNUSED)
+{
+ // Convert integer code to lang.opt enum codes with names.
+ enum opt_code code = (enum opt_code) scode;
+
+ // Delegate to session manager
+ return Rust::Session::get_instance ().handle_option (code, arg, value, kind,
+ loc, handlers);
+}
+
+/* Run after parsing options. */
+static bool
+grs_langhook_post_options (const char **pfilename ATTRIBUTE_UNUSED)
+{
+ // can be used to override other options if required
+
+ // satisfies an assert in init_excess_precision in toplev.cc
+ if (flag_excess_precision /*_cmdline*/ == EXCESS_PRECISION_DEFAULT)
+ flag_excess_precision /*_cmdline*/ = EXCESS_PRECISION_STANDARD;
+
+ /* Returning false means that the backend should be used. */
+ return false;
+}
+
+/* Rust-specific gimplification. May need to gimplify e.g.
+ * CALL_EXPR_STATIC_CHAIN */
+static int
+grs_langhook_gimplify_expr (tree *expr_p ATTRIBUTE_UNUSED,
+ gimple_seq *pre_p ATTRIBUTE_UNUSED,
+ gimple_seq *post_p ATTRIBUTE_UNUSED)
+{
+ if (TREE_CODE (*expr_p) == CALL_EXPR
+ && CALL_EXPR_STATIC_CHAIN (*expr_p) != NULL_TREE)
+ gimplify_expr (&CALL_EXPR_STATIC_CHAIN (*expr_p), pre_p, post_p,
+ is_gimple_val, fb_rvalue);
+ return GS_UNHANDLED;
+}
+
+static tree
+grs_langhook_eh_personality (void)
+{
+ static tree personality_decl;
+ if (personality_decl == NULL_TREE)
+ {
+ personality_decl = build_personality_function ("gccrs");
+ rust_preserve_from_gc (personality_decl);
+ }
+ return personality_decl;
+}
+
+tree
+convert (tree type, tree expr)
+{
+ if (type == error_mark_node || expr == error_mark_node
+ || TREE_TYPE (expr) == error_mark_node)
+ return error_mark_node;
+
+ if (type == TREE_TYPE (expr))
+ return expr;
+
+ if (TYPE_MAIN_VARIANT (type) == TYPE_MAIN_VARIANT (TREE_TYPE (expr)))
+ return fold_convert (type, expr);
+
+ switch (TREE_CODE (type))
+ {
+ case VOID_TYPE:
+ case BOOLEAN_TYPE:
+ return fold_convert (type, expr);
+ case INTEGER_TYPE:
+ return fold (convert_to_integer (type, expr));
+ case POINTER_TYPE:
+ return fold (convert_to_pointer (type, expr));
+ case REAL_TYPE:
+ return fold (convert_to_real (type, expr));
+ case COMPLEX_TYPE:
+ return fold (convert_to_complex (type, expr));
+ default:
+ break;
+ }
+
+ gcc_unreachable ();
+}
+
+/* FIXME: This is a hack to preserve trees that we create from the
+ garbage collector. */
+
+static GTY (()) tree rust_gc_root;
+
+void
+rust_preserve_from_gc (tree t)
+{
+ rust_gc_root = tree_cons (NULL_TREE, t, rust_gc_root);
+}
+
+/* Convert an identifier for use in an error message. */
+
+const char *
+rust_localize_identifier (const char *ident)
+{
+ return identifier_to_locale (ident);
+}
+
+/* The language hooks data structure. This is the main interface between the GCC
+ * front-end and the GCC middle-end/back-end. A list of language hooks could be
+ * found in <gcc>/langhooks.h
+ */
+#undef LANG_HOOKS_NAME
+#undef LANG_HOOKS_INIT
+#undef LANG_HOOKS_OPTION_LANG_MASK
+#undef LANG_HOOKS_INIT_OPTIONS_STRUCT
+#undef LANG_HOOKS_HANDLE_OPTION
+#undef LANG_HOOKS_POST_OPTIONS
+#undef LANG_HOOKS_PARSE_FILE
+#undef LANG_HOOKS_TYPE_FOR_MODE
+#undef LANG_HOOKS_BUILTIN_FUNCTION
+#undef LANG_HOOKS_GLOBAL_BINDINGS_P
+#undef LANG_HOOKS_PUSHDECL
+#undef LANG_HOOKS_GETDECLS
+#undef LANG_HOOKS_WRITE_GLOBALS
+#undef LANG_HOOKS_GIMPLIFY_EXPR
+#undef LANG_HOOKS_EH_PERSONALITY
+
+#define LANG_HOOKS_NAME "GNU Rust"
+#define LANG_HOOKS_INIT grs_langhook_init
+#define LANG_HOOKS_OPTION_LANG_MASK grs_langhook_option_lang_mask
+#define LANG_HOOKS_INIT_OPTIONS_STRUCT grs_langhook_init_options_struct
+#define LANG_HOOKS_HANDLE_OPTION grs_langhook_handle_option
+#define LANG_HOOKS_POST_OPTIONS grs_langhook_post_options
+/* Main lang-hook, apparently. Finds input file names in global vars in_fnames
+ * and num_in_fnames From this, frontend can take over and do actual parsing and
+ * initial compilation.
+ * This hook must create a complete parse tree in a global var, and then return.
+ */
+#define LANG_HOOKS_PARSE_FILE grs_langhook_parse_file
+#define LANG_HOOKS_TYPE_FOR_MODE grs_langhook_type_for_mode
+#define LANG_HOOKS_BUILTIN_FUNCTION grs_langhook_builtin_function
+#define LANG_HOOKS_GLOBAL_BINDINGS_P grs_langhook_global_bindings_p
+#define LANG_HOOKS_PUSHDECL grs_langhook_pushdecl
+#define LANG_HOOKS_GETDECLS grs_langhook_getdecls
+#define LANG_HOOKS_GIMPLIFY_EXPR grs_langhook_gimplify_expr
+#define LANG_HOOKS_EH_PERSONALITY grs_langhook_eh_personality
+
+#if CHECKING_P
+
+#undef LANG_HOOKS_RUN_LANG_SELFTESTS
+#define LANG_HOOKS_RUN_LANG_SELFTESTS selftest::run_rust_tests
+
+namespace selftest {
+
+void
+run_rust_tests ()
+{
+ // Call tests for the rust frontend here
+ rust_cfg_parser_test ();
+ rust_privacy_ctx_test ();
+ rust_crate_name_validation_test ();
+ rust_simple_path_resolve_test ();
+ rust_optional_test ();
+}
+} // namespace selftest
+
+#endif /* !CHECKING_P */
+
+// Expands all LANG_HOOKS_x of GCC
+struct lang_hooks lang_hooks = LANG_HOOKS_INITIALIZER;
+
+// These are for GCC's garbage collector to work properly or something
+#include "gt-rust-rust-lang.h"
+#include "gtype-rust.h"
new file mode 100644
@@ -0,0 +1,1189 @@
+// Copyright (C) 2020-2022 Free Software Foundation, Inc.
+
+// This file is part of GCC.
+
+// GCC is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3, or (at your option) any later
+// version.
+
+// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+// for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with GCC; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+#include "rust-session-manager.h"
+#include "rust-diagnostics.h"
+#include "rust-unsafe-checker.h"
+#include "rust-lex.h"
+#include "rust-parse.h"
+#include "rust-macro-expand.h"
+#include "rust-ast-resolve.h"
+#include "rust-ast-lower.h"
+#include "rust-hir-type-check.h"
+#include "rust-privacy-check.h"
+#include "rust-const-checker.h"
+#include "rust-tycheck-dump.h"
+#include "rust-compile.h"
+#include "rust-cfg-parser.h"
+#include "rust-lint-scan-deadcode.h"
+#include "rust-lint-unused-var.h"
+#include "rust-hir-dump.h"
+#include "rust-ast-dump.h"
+#include "rust-export-metadata.h"
+#include "rust-imports.h"
+#include "rust-extern-crate.h"
+#include "rust-attributes.h"
+
+#include "diagnostic.h"
+#include "input.h"
+#include "selftest.h"
+#include "target.h"
+
+extern bool
+saw_errors (void);
+
+extern Linemap *
+rust_get_linemap ();
+
+extern Backend *
+rust_get_backend ();
+
+namespace Rust {
+
+const char *kLexDumpFile = "gccrs.lex.dump";
+const char *kASTDumpFile = "gccrs.ast.dump";
+const char *kASTPrettyDumpFile = "gccrs.ast-pretty.dump";
+const char *kASTExpandedDumpFile = "gccrs.ast-expanded.dump";
+const char *kHIRDumpFile = "gccrs.hir.dump";
+const char *kHIRPrettyDumpFile = "gccrs.hir-pretty.dump";
+const char *kHIRTypeResolutionDumpFile = "gccrs.type-resolution.dump";
+const char *kTargetOptionsDumpFile = "gccrs.target-options.dump";
+
+const std::string kDefaultCrateName = "rust_out";
+const size_t kMaxNameLength = 64;
+
+Session &
+Session::get_instance ()
+{
+ static Session instance;
+ return instance;
+}
+
+static std::string
+infer_crate_name (const std::string &filename)
+{
+ if (filename == "-")
+ return kDefaultCrateName;
+
+ std::string crate = std::string (filename);
+ size_t path_sep = crate.find_last_of (file_separator);
+
+ // find the base filename
+ if (path_sep != std::string::npos)
+ crate.erase (0, path_sep + 1);
+
+ // find the file stem name (remove file extension)
+ size_t ext_position = crate.find_last_of ('.');
+ if (ext_position != std::string::npos)
+ crate.erase (ext_position);
+
+ // Replace all the '-' symbols with '_' per Rust rules
+ for (auto &c : crate)
+ {
+ if (c == '-')
+ c = '_';
+ }
+ return crate;
+}
+
+/* Validate the crate name using the ASCII rules
+ TODO: Support Unicode version of the rules */
+
+static bool
+validate_crate_name (const std::string &crate_name, Error &error)
+{
+ if (crate_name.empty ())
+ {
+ error = Error (Location (), "crate name cannot be empty");
+ return false;
+ }
+ if (crate_name.length () > kMaxNameLength)
+ {
+ error = Error (Location (), "crate name cannot exceed %lu characters",
+ (unsigned long) kMaxNameLength);
+ return false;
+ }
+ for (auto &c : crate_name)
+ {
+ if (!(ISALNUM (c) || c == '_'))
+ {
+ error = Error (Location (),
+ "invalid character %<%c%> in crate name: %<%s%>", c,
+ crate_name.c_str ());
+ return false;
+ }
+ }
+ return true;
+}
+
+void
+Session::init ()
+{
+ options.target_data.insert_key_value_pair ("target_pointer_width",
+ std::to_string (POINTER_SIZE));
+ options.target_data.insert_key_value_pair ("target_endian", BYTES_BIG_ENDIAN
+ ? "big"
+ : "little");
+
+ // setup singleton linemap
+ linemap = rust_get_linemap ();
+
+ // setup backend to GCC GIMPLE
+ backend = rust_get_backend ();
+
+ // setup mappings class
+ mappings = Analysis::Mappings::get ();
+}
+
+/* Initialise default options. Actually called before handle_option, unlike init
+ * itself. */
+void
+Session::init_options ()
+{}
+
+// Handle option selection.
+bool
+Session::handle_option (
+ enum opt_code code, const char *arg, HOST_WIDE_INT value ATTRIBUTE_UNUSED,
+ int kind ATTRIBUTE_UNUSED, location_t loc ATTRIBUTE_UNUSED,
+ const struct cl_option_handlers *handlers ATTRIBUTE_UNUSED)
+{
+ // used to store whether results of various stuff are successful
+ bool ret = true;
+
+ // Handles options as listed in lang.opt.
+ switch (code)
+ {
+ case OPT_I:
+ case OPT_L: {
+ // TODO: add search path
+ const std::string p = std::string (arg);
+ add_search_path (p);
+ }
+ break;
+
+ case OPT_frust_crate_:
+ // set the crate name
+ if (arg != nullptr)
+ {
+ auto error = Error (Location (), std::string ());
+ if ((ret = validate_crate_name (arg, error)))
+ {
+ options.set_crate_name (arg);
+ options.crate_name_set_manually = true;
+ }
+ else
+ {
+ rust_assert (!error.message.empty ());
+ error.emit_error ();
+ }
+ }
+ else
+ ret = false;
+ break;
+
+ case OPT_frust_dump_:
+ // enable dump and return whether this was successful
+ if (arg != nullptr)
+ {
+ ret = enable_dump (std::string (arg));
+ }
+ else
+ {
+ ret = false;
+ }
+ break;
+
+ case OPT_frust_mangling_:
+ Compile::Mangler::set_mangling (flag_rust_mangling);
+ break;
+
+ case OPT_frust_cfg_: {
+ auto string_arg = std::string (arg);
+ ret = handle_cfg_option (string_arg);
+ break;
+ }
+
+ case OPT_frust_edition_:
+ options.set_edition (flag_rust_edition);
+ break;
+
+ case OPT_frust_metadata_output_:
+ options.set_metadata_output (arg);
+ break;
+
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+bool
+Session::handle_cfg_option (std::string &input)
+{
+ std::string key;
+ std::string value;
+
+ // Refactor this if needed
+ if (!parse_cfg_option (input, key, value))
+ {
+ rust_error_at (
+ Location (),
+ "invalid argument to %<-frust-cfg%>: Accepted formats are "
+ "%<-frust-cfg=key%> or %<-frust-cfg=key=\"value\"%> (quoted)");
+ return false;
+ }
+
+ if (value.empty ())
+ // rustc does not seem to error on dup key
+ options.target_data.insert_key (key);
+ else
+ options.target_data.insert_key_value_pair (key, value);
+
+ return true;
+}
+
+/* Enables a certain dump depending on the name passed in. Returns true if
+ * name is valid, false otherwise. */
+bool
+Session::enable_dump (std::string arg)
+{
+ if (arg.empty ())
+ {
+ rust_error_at (
+ Location (),
+ "dump option was not given a name. choose %<lex%>, %<parse%>, "
+ "%<register_plugins%>, %<injection%>, %<expansion%>, %<resolution%>,"
+ " %<target_options%>, %<hir%>, or %<all%>");
+ return false;
+ }
+
+ if (arg == "all")
+ {
+ options.enable_all_dump_options ();
+ }
+ else if (arg == "lex")
+ {
+ options.enable_dump_option (CompileOptions::LEXER_DUMP);
+ }
+ else if (arg == "parse")
+ {
+ options.enable_dump_option (CompileOptions::PARSER_AST_DUMP);
+ }
+ else if (arg == "ast-pretty")
+ {
+ options.enable_dump_option (CompileOptions::AST_DUMP_PRETTY);
+ }
+ else if (arg == "register_plugins")
+ {
+ options.enable_dump_option (CompileOptions::REGISTER_PLUGINS_DUMP);
+ }
+ else if (arg == "injection")
+ {
+ options.enable_dump_option (CompileOptions::INJECTION_DUMP);
+ }
+ else if (arg == "expansion")
+ {
+ options.enable_dump_option (CompileOptions::EXPANSION_DUMP);
+ }
+ else if (arg == "resolution")
+ {
+ options.enable_dump_option (CompileOptions::RESOLUTION_DUMP);
+ }
+ else if (arg == "target_options")
+ {
+ options.enable_dump_option (CompileOptions::TARGET_OPTION_DUMP);
+ }
+ else if (arg == "hir")
+ {
+ options.enable_dump_option (CompileOptions::HIR_DUMP);
+ }
+ else if (arg == "hir-pretty")
+ {
+ options.enable_dump_option (CompileOptions::HIR_DUMP_PRETTY);
+ }
+ else
+ {
+ rust_error_at (
+ Location (),
+ "dump option %qs was unrecognised. choose %<lex%>, %<parse%>, "
+ "%<register_plugins%>, %<injection%>, %<expansion%>, %<resolution%>,"
+ " %<target_options%>, or %<hir%>",
+ arg.c_str ());
+ return false;
+ }
+ return true;
+}
+
+/* Actual main entry point for front-end. Called from langhook to parse files.
+ */
+void
+Session::handle_input_files (int num_files, const char **files)
+{
+ if (num_files != 1)
+ rust_fatal_error (Location (),
+ "only one file may be specified on the command line");
+
+ const auto &file = files[0];
+
+ if (options.crate_name.empty ())
+ {
+ auto filename = "-";
+ if (num_files > 0)
+ filename = files[0];
+
+ auto crate_name = infer_crate_name (filename);
+ rust_debug ("inferred crate name: %s", crate_name.c_str ());
+ // set the preliminary crate name here
+ // we will figure out the real crate name in `handle_crate_name`
+ options.set_crate_name (crate_name);
+ }
+
+ CrateNum crate_num = mappings->get_next_crate_num (options.get_crate_name ());
+ mappings->set_current_crate (crate_num);
+
+ rust_debug ("Attempting to parse file: %s", file);
+ compile_crate (file);
+}
+
+void
+Session::handle_crate_name (const AST::Crate &parsed_crate)
+{
+ auto mappings = Analysis::Mappings::get ();
+ auto crate_name_changed = false;
+ auto error = Error (Location (), std::string ());
+
+ for (const auto &attr : parsed_crate.inner_attrs)
+ {
+ if (attr.get_path () != "crate_name")
+ continue;
+ if (!attr.has_attr_input ())
+ {
+ rust_error_at (attr.get_locus (),
+ "%<crate_name%> accepts one argument");
+ continue;
+ }
+
+ auto &literal
+ = static_cast<AST::AttrInputLiteral &> (attr.get_attr_input ());
+ const auto &msg_str = literal.get_literal ().as_string ();
+ if (!validate_crate_name (msg_str, error))
+ {
+ error.locus = attr.get_locus ();
+ error.emit_error ();
+ continue;
+ }
+
+ auto options = Session::get_instance ().options;
+ if (options.crate_name_set_manually && (options.crate_name != msg_str))
+ {
+ rust_error_at (attr.get_locus (),
+ "%<-frust-crate-name%> and %<#[crate_name]%> are "
+ "required to match, but %qs does not match %qs",
+ options.crate_name.c_str (), msg_str.c_str ());
+ }
+ crate_name_changed = true;
+ options.set_crate_name (msg_str);
+ mappings->set_crate_name (mappings->get_current_crate (), msg_str);
+ }
+
+ options.crate_name_set_manually |= crate_name_changed;
+ if (!options.crate_name_set_manually
+ && !validate_crate_name (options.crate_name, error))
+ {
+ error.emit_error ();
+ rust_inform (linemap->get_location (0),
+ "crate name inferred from this file");
+ }
+}
+
+// Parses a single file with filename filename.
+void
+Session::compile_crate (const char *filename)
+{
+ RAIIFile file_wrap (filename);
+ if (!file_wrap.ok ())
+ {
+ rust_error_at (Location (), "cannot open filename %s: %m", filename);
+ return;
+ }
+
+ // parse file here
+ /* create lexer and parser - these are file-specific and so aren't instance
+ * variables */
+ Lexer lex (filename, std::move (file_wrap), linemap);
+ Parser<Lexer> parser (lex);
+
+ // generate crate from parser
+ std::unique_ptr<AST::Crate> ast_crate = parser.parse_crate ();
+
+ // handle crate name
+ handle_crate_name (*ast_crate.get ());
+
+ // dump options
+ if (options.dump_option_enabled (CompileOptions::LEXER_DUMP))
+ {
+ dump_lex (parser);
+ }
+ if (options.dump_option_enabled (CompileOptions::PARSER_AST_DUMP))
+ {
+ dump_ast (parser, *ast_crate.get ());
+ }
+ if (options.dump_option_enabled (CompileOptions::AST_DUMP_PRETTY))
+ {
+ dump_ast_pretty (*ast_crate.get ());
+ }
+ if (options.dump_option_enabled (CompileOptions::TARGET_OPTION_DUMP))
+ {
+ options.target_data.dump_target_options ();
+ }
+
+ if (saw_errors ())
+ return;
+
+ // setup the mappings for this AST
+ CrateNum current_crate = mappings->get_current_crate ();
+ AST::Crate &parsed_crate
+ = mappings->insert_ast_crate (std::move (ast_crate), current_crate);
+
+ /* basic pipeline:
+ * - lex
+ * - parse
+ * - register plugins (dummy stage for now) - attribute injection? what is
+ * this? (attribute injection is injecting attributes specified in command
+ * line into crate root)
+ * - injection (some lint checks or dummy, register builtin macros, crate
+ * injection)
+ * - expansion (expands all macros, maybe build test harness, AST
+ * validation, maybe macro crate)
+ * - resolution (name resolution, type resolution, maybe feature checking,
+ * maybe buffered lints)
+ * TODO not done */
+
+ rust_debug ("\033[0;31mSUCCESSFULLY PARSED CRATE \033[0m");
+
+ // If -fsyntax-only was passed, we can just skip the remaining passes.
+ // Parsing errors are already emitted in `parse_crate()`
+ if (flag_syntax_only)
+ return;
+
+ // register plugins pipeline stage
+ register_plugins (parsed_crate);
+ rust_debug ("\033[0;31mSUCCESSFULLY REGISTERED PLUGINS \033[0m");
+ if (options.dump_option_enabled (CompileOptions::REGISTER_PLUGINS_DUMP))
+ {
+ // TODO: what do I dump here?
+ }
+
+ // injection pipeline stage
+ injection (parsed_crate);
+ rust_debug ("\033[0;31mSUCCESSFULLY FINISHED INJECTION \033[0m");
+ if (options.dump_option_enabled (CompileOptions::INJECTION_DUMP))
+ {
+ // TODO: what do I dump here? injected crate names?
+ }
+
+ Analysis::AttributeChecker ().go (parsed_crate);
+
+ // expansion pipeline stage
+ expansion (parsed_crate);
+ rust_debug ("\033[0;31mSUCCESSFULLY FINISHED EXPANSION \033[0m");
+ if (options.dump_option_enabled (CompileOptions::EXPANSION_DUMP))
+ {
+ // dump AST with expanded stuff
+ rust_debug ("BEGIN POST-EXPANSION AST DUMP");
+ dump_ast_expanded (parser, parsed_crate);
+ rust_debug ("END POST-EXPANSION AST DUMP");
+ }
+
+ // resolution pipeline stage
+ Resolver::NameResolution::Resolve (parsed_crate);
+ if (options.dump_option_enabled (CompileOptions::RESOLUTION_DUMP))
+ {
+ // TODO: what do I dump here? resolved names? AST with resolved names?
+ }
+
+ if (saw_errors ())
+ return;
+
+ // lower AST to HIR
+ std::unique_ptr<HIR::Crate> lowered
+ = HIR::ASTLowering::Resolve (parsed_crate);
+ if (saw_errors ())
+ return;
+
+ // add the mappings to it
+ HIR::Crate &hir = mappings->insert_hir_crate (std::move (lowered));
+ if (options.dump_option_enabled (CompileOptions::HIR_DUMP))
+ {
+ dump_hir (hir);
+ }
+ if (options.dump_option_enabled (CompileOptions::HIR_DUMP_PRETTY))
+ {
+ dump_hir_pretty (hir);
+ }
+
+ // type resolve
+ Resolver::TypeResolution::Resolve (hir);
+ if (options.dump_option_enabled (CompileOptions::TYPE_RESOLUTION_DUMP))
+ {
+ dump_type_resolution (hir);
+ }
+
+ if (saw_errors ())
+ return;
+
+ // Various HIR error passes. The privacy pass happens before the unsafe checks
+ Privacy::Resolver::resolve (hir);
+ if (saw_errors ())
+ return;
+
+ HIR::UnsafeChecker ().go (hir);
+ HIR::ConstChecker ().go (hir);
+
+ if (saw_errors ())
+ return;
+
+ // do compile to gcc generic
+ Compile::Context ctx (backend);
+ Compile::CompileCrate::Compile (hir, &ctx);
+
+ // we can't do static analysis if there are errors to worry about
+ if (!saw_errors ())
+ {
+ // lints
+ Analysis::ScanDeadcode::Scan (hir);
+ Analysis::UnusedVariables::Lint (ctx);
+
+ // metadata
+ bool specified_emit_metadata
+ = flag_rust_embed_metadata || options.metadata_output_path_set ();
+ if (!specified_emit_metadata)
+ {
+ Metadata::PublicInterface::ExportTo (
+ hir, Metadata::PublicInterface::expected_metadata_filename ());
+ }
+ else
+ {
+ if (flag_rust_embed_metadata)
+ Metadata::PublicInterface::Export (hir);
+ if (options.metadata_output_path_set ())
+ Metadata::PublicInterface::ExportTo (
+ hir, options.get_metadata_output ());
+ }
+ }
+
+ // pass to GCC middle-end
+ ctx.write_to_backend ();
+}
+
+void
+Session::register_plugins (AST::Crate &crate ATTRIBUTE_UNUSED)
+{
+ rust_debug ("ran register_plugins (with no body)");
+}
+
+// TODO: move somewhere else
+bool
+contains_name (const AST::AttrVec &attrs, std::string name)
+{
+ for (const auto &attr : attrs)
+ {
+ if (attr.get_path () == name)
+ return true;
+ }
+
+ return false;
+}
+
+void
+Session::injection (AST::Crate &crate)
+{
+ rust_debug ("started injection");
+
+ // lint checks in future maybe?
+
+ // register builtin macros
+ /* In rustc, builtin macros are divided into 3 categories depending on use -
+ * "bang" macros, "attr" macros, and "derive" macros. I think the meanings
+ * of these categories should be fairly obvious to anyone who has used rust.
+ * Builtin macro list by category: Bang
+ * - asm
+ * - assert
+ * - cfg
+ * - column
+ * - compile_error
+ * - concat_idents
+ * - concat
+ * - env
+ * - file
+ * - format_args_nl
+ * - format_args
+ * - global_asm
+ * - include_bytes
+ * - include_str
+ * - include
+ * - line
+ * - log_syntax
+ * - module_path
+ * - option_env
+ * - stringify
+ * - trace_macros
+ * Attr
+ * - bench
+ * - global_allocator
+ * - test
+ * - test_case
+ * Derive
+ * - Clone
+ * - Copy
+ * - Debug
+ * - Default
+ * - Eq
+ * - Hash
+ * - Ord
+ * - PartialEq
+ * - PartialOrd
+ * - RustcDecodable
+ * - RustcEncodable
+ * rustc also has a "quote" macro that is defined differently and is
+ * supposedly not stable so eh. */
+ /* TODO: actually implement injection of these macros. In particular, derive
+ * macros, cfg, and test should be prioritised since they seem to be used
+ * the most. */
+
+ // crate injection
+ std::vector<std::string> names;
+ if (contains_name (crate.inner_attrs, "no_core"))
+ {
+ // no prelude
+ injected_crate_name = "";
+ }
+ else if (contains_name (crate.inner_attrs, "no_std"))
+ {
+ names.push_back ("core");
+
+ if (!contains_name (crate.inner_attrs, "compiler_builtins"))
+ {
+ names.push_back ("compiler_builtins");
+ }
+
+ injected_crate_name = "core";
+ }
+ else
+ {
+ names.push_back ("std");
+
+ injected_crate_name = "std";
+ }
+
+ // reverse iterate through names to insert crate items in "forward" order at
+ // beginning of crate
+ for (auto it = names.rbegin (); it != names.rend (); ++it)
+ {
+ // create "macro use" attribute for use on extern crate item to enable
+ // loading macros from it
+ AST::Attribute attr (AST::SimplePath::from_str ("macro_use", Location ()),
+ nullptr);
+
+ // create "extern crate" item with the name
+ std::unique_ptr<AST::ExternCrate> extern_crate (
+ new AST::ExternCrate (*it, AST::Visibility::create_error (),
+ {std::move (attr)},
+ Linemap::unknown_location ()));
+
+ // insert at beginning
+ // crate.items.insert (crate.items.begin (), std::move (extern_crate));
+ }
+
+ // create use tree path
+ // prelude is injected_crate_name
+ // FIXME: Once we do want to include the standard library, add the prelude
+ // use item
+ // std::vector<AST::SimplePathSegment> segments
+ // = {AST::SimplePathSegment (injected_crate_name, Location ()),
+ // AST::SimplePathSegment ("prelude", Location ()),
+ // AST::SimplePathSegment ("v1", Location ())};
+ // // create use tree and decl
+ // std::unique_ptr<AST::UseTreeGlob> use_tree (
+ // new AST::UseTreeGlob (AST::UseTreeGlob::PATH_PREFIXED,
+ // AST::SimplePath (std::move (segments)), Location ()));
+ // AST::Attribute prelude_attr (AST::SimplePath::from_str ("prelude_import",
+ // Location ()),
+ // nullptr);
+ // std::unique_ptr<AST::UseDeclaration> use_decl (
+ // new AST::UseDeclaration (std::move (use_tree),
+ // AST::Visibility::create_error (),
+ // {std::move (prelude_attr)}, Location ()));
+
+ // crate.items.insert (crate.items.begin (), std::move (use_decl));
+
+ /* TODO: potentially add checking attribute crate type? I can't figure out
+ * what this does currently comment says "Unconditionally collect crate
+ * types from attributes to make them used", which presumably refers to
+ * checking the linkage info by "crate_type". It also seems to ensure that
+ * an invalid crate type is not specified, so maybe just do that. Valid
+ * crate types: bin lib dylib staticlib cdylib rlib proc-macro */
+
+ // this crate type will have options affecting the metadata ouput
+
+ rust_debug ("finished injection");
+}
+
+void
+Session::expansion (AST::Crate &crate)
+{
+ rust_debug ("started expansion");
+
+ /* rustc has a modification to windows PATH temporarily here, which may end
+ * up being required */
+
+ // create macro expansion config?
+ // if not, would at least have to configure recursion_limit
+ ExpansionCfg cfg;
+
+ // create extctxt? from parse session, cfg, and resolver?
+ /* expand by calling cxtctxt object's monotonic_expander's expand_crate
+ * method. */
+ MacroExpander expander (crate, cfg, *this);
+ expander.expand_crate ();
+
+ // error reporting - check unused macros, get missing fragment specifiers
+
+ // build test harness
+
+ // ast validation (also with proc macro decls)
+
+ // maybe create macro crate if not rustdoc
+
+ rust_debug ("finished expansion");
+}
+
+void
+Session::dump_lex (Parser<Lexer> &parser) const
+{
+ std::ofstream out;
+ out.open (kLexDumpFile);
+ if (out.fail ())
+ {
+ rust_error_at (Linemap::unknown_location (), "cannot open %s:%m; ignored",
+ kLexDumpFile);
+ return;
+ }
+
+ // TODO: rewrite lexer dump or something so that it allows for the crate
+ // to already be parsed
+ parser.debug_dump_lex_output (out);
+ out.close ();
+}
+
+void
+Session::dump_ast (Parser<Lexer> &parser, AST::Crate &crate) const
+{
+ std::ofstream out;
+ out.open (kASTDumpFile);
+ if (out.fail ())
+ {
+ rust_error_at (Linemap::unknown_location (), "cannot open %s:%m; ignored",
+ kASTDumpFile);
+ return;
+ }
+
+ parser.debug_dump_ast_output (crate, out);
+ out.close ();
+}
+
+void
+Session::dump_ast_pretty (AST::Crate &crate) const
+{
+ std::ofstream out;
+ out.open (kASTPrettyDumpFile);
+ if (out.fail ())
+ {
+ rust_error_at (Linemap::unknown_location (), "cannot open %s:%m; ignored",
+ kASTDumpFile);
+ return;
+ }
+
+ AST::Dump (out).go (crate);
+
+ out.close ();
+}
+
+void
+Session::dump_ast_expanded (Parser<Lexer> &parser, AST::Crate &crate) const
+{
+ std::ofstream out;
+ out.open (kASTExpandedDumpFile);
+ if (out.fail ())
+ {
+ rust_error_at (Linemap::unknown_location (), "cannot open %s:%m; ignored",
+ kASTExpandedDumpFile);
+ return;
+ }
+
+ parser.debug_dump_ast_output (crate, out);
+ out.close ();
+}
+
+void
+Session::dump_hir (HIR::Crate &crate) const
+{
+ std::ofstream out;
+ out.open (kHIRDumpFile);
+ if (out.fail ())
+ {
+ rust_error_at (Linemap::unknown_location (), "cannot open %s:%m; ignored",
+ kHIRDumpFile);
+ return;
+ }
+
+ out << crate.as_string ();
+ out.close ();
+}
+
+void
+Session::dump_hir_pretty (HIR::Crate &crate) const
+{
+ std::ofstream out;
+ out.open (kHIRPrettyDumpFile);
+ if (out.fail ())
+ {
+ rust_error_at (Linemap::unknown_location (), "cannot open %s:%m; ignored",
+ kHIRPrettyDumpFile);
+ return;
+ }
+
+ HIR::Dump (out).go (crate);
+ out.close ();
+}
+
+void
+Session::dump_type_resolution (HIR::Crate &hir) const
+{
+ std::ofstream out;
+ out.open (kHIRTypeResolutionDumpFile);
+ if (out.fail ())
+ {
+ rust_error_at (Linemap::unknown_location (), "cannot open %s:%m; ignored",
+ kHIRTypeResolutionDumpFile);
+ return;
+ }
+
+ Resolver::TypeResolverDump::go (hir, out);
+ out.close ();
+}
+
+// imports
+
+NodeId
+Session::load_extern_crate (const std::string &crate_name, Location locus)
+{
+ // has it already been loaded?
+ CrateNum found_crate_num = UNKNOWN_CREATENUM;
+ bool found = mappings->lookup_crate_name (crate_name, found_crate_num);
+ if (found)
+ {
+ NodeId resolved_node_id = UNKNOWN_NODEID;
+ bool resolved
+ = mappings->crate_num_to_nodeid (found_crate_num, resolved_node_id);
+ rust_assert (resolved);
+
+ return resolved_node_id;
+ }
+
+ std::string relative_import_path = "";
+ Import::Stream *s
+ = Import::open_package (crate_name, locus, relative_import_path);
+ if (s == NULL)
+ {
+ rust_error_at (locus, "failed to locate crate %<%s%>",
+ crate_name.c_str ());
+ return UNKNOWN_NODEID;
+ }
+
+ Imports::ExternCrate extern_crate (*s);
+ bool ok = extern_crate.load (locus);
+ if (!ok)
+ {
+ rust_error_at (locus, "failed to load crate metadata");
+ return UNKNOWN_NODEID;
+ }
+
+ // ensure the current vs this crate name don't collide
+ const std::string current_crate_name = mappings->get_current_crate_name ();
+ if (current_crate_name.compare (extern_crate.get_crate_name ()) == 0)
+ {
+ rust_error_at (locus, "current crate name %<%s%> collides with this",
+ current_crate_name.c_str ());
+ return UNKNOWN_NODEID;
+ }
+
+ // setup mappings
+ CrateNum saved_crate_num = mappings->get_current_crate ();
+ CrateNum crate_num
+ = mappings->get_next_crate_num (extern_crate.get_crate_name ());
+ mappings->set_current_crate (crate_num);
+
+ // then lets parse this as a 2nd crate
+ Lexer lex (extern_crate.get_metadata ());
+ Parser<Lexer> parser (lex);
+ std::unique_ptr<AST::Crate> metadata_crate = parser.parse_crate ();
+ AST::Crate &parsed_crate
+ = mappings->insert_ast_crate (std::move (metadata_crate), crate_num);
+
+ // name resolve it
+ Resolver::NameResolution::Resolve (parsed_crate);
+
+ // perform hir lowering
+ std::unique_ptr<HIR::Crate> lowered
+ = HIR::ASTLowering::Resolve (parsed_crate);
+ HIR::Crate &hir = mappings->insert_hir_crate (std::move (lowered));
+
+ // perform type resolution
+ Resolver::TypeResolution::Resolve (hir);
+
+ // always restore the crate_num
+ mappings->set_current_crate (saved_crate_num);
+
+ return parsed_crate.get_node_id ();
+}
+//
+
+void
+TargetOptions::dump_target_options () const
+{
+ std::ofstream out;
+ out.open (kTargetOptionsDumpFile);
+ if (out.fail ())
+ {
+ rust_error_at (Linemap::unknown_location (), "cannot open %s:%m; ignored",
+ kTargetOptionsDumpFile);
+ return;
+ }
+
+ if (features.empty ())
+ {
+ out << "No target options available!\n";
+ }
+
+ for (const auto &pairs : features)
+ {
+ for (const auto &value : pairs.second)
+ out << pairs.first + ": \"" + value + "\"\n";
+
+ if (pairs.second.empty ())
+ out << pairs.first + "\n";
+ }
+
+ out.close ();
+}
+
+void
+TargetOptions::init_derived_values ()
+{
+ // enable derived values based on target families
+ if (has_key_value_pair ("target_family", "unix"))
+ insert_key ("unix");
+ if (has_key_value_pair ("target_family", "windows"))
+ insert_key ("windows");
+
+ // implicitly enable features - this should not be required in general
+ if (has_key_value_pair ("target_feature", "aes"))
+ enable_implicit_feature_reqs ("aes");
+ if (has_key_value_pair ("target_feature", "avx"))
+ enable_implicit_feature_reqs ("sse4.2");
+ if (has_key_value_pair ("target_feature", "avx2"))
+ enable_implicit_feature_reqs ("avx");
+ if (has_key_value_pair ("target_feature", "pclmulqdq"))
+ enable_implicit_feature_reqs ("sse2");
+ if (has_key_value_pair ("target_feature", "sha"))
+ enable_implicit_feature_reqs ("sse2");
+ if (has_key_value_pair ("target_feature", "sse2"))
+ enable_implicit_feature_reqs ("sse");
+ if (has_key_value_pair ("target_feature", "sse3"))
+ enable_implicit_feature_reqs ("sse2");
+ if (has_key_value_pair ("target_feature", "sse4.1"))
+ enable_implicit_feature_reqs ("sse3");
+ if (has_key_value_pair ("target_feature", "sse4.2"))
+ enable_implicit_feature_reqs ("sse4.1");
+ if (has_key_value_pair ("target_feature", "ssse3"))
+ enable_implicit_feature_reqs ("sse3");
+}
+
+void
+TargetOptions::enable_implicit_feature_reqs (std::string feature)
+{
+ if (feature == "aes")
+ enable_implicit_feature_reqs ("sse2");
+ else if (feature == "avx")
+ enable_implicit_feature_reqs ("sse4.2");
+ else if (feature == "avx2")
+ enable_implicit_feature_reqs ("avx");
+ else if (feature == "fma")
+ enable_implicit_feature_reqs ("avx");
+ else if (feature == "pclmulqdq")
+ enable_implicit_feature_reqs ("sse2");
+ else if (feature == "sha")
+ enable_implicit_feature_reqs ("sse2");
+ else if (feature == "sse2")
+ enable_implicit_feature_reqs ("sse");
+ else if (feature == "sse3")
+ enable_implicit_feature_reqs ("sse2");
+ else if (feature == "sse4.1")
+ enable_implicit_feature_reqs ("sse3");
+ else if (feature == "sse4.2")
+ enable_implicit_feature_reqs ("sse4.1");
+ else if (feature == "ssse3")
+ enable_implicit_feature_reqs ("sse3");
+
+ if (!has_key_value_pair ("target_feature", feature))
+ {
+ insert_key_value_pair ("target_feature", feature);
+
+ rust_debug ("had to implicitly enable feature '%s'!", feature.c_str ());
+ }
+}
+
+// NOTEs:
+/* mrustc compile pipeline:
+ * - target load (pass target spec to parser?)
+ * - parse (convert source to AST)
+ * - load crates (load any explicitly mentioned extern crates [not all of
+ * them])
+ * - expand (AST transformations from attributes and macros, loads remaining
+ * extern crates [std/core and any triggered by macro expansion])
+ * - implicit crates (test harness, allocator crate, panic crate)
+ * - resolve use (annotate every 'use' item with source [supposedly handles
+ * nasty recursion])
+ * - resolve index (generate index of visible items for every module [avoids
+ * recursion in next pass])
+ * - resolve absolute (resolve all paths into either variable names
+ * [types/values] or absolute paths)
+ * - HIR lower (convert modified AST to simpler HIR [both expressions and
+ * module tree])
+ * - resolve type aliases (replace any usages of type aliases with actual
+ * type [except associated types])
+ * - resolve bind (iterate HIR tree and set binding annotations on all
+ * concrete types [avoids path lookups later])
+ * - resolve HIR markings (generate "markings" [e.g. for Copy/Send/Sync/...]
+ * for all types
+ * - sort impls (small pass - sort impls into groups)
+ * - resolve UFCS outer (determine source trait for all top-level <T>::Type
+ * [qualified] paths)
+ * - resolve UFCS paths (do the same, but include for exprs this time. also
+ * normalises results of previous pass [expanding known associated types])
+ * - constant evaluate (evaluate all constants)
+ * - typecheck outer (checks impls are sane)
+ * - typecheck expressions (resolve and check types for all exprs)
+ * - expand HIR annotate (annotate how exprs are used - used for closure
+ * extractions and reborrows)
+ * - expand HIR closures (extract closures into structs implementing Fn*
+ * traits)
+ * - expand HIR vtables (generate vtables for types with dyn dispatch)
+ * - expand HIR calls (converts method and callable calls into explicit
+ * function calls)
+ * - expand HIR reborrows (apply reborrow rules [taking '&mut *v' instead of
+ * 'v'])
+ * - expand HIR erasedtype (replace all erased types 'impl Trait' with the
+ * true type)
+ * - typecheck expressions (validate - double check that previous passes
+ * haven't broke type system rules)
+ * - lower MIR (convert HIR exprs into a control-flow graph [MIR])
+ * - MIR validate (check that the generated MIR is consistent)
+ * - MIR cleanup (perform various transformations on MIR - replace reads of
+ * const items with the item itself; convert casts to unsized types into
+ * 'MakeDst' operations)
+ * - MIR optimise (perform various simple optimisations on the MIR - constant
+ * propagation, dead code elimination, borrow elimination, some inlining)
+ * - MIR validate PO (re-validate the MIR)
+ * - MIR validate full (optionally: perform expensive state-tracking
+ * validation on MIR)
+ * - trans enumerate (enumerate all items needed for code generation,
+ * primarily types used for generics)
+ * - trans auto impls (create magic trait impls as enumerated in previous
+ * pass)
+ * - trans monomorph (generate monomorphised copies of all functions [with
+ * generics replaced with real types])
+ * - MIR optimise inline (run optimisation again, this time with full type
+ * info [primarily for inlining])
+ * - HIR serialise (write out HIR dump [module tree and generic/inline MIR])
+ * - trans codegen (generate final output file: emit C source file and call C
+ * compiler) */
+
+/* rustc compile pipeline (basic, in way less detail):
+ * - parse input (parse .rs to AST)
+ * - name resolution, macro expansion, and configuration (process AST
+ * recursively, resolving paths, expanding macros, processing #[cfg] nodes
+ * [i.e. maybe stripping stuff from AST])
+ * - lower to HIR
+ * - type check and other analyses (e.g. privacy checking)
+ * - lower to MIR and post-processing (and do stuff like borrow checking)
+ * - translation to LLVM IR and LLVM optimisations (produce the .o files)
+ * - linking (link together .o files) */
+
+/* Pierced-together rustc compile pipeline (from source):
+ * - parse input (parse file to crate)
+ * - register plugins (attributes injection, set various options, register
+ * lints, load plugins)
+ * - expansion/configure and expand (initial 'cfg' processing, 'loading
+ * compiler plugins', syntax expansion, secondary 'cfg' expansion, synthesis
+ * of a test harness if required, injection of any std lib dependency and
+ * prelude, and name resolution) - actually documented inline
+ * - seeming pierced-together order: pre-AST expansion lint checks,
+ * registering builtin macros, crate injection, then expand all macros, then
+ * maybe build test harness, AST validation, maybe create a macro crate (if
+ * not rustdoc), name resolution, complete gated feature checking, add all
+ * buffered lints
+ * - create global context (lower to HIR)
+ * - analysis on global context (HIR optimisations? create MIR?)
+ * - code generation
+ * - link */
+} // namespace Rust
+
+#if CHECKING_P
+namespace selftest {
+void
+rust_crate_name_validation_test (void)
+{
+ auto error = Rust::Error (Location (), std::string ());
+ ASSERT_TRUE (Rust::validate_crate_name ("example", error));
+ ASSERT_TRUE (Rust::validate_crate_name ("abcdefg_1234", error));
+ ASSERT_TRUE (Rust::validate_crate_name ("1", error));
+ // FIXME: The next test does not pass as of current implementation
+ // ASSERT_TRUE (Rust::CompileOptions::validate_crate_name ("惊吓"));
+ // NOTE: - is not allowed in the crate name ...
+
+ ASSERT_FALSE (Rust::validate_crate_name ("abcdefg-1234", error));
+ ASSERT_FALSE (Rust::validate_crate_name ("a+b", error));
+ ASSERT_FALSE (Rust::validate_crate_name ("/a+b/", error));
+
+ /* Tests for crate name inference */
+ ASSERT_EQ (Rust::infer_crate_name ("c.rs"), "c");
+ // NOTE: ... but - is allowed when in the filename
+ ASSERT_EQ (Rust::infer_crate_name ("a-b.rs"), "a_b");
+ ASSERT_EQ (Rust::infer_crate_name ("book.rs.txt"), "book.rs");
+#if defined(HAVE_DOS_BASED_FILE_SYSTEM)
+ ASSERT_EQ (Rust::infer_crate_name ("a\\c\\a-b.rs"), "a_b");
+#else
+ ASSERT_EQ (Rust::infer_crate_name ("a/c/a-b.rs"), "a_b");
+#endif
+}
+} // namespace selftest
+#endif // CHECKING_P
new file mode 100644
@@ -0,0 +1,358 @@
+// Copyright (C) 2020-2022 Free Software Foundation, Inc.
+
+// This file is part of GCC.
+
+// GCC is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3, or (at your option) any later
+// version.
+
+// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+// for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with GCC; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+// #include "rust-session-manager.h"
+
+#ifndef RUST_SESSION_MANAGER_H
+#define RUST_SESSION_MANAGER_H
+
+#include "rust-linemap.h"
+#include "rust-backend.h"
+#include "rust-hir-map.h"
+#include "safe-ctype.h"
+
+#include "config.h"
+#include "rust-system.h"
+#include "coretypes.h"
+#include "options.h"
+
+namespace Rust {
+// parser forward decl
+template <typename ManagedTokenSource> class Parser;
+class Lexer;
+// crate forward decl
+namespace AST {
+struct Crate;
+}
+// crate forward decl
+namespace HIR {
+struct Crate;
+}
+
+/* Data related to target, most useful for conditional compilation and
+ * whatever. */
+struct TargetOptions
+{
+ /* TODO: maybe make private and access through helpers to allow changes to
+ * impl */
+ std::unordered_map<std::string, std::unordered_set<std::string> > features;
+
+public:
+ // Returns whether a key is defined in the feature set.
+ bool has_key (std::string key) const
+ {
+ return features.find (key) != features.end ();
+ }
+
+ // Returns whether a key exists with the given value in the feature set.
+ bool has_key_value_pair (std::string key, std::string value) const
+ {
+ auto it = features.find (key);
+ if (it != features.end ())
+ {
+ auto set = it->second;
+ auto it2 = set.find (value);
+ if (it2 != set.end ())
+ return true;
+ }
+ return false;
+ }
+
+ /* Returns the singular value from the key, or if the key has multiple, an
+ * empty string. */
+ std::string get_singular_value (std::string key) const
+ {
+ auto it = features.find (key);
+ if (it != features.end ())
+ {
+ auto set = it->second;
+ if (set.size () == 1)
+ return *set.begin ();
+ }
+ return "";
+ }
+
+ /* Returns all values associated with a key (including none), or an empty
+ * set if no key is found. */
+ std::unordered_set<std::string> get_values_for_key (std::string key) const
+ {
+ auto it = features.find (key);
+ if (it != features.end ())
+ return it->second;
+ return {};
+ }
+
+ /* Inserts a key (no value) into the feature set. This will do nothing if
+ * the key already exists. This returns whether the insertion was successful
+ * (i.e. whether key already existed). */
+ bool insert_key (std::string key)
+ {
+ return features
+ .insert (std::make_pair (key, std::unordered_set<std::string> ()))
+ .second;
+ }
+
+ // Inserts a key-value pair into the feature set.
+ void insert_key_value_pair (std::string key, std::string value)
+ {
+ auto existing_set = get_values_for_key (key);
+ existing_set.insert (std::move (value));
+ features[std::move (key)] = std::move (existing_set);
+ }
+
+ // Dump all target options to stderr.
+ void dump_target_options () const;
+
+ /* Creates derived values and implicit enables after all target info is
+ * added (e.g. "unix"). */
+ void init_derived_values ();
+
+ /* Enables all requirements for the feature given, and will enable feature
+ * itself if not enabled. */
+ void enable_implicit_feature_reqs (std::string feature);
+
+ /* According to reference, Rust uses either multi-map key-values or just
+ * values (although values may be aliases for a key-value value). This seems
+ * like overkill. Thus, depending on whether the attributes used in cfg are
+ * fixed or not, I think I'll either put each non-multimap "key-value" as a
+ * separate field and have the multimap "key-values" in a regular map for
+ * that one key, or actually use a multimap.
+ *
+ * rustc itself uses a set of key-value tuples where the second tuple
+ * element is optional. This gets rid of the requirement to make a
+ * multi-map, I guess, but seems like it might make search slow (unless all
+ * "is defined"-only ones have empty string as second element). */
+ /* cfg attributes:
+ * - target_arch: single value
+ * - target_feature: multiple values possible
+ * - target_os: single value
+ * - target_family: single value (or no value?)
+ * - unix: set when target_family = "unix"
+ * - windows: set when target_family = "windows"
+ * - if these are just syntactic sugar, then maybe have a separate set or
+ * map for this kind of stuff
+ * - target_env: set when needed for disambiguation about ABI - usually
+ * empty string for GNU, complicated
+ * - seems to be a single value (if any)
+ * - target_endian: single value; "little" or "big"
+ * - target_pointer_width: single value, "32" for 32-bit pointers, etc.
+ * - target_vendor, single value
+ * - test: set when testing is being done
+ * - again, seems similar to a "is defined" rather than "is equal to" like
+ * unix
+ * - debug_assertions: seems to "is defined"
+ * - proc_macro: no idea, bad docs. seems to be boolean, so maybe "is
+ * defined"
+ */
+};
+
+// Defines compiler options (e.g. dump, etc.).
+struct CompileOptions
+{
+ enum DumpOption
+ {
+ LEXER_DUMP,
+ PARSER_AST_DUMP,
+ AST_DUMP_PRETTY,
+ REGISTER_PLUGINS_DUMP,
+ INJECTION_DUMP,
+ EXPANSION_DUMP,
+ RESOLUTION_DUMP,
+ TARGET_OPTION_DUMP,
+ HIR_DUMP,
+ HIR_DUMP_PRETTY,
+ TYPE_RESOLUTION_DUMP,
+ };
+
+ std::set<DumpOption> dump_options;
+
+ /* configuration options - actually useful for conditional compilation and
+ * whatever data related to target arch, features, os, family, env, endian,
+ * pointer width, vendor */
+ TargetOptions target_data;
+ std::string crate_name;
+ bool crate_name_set_manually = false;
+ bool enable_test = false;
+ bool debug_assertions = false;
+ bool proc_macro = false;
+ std::string metadata_output_path;
+
+ enum class Edition
+ {
+ E2015 = 0,
+ E2018,
+ E2021,
+ } edition
+ = Edition::E2015;
+
+ bool dump_option_enabled (DumpOption option) const
+ {
+ return dump_options.find (option) != dump_options.end ();
+ }
+
+ void enable_dump_option (DumpOption option) { dump_options.insert (option); }
+
+ void enable_all_dump_options ()
+ {
+ enable_dump_option (DumpOption::LEXER_DUMP);
+ enable_dump_option (DumpOption::PARSER_AST_DUMP);
+ enable_dump_option (DumpOption::AST_DUMP_PRETTY);
+ enable_dump_option (DumpOption::REGISTER_PLUGINS_DUMP);
+ enable_dump_option (DumpOption::INJECTION_DUMP);
+ enable_dump_option (DumpOption::EXPANSION_DUMP);
+ enable_dump_option (DumpOption::RESOLUTION_DUMP);
+ enable_dump_option (DumpOption::TARGET_OPTION_DUMP);
+ enable_dump_option (DumpOption::HIR_DUMP);
+ enable_dump_option (DumpOption::HIR_DUMP_PRETTY);
+ enable_dump_option (DumpOption::TYPE_RESOLUTION_DUMP);
+ }
+
+ void set_crate_name (std::string name)
+ {
+ rust_assert (!name.empty ());
+
+ crate_name = std::move (name);
+ }
+
+ const std::string &get_crate_name () const
+ {
+ rust_assert (!crate_name.empty ());
+ return crate_name;
+ }
+
+ void set_edition (int raw_edition)
+ {
+ edition = static_cast<Edition> (raw_edition);
+ }
+
+ const Edition &get_edition () { return edition; }
+
+ void set_metadata_output (const std::string &path)
+ {
+ metadata_output_path = path;
+ }
+
+ const std::string &get_metadata_output () const
+ {
+ return metadata_output_path;
+ }
+
+ bool metadata_output_path_set () const
+ {
+ return !metadata_output_path.empty ();
+ }
+};
+
+/* Defines a compiler session. This is for a single compiler invocation, so
+ * potentially includes parsing multiple crates. */
+struct Session
+{
+ CompileOptions options;
+ /* This should really be in a per-crate storage area but it is wiped with
+ * every file so eh. */
+ std::string injected_crate_name;
+
+ /* extra files get included during late stages of compilation (e.g. macro
+ * expansion) */
+ std::vector<std::string> extra_files;
+
+ // backend wrapper to GCC GENERIC
+ Backend *backend;
+
+ // backend linemap
+ Linemap *linemap;
+
+ // mappings
+ Analysis::Mappings *mappings;
+
+public:
+ /* Get a reference to the static session instance */
+ static Session &get_instance ();
+
+ Session () = default;
+ ~Session () = default;
+
+ /* This initializes the compiler session. Corresponds to langhook
+ * grs_langhook_init(). Note that this is called after option handling. */
+ void init ();
+
+ // delete those constructors so we don't access the singleton in any
+ // other way than via `get_instance()`
+ Session (Session const &) = delete;
+ void operator= (Session const &) = delete;
+
+ bool handle_option (enum opt_code code, const char *arg, HOST_WIDE_INT value,
+ int kind, location_t loc,
+ const struct cl_option_handlers *handlers);
+ void handle_input_files (int num_files, const char **files);
+ void init_options ();
+ void handle_crate_name (const AST::Crate &parsed_crate);
+
+ /* This function saves the filename data into the session manager using the
+ * `move` semantics, and returns a C-style string referencing the input
+ * std::string */
+ inline const char *include_extra_file (std::string filename)
+ {
+ extra_files.push_back (std::move (filename));
+ return extra_files.back ().c_str ();
+ }
+
+ NodeId load_extern_crate (const std::string &crate_name, Location locus);
+
+private:
+ void compile_crate (const char *filename);
+ bool enable_dump (std::string arg);
+
+ void dump_lex (Parser<Lexer> &parser) const;
+ void dump_ast (Parser<Lexer> &parser, AST::Crate &crate) const;
+ void dump_ast_pretty (AST::Crate &crate) const;
+ void dump_ast_expanded (Parser<Lexer> &parser, AST::Crate &crate) const;
+ void dump_hir (HIR::Crate &crate) const;
+ void dump_hir_pretty (HIR::Crate &crate) const;
+ void dump_type_resolution (HIR::Crate &crate) const;
+
+ // pipeline stages - TODO maybe move?
+ /* Register plugins pipeline stage. TODO maybe move to another object?
+ * Currently dummy stage. In future will handle attribute injection
+ * (top-level inner attribute creation from command line arguments), setting
+ * options maybe, registering lints maybe, loading plugins maybe. */
+ void register_plugins (AST::Crate &crate);
+
+ /* Injection pipeline stage. TODO maybe move to another object? Maybe have
+ * some lint checks (in future, obviously), register builtin macros, crate
+ * injection. */
+ void injection (AST::Crate &crate);
+
+ /* Expansion pipeline stage. TODO maybe move to another object? Expands all
+ * macros, maybe build test harness in future, AST validation, maybe create
+ * macro crate (if not rustdoc).*/
+ void expansion (AST::Crate &crate);
+
+ // handle cfg_option
+ bool handle_cfg_option (std::string &data);
+};
+
+} // namespace Rust
+
+#if CHECKING_P
+namespace selftest {
+extern void
+rust_crate_name_validation_test (void);
+}
+#endif // CHECKING_P
+
+#endif