/**
 * Provides an implicit conversion table for basic types.
 *
 * Used to determine integer promotions and common types.
 *
 * Specification: $(LINK2 https://dlang.org/spec/type.html#integer-promotions, Integer Promotions),
 *   $(LINK2 https://dlang.org/spec/type.html#usual-arithmetic-conversions, Usual Arithmetic Conversions).
 *
 * 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/impcnvtab.d, _impcnvtab.d)
 * Documentation:  https://dlang.org/phobos/dmd_impcnvtab.html
 * Coverage:    https://codecov.io/gh/dlang/dmd/src/master/src/dmd/impcnvtab.d
 */

module dmd.impcnvtab;

import dmd.astenums;
import dmd.mtype;

pure @nogc nothrow @safe:

/*************************************************
 * If ty1 and ty2 are basic types, return the TY that both can
 * be implicitly converted to.
 * Params:
 *      ty1 = first operand type
 *      ty2 = second operand type
 * Returns:
 *      ty = common type, else Terror
 */
TY implicitConvCommonTy(TY ty1, TY ty2)
{
    return impCnvTab.impcnvResultTab[ty1][ty2];
}

/*************************************************
 * If ty1 and ty2 are basic types, return the TY that ty1 can
 * be implicitly converted to to bring them to a common ty.
 * It's symmetric, i.e. the operands can be swapped.
 * Params:
 *      ty1 = first operand type
 *      ty2 = second operand type
 * Returns:
 *      ty = what ty1 should be converted to, else Terror
 */
TY implicitConvTy1(TY ty1, TY ty2)
{
    return impCnvTab.impcnvType1Tab[ty1][ty2];
}

/******************************************************************************/

private:

struct ImpCnvTab
{
    TY[TMAX][TMAX] impcnvResultTab;
    TY[TMAX][TMAX] impcnvType1Tab;
}

enum ImpCnvTab impCnvTab = generateImpCnvTab();

