| /** |
| * 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, STC.temp); |
| 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.buffer || global.params.moduleDeps.name) |
| return; |
| OutBuffer* ob = global.params.moduleDeps.buffer; |
| 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'); |
| } |