1 /** 2 * Describes a back-end compiler and implements compiler-specific actions. 3 * 4 * Copyright: Copyright (C) 1999-2021 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 version (DMDLIB) 34 { 35 version = CallbackAPI; 36 } 37 38 extern (C++) __gshared 39 { 40 /// Module in which the D main is 41 Module rootHasMain = null; 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 d_int32 int32value; 66 d_int64 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(d_int32) e.toInteger(); 79 break; 80 case Tint64: 81 case Tuns64: 82 u.int64value = cast(d_int64) 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) 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.dim) 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.dim, "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.dim) 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.dim == 0) 307 { 308 foreach (modulePattern; includeModulePatterns) 309 { 310 auto depth = parseModulePatternDepth(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 private ushort parseModulePatternDepth(const(char)* modulePattern) 348 { 349 if (modulePattern[0] == '-') 350 modulePattern++; 351 352 // handle special case 353 if (modulePattern[0] == '.' && modulePattern[1] == '\0') 354 return 0; 355 356 ushort depth = 1; 357 for (;; modulePattern++) 358 { 359 auto c = *modulePattern; 360 if (c == '.') 361 depth++; 362 if (c == '\0') 363 return depth; 364 } 365 } 366 unittest 367 { 368 assert(".".parseModulePatternDepth == 0); 369 assert("-.".parseModulePatternDepth == 0); 370 assert("abc".parseModulePatternDepth == 1); 371 assert("-abc".parseModulePatternDepth == 1); 372 assert("abc.foo".parseModulePatternDepth == 2); 373 assert("-abc.foo".parseModulePatternDepth == 2); 374 } 375 376 /* 377 * Parses a 'module pattern', which is the "include import" components 378 * given on the command line, i.e. "-i=<module_pattern>,<module_pattern>,...". 379 * Params: 380 * modulePattern = The module pattern to parse. 381 * dst = the data structure to save the parsed module pattern to. 382 * depth = the depth of the module pattern previously retrieved from $(D parseModulePatternDepth). 383 */ 384 private void parseModulePattern(const(char)* modulePattern, MatcherNode* dst, ushort depth) 385 { 386 bool isExclude = false; 387 if (modulePattern[0] == '-') 388 { 389 isExclude = true; 390 modulePattern++; 391 } 392 393 *dst = MatcherNode(isExclude, depth); 394 dst++; 395 396 // Create and add identifiers for each component in the modulePattern 397 if (depth > 0) 398 { 399 auto idStart = modulePattern; 400 auto lastNode = dst + depth - 1; 401 for (; dst < lastNode; dst++) 402 { 403 for (;; modulePattern++) 404 { 405 if (*modulePattern == '.') 406 { 407 assert(modulePattern > idStart, "empty module pattern"); 408 *dst = MatcherNode(Identifier.idPool(idStart, cast(uint)(modulePattern - idStart))); 409 modulePattern++; 410 idStart = modulePattern; 411 break; 412 } 413 } 414 } 415 for (;; modulePattern++) 416 { 417 if (*modulePattern == '\0') 418 { 419 assert(modulePattern > idStart, "empty module pattern"); 420 *lastNode = MatcherNode(Identifier.idPool(idStart, cast(uint)(modulePattern - idStart))); 421 break; 422 } 423 } 424 } 425 }