| // Copyright (C) 2020-2023 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-system.h" |
| #include "rust-diagnostics.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-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); |
| |
| // 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; |
| |
| /* We need to warn on unused variables by default */ |
| opts->x_warn_unused_variable = 1; |
| /* For const variables too */ |
| opts->x_warn_unused_const_variable = 1; |
| /* And finally unused result for #[must_use] */ |
| opts->x_warn_unused_result = 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; |
| } |
| |
| /* See (a) <https://github.com/Rust-GCC/gccrs/issues/1713> |
| "Test failure on msp430-elfbare target", and |
| (b) <https://gcc.gnu.org/PR46805> |
| "ICE: SIGSEGV in optab_for_tree_code (optabs.c:407) with -O -fno-tree-scev-cprop -ftree-vectorize" |
| -- we have to support "random" modes/types here. |
| TODO Clean all this up (either locally, or preferably per PR46805: |
| "Ideally we'd never use lang_hooks.types.type_for_mode (or _for_size) in the |
| middle-end but had a pure middle-end based implementation". */ |
| for (size_t i = 0; i < NUM_INT_N_ENTS; i ++) |
| if (int_n_enabled_p[i] |
| && mode == int_n_data[i].m) |
| return (unsignedp ? int_n_trees[i].unsigned_type |
| : int_n_trees[i].signed_type); |
| |
| /* 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 convert_to_integer (type, expr); |
| case POINTER_TYPE: |
| return convert_to_pointer (type, expr); |
| case REAL_TYPE: |
| return convert_to_real (type, expr); |
| case COMPLEX_TYPE: |
| return 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" |