/* d-lang.cc -- Language-dependent hooks for D. Copyright (C) 2006-2020 Free Software Foundation, Inc. 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 . */ #include "config.h" #include "system.h" #include "coretypes.h" #include "dmd/aggregate.h" #include "dmd/cond.h" #include "dmd/declaration.h" #include "dmd/doc.h" #include "dmd/errors.h" #include "dmd/expression.h" #include "dmd/hdrgen.h" #include "dmd/identifier.h" #include "dmd/json.h" #include "dmd/mangle.h" #include "dmd/mars.h" #include "dmd/module.h" #include "dmd/mtype.h" #include "dmd/target.h" #include "opts.h" #include "alias.h" #include "tree.h" #include "diagnostic.h" #include "fold-const.h" #include "toplev.h" #include "langhooks.h" #include "langhooks-def.h" #include "target.h" #include "stringpool.h" #include "stor-layout.h" #include "varasm.h" #include "output.h" #include "print-tree.h" #include "gimple-expr.h" #include "gimplify.h" #include "debug.h" #include "d-tree.h" #include "id.h" /* Array of D frontend type/decl nodes. */ tree d_global_trees[DTI_MAX]; /* True if compilation is currently inside the D frontend semantic passes. */ bool doing_semantic_analysis_p = false; /* Options handled by the compiler that are separate from the frontend. */ struct d_option_data { const char *fonly; /* -fonly= */ const char *multilib; /* -imultilib */ const char *prefix; /* -iprefix */ bool deps; /* -M */ bool deps_skip_system; /* -MM */ const char *deps_filename; /* -M[M]D */ const char *deps_filename_user; /* -MF */ OutBuffer *deps_target; /* -M[QT] */ bool deps_phony; /* -MP */ bool stdinc; /* -nostdinc */ } d_option; /* List of modules being compiled. */ static Modules builtin_modules; /* Module where `C main' is defined, compiled in if needed. */ static Module *entrypoint_module = NULL; static Module *entrypoint_root_module = NULL; /* The current and global binding level in effect. */ struct binding_level *current_binding_level; struct binding_level *global_binding_level; /* The context to be used for global declarations. */ static GTY(()) tree global_context; /* Array of all global declarations to pass back to the middle-end. */ static GTY(()) vec *global_declarations; /* Support for GCC-style command-line make dependency generation. Adds TARGET to the make dependencies target buffer. QUOTED is true if the string should be quoted. */ static void deps_add_target (const char *target, bool quoted) { if (!d_option.deps_target) d_option.deps_target = new OutBuffer (); else d_option.deps_target->writeByte (' '); d_option.deps_target->reserve (strlen (target)); if (!quoted) { d_option.deps_target->writestring (target); return; } /* Quote characters in target which are significant to Make. */ for (const char *p = target; *p != '\0'; p++) { switch (*p) { case ' ': case '\t': for (const char *q = p - 1; target <= q && *q == '\\'; q--) d_option.deps_target->writeByte ('\\'); d_option.deps_target->writeByte ('\\'); break; case '$': d_option.deps_target->writeByte ('$'); break; case '#': d_option.deps_target->writeByte ('\\'); break; default: break; } d_option.deps_target->writeByte (*p); } } /* Write out all dependencies of a given MODULE to the specified BUFFER. COLMAX is the number of columns to word-wrap at (0 means don't wrap). */ static void deps_write (Module *module, OutBuffer *buffer, unsigned colmax = 72) { hash_set seen_modules; vec dependencies = vNULL; Modules modlist; modlist.push (module); vec phonylist = vNULL; unsigned column = 0; /* Write out make target module name. */ if (d_option.deps_target) { buffer->writestring (d_option.deps_target->extractString ()); column = d_option.deps_target->offset; } else { buffer->writestring (module->objfile->name->str); column = buffer->offset; } buffer->writestring (":"); column++; /* Search all modules for file dependencies. */ while (modlist.dim > 0) { Module *depmod = modlist.pop (); const char *modstr = depmod->srcfile->name->str; /* Skip modules that have already been looked at. */ if (seen_modules.add (modstr)) continue; dependencies.safe_push (modstr); /* Add to list of phony targets if is not being compile. */ if (d_option.deps_phony && !depmod->isRoot ()) phonylist.safe_push (modstr); /* Add imported files to dependency list. */ for (size_t i = 0; i < depmod->contentImportedFiles.dim; i++) { const char *impstr = depmod->contentImportedFiles[i]; dependencies.safe_push (impstr); phonylist.safe_push (impstr); } /* Search all imports of the module. */ for (size_t i = 0; i < depmod->aimports.dim; i++) { Module *m = depmod->aimports[i]; /* Ignore compiler-generated modules. */ if ((m->ident == Identifier::idPool ("__entrypoint") || m->ident == Identifier::idPool ("__main")) && m->parent == NULL) continue; /* Don't search system installed modules, this includes object, core.*, std.*, and gcc.* packages. */ if (d_option.deps_skip_system) { if (m->ident == Identifier::idPool ("object") && m->parent == NULL) continue; if (m->md && m->md->packages) { Identifier *package = (*m->md->packages)[0]; if (package == Identifier::idPool ("core") || package == Identifier::idPool ("std") || package == Identifier::idPool ("gcc")) continue; } } modlist.push (m); } } /* Write out all make dependencies. */ for (size_t i = 0; i < dependencies.length (); i++) { const char *str = dependencies[i]; unsigned size = strlen (str); column += size; if (colmax && column > colmax) { buffer->writestring (" \\\n "); column = size + 1; } else { buffer->writestring (" "); column++; } buffer->writestring (str); } buffer->writenl (); /* Write out all phony targets. */ for (size_t i = 0; i < phonylist.length (); i++) { buffer->writenl (); buffer->writestring (phonylist[i]); buffer->writestring (":\n"); } } /* Implements the lang_hooks.init_options routine for language D. This initializes the global state for the D frontend before calling the option handlers. */ static void d_init_options (unsigned int, cl_decoded_option *decoded_options) { /* Set default values. */ global._init (); global.vendor = lang_hooks.name; global.params.argv0 = xstrdup (decoded_options[0].arg); global.params.link = true; global.params.useAssert = true; global.params.useInvariants = true; global.params.useIn = true; global.params.useOut = true; global.params.useArrayBounds = BOUNDSCHECKdefault; global.params.useSwitchError = true; global.params.useModuleInfo = true; global.params.useTypeInfo = true; global.params.useExceptions = true; global.params.useInline = false; global.params.obj = true; global.params.hdrStripPlainFunctions = true; global.params.betterC = false; global.params.allInst = false; /* Default extern(C++) mangling to C++14. */ global.params.cplusplus = CppStdRevisionCpp14; global.params.linkswitches = new Strings (); global.params.libfiles = new Strings (); global.params.objfiles = new Strings (); global.params.ddocfiles = new Strings (); /* Warnings and deprecations are disabled by default. */ global.params.useDeprecated = DIAGNOSTICoff; global.params.warnings = DIAGNOSTICoff; global.params.imppath = new Strings (); global.params.fileImppath = new Strings (); global.params.modFileAliasStrings = new Strings (); /* Extra GDC-specific options. */ d_option.fonly = NULL; d_option.multilib = NULL; d_option.prefix = NULL; d_option.deps = false; d_option.deps_skip_system = false; d_option.deps_filename = NULL; d_option.deps_filename_user = NULL; d_option.deps_target = NULL; d_option.deps_phony = false; d_option.stdinc = true; } /* Implements the lang_hooks.init_options_struct routine for language D. Initializes the options structure OPTS. */ static void d_init_options_struct (gcc_options *opts) { /* GCC options. */ opts->x_flag_exceptions = 1; /* Avoid range issues for complex multiply and divide. */ opts->x_flag_complex_method = 2; /* Unlike C, there is no global 'errno' variable. */ opts->x_flag_errno_math = 0; opts->frontend_set_flag_errno_math = true; /* Keep in sync with existing -fbounds-check flag. */ opts->x_flag_bounds_check = global.params.useArrayBounds; /* D says that signed overflow is precisely defined. */ opts->x_flag_wrapv = 1; } /* Implements the lang_hooks.lang_mask routine for language D. Returns language mask for option parsing. */ static unsigned int d_option_lang_mask (void) { return CL_D; } /* Implements the lang_hooks.init routine for language D. */ static bool d_init (void) { Type::_init (); Id::initialize (); Module::_init (); Expression::_init (); Objc::_init (); /* Back-end init. */ global_binding_level = ggc_cleared_alloc (); current_binding_level = global_binding_level; /* 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); d_init_builtins (); if (flag_exceptions) using_eh_for_cleanups (); if (!supports_one_only ()) flag_weak = 0; /* This is the C main, not the D main. */ main_identifier_node = get_identifier ("main"); Target::_init (); d_init_versions (); /* Insert all library-configured identifiers and import paths. */ add_import_paths (d_option.prefix, d_option.multilib, d_option.stdinc); return 1; } /* Implements the lang_hooks.init_ts routine for language D. */ static void d_init_ts (void) { MARK_TS_TYPED (FLOAT_MOD_EXPR); MARK_TS_TYPED (UNSIGNED_RSHIFT_EXPR); } /* Implements the lang_hooks.handle_option routine for language D. Handles D specific options. Return false if we didn't do anything. */ static bool d_handle_option (size_t scode, const char *arg, HOST_WIDE_INT value, int kind ATTRIBUTE_UNUSED, location_t loc ATTRIBUTE_UNUSED, const cl_option_handlers *handlers ATTRIBUTE_UNUSED) { opt_code code = (opt_code) scode; bool result = true; switch (code) { case OPT_fall_instantiations: global.params.allInst = value; break; case OPT_fassert: global.params.useAssert = value; break; case OPT_fbounds_check: global.params.useArrayBounds = value ? BOUNDSCHECKon : BOUNDSCHECKoff; break; case OPT_fbounds_check_: global.params.useArrayBounds = (value == 2) ? BOUNDSCHECKon : (value == 1) ? BOUNDSCHECKsafeonly : BOUNDSCHECKoff; break; case OPT_fdebug: global.params.debuglevel = value ? 1 : 0; break; case OPT_fdebug_: if (ISDIGIT (arg[0])) { int level = integral_argument (arg); if (level != -1) { DebugCondition::setGlobalLevel (level); break; } } if (Identifier::isValidIdentifier (CONST_CAST (char *, arg))) { DebugCondition::addGlobalIdent (arg); break; } error ("bad argument for %<-fdebug%>: %qs", arg); break; case OPT_fdoc: global.params.doDocComments = value; break; case OPT_fdoc_dir_: global.params.doDocComments = true; global.params.docdir = arg; break; case OPT_fdoc_file_: global.params.doDocComments = true; global.params.docname = arg; break; case OPT_fdoc_inc_: global.params.ddocfiles->push (arg); break; case OPT_fdruntime: global.params.betterC = !value; break; case OPT_fdump_d_original: global.params.vcg_ast = value; break; case OPT_fexceptions: global.params.useExceptions = value; break; case OPT_fignore_unknown_pragmas: global.params.ignoreUnsupportedPragmas = value; break; case OPT_finvariants: global.params.useInvariants = value; break; case OPT_fmain: global.params.addMain = value; break; case OPT_fmodule_file_: global.params.modFileAliasStrings->push (arg); if (!strchr (arg, '=')) error ("bad argument for %<-fmodule-file%>: %qs", arg); break; case OPT_fmoduleinfo: global.params.useModuleInfo = value; break; case OPT_fonly_: d_option.fonly = arg; break; case OPT_fpostconditions: global.params.useOut = value; break; case OPT_fpreconditions: global.params.useIn = value; break; case OPT_frelease: global.params.release = value; break; case OPT_frtti: global.params.useTypeInfo = value; break; case OPT_fswitch_errors: global.params.useSwitchError = value; break; case OPT_ftransition_all: global.params.vtls = value; global.params.vfield = value; global.params.vcomplex = value; break; case OPT_ftransition_checkimports: global.params.check10378 = value; break; case OPT_ftransition_complex: global.params.vcomplex = value; break; case OPT_ftransition_dip1000: global.params.vsafe = value; global.params.useDIP25 = value; break; case OPT_ftransition_dip25: global.params.useDIP25 = value; break; case OPT_ftransition_field: global.params.vfield = value; break; case OPT_ftransition_import: global.params.bug10378 = value; break; case OPT_ftransition_nogc: global.params.vgc = value; break; case OPT_ftransition_tls: global.params.vtls = value; break; case OPT_funittest: global.params.useUnitTests = value; break; case OPT_fversion_: if (ISDIGIT (arg[0])) { int level = integral_argument (arg); if (level != -1) { VersionCondition::setGlobalLevel (level); break; } } if (Identifier::isValidIdentifier (CONST_CAST (char *, arg))) { VersionCondition::addGlobalIdent (arg); break; } error ("bad argument for %<-fversion%>: %qs", arg); break; case OPT_H: global.params.doHdrGeneration = true; break; case OPT_Hd: global.params.doHdrGeneration = true; global.params.hdrdir = arg; break; case OPT_Hf: global.params.doHdrGeneration = true; global.params.hdrname = arg; break; case OPT_imultilib: d_option.multilib = arg; break; case OPT_iprefix: d_option.prefix = arg; break; case OPT_I: global.params.imppath->push (arg); break; case OPT_J: global.params.fileImppath->push (arg); break; case OPT_MM: d_option.deps_skip_system = true; /* Fall through. */ case OPT_M: d_option.deps = true; break; case OPT_MMD: d_option.deps_skip_system = true; /* Fall through. */ case OPT_MD: d_option.deps = true; d_option.deps_filename = arg; break; case OPT_MF: /* If specified multiple times, last one wins. */ d_option.deps_filename_user = arg; break; case OPT_MP: d_option.deps_phony = true; break; case OPT_MQ: deps_add_target (arg, true); break; case OPT_MT: deps_add_target (arg, false); break; case OPT_nostdinc: d_option.stdinc = false; break; case OPT_v: global.params.verbose = value; break; case OPT_Wall: if (value) global.params.warnings = DIAGNOSTICinform; break; case OPT_Wdeprecated: global.params.useDeprecated = value ? DIAGNOSTICinform : DIAGNOSTICoff; break; case OPT_Werror: if (value) global.params.warnings = DIAGNOSTICerror; break; case OPT_Wspeculative: if (value) global.params.showGaggedErrors = 1; break; case OPT_Xf: global.params.jsonfilename = arg; /* Fall through. */ case OPT_X: global.params.doJsonGeneration = true; break; default: break; } D_handle_option_auto (&global_options, &global_options_set, scode, arg, value, d_option_lang_mask (), kind, loc, handlers, global_dc); return result; } /* Implements the lang_hooks.post_options routine for language D. Deal with any options that imply the turning on/off of features. FN is the main input filename passed on the command line. */ static bool d_post_options (const char ** fn) { /* Verify the input file name. */ const char *filename = *fn; if (!filename || strcmp (filename, "-") == 0) filename = ""; /* The front end considers the first input file to be the main one. */ *fn = filename; /* Release mode doesn't turn off bounds checking for safe functions. */ if (global.params.useArrayBounds == BOUNDSCHECKdefault) { global.params.useArrayBounds = global.params.release ? BOUNDSCHECKsafeonly : BOUNDSCHECKon; flag_bounds_check = !global.params.release; } if (global.params.release) { if (!global_options_set.x_flag_invariants) global.params.useInvariants = false; if (!global_options_set.x_flag_preconditions) global.params.useIn = false; if (!global_options_set.x_flag_postconditions) global.params.useOut = false; if (!global_options_set.x_flag_assert) global.params.useAssert = false; if (!global_options_set.x_flag_switch_errors) global.params.useSwitchError = false; } if (global.params.betterC) { if (!global_options_set.x_flag_moduleinfo) global.params.useModuleInfo = false; if (!global_options_set.x_flag_rtti) global.params.useTypeInfo = false; if (!global_options_set.x_flag_exceptions) global.params.useExceptions = false; global.params.checkAction = CHECKACTION_halt; } /* Turn off partitioning unless it was explicitly requested, as it doesn't work with D exception chaining, where EH handler uses LSDA to determine whether two thrown exception are in the same context. */ if (!global_options_set.x_flag_reorder_blocks_and_partition) global_options.x_flag_reorder_blocks_and_partition = 0; /* Error about use of deprecated features. */ if (global.params.useDeprecated == DIAGNOSTICinform && global.params.warnings == DIAGNOSTICerror) global.params.useDeprecated = DIAGNOSTICerror; /* Make -fmax-errors visible to frontend's diagnostic machinery. */ if (global_options_set.x_flag_max_errors) global.errorLimit = flag_max_errors; if (flag_excess_precision == EXCESS_PRECISION_DEFAULT) flag_excess_precision = EXCESS_PRECISION_STANDARD; if (global.params.useUnitTests) global.params.useAssert = true; global.params.symdebug = write_symbols != NO_DEBUG; global.params.useInline = flag_inline_functions; global.params.showColumns = flag_show_column; if (global.params.useInline) global.params.hdrStripPlainFunctions = false; global.params.obj = !flag_syntax_only; /* Has no effect yet. */ global.params.pic = flag_pic != 0; if (warn_return_type == -1) warn_return_type = 0; return false; } /* Return TRUE if an operand OP of a given TYPE being copied has no data. The middle-end does a similar check with zero sized types. */ static bool empty_modify_p (tree type, tree op) { tree_code code = TREE_CODE (op); switch (code) { case COMPOUND_EXPR: return empty_modify_p (type, TREE_OPERAND (op, 1)); case CONSTRUCTOR: /* Non-empty construcors are valid. */ if (CONSTRUCTOR_NELTS (op) != 0 || TREE_CLOBBER_P (op)) return false; break; case CALL_EXPR: /* Leave nrvo alone because it isn't a copy. */ if (CALL_EXPR_RETURN_SLOT_OPT (op)) return false; break; default: /* If the operand doesn't have a simple form. */ if (!is_gimple_lvalue (op) && !INDIRECT_REF_P (op)) return false; break; } return empty_aggregate_p (type); } /* Implements the lang_hooks.gimplify_expr routine for language D. Do gimplification of D specific expression trees in EXPR_P. */ int d_gimplify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p ATTRIBUTE_UNUSED) { tree_code code = TREE_CODE (*expr_p); enum gimplify_status ret = GS_UNHANDLED; tree op0, op1; tree type; switch (code) { case INIT_EXPR: case MODIFY_EXPR: op0 = TREE_OPERAND (*expr_p, 0); op1 = TREE_OPERAND (*expr_p, 1); if (!error_operand_p (op0) && !error_operand_p (op1) && (AGGREGATE_TYPE_P (TREE_TYPE (op0)) || AGGREGATE_TYPE_P (TREE_TYPE (op1))) && !useless_type_conversion_p (TREE_TYPE (op1), TREE_TYPE (op0))) { /* If the back end isn't clever enough to know that the lhs and rhs types are the same, add an explicit conversion. */ TREE_OPERAND (*expr_p, 1) = build1 (VIEW_CONVERT_EXPR, TREE_TYPE (op0), op1); ret = GS_OK; } else if (empty_modify_p (TREE_TYPE (op0), op1)) { /* Remove any copies of empty aggregates. */ gimplify_expr (&TREE_OPERAND (*expr_p, 0), pre_p, post_p, is_gimple_lvalue, fb_lvalue); if (TREE_SIDE_EFFECTS (op1)) gimplify_and_add (op1, pre_p); *expr_p = TREE_OPERAND (*expr_p, 0); ret = GS_OK; } break; case ADDR_EXPR: op0 = TREE_OPERAND (*expr_p, 0); /* Constructors are not lvalues, so make them one. */ if (TREE_CODE (op0) == CONSTRUCTOR) { TREE_OPERAND (*expr_p, 0) = force_target_expr (op0); ret = GS_OK; } break; case CALL_EXPR: if (CALL_EXPR_ARGS_ORDERED (*expr_p)) { /* Strictly evaluate all arguments from left to right. */ int nargs = call_expr_nargs (*expr_p); location_t loc = EXPR_LOC_OR_LOC (*expr_p, input_location); /* No need to enforce evaluation order if only one argument. */ if (nargs < 2) break; /* Or if all arguments are already free of side-effects. */ bool has_side_effects = false; for (int i = 0; i < nargs; i++) { if (TREE_SIDE_EFFECTS (CALL_EXPR_ARG (*expr_p, i))) { has_side_effects = true; break; } } if (!has_side_effects) break; /* Leave the last argument for gimplify_call_expr. */ for (int i = 0; i < nargs - 1; i++) { tree new_arg = CALL_EXPR_ARG (*expr_p, i); /* If argument has a side-effect, gimplify_arg will handle it. */ if (gimplify_arg (&new_arg, pre_p, loc) == GS_ERROR) ret = GS_ERROR; /* Even if an argument itself doesn't have any side-effects, it might be altered by another argument in the list. */ if (new_arg == CALL_EXPR_ARG (*expr_p, i) && !really_constant_p (new_arg)) new_arg = get_formal_tmp_var (new_arg, pre_p); CALL_EXPR_ARG (*expr_p, i) = new_arg; } if (ret != GS_ERROR) ret = GS_OK; } break; case UNSIGNED_RSHIFT_EXPR: /* Convert op0 to an unsigned type. */ op0 = TREE_OPERAND (*expr_p, 0); op1 = TREE_OPERAND (*expr_p, 1); type = d_unsigned_type (TREE_TYPE (op0)); *expr_p = convert (TREE_TYPE (*expr_p), build2 (RSHIFT_EXPR, type, convert (type, op0), op1)); ret = GS_OK; break; case FLOAT_MOD_EXPR: gcc_unreachable (); default: break; } return ret; } /* Add the module M to the list of modules that may declare GCC builtins. These are scanned after first semantic and before codegen passes. See d_maybe_set_builtin() for the implementation. */ void d_add_builtin_module (Module *m) { builtin_modules.push (m); } /* Record the entrypoint module ENTRY which will be compiled in the current compilation. ROOT is the module scope where this was requested from. */ void d_add_entrypoint_module (Module *entry, Module *root) { /* We are emitting this straight to object file. */ entrypoint_module = entry; entrypoint_root_module = root; } /* Implements the lang_hooks.parse_file routine for language D. */ void d_parse_file (void) { if (global.params.verbose) { message ("binary %s", global.params.argv0); message ("version %s", global.version); if (global.params.versionids) { OutBuffer buf; buf.writestring ("predefs "); for (size_t i = 0; i < global.params.versionids->dim; i++) { const char *s = (*global.params.versionids)[i]; buf.writestring (" "); buf.writestring (s); } message ("%.*s", (int) buf.offset, (char *) buf.data); } } /* Start the main input file, if the debug writer wants it. */ if (debug_hooks->start_end_main_source_file) debug_hooks->start_source_file (0, main_input_filename); /* Create Module's for all sources we will load. */ Modules modules; modules.reserve (num_in_fnames); /* In this mode, the first file name is supposed to be a duplicate of one of the input files. */ if (d_option.fonly && strcmp (d_option.fonly, main_input_filename) != 0) error ("%<-fonly=%> argument is different from first input file name"); for (size_t i = 0; i < num_in_fnames; i++) { if (strcmp (in_fnames[i], "-") == 0) { /* Load the entire contents of stdin into memory. 8 kilobytes should be a good enough initial size, but double on each iteration. 16 bytes are added for the final '\n' and 15 bytes of padding. */ ssize_t size = 8 * 1024; uchar *buffer = XNEWVEC (uchar, size + 16); ssize_t len = 0; ssize_t count; while ((count = read (STDIN_FILENO, buffer + len, size - len)) > 0) { len += count; if (len == size) { size *= 2; buffer = XRESIZEVEC (uchar, buffer, size + 16); } } if (count < 0) { error (Loc ("stdin", 0, 0), "%s", xstrerror (errno)); free (buffer); continue; } /* Handling stdin, generate a unique name for the module. */ Module *m = Module::create (in_fnames[i], Identifier::generateId ("__stdin"), global.params.doDocComments, global.params.doHdrGeneration); modules.push (m); /* Overwrite the source file for the module, the one created by Module::create would have a forced a `.d' suffix. */ m->srcfile = File::create (""); m->srcfile->len = len; m->srcfile->buffer = buffer; } else { /* Handling a D source file, strip off the path and extension. */ const char *basename = FileName::name (in_fnames[i]); const char *name = FileName::removeExt (basename); Module *m = Module::create (in_fnames[i], Identifier::idPool (name), global.params.doDocComments, global.params.doHdrGeneration); modules.push (m); FileName::free (name); } } /* Read all D source files. */ for (size_t i = 0; i < modules.dim; i++) { Module *m = modules[i]; m->read (Loc ()); } /* Parse all D source files. */ for (size_t i = 0; i < modules.dim; i++) { Module *m = modules[i]; if (global.params.verbose) message ("parse %s", m->toChars ()); if (!Module::rootModule) Module::rootModule = m; m->importedFrom = m; m->parse (); Compiler::loadModule (m); if (m->isDocFile) { gendocfile (m); /* Remove M from list of modules. */ modules.remove (i); i--; } } /* Load the module containing D main. */ if (global.params.addMain) { unsigned errors = global.startGagging (); Module *m = Module::load (Loc (), NULL, Identifier::idPool ("__main")); if (! global.endGagging (errors)) { m->importedFrom = m; modules.push (m); } } if (global.errors) goto had_errors; if (global.params.doHdrGeneration) { /* Generate 'header' import files. Since 'header' import files must be independent of command line switches and what else is imported, they are generated before any semantic analysis. */ for (size_t i = 0; i < modules.dim; i++) { Module *m = modules[i]; if (d_option.fonly && m != Module::rootModule) continue; if (global.params.verbose) message ("import %s", m->toChars ()); genhdrfile (m); } } if (global.errors) goto had_errors; /* Load all unconditional imports for better symbol resolving. */ for (size_t i = 0; i < modules.dim; i++) { Module *m = modules[i]; if (global.params.verbose) message ("importall %s", m->toChars ()); m->importAll (NULL); } if (global.errors) goto had_errors; /* Do semantic analysis. */ doing_semantic_analysis_p = true; for (size_t i = 0; i < modules.dim; i++) { Module *m = modules[i]; if (global.params.verbose) message ("semantic %s", m->toChars ()); m->semantic (NULL); } /* Do deferred semantic analysis. */ Module::dprogress = 1; Module::runDeferredSemantic (); if (Module::deferred.dim) { for (size_t i = 0; i < Module::deferred.dim; i++) { Dsymbol *sd = Module::deferred[i]; error_at (make_location_t (sd->loc), "unable to resolve forward reference in definition"); } } /* Process all built-in modules or functions now for CTFE. */ while (builtin_modules.dim != 0) { Module *m = builtin_modules.pop (); d_maybe_set_builtin (m); } /* Do pass 2 semantic analysis. */ for (size_t i = 0; i < modules.dim; i++) { Module *m = modules[i]; if (global.params.verbose) message ("semantic2 %s", m->toChars ()); m->semantic2 (NULL); } Module::runDeferredSemantic2 (); if (global.errors) goto had_errors; /* Do pass 3 semantic analysis. */ for (size_t i = 0; i < modules.dim; i++) { Module *m = modules[i]; if (global.params.verbose) message ("semantic3 %s", m->toChars ()); m->semantic3 (NULL); } Module::runDeferredSemantic3 (); /* Check again, incase semantic3 pass loaded any more modules. */ while (builtin_modules.dim != 0) { Module *m = builtin_modules.pop (); d_maybe_set_builtin (m); } /* Do not attempt to generate output files if errors or warnings occurred. */ if (global.errors || global.warnings) goto had_errors; /* Generate output files. */ doing_semantic_analysis_p = false; if (Module::rootModule) { /* Declare the name of the root module as the first global name in order to make the middle-end fully deterministic. */ OutBuffer buf; mangleToBuffer (Module::rootModule, &buf); first_global_object_name = buf.extractString (); } /* Make dependencies. */ if (d_option.deps) { OutBuffer buf; for (size_t i = 0; i < modules.dim; i++) deps_write (modules[i], &buf); /* -MF overrides -M[M]D. */ if (d_option.deps_filename_user) d_option.deps_filename = d_option.deps_filename_user; if (d_option.deps_filename) { File *fdeps = File::create (d_option.deps_filename); fdeps->setbuffer ((void *) buf.data, buf.offset); fdeps->ref = 1; writeFile (Loc (), fdeps); } else message ("%.*s", (int) buf.offset, (char *) buf.data); } /* Generate JSON files. */ if (global.params.doJsonGeneration) { OutBuffer buf; json_generate (&buf, &modules); const char *name = global.params.jsonfilename; if (name && (name[0] != '-' || name[1] != '\0')) { const char *nameext = FileName::defaultExt (name, global.json_ext); File *fjson = File::create (nameext); fjson->setbuffer ((void *) buf.data, buf.offset); fjson->ref = 1; writeFile (Loc (), fjson); } else message ("%.*s", (int) buf.offset, (char *) buf.data); } /* Generate Ddoc files. */ if (global.params.doDocComments && !global.errors && !errorcount) { for (size_t i = 0; i < modules.dim; i++) { Module *m = modules[i]; gendocfile (m); } } /* Handle -fdump-d-original. */ if (global.params.vcg_ast) { for (size_t i = 0; i < modules.dim; i++) { Module *m = modules[i]; OutBuffer buf; buf.doindent = 1; moduleToBuffer (&buf, m); message ("%.*s", (int) buf.offset, (char *) buf.data); } } for (size_t i = 0; i < modules.dim; i++) { Module *m = modules[i]; if (d_option.fonly && m != Module::rootModule) continue; if (global.params.verbose) message ("code %s", m->toChars ()); if (!flag_syntax_only) { if ((entrypoint_module != NULL) && (m == entrypoint_root_module)) build_decl_tree (entrypoint_module); build_decl_tree (m); } } /* And end the main input file, if the debug writer wants it. */ if (debug_hooks->start_end_main_source_file) debug_hooks->end_source_file (0); had_errors: /* Add the D frontend error count to the GCC error count to correctly exit with an error status. */ errorcount += (global.errors + global.warnings); /* Write out globals. */ d_finish_compilation (vec_safe_address (global_declarations), vec_safe_length (global_declarations)); } /* Implements the lang_hooks.types.type_for_mode routine for language D. */ static tree d_type_for_mode (machine_mode mode, int unsignedp) { if (mode == QImode) return unsignedp ? d_ubyte_type : d_byte_type; if (mode == HImode) return unsignedp ? d_ushort_type : d_short_type; if (mode == SImode) return unsignedp ? d_uint_type : d_int_type; if (mode == DImode) return unsignedp ? d_ulong_type : d_long_type; if (mode == TYPE_MODE (d_cent_type)) return unsignedp ? d_ucent_type : d_cent_type; 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 (long_double_type_node)) return long_double_type_node; if (mode == TYPE_MODE (build_pointer_type (char8_type_node))) return build_pointer_type (char8_type_node); if (mode == TYPE_MODE (build_pointer_type (d_int_type))) return build_pointer_type (d_int_type); for (int i = 0; i < NUM_INT_N_ENTS; i ++) { if (int_n_enabled_p[i] && mode == int_n_data[i].m) { if (unsignedp) return int_n_trees[i].unsigned_type; else return int_n_trees[i].signed_type; } } if (COMPLEX_MODE_P (mode)) { machine_mode inner_mode; tree inner_type; 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; inner_mode = (machine_mode) GET_MODE_INNER (mode); inner_type = d_type_for_mode (inner_mode, unsignedp); if (inner_type != NULL_TREE) return build_complex_type (inner_type); } else if (VECTOR_MODE_P (mode)) { machine_mode inner_mode = (machine_mode) GET_MODE_INNER (mode); tree inner_type = d_type_for_mode (inner_mode, unsignedp); if (inner_type != NULL_TREE) return build_vector_type_for_mode (inner_type, mode); } return 0; } /* Implements the lang_hooks.types.type_for_size routine for language D. */ static tree d_type_for_size (unsigned bits, int unsignedp) { if (bits <= TYPE_PRECISION (d_byte_type)) return unsignedp ? d_ubyte_type : d_byte_type; if (bits <= TYPE_PRECISION (d_short_type)) return unsignedp ? d_ushort_type : d_short_type; if (bits <= TYPE_PRECISION (d_int_type)) return unsignedp ? d_uint_type : d_int_type; if (bits <= TYPE_PRECISION (d_long_type)) return unsignedp ? d_ulong_type : d_long_type; if (bits <= TYPE_PRECISION (d_cent_type)) return unsignedp ? d_ucent_type : d_cent_type; for (int i = 0; i < NUM_INT_N_ENTS; i ++) { if (int_n_enabled_p[i] && bits == int_n_data[i].bitsize) { if (unsignedp) return int_n_trees[i].unsigned_type; else return int_n_trees[i].signed_type; } } return 0; } /* Return the signed or unsigned version of TYPE, an integral type, the signedness being specified by UNSIGNEDP. */ static tree d_signed_or_unsigned_type (int unsignedp, tree type) { if (TYPE_UNSIGNED (type) == (unsigned) unsignedp) return type; if (TYPE_PRECISION (type) == TYPE_PRECISION (d_cent_type)) return unsignedp ? d_ucent_type : d_cent_type; if (TYPE_PRECISION (type) == TYPE_PRECISION (d_long_type)) return unsignedp ? d_ulong_type : d_long_type; if (TYPE_PRECISION (type) == TYPE_PRECISION (d_int_type)) return unsignedp ? d_uint_type : d_int_type; if (TYPE_PRECISION (type) == TYPE_PRECISION (d_short_type)) return unsignedp ? d_ushort_type : d_short_type; if (TYPE_PRECISION (type) == TYPE_PRECISION (d_byte_type)) return unsignedp ? d_ubyte_type : d_byte_type; return signed_or_unsigned_type_for (unsignedp, type); } /* Return the unsigned version of TYPE, an integral type. */ tree d_unsigned_type (tree type) { return d_signed_or_unsigned_type (1, type); } /* Return the signed version of TYPE, an integral type. */ tree d_signed_type (tree type) { return d_signed_or_unsigned_type (0, type); } /* Implements the lang_hooks.types.type_promotes_to routine for language D. All promotions for variable arguments are handled by the D frontend. */ static tree d_type_promotes_to (tree type) { return type; } /* Implements the lang_hooks.decls.global_bindings_p routine for language D. Return true if we are in the global binding level. */ static bool d_global_bindings_p (void) { return (current_binding_level == global_binding_level); } /* Return global_context, but create it first if need be. */ static tree get_global_context (void) { if (!global_context) { global_context = build_translation_unit_decl (NULL_TREE); debug_hooks->register_main_translation_unit (global_context); } return global_context; } /* Implements the lang_hooks.decls.pushdecl routine for language D. Record DECL as belonging to the current lexical scope. */ tree d_pushdecl (tree decl) { /* Set the context of the decl. If current_function_decl did not help in determining the context, use global scope. */ if (!DECL_CONTEXT (decl)) { if (current_function_decl) DECL_CONTEXT (decl) = current_function_decl; else DECL_CONTEXT (decl) = get_global_context (); } /* Put decls on list in reverse order. */ if (TREE_STATIC (decl) || d_global_bindings_p ()) vec_safe_push (global_declarations, decl); else { TREE_CHAIN (decl) = current_binding_level->names; current_binding_level->names = decl; } return decl; } /* Implements the lang_hooks.decls.getdecls routine for language D. Return the list of declarations of the current level. */ static tree d_getdecls (void) { if (current_binding_level) return current_binding_level->names; return NULL_TREE; } /* Implements the lang_hooks.get_alias_set routine for language D. Get the alias set corresponding to type or expression T. Return -1 if we don't do anything special. */ static alias_set_type d_get_alias_set (tree) { /* For now in D, assume everything aliases everything else, until we define some solid rules backed by a specification. There are also some parts of code generation routines that don't adhere to C alias rules, such as build_vconvert. In any case, a lot of user code already assumes there is no strict aliasing and will break if we were to change that. */ return 0; } /* Implements the lang_hooks.types_compatible_p routine for language D. Compares two types for equivalence in the D programming language. This routine should only return 1 if it is sure, even though the frontend should have already ensured that all types are compatible before handing over the parsed ASTs to the code generator. */ static int d_types_compatible_p (tree x, tree y) { Type *tx = TYPE_LANG_FRONTEND (x); Type *ty = TYPE_LANG_FRONTEND (y); /* Try validating the types in the frontend. */ if (tx != NULL && ty != NULL) { /* Types are equivalent. */ if (same_type_p (tx, ty)) return true; /* Type system allows implicit conversion between. */ if (tx->implicitConvTo (ty) || ty->implicitConvTo (tx)) return true; } /* Fallback on using type flags for comparison. E.g: all dynamic arrays are distinct types in D, but are VIEW_CONVERT compatible. */ if (TREE_CODE (x) == RECORD_TYPE && TREE_CODE (y) == RECORD_TYPE) { if (TYPE_DYNAMIC_ARRAY (x) && TYPE_DYNAMIC_ARRAY (y)) return true; if (TYPE_DELEGATE (x) && TYPE_DELEGATE (y)) return true; if (TYPE_ASSOCIATIVE_ARRAY (x) && TYPE_ASSOCIATIVE_ARRAY (y)) return true; } return false; } /* Implements the lang_hooks.finish_incomplete_decl routine for language D. */ static void d_finish_incomplete_decl (tree decl) { if (VAR_P (decl)) { /* D allows zero-length declarations. Such a declaration ends up with DECL_SIZE (t) == NULL_TREE which is what the back-end function assembler_variable checks. This could change in later versions, or maybe all of these variables should be aliased to one symbol. */ if (DECL_SIZE (decl) == 0) { DECL_SIZE (decl) = bitsize_zero_node; DECL_SIZE_UNIT (decl) = size_zero_node; } } } /* Implements the lang_hooks.types.classify_record routine for language D. Return the true debug type for TYPE. */ static classify_record d_classify_record (tree type) { Type *t = TYPE_LANG_FRONTEND (type); if (t && t->ty == Tclass) { TypeClass *tc = (TypeClass *) t; /* extern(C++) interfaces get emitted as classes. */ if (tc->sym->isInterfaceDeclaration () && !tc->sym->isCPPinterface ()) return RECORD_IS_INTERFACE; return RECORD_IS_CLASS; } return RECORD_IS_STRUCT; } /* Implements the lang_hooks.tree_size routine for language D. Determine the size of our tcc_constant or tcc_exceptional nodes. */ static size_t d_tree_size (tree_code code) { switch (code) { case FUNCFRAME_INFO: return sizeof (tree_frame_info); default: gcc_unreachable (); } } /* Implements the lang_hooks.print_xnode routine for language D. */ static void d_print_xnode (FILE *file, tree node, int indent) { switch (TREE_CODE (node)) { case FUNCFRAME_INFO: print_node (file, "frame_type", FRAMEINFO_TYPE (node), indent + 4); break; default: break; } } /* Return which tree structure is used by NODE, or TS_D_GENERIC if NODE is one of the language-independent trees. */ d_tree_node_structure_enum d_tree_node_structure (lang_tree_node *t) { switch (TREE_CODE (&t->generic)) { case IDENTIFIER_NODE: return TS_D_IDENTIFIER; case FUNCFRAME_INFO: return TS_D_FRAMEINFO; default: return TS_D_GENERIC; } } /* Allocate and return a lang specific structure for the frontend type. */ struct lang_type * build_lang_type (Type *t) { struct lang_type *lt = ggc_cleared_alloc (); lt->type = t; return lt; } /* Allocate and return a lang specific structure for the frontend decl. */ struct lang_decl * build_lang_decl (Declaration *d) { /* For compiler generated run-time typeinfo, a lang_decl is allocated even if there's no associated frontend symbol to refer to (yet). If the symbol appears later in the compilation, then the slot will be re-used. */ if (d == NULL) return ggc_cleared_alloc (); struct lang_decl *ld = (d->csym) ? DECL_LANG_SPECIFIC (d->csym) : NULL; if (ld == NULL) ld = ggc_cleared_alloc (); if (ld->decl == NULL) ld->decl = d; return ld; } /* Implements the lang_hooks.dup_lang_specific_decl routine for language D. Replace the DECL_LANG_SPECIFIC field of NODE with a copy. */ static void d_dup_lang_specific_decl (tree node) { if (! DECL_LANG_SPECIFIC (node)) return; struct lang_decl *ld = ggc_alloc (); memcpy (ld, DECL_LANG_SPECIFIC (node), sizeof (struct lang_decl)); DECL_LANG_SPECIFIC (node) = ld; } /* This preserves trees we create from the garbage collector. */ static GTY(()) tree d_keep_list = NULL_TREE; void d_keep (tree t) { d_keep_list = tree_cons (NULL_TREE, t, d_keep_list); } /* Implements the lang_hooks.eh_personality routine for language D. Return the GDC personality function decl. */ static GTY(()) tree d_eh_personality_decl; static tree d_eh_personality (void) { if (!d_eh_personality_decl) d_eh_personality_decl = build_personality_function ("gdc"); return d_eh_personality_decl; } /* Implements the lang_hooks.eh_runtime_type routine for language D. */ static tree d_build_eh_runtime_type (tree type) { Type *t = TYPE_LANG_FRONTEND (type); if (t != NULL) t = t->toBasetype (); gcc_assert (t != NULL && t->ty == Tclass); ClassDeclaration *cd = ((TypeClass *) t)->sym; tree decl; if (cd->isCPPclass ()) decl = get_cpp_typeinfo_decl (cd); else decl = get_classinfo_decl (cd); return convert (ptr_type_node, build_address (decl)); } /* Definitions for our language-specific hooks. */ #undef LANG_HOOKS_NAME #undef LANG_HOOKS_INIT #undef LANG_HOOKS_INIT_TS #undef LANG_HOOKS_INIT_OPTIONS #undef LANG_HOOKS_INIT_OPTIONS_STRUCT #undef LANG_HOOKS_OPTION_LANG_MASK #undef LANG_HOOKS_HANDLE_OPTION #undef LANG_HOOKS_POST_OPTIONS #undef LANG_HOOKS_PARSE_FILE #undef LANG_HOOKS_COMMON_ATTRIBUTE_TABLE #undef LANG_HOOKS_ATTRIBUTE_TABLE #undef LANG_HOOKS_GET_ALIAS_SET #undef LANG_HOOKS_TYPES_COMPATIBLE_P #undef LANG_HOOKS_BUILTIN_FUNCTION #undef LANG_HOOKS_BUILTIN_FUNCTION_EXT_SCOPE #undef LANG_HOOKS_REGISTER_BUILTIN_TYPE #undef LANG_HOOKS_FINISH_INCOMPLETE_DECL #undef LANG_HOOKS_GIMPLIFY_EXPR #undef LANG_HOOKS_CLASSIFY_RECORD #undef LANG_HOOKS_TREE_SIZE #undef LANG_HOOKS_PRINT_XNODE #undef LANG_HOOKS_DUP_LANG_SPECIFIC_DECL #undef LANG_HOOKS_EH_PERSONALITY #undef LANG_HOOKS_EH_RUNTIME_TYPE #undef LANG_HOOKS_PUSHDECL #undef LANG_HOOKS_GETDECLS #undef LANG_HOOKS_GLOBAL_BINDINGS_P #undef LANG_HOOKS_TYPE_FOR_MODE #undef LANG_HOOKS_TYPE_FOR_SIZE #undef LANG_HOOKS_TYPE_PROMOTES_TO #define LANG_HOOKS_NAME "GNU D" #define LANG_HOOKS_INIT d_init #define LANG_HOOKS_INIT_TS d_init_ts #define LANG_HOOKS_INIT_OPTIONS d_init_options #define LANG_HOOKS_INIT_OPTIONS_STRUCT d_init_options_struct #define LANG_HOOKS_OPTION_LANG_MASK d_option_lang_mask #define LANG_HOOKS_HANDLE_OPTION d_handle_option #define LANG_HOOKS_POST_OPTIONS d_post_options #define LANG_HOOKS_PARSE_FILE d_parse_file #define LANG_HOOKS_COMMON_ATTRIBUTE_TABLE d_langhook_common_attribute_table #define LANG_HOOKS_ATTRIBUTE_TABLE d_langhook_attribute_table #define LANG_HOOKS_GET_ALIAS_SET d_get_alias_set #define LANG_HOOKS_TYPES_COMPATIBLE_P d_types_compatible_p #define LANG_HOOKS_BUILTIN_FUNCTION d_builtin_function #define LANG_HOOKS_BUILTIN_FUNCTION_EXT_SCOPE d_builtin_function_ext_scope #define LANG_HOOKS_REGISTER_BUILTIN_TYPE d_register_builtin_type #define LANG_HOOKS_FINISH_INCOMPLETE_DECL d_finish_incomplete_decl #define LANG_HOOKS_GIMPLIFY_EXPR d_gimplify_expr #define LANG_HOOKS_CLASSIFY_RECORD d_classify_record #define LANG_HOOKS_TREE_SIZE d_tree_size #define LANG_HOOKS_PRINT_XNODE d_print_xnode #define LANG_HOOKS_DUP_LANG_SPECIFIC_DECL d_dup_lang_specific_decl #define LANG_HOOKS_EH_PERSONALITY d_eh_personality #define LANG_HOOKS_EH_RUNTIME_TYPE d_build_eh_runtime_type #define LANG_HOOKS_PUSHDECL d_pushdecl #define LANG_HOOKS_GETDECLS d_getdecls #define LANG_HOOKS_GLOBAL_BINDINGS_P d_global_bindings_p #define LANG_HOOKS_TYPE_FOR_MODE d_type_for_mode #define LANG_HOOKS_TYPE_FOR_SIZE d_type_for_size #define LANG_HOOKS_TYPE_PROMOTES_TO d_type_promotes_to struct lang_hooks lang_hooks = LANG_HOOKS_INITIALIZER; #include "gt-d-d-lang.h" #include "gtype-d.h"