// PERMUTE_ARGS: -O -inline

// Test value-range propagation.
// https://issues.dlang.org/show_bug.cgi?id=3147
// https://issues.dlang.org/show_bug.cgi?id=6000
// https://issues.dlang.org/show_bug.cgi?id=5225

void add()
{
    byte x, y;
    short a = x + y;
}

void leftShift()
{
    byte x, y;
    short z = x << 1;
}

void leftShiftFail()
{
    {
        ubyte x, y;
        ushort z;
        static assert(!__traits(compiles, z = x << y));
        // 1 << 31 surely overflows the range of 'ushort'.
    }
    {
        ulong a, b;
        int res;
        static assert(!__traits(compiles, res = a << (b % 65U)));
    }
}

void rightShiftFail()
{
    {
        short x;
        byte y, z;
        static assert(!__traits(compiles, z = x >> y));
        // [this passes in 2.053.]
    }
    {
        ulong a, b;
        int res;
        static assert(!__traits(compiles, res = a >> (b % 65U)));
    }
}

void rightShift()
{
    ushort x;
    ubyte y = x >> 16;
}

void unsignedRightShiftFail()
{
    int x;
    ubyte y;
    static assert(!__traits(compiles, y = x >>> 2));
    // [this passes in 2.053.]
}

void subtract()
{
    ubyte x, y;
    short z = x - y;
}

void multiply()
{
    byte x, y;
    short z = x * y;
}

void subMulFail()
{
    ubyte x, y;
    ubyte z;
    static assert(!__traits(compiles, z = x - y));
    static assert(!__traits(compiles, z = x * y));
    // [these pass in 2.053.]
}

void multiplyNeg1()
{
    byte b;
    b = -1 + (b * -1);
    static assert(!__traits(compiles, b = -1 + b * ulong.max));
}

void divide()
{
    short w;
    byte y = w / 300;
}

void divideFail()
{
    short w;
    byte y;
    static assert(!__traits(compiles, y = w / -1));
}

void plus1Fail()
{
    byte u, v;
    static assert(!__traits(compiles, v = u + 1));
    // [these pass in 2.053.]
}

void modulus()
{
    int x;
    byte u = x % 128;
}

void modulus_bug6000a()
{
    ulong t;
    uint u = t % 16;
}

void modulus_bug6000b()
{
    long n = 10520;
    ubyte b;
    static assert(!__traits(compiles, b = n % 10));
}

void modulus2()
{
    short s;
    byte b = byte.max;
    byte c = s % b;
}

void modulus3()
{
    int i;
    short s = short.max;
    short t = i % s;
}

void modulus4()
{
    uint i;
    ushort s;
    short t;
    static assert(!__traits(compiles, t = i % s));
}

void modulus5()
{
    short a;
    byte foo = (a - short.max - 1) % 127;
}

void modulusFail()
{
    int i;
    short s;
    byte b;
    static assert(!__traits(compiles, b = i % s));
    static assert(!__traits(compiles, b = i % 257));
    // [these pass in 2.053.]
}

void bitwise()
{
    ubyte a, b, c;
    uint d;
    c = a & b;
    c = a | b;
    c = a ^ b;
    c = d & 0xff;
    // [these pass in 2.053.]
}

void bitAnd()
{
    byte c;
    int d;
    c = (0x3ff_ffffU << (0&c)) & (0x4000_0000U << (0&c));
    // the result of the above is always 0 :).
}

void bitAndTest()
{
    {
        ushort a, b;
        byte res = ((a % 7) - 6) & ((b % 7) - 6);
    }
    {
        // rhs[-128..127] outside range of lhs[0..255]
        //   -> calls byte.implicitConvTo(ubyte) => MATCH.convert
        byte a, b;
        ubyte res;

        res = cast(byte)(a + 5) & b;
        res = cast(byte)(a - 5) & b;
        res = cast(byte)(a / 5) & b;
        res = cast(byte)(a * 5) & b;
        res = cast(byte)(a % 5) & b;
    }
}

