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