/**
 * Compiler implementation of the
 * $(LINK2 http://www.dlang.org, D programming language).
 *
 * Copyright:   Copyright (c) 1999-2016 by Digital Mars, All Rights Reserved
 * Authors:     $(LINK2 http://www.digitalmars.com, Walter Bright)
 * License:     $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
 * Source:      $(DMDSRC _globals.d)
 */

module ddmd.globals;

import core.stdc.stdint;
import core.stdc.stdio;
import ddmd.root.array;
import ddmd.root.filename;
import ddmd.root.outbuffer;

template xversion(string s)
{
    enum xversion = mixin(`{ version (` ~ s ~ `) return true; else return false; }`)();
}

private string stripRight(string s)
{
    while (s.length && (s[$ - 1] == ' ' || s[$ - 1] == '\n' || s[$ - 1] == '\r'))
        s = s[0 .. $ - 1];
    return s;
}

enum IN_GCC     = xversion!`IN_GCC`;
enum IN_LLVM    = xversion!`IN_LLVM`;
enum IN_LLVM_MSVC = xversion!`IN_LLVM_MSVC`;

enum TARGET_LINUX   = xversion!`linux`;
enum TARGET_OSX     = xversion!`OSX`;
enum TARGET_FREEBSD = xversion!`FreeBSD`;
enum TARGET_OPENBSD = xversion!`OpenBSD`;
enum TARGET_SOLARIS = xversion!`Solaris`;
enum TARGET_WINDOS  = xversion!`Windows`;

version(IN_LLVM)
{
    enum OUTPUTFLAG : int
    {
        OUTPUTFLAGno,
        OUTPUTFLAGdefault, // for the .o default
        OUTPUTFLAGset      // for -output
    }
    alias OUTPUTFLAGno      = OUTPUTFLAG.OUTPUTFLAGno;
    alias OUTPUTFLAGdefault = OUTPUTFLAG.OUTPUTFLAGdefault;
    alias OUTPUTFLAGset     = OUTPUTFLAG.OUTPUTFLAGset;
}

enum BOUNDSCHECK : int
{
    BOUNDSCHECKdefault,     // initial value
    BOUNDSCHECKoff,         // never do bounds checking
    BOUNDSCHECKon,          // always do bounds checking
    BOUNDSCHECKsafeonly,    // do bounds checking only in @safe functions
}

alias BOUNDSCHECKdefault = BOUNDSCHECK.BOUNDSCHECKdefault;
alias BOUNDSCHECKoff = BOUNDSCHECK.BOUNDSCHECKoff;
alias BOUNDSCHECKon = BOUNDSCHECK.BOUNDSCHECKon;
alias BOUNDSCHECKsafeonly = BOUNDSCHECK.BOUNDSCHECKsafeonly;

enum CPU
{
    x87,
    mmx,
    sse,
    sse2,
    sse3,
    ssse3,
    sse4_1,
    sse4_2,
    avx,                // AVX1 instruction set
    avx2,               // AVX2 instruction set
    avx512,             // AVX-512 instruction set

    // Special values that don't survive past the command line processing
    baseline,           // (default) the minimum capability CPU
    native              // the machine the compiler is being run on
}

