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-2021 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 : ubyte 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 ? aggrfe.syntaxCopy() : null, 134 rangefe ? 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(const ref Loc loc, Module mod, uint level, Identifier ident) 487 { 488 super(loc); 489 this.mod = mod; 490 this.level = level; 491 this.ident = ident; 492 } 493 494 override final DVCondition 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 * loc = Location in the source file 552 */ 553 extern (D) this(const ref Loc loc, Module mod, uint level, Identifier ident) 554 { 555 super(loc, mod, level, ident); 556 } 557 558 override int include(Scope* sc) 559 { 560 //printf("DebugCondition::include() level = %d, debuglevel = %d\n", level, global.params.debuglevel); 561 if (inc == Include.notComputed) 562 { 563 inc = Include.no; 564 bool definedInModule = false; 565 if (ident) 566 { 567 if (findCondition(mod.debugids, ident)) 568 { 569 inc = Include.yes; 570 definedInModule = true; 571 } 572 else if (findCondition(global.debugids, ident)) 573 inc = Include.yes; 574 else 575 { 576 if (!mod.debugidsNot) 577 mod.debugidsNot = new Identifiers(); 578 mod.debugidsNot.push(ident); 579 } 580 } 581 else if (level <= global.params.debuglevel || level <= mod.debuglevel) 582 inc = Include.yes; 583 if (!definedInModule) 584 printDepsConditional(sc, this, "depsDebug "); 585 } 586 return (inc == Include.yes); 587 } 588 589 override inout(DebugCondition) isDebugCondition() inout 590 { 591 return this; 592 } 593 594 override void accept(Visitor v) 595 { 596 v.visit(this); 597 } 598 599 override const(char)* toChars() const 600 { 601 return ident ? ident.toChars() : "debug".ptr; 602 } 603 } 604 605 /** 606 * Node to represent a version condition 607 * 608 * A version condition is of the form: 609 * --- 610 * version (Identifier) 611 * --- 612 * In user code. 613 * This class also provides means to add version identifier 614 * to the list of global (cross module) identifiers. 615 */ 616 extern (C++) final class VersionCondition : DVCondition 617 { 618 /** 619 * Check if a given version identifier is reserved. 620 * 621 * Params: 622 * ident = identifier being checked 623 * 624 * Returns: 625 * `true` if it is reserved, `false` otherwise 626 */ 627 extern(D) private static bool isReserved(const(char)[] ident) 628 { 629 // This list doesn't include "D_*" versions, see the last return 630 switch (ident) 631 { 632 case "AArch64": 633 case "AIX": 634 case "all": 635 case "Alpha": 636 case "Alpha_HardFloat": 637 case "Alpha_SoftFloat": 638 case "Android": 639 case "ARM": 640 case "ARM_HardFloat": 641 case "ARM_SoftFloat": 642 case "ARM_SoftFP": 643 case "ARM_Thumb": 644 case "AsmJS": 645 case "assert": 646 case "AVR": 647 case "BigEndian": 648 case "BSD": 649 case "CppRuntime_Clang": 650 case "CppRuntime_DigitalMars": 651 case "CppRuntime_Gcc": 652 case "CppRuntime_Microsoft": 653 case "CppRuntime_Sun": 654 case "CRuntime_Bionic": 655 case "CRuntime_DigitalMars": 656 case "CRuntime_Glibc": 657 case "CRuntime_Microsoft": 658 case "CRuntime_Musl": 659 case "CRuntime_Newlib": 660 case "CRuntime_UClibc": 661 case "CRuntime_WASI": 662 case "Cygwin": 663 case "DigitalMars": 664 case "DragonFlyBSD": 665 case "Emscripten": 666 case "ELFv1": 667 case "ELFv2": 668 case "Epiphany": 669 case "FreeBSD": 670 case "FreeStanding": 671 case "GNU": 672 case "Haiku": 673 case "HPPA": 674 case "HPPA64": 675 case "Hurd": 676 case "IA64": 677 case "iOS": 678 case "LDC": 679 case "linux": 680 case "LittleEndian": 681 case "MinGW": 682 case "MIPS32": 683 case "MIPS64": 684 case "MIPS_EABI": 685 case "MIPS_HardFloat": 686 case "MIPS_N32": 687 case "MIPS_N64": 688 case "MIPS_O32": 689 case "MIPS_O64": 690 case "MIPS_SoftFloat": 691 case "MSP430": 692 case "NetBSD": 693 case "none": 694 case "NVPTX": 695 case "NVPTX64": 696 case "OpenBSD": 697 case "OSX": 698 case "PlayStation": 699 case "PlayStation4": 700 case "Posix": 701 case "PPC": 702 case "PPC64": 703 case "PPC_HardFloat": 704 case "PPC_SoftFloat": 705 case "RISCV32": 706 case "RISCV64": 707 case "S390": 708 case "S390X": 709 case "SDC": 710 case "SH": 711 case "SkyOS": 712 case "Solaris": 713 case "SPARC": 714 case "SPARC64": 715 case "SPARC_HardFloat": 716 case "SPARC_SoftFloat": 717 case "SPARC_V8Plus": 718 case "SystemZ": 719 case "SysV3": 720 case "SysV4": 721 case "TVOS": 722 case "unittest": 723 case "WASI": 724 case "WatchOS": 725 case "WebAssembly": 726 case "Win32": 727 case "Win64": 728 case "Windows": 729 case "X86": 730 case "X86_64": 731 return true; 732 733 default: 734 // Anything that starts with "D_" is reserved 735 return (ident.length >= 2 && ident[0 .. 2] == "D_"); 736 } 737 } 738 739 /** 740 * Raises an error if a version identifier is reserved. 741 * 742 * Called when setting a version identifier, e.g. `-version=identifier` 743 * parameter to the compiler or `version = Foo` in user code. 744 * 745 * Params: 746 * loc = Where the identifier is set 747 * ident = identifier being checked (ident[$] must be '\0') 748 */ 749 extern(D) static void checkReserved(const ref Loc loc, const(char)[] ident) 750 { 751 if (isReserved(ident)) 752 error(loc, "version identifier `%s` is reserved and cannot be set", 753 ident.ptr); 754 } 755 756 /** 757 * Add an user-supplied global identifier to the list 758 * 759 * Only called from the driver for `-version=Ident` parameters. 760 * Will raise an error if the identifier is reserved. 761 * 762 * Params: 763 * ident = identifier to add 764 */ 765 deprecated("Kept for C++ compat - Use the string overload instead") 766 static void addGlobalIdent(const(char)* ident) 767 { 768 addGlobalIdent(ident[0 .. ident.strlen]); 769 } 770 771 /// Ditto 772 extern(D) static void addGlobalIdent(string ident) 773 { 774 // Overload necessary for string literals 775 addGlobalIdent(cast(const(char)[])ident); 776 } 777 778 779 /// Ditto 780 extern(D) static void addGlobalIdent(const(char)[] ident) 781 { 782 checkReserved(Loc.initial, ident); 783 addPredefinedGlobalIdent(ident); 784 } 785 786 /** 787 * Add any global identifier to the list, without checking 788 * if it's predefined 789 * 790 * Only called from the driver after platform detection, 791 * and internally. 792 * 793 * Params: 794 * ident = identifier to add (ident[$] must be '\0') 795 */ 796 deprecated("Kept for C++ compat - Use the string overload instead") 797 static void addPredefinedGlobalIdent(const(char)* ident) 798 { 799 addPredefinedGlobalIdent(ident.toDString()); 800 } 801 802 /// Ditto 803 extern(D) static void addPredefinedGlobalIdent(string ident) 804 { 805 // Forward: Overload necessary for string literal 806 addPredefinedGlobalIdent(cast(const(char)[])ident); 807 } 808 809 810 /// Ditto 811 extern(D) static void addPredefinedGlobalIdent(const(char)[] ident) 812 { 813 if (!global.versionids) 814 global.versionids = new Identifiers(); 815 global.versionids.push(Identifier.idPool(ident)); 816 } 817 818 /** 819 * Instantiate a new `VersionCondition` 820 * 821 * Params: 822 * mod = Module this node belongs to 823 * level = Minimum global level this condition needs to pass. 824 * Only used if `ident` is `null`. 825 * ident = Identifier required for this condition to pass. 826 * If `null`, this conditiion will use an integer level. 827 * loc = Location in the source file 828 */ 829 extern (D) this(const ref Loc loc, Module mod, uint level, Identifier ident) 830 { 831 super(loc, mod, level, ident); 832 } 833 834 override int include(Scope* sc) 835 { 836 //printf("VersionCondition::include() level = %d, versionlevel = %d\n", level, global.params.versionlevel); 837 //if (ident) printf("\tident = '%s'\n", ident.toChars()); 838 if (inc == Include.notComputed) 839 { 840 inc = Include.no; 841 bool definedInModule = false; 842 if (ident) 843 { 844 if (findCondition(mod.versionids, ident)) 845 { 846 inc = Include.yes; 847 definedInModule = true; 848 } 849 else if (findCondition(global.versionids, ident)) 850 inc = Include.yes; 851 else 852 { 853 if (!mod.versionidsNot) 854 mod.versionidsNot = new Identifiers(); 855 mod.versionidsNot.push(ident); 856 } 857 } 858 else if (level <= global.params.versionlevel || level <= mod.versionlevel) 859 inc = Include.yes; 860 if (!definedInModule && 861 (!ident || (!isReserved(ident.toString()) && ident != Id._unittest && ident != Id._assert))) 862 { 863 printDepsConditional(sc, this, "depsVersion "); 864 } 865 } 866 return (inc == Include.yes); 867 } 868 869 override inout(VersionCondition) isVersionCondition() inout 870 { 871 return this; 872 } 873 874 override void accept(Visitor v) 875 { 876 v.visit(this); 877 } 878 879 override const(char)* toChars() const 880 { 881 return ident ? ident.toChars() : "version".ptr; 882 } 883 } 884 885 /*********************************************************** 886 */ 887 extern (C++) final class StaticIfCondition : Condition 888 { 889 Expression exp; 890 891 extern (D) this(const ref Loc loc, Expression exp) 892 { 893 super(loc); 894 this.exp = exp; 895 } 896 897 override StaticIfCondition syntaxCopy() 898 { 899 return new StaticIfCondition(loc, exp.syntaxCopy()); 900 } 901 902 override int include(Scope* sc) 903 { 904 // printf("StaticIfCondition::include(sc = %p) this=%p inc = %d\n", sc, this, inc); 905 906 int errorReturn() 907 { 908 if (!global.gag) 909 inc = Include.no; // so we don't see the error message again 910 return 0; 911 } 912 913 if (inc == Include.notComputed) 914 { 915 if (!sc) 916 { 917 error(loc, "`static if` conditional cannot be at global scope"); 918 inc = Include.no; 919 return 0; 920 } 921 922 import dmd.staticcond; 923 bool errors; 924 925 if (!exp) 926 return errorReturn(); 927 928 bool result = evalStaticCondition(sc, exp, exp, errors); 929 930 // Prevent repeated condition evaluation. 931 // See: fail_compilation/fail7815.d 932 if (inc != Include.notComputed) 933 return (inc == Include.yes); 934 if (errors) 935 return errorReturn(); 936 if (result) 937 inc = Include.yes; 938 else 939 inc = Include.no; 940 } 941 return (inc == Include.yes); 942 } 943 944 override void accept(Visitor v) 945 { 946 v.visit(this); 947 } 948 949 override const(char)* toChars() const 950 { 951 return exp ? exp.toChars() : "static if".ptr; 952 } 953 } 954 955 956 /**************************************** 957 * Find `ident` in an array of identifiers. 958 * Params: 959 * ids = array of identifiers 960 * ident = identifier to search for 961 * Returns: 962 * true if found 963 */ 964 bool findCondition(Identifiers* ids, Identifier ident) @safe nothrow pure 965 { 966 if (ids) 967 { 968 foreach (id; *ids) 969 { 970 if (id == ident) 971 return true; 972 } 973 } 974 return false; 975 } 976 977 // Helper for printing dependency information 978 private void printDepsConditional(Scope* sc, DVCondition condition, const(char)[] depType) 979 { 980 if (!global.params.moduleDeps || global.params.moduleDepsFile) 981 return; 982 OutBuffer* ob = global.params.moduleDeps; 983 Module imod = sc ? sc.instantiatingModule() : condition.mod; 984 if (!imod) 985 return; 986 ob.writestring(depType); 987 ob.writestring(imod.toPrettyChars()); 988 ob.writestring(" ("); 989 escapePath(ob, imod.srcfile.toChars()); 990 ob.writestring(") : "); 991 if (condition.ident) 992 ob.writestring(condition.ident.toString()); 993 else 994 ob.print(condition.level); 995 ob.writeByte('\n'); 996 }