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 }