1 /**
2  * Stores command line options and contains other miscellaneous declarations.
3  *
4  * Copyright:   Copyright (C) 1999-2023 by The D Language Foundation, All Rights Reserved
5  * Authors:     $(LINK2 https://www.digitalmars.com, Walter Bright)
6  * License:     $(LINK2 https://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.stdio;
15 import core.stdc.stdint;
16 import core.stdc.string;
17 
18 import dmd.root.array;
19 import dmd.root.filename;
20 import dmd.common.outbuffer;
21 import dmd.errorsink;
22 import dmd.errors;
23 import dmd.file_manager;
24 import dmd.identifier;
25 import dmd.location;
26 import dmd.lexer : CompileEnv;
27 import dmd.utils;
28 
29 version (IN_GCC) {}
30 else version (IN_LLVM) {}
31 else version = MARS;
32 
33 /// Defines a setting for how compiler warnings and deprecations are handled
34 enum DiagnosticReporting : ubyte
35 {
36     error,        /// generate an error
37     inform,       /// generate a warning
38     off,          /// disable diagnostic
39 }
40 
41 /// In which context checks for assertions, contracts, bounds checks etc. are enabled
42 enum CHECKENABLE : ubyte
43 {
44     _default,     /// initial value
45     off,          /// never do checking
46     on,           /// always do checking
47     safeonly,     /// do checking only in @safe functions
48 }
49 
50 /// What should happend when an assertion fails
51 enum CHECKACTION : ubyte
52 {
53     D,            /// call D assert on failure
54     C,            /// call C assert on failure
55     halt,         /// cause program halt on failure
56     context,      /// call D assert with the error context on failure
57 }
58 
59 /**
60 Each flag represents a field that can be included in the JSON output.
61 
62 NOTE: set type to uint so its size matches C++ unsigned type
63 */
64 enum JsonFieldFlags : uint
65 {
66     none         = 0,
67     compilerInfo = (1 << 0),
68     buildInfo    = (1 << 1),
69     modules      = (1 << 2),
70     semantics    = (1 << 3),
71 }
72 
73 /// Version of C++ standard to support
74 enum CppStdRevision : uint
75 {
76     cpp98 = 1997_11,
77     cpp11 = 2011_03,
78     cpp14 = 2014_02,
79     cpp17 = 2017_03,
80     cpp20 = 2020_02,
81 }
82 
83 /// Trivalent boolean to represent the state of a `revert`able change
84 enum FeatureState : ubyte
85 {
86     default_ = 0,  /// Not specified by the user
87     disabled = 1,  /// Specified as `-revert=`
88     enabled  = 2,  /// Specified as `-preview=`
89 }
90 
91 extern(C++) struct Output
92 {
93     bool doOutput;      // Output is enabled
94     bool fullOutput;    // Generate comments for hidden declarations (for -HC),
95                         // and don't strip the bodies of plain (non-template) functions (for -H)
96 
97     const(char)[] dir;  // write to directory 'dir'
98     const(char)[] name; // write to file 'name'
99     Array!(const(char)*) files; // Other files associated with this output,
100                                 // e.g. macro include files for Ddoc, dependencies for makedeps
101     OutBuffer* buffer;  // if this output is buffered, this is the buffer
102     int bufferLines;    // number of lines written to the buffer
103 }
104 
105 /// Command line state related to printing usage about other switches
106 extern(C++) struct Help
107 {
108     bool manual;       // open browser on compiler manual
109     bool usage;        // print usage and exit
110     // print help of switch:
111     bool mcpu;         // -mcpu
112     bool transition;   // -transition
113     bool check;        // -check
114     bool checkAction;  // -checkaction
115     bool revert;       // -revert
116     bool preview;      // -preview
117     bool externStd;    // -extern-std
118     bool hc;           // -HC
119 }
120 
121 extern(C++) struct Verbose
122 {
123     bool verbose;           // verbose compile
124     bool showColumns;       // print character (column) numbers in diagnostics
125     bool tls;               // identify thread local variables
126     bool templates;         // collect and list statistics on template instantiations
127     // collect and list statistics on template instantiations origins.
128     // TODO: make this an enum when we want to list other kinds of instances
129     bool templatesListInstances;
130     bool gc;                // identify gc usage
131     bool field;             // identify non-mutable field variables
132     bool complex = true;    // identify complex/imaginary type usage
133     bool vin;               // identify 'in' parameters
134     bool showGaggedErrors;  // print gagged errors anyway
135     bool printErrorContext; // print errors with the error context (the error line in the source file)
136     bool logo;              // print compiler logo
137     bool color;             // use ANSI colors in console output
138     bool cov;               // generate code coverage data
139     MessageStyle messageStyle = MessageStyle.digitalmars; // style of file/line annotations on messages
140     uint errorLimit = 20;
141     uint errorSupplementLimit = 6;      // Limit the number of supplemental messages for each error (0 means unlimited)
142 
143     uint errorSupplementCount()
144     {
145         if (verbose)
146             return uint.max;
147         if (errorSupplementLimit == 0)
148             return uint.max;
149         return errorSupplementLimit;
150     }
151 }
152 
153 /// Put command line switches in here
154 extern (C++) struct Param
155 {
156     bool obj = true;        // write object file
157     bool multiobj;          // break one object file into multiple ones
158     bool trace;             // insert profiling hooks
159     bool tracegc;           // instrument calls to 'new'
160     bool vcg_ast;           // write-out codegen-ast
161     DiagnosticReporting useDeprecated = DiagnosticReporting.inform;  // how use of deprecated features are handled
162     bool useUnitTests;          // generate unittest code
163     bool useInline = false;     // inline expand functions
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     bool cov;               // generate code coverage data
168     ubyte covPercent;       // 0..100 code coverage percentage required
169     bool ctfe_cov = false;  // generate coverage data for ctfe
170     bool ignoreUnsupportedPragmas;  // rather than error on them
171     bool useModuleInfo = true;   // generate runtime module information
172     bool useTypeInfo = true;     // generate runtime type information
173     bool useExceptions = true;   // support exception handling
174     bool useGC = true;           // support features that require the D runtime GC
175     bool betterC;           // be a "better C" compiler; no dependency on D runtime
176     bool addMain;           // add a default main() function
177     bool allInst;           // generate code for all template instantiations
178     bool bitfields;         // support C style bit fields
179 
180     CppStdRevision cplusplus = CppStdRevision.cpp11;    // version of C++ standard to support
181 
182     Help help;
183     Verbose v;
184 
185     // Options for `-preview=/-revert=`
186     FeatureState useDIP25 = FeatureState.enabled; // implement https://wiki.dlang.org/DIP25
187     FeatureState useDIP1000;     // implement https://dlang.org/spec/memory-safe-d.html#scope-return-params
188     bool ehnogc;                 // use @nogc exception handling
189     bool useDIP1021;             // implement https://github.com/dlang/DIPs/blob/master/DIPs/accepted/DIP1021.md
190     FeatureState fieldwise;      // do struct equality testing field-wise rather than by memcmp()
191     bool fixAliasThis;           // if the current scope has an alias this, check it before searching upper scopes
192     FeatureState rvalueRefParam; // allow rvalues to be arguments to ref parameters
193                                  // https://dconf.org/2019/talks/alexandrescu.html
194                                  // https://gist.github.com/andralex/e5405a5d773f07f73196c05f8339435a
195                                  // https://digitalmars.com/d/archives/digitalmars/D/Binding_rvalues_to_ref_parameters_redux_325087.html
196                                  // Implementation: https://github.com/dlang/dmd/pull/9817
197     FeatureState noSharedAccess; // read/write access to shared memory objects
198     bool previewIn;              // `in` means `[ref] scope const`, accepts rvalues
199     bool inclusiveInContracts;   // 'in' contracts of overridden methods must be a superset of parent contract
200     bool shortenedMethods = true;       // allow => in normal function declarations
201     bool fixImmutableConv;       // error on unsound immutable conversion - https://github.com/dlang/dmd/pull/14070
202     bool fix16997 = true;        // fix integral promotions for unary + - ~ operators
203                                  // https://issues.dlang.org/show_bug.cgi?id=16997
204     FeatureState dtorFields;     // destruct fields of partially constructed objects
205                                  // https://issues.dlang.org/show_bug.cgi?id=14246
206     FeatureState systemVariables; // limit access to variables marked @system from @safe code
207 
208     CHECKENABLE useInvariants  = CHECKENABLE._default;  // generate class invariant checks
209     CHECKENABLE useIn          = CHECKENABLE._default;  // generate precondition checks
210     CHECKENABLE useOut         = CHECKENABLE._default;  // generate postcondition checks
211     CHECKENABLE useArrayBounds = CHECKENABLE._default;  // when to generate code for array bounds checks
212     CHECKENABLE useAssert      = CHECKENABLE._default;  // when to generate code for assert()'s
213     CHECKENABLE useSwitchError = CHECKENABLE._default;  // check for switches without a default
214     CHECKENABLE boundscheck    = CHECKENABLE._default;  // state of -boundscheck switch
215 
216     CHECKACTION checkAction = CHECKACTION.D; // action to take when bounds, asserts or switch defaults are violated
217 
218     const(char)[] argv0;                // program name
219     Array!(const(char)*) modFileAliasStrings; // array of char*'s of -I module filename alias strings
220     Array!(const(char)*)* imppath;      // array of char*'s of where to look for import modules
221     Array!(const(char)*)* fileImppath;  // array of char*'s of where to look for file import modules
222     const(char)[] objdir;                // .obj/.lib file output directory
223     const(char)[] objname;               // .obj file output name
224     const(char)[] libname;               // .lib file output name
225 
226     Output ddoc;                        // Generate embedded documentation comments
227     Output dihdr;                       // Generate `.di` 'header' files
228     Output cxxhdr;                      // Generate 'Cxx header' file
229     Output json;                        // Generate JSON file
230     JsonFieldFlags jsonFieldFlags;      // JSON field flags to include
231     Output makeDeps;                    // Generate make file dependencies
232     Output mixinOut;                    // write expanded mixins for debugging
233     Output moduleDeps;                  // Generate `.deps` module dependencies
234 
235     uint debuglevel;                    // debug level
236     uint versionlevel;                  // version level
237 
238     bool run; // run resulting executable
239     Strings runargs; // arguments for executable
240     Array!(const(char)*) cppswitches;   // C preprocessor switches
241     const(char)* cpp;                   // if not null, then this specifies the C preprocessor
242 
243     // Linker stuff
244     Array!(const(char)*) objfiles;
245     Array!(const(char)*) linkswitches;
246     Array!bool linkswitchIsForCC;
247     Array!(const(char)*) libfiles;
248     Array!(const(char)*) dllfiles;
249     const(char)[] deffile;
250     const(char)[] resfile;
251     const(char)[] exefile;
252     const(char)[] mapfile;
253 }
254 
255 enum mars_ext = "d";        // for D source files
256 enum doc_ext  = "html";     // for Ddoc generated files
257 enum ddoc_ext = "ddoc";     // for Ddoc macro include files
258 enum dd_ext   = "dd";       // for Ddoc source files
259 enum hdr_ext  = "di";       // for D 'header' import files
260 enum json_ext = "json";     // for JSON files
261 enum map_ext  = "map";      // for .map files
262 enum c_ext    = "c";        // for C source files
263 enum i_ext    = "i";        // for preprocessed C source file
264 
265 /**
266  * Collection of global compiler settings and global state used by the frontend
267  */
268 extern (C++) struct Global
269 {
270     const(char)[] inifilename; /// filename of configuration file as given by `-conf=`, or default value
271 
272     string copyright = "Copyright (C) 1999-2023 by The D Language Foundation, All Rights Reserved";
273     string written = "written by Walter Bright";
274 
275     Array!(const(char)*)* path;         /// Array of char*'s which form the import lookup path
276     Array!(const(char)*)* filePath;     /// Array of char*'s which form the file import lookup path
277 
278     private enum string _version = import("VERSION");
279     char[26] datetime;      /// string returned by ctime()
280     CompileEnv compileEnv;
281 
282     Param params;           /// command line parameters
283     uint errors;            /// number of errors reported so far
284     uint warnings;          /// number of warnings reported so far
285     uint gag;               /// !=0 means gag reporting of errors & warnings
286     uint gaggedErrors;      /// number of errors reported while gagged
287     uint gaggedWarnings;    /// number of warnings reported while gagged
288 
289     void* console;         /// opaque pointer to console for controlling text attributes
290 
291     Array!Identifier* versionids; /// command line versions and predefined versions
292     Array!Identifier* debugids;   /// command line debug versions and predefined versions
293 
294     bool hasMainFunction; /// Whether a main function has already been compiled in (for -main switch)
295     uint varSequenceNumber = 1; /// Relative lifetime of `VarDeclaration` within a function, used for `scope` checks
296 
297     /// Cache files read from disk
298     FileManager fileManager;
299 
300     enum recursionLimit = 500; /// number of recursive template expansions before abort
301 
302     ErrorSink errorSink;       /// where the error messages go
303     ErrorSink errorSinkNull;   /// where the error messages are ignored
304 
305     extern (C++) FileName function(FileName, ref const Loc, out bool, OutBuffer*) preprocess;
306 
307   nothrow:
308 
309     /**
310      * Start ignoring compile errors instead of reporting them.
311      *
312      * Used for speculative compilation like `__traits(compiles, XXX)`, but also internally
313      * to e.g. try out an `alias this` rewrite without comitting to it.
314      *
315      * Works like a stack, so N calls to `startGagging` should be paired with N
316      * calls to `endGagging`.
317      *
318      * Returns: the current number of gagged errors, which should later be passed to `endGagging`
319      */
320     extern (C++) uint startGagging() @safe
321     {
322         ++gag;
323         gaggedWarnings = 0;
324         return gaggedErrors;
325     }
326 
327     /**
328      * Stop gagging, restoring the old gagged state before the most recent call to `startGagging`.
329      *
330      * Params:
331      *   oldGagged = the previous number of errors, as returned by `startGagging`
332      * Returns: true if errors occurred while gagged.
333      */
334     extern (C++) bool endGagging(uint oldGagged) @safe
335     {
336         bool anyErrs = (gaggedErrors != oldGagged);
337         --gag;
338         // Restore the original state of gagged errors; set total errors
339         // to be original errors + new ungagged errors.
340         errors -= (gaggedErrors - oldGagged);
341         gaggedErrors = oldGagged;
342         return anyErrs;
343     }
344 
345     /**
346      * Increment the error count to record that an error has occurred in the current context.
347      *
348      * An error message may or may not have been printed.
349      */
350     extern (C++) void increaseErrorCount() @safe
351     {
352         if (gag)
353             ++gaggedErrors;
354         ++errors;
355     }
356 
357     extern (C++) void _init()
358     {
359         errorSink = new ErrorSinkCompiler;
360         errorSinkNull = new ErrorSinkNull;
361 
362         this.fileManager = new FileManager();
363         version (MARS)
364         {
365             compileEnv.vendor = "Digital Mars D";
366 
367             // -color=auto is the default value
368             import dmd.console : detectTerminal, detectColorPreference;
369             params.v.color = detectTerminal() && detectColorPreference();
370         }
371         else version (IN_GCC)
372         {
373             compileEnv.vendor = "GNU D";
374         }
375         compileEnv.versionNumber = parseVersionNumber(_version);
376 
377         /* Initialize date, time, and timestamp
378          */
379         import core.stdc.time;
380         import core.stdc.stdlib : getenv;
381 
382         time_t ct;
383         // https://issues.dlang.org/show_bug.cgi?id=20444
384         if (auto p = getenv("SOURCE_DATE_EPOCH"))
385         {
386             if (!ct.parseDigits(p[0 .. strlen(p)]))
387                 errorSink.error(Loc.initial, "value of environment variable `SOURCE_DATE_EPOCH` should be a valid UNIX timestamp, not: `%s`", p);
388         }
389         else
390             core.stdc.time.time(&ct);
391         const p = ctime(&ct);
392         assert(p);
393         datetime[] = p[0 .. 26];
394 
395         __gshared char[11 + 1] date = 0;        // put in BSS segment
396         __gshared char[8  + 1] time = 0;
397         __gshared char[24 + 1] timestamp = 0;
398 
399         const dsz = snprintf(&date[0], date.length, "%.6s %.4s", p + 4, p + 20);
400         const tsz = snprintf(&time[0], time.length, "%.8s", p + 11);
401         const tssz = snprintf(&timestamp[0], timestamp.length, "%.24s", p);
402         assert(dsz > 0 && tsz > 0 && tssz > 0);
403         compileEnv.time = time[0 .. tsz];
404         compileEnv.date = date[0 .. dsz];
405         compileEnv.timestamp = timestamp[0 .. tssz];
406     }
407 
408     /**
409      * Deinitializes the global state of the compiler.
410      *
411      * This can be used to restore the state set by `_init` to its original
412      * state.
413      */
414     extern (D) void deinitialize()
415     {
416         this = this.init;
417     }
418 
419     /**
420      * Computes the version number __VERSION__ from the compiler version string.
421      */
422     extern (D) private static uint parseVersionNumber(string version_) @safe
423     {
424         //
425         // parse _version
426         //
427         uint major = 0;
428         uint minor = 0;
429         bool point = false;
430         // skip initial 'v'
431         foreach (const c; version_[1..$])
432         {
433             if ('0' <= c && c <= '9') // isdigit
434             {
435                 minor = minor * 10 + c - '0';
436             }
437             else if (c == '.')
438             {
439                 if (point)
440                     break; // ignore everything after second '.'
441                 point = true;
442                 major = minor;
443                 minor = 0;
444             }
445             else
446                 break;
447         }
448         return major * 1000 + minor;
449     }
450 
451     /**
452     Returns: the version as the number that would be returned for __VERSION__
453     */
454     extern(C++) uint versionNumber() @safe
455     {
456         return compileEnv.versionNumber;
457     }
458 
459     /**
460     Returns: compiler version string.
461     */
462     extern(D) string versionString() @safe
463     {
464         return _version;
465     }
466 
467     /**
468     Returns: compiler version as char string.
469     */
470     extern(C++) const(char*) versionChars()
471     {
472         return _version.ptr;
473     }
474 }
475 
476 // Because int64_t and friends may be any integral type of the
477 // correct size, we have to explicitly ask for the correct
478 // integer type to get the correct mangling with dmd
479 
480 // Be careful not to care about sign when using dinteger_t
481 // use this instead of integer_t to
482 // avoid conflicts with system #include's
483 alias dinteger_t = ulong;
484 // Signed and unsigned variants
485 alias sinteger_t = long;
486 alias uinteger_t = ulong;
487 
488 /// Collection of global state
489 extern (C++) __gshared Global global;