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 }