/* * MRustC - Rust Compiler * - By John Hodge (Mutabah/thePowersGang) * * main.cpp * - Compiler Entrypoint */ #include #include #include #include #include #include #include "parse/lex.hpp" #include "parse/parseerror.hpp" #include "ast/ast.hpp" #include "ast/crate.hpp" #include #include #include "resolve/main_bindings.hpp" #include "hir/main_bindings.hpp" #include "hir_conv/main_bindings.hpp" #include "hir_typeck/main_bindings.hpp" #include "hir_expand/main_bindings.hpp" #include "mir/main_bindings.hpp" #include "trans/main_bindings.hpp" #include "trans/target.hpp" #include "expand/cfg.hpp" #include // tools/common/target_detect.h int g_debug_indent_level = 0; bool g_debug_enabled = true; ::std::string g_cur_phase; ::std::set< ::std::string> g_debug_disable_map; TargetVersion gTargetVersion = TargetVersion::Rustc1_29; void init_debug_list() { g_debug_disable_map.insert( "Parse" ); g_debug_disable_map.insert( "LoadCrates" ); g_debug_disable_map.insert( "Expand" ); g_debug_disable_map.insert( "Dump Expanded" ); g_debug_disable_map.insert( "Implicit Crates" ); g_debug_disable_map.insert( "Resolve Use" ); g_debug_disable_map.insert( "Resolve Index" ); g_debug_disable_map.insert( "Resolve Absolute" ); g_debug_disable_map.insert( "HIR Lower" ); g_debug_disable_map.insert( "Resolve Type Aliases" ); g_debug_disable_map.insert( "Resolve Bind" ); g_debug_disable_map.insert( "Resolve UFCS Outer" ); g_debug_disable_map.insert( "Resolve UFCS paths" ); g_debug_disable_map.insert( "Resolve HIR Markings" ); g_debug_disable_map.insert( "Constant Evaluate" ); g_debug_disable_map.insert( "Typecheck Outer"); g_debug_disable_map.insert( "Typecheck Expressions" ); g_debug_disable_map.insert( "Expand HIR Annotate" ); g_debug_disable_map.insert( "Expand HIR Closures" ); g_debug_disable_map.insert( "Expand HIR Calls" ); g_debug_disable_map.insert( "Expand HIR VTables" ); g_debug_disable_map.insert( "Expand HIR Reborrows" ); g_debug_disable_map.insert( "Expand HIR ErasedType" ); g_debug_disable_map.insert( "Typecheck Expressions (validate)" ); g_debug_disable_map.insert( "Dump HIR" ); g_debug_disable_map.insert( "Lower MIR" ); g_debug_disable_map.insert( "MIR Validate" ); g_debug_disable_map.insert( "MIR Validate Full Early" ); g_debug_disable_map.insert( "Dump MIR" ); g_debug_disable_map.insert( "Constant Evaluate Full" ); g_debug_disable_map.insert( "MIR Cleanup" ); g_debug_disable_map.insert( "MIR Optimise" ); g_debug_disable_map.insert( "MIR Validate PO" ); g_debug_disable_map.insert( "MIR Validate Full" ); g_debug_disable_map.insert( "HIR Serialise" ); g_debug_disable_map.insert( "Trans Enumerate" ); g_debug_disable_map.insert( "Trans Auto Impls" ); g_debug_disable_map.insert( "Trans Monomorph" ); g_debug_disable_map.insert( "MIR Optimise Inline" ); g_debug_disable_map.insert( "Trans Codegen" ); // Mutate this map using an environment variable const char* debug_string = ::std::getenv("MRUSTC_DEBUG"); if( debug_string ) { while( debug_string[0] ) { const char* end = strchr(debug_string, ':'); ::std::string s; if( end ) { s = ::std::string { debug_string, end }; debug_string = end + 1; } else { s = debug_string; } if( g_debug_disable_map.erase(s) == 0 ) { ::std::cerr << "WARN: Unknown compiler phase '" << s << "' in $MRUSTC_DEBUG" << ::std::endl; } if( !end ) { break; } } } } bool debug_enabled_update() { if( g_debug_disable_map.count(g_cur_phase) != 0 ) { return false; } else { return true; } } bool debug_enabled() { return g_debug_enabled; } ::std::ostream& debug_output(int indent, const char* function) { return ::std::cout << g_cur_phase << "- " << RepeatLitStr { " ", indent } << function << ": "; } struct ProgramParams { enum eLastStage { STAGE_PARSE, STAGE_EXPAND, STAGE_RESOLVE, STAGE_TYPECK, STAGE_BORROWCK, STAGE_MIR, STAGE_ALL, } last_stage = STAGE_ALL; ::std::string infile; ::std::string outfile; ::std::string output_dir = ""; ::std::string target = DEFAULT_TARGET_NAME; ::std::string emit_depfile; ::AST::Crate::Type crate_type = ::AST::Crate::Type::Unknown; ::std::string crate_name; ::std::string crate_name_suffix; unsigned opt_level = 0; bool emit_debug_info = false; bool test_harness = false; ::std::vector lib_search_dirs; ::std::vector libraries; ::std::map<::std::string, ::std::string> crate_overrides; // --extern name=path ::std::set< ::std::string> features; struct { bool disable_mir_optimisations = false; bool full_validate = false; bool full_validate_early = false; bool dump_ast = false; bool dump_hir = false; bool dump_mir = false; } debug; struct { ::std::string codegen_type; ::std::string emit_build_command; } codegen; ProgramParams(int argc, char *argv[]); void show_help() const; }; template Rv CompilePhase(const char *name, Fcn f) { ::std::cout << name << ": V V V" << ::std::endl; g_cur_phase = name; g_debug_enabled = debug_enabled_update(); auto start = clock(); auto rv = f(); auto end = clock(); g_cur_phase = ""; g_debug_enabled = debug_enabled_update(); // TODO: Show wall time too? ::std::cout <<"(" << ::std::fixed << ::std::setprecision(2) << static_cast(end - start) / static_cast(CLOCKS_PER_SEC) << " s) "; ::std::cout << name << ": DONE"; ::std::cout << ::std::endl; return rv; } template void CompilePhaseV(const char *name, Fcn f) { CompilePhase(name, [&]() { f(); return 0; }); } /// main! int main(int argc, char *argv[]) { init_debug_list(); ProgramParams params(argc, argv); // Set up cfg values Cfg_SetValue("rust_compiler", "mrustc"); Cfg_SetValueCb("feature", [¶ms](const ::std::string& s) { return params.features.count(s) != 0; }); Target_SetCfg(params.target); if( params.test_harness ) { Cfg_SetFlag("test"); } Expand_Init(); try { // Parse the crate into AST AST::Crate crate = CompilePhase("Parse", [&]() { return Parse_Crate(params.infile); }); crate.m_test_harness = params.test_harness; crate.m_crate_name_suffix = params.crate_name_suffix; if( params.last_stage == ProgramParams::STAGE_PARSE ) { return 0; } // Load external crates. CompilePhaseV("LoadCrates", [&]() { // Hacky! AST::g_crate_overrides = params.crate_overrides; for(const auto& ld : params.lib_search_dirs) { AST::g_crate_load_dirs.push_back(ld); } crate.load_externs(); }); // Iterate all items in the AST, applying syntax extensions CompilePhaseV("Expand", [&]() { Expand(crate); }); if( params.test_harness ) { Expand_TestHarness(crate); } // Extract the crate type and name from the crate attributes auto crate_type = params.crate_type; if( crate_type == ::AST::Crate::Type::Unknown ) { crate_type = crate.m_crate_type; } if( crate_type == ::AST::Crate::Type::Unknown ) { // Assume to be executable crate_type = ::AST::Crate::Type::Executable; } crate.m_crate_type = crate_type; if( crate.m_crate_type == ::AST::Crate::Type::ProcMacro ) { Expand_ProcMacro(crate); } auto crate_name = params.crate_name; if( crate_name == "" ) { crate_name = crate.m_crate_name; } if( crate_name == "" ) { auto s = params.infile.find_last_of('/'); if( s == ::std::string::npos ) s = 0; else s += 1; auto e = params.infile.find_first_of('.', s); if( e == ::std::string::npos ) e = params.infile.size() - s; crate_name = ::std::string(params.infile.begin() + s, params.infile.begin() + e); for(auto& b : crate_name) { if ('0' <= b && b <= '9') { } else if ('A' <= b && b <= 'Z') { } else if (b == '_') { } else if (b == '-') { b = '_'; } else { // TODO: Error? } } } crate.m_crate_name = crate_name; if( params.test_harness ) { crate.m_crate_name += "$test"; } if( params.outfile == "" ) { switch( crate.m_crate_type ) { case ::AST::Crate::Type::RustLib: params.outfile = FMT(params.output_dir << "lib" << crate.m_crate_name << ".hir"); break; case ::AST::Crate::Type::Executable: params.outfile = FMT(params.output_dir << crate.m_crate_name); break; default: params.outfile = FMT(params.output_dir << crate.m_crate_name << ".o"); break; } DEBUG("params.outfile = " << params.outfile); } if( params.debug.dump_ast ) { CompilePhaseV("Dump Expanded", [&]() { Dump_Rust( FMT(params.outfile << "_1_ast.rs").c_str(), crate ); }); } if( params.last_stage == ProgramParams::STAGE_EXPAND ) { return 0; } // Allocator and panic strategies CompilePhaseV("Implicit Crates", [&]() { if( params.test_harness ) { crate.load_extern_crate(Span(), "test"); } if( crate.m_crate_type == ::AST::Crate::Type::Executable || params.test_harness || crate.m_crate_type == ::AST::Crate::Type::ProcMacro ) { bool allocator_crate_loaded = false; ::std::string alloc_crate_name; bool panic_runtime_loaded = false; ::std::string panic_crate_name; bool panic_runtime_needed = false; for(const auto& ec : crate.m_extern_crates) { ::std::ostringstream ss; for(const auto& e : ec.second.m_hir->m_lang_items) ss << e << ","; DEBUG("Looking at lang items from " << ec.first << " : " << ss.str()); if(ec.second.m_hir->m_lang_items.count("mrustc-allocator")) { if( allocator_crate_loaded ) { ERROR(Span(), E0000, "Multiple allocator crates loaded - " << alloc_crate_name << " and " << ec.first); } alloc_crate_name = ec.first; allocator_crate_loaded = true; } if(ec.second.m_hir->m_lang_items.count("mrustc-panic_runtime")) { if( panic_runtime_loaded ) { ERROR(Span(), E0000, "Multiple panic_runtime crates loaded - " << panic_crate_name << " and " << ec.first); } panic_crate_name = ec.first; panic_runtime_loaded = true; } if(ec.second.m_hir->m_lang_items.count("mrustc-needs_panic_runtime")) { panic_runtime_needed = true; } } if( !allocator_crate_loaded ) { crate.load_extern_crate(Span(), "alloc_system"); } if( panic_runtime_needed && !panic_runtime_loaded ) { // TODO: Get a panic method from the command line // - Fall back to abort by default, because mrustc doesn't do unwinding yet. crate.load_extern_crate(Span(), "panic_abort"); } // - `mrustc-main` lang item default crate.m_lang_items.insert(::std::make_pair( ::std::string("mrustc-main"), ::AST::Path("", {AST::PathNode("main")}) )); } }); if( params.emit_depfile != "" ) { ::std::ofstream of { params.emit_depfile }; of << params.outfile << ":"; // - Iterate all loaded files for modules struct H { ::std::ofstream& of; H(::std::ofstream& of): of(of) {} void visit_module(::AST::Module& mod) { if( mod.m_file_info.path != "!" && mod.m_file_info.path.back() != '/' ) { of << " " << mod.m_file_info.path; } // TODO: Should we check anon modules? //for(auto& amod : mod.anon_mods()) { // this->visit_module(*amod); //} for(auto& i : mod.items()) { if(i.data.is_Module()) { this->visit_module(i.data.as_Module()); } } } }; H(of).visit_module(crate.m_root_module); // - Iterate all loaded crates files for(const auto& ec : crate.m_extern_crates) { of << " " << ec.second.m_filename; } // - Iterate all extra files (include! and friends) } // Resolve names to be absolute names (include references to the relevant struct/global/function) // - This does name checking on types and free functions. // - Resolves all identifiers/paths to references CompilePhaseV("Resolve Use", [&]() { Resolve_Use(crate); // - Absolutise and resolve use statements }); CompilePhaseV("Resolve Index", [&]() { Resolve_Index(crate); // - Build up a per-module index of avalable names (faster and simpler later resolve) }); CompilePhaseV("Resolve Absolute", [&]() { Resolve_Absolutise(crate); // - Convert all paths to Absolute or UFCS, and resolve variables }); if( params.debug.dump_ast ) { CompilePhaseV("Temp output - Resolved", [&]() { Dump_Rust( FMT(params.outfile << "_1_ast.rs").c_str(), crate ); }); } if( params.last_stage == ProgramParams::STAGE_RESOLVE ) { return 0; } // -------------------------------------- // HIR Section // -------------------------------------- // Construc the HIR from the AST ::HIR::CratePtr hir_crate = CompilePhase< ::HIR::CratePtr>("HIR Lower", [&]() { return LowerHIR_FromAST(mv$( crate )); }); // Deallocate the original crate crate = ::AST::Crate(); // Replace type aliases (`type`) into the actual type // - Also inserts defaults in trait impls CompilePhaseV("Resolve Type Aliases", [&]() { ConvertHIR_ExpandAliases(*hir_crate); }); // Set up bindings and other useful information. CompilePhaseV("Resolve Bind", [&]() { ConvertHIR_Bind(*hir_crate); }); // Enumerate marker impls on types and other useful metadata CompilePhaseV("Resolve HIR Markings", [&]() { ConvertHIR_Markings(*hir_crate); }); // Determine what trait to use for ::Foo in outer scope CompilePhaseV("Resolve UFCS Outer", [&]() { ConvertHIR_ResolveUFCS_Outer(*hir_crate); }); if( params.debug.dump_hir ) { CompilePhaseV("Dump HIR", [&]() { ::std::ofstream os (FMT(params.outfile << "_2_hir.rs")); HIR_Dump( os, *hir_crate ); }); } // Determine what trait to use for ::Foo (and does some associated type expansion) CompilePhaseV("Resolve UFCS paths", [&]() { ConvertHIR_ResolveUFCS(*hir_crate); }); // Basic constant evalulation (intergers/floats only) CompilePhaseV("Constant Evaluate", [&]() { ConvertHIR_ConstantEvaluate(*hir_crate); }); if( params.debug.dump_hir ) { // DUMP after initial consteval CompilePhaseV("Dump HIR", [&]() { ::std::ofstream os (FMT(params.outfile << "_2_hir.rs")); HIR_Dump( os, *hir_crate ); }); } // === Type checking === // - This can recurse and call the MIR lower to evaluate constants // Check outer items first (types of constants/functions/statics/impls/...) // - Doesn't do any expressions except those in types CompilePhaseV("Typecheck Outer", [&]() { Typecheck_ModuleLevel(*hir_crate); }); // Check the rest of the expressions (including function bodies) CompilePhaseV("Typecheck Expressions", [&]() { Typecheck_Expressions(*hir_crate); }); // === HIR Expansion === // Annotate how each node's result is used CompilePhaseV("Expand HIR Annotate", [&]() { HIR_Expand_AnnotateUsage(*hir_crate); }); // - Now that all types are known, closures can be desugared CompilePhaseV("Expand HIR Closures", [&]() { HIR_Expand_Closures(*hir_crate); }); // - Construct VTables for all traits and impls. // TODO: How early can this be done? CompilePhaseV("Expand HIR VTables", [&]() { HIR_Expand_VTables(*hir_crate); }); // - And calls can be turned into UFCS CompilePhaseV("Expand HIR Calls", [&]() { HIR_Expand_UfcsEverything(*hir_crate); }); CompilePhaseV("Expand HIR Reborrows", [&]() { HIR_Expand_Reborrows(*hir_crate); }); CompilePhaseV("Expand HIR ErasedType", [&]() { HIR_Expand_ErasedType(*hir_crate); }); if( params.debug.dump_hir ) { // DUMP after typecheck (before validation) CompilePhaseV("Dump HIR", [&]() { ::std::ofstream os (FMT(params.outfile << "_2_hir.rs")); HIR_Dump( os, *hir_crate ); }); } // - Ensure that typeck worked (including Fn trait call insertion etc) CompilePhaseV("Typecheck Expressions (validate)", [&]() { Typecheck_Expressions_Validate(*hir_crate); }); if( params.last_stage == ProgramParams::STAGE_TYPECK ) { return 0; } // Lower expressions into MIR CompilePhaseV("Lower MIR", [&]() { HIR_GenerateMIR(*hir_crate); }); if( params.debug.dump_mir ) { // DUMP after generation CompilePhaseV("Dump MIR", [&]() { ::std::ofstream os (FMT(params.outfile << "_3_mir.rs")); MIR_Dump( os, *hir_crate ); }); } // Validate the MIR CompilePhaseV("MIR Validate", [&]() { MIR_CheckCrate(*hir_crate); }); if( params.debug.dump_hir ) { // DUMP after consteval (full HIR again) CompilePhaseV("Dump HIR", [&]() { ::std::ofstream os (FMT(params.outfile << "_2_hir.rs")); HIR_Dump( os, *hir_crate ); }); } // - Expand constants in HIR and virtualise calls CompilePhaseV("MIR Cleanup", [&]() { MIR_CleanupCrate(*hir_crate); }); if( params.debug.full_validate_early || getenv("MRUSTC_FULL_VALIDATE_PREOPT") ) { CompilePhaseV("MIR Validate Full Early", [&]() { MIR_CheckCrate_Full(*hir_crate); }); } // Optimise the MIR CompilePhaseV("MIR Optimise", [&]() { MIR_OptimiseCrate(*hir_crate, params.debug.disable_mir_optimisations); }); if( params.debug.dump_mir ) { // DUMP: After optimisation CompilePhaseV("Dump MIR", [&]() { ::std::ofstream os (FMT(params.outfile << "_3_mir.rs")); MIR_Dump( os, *hir_crate ); }); } CompilePhaseV("MIR Validate PO", [&]() { MIR_CheckCrate(*hir_crate); }); // - Exhaustive MIR validation (follows every code path and checks variable validity) // > DEBUGGING ONLY CompilePhaseV("MIR Validate Full", [&]() { if( params.debug.full_validate || getenv("MRUSTC_FULL_VALIDATE") ) MIR_CheckCrate_Full(*hir_crate); }); if( params.last_stage == ProgramParams::STAGE_MIR ) { return 0; } // TODO: Pass to mark items that are.. // - Signature Exportable (public) // - MIR Exportable (public generic, #[inline], or used by a either of those) // - Require codegen (public or used by an exported function) TransOptions trans_opt; trans_opt.mode = params.codegen.codegen_type == "" ? "c" : params.codegen.codegen_type; trans_opt.build_command_file = params.codegen.emit_build_command; trans_opt.opt_level = params.opt_level; for(const char* libdir : params.lib_search_dirs ) { // Store these paths for use in final linking. hir_crate->m_link_paths.push_back( libdir ); } for(const char* libname : params.libraries ) { hir_crate->m_ext_libs.push_back(::HIR::ExternLibrary { libname }); } trans_opt.emit_debug_info = params.emit_debug_info; // Generate code for non-generic public items (if requested) if( params.test_harness ) { // If the test harness is enabled, override crate type to "Executable" crate_type = ::AST::Crate::Type::Executable; } // TODO: For 1.29 executables/dylibs, add oom/panic shims // Enumerate items to be passed to codegen TransList items = CompilePhase("Trans Enumerate", [&]() { switch( crate_type ) { case ::AST::Crate::Type::Unknown: ::std::cerr << "BUG? Unknown crate type" << ::std::endl; exit(1); break; case ::AST::Crate::Type::RustLib: case ::AST::Crate::Type::RustDylib: case ::AST::Crate::Type::CDylib: return Trans_Enumerate_Public(*hir_crate); case ::AST::Crate::Type::ProcMacro: // TODO: proc macros enumerate twice, once as a library (why?) and again as an executable return Trans_Enumerate_Public(*hir_crate); case ::AST::Crate::Type::Executable: return Trans_Enumerate_Main(*hir_crate); } throw ::std::runtime_error("Invalid crate_type value"); }); // - Generate automatic impls (mainly Clone for 1.29) CompilePhaseV("Trans Auto Impls", [&]() { // TODO: Drop glue generation? Trans_AutoImpls(*hir_crate, items); }); // - Generate monomorphised versions of all functions CompilePhaseV("Trans Monomorph", [&]() { Trans_Monomorphise_List(*hir_crate, items); }); // - Do post-monomorph inlining CompilePhaseV("MIR Optimise Inline", [&]() { MIR_OptimiseCrate_Inlining(*hir_crate, items); }); // - Clean up no-unused functions //CompilePhaseV("Trans Enumerate Cleanup", [&]() { Trans_Enumerate_Cleanup(*hir_crate, items); }); switch(crate_type) { case ::AST::Crate::Type::Unknown: throw ""; case ::AST::Crate::Type::RustLib: // Generate a loadable .o CompilePhaseV("Trans Codegen", [&]() { Trans_Codegen(params.outfile + ".o", trans_opt, *hir_crate, items, /*is_executable=*/false); }); // Save a loadable HIR dump CompilePhaseV("HIR Serialise", [&]() { HIR_Serialise(params.outfile, *hir_crate); }); // TODO: Link metatdata and object into a .rlib //Trans_Link(params.outfile, params.outfile + ".hir", params.outfile + ".o", CodegenOutput::StaticLibrary); break; case ::AST::Crate::Type::RustDylib: // Generate a .so //CompilePhaseV("Trans Codegen", [&]() { Trans_Codegen(params.outfile + ".so", trans_opt, *hir_crate, items, CodegenOutput::DynamicLibrary); }); CompilePhaseV("Trans Codegen", [&]() { Trans_Codegen(params.outfile + ".o", trans_opt, *hir_crate, items, /*is_executable=*/false); }); // Save a loadable HIR dump CompilePhaseV("HIR Serialise", [&]() { HIR_Serialise(params.outfile, *hir_crate); }); // TODO: Add the metadata to the .so as a non-loadable segment //Trans_Link(params.outfile, params.outfile + ".hir", params.outfile + ".o", CodegenOutput::DynamicLibrary); break; case ::AST::Crate::Type::CDylib: // Generate a .so/.dll CompilePhaseV("Trans Codegen", [&]() { Trans_Codegen(params.outfile, trans_opt, *hir_crate, items, /*is_executable=*/false); }); // - No metadata file //Trans_Link(params.outfile, "", params.outfile + ".o", CodegenOutput::DynamicLibrary); break; case ::AST::Crate::Type::ProcMacro: { // Needs: An executable (the actual macro handler), metadata (for `extern crate foo;`) // 1. Generate code for the .o file // TODO: Is the .o actually needed for proc macros? CompilePhaseV("Trans Codegen", [&]() { Trans_Codegen(params.outfile + ".o", trans_opt, *hir_crate, items, /*is_executable=*/false); }); // 2. Generate code for the plugin itself TransList items2 = CompilePhase("Trans Enumerate", [&]() { return Trans_Enumerate_Main(*hir_crate); }); CompilePhaseV("Trans Monomorph", [&]() { Trans_Monomorphise_List(*hir_crate, items2); }); CompilePhaseV("MIR Optimise Inline", [&]() { MIR_OptimiseCrate_Inlining(*hir_crate, items2); }); CompilePhaseV("Trans Codegen", [&]() { Trans_Codegen(params.outfile + "-plugin", trans_opt, *hir_crate, items2, /*is_executable=*/true); }); // - Save a very basic HIR dump, making sure that there's no lang items in it (e.g. `mrustc-main`) hir_crate->m_lang_items.clear(); CompilePhaseV("HIR Serialise", [&]() { HIR_Serialise(params.outfile, *hir_crate); }); //Trans_Link(params.outfile, params.outfile + ".hir", params.outfile + ".o", CodegenOutput::StaticLibrary); //Trans_Link(params.outfile+"-plugin", "", params.outfile + "-plugin.o", CodegenOutput::StaticLibrary); break; } case ::AST::Crate::Type::Executable: CompilePhaseV("Trans Codegen", [&]() { Trans_Codegen(params.outfile, trans_opt, *hir_crate, items, /*is_executable=*/true); }); //Trans_Link(params.outfile, "", params.outfile + ".o", CodegenOutput::Executable); break; } } catch(unsigned int) {} //catch(const CompileError::Base& e) //{ // ::std::cerr << "Parser Error: " << e.what() << ::std::endl; // return 2; //} //catch(const ::std::exception& e) //{ // ::std::cerr << "Misc Error: " << e.what() << ::std::endl; // return 2; //} //catch(const char* e) //{ // ::std::cerr << "Internal Compiler Error: " << e << ::std::endl; // return 2; //} return 0; } ProgramParams::ProgramParams(int argc, char *argv[]) { // Hacky command-line parsing for( int i = 1; i < argc; i ++ ) { const char* arg = argv[i]; if( arg[0] != '-' ) { if (this->infile == "") { this->infile = arg; } else { ::std::cerr << "Unexpected free argument" << ::std::endl; exit(1); } } else if( arg[1] != '-' ) { arg ++; // eat '-' switch( *arg ) { case 'L': if( arg[1] == '\0' ) { if( i == argc - 1 ) { ::std::cerr << "Option " << arg << " requires an argument" << ::std::endl; exit(1); } this->lib_search_dirs.push_back( argv[++i] ); } else { this->lib_search_dirs.push_back( arg+1 ); } continue ; case 'l': if( arg[1] == '\0' ) { if( i == argc - 1 ) { ::std::cerr << "Option " << arg << " requires an argument" << ::std::endl; exit(1); } this->libraries.push_back( argv[++i] ); } else { this->libraries.push_back( arg+1 ); } continue ; case 'C': { ::std::string optname; ::std::string optval; if( arg[1] == '\0' ) { if( i == argc - 1) { ::std::cerr << "Option " << arg << " requires an argument" << ::std::endl; exit(1); } optname = argv[++i]; } else { optname = arg+1; } auto eq_pos = optname.find('='); if( eq_pos != ::std::string::npos ) { optval = optname.substr(eq_pos+1); optname.resize(eq_pos); } auto get_optval = [&]() { if( eq_pos == ::std::string::npos ) { ::std::cerr << "Flag -Z " << optname << " requires an argument" << ::std::endl; exit(1); } }; //auto no_optval = [&]() { // if(eq_pos != ::std::string::npos) { // ::std::cerr << "Flag -Z " << optname << " doesn't take an argument" << ::std::endl; // exit(1); // } // }; if( optname == "emit-build-command" ) { get_optval(); this->codegen.emit_build_command = optval; } else if( optname == "codegen-type" ) { get_optval(); this->codegen.codegen_type = optval; } else if( optname == "emit-depfile" ) { get_optval(); this->emit_depfile = optval; } else { ::std::cerr << "Unknown codegen option: '" << optname << "'" << ::std::endl; exit(1); } } continue; case 'Z': { ::std::string optname; ::std::string optval; if( arg[1] == '\0' ) { if( i == argc - 1) { ::std::cerr << "Option " << arg << " requires an argument" << ::std::endl; exit(1); } optname = argv[++i]; } else { optname = arg+1; } auto eq_pos = optname.find('='); if( eq_pos != ::std::string::npos ) { optval = optname.substr(eq_pos+1); optname.resize(eq_pos); } auto get_optval = [&]() { if( eq_pos == ::std::string::npos ) { ::std::cerr << "Flag -Z " << optname << " requires an argument" << ::std::endl; exit(1); } }; auto no_optval = [&]() { if(eq_pos != ::std::string::npos) { ::std::cerr << "Flag -Z " << optname << " doesn't take an argument" << ::std::endl; exit(1); } }; if( optname == "disable-mir-opt" ) { no_optval(); this->debug.disable_mir_optimisations = true; } else if( optname == "full-validate" ) { no_optval(); this->debug.full_validate = true; } else if( optname == "full-validate-early" ) { no_optval(); this->debug.full_validate_early = true; } else if( optname == "dump-ast" ) { no_optval(); this->debug.dump_ast = true; } else if( optname == "dump-hir" ) { no_optval(); this->debug.dump_hir = true; } else if( optname == "dump-mir" ) { no_optval(); this->debug.dump_mir = true; } else if( optname == "stop-after" ) { get_optval(); if( optval == "parse" ) this->last_stage = STAGE_PARSE; else if( optval == "expand" ) this->last_stage = STAGE_EXPAND; else if( optval == "resolve" ) this->last_stage = STAGE_RESOLVE; else if( optval == "typeck" ) this->last_stage = STAGE_TYPECK; else if( optval == "mir" ) this->last_stage = STAGE_MIR; else { ::std::cerr << "Unknown argument to -Z stop-after - '" << optval << "'" << ::std::endl; exit(1); } } else { ::std::cerr << "Unknown debug option: '" << optname << "'" << ::std::endl; exit(1); } } continue; default: // Fall through to the for loop below break; } for( ; *arg; arg ++ ) { switch(*arg) { // "-o " : Set output file case 'o': if( i == argc - 1 ) { ::std::cerr << "Option -" << *arg << " requires an argument" << ::std::endl; exit(1); } this->outfile = argv[++i]; break; case 'O': this->opt_level = 2; break; case 'g': this->emit_debug_info = true; break; default: ::std::cerr << "Unknown option: '-" << *arg << "'" << ::std::endl; exit(1); } } } else { if( strcmp(arg, "--help") == 0 ) { this->show_help(); exit(0); } else if( strcmp(arg, "--version" ) == 0 ) { ::std::cout << "MRustC " << Version_GetString() << ::std::endl; ::std::cout << "- Build time: " << gsVersion_BuildTime << ::std::endl; ::std::cout << "- Commit: " << gsVersion_GitHash << (gbVersion_GitDirty ? " (dirty tree)" : "") << ::std::endl; exit(0); } // --out-dir >> Set the output directory for automatically-named files else if( strcmp(arg, "--out-dir") == 0) { if (i == argc - 1) { ::std::cerr << "Flag " << arg << " requires an argument" << ::std::endl; exit(1); } this->output_dir = argv[++i]; if( this->output_dir != "" && this->output_dir.back() != '/' ) { this->output_dir += '/'; } } // --extern = >> Override the file to load for `extern crate ;` else if( strcmp(arg, "--extern") == 0 ) { if( i == argc - 1 ) { ::std::cerr << "Option " << arg << " requires an argument" << ::std::endl; exit(1); } const char* desc = argv[++i]; const char* pos = ::std::strchr(desc, '='); if( pos == nullptr ) { ::std::cerr << "--extern takes an argument of the format name=path" << ::std::endl; exit(1); } auto name = ::std::string(desc, pos); auto path = ::std::string(pos+1); this->crate_overrides.insert(::std::make_pair( mv$(name), mv$(path) )); } // --crate-tag >> Specify a version/identifier suffix for the crate else if( strcmp(arg, "--crate-tag") == 0 ) { if( i == argc - 1 ) { ::std::cerr << "Flag " << arg << " requires an argument" << ::std::endl; exit(1); } const char* name_str = argv[++i]; this->crate_name_suffix = name_str; } // --crate-name >> Specify the crate name (overrides `#![crate_name=""]`) else if( strcmp(arg, "--crate-name") == 0 ) { if( i == argc - 1 ) { ::std::cerr << "Flag --crate-name requires an argument" << ::std::endl; exit(1); } const char* name_str = argv[++i]; this->crate_name = name_str; } // `--crate-type ` - Specify the crate type (overrides `#![crate_type=""]`) else if( strcmp(arg, "--crate-type") == 0 ) { if( i == argc - 1 ) { ::std::cerr << "Flag --crate-type requires an argument" << ::std::endl; exit(1); } const char* type_str = argv[++i]; if( strcmp(type_str, "rlib") == 0 ) { this->crate_type = ::AST::Crate::Type::RustLib; } else if( strcmp(type_str, "bin") == 0 ) { this->crate_type = ::AST::Crate::Type::Executable; } else if( strcmp(type_str, "proc-macro") == 0 ) { this->crate_type = ::AST::Crate::Type::ProcMacro; } else { ::std::cerr << "Unknown value for --crate-type" << ::std::endl; exit(1); } } // `--cfg ` // `--cfg =` else if( strcmp(arg, "--cfg") == 0 ) { if( i == argc - 1 ) { ::std::cerr << "Flag --cfg requires an argument" << ::std::endl; exit(1); } char* opt_and_val = argv[++i]; if( char* p = strchr(opt_and_val, '=') ) { *p = '\0'; const char* opt = opt_and_val; const char* val = p + 1; // TODO: Correctly parse the values. // - Value should be a double-quoted string. if( ::std::strcmp(opt, "feature") == 0 ) { this->features.insert( ::std::string(val) ); } else { if( val[0] == '"' ) { // TODO: Something cleaner than this. ::std::string s = val+1; assert(s.back() == '"'); s.pop_back(); Cfg_SetValue(opt, s); } else { Cfg_SetValue(opt, val); } } } else { Cfg_SetFlag(opt_and_val); } } // `--target ` - Override the default compiler target else if( strcmp(arg, "--target") == 0 ) { if (i == argc - 1) { ::std::cerr << "Flag " << arg << " requires an argument" << ::std::endl; exit(1); } this->target = argv[++i]; } else if( strcmp(arg, "--test") == 0 ) { this->test_harness = true; } else { ::std::cerr << "Unknown option '" << arg << "'" << ::std::endl; exit(1); } } } if (this->infile == "") { ::std::cerr << "No input file passed" << ::std::endl; exit(1); } if( const auto* a = getenv("MRUSTC_TARGET_VER") ) { if( strcmp(a, "1.19") == 0 ) { gTargetVersion = TargetVersion::Rustc1_19; } else if( strcmp(a, "1.29") == 0 ) { gTargetVersion = TargetVersion::Rustc1_29; } else { } } if( const auto* a = getenv("MRUSTC_DUMP") ) { while( a[0] ) { const char* end = strchr(a, ':'); ::stdx::string_view s; if( end ) { s = ::stdx::string_view { a, end }; a = end + 1; } else { end = a + strlen(a); s = ::stdx::string_view { a, end }; a = end; } if( s == "" ) { // Ignore } else if( s == "ast" ) { this->debug.dump_ast = true; } else if( s == "hir" ) { this->debug.dump_hir = true; } else if( s == "mir" ) { this->debug.dump_mir = true; } else { ::std::cerr << "Unknown option in $MRUSTC_DUMP '" << s << "'" << ::std::endl; // - No terminate, just warn } } } } void ProgramParams::show_help() const { ::std::cout << "USAGE: mrustc \n" "\n" "OPTIONS:\n" "-L : Search for crate files (.hir) in this directory\n" "-o : Write compiler output (library or executable) to this file\n" "-O : Enable optimistion\n" "-g : Emit debugging information\n" "--out-dir : Specify the output directory (alternative to `-o`)\n" "--extern =\n" " : Specify the path for a given crate (instead of searching for it)\n" "--crate-tag : Specify a suffix for symbols and output files\n" "--crate-name : Override/set the crate name\n" "--crate-type : Override/set the crate type (rlib, bin, proc-macro)\n" "--cfg flag : Set a boolean #[cfg]/cfg! flag\n" "--cfg flag=\"val\" : Set a string #[cfg]/cfg! flag\n" "--target : Compile code for the given target\n" "--test : Generate a unit test executable\n" "-C