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