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 }