void bitOrFail()
{
    {
        ubyte c;
        static assert(!__traits(compiles, c = c | 0x100));
        // [this passes in 2.053.]
    }
    {
        byte a, b;
        ubyte res;

        static assert(!__traits(compiles, res = (a + 5) | b)); // [-128..255]
        static assert(!__traits(compiles, res = (a - 5) | b)); // [-133..127]
        static assert(!__traits(compiles, res = (a / 5) | b)); // [-128..127]
        static assert(!__traits(compiles, res = (a * 5) | b)); // [-640..639]
        static assert(!__traits(compiles, res = (a % 5) | b)); // [-128..127]
    }
}

void bitAndOr()
{
    ubyte c;
    c = (c | 0x1000) & ~0x1000;
}

void bitOrTest()
{
    {
        // Tests condition for different signs between min & max
        // ((imin.negative ^ imax.negative) == 1 && (rhs.imin.negative ^ rhs.imax.negative) == 1
        ushort a, b;
        byte res = ((a % 127) - 126) | ((b % 6) - 5);
    }
    {
        // rhs[-128..127] outside range of lhs[0..255]
        //   -> calls byte.implicitConvTo(ubyte) => MATCH.convert
        byte a, b, c;
        ubyte res;

        res = cast(byte)(a + 5) | b;
        res = cast(byte)(a - 5) | b;
        res = cast(byte)(a / 5) | b;
        res = cast(byte)(a * 5) | b;
        res = cast(byte)(a % 5) | b;
    }
}

void bitAndFail()
{
    {
        int d;
        short s;
        byte c;
        static assert(!__traits(compiles, c = d & s));
        static assert(!__traits(compiles, c = d & 256));
        // [these pass in 2.053.]
    }
    {
        byte a, b;
        ubyte res;

        static assert(!__traits(compiles, res = (a + 5) & b)); // [-128..132]
        static assert(!__traits(compiles, res = (a - 5) & b)); // [-256..127]
        static assert(!__traits(compiles, res = (a / 5) & b)); // [-128..127]
        static assert(!__traits(compiles, res = (a * 5) & b)); // [-640..635]
        static assert(!__traits(compiles, res = (a % 5) & b)); // [-128..127]
    }
}

void bitXor()
{
    {
        ushort s;
        ubyte c;
        c = (0xffff << (s & 0)) ^ 0xff00;
    }
    {
        // rhs[-128..127] outside range of lhs[0..255]
        //   -> calls byte.implicitConvTo(ubyte) => MATCH.convert
        byte a, b, c;
        ubyte res;

        res = cast(byte)(a + 5) ^ b;
        res = cast(byte)(a - 5) ^ b;
        res = cast(byte)(a / 5) ^ b;
        res = cast(byte)(a * 5) ^ b;
        res = cast(byte)(a % 5) ^ b;
    }
}

void bitXorFail()
{
    {
        byte a, b;
        ubyte res;

        static assert(!__traits(compiles, res = (a + 5) ^ b)); // [-256..255]
        static assert(!__traits(compiles, res = (a - 5) ^ b)); // [-256..255]
        static assert(!__traits(compiles, res = (a / 5) ^ b)); // [-128..127]
        static assert(!__traits(compiles, res = (a * 5) ^ b)); // [-640..1023]
        static assert(!__traits(compiles, res = (a % 5) ^ b)); // [-128..127]
    }
}

void bitComplement()
{
    int i;
    ubyte b = ~(i | ~0xff);
}

void bitComplementFail()
{
    ubyte b;
    static assert(!__traits(compiles, b = ~(b | 1)));
    // [this passes in 2.053.]
}

void negation()
{
    int x;
    byte b = -(x & 0x7);
}

void negationFail()
{
    int x;
    byte b;
    static assert(!__traits(compiles, b = -(x & 255)));
    // [this passes in 2.053.]
}

short bug5225(short a) {
    return a>>1;
}

short bug1977_comment5(byte i) {
  byte t = 1;
  short o = t - i;
  return o;
}

void testDchar()
{
    dchar d;
    uint i;
    /+
    static assert(!__traits(compiles, d = i));
    static assert(!__traits(compiles, d = i & 0x1fffff));
    +/
    d = i % 0x110000;
}

void bug1977_comment11()
{
    uint a;
    byte b = a & 1;
    // [this passes in 2.053.]
}

void bug1977_comment20()
{
    long a;
    int b = a % 1000;
}

/******************************************/
// https://issues.dlang.org/show_bug.cgi?id=9617

