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