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