1 /**
2  * Stores command line options and contains other miscellaneous declarations.
3  *
4  * Copyright:   Copyright (C) 1999-2020 by The D Language Foundation, All Rights Reserved
5  * Authors:     $(LINK2 http://www.digitalmars.com, Walter Bright)
6  * License:     $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
7  * Source:      $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/globals.d, _globals.d)
8  * Documentation:  https://dlang.org/phobos/dmd_globals.html
9  * Coverage:    https://codecov.io/gh/dlang/dmd/src/master/src/dmd/globals.d
10  */
11 
12 module dmd.globals;
13 
14 import core.stdc.stdint;
15 import dmd.root.array;
16 import dmd.root.filename;
17 import dmd.root.outbuffer;
18 import dmd.identifier;
19 
20 template xversion(string s)
21 {
22     enum xversion = mixin(`{ version (` ~ s ~ `) return true; else return false; }`)();
23 }
24 
25 enum TARGET : bool
26 {
27     Linux        = xversion!`linux`,
28     OSX          = xversion!`OSX`,
29     FreeBSD      = xversion!`FreeBSD`,
30     OpenBSD      = xversion!`OpenBSD`,
31     Solaris      = xversion!`Solaris`,
32     Windows      = xversion!`Windows`,
33     DragonFlyBSD = xversion!`DragonFlyBSD`,
34 }
35 
36 enum DiagnosticReporting : ubyte
37 {
38     error,        // generate an error
39     inform,       // generate a warning
40     off,          // disable diagnostic
41 }
42 
43 enum MessageStyle : ubyte
44 {
45     digitalmars,  // filename.d(line): message
46     gnu,          // filename.d:line: message, see https://www.gnu.org/prep/standards/html_node/Errors.html
47 }
48 
49 enum CHECKENABLE : ubyte
50 {
51     _default,     // initial value
52     off,          // never do checking
53     on,           // always do checking
54     safeonly,     // do checking only in @safe functions
55 }
56 
57 enum CHECKACTION : ubyte
58 {
59     D,            // call D assert on failure
60     C,            // call C assert on failure
61     halt,         // cause program halt on failure
62     context,      // call D assert with the error context on failure
63 }
64 
65 enum CPU
66 {
67     x87,
68     mmx,
69     sse,
70     sse2,
71     sse3,
72     ssse3,
73     sse4_1,
74     sse4_2,
75     avx,                // AVX1 instruction set
76     avx2,               // AVX2 instruction set
77     avx512,             // AVX-512 instruction set
78 
79     // Special values that don't survive past the command line processing
80     baseline,           // (default) the minimum capability CPU
81     native              // the machine the compiler is being run on
82 }
83 
84 enum PIC : ubyte
85 {
86     fixed,              /// located at a specific address
87     pic,                /// Position Independent Code
88     pie,                /// Position Independent Executable
89 }
90 
91 /**
92 Each flag represents a field that can be included in the JSON output.
93 
94 NOTE: set type to uint so its size matches C++ unsigned type
95 */
96 enum JsonFieldFlags : uint
97 {
98     none         = 0,
99     compilerInfo = (1 << 0),
100     buildInfo    = (1 << 1),
101     modules      = (1 << 2),
102     semantics    = (1 << 3),
103 }
104 
105 enum CppStdRevision : uint
106 {
107     cpp98 = 1997_11,
108     cpp11 = 2011_03,
109     cpp14 = 2014_02,
110     cpp17 = 2017_03
111 }
112 
113 /// Configuration for the C++ header generator
114 enum CxxHeaderMode : uint
115 {
116     none,   /// Don't generate headers
117     silent, /// Generate headers
118     verbose /// Generate headers and add comments for hidden declarations
119 }
120 
121 // Put command line switches in here
122 extern (C++) struct Param
123 {
124     bool obj = true;        // write object file
125     bool link = true;       // perform link
126     bool dll;               // generate shared dynamic library
127     bool lib;               // write library file instead of object file(s)
128     bool multiobj;          // break one object file into multiple ones
129     bool oneobj;            // write one object file instead of multiple ones
130     bool trace;             // insert profiling hooks
131     bool tracegc;           // instrument calls to 'new'
132     bool verbose;           // verbose compile
133     bool vcg_ast;           // write-out codegen-ast
134     bool showColumns;       // print character (column) numbers in diagnostics
135     bool vtls;              // identify thread local variables
136     bool vtemplates;        // collect and list statistics on template instantiations
137     bool vtemplatesListInstances; // collect and list statistics on template instantiations origins. TODO: make this an enum when we want to list other kinds of instances
138     bool vgc;               // identify gc usage
139     bool vfield;            // identify non-mutable field variables
140     bool vcomplex;          // identify complex/imaginary type usage
141     ubyte symdebug;         // insert debug symbolic information
142     bool symdebugref;       // insert debug information for all referenced types, too
143     bool alwaysframe;       // always emit standard stack frame
144     bool optimize;          // run optimizer
145     bool map;               // generate linker .map file
146     bool is64bit = (size_t.sizeof == 8);  // generate 64 bit code; true by default for 64 bit dmd
147     bool isLP64;            // generate code for LP64
148     bool isLinux;           // generate code for linux
149     bool isOSX;             // generate code for Mac OSX
150     bool isWindows;         // generate code for Windows
151     bool isFreeBSD;         // generate code for FreeBSD
152     bool isOpenBSD;         // generate code for OpenBSD
153     bool isDragonFlyBSD;    // generate code for DragonFlyBSD
154     bool isSolaris;         // generate code for Solaris
155     bool hasObjectiveC;     // target supports Objective-C
156     bool mscoff = false;    // for Win32: write MsCoff object files instead of OMF
157     DiagnosticReporting useDeprecated = DiagnosticReporting.inform;  // how use of deprecated features are handled
158     bool stackstomp;            // add stack stomping code
159     bool useUnitTests;          // generate unittest code
160     bool useInline = false;     // inline expand functions
161     bool useDIP25;          // implement http://wiki.dlang.org/DIP25
162     bool noDIP25;           // revert to pre-DIP25 behavior
163     bool useDIP1021;        // implement https://github.com/dlang/DIPs/blob/master/DIPs/DIP1021.md
164     bool release;           // build release version
165     bool preservePaths;     // true means don't strip path from source file
166     DiagnosticReporting warnings = DiagnosticReporting.off;  // how compiler warnings are handled
167     PIC pic = PIC.fixed;    // generate fixed, pic or pie code
168     bool color;             // use ANSI colors in console output
169     bool cov;               // generate code coverage data
170     ubyte covPercent;       // 0..100 code coverage percentage required
171     bool ctfe_cov = false;  // generate coverage data for ctfe
172     bool nofloat;           // code should not pull in floating point support
173     bool ignoreUnsupportedPragmas;  // rather than error on them
174     bool useModuleInfo = true;   // generate runtime module information
175     bool useTypeInfo = true;     // generate runtime type information
176     bool useExceptions = true;   // support exception handling
177     bool noSharedAccess;         // read/write access to shared memory objects
178     bool previewIn;         // `in` means `[ref] scope const`, accepts rvalues
179     bool betterC;           // be a "better C" compiler; no dependency on D runtime
180     bool addMain;           // add a default main() function
181     bool allInst;           // generate code for all template instantiations
182     bool fix16997;          // fix integral promotions for unary + - ~ operators
183                             // https://issues.dlang.org/show_bug.cgi?id=16997
184     bool fixAliasThis;      // if the current scope has an alias this, check it before searching upper scopes
185     /** The --transition=safe switch should only be used to show code with
186      * silent semantics changes related to @safe improvements.  It should not be
187      * used to hide a feature that will have to go through deprecate-then-error
188      * before becoming default.
189      */
190     bool vsafe;             // use enhanced @safe checking
191     bool ehnogc;            // use @nogc exception handling
192     bool dtorFields;        // destruct fields of partially constructed objects
193                             // https://issues.dlang.org/show_bug.cgi?id=14246
194     bool fieldwise;         // do struct equality testing field-wise rather than by memcmp()
195     bool rvalueRefParam;    // allow rvalues to be arguments to ref parameters
196                             // http://dconf.org/2019/talks/alexandrescu.html
197                             // https://gist.github.com/andralex/e5405a5d773f07f73196c05f8339435a
198                             // https://digitalmars.com/d/archives/digitalmars/D/Binding_rvalues_to_ref_parameters_redux_325087.html
199                             // Implementation: https://github.com/dlang/dmd/pull/9817
200 
201     CppStdRevision cplusplus = CppStdRevision.cpp98;    // version of C++ standard to support
202 
203     bool markdown = true;   // enable Markdown replacements in Ddoc
204     bool vmarkdown;         // list instances of Markdown replacements in Ddoc
205 
206     bool showGaggedErrors;  // print gagged errors anyway
207     bool printErrorContext;  // print errors with the error context (the error line in the source file)
208     bool manual;            // open browser on compiler manual
209     bool usage;             // print usage and exit
210     bool mcpuUsage;         // print help on -mcpu switch
211     bool transitionUsage;   // print help on -transition switch
212     bool checkUsage;        // print help on -check switch
213     bool checkActionUsage;  // print help on -checkaction switch
214     bool revertUsage;       // print help on -revert switch
215     bool previewUsage;      // print help on -preview switch
216     bool externStdUsage;    // print help on -extern-std switch
217     bool hcUsage;           // print help on -HC switch
218     bool logo;              // print compiler logo
219 
220     CPU cpu = CPU.baseline; // CPU instruction set to target
221 
222     CHECKENABLE useInvariants  = CHECKENABLE._default;  // generate class invariant checks
223     CHECKENABLE useIn          = CHECKENABLE._default;  // generate precondition checks
224     CHECKENABLE useOut         = CHECKENABLE._default;  // generate postcondition checks
225     CHECKENABLE useArrayBounds = CHECKENABLE._default;  // when to generate code for array bounds checks
226     CHECKENABLE useAssert      = CHECKENABLE._default;  // when to generate code for assert()'s
227     CHECKENABLE useSwitchError = CHECKENABLE._default;  // check for switches without a default
228     CHECKENABLE boundscheck    = CHECKENABLE._default;  // state of -boundscheck switch
229 
230     CHECKACTION checkAction = CHECKACTION.D; // action to take when bounds, asserts or switch defaults are violated
231 
232     uint errorLimit = 20;
233 
234     const(char)[] argv0;                // program name
235     Array!(const(char)*) modFileAliasStrings; // array of char*'s of -I module filename alias strings
236     Array!(const(char)*)* imppath;      // array of char*'s of where to look for import modules
237     Array!(const(char)*)* fileImppath;  // array of char*'s of where to look for file import modules
238     const(char)[] objdir;                // .obj/.lib file output directory
239     const(char)[] objname;               // .obj file output name
240     const(char)[] libname;               // .lib file output name
241 
242     bool doDocComments;                 // process embedded documentation comments
243     const(char)[] docdir;               // write documentation file to docdir directory
244     const(char)[] docname;              // write documentation file to docname
245     Array!(const(char)*) ddocfiles;     // macro include files for Ddoc
246 
247     bool doHdrGeneration;               // process embedded documentation comments
248     const(char)[] hdrdir;                // write 'header' file to docdir directory
249     const(char)[] hdrname;               // write 'header' file to docname
250     bool hdrStripPlainFunctions = true; // strip the bodies of plain (non-template) functions
251 
252     CxxHeaderMode doCxxHdrGeneration;      /// Generate 'Cxx header' file
253     const(char)[] cxxhdrdir;            // write 'header' file to docdir directory
254     const(char)[] cxxhdrname;           // write 'header' file to docname
255 
256     bool doJsonGeneration;              // write JSON file
257     const(char)[] jsonfilename;          // write JSON file to jsonfilename
258     JsonFieldFlags jsonFieldFlags;      // JSON field flags to include
259 
260     OutBuffer* mixinOut;                // write expanded mixins for debugging
261     const(char)* mixinFile;             // .mixin file output name
262     int mixinLines;                     // Number of lines in writeMixins
263 
264     uint debuglevel;                    // debug level
265     Array!(const(char)*)* debugids;     // debug identifiers
266 
267     uint versionlevel;                  // version level
268     Array!(const(char)*)* versionids;   // version identifiers
269 
270     const(char)[] defaultlibname;        // default library for non-debug builds
271     const(char)[] debuglibname;          // default library for debug builds
272     const(char)[] mscrtlib;              // MS C runtime library
273 
274     const(char)[] moduleDepsFile;        // filename for deps output
275     OutBuffer* moduleDeps;              // contents to be written to deps file
276     MessageStyle messageStyle = MessageStyle.digitalmars; // style of file/line annotations on messages
277 
278     // Hidden debug switches
279     bool debugb;
280     bool debugc;
281     bool debugf;
282     bool debugr;
283     bool debugx;
284     bool debugy;
285 
286     bool run; // run resulting executable
287     Strings runargs; // arguments for executable
288 
289     // Linker stuff
290     Array!(const(char)*) objfiles;
291     Array!(const(char)*) linkswitches;
292     Array!bool linkswitchIsForCC;
293     Array!(const(char)*) libfiles;
294     Array!(const(char)*) dllfiles;
295     const(char)[] deffile;
296     const(char)[] resfile;
297     const(char)[] exefile;
298     const(char)[] mapfile;
299 }
300 
301 alias structalign_t = uint;
302 
303 // magic value means "match whatever the underlying C compiler does"
304 // other values are all powers of 2
305 enum STRUCTALIGN_DEFAULT = (cast(structalign_t)~0);
306 
307 extern (C++) struct Global
308 {
309     const(char)[] inifilename;
310     string mars_ext = "d";
311     const(char)[] obj_ext;
312     const(char)[] lib_ext;
313     const(char)[] dll_ext;
314     string doc_ext = "html";      // for Ddoc generated files
315     string ddoc_ext = "ddoc";     // for Ddoc macro include files
316     string hdr_ext = "di";        // for D 'header' import files
317     string cxxhdr_ext = "h";      // for C/C++ 'header' files
318     string json_ext = "json";     // for JSON files
319     string map_ext = "map";       // for .map files
320     bool run_noext;                     // allow -run sources without extensions.
321 
322     string copyright = "Copyright (C) 1999-2020 by The D Language Foundation, All Rights Reserved";
323     string written = "written by Walter Bright";
324 
325     Array!(const(char)*)* path;         // Array of char*'s which form the import lookup path
326     Array!(const(char)*)* filePath;     // Array of char*'s which form the file import lookup path
327 
328     private enum string _version = import("VERSION");
329     private enum uint _versionNumber = parseVersionNumber(_version);
330 
331     const(char)[] vendor;    // Compiler backend name
332 
333     Param params;
334     uint errors;            // number of errors reported so far
335     uint warnings;          // number of warnings reported so far
336     uint gag;               // !=0 means gag reporting of errors & warnings
337     uint gaggedErrors;      // number of errors reported while gagged
338     uint gaggedWarnings;    // number of warnings reported while gagged
339 
340     void* console;         // opaque pointer to console for controlling text attributes
341 
342     Array!Identifier* versionids;    // command line versions and predefined versions
343     Array!Identifier* debugids;      // command line debug versions and predefined versions
344 
345     enum recursionLimit = 500; // number of recursive template expansions before abort
346 
347   nothrow:
348 
349     /* Start gagging. Return the current number of gagged errors
350      */
351     extern (C++) uint startGagging()
352     {
353         ++gag;
354         gaggedWarnings = 0;
355         return gaggedErrors;
356     }
357 
358     /* End gagging, restoring the old gagged state.
359      * Return true if errors occurred while gagged.
360      */
361     extern (C++) bool endGagging(uint oldGagged)
362     {
363         bool anyErrs = (gaggedErrors != oldGagged);
364         --gag;
365         // Restore the original state of gagged errors; set total errors
366         // to be original errors + new ungagged errors.
367         errors -= (gaggedErrors - oldGagged);
368         gaggedErrors = oldGagged;
369         return anyErrs;
370     }
371 
372     /*  Increment the error count to record that an error
373      *  has occurred in the current context. An error message
374      *  may or may not have been printed.
375      */
376     extern (C++) void increaseErrorCount()
377     {
378         if (gag)
379             ++gaggedErrors;
380         ++errors;
381     }
382 
383     extern (C++) void _init()
384     {
385         version (MARS)
386         {
387             vendor = "Digital Mars D";
388             static if (TARGET.Windows)
389             {
390                 obj_ext = "obj";
391             }
392             else static if (TARGET.Linux || TARGET.OSX || TARGET.FreeBSD || TARGET.OpenBSD || TARGET.Solaris || TARGET.DragonFlyBSD)
393             {
394                 obj_ext = "o";
395             }
396             else
397             {
398                 static assert(0, "fix this");
399             }
400             static if (TARGET.Windows)
401             {
402                 lib_ext = "lib";
403             }
404             else static if (TARGET.Linux || TARGET.OSX || TARGET.FreeBSD || TARGET.OpenBSD || TARGET.Solaris || TARGET.DragonFlyBSD)
405             {
406                 lib_ext = "a";
407             }
408             else
409             {
410                 static assert(0, "fix this");
411             }
412             static if (TARGET.Windows)
413             {
414                 dll_ext = "dll";
415             }
416             else static if (TARGET.Linux || TARGET.FreeBSD || TARGET.OpenBSD || TARGET.Solaris || TARGET.DragonFlyBSD)
417             {
418                 dll_ext = "so";
419             }
420             else static if (TARGET.OSX)
421             {
422                 dll_ext = "dylib";
423             }
424             else
425             {
426                 static assert(0, "fix this");
427             }
428             static if (TARGET.Windows)
429             {
430                 run_noext = false;
431             }
432             else static if (TARGET.Linux || TARGET.OSX || TARGET.FreeBSD || TARGET.OpenBSD || TARGET.Solaris || TARGET.DragonFlyBSD)
433             {
434                 // Allow 'script' D source files to have no extension.
435                 run_noext = true;
436             }
437             else
438             {
439                 static assert(0, "fix this");
440             }
441             static if (TARGET.Windows)
442             {
443                 params.mscoff = params.is64bit;
444             }
445 
446             // -color=auto is the default value
447             import dmd.console : Console;
448             params.color = Console.detectTerminal();
449         }
450         else version (IN_GCC)
451         {
452             vendor = "GNU D";
453             obj_ext = "o";
454             lib_ext = "a";
455             dll_ext = "so";
456             run_noext = true;
457         }
458     }
459 
460     /**
461      * Deinitializes the global state of the compiler.
462      *
463      * This can be used to restore the state set by `_init` to its original
464      * state.
465      */
466     extern (D) void deinitialize()
467     {
468         this = this.init;
469     }
470 
471     /**
472      * Computes the version number __VERSION__ from the compiler version string.
473      */
474     extern (D) private static uint parseVersionNumber(string version_)
475     {
476         //
477         // parse _version
478         //
479         uint major = 0;
480         uint minor = 0;
481         bool point = false;
482         // skip initial 'v'
483         foreach (const c; version_[1..$])
484         {
485             if ('0' <= c && c <= '9') // isdigit
486             {
487                 minor = minor * 10 + c - '0';
488             }
489             else if (c == '.')
490             {
491                 if (point)
492                     break; // ignore everything after second '.'
493                 point = true;
494                 major = minor;
495                 minor = 0;
496             }
497             else
498                 break;
499         }
500         return major * 1000 + minor;
501     }
502 
503     /**
504     Returns: the version as the number that would be returned for __VERSION__
505     */
506     extern(C++) uint versionNumber()
507     {
508         return _versionNumber;
509     }
510 
511     /**
512     Returns: compiler version string.
513     */
514     extern(D) string versionString()
515     {
516         return _version;
517     }
518 
519     /**
520     Returns: compiler version as char string.
521     */
522     extern(C++) const(char*) versionChars()
523     {
524         return _version.ptr;
525     }
526 
527     /**
528     Returns: the final defaultlibname based on the command-line parameters
529     */
530     extern (D) const(char)[] finalDefaultlibname() const
531     {
532         return params.betterC ? null :
533             params.symdebug ? params.debuglibname : params.defaultlibname;
534     }
535 }
536 
537 // Because int64_t and friends may be any integral type of the
538 // correct size, we have to explicitly ask for the correct
539 // integer type to get the correct mangling with dmd
540 
541 // Be careful not to care about sign when using dinteger_t
542 // use this instead of integer_t to
543 // avoid conflicts with system #include's
544 alias dinteger_t = ulong;
545 // Signed and unsigned variants
546 alias sinteger_t = long;
547 alias uinteger_t = ulong;
548 
549 alias d_int8 = int8_t;
550 alias d_uns8 = uint8_t;
551 alias d_int16 = int16_t;
552 alias d_uns16 = uint16_t;
553 alias d_int32 = int32_t;
554 alias d_uns32 = uint32_t;
555 alias d_int64 = int64_t;
556 alias d_uns64 = uint64_t;
557 
558 // file location
559 struct Loc
560 {
561     const(char)* filename; // either absolute or relative to cwd
562     uint linnum;
563     uint charnum;
564 
565     static immutable Loc initial;       /// use for default initialization of const ref Loc's
566 
567 nothrow:
568     extern (D) this(const(char)* filename, uint linnum, uint charnum) pure
569     {
570         this.linnum = linnum;
571         this.charnum = charnum;
572         this.filename = filename;
573     }
574 
575     extern (C++) const(char)* toChars(
576         bool showColumns = global.params.showColumns,
577         ubyte messageStyle = global.params.messageStyle) const pure nothrow
578     {
579         OutBuffer buf;
580         if (filename)
581         {
582             buf.writestring(filename);
583         }
584         if (linnum)
585         {
586             final switch (messageStyle)
587             {
588                 case MessageStyle.digitalmars:
589                     buf.writeByte('(');
590                     buf.print(linnum);
591                     if (showColumns && charnum)
592                     {
593                         buf.writeByte(',');
594                         buf.print(charnum);
595                     }
596                     buf.writeByte(')');
597                     break;
598                 case MessageStyle.gnu: // https://www.gnu.org/prep/standards/html_node/Errors.html
599                     buf.writeByte(':');
600                     buf.print(linnum);
601                     if (showColumns && charnum)
602                     {
603                         buf.writeByte(':');
604                         buf.print(charnum);
605                     }
606                     break;
607             }
608         }
609         return buf.extractChars();
610     }
611 
612     /* Checks for equivalence,
613      * a) comparing the filename contents (not the pointer), case-
614      *    insensitively on Windows, and
615      * b) ignoring charnum if `global.params.showColumns` is false.
616      */
617     extern (C++) bool equals(ref const(Loc) loc) const
618     {
619         return (!global.params.showColumns || charnum == loc.charnum) &&
620                linnum == loc.linnum &&
621                FileName.equals(filename, loc.filename);
622     }
623 
624     /* opEquals() / toHash() for AA key usage:
625      *
626      * Compare filename contents (case-sensitively on Windows too), not
627      * the pointer - a static foreach loop repeatedly mixing in a mixin
628      * may lead to multiple equivalent filenames (`foo.d-mixin-<line>`),
629      * e.g., for test/runnable/test18880.d.
630      */
631     extern (D) bool opEquals(ref const(Loc) loc) const @trusted pure nothrow @nogc
632     {
633         import core.stdc.string : strcmp;
634 
635         return charnum == loc.charnum &&
636                linnum == loc.linnum &&
637                (filename == loc.filename ||
638                 (filename && loc.filename && strcmp(filename, loc.filename) == 0));
639     }
640 
641     extern (D) size_t toHash() const @trusted pure nothrow
642     {
643         import dmd.root.string : toDString;
644 
645         auto hash = hashOf(linnum);
646         hash = hashOf(charnum, hash);
647         hash = hashOf(filename.toDString, hash);
648         return hash;
649     }
650 
651     /******************
652      * Returns:
653      *   true if Loc has been set to other than the default initialization
654      */
655     bool isValid() const pure
656     {
657         return filename !is null;
658     }
659 }
660 
661 enum LINK : int
662 {
663     default_,
664     d,
665     c,
666     cpp,
667     windows,
668     pascal,
669     objc,
670     system,
671 }
672 
673 enum CPPMANGLE : int
674 {
675     def,
676     asStruct,
677     asClass,
678 }
679 
680 enum MATCH : int
681 {
682     nomatch,   // no match
683     convert,   // match with conversions
684     constant,  // match with conversion to const
685     exact,     // exact match
686 }
687 
688 enum PINLINE : int
689 {
690     default_,     // as specified on the command line
691     never,   // never inline
692     always,  // always inline
693 }
694 
695 alias StorageClass = uinteger_t;
696 
697 extern (C++) __gshared Global global;