// Put command line switches in here
struct Param
{
    bool obj;               // write object file
    bool link;              // perform link
    bool dll;               // generate shared dynamic library
    bool lib;               // write library file instead of object file(s)
    bool multiobj;          // break one object file into multiple ones
    bool oneobj;            // write one object file instead of multiple ones
    bool trace;             // insert profiling hooks
    bool tracegc;           // instrument calls to 'new'
    bool verbose;           // verbose compile
    bool showColumns;       // print character (column) numbers in diagnostics
    bool vtls;              // identify thread local variables
    bool vgc;               // identify gc usage
    bool vfield;            // identify non-mutable field variables
    bool vcomplex;          // identify complex/imaginary type usage
    ubyte symdebug;         // insert debug symbolic information
    bool alwaysframe;       // always emit standard stack frame
    bool optimize;          // run optimizer
    bool map;               // generate linker .map file
    bool is64bit;           // generate 64 bit code
    bool isLP64;            // generate code for LP64
    bool isLinux;           // generate code for linux
    bool isOSX;             // generate code for Mac OSX
    bool isWindows;         // generate code for Windows
    bool isFreeBSD;         // generate code for FreeBSD
    bool isOpenBSD;         // generate code for OpenBSD
    bool isSolaris;         // generate code for Solaris
    bool hasObjectiveC;     // target supports Objective-C
    bool mscoff;            // for Win32: write COFF object files instead of OMF
    // 0: don't allow use of deprecated features
    // 1: silently allow use of deprecated features
    // 2: warn about the use of deprecated features
    byte useDeprecated;
    bool useAssert;         // generate runtime code for assert()'s
    bool useInvariants;     // generate class invariant checks
    bool useIn;             // generate precondition checks
    bool useOut;            // generate postcondition checks
    bool stackstomp;        // add stack stomping code
    bool useSwitchError;    // check for switches without a default
    bool useUnitTests;      // generate unittest code
    bool useInline;         // inline expand functions
    bool useDIP25;          // implement http://wiki.dlang.org/DIP25
    bool release;           // build release version
    bool preservePaths;     // true means don't strip path from source file
    // 0: disable warnings
    // 1: warnings as errors
    // 2: informational warnings (no errors)
    byte warnings;
    bool pic;               // generate position-independent-code for shared libs
    bool color;             // use ANSI colors in console output
    bool cov;               // generate code coverage data
    ubyte covPercent;       // 0..100 code coverage percentage required
    bool nofloat;           // code should not pull in floating point support
    bool ignoreUnsupportedPragmas;  // rather than error on them
    bool enforcePropertySyntax;
    bool betterC;           // be a "better C" compiler; no dependency on D runtime
    bool addMain;           // add a default main() function
    bool allInst;           // generate code for all template instantiations
    bool check10378;        // check for issues transitioning to 10738
    bool bug10378;          // use pre-bugzilla 10378 search strategy
    bool vsafe;             // use enhanced @safe checking
    /** The --transition=safe switch should only be used to show code with
     * silent semantics changes related to @safe improvements.  It should not be
     * used to hide a feature that will have to go through deprecate-then-error
     * before becoming default.
     */

    bool showGaggedErrors;  // print gagged errors anyway

    CPU cpu;                // CPU instruction set to target
    BOUNDSCHECK useArrayBounds;

    const(char)* argv0;                 // program name
    Array!(const(char)*)* modFileAliasStrings; // array of char*'s of -I module filename alias strings
    Array!(const(char)*)* imppath;      // array of char*'s of where to look for import modules
    Array!(const(char)*)* fileImppath;  // array of char*'s of where to look for file import modules
    const(char)* objdir;                // .obj/.lib file output directory
    const(char)* objname;               // .obj file output name
    const(char)* libname;               // .lib file output name

    bool doDocComments;                 // process embedded documentation comments
    const(char)* docdir;                // write documentation file to docdir directory
    const(char)* docname;               // write documentation file to docname
    Array!(const(char)*)* ddocfiles;    // macro include files for Ddoc

    bool doHdrGeneration;               // process embedded documentation comments
    const(char)* hdrdir;                // write 'header' file to docdir directory
    const(char)* hdrname;               // write 'header' file to docname
    bool hdrStripPlainFunctions;        // strip the bodies of plain (non-template) functions

    bool doJsonGeneration;              // write JSON file
    const(char)* jsonfilename;          // write JSON file to jsonfilename

    uint debuglevel;                    // debug level
    Array!(const(char)*)* debugids;     // debug identifiers

    uint versionlevel;                  // version level
    Array!(const(char)*)* versionids;   // version identifiers

    const(char)* defaultlibname;        // default library for non-debug builds
    const(char)* debuglibname;          // default library for debug builds
    const(char)* mscrtlib;              // MS C runtime library

    const(char)* moduleDepsFile;        // filename for deps output
    OutBuffer* moduleDeps;              // contents to be written to deps file

    // Hidden debug switches
    bool debugb;
    bool debugc;
    bool debugf;
    bool debugr;
    bool debugx;
    bool debugy;

    bool run; // run resulting executable
    Strings runargs; // arguments for executable

    // Linker stuff
    Array!(const(char)*)* objfiles;
    Array!(const(char)*)* linkswitches;
    Array!(const(char)*)* libfiles;
    Array!(const(char)*)* dllfiles;
    const(char)* deffile;
    const(char)* resfile;
    const(char)* exefile;
    const(char)* mapfile;

