1 /** 2 * Compiler implementation of the 3 * $(LINK2 http://www.dlang.org, D programming language). 4 * 5 * Copyright: Copyright (c) 1999-2016 by Digital Mars, All Rights Reserved 6 * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright) 7 * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) 8 * Source: $(DMDSRC _cond.d) 9 */ 10 11 module ddmd.cond; 12 13 import core.stdc..string; 14 import ddmd.arraytypes; 15 import ddmd.dmodule; 16 import ddmd.dscope; 17 import ddmd.dsymbol; 18 import ddmd.errors; 19 import ddmd.expression; 20 import ddmd.globals; 21 import ddmd.identifier; 22 import ddmd.mtype; 23 import ddmd.root.outbuffer; 24 import ddmd.root.rootobject; 25 import ddmd.tokens; 26 import ddmd.utils; 27 import ddmd.visitor; 28 import ddmd.id; 29 30 31 /*********************************************************** 32 */ 33 extern (C++) abstract class Condition : RootObject 34 { 35 Loc loc; 36 // 0: not computed yet 37 // 1: include 38 // 2: do not include 39 int inc; 40 41 final extern (D) this(Loc loc) 42 { 43 this.loc = loc; 44 } 45 46 abstract Condition syntaxCopy(); 47 48 abstract int include(Scope* sc, ScopeDsymbol sds); 49 50 DebugCondition isDebugCondition() 51 { 52 return null; 53 } 54 55 void accept(Visitor v) 56 { 57 v.visit(this); 58 } 59 } 60 61 /*********************************************************** 62 */ 63 extern (C++) class DVCondition : Condition 64 { 65 uint level; 66 Identifier ident; 67 Module mod; 68 69 final extern (D) this(Module mod, uint level, Identifier ident) 70 { 71 super(Loc()); 72 this.mod = mod; 73 this.level = level; 74 this.ident = ident; 75 } 76 77 override final Condition syntaxCopy() 78 { 79 return this; // don't need to copy 80 } 81 82 override void accept(Visitor v) 83 { 84 v.visit(this); 85 } 86 } 87 88 /*********************************************************** 89 */ 90 extern (C++) final class DebugCondition : DVCondition 91 { 92 /** 93 * Set the global debug level 94 * 95 * Only called from the driver 96 * 97 * Params: 98 * level = Integer literal to set the global version to 99 */ 100 static void setGlobalLevel(uint level) 101 { 102 global.params.debuglevel = level; 103 } 104 105 106 /** 107 * Add an user-supplied identifier to the list of global debug identifiers 108 * 109 * Can be called from either the driver or a `debug = Ident;` statement. 110 * Unlike version identifier, there isn't any reserved debug identifier 111 * so no validation takes place. 112 * 113 * Params: 114 * ident = identifier to add 115 */ 116 deprecated("Kept for C++ compat - Use the string overload instead") 117 static void addGlobalIdent(const(char)* ident) 118 { 119 addGlobalIdent(ident[0 .. ident.strlen]); 120 } 121 122 /// Ditto 123 extern(D) static void addGlobalIdent(string ident) 124 { 125 // Overload necessary for string literals 126 addGlobalIdent(cast(const(char)[])ident); 127 } 128 129 130 /// Ditto 131 extern(D) static void addGlobalIdent(const(char)[] ident) 132 { 133 if (!global.params.debugids) 134 global.params.debugids = new Strings(); 135 global.params.debugids.push(cast(char*)ident); 136 } 137 138 139 /** 140 * Instantiate a new `DebugCondition` 141 * 142 * Params: 143 * mod = Module this node belongs to 144 * level = Minimum global level this condition needs to pass. 145 * Only used if `ident` is `null`. 146 * ident = Identifier required for this condition to pass. 147 * If `null`, this conditiion will use an integer level. 148 */ 149 extern (D) this(Module mod, uint level, Identifier ident) 150 { 151 super(mod, level, ident); 152 } 153 154 override int include(Scope* sc, ScopeDsymbol sds) 155 { 156 //printf("DebugCondition::include() level = %d, debuglevel = %d\n", level, global.params.debuglevel); 157 if (inc == 0) 158 { 159 inc = 2; 160 bool definedInModule = false; 161 if (ident) 162 { 163 if (findCondition(mod.debugids, ident)) 164 { 165 inc = 1; 166 definedInModule = true; 167 } 168 else if (findCondition(global.params.debugids, ident)) 169 inc = 1; 170 else 171 { 172 if (!mod.debugidsNot) 173 mod.debugidsNot = new Strings(); 174 mod.debugidsNot.push(ident.toChars()); 175 } 176 } 177 else if (level <= global.params.debuglevel || level <= mod.debuglevel) 178 inc = 1; 179 if (!definedInModule) 180 printDepsConditional(sc, this, "depsDebug "); 181 } 182 return (inc == 1); 183 } 184 185 override DebugCondition isDebugCondition() 186 { 187 return this; 188 } 189 190 override void accept(Visitor v) 191 { 192 v.visit(this); 193 } 194 195 override const(char)* toChars() 196 { 197 return ident ? ident.toChars() : "debug".ptr; 198 } 199 } 200 201 /** 202 * Node to represent a version condition 203 * 204 * A version condition is of the form: 205 * --- 206 * version (Identifier) 207 * --- 208 * In user code. 209 * This class also provides means to add version identifier 210 * to the list of global (cross module) identifiers. 211 */ 212 extern (C++) final class VersionCondition : DVCondition 213 { 214 /** 215 * Set the global version level 216 * 217 * Only called from the driver 218 * 219 * Params: 220 * level = Integer literal to set the global version to 221 */ 222 static void setGlobalLevel(uint level) 223 { 224 global.params.versionlevel = level; 225 } 226 227 /** 228 * Check if a given version identifier is reserved. 229 * 230 * Reserved identifier are the one documented below or 231 * those starting with 'D_'. 232 * 233 * Params: 234 * ident = identifier being checked 235 * 236 * Returns: 237 * `true` if it is reserved, `false` otherwise 238 */ 239 extern(D) private static bool isReserved(const(char)[] ident) 240 { 241 // This list doesn't include "D_*" versions, see the last return 242 static immutable string[] reserved = 243 [ 244 "DigitalMars", 245 "GNU", 246 "LDC", 247 "SDC", 248 "Windows", 249 "Win32", 250 "Win64", 251 "linux", 252 "OSX", 253 "iOS", 254 "TVOS", 255 "WatchOS", 256 "FreeBSD", 257 "OpenBSD", 258 "NetBSD", 259 "DragonFlyBSD", 260 "BSD", 261 "Solaris", 262 "Posix", 263 "AIX", 264 "Haiku", 265 "SkyOS", 266 "SysV3", 267 "SysV4", 268 "Hurd", 269 "Android", 270 "PlayStation", 271 "PlayStation4", 272 "Cygwin", 273 "MinGW", 274 "FreeStanding", 275 "X86", 276 "X86_64", 277 "ARM", 278 "ARM_Thumb", 279 "ARM_SoftFloat", 280 "ARM_SoftFP", 281 "ARM_HardFloat", 282 "AArch64", 283 "Epiphany", 284 "PPC", 285 "PPC_SoftFloat", 286 "PPC_HardFloat", 287 "PPC64", 288 "IA64", 289 "MIPS32", 290 "MIPS64", 291 "MIPS_O32", 292 "MIPS_N32", 293 "MIPS_O64", 294 "MIPS_N64", 295 "MIPS_EABI", 296 "MIPS_SoftFloat", 297 "MIPS_HardFloat", 298 "NVPTX", 299 "NVPTX64", 300 "SPARC", 301 "SPARC_V8Plus", 302 "SPARC_SoftFloat", 303 "SPARC_HardFloat", 304 "SPARC64", 305 "S390", 306 "S390X", 307 "SystemZ", 308 "HPPA", 309 "HPPA64", 310 "SH", 311 "SH64", 312 "Alpha", 313 "Alpha_SoftFloat", 314 "Alpha_HardFloat", 315 "LittleEndian", 316 "BigEndian", 317 "ELFv1", 318 "ELFv2", 319 "CRuntime_Bionic", 320 "CRuntime_DigitalMars", 321 "CRuntime_Glibc", 322 "CRuntime_Microsoft", 323 "unittest", 324 "assert", 325 "all", 326 "none" 327 ]; 328 foreach (r; reserved) 329 { 330 if (ident == r) 331 return true; 332 } 333 return (ident.length >= 2 && ident[0 .. 2] == "D_"); 334 } 335 336 /** 337 * Raises an error if a version identifier is reserved. 338 * 339 * Called when setting a version identifier, e.g. `-version=identifier` 340 * parameter to the compiler or `version = Foo` in user code. 341 * 342 * Params: 343 * loc = Where the identifier is set 344 * ident = identifier being checked (ident[$] must be '\0') 345 */ 346 extern(D) static void checkReserved(Loc loc, const(char)[] ident) 347 { 348 if (isReserved(ident)) 349 error(loc, "version identifier '%s' is reserved and cannot be set", 350 ident.ptr); 351 } 352 353 /** 354 * Add an user-supplied global identifier to the list 355 * 356 * Only called from the driver for `-version=Ident` parameters. 357 * Will raise an error if the identifier is reserved. 358 * 359 * Params: 360 * ident = identifier to add 361 */ 362 deprecated("Kept for C++ compat - Use the string overload instead") 363 static void addGlobalIdent(const(char)* ident) 364 { 365 addGlobalIdent(ident[0 .. ident.strlen]); 366 } 367 368 /// Ditto 369 extern(D) static void addGlobalIdent(string ident) 370 { 371 // Overload necessary for string literals 372 addGlobalIdent(cast(const(char)[])ident); 373 } 374 375 376 /// Ditto 377 extern(D) static void addGlobalIdent(const(char)[] ident) 378 { 379 checkReserved(Loc(), ident); 380 addPredefinedGlobalIdent(ident); 381 } 382 383 /** 384 * Add any global identifier to the list, without checking 385 * if it's predefined 386 * 387 * Only called from the driver after platform detection, 388 * and internally. 389 * 390 * Params: 391 * ident = identifier to add (ident[$] must be '\0') 392 */ 393 deprecated("Kept for C++ compat - Use the string overload instead") 394 static void addPredefinedGlobalIdent(const(char)* ident) 395 { 396 addPredefinedGlobalIdent(ident[0 .. ident.strlen]); 397 } 398 399 /// Ditto 400 extern(D) static void addPredefinedGlobalIdent(string ident) 401 { 402 // Forward: Overload necessary for string literal 403 addPredefinedGlobalIdent(cast(const(char)[])ident); 404 } 405 406 407 /// Ditto 408 extern(D) static void addPredefinedGlobalIdent(const(char)[] ident) 409 { 410 if (!global.params.versionids) 411 global.params.versionids = new Strings(); 412 global.params.versionids.push(cast(char*)ident); 413 } 414 415 /** 416 * Instantiate a new `VersionCondition` 417 * 418 * Params: 419 * mod = Module this node belongs to 420 * level = Minimum global level this condition needs to pass. 421 * Only used if `ident` is `null`. 422 * ident = Identifier required for this condition to pass. 423 * If `null`, this conditiion will use an integer level. 424 */ 425 extern (D) this(Module mod, uint level, Identifier ident) 426 { 427 super(mod, level, ident); 428 } 429 430 override int include(Scope* sc, ScopeDsymbol sds) 431 { 432 //printf("VersionCondition::include() level = %d, versionlevel = %d\n", level, global.params.versionlevel); 433 //if (ident) printf("\tident = '%s'\n", ident->toChars()); 434 if (inc == 0) 435 { 436 inc = 2; 437 bool definedInModule = false; 438 if (ident) 439 { 440 if (findCondition(mod.versionids, ident)) 441 { 442 inc = 1; 443 definedInModule = true; 444 } 445 else if (findCondition(global.params.versionids, ident)) 446 inc = 1; 447 else 448 { 449 if (!mod.versionidsNot) 450 mod.versionidsNot = new Strings(); 451 mod.versionidsNot.push(ident.toChars()); 452 } 453 } 454 else if (level <= global.params.versionlevel || level <= mod.versionlevel) 455 inc = 1; 456 if (!definedInModule && 457 (!ident || (!isReserved(ident.toString()) && ident != Id._unittest && ident != Id._assert))) 458 { 459 printDepsConditional(sc, this, "depsVersion "); 460 } 461 } 462 return (inc == 1); 463 } 464 465 override void accept(Visitor v) 466 { 467 v.visit(this); 468 } 469 470 override const(char)* toChars() 471 { 472 return ident ? ident.toChars() : "version".ptr; 473 } 474 } 475 476 /*********************************************************** 477 */ 478 extern (C++) final class StaticIfCondition : Condition 479 { 480 Expression exp; 481 int nest; // limit circular dependencies 482 483 extern (D) this(Loc loc, Expression exp) 484 { 485 super(loc); 486 this.exp = exp; 487 } 488 489 override Condition syntaxCopy() 490 { 491 return new StaticIfCondition(loc, exp.syntaxCopy()); 492 } 493 494 override int include(Scope* sc, ScopeDsymbol sds) 495 { 496 version (none) 497 { 498 printf("StaticIfCondition::include(sc = %p, sds = %p) this=%p inc = %d\n", sc, sds, this, inc); 499 if (sds) 500 { 501 printf("\ts = '%s', kind = %s\n", sds.toChars(), sds.kind()); 502 } 503 } 504 if (inc == 0) 505 { 506 if (exp.op == TOKerror || nest > 100) 507 { 508 error(loc, (nest > 1000) ? "unresolvable circular static if expression" : "error evaluating static if expression"); 509 goto Lerror; 510 } 511 if (!sc) 512 { 513 error(loc, "static if conditional cannot be at global scope"); 514 inc = 2; 515 return 0; 516 } 517 ++nest; 518 sc = sc.push(sc.scopesym); 519 sc.sds = sds; // sds gets any addMember() 520 //sc->speculative = true; // TODO: static if (is(T U)) { /* U is available */ } 521 sc.flags |= SCOPEcondition; 522 sc = sc.startCTFE(); 523 Expression e = exp.semantic(sc); 524 e = resolveProperties(sc, e); 525 sc = sc.endCTFE(); 526 sc.pop(); 527 --nest; 528 // Prevent repeated condition evaluation. 529 // See: fail_compilation/fail7815.d 530 if (inc != 0) 531 return (inc == 1); 532 if (!e.type.isBoolean()) 533 { 534 if (e.type.toBasetype() != Type.terror) 535 exp.error("expression %s of type %s does not have a boolean value", exp.toChars(), e.type.toChars()); 536 goto Lerror; 537 } 538 e = e.ctfeInterpret(); 539 if (e.op == TOKerror) 540 { 541 goto Lerror; 542 } 543 else if (e.isBool(true)) 544 inc = 1; 545 else if (e.isBool(false)) 546 inc = 2; 547 else 548 { 549 e.error("expression %s is not constant or does not evaluate to a bool", e.toChars()); 550 goto Lerror; 551 } 552 } 553 return (inc == 1); 554 Lerror: 555 if (!global.gag) 556 inc = 2; // so we don't see the error message again 557 return 0; 558 } 559 560 override void accept(Visitor v) 561 { 562 v.visit(this); 563 } 564 565 override const(char)* toChars() 566 { 567 return exp ? exp.toChars() : "static if".ptr; 568 } 569 } 570 571 extern (C++) int findCondition(Strings* ids, Identifier ident) 572 { 573 if (ids) 574 { 575 for (size_t i = 0; i < ids.dim; i++) 576 { 577 const(char)* id = (*ids)[i]; 578 if (strcmp(id, ident.toChars()) == 0) 579 return true; 580 } 581 } 582 return false; 583 } 584 585 // Helper for printing dependency information 586 private void printDepsConditional(Scope* sc, DVCondition condition, const(char)[] depType) 587 { 588 if (!global.params.moduleDeps || global.params.moduleDepsFile) 589 return; 590 OutBuffer* ob = global.params.moduleDeps; 591 Module imod = sc ? sc.instantiatingModule() : condition.mod; 592 if (!imod) 593 return; 594 ob.writestring(depType); 595 ob.writestring(imod.toPrettyChars()); 596 ob.writestring(" ("); 597 escapePath(ob, imod.srcfile.toChars()); 598 ob.writestring(") : "); 599 if (condition.ident) 600 ob.printf("%s\n", condition.ident.toChars()); 601 else 602 ob.printf("%d\n", condition.level); 603 }