]> git.ipfire.org Git - thirdparty/gcc.git/blobdiff - gcc/d/d-lang.cc
Update copyright years.
[thirdparty/gcc.git] / gcc / d / d-lang.cc
index dbf7a8b60b9353421f746ea838a67ed7a727392f..7840cf8a13283c5586e63a8d0e89ba819f137614 100644 (file)
@@ -1,5 +1,5 @@
 /* d-lang.cc -- Language-dependent hooks for D.
-   Copyright (C) 2006-2021 Free Software Foundation, Inc.
+   Copyright (C) 2006-2024 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
@@ -32,8 +32,8 @@ along with GCC; see the file COPYING3.  If not see
 #include "dmd/mangle.h"
 #include "dmd/module.h"
 #include "dmd/mtype.h"
-#include "dmd/root/file.h"
 #include "dmd/target.h"
+#include "dmd/template.h"
 
 #include "opts.h"
 #include "alias.h"
@@ -293,17 +293,17 @@ d_init_options (unsigned int, cl_decoded_option *decoded_options)
   /* Set default values.  */
   global._init ();
 
-  global.vendor = lang_hooks.name;
+  global.compileEnv.vendor = lang_hooks.name;
   global.params.argv0 = xstrdup (decoded_options[0].arg);
-  global.params.errorLimit = flag_max_errors;
 
-  /* Default extern(C++) mangling to C++14.  */
-  global.params.cplusplus = CppStdRevisionCpp14;
+  /* Default extern(C++) mangling to C++17.  */
+  global.params.cplusplus = CppStdRevisionCpp17;
 
   /* Warnings and deprecations are disabled by default.  */
   global.params.useDeprecated = DIAGNOSTICinform;
   global.params.warnings = DIAGNOSTICoff;
-  global.params.messageStyle = MESSAGESTYLEgnu;
+  global.params.v.errorLimit = flag_max_errors;
+  global.params.v.messageStyle = MessageStyle::gnu;
 
   global.params.imppath = d_gc_malloc<Strings> ();
   global.params.fileImppath = d_gc_malloc<Strings> ();