void test9617()
{
    void f1(int) {}
    void f2(short) {}
    void f3(byte) {}

    // Why these calls are accepted?
    static assert(!__traits(compiles, f1(ulong.max)));
    static assert(!__traits(compiles, f2(ulong.max)));
    static assert(!__traits(compiles, f3(ulong.max)));

    // But, if argument is not constant value, compilation fails.
    ulong x;
    static assert(!__traits(compiles, f1(x)));  // is not callable using argument types (ulong)
    static assert(!__traits(compiles, f2(x)));  // is not callable using argument types (ulong)
    static assert(!__traits(compiles, f3(x)));  // is not callable using argument types (ulong)

    void f4(uint) {}
    void f5(ushort) {}
    void f6(ubyte) {}

    // If parameter type is unsigned, it is collectly rejected
    static assert(!__traits(compiles, f4(ulong.max)));  // is not callable using argument types (ulong)
    static assert(!__traits(compiles, f5(ulong.max)));  // is not callable using argument types (ulong)
    static assert(!__traits(compiles, f6(ulong.max)));  // is not callable using argument types (ulong)
}

//import std.typetuple;
template TypeTuple(T...) { alias TypeTuple = T; }
template staticIota(size_t end)
{
    static if (0 < end)
        alias staticIota = TypeTuple!(staticIota!(end - 1), end - 1);
    else
        alias staticIota = TypeTuple!();
}
void test9617a()
{
    alias Repr = TypeTuple!(
        byte,   "127",      // T and literal representation of T.max
        ubyte,  "255",
        short,  "32767",
        ushort, "65535",
        int,    "2147483647",
        uint,   "4294967295",
        long,   "9223372036854775807",
        ulong,  "18446744073709551615"  // "" or "L" -> "signed integral overflow"
    );
    alias Indices = staticIota!(Repr.length / 2);

    foreach (t; Indices)
    {
        alias T = Repr[t * 2];
        void func(T)(T) {}
        alias func!T f;

        foreach (r; Indices)
        {
            alias S = Repr[r * 2];
            S src = S.max;

            enum x = Repr[r * 2 + 1];
            foreach (repr; TypeTuple!(S.stringof~".max", x~"", x~"U", x~"L", x~"LU"))
            {
                static if (S.sizeof != T.sizeof)
                static if (is(typeof(mixin(repr)) R))
                {
                    // "Compilable" test should be equal, even if
                    // the given argument is either constant or runtime variable.
                    enum ct = __traits(compiles, f( mixin(repr) ));
                    enum rt = __traits(compiles, f( src ));

                    static assert(ct == rt);
                    //import std.string;
                    //enum msg = format("%6s.max to %-6s variable/constant = %d/%d, constant_repr = (%s) %s",
                    //                    S.stringof, T.stringof, rt, ct, R.stringof, repr);
                    //static if (ct != rt) pragma(msg, msg);
                }
            }
        }
    }
}

void test10018(ubyte value)
{
    const int c = value;
    ubyte b = c;
    static assert(!__traits(compiles, b = c - 1));
    static assert(!__traits(compiles, b = c + 1));
    immutable int i = value;
    b = i;
    static assert(!__traits(compiles, b = i - 1));
    static assert(!__traits(compiles, b = i + 1));
}

void test13001(bool unknown)
{
    foreach (const i; 0..unknown?2:3)
    {
        ubyte b = i;
        static assert(!__traits(compiles, b = i - 1));
        b = i + 253;
        static assert(!__traits(compiles, b = i + 254));
    }
}

void test10310()
{
    int y;
    ubyte x = ((y & 252) ^ 2) + 1;
}

// https://issues.dlang.org/show_bug.cgi?id=15289
void test15289a()
{
    int [] arr = [1, 2, 3, 4];
    uint foo = 50 / arr.length;
}

void test15289b()
{
    int [] arr = [1, 2, 3, 4];
    uint foo = 50 % arr.length;
}

void testShiftRightOnNegative()
{
    int neg = -1;
    uint[] arr = [1, 2, 3];
    ubyte b;
    // Shift with negative value returns value in range [0, ulong.max]
    static assert(!__traits(compiles, b = arr.length >> neg));
    static assert(!__traits(compiles, b = arr.length << neg));
}
