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