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