    version(IN_LLVM)
    {
        Array!(const(char)*)* bitcodeFiles; // LLVM bitcode files passed on cmdline

        uint nestedTmpl; // maximum nested template instantiations

        // LDC stuff
        OUTPUTFLAG output_ll;
        OUTPUTFLAG output_bc;
        OUTPUTFLAG output_s;
        OUTPUTFLAG output_o;
        bool useInlineAsm;
        bool verbose_cg;
        bool fullyQualifiedObjectFiles;
        bool cleanupObjectFiles;

        // Profile-guided optimization:
        bool genInstrProf;             // Whether to generate PGO instrumented code
        const(char)* datafileInstrProf; // Either the input or output file for PGO data

        // target stuff
        const(void)* targetTriple; // const llvm::Triple*

        // Codegen cl options
        bool disableRedZone;
        uint dwarfVersion;

        uint hashThreshold; // MD5 hash symbols larger than this threshold (0 = no hashing)

        bool outputSourceLocations; // if true, output line tables.
    }
}

struct Compiler
{
    const(char)* vendor; // Compiler backend name
}

alias structalign_t = uint;

// magic value means "match whatever the underlying C compiler does"
// other values are all powers of 2
enum STRUCTALIGN_DEFAULT = (cast(structalign_t)~0);

struct Global
{
    const(char)* inifilename;
    const(char)* mars_ext;
    const(char)* obj_ext;
    version(IN_LLVM)
    {
        const(char)* ll_ext;
        const(char)* bc_ext;
        const(char)* s_ext;
        const(char)* ldc_version;
        const(char)* llvm_version;

        bool gaggedForInlining; // Set for functionSemantic3 for external inlining candidates
    }
    const(char)* lib_ext;
    const(char)* dll_ext;
    const(char)* doc_ext;           // for Ddoc generated files
    const(char)* ddoc_ext;          // for Ddoc macro include files
    const(char)* hdr_ext;           // for D 'header' import files
    const(char)* json_ext;          // for JSON files
    const(char)* map_ext;           // for .map files
    bool run_noext;                 // allow -run sources without extensions.

    const(char)* copyright;
    const(char)* written;
    const(char)* main_d;            // dummy filename for dummy main()
    Array!(const(char)*)* path;     // Array of char*'s which form the import lookup path
    Array!(const(char)*)* filePath; // Array of char*'s which form the file import lookup path

    const(char)* _version;

    Compiler compiler;
    Param params;
    uint errors;            // number of errors reported so far
    uint warnings;          // number of warnings reported so far
    FILE* stdmsg;           // where to send verbose messages
    uint gag;               // !=0 means gag reporting of errors & warnings
    uint gaggedErrors;      // number of errors reported while gagged

    uint errorLimit;

    /* Start gagging. Return the current number of gagged errors
     */
    extern (C++) uint startGagging()
    {
        ++gag;
        return gaggedErrors;
    }

    /* End gagging, restoring the old gagged state.
     * Return true if errors occured while gagged.
     */
    extern (C++) bool endGagging(uint oldGagged)
    {
        bool anyErrs = (gaggedErrors != oldGagged);
        --gag;
        // Restore the original state of gagged errors; set total errors
        // to be original errors + new ungagged errors.
        errors -= (gaggedErrors - oldGagged);
        gaggedErrors = oldGagged;
        return anyErrs;
    }

    /*  Increment the error count to record that an error
     *  has occured in the current context. An error message
     *  may or may not have been printed.
     */
    extern (C++) void increaseErrorCount()
    {
        if (gag)
            ++gaggedErrors;
        ++errors;
    }

    extern (C++) void _init()
    {
        inifilename = null;
        mars_ext = "d";
        hdr_ext = "di";
        doc_ext = "html";
        ddoc_ext = "ddoc";
        json_ext = "json";
        map_ext = "map";
version(IN_LLVM)
{
        obj_ext = "o";
        ll_ext  = "ll";
        bc_ext  = "bc";
        s_ext   = "s";
}
else
{
        static if (TARGET_WINDOS)
        {
            obj_ext = "obj";
        }
        else static if (TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS)
        {
            obj_ext = "o";
        }
        else
        {
            static assert(0, "fix this");
        }
        static if (TARGET_WINDOS)
        {
            lib_ext = "lib";
        }
        else static if (TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS)
        {
            lib_ext = "a";
        }
        else
        {
            static assert(0, "fix this");
        }
        static if (TARGET_WINDOS)
        {
            dll_ext = "dll";
        }
        else static if (TARGET_LINUX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS)
        {
            dll_ext = "so";
        }
        else static if (TARGET_OSX)
        {
            dll_ext = "dylib";
        }
        else
        {
            static assert(0, "fix this");
        }
        static if (TARGET_WINDOS)
        {
            run_noext = false;
        }
        else static if (TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS)
        {
            // Allow 'script' D source files to have no extension.
            run_noext = true;
        }
        else
        {
            static assert(0, "fix this");
        }
}
        copyright = "Copyright (c) 1999-2016 by Digital Mars";
        written = "written by Walter Bright";
version(IN_LLVM)
{
        compiler.vendor = "LDC";
}
else
{
        _version = ('v' ~ stripRight(import("verstr.h"))[1 .. $ - 1] ~ '\0').ptr;
        compiler.vendor = "Digital Mars D";
}
        stdmsg = stdout;
        main_d = "__main.d";
        errorLimit = 20;
    }
}