ImpCnvTab generateImpCnvTab()
{
    TY[TMAX] typeTYs =
    [
        Tarray,
        Tsarray,
        Taarray,
        Tpointer,
        Treference,
        Tfunction,
        Tident,
        Tclass,
        Tstruct,
        Tenum,
        Tdelegate,
        Tnone,
        Tvoid,
        Tint8,
        Tuns8,
        Tint16,
        Tuns16,
        Tint32,
        Tuns32,
        Tint64,
        Tuns64,
        Tfloat32,
        Tfloat64,
        Tfloat80,
        Timaginary32,
        Timaginary64,
        Timaginary80,
        Tcomplex32,
        Tcomplex64,
        Tcomplex80,
        Tbool,
        Tchar,
        Twchar,
        Tdchar,
        Terror,
        Tinstance,
        Ttypeof,
        Ttuple,
        Tslice,
        Treturn,
        Tnull,
        Tvector,
        Tint128,
        Tuns128,
        Ttraits,
        Tmixin,
        Tnoreturn,
        Ttag,
    ];
    ImpCnvTab impCnvTab;

    // Set conversion tables
    foreach (i; 0 .. cast(size_t)TMAX)
    {
        foreach (j; 0 .. cast(size_t)TMAX)
        {
            impCnvTab.impcnvResultTab[i][j] = Terror;
            impCnvTab.impcnvType1Tab[i][j] = Terror;
        }
    }

    void X(TY t1, TY t2, TY nt1, TY nt2, TY rt)
    {
        impCnvTab.impcnvResultTab[t1][t2] = rt;
        impCnvTab.impcnvResultTab[t2][t1] = rt;

        impCnvTab.impcnvType1Tab[t1][t2] = nt1;
        impCnvTab.impcnvType1Tab[t2][t1] = nt2;
    }

    /* ======================= */

    X(Tbool,Tbool,   Tbool,Tbool,    Tbool);
    X(Tbool,Tint8,   Tint32,Tint32,  Tint32);
    X(Tbool,Tuns8,   Tint32,Tint32,  Tint32);
    X(Tbool,Tint16,  Tint32,Tint32,  Tint32);
    X(Tbool,Tuns16,  Tint32,Tint32,  Tint32);
    X(Tbool,Tint32,  Tint32,Tint32,  Tint32);
    X(Tbool,Tuns32,  Tuns32,Tuns32,  Tuns32);
    X(Tbool,Tint64,  Tint64,Tint64,  Tint64);
    X(Tbool,Tuns64,  Tuns64,Tuns64,  Tuns64);
    X(Tbool,Tint128, Tint128,Tint128, Tint128);
    X(Tbool,Tuns128, Tuns128,Tuns128, Tuns128);

    X(Tbool,Tfloat32,     Tfloat32,Tfloat32,     Tfloat32);
    X(Tbool,Tfloat64,     Tfloat64,Tfloat64,     Tfloat64);
    X(Tbool,Tfloat80,     Tfloat80,Tfloat80,     Tfloat80);
    X(Tbool,Timaginary32, Tfloat32,Timaginary32, Tfloat32);
    X(Tbool,Timaginary64, Tfloat64,Timaginary64, Tfloat64);
    X(Tbool,Timaginary80, Tfloat80,Timaginary80, Tfloat80);
    X(Tbool,Tcomplex32,   Tfloat32,Tcomplex32,   Tcomplex32);
    X(Tbool,Tcomplex64,   Tfloat64,Tcomplex64,   Tcomplex64);
    X(Tbool,Tcomplex80,   Tfloat80,Tcomplex80,   Tcomplex80);

    /* ======================= */

    X(Tint8,Tint8,   Tint32,Tint32,  Tint32);
    X(Tint8,Tuns8,   Tint32,Tint32,  Tint32);
    X(Tint8,Tint16,  Tint32,Tint32,  Tint32);
    X(Tint8,Tuns16,  Tint32,Tint32,  Tint32);
    X(Tint8,Tint32,  Tint32,Tint32,  Tint32);
    X(Tint8,Tuns32,  Tuns32,Tuns32,  Tuns32);
    X(Tint8,Tint64,  Tint64,Tint64,  Tint64);
    X(Tint8,Tuns64,  Tuns64,Tuns64,  Tuns64);
    X(Tint8,Tint128, Tint128,Tint128, Tint128);
    X(Tint8,Tuns128, Tuns128,Tuns128, Tuns128);

    X(Tint8,Tfloat32,     Tfloat32,Tfloat32,     Tfloat32);
    X(Tint8,Tfloat64,     Tfloat64,Tfloat64,     Tfloat64);
    X(Tint8,Tfloat80,     Tfloat80,Tfloat80,     Tfloat80);
    X(Tint8,Timaginary32, Tfloat32,Timaginary32, Tfloat32);
    X(Tint8,Timaginary64, Tfloat64,Timaginary64, Tfloat64);
    X(Tint8,Timaginary80, Tfloat80,Timaginary80, Tfloat80);
    X(Tint8,Tcomplex32,   Tfloat32,Tcomplex32,   Tcomplex32);
    X(Tint8,Tcomplex64,   Tfloat64,Tcomplex64,   Tcomplex64);
    X(Tint8,Tcomplex80,   Tfloat80,Tcomplex80,   Tcomplex80);

    /* ======================= */

    X(Tuns8,Tuns8,   Tint32,Tint32,  Tint32);
    X(Tuns8,Tint16,  Tint32,Tint32,  Tint32);
    X(Tuns8,Tuns16,  Tint32,Tint32,  Tint32);
    X(Tuns8,Tint32,  Tint32,Tint32,  Tint32);
    X(Tuns8,Tuns32,  Tuns32,Tuns32,  Tuns32);
    X(Tuns8,Tint64,  Tint64,Tint64,  Tint64);
    X(Tuns8,Tuns64,  Tuns64,Tuns64,  Tuns64);
    X(Tuns8,Tint128,  Tint128,Tint128,  Tint128);
    X(Tuns8,Tuns128,  Tuns128,Tuns128,  Tuns128);

    X(Tuns8,Tfloat32,     Tfloat32,Tfloat32,     Tfloat32);
    X(Tuns8,Tfloat64,     Tfloat64,Tfloat64,     Tfloat64);
    X(Tuns8,Tfloat80,     Tfloat80,Tfloat80,     Tfloat80);
    X(Tuns8,Timaginary32, Tfloat32,Timaginary32, Tfloat32);
    X(Tuns8,Timaginary64, Tfloat64,Timaginary64, Tfloat64);
    X(Tuns8,Timaginary80, Tfloat80,Timaginary80, Tfloat80);
    X(Tuns8,Tcomplex32,   Tfloat32,Tcomplex32,   Tcomplex32);
    X(Tuns8,Tcomplex64,   Tfloat64,Tcomplex64,   Tcomplex64);
    X(Tuns8,Tcomplex80,   Tfloat80,Tcomplex80,   Tcomplex80);

    /* ======================= */

    X(Tint16,Tint16,  Tint32,Tint32,  Tint32);
    X(Tint16,Tuns16,  Tint32,Tint32,  Tint32);
    X(Tint16,Tint32,  Tint32,Tint32,  Tint32);
    X(Tint16,Tuns32,  Tuns32,Tuns32,  Tuns32);
    X(Tint16,Tint64,  Tint64,Tint64,  Tint64);
    X(Tint16,Tuns64,  Tuns64,Tuns64,  Tuns64);
    X(Tint16,Tint128,  Tint128,Tint128,  Tint128);
    X(Tint16,Tuns128,  Tuns128,Tuns128,  Tuns128);

    X(Tint16,Tfloat32,     Tfloat32,Tfloat32,     Tfloat32);
    X(Tint16,Tfloat64,     Tfloat64,Tfloat64,     Tfloat64);
    X(Tint16,Tfloat80,     Tfloat80,Tfloat80,     Tfloat80);
    X(Tint16,Timaginary32, Tfloat32,Timaginary32, Tfloat32);
    X(Tint16,Timaginary64, Tfloat64,Timaginary64, Tfloat64);
    X(Tint16,Timaginary80, Tfloat80,Timaginary80, Tfloat80);
    X(Tint16,Tcomplex32,   Tfloat32,Tcomplex32,   Tcomplex32);
    X(Tint16,Tcomplex64,   Tfloat64,Tcomplex64,   Tcomplex64);
    X(Tint16,Tcomplex80,   Tfloat80,Tcomplex80,   Tcomplex80);

    /* ======================= */

    X(Tuns16,Tuns16,  Tint32,Tint32,  Tint32);
    X(Tuns16,Tint32,  Tint32,Tint32,  Tint32);
    X(Tuns16,Tuns32,  Tuns32,Tuns32,  Tuns32);
    X(Tuns16,Tint64,  Tint64,Tint64,  Tint64);
    X(Tuns16,Tuns64,  Tuns64,Tuns64,  Tuns64);
    X(Tuns16,Tint128, Tint128,Tint128,  Tint128);
    X(Tuns16,Tuns128, Tuns128,Tuns128,  Tuns128);

    X(Tuns16,Tfloat32,     Tfloat32,Tfloat32,     Tfloat32);
    X(Tuns16,Tfloat64,     Tfloat64,Tfloat64,     Tfloat64);
    X(Tuns16,Tfloat80,     Tfloat80,Tfloat80,     Tfloat80);
    X(Tuns16,Timaginary32, Tfloat32,Timaginary32, Tfloat32);
    X(Tuns16,Timaginary64, Tfloat64,Timaginary64, Tfloat64);
    X(Tuns16,Timaginary80, Tfloat80,Timaginary80, Tfloat80);
    X(Tuns16,Tcomplex32,   Tfloat32,Tcomplex32,   Tcomplex32);
    X(Tuns16,Tcomplex64,   Tfloat64,Tcomplex64,   Tcomplex64);
    X(Tuns16,Tcomplex80,   Tfloat80,Tcomplex80,   Tcomplex80);

    /* ======================= */

    X(Tint32,Tint32,  Tint32,Tint32,  Tint32);
    X(Tint32,Tuns32,  Tuns32,Tuns32,  Tuns32);
    X(Tint32,Tint64,  Tint64,Tint64,  Tint64);
    X(Tint32,Tuns64,  Tuns64,Tuns64,  Tuns64);
    X(Tint32,Tint128, Tint128,Tint128,  Tint128);
    X(Tint32,Tuns128, Tuns128,Tuns128,  Tuns128);

    X(Tint32,Tfloat32,     Tfloat32,Tfloat32,     Tfloat32);
    X(Tint32,Tfloat64,     Tfloat64,Tfloat64,     Tfloat64);
    X(Tint32,Tfloat80,     Tfloat80,Tfloat80,     Tfloat80);
    X(Tint32,Timaginary32, Tfloat32,Timaginary32, Tfloat32);
    X(Tint32,Timaginary64, Tfloat64,Timaginary64, Tfloat64);
    X(Tint32,Timaginary80, Tfloat80,Timaginary80, Tfloat80);
    X(Tint32,Tcomplex32,   Tfloat32,Tcomplex32,   Tcomplex32);
    X(Tint32,Tcomplex64,   Tfloat64,Tcomplex64,   Tcomplex64);
    X(Tint32,Tcomplex80,   Tfloat80,Tcomplex80,   Tcomplex80);

    /* ======================= */

    X(Tuns32,Tuns32,  Tuns32,Tuns32,  Tuns32);
    X(Tuns32,Tint64,  Tint64,Tint64,  Tint64);
    X(Tuns32,Tuns64,  Tuns64,Tuns64,  Tuns64);
    X(Tuns32,Tint128,  Tint128,Tint128,  Tint128);
    X(Tuns32,Tuns128,  Tuns128,Tuns128,  Tuns128);

    X(Tuns32,Tfloat32,     Tfloat32,Tfloat32,     Tfloat32);
    X(Tuns32,Tfloat64,     Tfloat64,Tfloat64,     Tfloat64);
    X(Tuns32,Tfloat80,     Tfloat80,Tfloat80,     Tfloat80);
    X(Tuns32,Timaginary32, Tfloat32,Timaginary32, Tfloat32);
    X(Tuns32,Timaginary64, Tfloat64,Timaginary64, Tfloat64);
    X(Tuns32,Timaginary80, Tfloat80,Timaginary80, Tfloat80);
    X(Tuns32,Tcomplex32,   Tfloat32,Tcomplex32,   Tcomplex32);
    X(Tuns32,Tcomplex64,   Tfloat64,Tcomplex64,   Tcomplex64);
    X(Tuns32,Tcomplex80,   Tfloat80,Tcomplex80,   Tcomplex80);

    /* ======================= */

    X(Tint64,Tint64,  Tint64,Tint64,  Tint64);
    X(Tint64,Tuns64,  Tuns64,Tuns64,  Tuns64);
    X(Tint64,Tint128,  Tint128,Tint128,  Tint128);
    X(Tint64,Tuns128,  Tuns128,Tuns128,  Tuns128);

    X(Tint64,Tfloat32,     Tfloat32,Tfloat32,     Tfloat32);
    X(Tint64,Tfloat64,     Tfloat64,Tfloat64,     Tfloat64);
    X(Tint64,Tfloat80,     Tfloat80,Tfloat80,     Tfloat80);
    X(Tint64,Timaginary32, Tfloat32,Timaginary32, Tfloat32);
    X(Tint64,Timaginary64, Tfloat64,Timaginary64, Tfloat64);
    X(Tint64,Timaginary80, Tfloat80,Timaginary80, Tfloat80);
    X(Tint64,Tcomplex32,   Tfloat32,Tcomplex32,   Tcomplex32);
    X(Tint64,Tcomplex64,   Tfloat64,Tcomplex64,   Tcomplex64);
    X(Tint64,Tcomplex80,   Tfloat80,Tcomplex80,   Tcomplex80);

    /* ======================= */

    X(Tuns64,Tuns64,  Tuns64,Tuns64,  Tuns64);
    X(Tuns64,Tint128,  Tint128,Tint128,  Tint128);
    X(Tuns64,Tuns128,  Tuns128,Tuns128,  Tuns128);

    X(Tuns64,Tfloat32,     Tfloat32,Tfloat32,     Tfloat32);
    X(Tuns64,Tfloat64,     Tfloat64,Tfloat64,     Tfloat64);
    X(Tuns64,Tfloat80,     Tfloat80,Tfloat80,     Tfloat80);
    X(Tuns64,Timaginary32, Tfloat32,Timaginary32, Tfloat32);
    X(Tuns64,Timaginary64, Tfloat64,Timaginary64, Tfloat64);
    X(Tuns64,Timaginary80, Tfloat80,Timaginary80, Tfloat80);
    X(Tuns64,Tcomplex32,   Tfloat32,Tcomplex32,   Tcomplex32);
    X(Tuns64,Tcomplex64,   Tfloat64,Tcomplex64,   Tcomplex64);
    X(Tuns64,Tcomplex80,   Tfloat80,Tcomplex80,   Tcomplex80);

    /* ======================= */

    X(Tint128,Tint128,  Tint128,Tint128,  Tint128);
    X(Tint128,Tuns128,  Tuns128,Tuns128,  Tuns128);

    X(Tint128,Tfloat32,     Tfloat32,Tfloat32,     Tfloat32);
    X(Tint128,Tfloat64,     Tfloat64,Tfloat64,     Tfloat64);
    X(Tint128,Tfloat80,     Tfloat80,Tfloat80,     Tfloat80);
    X(Tint128,Timaginary32, Tfloat32,Timaginary32, Tfloat32);
    X(Tint128,Timaginary64, Tfloat64,Timaginary64, Tfloat64);
    X(Tint128,Timaginary80, Tfloat80,Timaginary80, Tfloat80);
    X(Tint128,Tcomplex32,   Tfloat32,Tcomplex32,   Tcomplex32);
    X(Tint128,Tcomplex64,   Tfloat64,Tcomplex64,   Tcomplex64);
    X(Tint128,Tcomplex80,   Tfloat80,Tcomplex80,   Tcomplex80);

    /* ======================= */

    X(Tuns128,Tuns128,  Tuns128,Tuns128,  Tuns128);

    X(Tuns128,Tfloat32,     Tfloat32,Tfloat32,     Tfloat32);
    X(Tuns128,Tfloat64,     Tfloat64,Tfloat64,     Tfloat64);
    X(Tuns128,Tfloat80,     Tfloat80,Tfloat80,     Tfloat80);
    X(Tuns128,Timaginary32, Tfloat32,Timaginary32, Tfloat32);
    X(Tuns128,Timaginary64, Tfloat64,Timaginary64, Tfloat64);
    X(Tuns128,Timaginary80, Tfloat80,Timaginary80, Tfloat80);
    X(Tuns128,Tcomplex32,   Tfloat32,Tcomplex32,   Tcomplex32);
    X(Tuns128,Tcomplex64,   Tfloat64,Tcomplex64,   Tcomplex64);
    X(Tuns128,Tcomplex80,   Tfloat80,Tcomplex80,   Tcomplex80);

    /* ======================= */

    X(Tfloat32,Tfloat32,  Tfloat32,Tfloat32, Tfloat32);
    X(Tfloat32,Tfloat64,  Tfloat64,Tfloat64, Tfloat64);
    X(Tfloat32,Tfloat80,  Tfloat80,Tfloat80, Tfloat80);

    X(Tfloat32,Timaginary32,  Tfloat32,Timaginary32, Tfloat32);
    X(Tfloat32,Timaginary64,  Tfloat64,Timaginary64, Tfloat64);
    X(Tfloat32,Timaginary80,  Tfloat80,Timaginary80, Tfloat80);

    X(Tfloat32,Tcomplex32,  Tfloat32,Tcomplex32, Tcomplex32);
    X(Tfloat32,Tcomplex64,  Tfloat64,Tcomplex64, Tcomplex64);
    X(Tfloat32,Tcomplex80,  Tfloat80,Tcomplex80, Tcomplex80);

    /* ======================= */

    X(Tfloat64,Tfloat64,  Tfloat64,Tfloat64, Tfloat64);
    X(Tfloat64,Tfloat80,  Tfloat80,Tfloat80, Tfloat80);

    X(Tfloat64,Timaginary32,  Tfloat64,Timaginary64, Tfloat64);
    X(Tfloat64,Timaginary64,  Tfloat64,Timaginary64, Tfloat64);
    X(Tfloat64,Timaginary80,  Tfloat80,Timaginary80, Tfloat80);

    X(Tfloat64,Tcomplex32,  Tfloat64,Tcomplex64, Tcomplex64);
    X(Tfloat64,Tcomplex64,  Tfloat64,Tcomplex64, Tcomplex64);
    X(Tfloat64,Tcomplex80,  Tfloat80,Tcomplex80, Tcomplex80);

    /* ======================= */

    X(Tfloat80,Tfloat80,  Tfloat80,Tfloat80, Tfloat80);

    X(Tfloat80,Timaginary32,  Tfloat80,Timaginary80, Tfloat80);
    X(Tfloat80,Timaginary64,  Tfloat80,Timaginary80, Tfloat80);
    X(Tfloat80,Timaginary80,  Tfloat80,Timaginary80, Tfloat80);

    X(Tfloat80,Tcomplex32,  Tfloat80,Tcomplex80, Tcomplex80);
    X(Tfloat80,Tcomplex64,  Tfloat80,Tcomplex80, Tcomplex80);
    X(Tfloat80,Tcomplex80,  Tfloat80,Tcomplex80, Tcomplex80);

    /* ======================= */

    X(Timaginary32,Timaginary32,  Timaginary32,Timaginary32, Timaginary32);
    X(Timaginary32,Timaginary64,  Timaginary64,Timaginary64, Timaginary64);
    X(Timaginary32,Timaginary80,  Timaginary80,Timaginary80, Timaginary80);

    X(Timaginary32,Tcomplex32,  Timaginary32,Tcomplex32, Tcomplex32);
    X(Timaginary32,Tcomplex64,  Timaginary64,Tcomplex64, Tcomplex64);
    X(Timaginary32,Tcomplex80,  Timaginary80,Tcomplex80, Tcomplex80);

    /* ======================= */

    X(Timaginary64,Timaginary64,  Timaginary64,Timaginary64, Timaginary64);
    X(Timaginary64,Timaginary80,  Timaginary80,Timaginary80, Timaginary80);

    X(Timaginary64,Tcomplex32,  Timaginary64,Tcomplex64, Tcomplex64);
    X(Timaginary64,Tcomplex64,  Timaginary64,Tcomplex64, Tcomplex64);
    X(Timaginary64,Tcomplex80,  Timaginary80,Tcomplex80, Tcomplex80);

    /* ======================= */

    X(Timaginary80,Timaginary80,  Timaginary80,Timaginary80, Timaginary80);

    X(Timaginary80,Tcomplex32,  Timaginary80,Tcomplex80, Tcomplex80);
    X(Timaginary80,Tcomplex64,  Timaginary80,Tcomplex80, Tcomplex80);
    X(Timaginary80,Tcomplex80,  Timaginary80,Tcomplex80, Tcomplex80);

    /* ======================= */

    X(Tcomplex32,Tcomplex32,  Tcomplex32,Tcomplex32, Tcomplex32);
    X(Tcomplex32,Tcomplex64,  Tcomplex64,Tcomplex64, Tcomplex64);
    X(Tcomplex32,Tcomplex80,  Tcomplex80,Tcomplex80, Tcomplex80);

    /* ======================= */

    X(Tcomplex64,Tcomplex64,  Tcomplex64,Tcomplex64, Tcomplex64);
    X(Tcomplex64,Tcomplex80,  Tcomplex80,Tcomplex80, Tcomplex80);

    /* ======================= */

    X(Tcomplex80,Tcomplex80,  Tcomplex80,Tcomplex80, Tcomplex80);

    // "No type is implicitly convertible to noreturn, but noreturn is implicitly convertible to every other type"
    foreach(convertToTy; typeTYs)
        X(Tnoreturn, convertToTy, convertToTy, convertToTy, convertToTy);

    return impCnvTab;
}
