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