blob: 62a8ddd69b290e10a42243d5fa58ced8ae03a8b7 [file] [log] [blame]
/* d-lang.cc -- Language-dependent hooks for D.
Copyright (C) 2006-2019 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
<http://www.gnu.org/licenses/>. */
#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=<arg> */
const char *multilib; /* -imultilib <dir> */
const char *prefix; /* -iprefix <dir> */
bool deps; /* -M */
bool deps_skip_system; /* -MM */
const char *deps_filename; /* -M[M]D */
const char *deps_filename_user; /* -MF <arg> */
OutBuffer *deps_target; /* -M[QT] <arg> */
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<tree, va_gc> *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 <const char *> dependencies;
Modules modlist;
modlist.push (module);
Modules phonylist;
const char *str;
unsigned size;
unsigned column = 0;
/* Write out make target module name. */
if (d_option.deps_target)
{
size = d_option.deps_target->offset;
str = d_option.deps_target->extractString ();
}
else
{
str = module->objfile->name->str;
size = strlen (str);
}
buffer->writestring (str);
column = size;
buffer->writestring (":");
column++;
/* Write out all make dependencies. */
while (modlist.dim > 0)
{
Module *depmod = modlist.pop ();
str = depmod->srcfile->name->str;
size = strlen (str);
/* Skip dependencies that have already been written. */
if (dependencies.add (str))
continue;
column += size;
if (colmax && column > colmax)
{
buffer->writestring (" \\\n ");
column = size + 1;
}
else
{
buffer->writestring (" ");
column++;
}
buffer->writestring (str);
/* Add to list of phony targets if is not being compile. */
if (d_option.deps_phony && !depmod->isRoot ())
phonylist.push (depmod);
/* Search all imports of the written dependency. */
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);
}
}
buffer->writenl ();
/* Write out all phony targets. */
for (size_t i = 0; i < phonylist.dim; i++)
{
Module *m = phonylist[i];
buffer->writenl ();
buffer->writestring (m->srcfile->name->str);
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<binding_level> ();
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_cmdline == EXCESS_PRECISION_DEFAULT)
flag_excess_precision_cmdline = 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)
{
/* Handling stdin, generate a unique name for the module. */
obstack buffer;
gcc_obstack_init (&buffer);
int c;
Module *m = Module::create (in_fnames[i],
Identifier::generateId ("__stdin"),
global.params.doDocComments,
global.params.doHdrGeneration);
modules.push (m);
/* Load the entire contents of stdin into memory. */
while ((c = getc (stdin)) != EOF)
obstack_1grow (&buffer, c);
if (!obstack_object_size (&buffer))
obstack_1grow (&buffer, '\0');
/* Overwrite the source file for the module, the one created by
Module::create would have a forced a `.d' suffix. */
m->srcfile = File::create ("<stdin>");
m->srcfile->len = obstack_object_size (&buffer);
m->srcfile->buffer = (unsigned char *) obstack_finish (&buffer);
/* Tell the front-end not to free the buffer after parsing. */
m->srcfile->ref = 1;
}
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 <arg> 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);
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;
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<struct lang_type> ();
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> ();
struct lang_decl *ld = (d->csym) ? DECL_LANG_SPECIFIC (d->csym) : NULL;
if (ld == NULL)
ld = ggc_cleared_alloc<struct lang_decl> ();
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<struct lang_decl> ();
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_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_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"