@@ -456,21 +456,9 @@ d_handle_option (size_t scode, const char *arg, HOST_WIDE_INT value,
       break;
 
     case OPT_fdebug_:
-      if (ISDIGIT (arg[0]))
-       {
-         int level = integral_argument (arg);
-         if (level != -1)
-           {
-             global.params.debuglevel = level;
-             break;
-           }
-       }
-
       if (Identifier::isValidIdentifier (CONST_CAST (char *, arg)))
        {
-         if (!global.params.debugids)
-           global.params.debugids = d_gc_malloc<Strings> ();
-         global.params.debugids->push (arg);
+         DebugCondition::addGlobalIdent (arg);
          break;
        }
 
@@ -478,21 +466,21 @@ d_handle_option (size_t scode, const char *arg, HOST_WIDE_INT value,
       break;
 
     case OPT_fdoc:
-      global.params.doDocComments = value;
+      global.params.ddoc.doOutput = value;
       break;
 
     case OPT_fdoc_dir_:
-      global.params.doDocComments = true;
-      global.params.docdir = arg;
+      global.params.ddoc.doOutput = true;
+      global.params.ddoc.dir = arg;
       break;
 
     case OPT_fdoc_file_:
-      global.params.doDocComments = true;
-      global.params.docname = arg;
+      global.params.ddoc.doOutput = true;
+      global.params.ddoc.name = arg;
       break;
 
     case OPT_fdoc_inc_:
-      global.params.ddocfiles.push (arg);
+      global.params.ddoc.files.push (arg);
       break;
 
     case OPT_fdruntime:
@@ -500,13 +488,12 @@ d_handle_option (size_t scode, const char *arg, HOST_WIDE_INT value,
       break;
 
     case OPT_fdump_c___spec_:
-      if (global.params.doCxxHdrGeneration == CxxHeaderMode::none)
-       global.params.doCxxHdrGeneration = CxxHeaderMode::silent;
-      global.params.cxxhdrname = arg;
+      global.params.cxxhdr.doOutput = true;
+      global.params.cxxhdr.name = arg;
       break;
 
     case OPT_fdump_c___spec_verbose:
-      global.params.doCxxHdrGeneration = CxxHeaderMode::verbose;
+      global.params.cxxhdr.fullOutput = true;
       break;
 
     case OPT_fdump_d_original:
@@ -569,19 +556,23 @@ d_handle_option (size_t scode, const char *arg, HOST_WIDE_INT value,
 
     case OPT_fpreview_all:
       global.params.ehnogc = value;
-      global.params.useDIP25 = FeatureState::enabled;
       global.params.useDIP1000 = FeatureState::enabled;
       global.params.useDIP1021 = value;
+      global.params.bitfields = value;
       global.params.dtorFields = FeatureState::enabled;
-      global.params.fieldwise = value;
+      global.params.fieldwise = FeatureState::enabled;
       global.params.fixAliasThis = value;
       global.params.previewIn = value;
       global.params.fix16997 = value;
-      global.params.markdown = value;
-      global.params.noSharedAccess = value;
-      global.params.rvalueRefParam = value;
+      global.params.noSharedAccess = FeatureState::enabled;
+      global.params.rvalueRefParam = FeatureState::enabled;
       global.params.inclusiveInContracts = value;
-      global.params.shortenedMethods = value;
+      global.params.systemVariables = FeatureState::enabled;
+      global.params.fixImmutableConv = value;
+      break;
+
+    case OPT_fpreview_bitfields:
+      global.params.bitfields = value;
       break;
 
     case OPT_fpreview_dip1000:
@@ -596,22 +587,22 @@ d_handle_option (size_t scode, const char *arg, HOST_WIDE_INT value,
       global.params.useDIP1021 = value;
       break;
 
-    case OPT_fpreview_dip25:
-      global.params.useDIP25 = FeatureState::enabled;
-      break;
-
     case OPT_fpreview_dtorfields:
       global.params.dtorFields = FeatureState::enabled;
       break;
 
     case OPT_fpreview_fieldwise:
-      global.params.fieldwise = value;
+      global.params.fieldwise = FeatureState::enabled;
       break;
 
     case OPT_fpreview_fixaliasthis:
       global.params.fixAliasThis = value;
       break;
 
+    case OPT_fpreview_fiximmutableconv:
+      global.params.fixImmutableConv = value;
+      break;
+
     case OPT_fpreview_in:
       global.params.previewIn = value;
       break;
@@ -620,20 +611,16 @@ d_handle_option (size_t scode, const char *arg, HOST_WIDE_INT value,
       global.params.inclusiveInContracts = value;
       break;
 
-    case OPT_fpreview_intpromote:
-      global.params.fix16997 = value;
-      break;
-
     case OPT_fpreview_nosharedaccess:
-      global.params.noSharedAccess = value;
+      global.params.noSharedAccess = FeatureState::enabled;
       break;
 
     case OPT_fpreview_rvaluerefparam:
-      global.params.rvalueRefParam = value;
+      global.params.rvalueRefParam = FeatureState::enabled;
       break;
 
-    case OPT_fpreview_shortenedmethods:
-      global.params.shortenedMethods = value;
+    case OPT_fpreview_systemvariables:
+      global.params.systemVariables = FeatureState::enabled;
       break;
 
     case OPT_frelease:
@@ -641,21 +628,21 @@ d_handle_option (size_t scode, const char *arg, HOST_WIDE_INT value,
       break;
 
     case OPT_frevert_all:
-      global.params.useDIP25 = FeatureState::disabled;
-      global.params.markdown = !value;
+      global.params.useDIP1000 = FeatureState::disabled;
       global.params.dtorFields = FeatureState::disabled;
+      global.params.fix16997 = !value;
       break;
 
-    case OPT_frevert_dip25:
-      global.params.useDIP25 = FeatureState::disabled;
+    case OPT_frevert_dip1000:
+      global.params.useDIP1000 = FeatureState::disabled;
       break;
 
     case OPT_frevert_dtorfields:
       global.params.dtorFields = FeatureState::disabled;
       break;
 
-    case OPT_frevert_markdown:
-      global.params.markdown = !value;
+    case OPT_frevert_intpromote:
+      global.params.fix16997 = !value;
       break;
 
     case OPT_frtti:
@@ -663,8 +650,9 @@ d_handle_option (size_t scode, const char *arg, HOST_WIDE_INT value,
       break;
 
     case OPT_fsave_mixins_:
-      global.params.mixinFile = arg;
-      global.params.mixinOut = d_gc_malloc<OutBuffer> ();
+      global.params.mixinOut.doOutput = true;
+      global.params.mixinOut.name = arg;
+      global.params.mixinOut.buffer = d_gc_malloc<OutBuffer> ();
       break;
 
     case OPT_fswitch_errors:
@@ -672,30 +660,30 @@ d_handle_option (size_t scode, const char *arg, HOST_WIDE_INT value,
       break;
 
     case OPT_ftransition_all:
-      global.params.vfield = value;
-      global.params.vgc = value;
-      global.params.vmarkdown= value;
-      global.params.vtls = value;
+      global.params.v.field = value;
+      global.params.v.gc = value;
+      global.params.v.vin = value;
+      global.params.v.tls = value;
       break;
 
     case OPT_ftransition_field:
-      global.params.vfield = value;
+      global.params.v.field = value;
       break;
 
-    case OPT_ftransition_nogc:
-      global.params.vgc = value;
+    case OPT_ftransition_in:
+      global.params.v.vin = value;
       break;
 
-    case OPT_ftransition_vmarkdown:
-      global.params.vmarkdown = value;
+    case OPT_ftransition_nogc:
+      global.params.v.gc = value;
       break;
 
     case OPT_ftransition_templates:
-      global.params.vtemplates = value;
+      global.params.v.templates = value;
       break;
 
     case OPT_ftransition_tls:
-      global.params.vtls = value;
+      global.params.v.tls = value;
       break;
 
     case OPT_funittest:
@@ -703,21 +691,9 @@ d_handle_option (size_t scode, const char *arg, HOST_WIDE_INT value,
       break;
 
     case OPT_fversion_:
-      if (ISDIGIT (arg[0]))
-       {
-         int level = integral_argument (arg);
-         if (level != -1)
-           {
-             global.params.versionlevel = level;
-             break;
-           }
-       }
-
       if (Identifier::isValidIdentifier (CONST_CAST (char *, arg)))
        {
-         if (!global.params.versionids)
-           global.params.versionids = d_gc_malloc<Strings> ();
-         global.params.versionids->push (arg);
+         VersionCondition::addGlobalIdent (arg);
          break;
        }
 
@@ -725,17 +701,17 @@ d_handle_option (size_t scode, const char *arg, HOST_WIDE_INT value,
       break;
 
     case OPT_H:
-      global.params.doHdrGeneration = true;
+      global.params.dihdr.doOutput = true;
       break;
 
     case OPT_Hd:
-      global.params.doHdrGeneration = true;
-      global.params.hdrdir = arg;
+      global.params.dihdr.doOutput = true;
+      global.params.dihdr.dir = arg;
       break;
 
     case OPT_Hf:
-      global.params.doHdrGeneration = true;
-      global.params.hdrname = arg;
+      global.params.dihdr.doOutput = true;
+      global.params.dihdr.name = arg;
       break;
 
     case OPT_imultilib:
@@ -793,7 +769,7 @@ d_handle_option (size_t scode, const char *arg, HOST_WIDE_INT value,
       break;
 
     case OPT_v:
-      global.params.verbose = value;
+      global.params.v.verbose = value;
       break;
 
     case OPT_Wall:
@@ -812,15 +788,15 @@ d_handle_option (size_t scode, const char *arg, HOST_WIDE_INT value,
 
     case OPT_Wspeculative:
       if (value)
-       global.params.showGaggedErrors = 1;
+       global.params.v.showGaggedErrors = 1;
       break;
 
     case OPT_Xf:
-      global.params.jsonfilename = arg;
+      global.params.json.name = arg;
       /* Fall through.  */
 
     case OPT_X:
-      global.params.doJsonGeneration = true;
+      global.params.json.doOutput = true;
       break;
 
     default:
@@ -893,17 +869,28 @@ d_post_options (const char ** fn)
        ? CHECKENABLEoff : CHECKENABLEon;
     }
 
+  /* When not linking against D runtime, turn off all code generation that
+     would otherwise reference it.  */
   if (global.params.betterC)
     {
       if (!OPTION_SET_P (flag_moduleinfo))
        global.params.useModuleInfo = false;
 
+      /* Ensure that the front-end options are in sync with the `-frtti' and
+        `-fexceptions' flags.  */
       if (!OPTION_SET_P (flag_rtti))
-       global.params.useTypeInfo = false;
+       {
+         global.params.useTypeInfo = false;
+         flag_rtti = false;
+       }
 
       if (!OPTION_SET_P (flag_exceptions))
-       global.params.useExceptions = false;
+       {
+         global.params.useExceptions = false;
+         flag_exceptions = false;
+       }
 
+      global.params.useGC = false;
       global.params.checkAction = CHECKACTION_C;
     }
 
@@ -911,10 +898,6 @@ d_post_options (const char ** fn)
   if (global.params.useDIP1021)
     global.params.useDIP1000 = FeatureState::enabled;
 
-  /* Enabling DIP1000 implies DIP25.  */
-  if (global.params.useDIP1000 == FeatureState::enabled)
-    global.params.useDIP25 = FeatureState::enabled;
-
   /* Keep in sync with existing -fbounds-check flag.  */
   flag_bounds_check = (global.params.useArrayBounds == CHECKENABLEon);
 
@@ -929,44 +912,30 @@ d_post_options (const char ** fn)
       && global.params.warnings == DIAGNOSTICerror)
     global.params.useDeprecated = DIAGNOSTICerror;
 
-  /* Make -fmax-errors visible to frontend's diagnostic machinery.  */
-  if (OPTION_SET_P (flag_max_errors))
-    global.params.errorLimit = flag_max_errors;
-
   if (flag_excess_precision == EXCESS_PRECISION_DEFAULT)
     flag_excess_precision = EXCESS_PRECISION_STANDARD;
 
-  global.params.symdebug = write_symbols != NO_DEBUG;
   global.params.useInline = flag_inline_functions;
-  global.params.showColumns = flag_show_column;
-  global.params.printErrorContext = flag_diagnostics_show_caret;
 
-  if (global.params.useInline)
-    global.params.hdrStripPlainFunctions = false;
+  /* Make -fmax-errors visible to frontend's diagnostic machinery.  */
+  if (OPTION_SET_P (flag_max_errors))
+    global.params.v.errorLimit = flag_max_errors;
 
-  global.params.obj = !flag_syntax_only;
+  global.params.v.showColumns = flag_show_column;
+  global.params.v.printErrorContext = flag_diagnostics_show_caret;
 
-  /* Has no effect yet.  */
-  global.params.pic = flag_pic != 0;
+  /* Keep the front-end location type in sync with params.  */
+  Loc::set (global.params.v.showColumns, global.params.v.messageStyle);
 
-  /* Add in versions given on the command line.  */
-  if (global.params.versionids)
-    {
-      for (size_t i = 0; i < global.params.versionids->length; i++)
-       {
-         const char *s = (*global.params.versionids)[i];
-         VersionCondition::addGlobalIdent (s);
-       }
-    }
+  if (global.params.useInline)
+    global.params.dihdr.fullOutput = true;
 
-  if (global.params.debugids)
-    {
-      for (size_t i = 0; i < global.params.debugids->length; i++)
-       {
-         const char *s = (*global.params.debugids)[i];
-         DebugCondition::addGlobalIdent (s);
-       }
-    }
+  global.params.obj = !flag_syntax_only;
+
+  /* The front-end parser only has access to `compileEnv', synchronize its
+     fields with params.  */
+  global.compileEnv.previewIn = global.params.previewIn;
+  global.compileEnv.ddocOutput = global.params.ddoc.doOutput;
 
   if (warn_return_type == -1)
     warn_return_type = 0;
@@ -984,12 +953,96 @@ d_add_builtin_module (Module *m)
   builtin_modules.push (m);
 }
 
+/* Writes to FILENAME.  DATA is the full content of the file to be written.  */
+
+static void
+d_write_file (const char *filename, const char *data)
+{
+  FILE *stream;
+
+  if (filename && (filename[0] != '-' || filename[1] != '\0'))
+    stream = fopen (filename, "w");
+  else
+    stream = stdout;
+
+  if (!stream)
+    {
+      error ("unable to open %s for writing: %m", filename);
+      return;
+    }
+
+  fprintf (stream, "%s", data);
+
+  if (stream != stdout && (ferror (stream) || fclose (stream)))
+    error ("writing output file %s: %m", filename);
+}
+
+/* Read ddoc macro files named by the DDOCFILES, then write the concatenated
+   the contents into DDOCBUF.  */
+
+static void
+d_read_ddoc_files (Strings &ddocfiles, OutBuffer &ddocbuf)
+{
+  if (ddocbuf.length ())
+    return;
+
+  for (size_t i = 0; i < ddocfiles.length; i++)
+    {
+      int fd = open (ddocfiles[i], O_RDONLY);
+      bool ok = false;
+      struct stat buf;
+
+      if (fd == -1 || fstat (fd, &buf))
+       {
+         error ("unable to open %s for reading: %m", ddocfiles[i]);
+         continue;
+       }
+
+      /* Check we've not been given a directory, or a file bigger than 4GB.  */
+      if (S_ISDIR (buf.st_mode))
+       errno = ENOENT;
+      else if (buf.st_size != unsigned (buf.st_size))
+       errno = EMFILE;
+      else
+       {
+         unsigned size = unsigned (buf.st_size);
+         char *buffer = (char *) xmalloc (size);
+
+         if (read (fd, buffer, size) == ssize_t (size))
+           {
+             ddocbuf.write (buffer, size);
+             ok = true;
+           }
+
+         free (buffer);
+       }
+
+      close (fd);
+      if (!ok)
+       fatal_error (input_location, "reading ddoc file %s: %m", ddocfiles[i]);
+    }
+}
+
+static void
+d_generate_ddoc_file (Module *m, OutBuffer &ddocbuf)
+{
+  input_location = make_location_t (m->loc);
+
+  d_read_ddoc_files (global.params.ddoc.files, ddocbuf);
+
+  OutBuffer ddocbuf_out;
+  gendocfile (m, ddocbuf.peekChars (), ddocbuf.length (), global.datetime,
+             global.errorSink, ddocbuf_out);
+
+  d_write_file (m->docfile.toChars (), ddocbuf_out.peekChars ());
+}
+
 /* Implements the lang_hooks.parse_file routine for language D.  */
 
 static void
 d_parse_file (void)
 {
-  if (global.params.verbose)
+  if (global.params.v.verbose)
     {
       message ("binary    %s", global.params.argv0.ptr);
       message ("version   %s", global.versionChars ());
@@ -1020,6 +1073,9 @@ d_parse_file (void)
   Modules modules;
   modules.reserve (num_in_fnames);
 
+  /* Buffer for contents of .ddoc files.  */
+  OutBuffer ddocbuf;
+
   /* 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)
@@ -1057,15 +1113,18 @@ d_parse_file (void)
          /* Handling stdin, generate a unique name for the module.  */
          Module *m = Module::create (in_fnames[i],
                                      Identifier::idPool ("__stdin"),
-                                     global.params.doDocComments,
-                                     global.params.doHdrGeneration);
+                                     global.params.ddoc.doOutput,
+                                     global.params.dihdr.doOutput);
          modules.push (m);
 
+         /* Zero the padding past the end of the buffer so the D lexer has a
+            sentinel.  The lexer only reads up to 4 bytes at a time.  */
+         memset (buffer + len, '\0', 16);
+
          /* Overwrite the source file for the module, the one created by
             Module::create would have a forced a `.d' suffix.  */
-         m->srcBuffer = FileBuffer::create ();
-         m->srcBuffer->data.length = len;
-         m->srcBuffer->data.ptr = buffer;
+         m->src.length = len;
+         m->src.ptr = buffer;
        }
       else
        {
@@ -1074,8 +1133,8 @@ d_parse_file (void)
          const char *name = FileName::removeExt (basename);
 
          Module *m = Module::create (in_fnames[i], Identifier::idPool (name),
-                                     global.params.doDocComments,
-                                     global.params.doHdrGeneration);
+                                     global.params.ddoc.doOutput,
+                                     global.params.dihdr.doOutput);
          modules.push (m);
          FileName::free (name);
        }
@@ -1093,7 +1152,7 @@ d_parse_file (void)
     {
       Module *m = modules[i];
 
-      if (global.params.verbose)
+      if (global.params.v.verbose)
        message ("parse     %s", m->toChars ());
 
       if (!Module::rootModule)
@@ -1102,9 +1161,10 @@ d_parse_file (void)
       m->importedFrom = m;
       m->parse ();
 
-      if (m->isDocFile)
+      if (m->filetype == FileType::ddoc)
        {
-         gendocfile (m);
+         d_generate_ddoc_file (m, ddocbuf);
+
          /* Remove M from list of modules.  */
          modules.remove (i);
          i--;
@@ -1132,7 +1192,7 @@ d_parse_file (void)
   if (global.errors)
     goto had_errors;
 
-  if (global.params.doHdrGeneration)
+  if (global.params.dihdr.doOutput)
     {
       /* Generate 'header' import files.  Since 'header' import files must be
         independent of command line switches and what else is imported, they
@@ -1140,13 +1200,16 @@ d_parse_file (void)
       for (size_t i = 0; i < modules.length; i++)
        {
          Module *m = modules[i];
-         if (m->isHdrFile || (d_option.fonly && m != Module::rootModule))
+         if (m->filetype == FileType::dhdr
+             || (d_option.fonly && m != Module::rootModule))
            continue;
 
-         if (global.params.verbose)
+         if (global.params.v.verbose)
            message ("import    %s", m->toChars ());
 
-         genhdrfile (m);
+         OutBuffer buf;
+         genhdrfile (m, buf);
+         d_write_file (m->hdrfile.toChars (), buf.peekChars ());
        }
 
       dump_headers = true;
@@ -1160,7 +1223,7 @@ d_parse_file (void)
     {
       Module *m = modules[i];
 
-      if (global.params.verbose)
+      if (global.params.v.verbose)
        message ("importall %s", m->toChars ());
 
       m->importAll (NULL);
@@ -1176,14 +1239,21 @@ d_parse_file (void)
     {
       Module *m = modules[i];
 
-      if (global.params.verbose)
+      /* If this is the `__main` module, check that `D main` hasn't already
+        been declared in user code before running semantic on it.  */
+      if (m == main_module && global.hasMainFunction)
+       {
+         modules.remove (i);
+         continue;
+       }
+
+      if (global.params.v.verbose)
        message ("semantic  %s", m->toChars ());
 
       dsymbolSemantic (m, NULL);
     }
 
   /* Do deferred semantic analysis.  */
-  Module::dprogress = 1;
   Module::runDeferredSemantic ();
 
   if (Module::deferred.length)
@@ -1208,7 +1278,7 @@ d_parse_file (void)
     {
       Module *m = modules[i];
 
-      if (global.params.verbose)
+      if (global.params.v.verbose)
        message ("semantic2 %s", m->toChars ());
 
       semantic2 (m, NULL);
@@ -1224,7 +1294,7 @@ d_parse_file (void)
     {
       Module *m = modules[i];
 
-      if (global.params.verbose)
+      if (global.params.v.verbose)
        message ("semantic3 %s", m->toChars ());
 
       semantic3 (m, NULL);
@@ -1251,7 +1321,7 @@ d_parse_file (void)
       /* 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);
+      mangleToBuffer (Module::rootModule, buf);
       first_global_object_name = buf.extractChars ();
     }
 
@@ -1259,8 +1329,6 @@ d_parse_file (void)
   if (d_option.deps)
     {
       obstack buffer;
-      FILE *deps_stream;
-
       gcc_obstack_init (&buffer);
 
       for (size_t i = 0; i < modules.length; i++)
@@ -1270,66 +1338,28 @@ d_parse_file (void)
       if (d_option.deps_filename_user)
        d_option.deps_filename = d_option.deps_filename_user;
 
-      if (d_option.deps_filename)
-       {
-         deps_stream = fopen (d_option.deps_filename, "w");
-         if (!deps_stream)
-           {
-             fatal_error (input_location, "opening dependency file %s: %m",
-                          d_option.deps_filename);
-             goto had_errors;
-           }
-       }
-      else
-       deps_stream = stdout;
-
-      fprintf (deps_stream, "%s", (char *) obstack_finish (&buffer));
-
-      if (deps_stream != stdout
-         && (ferror (deps_stream) || fclose (deps_stream)))
-       {
-         fatal_error (input_location, "closing dependency file %s: %m",
-                      d_option.deps_filename);
-       }
+      d_write_file (d_option.deps_filename,
+                   (char *) obstack_finish (&buffer));
     }
 
+  if (global.params.v.templates)
+    printTemplateStats ();
+
   /* Generate JSON files.  */
-  if (global.params.doJsonGeneration)
+  if (global.params.json.doOutput)
     {
       OutBuffer buf;
-      json_generate (&buf, &modules);
-
-      const char *name = global.params.jsonfilename.ptr;
-      FILE *json_stream;
-
-      if (name && (name[0] != '-' || name[1] != '\0'))
-       {
-         const char *nameext
-           = FileName::defaultExt (name, json_ext.ptr);
-         json_stream = fopen (nameext, "w");
-         if (!json_stream)
-           {
-             fatal_error (input_location, "opening json file %s: %m", nameext);
-             goto had_errors;
-           }
-       }
-      else
-       json_stream = stdout;
-
-      fprintf (json_stream, "%s", buf.peekChars ());
-
-      if (json_stream != stdout
-         && (ferror (json_stream) || fclose (json_stream)))
-       fatal_error (input_location, "closing json file %s: %m", name);
+      json_generate (modules, buf);
+      d_write_file (global.params.json.name.ptr, buf.peekChars ());
     }
 
   /* Generate Ddoc files.  */
-  if (global.params.doDocComments && !global.errors && !errorcount)
+  if (global.params.ddoc.doOutput && !global.errors && !errorcount)
     {
       for (size_t i = 0; i < modules.length; i++)
        {
          Module *m = modules[i];
-         gendocfile (m);
+         d_generate_ddoc_file (m, ddocbuf);
        }
     }
 
@@ -1342,13 +1372,13 @@ d_parse_file (void)
          OutBuffer buf;
          buf.doindent = 1;
 
-         moduleToBuffer (&buf, m);
+         moduleToBuffer (buf, m);
          message ("%s", buf.peekChars ());
        }
     }
 
   /* Generate C++ header files.  */
-  if (global.params.doCxxHdrGeneration != CxxHeaderMode::none)
+  if (global.params.cxxhdr.doOutput)
     genCppHdrFiles (modules);
 
   if (global.errors)
@@ -1357,11 +1387,14 @@ d_parse_file (void)
   for (size_t i = 0; i < modules.length; i++)
     {
       Module *m = modules[i];
-      if ((m->isHdrFile && m != main_module)
+
+      /* Skip generating code for header files, or when the module wasn't
+        specified by `-fonly=`.  */
+      if ((m->filetype == FileType::dhdr && m != main_module)
          || (d_option.fonly && m != Module::rootModule))
        continue;
 
-      if (global.params.verbose)
+      if (global.params.v.verbose)
        message ("code      %s", m->toChars ());
 
       if (!flag_syntax_only)
@@ -1378,24 +1411,10 @@ d_parse_file (void)
   errorcount += (global.errors + global.warnings);
 
   /* We want to write the mixin expansion file also on error.  */
-  if (global.params.mixinOut)
+  if (global.params.mixinOut.doOutput)
     {
-      FILE *mixin_stream = fopen (global.params.mixinFile, "w");
-
-      if (mixin_stream)
-       {
-         OutBuffer *buf = global.params.mixinOut;
-         fprintf (mixin_stream, "%s", buf->peekChars ());
-
-         if (ferror (mixin_stream) || fclose (mixin_stream))
-           fatal_error (input_location, "closing mixin file %s: %m",
-                        global.params.mixinFile);
-       }
-      else
-       {
-         fatal_error (input_location, "opening mixin file %s: %m",
-                      global.params.mixinFile);
-       }
+      d_write_file (global.params.mixinOut.name.ptr,
+                   global.params.mixinOut.buffer->peekChars ());
     }
 
   /* Remove generated .di files on error.  */
@@ -1404,7 +1423,8 @@ d_parse_file (void)
       for (size_t i = 0; i < modules.length; i++)
        {
          Module *m = modules[i];
-         if (m->isHdrFile || (d_option.fonly && m != Module::rootModule))
+         if (m->filetype == FileType::dhdr
+             || (d_option.fonly && m != Module::rootModule))
            continue;
 
          remove (m->hdrfile.toChars ());
@@ -1532,7 +1552,7 @@ d_type_promotes_to (tree type)
   /* Promotions are only applied on unnamed function arguments for declarations
      with `extern(C)' or `extern(C++)' linkage.  */
   if (cfun && DECL_LANG_FRONTEND (cfun->decl)
-      && DECL_LANG_FRONTEND (cfun->decl)->linkage != LINK::d)
+      && DECL_LANG_FRONTEND (cfun->decl)->resolvedLinkage () != LINK::d)
     {
       /* In [type/integer-promotions], integer promotions are conversions of the
         following types:
@@ -1898,6 +1918,21 @@ d_enum_underlying_base_type (const_tree type)
   return TREE_TYPE (type);
 }
 
+/* Get a value for the SARIF v2.1.0 "artifact.sourceLanguage" property,
+   based on the list in SARIF v2.1.0 Appendix J.  */
+
+static const char *
+d_get_sarif_source_language (const char *)
+{
+  return "d";
+}
+
+const scoped_attribute_specs *const d_langhook_attribute_table[] =
+{
+  &d_langhook_gnu_attribute_table,
+  &d_langhook_common_attribute_table,
+};
+
 /* Definitions for our language-specific hooks.  */
 
 #undef LANG_HOOKS_NAME
@@ -1909,7 +1944,6 @@ d_enum_underlying_base_type (const_tree type)
 #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
@@ -1931,6 +1965,7 @@ d_enum_underlying_base_type (const_tree type)
 #undef LANG_HOOKS_TYPE_FOR_MODE
 #undef LANG_HOOKS_TYPE_FOR_SIZE
 #undef LANG_HOOKS_TYPE_PROMOTES_TO
+#undef LANG_HOOKS_GET_SARIF_SOURCE_LANGUAGE
 
 #define LANG_HOOKS_NAME                            "GNU D"
 #define LANG_HOOKS_INIT                            d_init
@@ -1941,7 +1976,6 @@ d_enum_underlying_base_type (const_tree type)
 #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
@@ -1963,6 +1997,7 @@ d_enum_underlying_base_type (const_tree type)
 #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
+#define LANG_HOOKS_GET_SARIF_SOURCE_LANGUAGE d_get_sarif_source_language
 
 struct lang_hooks lang_hooks = LANG_HOOKS_INITIALIZER;