AVR: target/116407 - Fix linker error "relocation truncated to fit".
[official-gcc.git] / gcc / d / dmd / cond.d
blobe194664383e9a91dccfa9be33c18ad64fddd2597
1 /**
2 * Evaluate compile-time conditionals, such as `static if` `version` and `debug`.
4 * Specification: $(LINK2 https://dlang.org/spec/version.html, Conditional Compilation)
6 * Copyright: Copyright (C) 1999-2024 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
14 module dmd.cond;
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.dinterpret;
22 import dmd.dmodule;
23 import dmd.dscope;
24 import dmd.dsymbol;
25 import dmd.errors;
26 import dmd.expression;
27 import dmd.expressionsem;
28 import dmd.globals;
29 import dmd.identifier;
30 import dmd.location;
31 import dmd.mtype;
32 import dmd.optimize;
33 import dmd.typesem;
34 import dmd.common.outbuffer;
35 import dmd.rootobject;
36 import dmd.root.string;
37 import dmd.tokens;
38 import dmd.utils;
39 import dmd.visitor;
40 import dmd.id;
41 import dmd.statement;
42 import dmd.declaration;
43 import dmd.dstruct;
44 import dmd.func;
46 /***********************************************************
49 enum Include : ubyte
51 notComputed, /// not computed yet
52 yes, /// include the conditional code
53 no, /// do not include the conditional code
56 extern (C++) abstract class Condition : ASTNode
58 Loc loc;
60 Include inc;
62 override final DYNCAST dyncast() const
64 return DYNCAST.condition;
67 extern (D) this(const ref Loc loc) @safe
69 this.loc = loc;
72 abstract Condition syntaxCopy();
74 abstract int include(Scope* sc);
76 inout(DebugCondition) isDebugCondition() inout
78 return null;
81 inout(VersionCondition) isVersionCondition() inout
83 return null;
86 inout(StaticIfCondition) isStaticIfCondition() inout
88 return null;
91 override void accept(Visitor v)
93 v.visit(this);
97 /***********************************************************
98 * Implements common functionality for StaticForeachDeclaration and
99 * StaticForeachStatement This performs the necessary lowerings before
100 * dmd.statementsem.makeTupleForeach can be used to expand the
101 * corresponding `static foreach` declaration or statement.
104 extern (C++) final class StaticForeach : RootObject
106 extern(D) static immutable tupleFieldName = "tuple"; // used in lowering
108 Loc loc;
110 /***************
111 * Not `null` iff the `static foreach` is over an aggregate. In
112 * this case, it contains the corresponding ForeachStatement. For
113 * StaticForeachDeclaration, the body is `null`.
115 ForeachStatement aggrfe;
116 /***************
117 * Not `null` iff the `static foreach` is over a range. Exactly
118 * one of the `aggrefe` and `rangefe` fields is not null. See
119 * `aggrfe` field for more details.
121 ForeachRangeStatement rangefe;
123 /***************
124 * true if it is necessary to expand a tuple into multiple
125 * variables (see lowerNonArrayAggregate).
127 bool needExpansion = false;
129 extern (D) this(const ref Loc loc, ForeachStatement aggrfe, ForeachRangeStatement rangefe) @safe
131 assert(!!aggrfe ^ !!rangefe);
133 this.loc = loc;
134 this.aggrfe = aggrfe;
135 this.rangefe = rangefe;
138 extern (D) StaticForeach syntaxCopy()
140 return new StaticForeach(
141 loc,
142 aggrfe ? aggrfe.syntaxCopy() : null,
143 rangefe ? rangefe.syntaxCopy() : null
147 /*****************************************
148 * Turn an aggregate which is an array into an expression tuple
149 * of its elements. I.e., lower
150 * static foreach (x; [1, 2, 3, 4]) { ... }
151 * to
152 * static foreach (x; AliasSeq!(1, 2, 3, 4)) { ... }
154 private extern(D) void lowerArrayAggregate(Scope* sc)
156 auto aggr = aggrfe.aggr;
157 Expression el = new ArrayLengthExp(aggr.loc, aggr);
158 sc = sc.startCTFE();
159 el = el.expressionSemantic(sc);
160 sc = sc.endCTFE();
161 el = el.optimize(WANTvalue);
162 el = el.ctfeInterpret();
163 if (el.op == EXP.int64)
165 Expressions *es = void;
166 if (auto ale = aggr.isArrayLiteralExp())
168 // Directly use the elements of the array for the TupleExp creation
169 es = ale.elements;
171 else
173 const length = cast(size_t)el.toInteger();
174 es = new Expressions(length);
175 foreach (i; 0 .. length)
177 auto index = new IntegerExp(loc, i, Type.tsize_t);
178 auto value = new IndexExp(aggr.loc, aggr, index);
179 (*es)[i] = value;
182 aggrfe.aggr = new TupleExp(aggr.loc, es);
183 aggrfe.aggr = aggrfe.aggr.expressionSemantic(sc);
184 aggrfe.aggr = aggrfe.aggr.optimize(WANTvalue);
185 aggrfe.aggr = aggrfe.aggr.ctfeInterpret();
187 else
189 aggrfe.aggr = ErrorExp.get();
193 /*****************************************
194 * Wrap a statement into a function literal and call it.
196 * Params:
197 * loc = The source location.
198 * s = The statement.
199 * Returns:
200 * AST of the expression `(){ s; }()` with location loc.
202 private extern(D) Expression wrapAndCall(const ref Loc loc, Statement s)
204 auto tf = new TypeFunction(ParameterList(), null, LINK.default_, 0);
205 auto fd = new FuncLiteralDeclaration(loc, loc, tf, TOK.reserved, null);
206 fd.fbody = s;
207 auto fe = new FuncExp(loc, fd);
208 auto ce = new CallExp(loc, fe, new Expressions());
209 return ce;
212 /*****************************************
213 * Create a `foreach` statement from `aggrefe/rangefe` with given
214 * `foreach` variables and body `s`.
216 * Params:
217 * loc = The source location.
218 * parameters = The foreach variables.
219 * s = The `foreach` body.
220 * Returns:
221 * `foreach (parameters; aggregate) s;` or
222 * `foreach (parameters; lower .. upper) s;`
223 * Where aggregate/lower, upper are as for the current StaticForeach.
225 private extern(D) Statement createForeach(const ref Loc loc, Parameters* parameters, Statement s)
227 if (aggrfe)
229 return new ForeachStatement(loc, aggrfe.op, parameters, aggrfe.aggr, s, loc);
231 else
233 assert(rangefe && parameters.length == 1);
234 return new ForeachRangeStatement(loc, rangefe.op, (*parameters)[0], rangefe.lwr, rangefe.upr, s, loc);
238 /*****************************************
239 * For a `static foreach` with multiple loop variables, the
240 * aggregate is lowered to an array of tuples. As D does not have
241 * built-in tuples, we need a suitable tuple type. This generates
242 * a `struct` that serves as the tuple type. This type is only
243 * used during CTFE and hence its typeinfo will not go to the
244 * object file.
246 * Params:
247 * loc = The source location.
248 * e = The expressions we wish to store in the tuple.
249 * sc = The current scope.
250 * Returns:
251 * A struct type of the form
252 * struct Tuple
254 * typeof(AliasSeq!(e)) tuple;
258 private extern(D) TypeStruct createTupleType(const ref Loc loc, Expressions* e, Scope* sc)
259 { // TODO: move to druntime?
260 auto sid = Identifier.generateId("Tuple");
261 auto sdecl = new StructDeclaration(loc, sid, false);
262 sdecl.storage_class |= STC.static_;
263 sdecl.members = new Dsymbols();
264 auto fid = Identifier.idPool(tupleFieldName);
265 auto ty = new TypeTypeof(loc, new TupleExp(loc, e));
266 sdecl.members.push(new VarDeclaration(loc, ty, fid, null, 0));
267 auto r = cast(TypeStruct)sdecl.type;
268 if (global.params.useTypeInfo && Type.dtypeinfo)
269 r.vtinfo = TypeInfoStructDeclaration.create(r); // prevent typeinfo from going to object file
270 return r;
273 /*****************************************
274 * Create the AST for an instantiation of a suitable tuple type.
276 * Params:
277 * loc = The source location.
278 * type = A Tuple type, created with createTupleType.
279 * e = The expressions we wish to store in the tuple.
280 * Returns:
281 * An AST for the expression `Tuple(e)`.
284 private extern(D) Expression createTuple(const ref Loc loc, TypeStruct type, Expressions* e) @safe
285 { // TODO: move to druntime?
286 return new CallExp(loc, new TypeExp(loc, type), e);
290 /*****************************************
291 * Lower any aggregate that is not an array to an array using a
292 * regular foreach loop within CTFE. If there are multiple
293 * `static foreach` loop variables, an array of tuples is
294 * generated. In thise case, the field `needExpansion` is set to
295 * true to indicate that the static foreach loop expansion will
296 * need to expand the tuples into multiple variables.
298 * For example, `static foreach (x; range) { ... }` is lowered to:
300 * static foreach (x; {
301 * typeof({
302 * foreach (x; range) return x;
303 * }())[] __res;
304 * foreach (x; range) __res ~= x;
305 * return __res;
306 * }()) { ... }
308 * Finally, call `lowerArrayAggregate` to turn the produced
309 * array into an expression tuple.
311 * Params:
312 * sc = The current scope.
315 private void lowerNonArrayAggregate(Scope* sc)
317 auto nvars = aggrfe ? aggrfe.parameters.length : 1;
318 auto aloc = aggrfe ? aggrfe.aggr.loc : rangefe.lwr.loc;
319 // We need three sets of foreach loop variables because the
320 // lowering contains three foreach loops.
321 Parameters*[3] pparams = [new Parameters(), new Parameters(), new Parameters()];
322 foreach (i; 0 .. nvars)
324 foreach (params; pparams)
326 auto p = aggrfe ? (*aggrfe.parameters)[i] : rangefe.prm;
327 params.push(new Parameter(aloc, p.storageClass, p.type, p.ident, null, null));
330 Expression[2] res;
331 TypeStruct tplty = null;
332 if (nvars == 1) // only one `static foreach` variable, generate identifiers.
334 foreach (i; 0 .. 2)
336 res[i] = new IdentifierExp(aloc, (*pparams[i])[0].ident);
339 else // multiple `static foreach` variables, generate tuples.
341 foreach (i; 0 .. 2)
343 auto e = new Expressions(pparams[0].length);
344 foreach (j, ref elem; *e)
346 auto p = (*pparams[i])[j];
347 elem = new IdentifierExp(aloc, p.ident);
349 if (!tplty)
351 tplty = createTupleType(aloc, e, sc);
353 res[i] = createTuple(aloc, tplty, e);
355 needExpansion = true; // need to expand the tuples later
357 // generate remaining code for the new aggregate which is an
358 // array (see documentation comment).
359 if (rangefe)
361 sc = sc.startCTFE();
362 rangefe.lwr = rangefe.lwr.expressionSemantic(sc);
363 rangefe.lwr = resolveProperties(sc, rangefe.lwr);
364 rangefe.upr = rangefe.upr.expressionSemantic(sc);
365 rangefe.upr = resolveProperties(sc, rangefe.upr);
366 sc = sc.endCTFE();
367 rangefe.lwr = rangefe.lwr.optimize(WANTvalue);
368 rangefe.lwr = rangefe.lwr.ctfeInterpret();
369 rangefe.upr = rangefe.upr.optimize(WANTvalue);
370 rangefe.upr = rangefe.upr.ctfeInterpret();
372 auto s1 = new Statements();
373 auto sfe = new Statements();
374 if (tplty) sfe.push(new ExpStatement(loc, tplty.sym));
375 sfe.push(new ReturnStatement(aloc, res[0]));
376 s1.push(createForeach(aloc, pparams[0], new CompoundStatement(aloc, sfe)));
377 s1.push(new ExpStatement(aloc, new AssertExp(aloc, IntegerExp.literal!0)));
378 Type ety = new TypeTypeof(aloc, wrapAndCall(aloc, new CompoundStatement(aloc, s1)));
379 auto aty = ety.arrayOf();
380 auto idres = Identifier.generateId("__res");
381 auto vard = new VarDeclaration(aloc, aty, idres, null, STC.temp);
382 auto s2 = new Statements();
384 // Run 'typeof' gagged to avoid duplicate errors and if it fails just create
385 // an empty foreach to expose them.
386 uint olderrors = global.startGagging();
387 ety = ety.typeSemantic(aloc, sc);
388 if (global.endGagging(olderrors))
389 s2.push(createForeach(aloc, pparams[1], null));
390 else
392 s2.push(new ExpStatement(aloc, vard));
393 auto catass = new CatAssignExp(aloc, new IdentifierExp(aloc, idres), res[1]);
394 s2.push(createForeach(aloc, pparams[1], new ExpStatement(aloc, catass)));
395 s2.push(new ReturnStatement(aloc, new IdentifierExp(aloc, idres)));
398 Expression aggr = void;
399 Type indexty = void;
401 if (rangefe && (indexty = ety).isintegral())
403 rangefe.lwr.type = indexty;
404 rangefe.upr.type = indexty;
405 auto lwrRange = getIntRange(rangefe.lwr);
406 auto uprRange = getIntRange(rangefe.upr);
408 const lwr = rangefe.lwr.toInteger();
409 auto upr = rangefe.upr.toInteger();
410 size_t length = 0;
412 if (lwrRange.imin <= uprRange.imax)
413 length = cast(size_t) (upr - lwr);
415 auto exps = new Expressions(length);
417 if (rangefe.op == TOK.foreach_)
419 foreach (i; 0 .. length)
420 (*exps)[i] = new IntegerExp(aloc, lwr + i, indexty);
422 else
424 --upr;
425 foreach (i; 0 .. length)
426 (*exps)[i] = new IntegerExp(aloc, upr - i, indexty);
428 aggr = new ArrayLiteralExp(aloc, indexty.arrayOf(), exps);
430 else
432 aggr = wrapAndCall(aloc, new CompoundStatement(aloc, s2));
433 sc = sc.startCTFE();
434 aggr = aggr.expressionSemantic(sc);
435 aggr = resolveProperties(sc, aggr);
436 sc = sc.endCTFE();
437 aggr = aggr.optimize(WANTvalue);
438 aggr = aggr.ctfeInterpret();
441 assert(!!aggrfe ^ !!rangefe);
442 aggrfe = new ForeachStatement(loc, TOK.foreach_, pparams[2], aggr,
443 aggrfe ? aggrfe._body : rangefe._body,
444 aggrfe ? aggrfe.endloc : rangefe.endloc);
445 rangefe = null;
446 lowerArrayAggregate(sc); // finally, turn generated array into expression tuple
449 /*****************************************
450 * Perform `static foreach` lowerings that are necessary in order
451 * to finally expand the `static foreach` using
452 * `dmd.statementsem.makeTupleForeach`.
454 extern(D) void prepare(Scope* sc)
456 assert(sc);
458 if (aggrfe)
460 sc = sc.startCTFE();
461 aggrfe.aggr = aggrfe.aggr.expressionSemantic(sc);
462 sc = sc.endCTFE();
465 if (aggrfe && aggrfe.aggr.type.toBasetype().ty == Terror)
467 return;
470 if (!ready())
472 if (aggrfe && aggrfe.aggr.type.toBasetype().ty == Tarray)
474 lowerArrayAggregate(sc);
476 else
478 lowerNonArrayAggregate(sc);
483 /*****************************************
484 * Returns:
485 * `true` iff ready to call `dmd.statementsem.makeTupleForeach`.
487 extern(D) bool ready()
489 return aggrfe && aggrfe.aggr && aggrfe.aggr.type && aggrfe.aggr.type.toBasetype().ty == Ttuple;
493 /***********************************************************
495 extern (C++) class DVCondition : Condition
497 uint level;
498 Identifier ident;
499 Module mod;
501 extern (D) this(const ref Loc loc, Module mod, uint level, Identifier ident) @safe
503 super(loc);
504 this.mod = mod;
505 this.level = level;
506 this.ident = ident;
509 override final DVCondition syntaxCopy()
511 return this; // don't need to copy
514 override void accept(Visitor v)
516 v.visit(this);
520 /***********************************************************
522 extern (C++) final class DebugCondition : DVCondition
525 * Add an user-supplied identifier to the list of global debug identifiers
527 * Can be called from either the driver or a `debug = Ident;` statement.
528 * Unlike version identifier, there isn't any reserved debug identifier
529 * so no validation takes place.
531 * Params:
532 * ident = identifier to add
534 deprecated("Kept for C++ compat - Use the string overload instead")
535 static void addGlobalIdent(const(char)* ident)
537 addGlobalIdent(ident[0 .. ident.strlen]);
540 /// Ditto
541 extern(D) static void addGlobalIdent(string ident)
543 // Overload necessary for string literals
544 addGlobalIdent(cast(const(char)[])ident);
548 /// Ditto
549 extern(D) static void addGlobalIdent(const(char)[] ident)
551 global.debugids.push(Identifier.idPool(ident));
556 * Instantiate a new `DebugCondition`
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
566 extern (D) this(const ref Loc loc, Module mod, uint level, Identifier ident) @safe
568 super(loc, mod, level, ident);
571 override int include(Scope* sc)
573 //printf("DebugCondition::include() level = %d, debuglevel = %d\n", level, global.params.debuglevel);
574 if (inc == Include.notComputed)
576 inc = Include.no;
577 bool definedInModule = false;
578 if (ident)
580 if (mod.debugids && findCondition(*mod.debugids, ident))
582 inc = Include.yes;
583 definedInModule = true;
585 else if (findCondition(global.debugids, ident))
586 inc = Include.yes;
587 else
589 if (!mod.debugidsNot)
590 mod.debugidsNot = new Identifiers();
591 mod.debugidsNot.push(ident);
594 else if (level <= global.params.debuglevel || level <= mod.debuglevel)
595 inc = Include.yes;
596 if (!definedInModule)
597 printDepsConditional(sc, this, "depsDebug ");
599 return (inc == Include.yes);
602 override inout(DebugCondition) isDebugCondition() inout
604 return this;
607 override void accept(Visitor v)
609 v.visit(this);
612 override const(char)* toChars() const
614 return ident ? ident.toChars() : "debug".ptr;
619 * Node to represent a version condition
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.
629 extern (C++) final class VersionCondition : DVCondition
632 * Check if a given version identifier is reserved.
634 * Params:
635 * ident = identifier being checked
637 * Returns:
638 * `true` if it is reserved, `false` otherwise
640 extern(D) private static bool isReserved(const(char)[] ident) @safe
642 // This list doesn't include "D_*" versions, see the last return
643 switch (ident)
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 "LoongArch32":
695 case "LoongArch64":
696 case "LoongArch_HardFloat":
697 case "LoongArch_SoftFloat":
698 case "MinGW":
699 case "MIPS32":
700 case "MIPS64":
701 case "MIPS_EABI":
702 case "MIPS_HardFloat":
703 case "MIPS_N32":
704 case "MIPS_N64":
705 case "MIPS_O32":
706 case "MIPS_O64":
707 case "MIPS_SoftFloat":
708 case "MSP430":
709 case "NetBSD":
710 case "none":
711 case "NVPTX":
712 case "NVPTX64":
713 case "OpenBSD":
714 case "OSX":
715 case "PlayStation":
716 case "PlayStation4":
717 case "Posix":
718 case "PPC":
719 case "PPC64":
720 case "PPC_HardFloat":
721 case "PPC_SoftFloat":
722 case "RISCV32":
723 case "RISCV64":
724 case "S390":
725 case "S390X":
726 case "SDC":
727 case "SH":
728 case "SkyOS":
729 case "Solaris":
730 case "SPARC":
731 case "SPARC64":
732 case "SPARC_HardFloat":
733 case "SPARC_SoftFloat":
734 case "SPARC_V8Plus":
735 case "SystemZ":
736 case "SysV3":
737 case "SysV4":
738 case "TVOS":
739 case "unittest":
740 case "VisionOS":
741 case "WASI":
742 case "WatchOS":
743 case "WebAssembly":
744 case "Win32":
745 case "Win64":
746 case "Windows":
747 case "X86":
748 case "X86_64":
749 return true;
751 default:
752 // Anything that starts with "D_" is reserved
753 return (ident.length >= 2 && ident[0 .. 2] == "D_");
758 * Raises an error if a version identifier is reserved.
760 * Called when setting a version identifier, e.g. `-version=identifier`
761 * parameter to the compiler or `version = Foo` in user code.
763 * Params:
764 * loc = Where the identifier is set
765 * ident = identifier being checked (ident[$] must be '\0')
767 extern(D) static void checkReserved(const ref Loc loc, const(char)[] ident)
769 if (isReserved(ident))
770 error(loc, "version identifier `%s` is reserved and cannot be set",
771 ident.ptr);
775 * Add an user-supplied global identifier to the list
777 * Only called from the driver for `-version=Ident` parameters.
778 * Will raise an error if the identifier is reserved.
780 * Params:
781 * ident = identifier to add
783 deprecated("Kept for C++ compat - Use the string overload instead")
784 static void addGlobalIdent(const(char)* ident)
786 addGlobalIdent(ident[0 .. ident.strlen]);
789 /// Ditto
790 extern(D) static void addGlobalIdent(string ident)
792 // Overload necessary for string literals
793 addGlobalIdent(cast(const(char)[])ident);
797 /// Ditto
798 extern(D) static void addGlobalIdent(const(char)[] ident)
800 checkReserved(Loc.initial, ident);
801 addPredefinedGlobalIdent(ident);
805 * Add any global identifier to the list, without checking
806 * if it's predefined
808 * Only called from the driver after platform detection,
809 * and internally.
811 * Params:
812 * ident = identifier to add (ident[$] must be '\0')
814 deprecated("Kept for C++ compat - Use the string overload instead")
815 static void addPredefinedGlobalIdent(const(char)* ident)
817 addPredefinedGlobalIdent(ident.toDString());
820 /// Ditto
821 extern(D) static void addPredefinedGlobalIdent(string ident)
823 // Forward: Overload necessary for string literal
824 addPredefinedGlobalIdent(cast(const(char)[])ident);
828 /// Ditto
829 extern(D) static void addPredefinedGlobalIdent(const(char)[] ident)
831 global.versionids.push(Identifier.idPool(ident));
835 * Instantiate a new `VersionCondition`
837 * Params:
838 * mod = Module this node belongs to
839 * level = Minimum global level this condition needs to pass.
840 * Only used if `ident` is `null`.
841 * ident = Identifier required for this condition to pass.
842 * If `null`, this conditiion will use an integer level.
843 * loc = Location in the source file
845 extern (D) this(const ref Loc loc, Module mod, uint level, Identifier ident) @safe
847 super(loc, mod, level, ident);
850 override int include(Scope* sc)
852 //printf("VersionCondition::include() level = %d, versionlevel = %d\n", level, global.params.versionlevel);
853 //if (ident) printf("\tident = '%s'\n", ident.toChars());
854 if (inc == Include.notComputed)
856 inc = Include.no;
857 bool definedInModule = false;
858 if (ident)
860 if (mod.versionids && findCondition(*mod.versionids, ident))
862 inc = Include.yes;
863 definedInModule = true;
865 else if (findCondition(global.versionids, ident))
866 inc = Include.yes;
867 else
869 if (!mod.versionidsNot)
870 mod.versionidsNot = new Identifiers();
871 mod.versionidsNot.push(ident);
874 else if (level <= global.params.versionlevel || level <= mod.versionlevel)
875 inc = Include.yes;
876 if (!definedInModule &&
877 (!ident || (!isReserved(ident.toString()) && ident != Id._unittest && ident != Id._assert)))
879 printDepsConditional(sc, this, "depsVersion ");
882 return (inc == Include.yes);
885 override inout(VersionCondition) isVersionCondition() inout
887 return this;
890 override void accept(Visitor v)
892 v.visit(this);
895 override const(char)* toChars() const
897 return ident ? ident.toChars() : "version".ptr;
901 /***********************************************************
903 extern (C++) final class StaticIfCondition : Condition
905 Expression exp;
907 extern (D) this(const ref Loc loc, Expression exp) @safe
909 super(loc);
910 this.exp = exp;
913 override StaticIfCondition syntaxCopy()
915 return new StaticIfCondition(loc, exp.syntaxCopy());
918 override int include(Scope* sc)
920 // printf("StaticIfCondition::include(sc = %p) this=%p inc = %d\n", sc, this, inc);
922 int errorReturn()
924 if (!global.gag)
925 inc = Include.no; // so we don't see the error message again
926 return 0;
929 if (inc == Include.notComputed)
931 if (!sc)
933 error(loc, "`static if` conditional cannot be at global scope");
934 inc = Include.no;
935 return 0;
938 import dmd.staticcond;
939 bool errors;
941 bool result = evalStaticCondition(sc, exp, exp, errors);
943 // Prevent repeated condition evaluation.
944 // See: fail_compilation/fail7815.d
945 if (inc != Include.notComputed)
946 return (inc == Include.yes);
947 if (errors)
948 return errorReturn();
949 if (result)
950 inc = Include.yes;
951 else
952 inc = Include.no;
954 return (inc == Include.yes);
957 override void accept(Visitor v)
959 v.visit(this);
962 override inout(StaticIfCondition) isStaticIfCondition() inout
964 return this;
967 override const(char)* toChars() const
969 return exp ? exp.toChars() : "static if".ptr;
974 /****************************************
975 * Find `ident` in an array of identifiers.
976 * Params:
977 * ids = array of identifiers
978 * ident = identifier to search for
979 * Returns:
980 * true if found
982 bool findCondition(ref Identifiers ids, Identifier ident) @safe nothrow pure
984 foreach (id; ids)
986 if (id == ident)
987 return true;
989 return false;
992 // Helper for printing dependency information
993 private void printDepsConditional(Scope* sc, DVCondition condition, const(char)[] depType)
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');