/* Compiler implementation of the D programming language * Copyright (C) 1999-2019 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 "declaration.h" #include "identifier.h" #include "expression.h" #include "cond.h" #include "module.h" #include "template.h" #include "mtype.h" #include "scope.h" #include "arraytypes.h" #include "tokens.h" Expression *semantic(Expression *e, Scope *sc); bool evalStaticCondition(Scope *sc, Expression *exp, Expression *e, bool &errors); int findCondition(Strings *ids, Identifier *ident) { if (ids) { for (size_t i = 0; i < ids->dim; i++) { const char *id = (*ids)[i]; if (strcmp(id, ident->toChars()) == 0) return true; } } return false; } /* ============================================================ */ Condition::Condition(Loc loc) { this->loc = loc; inc = 0; } /* ============================================================ */ 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::setGlobalLevel(unsigned level) { global.params.debuglevel = level; } void DebugCondition::addGlobalIdent(const char *ident) { if (!global.params.debugids) global.params.debugids = new Strings(); global.params.debugids->push(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) 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, ScopeDsymbol *) { //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.params.debugids, ident)) inc = 1; else { if (!mod->debugidsNot) mod->debugidsNot = new Strings(); mod->debugidsNot->push(ident->toChars()); } } else if (level <= global.params.debuglevel || level <= mod->debuglevel) inc = 1; if (!definedInModule) printDepsConditional(sc, this, "depsDebug "); } return (inc == 1); } /* ============================================================ */ void VersionCondition::setGlobalLevel(unsigned level) { global.params.versionlevel = level; } 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.params.versionids) global.params.versionids = new Strings(); global.params.versionids->push(ident); } VersionCondition::VersionCondition(Module *mod, unsigned level, Identifier *ident) : DVCondition(mod, level, ident) { } int VersionCondition::include(Scope *sc, ScopeDsymbol *) { //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.params.versionids, ident)) inc = 1; else { if (!mod->versionidsNot) mod->versionidsNot = new Strings(); mod->versionidsNot->push(ident->toChars()); } } 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; this->nest = 0; } Condition *StaticIfCondition::syntaxCopy() { return new StaticIfCondition(loc, exp->syntaxCopy()); } int StaticIfCondition::include(Scope *sc, ScopeDsymbol *sds) { if (inc == 0) { if (exp->op == TOKerror || nest > 100) { error(loc, (nest > 1000) ? "unresolvable circular static if expression" : "error evaluating static if expression"); goto Lerror; } if (!sc) { error(loc, "static if conditional cannot be at global scope"); inc = 2; return 0; } ++nest; sc = sc->push(sc->scopesym); sc->sds = sds; // sds gets any addMember() bool errors = false; bool result = evalStaticCondition(sc, exp, exp, errors); sc->pop(); --nest; // 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; }