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