1 /**
2  * Describes a back-end compiler and implements compiler-specific actions.
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/compiler.d, _compiler.d)
8  * Documentation:  https://dlang.org/phobos/dmd_compiler.html
9  * Coverage:    https://codecov.io/gh/dlang/dmd/src/master/src/dmd/compiler.d
10  */
11 
12 module dmd.compiler;
13 
14 import dmd.astcodegen;
15 import dmd.arraytypes;
16 import dmd.dmodule;
17 import dmd.dscope;
18 import dmd.dsymbolsem;
19 import dmd.errors;
20 import dmd.expression;
21 import dmd.globals;
22 import dmd.id;
23 import dmd.identifier;
24 import dmd.mtype;
25 import dmd.parse;
26 import dmd.root.array;
27 import dmd.root.ctfloat;
28 import dmd.semantic2;
29 import dmd.semantic3;
30 import dmd.tokens;
31 import dmd.statement;
32 
33 extern (C++) __gshared
34 {
35     /// Module in which the D main is
36     Module rootHasMain = null;
37 
38     bool includeImports = false;
39     // array of module patterns used to include/exclude imported modules
40     Array!(const(char)*) includeModulePatterns;
41     Modules compiledImports;
42 }
43 
44 
45 /**
46  * A data structure that describes a back-end compiler and implements
47  * compiler-specific actions.
48  */
49 extern (C++) struct Compiler
50 {
51     /******************************
52      * Encode the given expression, which is assumed to be an rvalue literal
53      * as another type for use in CTFE.
54      * This corresponds roughly to the idiom *(Type *)&e.
55      */
56     extern (C++) static Expression paintAsType(UnionExp* pue, Expression e, Type type)
57     {
58         union U
59         {
60             d_int32 int32value;
61             d_int64 int64value;
62             float float32value;
63             double float64value;
64         }
65         U u = void;
66 
67         assert(e.type.size() == type.size());
68 
69         switch (e.type.ty)
70         {
71         case Tint32:
72         case Tuns32:
73             u.int32value = cast(d_int32) e.toInteger();
74             break;
75         case Tint64:
76         case Tuns64:
77             u.int64value = cast(d_int64) e.toInteger();
78             break;
79         case Tfloat32:
80             u.float32value = cast(float) e.toReal();
81             break;
82         case Tfloat64:
83             u.float64value = cast(double) e.toReal();
84             break;
85         case Tfloat80:
86             assert(e.type.size() == 8); // 64-bit target `real`
87             goto case Tfloat64;
88         default:
89             assert(0, "Unsupported source type");
90         }
91 
92         real_t r = void;
93         switch (type.ty)
94         {
95         case Tint32:
96         case Tuns32:
97             emplaceExp!(IntegerExp)(pue, e.loc, u.int32value, type);
98             break;
99 
100         case Tint64:
101         case Tuns64:
102             emplaceExp!(IntegerExp)(pue, e.loc, u.int64value, type);
103             break;
104 
105         case Tfloat32:
106             r = u.float32value;
107             emplaceExp!(RealExp)(pue, e.loc, r, type);
108             break;
109 
110         case Tfloat64:
111             r = u.float64value;
112             emplaceExp!(RealExp)(pue, e.loc, r, type);
113             break;
114 
115         case Tfloat80:
116             assert(type.size() == 8); // 64-bit target `real`
117             goto case Tfloat64;
118 
119         default:
120             assert(0, "Unsupported target type");
121         }
122         return pue.exp();
123     }
124 
125     /******************************
126      * For the given module, perform any post parsing analysis.
127      * Certain compiler backends (ie: GDC) have special placeholder
128      * modules whose source are empty, but code gets injected
129      * immediately after loading.
130      */
131     extern (C++) static void onParseModule(Module m)
132     {
133     }
134 
135     /**
136      * A callback function that is called once an imported module is
137      * parsed. If the callback returns true, then it tells the
138      * frontend that the driver intends on compiling the import.
139      */
140     extern(C++) static bool onImport(Module m)
141     {
142         if (includeImports)
143         {
144             Identifiers empty;
145             if (includeImportedModuleCheck(ModuleComponentRange(
146                 (m.md && m.md.packages) ? m.md.packages : &empty, m.ident, m.isPackageFile)))
147             {
148                 if (global.params.verbose)
149                     message("compileimport (%s)", m.srcfile.toChars);
150                 compiledImports.push(m);
151                 return true; // this import will be compiled
152             }
153         }
154         return false; // this import will not be compiled
155     }
156 
157     version (CallbackAPI)
158     {
159         alias OnStatementSemanticStart = void function(Statement, Scope*);
160         alias OnStatementSemanticDone = void function(Statement, Scope*);
161 
162         /**
163          * Used to insert functionality before the start of the
164          * semantic analysis of a statement when importing DMD as a library
165          */
166         __gshared OnStatementSemanticStart onStatementSemanticStart
167                     = function void(Statement s, Scope *sc) {};
168 
169         /**
170          * Used to insert functionality after the end of the
171          * semantic analysis of a statement when importing DMD as a library
172          */
173         __gshared OnStatementSemanticDone onStatementSemanticDone
174                     = function void(Statement s, Scope *sc) {};
175     }
176 }
177 
178 /******************************
179  * Private helpers for Compiler::onImport.
180  */
181 // A range of component identifiers for a module
182 private struct ModuleComponentRange
183 {
184     Identifiers* packages;
185     Identifier name;
186     bool isPackageFile;
187     size_t index;
188     @property auto totalLength() const { return packages.dim + 1 + (isPackageFile ? 1 : 0); }
189 
190     @property auto empty() { return index >= totalLength(); }
191     @property auto front() const
192     {
193         if (index < packages.dim)
194             return (*packages)[index];
195         if (index == packages.dim)
196             return name;
197         else
198             return Identifier.idPool("package");
199     }
200     void popFront() { index++; }
201 }
202 
203 /*
204  * Determines if the given module should be included in the compilation.
205  * Returns:
206  *  True if the given module should be included in the compilation.
207  */
208 private bool includeImportedModuleCheck(ModuleComponentRange components)
209     in { assert(includeImports); }
210 do
211 {
212     createMatchNodes();
213     size_t nodeIndex = 0;
214     while (nodeIndex < matchNodes.dim)
215     {
216         //printf("matcher ");printMatcher(nodeIndex);printf("\n");
217         auto info = matchNodes[nodeIndex++];
218         if (info.depth <= components.totalLength())
219         {
220             size_t nodeOffset = 0;
221             for (auto range = components;;range.popFront())
222             {
223                 if (range.empty || nodeOffset >= info.depth)
224                 {
225                     // MATCH
226                     return !info.isExclude;
227                 }
228                 if (!(range.front is matchNodes[nodeIndex + nodeOffset].id))
229                 {
230                     break;
231                 }
232                 nodeOffset++;
233             }
234         }
235         nodeIndex += info.depth;
236     }
237     assert(nodeIndex == matchNodes.dim, "code bug");
238     return includeByDefault;
239 }
240 
241 // Matching module names is done with an array of matcher nodes.
242 // The nodes are sorted by "component depth" from largest to smallest
243 // so that the first match is always the longest (best) match.
244 private struct MatcherNode
245 {
246     union
247     {
248         struct
249         {
250             ushort depth;
251             bool isExclude;
252         }
253         Identifier id;
254     }
255     this(Identifier id) { this.id = id; }
256     this(bool isExclude, ushort depth)
257     {
258         this.depth = depth;
259         this.isExclude = isExclude;
260     }
261 }
262 
263 /*
264  * $(D includeByDefault) determines whether to include/exclude modules when they don't
265  * match any pattern. This setting changes depending on if the user provided any "inclusive" module
266  * patterns. When a single "inclusive" module pattern is given, it likely means the user only
267  * intends to include modules they've "included", however, if no module patterns are given or they
268  * are all "exclusive", then it is likely they intend to include everything except modules
269  * that have been excluded. i.e.
270  * ---
271  * -i=-foo // include everything except modules that match "foo*"
272  * -i=foo  // only include modules that match "foo*" (exclude everything else)
273  * ---
274  * Note that this default behavior can be overriden using the '.' module pattern. i.e.
275  * ---
276  * -i=-foo,-.  // this excludes everything
277  * -i=foo,.    // this includes everything except the default exclusions (-std,-core,-etc.-object)
278  * ---
279 */
280 private __gshared bool includeByDefault = true;
281 private __gshared Array!MatcherNode matchNodes;
282 
283 /*
284  * Creates the global list of match nodes used to match module names
285  * given strings provided by the -i commmand line option.
286  */
287 private void createMatchNodes()
288 {
289     static size_t findSortedIndexToAddForDepth(size_t depth)
290     {
291         size_t index = 0;
292         while (index < matchNodes.dim)
293         {
294             auto info = matchNodes[index];
295             if (depth > info.depth)
296                 break;
297             index += 1 + info.depth;
298         }
299         return index;
300     }
301 
302     if (matchNodes.dim == 0)
303     {
304         foreach (modulePattern; includeModulePatterns)
305         {
306             auto depth = parseModulePatternDepth(modulePattern);
307             auto entryIndex = findSortedIndexToAddForDepth(depth);
308             matchNodes.split(entryIndex, depth + 1);
309             parseModulePattern(modulePattern, &matchNodes[entryIndex], depth);
310             // if at least 1 "include pattern" is given, then it is assumed the
311             // user only wants to include modules that were explicitly given, which
312             // changes the default behavior from inclusion to exclusion.
313             if (includeByDefault && !matchNodes[entryIndex].isExclude)
314             {
315                 //printf("Matcher: found 'include pattern', switching default behavior to exclusion\n");
316                 includeByDefault = false;
317             }
318         }
319 
320         // Add the default 1 depth matchers
321         MatcherNode[8] defaultDepth1MatchNodes = [
322             MatcherNode(true, 1), MatcherNode(Id.std),
323             MatcherNode(true, 1), MatcherNode(Id.core),
324             MatcherNode(true, 1), MatcherNode(Id.etc),
325             MatcherNode(true, 1), MatcherNode(Id.object),
326         ];
327         {
328             auto index = findSortedIndexToAddForDepth(1);
329             matchNodes.split(index, defaultDepth1MatchNodes.length);
330             auto slice = matchNodes[];
331             slice[index .. index + defaultDepth1MatchNodes.length] = defaultDepth1MatchNodes[];
332         }
333     }
334 }
335 
336 /*
337  * Determines the depth of the given module pattern.
338  * Params:
339  *  modulePattern = The module pattern to determine the depth of.
340  * Returns:
341  *  The component depth of the given module pattern.
342  */
343 private ushort parseModulePatternDepth(const(char)* modulePattern)
344 {
345     if (modulePattern[0] == '-')
346         modulePattern++;
347 
348     // handle special case
349     if (modulePattern[0] == '.' && modulePattern[1] == '\0')
350         return 0;
351 
352     ushort depth = 1;
353     for (;; modulePattern++)
354     {
355         auto c = *modulePattern;
356         if (c == '.')
357             depth++;
358         if (c == '\0')
359             return depth;
360     }
361 }
362 unittest
363 {
364     assert(".".parseModulePatternDepth == 0);
365     assert("-.".parseModulePatternDepth == 0);
366     assert("abc".parseModulePatternDepth == 1);
367     assert("-abc".parseModulePatternDepth == 1);
368     assert("abc.foo".parseModulePatternDepth == 2);
369     assert("-abc.foo".parseModulePatternDepth == 2);
370 }
371 
372 /*
373  * Parses a 'module pattern', which is the "include import" components
374  * given on the command line, i.e. "-i=<module_pattern>,<module_pattern>,...".
375  * Params:
376  *  modulePattern = The module pattern to parse.
377  *  dst = the data structure to save the parsed module pattern to.
378  *  depth = the depth of the module pattern previously retrieved from $(D parseModulePatternDepth).
379  */
380 private void parseModulePattern(const(char)* modulePattern, MatcherNode* dst, ushort depth)
381 {
382     bool isExclude = false;
383     if (modulePattern[0] == '-')
384     {
385         isExclude = true;
386         modulePattern++;
387     }
388 
389     *dst = MatcherNode(isExclude, depth);
390     dst++;
391 
392     // Create and add identifiers for each component in the modulePattern
393     if (depth > 0)
394     {
395         auto idStart = modulePattern;
396         auto lastNode = dst + depth - 1;
397         for (; dst < lastNode; dst++)
398         {
399             for (;; modulePattern++)
400             {
401                 if (*modulePattern == '.')
402                 {
403                     assert(modulePattern > idStart, "empty module pattern");
404                     *dst = MatcherNode(Identifier.idPool(idStart, cast(uint)(modulePattern - idStart)));
405                     modulePattern++;
406                     idStart = modulePattern;
407                     break;
408                 }
409             }
410         }
411         for (;; modulePattern++)
412         {
413             if (*modulePattern == '\0')
414             {
415                 assert(modulePattern > idStart, "empty module pattern");
416                 *lastNode = MatcherNode(Identifier.idPool(idStart, cast(uint)(modulePattern - idStart)));
417                 break;
418             }
419         }
420     }
421 }