| /** | |
| * Evaluate compile-time conditionals, such as `static if` `version` and `debug`. | |
| * | |
| * Specification: $(LINK2 https://dlang.org/spec/version.html, Conditional Compilation) | |
| * | |
| * Copyright: Copyright (C) 1999-2022 by The D Language Foundation, All Rights Reserved | |
| * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) | |
| * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) | |
| * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/cond.d, _cond.d) | |
| * Documentation: https://dlang.org/phobos/dmd_cond.html | |
| * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/cond.d | |
| */ | |
| module dmd.cond; | |
| import core.stdc.string; | |
| import dmd.arraytypes; | |
| import dmd.astenums; | |
| import dmd.ast_node; | |
| import dmd.dcast; | |
| import dmd.dmodule; | |
| import dmd.dscope; | |
| import dmd.dsymbol; | |
| import dmd.errors; | |
| import dmd.expression; | |
| import dmd.expressionsem; | |
| import dmd.globals; | |
| import dmd.identifier; | |
| import dmd.mtype; | |
| import dmd.typesem; | |
| import dmd.common.outbuffer; | |
| import dmd.root.rootobject; | |
| import dmd.root.string; | |
| import dmd.tokens; | |
| import dmd.utils; | |
| import dmd.visitor; | |
| import dmd.id; | |
| import dmd.statement; | |
| import dmd.declaration; | |
| import dmd.dstruct; | |
| import dmd.func; | |
| /*********************************************************** | |
| */ | |
| enum Include : ubyte | |
| { | |
| notComputed, /// not computed yet | |
| yes, /// include the conditional code | |
| no, /// do not include the conditional code | |
| } | |
| extern (C++) abstract class Condition : ASTNode | |
| { | |
| Loc loc; | |
| Include inc; | |
| override final DYNCAST dyncast() const | |
| { | |
| return DYNCAST.condition; | |
| } | |
| extern (D) this(const ref Loc loc) | |
| { | |
| this.loc = loc; | |
| } | |
| abstract Condition syntaxCopy(); | |
| abstract int include(Scope* sc); | |
| inout(DebugCondition) isDebugCondition() inout | |
| { | |
| return null; | |
| } | |
| inout(VersionCondition) isVersionCondition() inout | |
| { | |
| return null; | |
| } | |
| override void accept(Visitor v) | |
| { | |
| v.visit(this); | |
| } | |
| } | |
| /*********************************************************** | |
| * Implements common functionality for StaticForeachDeclaration and | |
| * StaticForeachStatement This performs the necessary lowerings before | |
| * dmd.statementsem.makeTupleForeach can be used to expand the | |
| * corresponding `static foreach` declaration or statement. | |
| */ | |
| extern (C++) final class StaticForeach : RootObject | |
| { | |
| extern(D) static immutable tupleFieldName = "tuple"; // used in lowering | |
| Loc loc; | |
| /*************** | |
| * Not `null` iff the `static foreach` is over an aggregate. In | |
| * this case, it contains the corresponding ForeachStatement. For | |
| * StaticForeachDeclaration, the body is `null`. | |
| */ | |
| ForeachStatement aggrfe; | |
| /*************** | |
| * Not `null` iff the `static foreach` is over a range. Exactly | |
| * one of the `aggrefe` and `rangefe` fields is not null. See | |
| * `aggrfe` field for more details. | |
| */ | |
| ForeachRangeStatement rangefe; | |
| /*************** | |
| * true if it is necessary to expand a tuple into multiple | |
| * variables (see lowerNonArrayAggregate). | |
| */ | |
| bool needExpansion = false; | |
| extern (D) this(const ref Loc loc, ForeachStatement aggrfe, ForeachRangeStatement rangefe) | |
| { | |
| assert(!!aggrfe ^ !!rangefe); | |
| this.loc = loc; | |
| this.aggrfe = aggrfe; | |
| this.rangefe = rangefe; | |
| } | |
| StaticForeach syntaxCopy() | |
| { | |
| return new StaticForeach( | |
| loc, | |
| aggrfe ? aggrfe.syntaxCopy() : null, | |
| rangefe ? rangefe.syntaxCopy() : null | |
| ); | |
| } | |
| /***************************************** | |
| * Turn an aggregate which is an array into an expression tuple | |
| * of its elements. I.e., lower | |
| * static foreach (x; [1, 2, 3, 4]) { ... } | |
| * to | |
| * static foreach (x; AliasSeq!(1, 2, 3, 4)) { ... } | |
| */ | |
| private extern(D) void lowerArrayAggregate(Scope* sc) | |
| { | |
| auto aggr = aggrfe.aggr; | |
| Expression el = new ArrayLengthExp(aggr.loc, aggr); | |
| sc = sc.startCTFE(); | |
| el = el.expressionSemantic(sc); | |
| sc = sc.endCTFE(); | |
| el = el.optimize(WANTvalue); | |
| el = el.ctfeInterpret(); | |
| if (el.op == EXP.int64) | |
| { | |
| Expressions *es = void; | |
| if (auto ale = aggr.isArrayLiteralExp()) | |
| { | |
| // Directly use the elements of the array for the TupleExp creation | |
| es = ale.elements; | |
| } | |
| else | |
| { | |
| const length = cast(size_t)el.toInteger(); | |
| es = new Expressions(length); | |
| foreach (i; 0 .. length) | |
| { | |
| auto index = new IntegerExp(loc, i, Type.tsize_t); | |
| auto value = new IndexExp(aggr.loc, aggr, index); | |
| (*es)[i] = value; | |
| } | |
| } | |
| aggrfe.aggr = new TupleExp(aggr.loc, es); | |
| aggrfe.aggr = aggrfe.aggr.expressionSemantic(sc); | |
| aggrfe.aggr = aggrfe.aggr.optimize(WANTvalue); | |
| aggrfe.aggr = aggrfe.aggr.ctfeInterpret(); | |
| } | |
| else | |
| { | |
| aggrfe.aggr = ErrorExp.get(); | |
| } | |
| } | |
| /***************************************** | |
| * Wrap a statement into a function literal and call it. | |
| * | |
| * Params: | |
| * loc = The source location. | |
| * s = The statement. | |
| * Returns: | |
| * AST of the expression `(){ s; }()` with location loc. | |
| */ | |
| private extern(D) Expression wrapAndCall(const ref Loc loc, Statement s) | |
| { | |
| auto tf = new TypeFunction(ParameterList(), null, LINK.default_, 0); | |
| auto fd = new FuncLiteralDeclaration(loc, loc, tf, TOK.reserved, null); | |
| fd.fbody = s; | |
| auto fe = new FuncExp(loc, fd); | |
| auto ce = new CallExp(loc, fe, new Expressions()); | |
| return ce; | |
| } | |
| /***************************************** | |
| * Create a `foreach` statement from `aggrefe/rangefe` with given | |
| * `foreach` variables and body `s`. | |
| * | |
| * Params: | |
| * loc = The source location. | |
| * parameters = The foreach variables. | |
| * s = The `foreach` body. | |
| * Returns: | |
| * `foreach (parameters; aggregate) s;` or | |
| * `foreach (parameters; lower .. upper) s;` | |
| * Where aggregate/lower, upper are as for the current StaticForeach. | |
| */ | |
| private extern(D) Statement createForeach(const ref Loc loc, Parameters* parameters, Statement s) | |
| { | |
| if (aggrfe) | |
| { | |
| return new ForeachStatement(loc, aggrfe.op, parameters, aggrfe.aggr.syntaxCopy(), s, loc); | |
| } | |
| else | |
| { | |
| assert(rangefe && parameters.dim == 1); | |
| return new ForeachRangeStatement(loc, rangefe.op, (*parameters)[0], rangefe.lwr.syntaxCopy(), rangefe.upr.syntaxCopy(), s, loc); | |
| } | |
| } | |
| /***************************************** | |
| * For a `static foreach` with multiple loop variables, the | |
| * aggregate is lowered to an array of tuples. As D does not have | |
| * built-in tuples, we need a suitable tuple type. This generates | |
| * a `struct` that serves as the tuple type. This type is only | |
| * used during CTFE and hence its typeinfo will not go to the | |
| * object file. | |
| * | |
| * Params: | |
| * loc = The source location. | |
| * e = The expressions we wish to store in the tuple. | |
| * sc = The current scope. | |
| * Returns: | |
| * A struct type of the form | |
| * struct Tuple | |
| * { | |
| * typeof(AliasSeq!(e)) tuple; | |
| * } | |
| */ | |
| private extern(D) TypeStruct createTupleType(const ref Loc loc, Expressions* e, Scope* sc) | |
| { // TODO: move to druntime? | |
| auto sid = Identifier.generateId("Tuple"); | |
| auto sdecl = new StructDeclaration(loc, sid, false); | |
| sdecl.storage_class |= STC.static_; | |
| sdecl.members = new Dsymbols(); | |
| auto fid = Identifier.idPool(tupleFieldName.ptr, tupleFieldName.length); | |
| auto ty = new TypeTypeof(loc, new TupleExp(loc, e)); | |
| sdecl.members.push(new VarDeclaration(loc, ty, fid, null, 0)); | |
| auto r = cast(TypeStruct)sdecl.type; | |
| if (global.params.useTypeInfo && Type.dtypeinfo) | |
| r.vtinfo = TypeInfoStructDeclaration.create(r); // prevent typeinfo from going to object file | |
| return r; | |
| } | |
| /***************************************** | |
| * Create the AST for an instantiation of a suitable tuple type. | |
| * | |
| * Params: | |
| * loc = The source location. | |
| * type = A Tuple type, created with createTupleType. | |
| * e = The expressions we wish to store in the tuple. | |
| * Returns: | |
| * An AST for the expression `Tuple(e)`. | |
| */ | |
| private extern(D) Expression createTuple(const ref Loc loc, TypeStruct type, Expressions* e) | |
| { // TODO: move to druntime? | |
| return new CallExp(loc, new TypeExp(loc, type), e); | |
| } | |
| /***************************************** | |
| * Lower any aggregate that is not an array to an array using a | |
| * regular foreach loop within CTFE. If there are multiple | |
| * `static foreach` loop variables, an array of tuples is | |
| * generated. In thise case, the field `needExpansion` is set to | |
| * true to indicate that the static foreach loop expansion will | |
| * need to expand the tuples into multiple variables. | |
| * | |
| * For example, `static foreach (x; range) { ... }` is lowered to: | |
| * | |
| * static foreach (x; { | |
| * typeof({ | |
| * foreach (x; range) return x; | |
| * }())[] __res; | |
| * foreach (x; range) __res ~= x; | |
| * return __res; | |
| * }()) { ... } | |
| * | |
| * Finally, call `lowerArrayAggregate` to turn the produced | |
| * array into an expression tuple. | |
| * | |
| * Params: | |
| * sc = The current scope. | |
| */ | |
| private void lowerNonArrayAggregate(Scope* sc) | |
| { | |
| auto nvars = aggrfe ? aggrfe.parameters.dim : 1; | |
| auto aloc = aggrfe ? aggrfe.aggr.loc : rangefe.lwr.loc; | |
| // We need three sets of foreach loop variables because the | |
| // lowering contains three foreach loops. | |
| Parameters*[3] pparams = [new Parameters(), new Parameters(), new Parameters()]; | |
| foreach (i; 0 .. nvars) | |
| { | |
| foreach (params; pparams) | |
| { | |
| auto p = aggrfe ? (*aggrfe.parameters)[i] : rangefe.prm; | |
| params.push(new Parameter(p.storageClass, p.type, p.ident, null, null)); | |
| } | |
| } | |
| Expression[2] res; | |
| TypeStruct tplty = null; | |
| if (nvars == 1) // only one `static foreach` variable, generate identifiers. | |
| { | |
| foreach (i; 0 .. 2) | |
| { | |
| res[i] = new IdentifierExp(aloc, (*pparams[i])[0].ident); | |
| } | |
| } | |
| else // multiple `static foreach` variables, generate tuples. | |
| { | |
| foreach (i; 0 .. 2) | |
| { | |
| auto e = new Expressions(pparams[0].dim); | |
| foreach (j, ref elem; *e) | |
| { | |
| auto p = (*pparams[i])[j]; | |
| elem = new IdentifierExp(aloc, p.ident); | |
| } | |
| if (!tplty) | |
| { | |
| tplty = createTupleType(aloc, e, sc); | |
| } | |
| res[i] = createTuple(aloc, tplty, e); | |
| } | |
| needExpansion = true; // need to expand the tuples later | |
| } | |
| // generate remaining code for the new aggregate which is an | |
| // array (see documentation comment). | |
| if (rangefe) | |
| { | |
| sc = sc.startCTFE(); | |
| rangefe.lwr = rangefe.lwr.expressionSemantic(sc); | |
| rangefe.lwr = resolveProperties(sc, rangefe.lwr); | |
| rangefe.upr = rangefe.upr.expressionSemantic(sc); | |
| rangefe.upr = resolveProperties(sc, rangefe.upr); | |
| sc = sc.endCTFE(); | |
| rangefe.lwr = rangefe.lwr.optimize(WANTvalue); | |
| rangefe.lwr = rangefe.lwr.ctfeInterpret(); | |
| rangefe.upr = rangefe.upr.optimize(WANTvalue); | |
| rangefe.upr = rangefe.upr.ctfeInterpret(); | |
| } | |
| auto s1 = new Statements(); | |
| auto sfe = new Statements(); | |
| if (tplty) sfe.push(new ExpStatement(loc, tplty.sym)); | |
| sfe.push(new ReturnStatement(aloc, res[0])); | |
| s1.push(createForeach(aloc, pparams[0], new CompoundStatement(aloc, sfe))); | |
| s1.push(new ExpStatement(aloc, new AssertExp(aloc, IntegerExp.literal!0))); | |
| Type ety = new TypeTypeof(aloc, wrapAndCall(aloc, new CompoundStatement(aloc, s1))); | |
| auto aty = ety.arrayOf(); | |
| auto idres = Identifier.generateId("__res"); | |
| auto vard = new VarDeclaration(aloc, aty, idres, null); | |
| auto s2 = new Statements(); | |
| // Run 'typeof' gagged to avoid duplicate errors and if it fails just create | |
| // an empty foreach to expose them. | |
| uint olderrors = global.startGagging(); | |
| ety = ety.typeSemantic(aloc, sc); | |
| if (global.endGagging(olderrors)) | |
| s2.push(createForeach(aloc, pparams[1], null)); | |
| else | |
| { | |
| s2.push(new ExpStatement(aloc, vard)); | |
| auto catass = new CatAssignExp(aloc, new IdentifierExp(aloc, idres), res[1]); | |
| s2.push(createForeach(aloc, pparams[1], new ExpStatement(aloc, catass))); | |
| s2.push(new ReturnStatement(aloc, new IdentifierExp(aloc, idres))); | |
| } | |
| Expression aggr = void; | |
| Type indexty = void; | |
| if (rangefe && (indexty = ety).isintegral()) | |
| { | |
| rangefe.lwr.type = indexty; | |
| rangefe.upr.type = indexty; | |
| auto lwrRange = getIntRange(rangefe.lwr); | |
| auto uprRange = getIntRange(rangefe.upr); | |
| const lwr = rangefe.lwr.toInteger(); | |
| auto upr = rangefe.upr.toInteger(); | |
| size_t length = 0; | |
| if (lwrRange.imin <= uprRange.imax) | |
| length = cast(size_t) (upr - lwr); | |
| auto exps = new Expressions(length); | |
| if (rangefe.op == TOK.foreach_) | |
| { | |
| foreach (i; 0 .. length) | |
| (*exps)[i] = new IntegerExp(aloc, lwr + i, indexty); | |
| } | |
| else | |
| { | |
| --upr; | |
| foreach (i; 0 .. length) | |
| (*exps)[i] = new IntegerExp(aloc, upr - i, indexty); | |
| } | |
| aggr = new ArrayLiteralExp(aloc, indexty.arrayOf(), exps); | |
| } | |
| else | |
| { | |
| aggr = wrapAndCall(aloc, new CompoundStatement(aloc, s2)); | |
| sc = sc.startCTFE(); | |
| aggr = aggr.expressionSemantic(sc); | |
| aggr = resolveProperties(sc, aggr); | |
| sc = sc.endCTFE(); | |
| aggr = aggr.optimize(WANTvalue); | |
| aggr = aggr.ctfeInterpret(); | |
| } | |
| assert(!!aggrfe ^ !!rangefe); | |
| aggrfe = new ForeachStatement(loc, TOK.foreach_, pparams[2], aggr, | |
| aggrfe ? aggrfe._body : rangefe._body, | |
| aggrfe ? aggrfe.endloc : rangefe.endloc); | |
| rangefe = null; | |
| lowerArrayAggregate(sc); // finally, turn generated array into expression tuple | |
| } | |
| /***************************************** | |
| * Perform `static foreach` lowerings that are necessary in order | |
| * to finally expand the `static foreach` using | |
| * `dmd.statementsem.makeTupleForeach`. | |
| */ | |
| extern(D) void prepare(Scope* sc) | |
| { | |
| assert(sc); | |
| if (aggrfe) | |
| { | |
| sc = sc.startCTFE(); | |
| aggrfe.aggr = aggrfe.aggr.expressionSemantic(sc); | |
| sc = sc.endCTFE(); | |
| } | |
| if (aggrfe && aggrfe.aggr.type.toBasetype().ty == Terror) | |
| { | |
| return; | |
| } | |
| if (!ready()) | |
| { | |
| if (aggrfe && aggrfe.aggr.type.toBasetype().ty == Tarray) | |
| { | |
| lowerArrayAggregate(sc); | |
| } | |
| else | |
| { | |
| lowerNonArrayAggregate(sc); | |
| } | |
| } | |
| } | |
| /***************************************** | |
| * Returns: | |
| * `true` iff ready to call `dmd.statementsem.makeTupleForeach`. | |
| */ | |
| extern(D) bool ready() | |
| { | |
| return aggrfe && aggrfe.aggr && aggrfe.aggr.type && aggrfe.aggr.type.toBasetype().ty == Ttuple; | |
| } | |
| } | |
| /*********************************************************** | |
| */ | |
| extern (C++) class DVCondition : Condition | |
| { | |
| uint level; | |
| Identifier ident; | |
| Module mod; | |
| extern (D) this(const ref Loc loc, Module mod, uint level, Identifier ident) | |
| { | |
| super(loc); | |
| this.mod = mod; | |
| this.level = level; | |
| this.ident = ident; | |
| } | |
| override final DVCondition syntaxCopy() | |
| { | |
| return this; // don't need to copy | |
| } | |
| override void accept(Visitor v) | |
| { | |
| v.visit(this); | |
| } | |
| } | |
| /*********************************************************** | |
| */ | |
| extern (C++) final class DebugCondition : DVCondition | |
| { | |
| /** | |
| * Add an user-supplied identifier to the list of global debug identifiers | |
| * | |
| * Can be called from either the driver or a `debug = Ident;` statement. | |
| * Unlike version identifier, there isn't any reserved debug identifier | |
| * so no validation takes place. | |
| * | |
| * Params: | |
| * ident = identifier to add | |
| */ | |
| deprecated("Kept for C++ compat - Use the string overload instead") | |
| static void addGlobalIdent(const(char)* ident) | |
| { | |
| addGlobalIdent(ident[0 .. ident.strlen]); | |
| } | |
| /// Ditto | |
| extern(D) static void addGlobalIdent(string ident) | |
| { | |
| // Overload necessary for string literals | |
| addGlobalIdent(cast(const(char)[])ident); | |
| } | |
| /// Ditto | |
| extern(D) static void addGlobalIdent(const(char)[] ident) | |
| { | |
| if (!global.debugids) | |
| global.debugids = new Identifiers(); | |
| global.debugids.push(Identifier.idPool(ident)); | |
| } | |
| /** | |
| * Instantiate a new `DebugCondition` | |
| * | |
| * Params: | |
| * mod = Module this node belongs to | |
| * level = Minimum global level this condition needs to pass. | |
| * Only used if `ident` is `null`. | |
| * ident = Identifier required for this condition to pass. | |
| * If `null`, this conditiion will use an integer level. | |
| * loc = Location in the source file | |
| */ | |
| extern (D) this(const ref Loc loc, Module mod, uint level, Identifier ident) | |
| { | |
| super(loc, mod, level, ident); | |
| } | |
| override int include(Scope* sc) | |
| { | |
| //printf("DebugCondition::include() level = %d, debuglevel = %d\n", level, global.params.debuglevel); | |
| if (inc == Include.notComputed) | |
| { | |
| inc = Include.no; | |
| bool definedInModule = false; | |
| if (ident) | |
| { | |
| if (findCondition(mod.debugids, ident)) | |
| { | |
| inc = Include.yes; | |
| definedInModule = true; | |
| } | |
| else if (findCondition(global.debugids, ident)) | |
| inc = Include.yes; | |
| else | |
| { | |
| if (!mod.debugidsNot) | |
| mod.debugidsNot = new Identifiers(); | |
| mod.debugidsNot.push(ident); | |
| } | |
| } | |
| else if (level <= global.params.debuglevel || level <= mod.debuglevel) | |
| inc = Include.yes; | |
| if (!definedInModule) | |
| printDepsConditional(sc, this, "depsDebug "); | |
| } | |
| return (inc == Include.yes); | |
| } | |
| override inout(DebugCondition) isDebugCondition() inout | |
| { | |
| return this; | |
| } | |
| override void accept(Visitor v) | |
| { | |
| v.visit(this); | |
| } | |
| override const(char)* toChars() const | |
| { | |
| return ident ? ident.toChars() : "debug".ptr; | |
| } | |
| } | |
| /** | |
| * Node to represent a version condition | |
| * | |
| * A version condition is of the form: | |
| * --- | |
| * version (Identifier) | |
| * --- | |
| * In user code. | |
| * This class also provides means to add version identifier | |
| * to the list of global (cross module) identifiers. | |
| */ | |
| extern (C++) final class VersionCondition : DVCondition | |
| { | |
| /** | |
| * Check if a given version identifier is reserved. | |
| * | |
| * Params: | |
| * ident = identifier being checked | |
| * | |
| * Returns: | |
| * `true` if it is reserved, `false` otherwise | |
| */ | |
| extern(D) private static bool isReserved(const(char)[] ident) | |
| { | |
| // This list doesn't include "D_*" versions, see the last return | |
| switch (ident) | |
| { | |
| case "AArch64": | |
| case "AIX": | |
| case "all": | |
| case "Alpha": | |
| case "Alpha_HardFloat": | |
| case "Alpha_SoftFloat": | |
| case "Android": | |
| case "ARM": | |
| case "ARM_HardFloat": | |
| case "ARM_SoftFloat": | |
| case "ARM_SoftFP": | |
| case "ARM_Thumb": | |
| case "AsmJS": | |
| case "assert": | |
| case "AVR": | |
| case "BigEndian": | |
| case "BSD": | |
| case "CppRuntime_Clang": | |
| case "CppRuntime_DigitalMars": | |
| case "CppRuntime_Gcc": | |
| case "CppRuntime_Microsoft": | |
| case "CppRuntime_Sun": | |
| case "CRuntime_Bionic": | |
| case "CRuntime_DigitalMars": | |
| case "CRuntime_Glibc": | |
| case "CRuntime_Microsoft": | |
| case "CRuntime_Musl": | |
| case "CRuntime_Newlib": | |
| case "CRuntime_UClibc": | |
| case "CRuntime_WASI": | |
| case "Cygwin": | |
| case "DigitalMars": | |
| case "DragonFlyBSD": | |
| case "Emscripten": | |
| case "ELFv1": | |
| case "ELFv2": | |
| case "Epiphany": | |
| case "FreeBSD": | |
| case "FreeStanding": | |
| case "GNU": | |
| case "Haiku": | |
| case "HPPA": | |
| case "HPPA64": | |
| case "Hurd": | |
| case "IA64": | |
| case "iOS": | |
| case "LDC": | |
| case "linux": | |
| case "LittleEndian": | |
| case "MinGW": | |
| case "MIPS32": | |
| case "MIPS64": | |
| case "MIPS_EABI": | |
| case "MIPS_HardFloat": | |
| case "MIPS_N32": | |
| case "MIPS_N64": | |
| case "MIPS_O32": | |
| case "MIPS_O64": | |
| case "MIPS_SoftFloat": | |
| case "MSP430": | |
| case "NetBSD": | |
| case "none": | |
| case "NVPTX": | |
| case "NVPTX64": | |
| case "OpenBSD": | |
| case "OSX": | |
| case "PlayStation": | |
| case "PlayStation4": | |
| case "Posix": | |
| case "PPC": | |
| case "PPC64": | |
| case "PPC_HardFloat": | |
| case "PPC_SoftFloat": | |
| case "RISCV32": | |
| case "RISCV64": | |
| case "S390": | |
| case "S390X": | |
| case "SDC": | |
| case "SH": | |
| case "SkyOS": | |
| case "Solaris": | |
| case "SPARC": | |
| case "SPARC64": | |
| case "SPARC_HardFloat": | |
| case "SPARC_SoftFloat": | |
| case "SPARC_V8Plus": | |
| case "SystemZ": | |
| case "SysV3": | |
| case "SysV4": | |
| case "TVOS": | |
| case "unittest": | |
| case "WASI": | |
| case "WatchOS": | |
| case "WebAssembly": | |
| case "Win32": | |
| case "Win64": | |
| case "Windows": | |
| case "X86": | |
| case "X86_64": | |
| return true; | |
| default: | |
| // Anything that starts with "D_" is reserved | |
| return (ident.length >= 2 && ident[0 .. 2] == "D_"); | |
| } | |
| } | |
| /** | |
| * Raises an error if a version identifier is reserved. | |
| * | |
| * Called when setting a version identifier, e.g. `-version=identifier` | |
| * parameter to the compiler or `version = Foo` in user code. | |
| * | |
| * Params: | |
| * loc = Where the identifier is set | |
| * ident = identifier being checked (ident[$] must be '\0') | |
| */ | |
| extern(D) static void checkReserved(const ref Loc loc, const(char)[] ident) | |
| { | |
| if (isReserved(ident)) | |
| error(loc, "version identifier `%s` is reserved and cannot be set", | |
| ident.ptr); | |
| } | |
| /** | |
| * Add an user-supplied global identifier to the list | |
| * | |
| * Only called from the driver for `-version=Ident` parameters. | |
| * Will raise an error if the identifier is reserved. | |
| * | |
| * Params: | |
| * ident = identifier to add | |
| */ | |
| deprecated("Kept for C++ compat - Use the string overload instead") | |
| static void addGlobalIdent(const(char)* ident) | |
| { | |
| addGlobalIdent(ident[0 .. ident.strlen]); | |
| } | |
| /// Ditto | |
| extern(D) static void addGlobalIdent(string ident) | |
| { | |
| // Overload necessary for string literals | |
| addGlobalIdent(cast(const(char)[])ident); | |
| } | |
| /// Ditto | |
| extern(D) static void addGlobalIdent(const(char)[] ident) | |
| { | |
| checkReserved(Loc.initial, ident); | |
| addPredefinedGlobalIdent(ident); | |
| } | |
| /** | |
| * Add any global identifier to the list, without checking | |
| * if it's predefined | |
| * | |
| * Only called from the driver after platform detection, | |
| * and internally. | |
| * | |
| * Params: | |
| * ident = identifier to add (ident[$] must be '\0') | |
| */ | |
| deprecated("Kept for C++ compat - Use the string overload instead") | |
| static void addPredefinedGlobalIdent(const(char)* ident) | |
| { | |
| addPredefinedGlobalIdent(ident.toDString()); | |
| } | |
| /// Ditto | |
| extern(D) static void addPredefinedGlobalIdent(string ident) | |
| { | |
| // Forward: Overload necessary for string literal | |
| addPredefinedGlobalIdent(cast(const(char)[])ident); | |
| } | |
| /// Ditto | |
| extern(D) static void addPredefinedGlobalIdent(const(char)[] ident) | |
| { | |
| if (!global.versionids) | |
| global.versionids = new Identifiers(); | |
| global.versionids.push(Identifier.idPool(ident)); | |
| } | |
| /** | |
| * Instantiate a new `VersionCondition` | |
| * | |
| * Params: | |
| * mod = Module this node belongs to | |
| * level = Minimum global level this condition needs to pass. | |
| * Only used if `ident` is `null`. | |
| * ident = Identifier required for this condition to pass. | |
| * If `null`, this conditiion will use an integer level. | |
| * loc = Location in the source file | |
| */ | |
| extern (D) this(const ref Loc loc, Module mod, uint level, Identifier ident) | |
| { | |
| super(loc, mod, level, ident); | |
| } | |
| override int include(Scope* sc) | |
| { | |
| //printf("VersionCondition::include() level = %d, versionlevel = %d\n", level, global.params.versionlevel); | |
| //if (ident) printf("\tident = '%s'\n", ident.toChars()); | |
| if (inc == Include.notComputed) | |
| { | |
| inc = Include.no; | |
| bool definedInModule = false; | |
| if (ident) | |
| { | |
| if (findCondition(mod.versionids, ident)) | |
| { | |
| inc = Include.yes; | |
| definedInModule = true; | |
| } | |
| else if (findCondition(global.versionids, ident)) | |
| inc = Include.yes; | |
| else | |
| { | |
| if (!mod.versionidsNot) | |
| mod.versionidsNot = new Identifiers(); | |
| mod.versionidsNot.push(ident); | |
| } | |
| } | |
| else if (level <= global.params.versionlevel || level <= mod.versionlevel) | |
| inc = Include.yes; | |
| if (!definedInModule && | |
| (!ident || (!isReserved(ident.toString()) && ident != Id._unittest && ident != Id._assert))) | |
| { | |
| printDepsConditional(sc, this, "depsVersion "); | |
| } | |
| } | |
| return (inc == Include.yes); | |
| } | |
| override inout(VersionCondition) isVersionCondition() inout | |
| { | |
| return this; | |
| } | |
| override void accept(Visitor v) | |
| { | |
| v.visit(this); | |
| } | |
| override const(char)* toChars() const | |
| { | |
| return ident ? ident.toChars() : "version".ptr; | |
| } | |
| } | |
| /*********************************************************** | |
| */ | |
| extern (C++) final class StaticIfCondition : Condition | |
| { | |
| Expression exp; | |
| extern (D) this(const ref Loc loc, Expression exp) | |
| { | |
| super(loc); | |
| this.exp = exp; | |
| } | |
| override StaticIfCondition syntaxCopy() | |
| { | |
| return new StaticIfCondition(loc, exp.syntaxCopy()); | |
| } | |
| override int include(Scope* sc) | |
| { | |
| // printf("StaticIfCondition::include(sc = %p) this=%p inc = %d\n", sc, this, inc); | |
| int errorReturn() | |
| { | |
| if (!global.gag) | |
| inc = Include.no; // so we don't see the error message again | |
| return 0; | |
| } | |
| if (inc == Include.notComputed) | |
| { | |
| if (!sc) | |
| { | |
| error(loc, "`static if` conditional cannot be at global scope"); | |
| inc = Include.no; | |
| return 0; | |
| } | |
| import dmd.staticcond; | |
| bool errors; | |
| if (!exp) | |
| return errorReturn(); | |
| bool result = evalStaticCondition(sc, exp, exp, errors); | |
| // Prevent repeated condition evaluation. | |
| // See: fail_compilation/fail7815.d | |
| if (inc != Include.notComputed) | |
| return (inc == Include.yes); | |
| if (errors) | |
| return errorReturn(); | |
| if (result) | |
| inc = Include.yes; | |
| else | |
| inc = Include.no; | |
| } | |
| return (inc == Include.yes); | |
| } | |
| override void accept(Visitor v) | |
| { | |
| v.visit(this); | |
| } | |
| override const(char)* toChars() const | |
| { | |
| return exp ? exp.toChars() : "static if".ptr; | |
| } | |
| } | |
| /**************************************** | |
| * Find `ident` in an array of identifiers. | |
| * Params: | |
| * ids = array of identifiers | |
| * ident = identifier to search for | |
| * Returns: | |
| * true if found | |
| */ | |
| bool findCondition(Identifiers* ids, Identifier ident) @safe nothrow pure | |
| { | |
| if (ids) | |
| { | |
| foreach (id; *ids) | |
| { | |
| if (id == ident) | |
| return true; | |
| } | |
| } | |
| return false; | |
| } | |
| // Helper for printing dependency information | |
| private void printDepsConditional(Scope* sc, DVCondition condition, const(char)[] depType) | |
| { | |
| if (!global.params.moduleDeps || global.params.moduleDepsFile) | |
| return; | |
| OutBuffer* ob = global.params.moduleDeps; | |
| Module imod = sc ? sc._module : condition.mod; | |
| if (!imod) | |
| return; | |
| ob.writestring(depType); | |
| ob.writestring(imod.toPrettyChars()); | |
| ob.writestring(" ("); | |
| escapePath(ob, imod.srcfile.toChars()); | |
| ob.writestring(") : "); | |
| if (condition.ident) | |
| ob.writestring(condition.ident.toString()); | |
| else | |
| ob.print(condition.level); | |
| ob.writeByte('\n'); | |
| } |