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