// Because int64_t and friends may be any integral type of the
// correct size, we have to explicitly ask for the correct
// integer type to get the correct mangling with ddmd

// Be careful not to care about sign when using dinteger_t
// use this instead of integer_t to
// avoid conflicts with system #include's
alias dinteger_t = ulong;
// Signed and unsigned variants
alias sinteger_t = long;
alias uinteger_t = ulong;

alias d_int8 = int8_t;
alias d_uns8 = uint8_t;
alias d_int16 = int16_t;
alias d_uns16 = uint16_t;
alias d_int32 = int32_t;
alias d_uns32 = uint32_t;
alias d_int64 = int64_t;
alias d_uns64 = uint64_t;

// file location
struct Loc
{
    const(char)* filename;
    uint linnum;
    uint charnum;

    extern (D) this(const(char)* filename, uint linnum, uint charnum)
    {
        this.linnum = linnum;
        this.charnum = charnum;
        this.filename = filename;
    }

    extern (C++) const(char)* toChars() const
    {
        OutBuffer buf;
        if (filename)
        {
            buf.printf("%s", filename);
        }
        if (linnum)
        {
            buf.printf("(%d", linnum);
            if (global.params.showColumns && charnum)
                buf.printf(",%d", charnum);
            buf.writeByte(')');
        }
        return buf.extractString();
    }

    extern (C++) bool equals(ref const(Loc) loc)
    {
        return (!global.params.showColumns || charnum == loc.charnum) && linnum == loc.linnum && FileName.equals(filename, loc.filename);
    }
}

enum LINK : int
{
    def,        // default
    d,
    c,
    cpp,
    windows,
    pascal,
    objc,
}

alias LINKdefault = LINK.def;
alias LINKd = LINK.d;
alias LINKc = LINK.c;
alias LINKcpp = LINK.cpp;
alias LINKwindows = LINK.windows;
alias LINKpascal = LINK.pascal;
alias LINKobjc = LINK.objc;

enum CPPMANGLE : int
{
    def,
    asStruct,
    asClass,
}

alias CPPMANGLEdefault = CPPMANGLE.def;
alias CPPMANGLEstruct = CPPMANGLE.asStruct;
alias CPPMANGLEclass = CPPMANGLE.asClass;

enum DYNCAST : int
{
    object,
    expression,
    dsymbol,
    type,
    identifier,
    tuple,
    parameter,
}

alias DYNCAST_OBJECT = DYNCAST.object;
alias DYNCAST_EXPRESSION = DYNCAST.expression;
alias DYNCAST_DSYMBOL = DYNCAST.dsymbol;
alias DYNCAST_TYPE = DYNCAST.type;
alias DYNCAST_IDENTIFIER = DYNCAST.identifier;
alias DYNCAST_TUPLE = DYNCAST.tuple;
alias DYNCAST_PARAMETER = DYNCAST.parameter;

enum MATCH : int
{
    nomatch,   // no match
    convert,   // match with conversions
    constant,  // match with conversion to const
    exact,     // exact match
}

alias MATCHnomatch = MATCH.nomatch;
alias MATCHconvert = MATCH.convert;
alias MATCHconst = MATCH.constant;
alias MATCHexact = MATCH.exact;

enum PINLINE : int
{
    def,     // as specified on the command line
    never,   // never inline
    always,  // always inline
}

alias PINLINEdefault = PINLINE.def;
alias PINLINEnever = PINLINE.never;
alias PINLINEalways = PINLINE.always;

alias StorageClass = uinteger_t;

extern (C++) __gshared Global global;
