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