/* 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"
