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