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