| |
| /* Compiler implementation of the D programming language |
| * Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved |
| * written by Walter Bright |
| * http://www.digitalmars.com |
| * Distributed under the Boost Software License, Version 1.0. |
| * http://www.boost.org/LICENSE_1_0.txt |
| * https://github.com/D-Programming-Language/dmd/blob/master/src/cond.c |
| */ |
| |
| #include "root/dsystem.h" // strcmp() |
| |
| #include "mars.h" |
| #include "id.h" |
| #include "init.h" |
| #include "aggregate.h" |
| #include "declaration.h" |
| #include "identifier.h" |
| #include "expression.h" |
| #include "cond.h" |
| #include "module.h" |
| #include "template.h" |
| #include "mtype.h" |
| #include "scope.h" |
| #include "statement.h" |
| #include "arraytypes.h" |
| #include "tokens.h" |
| |
| bool evalStaticCondition(Scope *sc, Expression *exp, Expression *e, bool &errors); |
| |
| int findCondition(Identifiers *ids, Identifier *ident) |
| { |
| if (ids) |
| { |
| for (size_t i = 0; i < ids->length; i++) |
| { |
| Identifier *id = (*ids)[i]; |
| |
| if (id == ident) |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| /* ============================================================ */ |
| |
| Condition::Condition(Loc loc) |
| { |
| this->loc = loc; |
| inc = 0; |
| } |
| |
| /* ============================================================ */ |
| |
| StaticForeach::StaticForeach(Loc loc, ForeachStatement *aggrfe, ForeachRangeStatement *rangefe) |
| { |
| assert(!!aggrfe ^ !!rangefe); |
| this->loc = loc; |
| this->aggrfe = aggrfe; |
| this->rangefe = rangefe; |
| this->needExpansion = false; |
| } |
| |
| StaticForeach *StaticForeach::syntaxCopy() |
| { |
| return new StaticForeach( |
| loc, |
| aggrfe ? (ForeachStatement *)aggrfe->syntaxCopy() : NULL, |
| rangefe ? (ForeachRangeStatement *)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)) { ... } |
| */ |
| |
| static void lowerArrayAggregate(StaticForeach *sfe, Scope *sc) |
| { |
| Expression *aggr = sfe->aggrfe->aggr; |
| Expression *el = new ArrayLengthExp(aggr->loc, aggr); |
| sc = sc->startCTFE(); |
| el = expressionSemantic(el, sc); |
| sc = sc->endCTFE(); |
| el = el->optimize(WANTvalue); |
| el = el->ctfeInterpret(); |
| if (el->op == TOKint64) |
| { |
| Expressions *es; |
| if (ArrayLiteralExp *ale = aggr->isArrayLiteralExp()) |
| { |
| // Directly use the elements of the array for the TupleExp creation |
| es = ale->elements; |
| } |
| else |
| { |
| size_t length = (size_t)el->toInteger(); |
| es = new Expressions(); |
| es->setDim(length); |
| for (size_t i = 0; i < length; i++) |
| { |
| IntegerExp *index = new IntegerExp(sfe->loc, i, Type::tsize_t); |
| Expression *value = new IndexExp(aggr->loc, aggr, index); |
| (*es)[i] = value; |
| } |
| } |
| sfe->aggrfe->aggr = new TupleExp(aggr->loc, es); |
| sfe->aggrfe->aggr = expressionSemantic(sfe->aggrfe->aggr, sc); |
| sfe->aggrfe->aggr = sfe->aggrfe->aggr->optimize(WANTvalue); |
| sfe->aggrfe->aggr = sfe->aggrfe->aggr->ctfeInterpret(); |
| } |
| else |
| { |
| sfe->aggrfe->aggr = new ErrorExp(); |
| } |
| } |
| |
| /***************************************** |
| * 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. |
| */ |
| |
| static Expression *wrapAndCall(Loc loc, Statement *s) |
| { |
| TypeFunction *tf = new TypeFunction(ParameterList(), NULL, LINKdefault, 0); |
| FuncLiteralDeclaration *fd = new FuncLiteralDeclaration(loc, loc, tf, TOKreserved, NULL); |
| fd->fbody = s; |
| FuncExp *fe = new FuncExp(loc, fd); |
| Expression *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. |
| */ |
| |
| static Statement *createForeach(StaticForeach *sfe, Loc loc, Parameters *parameters, Statement *s) |
| { |
| if (sfe->aggrfe) |
| { |
| return new ForeachStatement(loc, sfe->aggrfe->op, parameters, sfe->aggrfe->aggr->syntaxCopy(), s, loc); |
| } |
| else |
| { |
| assert(sfe->rangefe && parameters->length == 1); |
| return new ForeachRangeStatement(loc, sfe->rangefe->op, (*parameters)[0], |
| sfe->rangefe->lwr->syntaxCopy(), |
| sfe->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; |
| * } |
| */ |
| |
| static TypeStruct *createTupleType(Loc loc, Expressions *e) |
| { // TODO: move to druntime? |
| Identifier *sid = Identifier::generateId("Tuple"); |
| StructDeclaration *sdecl = new StructDeclaration(loc, sid, false); |
| sdecl->storage_class |= STCstatic; |
| sdecl->members = new Dsymbols(); |
| Identifier *fid = Identifier::idPool("tuple"); |
| Type *ty = new TypeTypeof(loc, new TupleExp(loc, e)); |
| sdecl->members->push(new VarDeclaration(loc, ty, fid, NULL)); |
| TypeStruct *r = (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)`. |
| */ |
| |
| static Expression *createTuple(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. |
| */ |
| |
| static void lowerNonArrayAggregate(StaticForeach *sfe, Scope *sc) |
| { |
| size_t nvars = sfe->aggrfe ? sfe->aggrfe->parameters->length : 1; |
| Loc aloc = sfe->aggrfe ? sfe->aggrfe->aggr->loc : sfe->rangefe->lwr->loc; |
| // We need three sets of foreach loop variables because the |
| // lowering contains three foreach loops. |
| Parameters *pparams[3] = {new Parameters(), new Parameters(), new Parameters()}; |
| for (size_t i = 0; i < nvars; i++) |
| { |
| for (size_t j = 0; j < 3; j++) |
| { |
| Parameters *params = pparams[j]; |
| Parameter *p = sfe->aggrfe ? (*sfe->aggrfe->parameters)[i] : sfe->rangefe->prm; |
| params->push(new Parameter(p->storageClass, p->type, p->ident, NULL, NULL)); |
| } |
| } |
| Expression *res[2]; |
| TypeStruct *tplty = NULL; |
| if (nvars == 1) // only one `static foreach` variable, generate identifiers. |
| { |
| for (size_t i = 0; i < 2; i++) |
| { |
| res[i] = new IdentifierExp(aloc, (*pparams[i])[0]->ident); |
| } |
| } |
| else // multiple `static foreach` variables, generate tuples. |
| { |
| for (size_t i = 0; i < 2; i++) |
| { |
| Expressions *e = new Expressions(); |
| for (size_t j = 0; j < pparams[0]->length; j++) |
| { |
| Parameter *p = (*pparams[i])[j]; |
| e->push(new IdentifierExp(aloc, p->ident)); |
| } |
| if (!tplty) |
| { |
| tplty = createTupleType(aloc, e); |
| } |
| res[i] = createTuple(aloc, tplty, e); |
| } |
| sfe->needExpansion = true; // need to expand the tuples later |
| } |
| // generate remaining code for the new aggregate which is an |
| // array (see documentation comment). |
| if (sfe->rangefe) |
| { |
| sc = sc->startCTFE(); |
| sfe->rangefe->lwr = expressionSemantic(sfe->rangefe->lwr, sc); |
| sfe->rangefe->lwr = resolveProperties(sc, sfe->rangefe->lwr); |
| sfe->rangefe->upr = expressionSemantic(sfe->rangefe->upr, sc); |
| sfe->rangefe->upr = resolveProperties(sc, sfe->rangefe->upr); |
| sc = sc->endCTFE(); |
| sfe->rangefe->lwr = sfe->rangefe->lwr->optimize(WANTvalue); |
| sfe->rangefe->lwr = sfe->rangefe->lwr->ctfeInterpret(); |
| sfe->rangefe->upr = sfe->rangefe->upr->optimize(WANTvalue); |
| sfe->rangefe->upr = sfe->rangefe->upr->ctfeInterpret(); |
| } |
| Statements *s1 = new Statements(); |
| Statements *sfebody = new Statements(); |
| if (tplty) sfebody->push(new ExpStatement(sfe->loc, tplty->sym)); |
| sfebody->push(new ReturnStatement(aloc, res[0])); |
| s1->push(createForeach(sfe, aloc, pparams[0], new CompoundStatement(aloc, sfebody))); |
| s1->push(new ExpStatement(aloc, new AssertExp(aloc, new IntegerExp(aloc, 0, Type::tint32)))); |
| Type *ety = new TypeTypeof(aloc, wrapAndCall(aloc, new CompoundStatement(aloc, s1))); |
| Type *aty = ety->arrayOf(); |
| Identifier *idres = Identifier::generateId("__res"); |
| VarDeclaration *vard = new VarDeclaration(aloc, aty, idres, NULL); |
| Statements *s2 = new Statements(); |
| |
| // Run 'typeof' gagged to avoid duplicate errors and if it fails just create |
| // an empty foreach to expose them. |
| unsigned olderrors = global.startGagging(); |
| ety = typeSemantic(ety, aloc, sc); |
| if (global.endGagging(olderrors)) |
| s2->push(createForeach(sfe, aloc, pparams[1], NULL)); |
| else |
| { |
| s2->push(new ExpStatement(aloc, vard)); |
| Expression *catass = new CatAssignExp(aloc, new IdentifierExp(aloc, idres), res[1]); |
| s2->push(createForeach(sfe, aloc, pparams[1], new ExpStatement(aloc, catass))); |
| s2->push(new ReturnStatement(aloc, new IdentifierExp(aloc, idres))); |
| } |
| |
| Expression *aggr; |
| Type *indexty; |
| |
| if (sfe->rangefe && (indexty = ety)->isintegral()) |
| { |
| sfe->rangefe->lwr->type = indexty; |
| sfe->rangefe->upr->type = indexty; |
| IntRange lwrRange = getIntRange(sfe->rangefe->lwr); |
| IntRange uprRange = getIntRange(sfe->rangefe->upr); |
| |
| const dinteger_t lwr = sfe->rangefe->lwr->toInteger(); |
| dinteger_t upr = sfe->rangefe->upr->toInteger(); |
| size_t length = 0; |
| |
| if (lwrRange.imin <= uprRange.imax) |
| length = (size_t)(upr - lwr); |
| |
| Expressions *exps = new Expressions(); |
| exps->setDim(length); |
| |
| if (sfe->rangefe->op == TOKforeach) |
| { |
| for (size_t i = 0; i < length; i++) |
| (*exps)[i] = new IntegerExp(aloc, lwr + i, indexty); |
| } |
| else |
| { |
| --upr; |
| for (size_t i = 0; i < length; i++) |
| (*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 = expressionSemantic(aggr, sc); |
| aggr = resolveProperties(sc, aggr); |
| sc = sc->endCTFE(); |
| aggr = aggr->optimize(WANTvalue); |
| aggr = aggr->ctfeInterpret(); |
| } |
| |
| assert(!!sfe->aggrfe ^ !!sfe->rangefe); |
| sfe->aggrfe = new ForeachStatement(sfe->loc, TOKforeach, pparams[2], aggr, |
| sfe->aggrfe ? sfe->aggrfe->_body : sfe->rangefe->_body, |
| sfe->aggrfe ? sfe->aggrfe->endloc : sfe->rangefe->endloc); |
| sfe->rangefe = NULL; |
| lowerArrayAggregate(sfe, sc); // finally, turn generated array into expression tuple |
| } |
| |
| /***************************************** |
| * Perform `static foreach` lowerings that are necessary in order |
| * to finally expand the `static foreach` using |
| * `ddmd.statementsem.makeTupleForeach`. |
| */ |
| |
| void staticForeachPrepare(StaticForeach *sfe, Scope *sc) |
| { |
| assert(sc); |
| if (sfe->aggrfe) |
| { |
| sc = sc->startCTFE(); |
| sfe->aggrfe->aggr = expressionSemantic(sfe->aggrfe->aggr, sc); |
| sc = sc->endCTFE(); |
| sfe->aggrfe->aggr = sfe->aggrfe->aggr->optimize(WANTvalue); |
| } |
| |
| if (sfe->aggrfe && sfe->aggrfe->aggr->type->toBasetype()->ty == Terror) |
| { |
| return; |
| } |
| |
| if (!staticForeachReady(sfe)) |
| { |
| if (sfe->aggrfe && sfe->aggrfe->aggr->type->toBasetype()->ty == Tarray) |
| { |
| lowerArrayAggregate(sfe, sc); |
| } |
| else |
| { |
| lowerNonArrayAggregate(sfe, sc); |
| } |
| } |
| } |
| |
| /***************************************** |
| * Returns: |
| * `true` iff ready to call `ddmd.statementsem.makeTupleForeach`. |
| */ |
| |
| bool staticForeachReady(StaticForeach *sfe) |
| { |
| return sfe->aggrfe && sfe->aggrfe->aggr && sfe->aggrfe->aggr->type && |
| sfe->aggrfe->aggr->type->toBasetype()->ty == Ttuple; |
| } |
| |
| /* ============================================================ */ |
| |
| DVCondition::DVCondition(Module *mod, unsigned level, Identifier *ident) |
| : Condition(Loc()) |
| { |
| this->mod = mod; |
| this->level = level; |
| this->ident = ident; |
| } |
| |
| Condition *DVCondition::syntaxCopy() |
| { |
| return this; // don't need to copy |
| } |
| |
| /* ============================================================ */ |
| |
| void DebugCondition::addGlobalIdent(const char *ident) |
| { |
| if (!global.debugids) |
| global.debugids = new Identifiers(); |
| global.debugids->push(Identifier::idPool(ident)); |
| } |
| |
| |
| DebugCondition::DebugCondition(Module *mod, unsigned level, Identifier *ident) |
| : DVCondition(mod, level, ident) |
| { |
| } |
| |
| // Helper for printing dependency information |
| void printDepsConditional(Scope *sc, DVCondition* condition, const char* depType) |
| { |
| if (!global.params.moduleDeps || global.params.moduleDepsFile.length) |
| return; |
| OutBuffer *ob = global.params.moduleDeps; |
| Module* imod = sc ? sc->instantiatingModule() : 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->printf("%s\n", condition->ident->toChars()); |
| else |
| ob->printf("%d\n", condition->level); |
| } |
| |
| |
| int DebugCondition::include(Scope *sc) |
| { |
| //printf("DebugCondition::include() level = %d, debuglevel = %d\n", level, global.params.debuglevel); |
| if (inc == 0) |
| { |
| inc = 2; |
| bool definedInModule = false; |
| if (ident) |
| { |
| if (findCondition(mod->debugids, ident)) |
| { |
| inc = 1; |
| definedInModule = true; |
| } |
| else if (findCondition(global.debugids, ident)) |
| inc = 1; |
| else |
| { if (!mod->debugidsNot) |
| mod->debugidsNot = new Identifiers(); |
| mod->debugidsNot->push(ident); |
| } |
| } |
| else if (level <= global.params.debuglevel || level <= mod->debuglevel) |
| inc = 1; |
| if (!definedInModule) |
| printDepsConditional(sc, this, "depsDebug "); |
| } |
| return (inc == 1); |
| } |
| |
| /* ============================================================ */ |
| |
| static bool isReserved(const char *ident) |
| { |
| static const char* reserved[] = |
| { |
| "DigitalMars", |
| "GNU", |
| "LDC", |
| "SDC", |
| "Windows", |
| "Win32", |
| "Win64", |
| "linux", |
| "OSX", |
| "FreeBSD", |
| "OpenBSD", |
| "NetBSD", |
| "DragonFlyBSD", |
| "BSD", |
| "Solaris", |
| "Posix", |
| "AIX", |
| "Haiku", |
| "SkyOS", |
| "SysV3", |
| "SysV4", |
| "Hurd", |
| "Android", |
| "PlayStation", |
| "PlayStation4", |
| "Cygwin", |
| "MinGW", |
| "FreeStanding", |
| "X86", |
| "X86_64", |
| "ARM", |
| "ARM_Thumb", |
| "ARM_SoftFloat", |
| "ARM_SoftFP", |
| "ARM_HardFloat", |
| "AArch64", |
| "Epiphany", |
| "PPC", |
| "PPC_SoftFloat", |
| "PPC_HardFloat", |
| "PPC64", |
| "IA64", |
| "MIPS32", |
| "MIPS64", |
| "MIPS_O32", |
| "MIPS_N32", |
| "MIPS_O64", |
| "MIPS_N64", |
| "MIPS_EABI", |
| "MIPS_SoftFloat", |
| "MIPS_HardFloat", |
| "MSP430", |
| "NVPTX", |
| "NVPTX64", |
| "RISCV32", |
| "RISCV64", |
| "SPARC", |
| "SPARC_V8Plus", |
| "SPARC_SoftFloat", |
| "SPARC_HardFloat", |
| "SPARC64", |
| "S390", |
| "S390X", |
| "HPPA", |
| "HPPA64", |
| "SH", |
| "Alpha", |
| "Alpha_SoftFloat", |
| "Alpha_HardFloat", |
| "LittleEndian", |
| "BigEndian", |
| "ELFv1", |
| "ELFv2", |
| "CRuntime_Digitalmars", |
| "CRuntime_Glibc", |
| "CRuntime_Microsoft", |
| "CRuntime_Musl", |
| "CRuntime_UClibc", |
| "CppRuntime_Clang", |
| "CppRuntime_DigitalMars", |
| "CppRuntime_Gcc", |
| "CppRuntime_Microsoft", |
| "CppRuntime_Sun", |
| "D_Coverage", |
| "D_Ddoc", |
| "D_InlineAsm_X86", |
| "D_InlineAsm_X86_64", |
| "D_LP64", |
| "D_X32", |
| "D_HardFloat", |
| "D_SoftFloat", |
| "D_PIC", |
| "D_SIMD", |
| "D_Version2", |
| "D_NoBoundsChecks", |
| "unittest", |
| "assert", |
| "all", |
| "none", |
| NULL |
| }; |
| |
| for (unsigned i = 0; reserved[i]; i++) |
| { |
| if (strcmp(ident, reserved[i]) == 0) |
| return true; |
| } |
| |
| if (ident[0] == 'D' && ident[1] == '_') |
| return true; |
| return false; |
| } |
| |
| void checkReserved(Loc loc, const char *ident) |
| { |
| if (isReserved(ident)) |
| error(loc, "version identifier `%s` is reserved and cannot be set", ident); |
| } |
| |
| void VersionCondition::addGlobalIdent(const char *ident) |
| { |
| checkReserved(Loc(), ident); |
| addPredefinedGlobalIdent(ident); |
| } |
| |
| void VersionCondition::addPredefinedGlobalIdent(const char *ident) |
| { |
| if (!global.versionids) |
| global.versionids = new Identifiers(); |
| global.versionids->push(Identifier::idPool(ident)); |
| } |
| |
| |
| VersionCondition::VersionCondition(Module *mod, unsigned level, Identifier *ident) |
| : DVCondition(mod, level, ident) |
| { |
| } |
| |
| int VersionCondition::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 == 0) |
| { |
| inc = 2; |
| bool definedInModule=false; |
| if (ident) |
| { |
| if (findCondition(mod->versionids, ident)) |
| { |
| inc = 1; |
| definedInModule = true; |
| } |
| else if (findCondition(global.versionids, ident)) |
| inc = 1; |
| else |
| { |
| if (!mod->versionidsNot) |
| mod->versionidsNot = new Identifiers(); |
| mod->versionidsNot->push(ident); |
| } |
| } |
| else if (level <= global.params.versionlevel || level <= mod->versionlevel) |
| inc = 1; |
| if (!definedInModule && (!ident || (!isReserved(ident->toChars()) && ident != Id::_unittest && ident != Id::_assert))) |
| printDepsConditional(sc, this, "depsVersion "); |
| } |
| return (inc == 1); |
| } |
| |
| /**************************** StaticIfCondition *******************************/ |
| |
| StaticIfCondition::StaticIfCondition(Loc loc, Expression *exp) |
| : Condition(loc) |
| { |
| this->exp = exp; |
| } |
| |
| Condition *StaticIfCondition::syntaxCopy() |
| { |
| return new StaticIfCondition(loc, exp->syntaxCopy()); |
| } |
| |
| int StaticIfCondition::include(Scope *sc) |
| { |
| if (inc == 0) |
| { |
| if (!sc) |
| { |
| error(loc, "static if conditional cannot be at global scope"); |
| inc = 2; |
| return 0; |
| } |
| |
| sc = sc->push(sc->scopesym); |
| |
| bool errors = false; |
| |
| if (!exp) |
| goto Lerror; |
| |
| bool result = evalStaticCondition(sc, exp, exp, errors); |
| sc->pop(); |
| |
| // Prevent repeated condition evaluation. |
| // See: fail_compilation/fail7815.d |
| if (inc != 0) |
| return (inc == 1); |
| if (errors) |
| goto Lerror; |
| if (result) |
| inc = 1; |
| else |
| inc = 2; |
| } |
| return (inc == 1); |
| |
| Lerror: |
| if (!global.gag) |
| inc = 2; // so we don't see the error message again |
| return 0; |
| } |