1 /** 2 * Evaluate compile-time conditionals, such as `static if` `version` and `debug`. 3 * 4 * Specification: $(LINK2 https://dlang.org/spec/version.html, Conditional Compilation) 5 * 6 * Copyright: Copyright (C) 1999-2020 by The D Language Foundation, All Rights Reserved 7 * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright) 8 * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) 9 * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/cond.d, _cond.d) 10 * Documentation: https://dlang.org/phobos/dmd_cond.html 11 * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/cond.d 12 */ 13 14 module dmd.cond; 15 16 import core.stdc.string; 17 import dmd.arraytypes; 18 import dmd.ast_node; 19 import dmd.dmodule; 20 import dmd.dscope; 21 import dmd.dsymbol; 22 import dmd.errors; 23 import dmd.expression; 24 import dmd.expressionsem; 25 import dmd.globals; 26 import dmd.identifier; 27 import dmd.mtype; 28 import dmd.root.outbuffer; 29 import dmd.root.rootobject; 30 import dmd.root.string; 31 import dmd.tokens; 32 import dmd.utils; 33 import dmd.visitor; 34 import dmd.id; 35 import dmd.statement; 36 import dmd.declaration; 37 import dmd.dstruct; 38 import dmd.func; 39 40 /*********************************************************** 41 */ 42 43 enum Include 44 { 45 notComputed, /// not computed yet 46 yes, /// include the conditional code 47 no, /// do not include the conditional code 48 } 49 50 extern (C++) abstract class Condition : ASTNode 51 { 52 Loc loc; 53 54 Include inc; 55 56 override final DYNCAST dyncast() const 57 { 58 return DYNCAST.condition; 59 } 60 61 extern (D) this(const ref Loc loc) 62 { 63 this.loc = loc; 64 } 65 66 abstract Condition syntaxCopy(); 67 68 abstract int include(Scope* sc); 69 70 inout(DebugCondition) isDebugCondition() inout 71 { 72 return null; 73 } 74 75 inout(VersionCondition) isVersionCondition() inout 76 { 77 return null; 78 } 79 80 override void accept(Visitor v) 81 { 82 v.visit(this); 83 } 84 } 85 86 /*********************************************************** 87 * Implements common functionality for StaticForeachDeclaration and 88 * StaticForeachStatement This performs the necessary lowerings before 89 * dmd.statementsem.makeTupleForeach can be used to expand the 90 * corresponding `static foreach` declaration or statement. 91 */ 92 93 extern (C++) final class StaticForeach : RootObject 94 { 95 extern(D) static immutable tupleFieldName = "tuple"; // used in lowering 96 97 Loc loc; 98 99 /*************** 100 * Not `null` iff the `static foreach` is over an aggregate. In 101 * this case, it contains the corresponding ForeachStatement. For 102 * StaticForeachDeclaration, the body is `null`. 103 */ 104 ForeachStatement aggrfe; 105 /*************** 106 * Not `null` iff the `static foreach` is over a range. Exactly 107 * one of the `aggrefe` and `rangefe` fields is not null. See 108 * `aggrfe` field for more details. 109 */ 110 ForeachRangeStatement rangefe; 111 112 /*************** 113 * true if it is necessary to expand a tuple into multiple 114 * variables (see lowerNonArrayAggregate). 115 */ 116 bool needExpansion = false; 117 118 extern (D) this(const ref Loc loc,ForeachStatement aggrfe,ForeachRangeStatement rangefe) 119 { 120 assert(!!aggrfe ^ !!rangefe); 121 122 this.loc = loc; 123 this.aggrfe = aggrfe; 124 this.rangefe = rangefe; 125 } 126 127 StaticForeach syntaxCopy() 128 { 129 return new StaticForeach( 130 loc, 131 aggrfe ? cast(ForeachStatement)aggrfe.syntaxCopy() : null, 132 rangefe ? cast(ForeachRangeStatement)rangefe.syntaxCopy() : null 133 ); 134 } 135 136 /***************************************** 137 * Turn an aggregate which is an array into an expression tuple 138 * of its elements. I.e., lower 139 * static foreach (x; [1, 2, 3, 4]) { ... } 140 * to 141 * static foreach (x; AliasSeq!(1, 2, 3, 4)) { ... } 142 */ 143 private extern(D) void lowerArrayAggregate(Scope* sc) 144 { 145 auto aggr = aggrfe.aggr; 146 Expression el = new ArrayLengthExp(aggr.loc, aggr); 147 sc = sc.startCTFE(); 148 el = el.expressionSemantic(sc); 149 sc = sc.endCTFE(); 150 el = el.optimize(WANTvalue); 151 el = el.ctfeInterpret(); 152 if (el.op == TOK.int64) 153 { 154 dinteger_t length = el.toInteger(); 155 auto es = new Expressions(); 156 foreach (i; 0 .. length) 157 { 158 auto index = new IntegerExp(loc, i, Type.tsize_t); 159 auto value = new IndexExp(aggr.loc, aggr, index); 160 es.push(value); 161 } 162 aggrfe.aggr = new TupleExp(aggr.loc, es); 163 aggrfe.aggr = aggrfe.aggr.expressionSemantic(sc); 164 aggrfe.aggr = aggrfe.aggr.optimize(WANTvalue); 165 } 166 else 167 { 168 aggrfe.aggr = new ErrorExp(); 169 } 170 } 171 172 /***************************************** 173 * Wrap a statement into a function literal and call it. 174 * 175 * Params: 176 * loc = The source location. 177 * s = The statement. 178 * Returns: 179 * AST of the expression `(){ s; }()` with location loc. 180 */ 181 private extern(D) Expression wrapAndCall(const ref Loc loc, Statement s) 182 { 183 auto tf = new TypeFunction(ParameterList(), null, LINK.default_, 0); 184 auto fd = new FuncLiteralDeclaration(loc, loc, tf, TOK.reserved, null); 185 fd.fbody = s; 186 auto fe = new FuncExp(loc, fd); 187 auto ce = new CallExp(loc, fe, new Expressions()); 188 return ce; 189 } 190 191 /***************************************** 192 * Create a `foreach` statement from `aggrefe/rangefe` with given 193 * `foreach` variables and body `s`. 194 * 195 * Params: 196 * loc = The source location. 197 * parameters = The foreach variables. 198 * s = The `foreach` body. 199 * Returns: 200 * `foreach (parameters; aggregate) s;` or 201 * `foreach (parameters; lower .. upper) s;` 202 * Where aggregate/lower, upper are as for the current StaticForeach. 203 */ 204 private extern(D) Statement createForeach(const ref Loc loc, Parameters* parameters, Statement s) 205 { 206 if (aggrfe) 207 { 208 return new ForeachStatement(loc, aggrfe.op, parameters, aggrfe.aggr.syntaxCopy(), s, loc); 209 } 210 else 211 { 212 assert(rangefe && parameters.dim == 1); 213 return new ForeachRangeStatement(loc, rangefe.op, (*parameters)[0], rangefe.lwr.syntaxCopy(), rangefe.upr.syntaxCopy(), s, loc); 214 } 215 } 216 217 /***************************************** 218 * For a `static foreach` with multiple loop variables, the 219 * aggregate is lowered to an array of tuples. As D does not have 220 * built-in tuples, we need a suitable tuple type. This generates 221 * a `struct` that serves as the tuple type. This type is only 222 * used during CTFE and hence its typeinfo will not go to the 223 * object file. 224 * 225 * Params: 226 * loc = The source location. 227 * e = The expressions we wish to store in the tuple. 228 * sc = The current scope. 229 * Returns: 230 * A struct type of the form 231 * struct Tuple 232 * { 233 * typeof(AliasSeq!(e)) tuple; 234 * } 235 */ 236 237 private extern(D) TypeStruct createTupleType(const ref Loc loc, Expressions* e, Scope* sc) 238 { // TODO: move to druntime? 239 auto sid = Identifier.generateId("Tuple"); 240 auto sdecl = new StructDeclaration(loc, sid, false); 241 sdecl.storage_class |= STC.static_; 242 sdecl.members = new Dsymbols(); 243 auto fid = Identifier.idPool(tupleFieldName.ptr, tupleFieldName.length); 244 auto ty = new TypeTypeof(loc, new TupleExp(loc, e)); 245 sdecl.members.push(new VarDeclaration(loc, ty, fid, null, 0)); 246 auto r = cast(TypeStruct)sdecl.type; 247 r.vtinfo = TypeInfoStructDeclaration.create(r); // prevent typeinfo from going to object file 248 return r; 249 } 250 251 /***************************************** 252 * Create the AST for an instantiation of a suitable tuple type. 253 * 254 * Params: 255 * loc = The source location. 256 * type = A Tuple type, created with createTupleType. 257 * e = The expressions we wish to store in the tuple. 258 * Returns: 259 * An AST for the expression `Tuple(e)`. 260 */ 261 262 private extern(D) Expression createTuple(const ref Loc loc, TypeStruct type, Expressions* e) 263 { // TODO: move to druntime? 264 return new CallExp(loc, new TypeExp(loc, type), e); 265 } 266 267 268 /***************************************** 269 * Lower any aggregate that is not an array to an array using a 270 * regular foreach loop within CTFE. If there are multiple 271 * `static foreach` loop variables, an array of tuples is 272 * generated. In thise case, the field `needExpansion` is set to 273 * true to indicate that the static foreach loop expansion will 274 * need to expand the tuples into multiple variables. 275 * 276 * For example, `static foreach (x; range) { ... }` is lowered to: 277 * 278 * static foreach (x; { 279 * typeof({ 280 * foreach (x; range) return x; 281 * }())[] __res; 282 * foreach (x; range) __res ~= x; 283 * return __res; 284 * }()) { ... } 285 * 286 * Finally, call `lowerArrayAggregate` to turn the produced 287 * array into an expression tuple. 288 * 289 * Params: 290 * sc = The current scope. 291 */ 292 293 private void lowerNonArrayAggregate(Scope* sc) 294 { 295 auto nvars = aggrfe ? aggrfe.parameters.dim : 1; 296 auto aloc = aggrfe ? aggrfe.aggr.loc : rangefe.lwr.loc; 297 // We need three sets of foreach loop variables because the 298 // lowering contains three foreach loops. 299 Parameters*[3] pparams = [new Parameters(), new Parameters(), new Parameters()]; 300 foreach (i; 0 .. nvars) 301 { 302 foreach (params; pparams) 303 { 304 auto p = aggrfe ? (*aggrfe.parameters)[i] : rangefe.prm; 305 params.push(new Parameter(p.storageClass, p.type, p.ident, null, null)); 306 } 307 } 308 Expression[2] res; 309 TypeStruct tplty = null; 310 if (nvars == 1) // only one `static foreach` variable, generate identifiers. 311 { 312 foreach (i; 0 .. 2) 313 { 314 res[i] = new IdentifierExp(aloc, (*pparams[i])[0].ident); 315 } 316 } 317 else // multiple `static foreach` variables, generate tuples. 318 { 319 foreach (i; 0 .. 2) 320 { 321 auto e = new Expressions(pparams[0].dim); 322 foreach (j, ref elem; *e) 323 { 324 auto p = (*pparams[i])[j]; 325 elem = new IdentifierExp(aloc, p.ident); 326 } 327 if (!tplty) 328 { 329 tplty = createTupleType(aloc, e, sc); 330 } 331 res[i] = createTuple(aloc, tplty, e); 332 } 333 needExpansion = true; // need to expand the tuples later 334 } 335 // generate remaining code for the new aggregate which is an 336 // array (see documentation comment). 337 if (rangefe) 338 { 339 sc = sc.startCTFE(); 340 rangefe.lwr = rangefe.lwr.expressionSemantic(sc); 341 rangefe.lwr = resolveProperties(sc, rangefe.lwr); 342 rangefe.upr = rangefe.upr.expressionSemantic(sc); 343 rangefe.upr = resolveProperties(sc, rangefe.upr); 344 sc = sc.endCTFE(); 345 rangefe.lwr = rangefe.lwr.optimize(WANTvalue); 346 rangefe.lwr = rangefe.lwr.ctfeInterpret(); 347 rangefe.upr = rangefe.upr.optimize(WANTvalue); 348 rangefe.upr = rangefe.upr.ctfeInterpret(); 349 } 350 auto s1 = new Statements(); 351 auto sfe = new Statements(); 352 if (tplty) sfe.push(new ExpStatement(loc, tplty.sym)); 353 sfe.push(new ReturnStatement(aloc, res[0])); 354 s1.push(createForeach(aloc, pparams[0], new CompoundStatement(aloc, sfe))); 355 s1.push(new ExpStatement(aloc, new AssertExp(aloc, new IntegerExp(aloc, 0, Type.tint32)))); 356 auto ety = new TypeTypeof(aloc, wrapAndCall(aloc, new CompoundStatement(aloc, s1))); 357 auto aty = ety.arrayOf(); 358 auto idres = Identifier.generateId("__res"); 359 auto vard = new VarDeclaration(aloc, aty, idres, null); 360 auto s2 = new Statements(); 361 s2.push(new ExpStatement(aloc, vard)); 362 auto catass = new CatAssignExp(aloc, new IdentifierExp(aloc, idres), res[1]); 363 s2.push(createForeach(aloc, pparams[1], new ExpStatement(aloc, catass))); 364 s2.push(new ReturnStatement(aloc, new IdentifierExp(aloc, idres))); 365 auto aggr = wrapAndCall(aloc, new CompoundStatement(aloc, s2)); 366 sc = sc.startCTFE(); 367 aggr = aggr.expressionSemantic(sc); 368 aggr = resolveProperties(sc, aggr); 369 sc = sc.endCTFE(); 370 aggr = aggr.optimize(WANTvalue); 371 aggr = aggr.ctfeInterpret(); 372 373 assert(!!aggrfe ^ !!rangefe); 374 aggrfe = new ForeachStatement(loc, TOK.foreach_, pparams[2], aggr, 375 aggrfe ? aggrfe._body : rangefe._body, 376 aggrfe ? aggrfe.endloc : rangefe.endloc); 377 rangefe = null; 378 lowerArrayAggregate(sc); // finally, turn generated array into expression tuple 379 } 380 381 /***************************************** 382 * Perform `static foreach` lowerings that are necessary in order 383 * to finally expand the `static foreach` using 384 * `dmd.statementsem.makeTupleForeach`. 385 */ 386 extern(D) void prepare(Scope* sc) 387 { 388 assert(sc); 389 390 if (aggrfe) 391 { 392 sc = sc.startCTFE(); 393 aggrfe.aggr = aggrfe.aggr.expressionSemantic(sc); 394 sc = sc.endCTFE(); 395 aggrfe.aggr = aggrfe.aggr.optimize(WANTvalue); 396 auto tab = aggrfe.aggr.type.toBasetype(); 397 if (tab.ty != Ttuple) 398 { 399 aggrfe.aggr = aggrfe.aggr.ctfeInterpret(); 400 } 401 } 402 403 if (aggrfe && aggrfe.aggr.type.toBasetype().ty == Terror) 404 { 405 return; 406 } 407 408 if (!ready()) 409 { 410 if (aggrfe && aggrfe.aggr.type.toBasetype().ty == Tarray) 411 { 412 lowerArrayAggregate(sc); 413 } 414 else 415 { 416 lowerNonArrayAggregate(sc); 417 } 418 } 419 } 420 421 /***************************************** 422 * Returns: 423 * `true` iff ready to call `dmd.statementsem.makeTupleForeach`. 424 */ 425 extern(D) bool ready() 426 { 427 return aggrfe && aggrfe.aggr && aggrfe.aggr.type && aggrfe.aggr.type.toBasetype().ty == Ttuple; 428 } 429 } 430 431 /*********************************************************** 432 */ 433 extern (C++) class DVCondition : Condition 434 { 435 uint level; 436 Identifier ident; 437 Module mod; 438 439 extern (D) this(Module mod, uint level, Identifier ident) 440 { 441 super(Loc.initial); 442 this.mod = mod; 443 this.level = level; 444 this.ident = ident; 445 } 446 447 override final Condition syntaxCopy() 448 { 449 return this; // don't need to copy 450 } 451 452 override void accept(Visitor v) 453 { 454 v.visit(this); 455 } 456 } 457 458 /*********************************************************** 459 */ 460 extern (C++) final class DebugCondition : DVCondition 461 { 462 /** 463 * Add an user-supplied identifier to the list of global debug identifiers 464 * 465 * Can be called from either the driver or a `debug = Ident;` statement. 466 * Unlike version identifier, there isn't any reserved debug identifier 467 * so no validation takes place. 468 * 469 * Params: 470 * ident = identifier to add 471 */ 472 deprecated("Kept for C++ compat - Use the string overload instead") 473 static void addGlobalIdent(const(char)* ident) 474 { 475 addGlobalIdent(ident[0 .. ident.strlen]); 476 } 477 478 /// Ditto 479 extern(D) static void addGlobalIdent(string ident) 480 { 481 // Overload necessary for string literals 482 addGlobalIdent(cast(const(char)[])ident); 483 } 484 485 486 /// Ditto 487 extern(D) static void addGlobalIdent(const(char)[] ident) 488 { 489 if (!global.debugids) 490 global.debugids = new Identifiers(); 491 global.debugids.push(Identifier.idPool(ident)); 492 } 493 494 495 /** 496 * Instantiate a new `DebugCondition` 497 * 498 * Params: 499 * mod = Module this node belongs to 500 * level = Minimum global level this condition needs to pass. 501 * Only used if `ident` is `null`. 502 * ident = Identifier required for this condition to pass. 503 * If `null`, this conditiion will use an integer level. 504 */ 505 extern (D) this(Module mod, uint level, Identifier ident) 506 { 507 super(mod, level, ident); 508 } 509 510 override int include(Scope* sc) 511 { 512 //printf("DebugCondition::include() level = %d, debuglevel = %d\n", level, global.params.debuglevel); 513 if (inc == Include.notComputed) 514 { 515 inc = Include.no; 516 bool definedInModule = false; 517 if (ident) 518 { 519 if (findCondition(mod.debugids, ident)) 520 { 521 inc = Include.yes; 522 definedInModule = true; 523 } 524 else if (findCondition(global.debugids, ident)) 525 inc = Include.yes; 526 else 527 { 528 if (!mod.debugidsNot) 529 mod.debugidsNot = new Identifiers(); 530 mod.debugidsNot.push(ident); 531 } 532 } 533 else if (level <= global.params.debuglevel || level <= mod.debuglevel) 534 inc = Include.yes; 535 if (!definedInModule) 536 printDepsConditional(sc, this, "depsDebug "); 537 } 538 return (inc == Include.yes); 539 } 540 541 override inout(DebugCondition) isDebugCondition() inout 542 { 543 return this; 544 } 545 546 override void accept(Visitor v) 547 { 548 v.visit(this); 549 } 550 551 override const(char)* toChars() const 552 { 553 return ident ? ident.toChars() : "debug".ptr; 554 } 555 } 556 557 /** 558 * Node to represent a version condition 559 * 560 * A version condition is of the form: 561 * --- 562 * version (Identifier) 563 * --- 564 * In user code. 565 * This class also provides means to add version identifier 566 * to the list of global (cross module) identifiers. 567 */ 568 extern (C++) final class VersionCondition : DVCondition 569 { 570 /** 571 * Check if a given version identifier is reserved. 572 * 573 * Params: 574 * ident = identifier being checked 575 * 576 * Returns: 577 * `true` if it is reserved, `false` otherwise 578 */ 579 extern(D) private static bool isReserved(const(char)[] ident) 580 { 581 // This list doesn't include "D_*" versions, see the last return 582 switch (ident) 583 { 584 case "DigitalMars": 585 case "GNU": 586 case "LDC": 587 case "SDC": 588 case "Windows": 589 case "Win32": 590 case "Win64": 591 case "linux": 592 case "OSX": 593 case "iOS": 594 case "TVOS": 595 case "WatchOS": 596 case "FreeBSD": 597 case "OpenBSD": 598 case "NetBSD": 599 case "DragonFlyBSD": 600 case "BSD": 601 case "Solaris": 602 case "Posix": 603 case "AIX": 604 case "Haiku": 605 case "SkyOS": 606 case "SysV3": 607 case "SysV4": 608 case "Hurd": 609 case "Android": 610 case "Emscripten": 611 case "PlayStation": 612 case "PlayStation4": 613 case "Cygwin": 614 case "MinGW": 615 case "FreeStanding": 616 case "X86": 617 case "X86_64": 618 case "ARM": 619 case "ARM_Thumb": 620 case "ARM_SoftFloat": 621 case "ARM_SoftFP": 622 case "ARM_HardFloat": 623 case "AArch64": 624 case "AsmJS": 625 case "Epiphany": 626 case "PPC": 627 case "PPC_SoftFloat": 628 case "PPC_HardFloat": 629 case "PPC64": 630 case "IA64": 631 case "MIPS32": 632 case "MIPS64": 633 case "MIPS_O32": 634 case "MIPS_N32": 635 case "MIPS_O64": 636 case "MIPS_N64": 637 case "MIPS_EABI": 638 case "MIPS_SoftFloat": 639 case "MIPS_HardFloat": 640 case "MSP430": 641 case "NVPTX": 642 case "NVPTX64": 643 case "RISCV32": 644 case "RISCV64": 645 case "SPARC": 646 case "SPARC_V8Plus": 647 case "SPARC_SoftFloat": 648 case "SPARC_HardFloat": 649 case "SPARC64": 650 case "S390": 651 case "S390X": 652 case "SystemZ": 653 case "HPPA": 654 case "HPPA64": 655 case "SH": 656 case "WebAssembly": 657 case "WASI": 658 case "Alpha": 659 case "Alpha_SoftFloat": 660 case "Alpha_HardFloat": 661 case "LittleEndian": 662 case "BigEndian": 663 case "ELFv1": 664 case "ELFv2": 665 case "CRuntime_Bionic": 666 case "CRuntime_DigitalMars": 667 case "CRuntime_Glibc": 668 case "CRuntime_Microsoft": 669 case "CRuntime_Musl": 670 case "CRuntime_UClibc": 671 case "CRuntime_WASI": 672 case "CppRuntime_Clang": 673 case "CppRuntime_DigitalMars": 674 case "CppRuntime_Gcc": 675 case "CppRuntime_Microsoft": 676 case "CppRuntime_Sun": 677 case "unittest": 678 case "assert": 679 case "all": 680 case "none": 681 return true; 682 683 default: 684 // Anything that starts with "D_" is reserved 685 return (ident.length >= 2 && ident[0 .. 2] == "D_"); 686 } 687 } 688 689 /** 690 * Raises an error if a version identifier is reserved. 691 * 692 * Called when setting a version identifier, e.g. `-version=identifier` 693 * parameter to the compiler or `version = Foo` in user code. 694 * 695 * Params: 696 * loc = Where the identifier is set 697 * ident = identifier being checked (ident[$] must be '\0') 698 */ 699 extern(D) static void checkReserved(const ref Loc loc, const(char)[] ident) 700 { 701 if (isReserved(ident)) 702 error(loc, "version identifier `%s` is reserved and cannot be set", 703 ident.ptr); 704 } 705 706 /** 707 * Add an user-supplied global identifier to the list 708 * 709 * Only called from the driver for `-version=Ident` parameters. 710 * Will raise an error if the identifier is reserved. 711 * 712 * Params: 713 * ident = identifier to add 714 */ 715 deprecated("Kept for C++ compat - Use the string overload instead") 716 static void addGlobalIdent(const(char)* ident) 717 { 718 addGlobalIdent(ident[0 .. ident.strlen]); 719 } 720 721 /// Ditto 722 extern(D) static void addGlobalIdent(string ident) 723 { 724 // Overload necessary for string literals 725 addGlobalIdent(cast(const(char)[])ident); 726 } 727 728 729 /// Ditto 730 extern(D) static void addGlobalIdent(const(char)[] ident) 731 { 732 checkReserved(Loc.initial, ident); 733 addPredefinedGlobalIdent(ident); 734 } 735 736 /** 737 * Add any global identifier to the list, without checking 738 * if it's predefined 739 * 740 * Only called from the driver after platform detection, 741 * and internally. 742 * 743 * Params: 744 * ident = identifier to add (ident[$] must be '\0') 745 */ 746 deprecated("Kept for C++ compat - Use the string overload instead") 747 static void addPredefinedGlobalIdent(const(char)* ident) 748 { 749 addPredefinedGlobalIdent(ident.toDString()); 750 } 751 752 /// Ditto 753 extern(D) static void addPredefinedGlobalIdent(string ident) 754 { 755 // Forward: Overload necessary for string literal 756 addPredefinedGlobalIdent(cast(const(char)[])ident); 757 } 758 759 760 /// Ditto 761 extern(D) static void addPredefinedGlobalIdent(const(char)[] ident) 762 { 763 if (!global.versionids) 764 global.versionids = new Identifiers(); 765 global.versionids.push(Identifier.idPool(ident)); 766 } 767 768 /** 769 * Instantiate a new `VersionCondition` 770 * 771 * Params: 772 * mod = Module this node belongs to 773 * level = Minimum global level this condition needs to pass. 774 * Only used if `ident` is `null`. 775 * ident = Identifier required for this condition to pass. 776 * If `null`, this conditiion will use an integer level. 777 */ 778 extern (D) this(Module mod, uint level, Identifier ident) 779 { 780 super(mod, level, ident); 781 } 782 783 override int include(Scope* sc) 784 { 785 //printf("VersionCondition::include() level = %d, versionlevel = %d\n", level, global.params.versionlevel); 786 //if (ident) printf("\tident = '%s'\n", ident.toChars()); 787 if (inc == Include.notComputed) 788 { 789 inc = Include.no; 790 bool definedInModule = false; 791 if (ident) 792 { 793 if (findCondition(mod.versionids, ident)) 794 { 795 inc = Include.yes; 796 definedInModule = true; 797 } 798 else if (findCondition(global.versionids, ident)) 799 inc = Include.yes; 800 else 801 { 802 if (!mod.versionidsNot) 803 mod.versionidsNot = new Identifiers(); 804 mod.versionidsNot.push(ident); 805 } 806 } 807 else if (level <= global.params.versionlevel || level <= mod.versionlevel) 808 inc = Include.yes; 809 if (!definedInModule && 810 (!ident || (!isReserved(ident.toString()) && ident != Id._unittest && ident != Id._assert))) 811 { 812 printDepsConditional(sc, this, "depsVersion "); 813 } 814 } 815 return (inc == Include.yes); 816 } 817 818 override inout(VersionCondition) isVersionCondition() inout 819 { 820 return this; 821 } 822 823 override void accept(Visitor v) 824 { 825 v.visit(this); 826 } 827 828 override const(char)* toChars() const 829 { 830 return ident ? ident.toChars() : "version".ptr; 831 } 832 } 833 834 /*********************************************************** 835 */ 836 extern (C++) final class StaticIfCondition : Condition 837 { 838 Expression exp; 839 840 extern (D) this(const ref Loc loc, Expression exp) 841 { 842 super(loc); 843 this.exp = exp; 844 } 845 846 override Condition syntaxCopy() 847 { 848 return new StaticIfCondition(loc, exp.syntaxCopy()); 849 } 850 851 override int include(Scope* sc) 852 { 853 // printf("StaticIfCondition::include(sc = %p) this=%p inc = %d\n", sc, this, inc); 854 855 int errorReturn() 856 { 857 if (!global.gag) 858 inc = Include.no; // so we don't see the error message again 859 return 0; 860 } 861 862 if (inc == Include.notComputed) 863 { 864 if (!sc) 865 { 866 error(loc, "`static if` conditional cannot be at global scope"); 867 inc = Include.no; 868 return 0; 869 } 870 871 import dmd.staticcond; 872 bool errors; 873 bool result = evalStaticCondition(sc, exp, exp, errors); 874 875 // Prevent repeated condition evaluation. 876 // See: fail_compilation/fail7815.d 877 if (inc != Include.notComputed) 878 return (inc == Include.yes); 879 if (errors) 880 return errorReturn(); 881 if (result) 882 inc = Include.yes; 883 else 884 inc = Include.no; 885 } 886 return (inc == Include.yes); 887 } 888 889 override void accept(Visitor v) 890 { 891 v.visit(this); 892 } 893 894 override const(char)* toChars() const 895 { 896 return exp ? exp.toChars() : "static if".ptr; 897 } 898 } 899 900 901 /**************************************** 902 * Find `ident` in an array of identifiers. 903 * Params: 904 * ids = array of identifiers 905 * ident = identifier to search for 906 * Returns: 907 * true if found 908 */ 909 bool findCondition(Identifiers* ids, Identifier ident) 910 { 911 if (ids) 912 { 913 foreach (id; *ids) 914 { 915 if (id == ident) 916 return true; 917 } 918 } 919 return false; 920 } 921 922 // Helper for printing dependency information 923 private void printDepsConditional(Scope* sc, DVCondition condition, const(char)[] depType) 924 { 925 if (!global.params.moduleDeps || global.params.moduleDepsFile) 926 return; 927 OutBuffer* ob = global.params.moduleDeps; 928 Module imod = sc ? sc.instantiatingModule() : condition.mod; 929 if (!imod) 930 return; 931 ob.writestring(depType); 932 ob.writestring(imod.toPrettyChars()); 933 ob.writestring(" ("); 934 escapePath(ob, imod.srcfile.toChars()); 935 ob.writestring(") : "); 936 if (condition.ident) 937 ob.writestring(condition.ident.toString()); 938 else 939 ob.print(condition.level); 940 ob.writeByte('\n'); 941 }