/*
RUN_OUTPUT:
---
Success
---
*/
extern (C) int printf(const char* fmt, ...);

int pass(int n){ return n; }

struct X
{
    int m;

    int opIndex(int m, int n)
    {
        return n;
    }
}

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

struct S1f
{
    int opDispatch(string name, A...)(A args)
    {
      static if (args.length)
        return args[0];
      else
        return 0;
    }
}
struct S1p
{
    @property int opDispatch(string name, A...)(A args)
    {
      static if (args.length)
        return args[0];
      else
        return 0;
    }
}
void test1()
{
    S1f s1f;
    assert(s1f.func() == 0);            // ok -> ok
    assert(s1f.func(1) == 1);           // ok -> ok
    assert(pass(s1f.func()) == 0);      // ok -> ok
    assert(pass(s1f.func(1)) == 1);     // ok -> ok
    assert(X(s1f.func()).m == 0);
    assert(X()[0, s1f.func()] == 0);

    S1p s1p;
    assert(s1p.prop == 0);              // ok   -> ok
    assert((s1p.prop = 1) == 1);        // CTng -> CTng
    assert(pass(s1p.prop) == 0);        // ok   -> ok
    assert(pass(s1p.prop = 2) == 2);    // CTng -> CTng
    assert(X(s1p.prop).m == 0);
    assert(X()[0, s1p.prop] == 0);
}

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

struct S2f
{
    template opDispatch(string name)
    {
        int opDispatch(A...)(A args)
        {
          static if (args.length)
            return args[0];
          else
            return 0;
        }
    }
}
struct S2p
{
    template opDispatch(string name)
    {
        @property int opDispatch(A...)(A args)
        {
          static if (args.length)
            return args[0];
          else
            return 0;
        }
    }
}
void test2()
{
    S2f s2f;
    assert(s2f.func() == 0);            // ok -> ok
    assert(s2f.func(1) == 1);           // ok -> ok
    assert(pass(s2f.func()) == 0);      // ok -> ok
    assert(pass(s2f.func(1)) == 1);     // ok -> ok
    assert(X(s2f.func()).m == 0);
    assert(X()[0, s2f.func()] == 0);

    S2p s2p;
    assert(s2p.prop == 0);              // CTng -> ok
    assert((s2p.prop = 1) == 1);        // ok   -> ok
    assert(pass(s2p.prop) == 0);        // CTng -> ok
    assert(pass(s2p.prop = 2) == 2);    // ok   -> ok
    assert(X(s2p.prop).m == 0);
    assert(X()[0, s2p.prop] == 0);
}

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

struct S3f
{
    template opDispatch(string name)
    {
        template opDispatch(T)
        {
            int opDispatch(A...)(A args)
            {
              static if (args.length)
                return args[0];
              else
                return 0;
            }
        }
    }
}
struct S3p
{
    template opDispatch(string name)
    {
        template opDispatch(T)
        {
            @property int opDispatch(A...)(A args)
            {
              static if (args.length)
                return args[0];
              else
                return 0;
            }
        }
    }
}
void test3()
{
    S3f s3f;
    assert(s3f.func!int() == 0);            // ok -> ok
    assert(s3f.func!int(1) == 1);           // ok -> ok
    assert(pass(s3f.func!int()) == 0);      // ok -> ok
    assert(pass(s3f.func!int(1)) == 1);     // ok -> ok
    assert(X(s3f.func!int()).m == 0);
    assert(X()[0, s3f.func!int()] == 0);

    S3p s3p;
    assert(s3p.prop!int == 0);              // CTng -> ok
    assert((s3p.prop!int = 1) == 1);        // ok   -> ok
    assert(pass(s3p.prop!int) == 0);        // CTng -> ok
    assert(pass(s3p.prop!int = 2) == 2);    // ok   -> ok
    assert(X(s3p.prop!int).m == 0);
    assert(X()[0, s3p.prop!int] == 0);
}

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

struct S4f
{
    ref int opDispatch(string name, A...)(A args)
    {
        static int n;
        n = args.length;
        return n;
    }
}
struct S4p
{
    @property ref int opDispatch(string name, A...)(A args)
    {
        static int n;
        n = args.length;
        return n;
    }
}
void test4()
{
    S4f s4f;
    assert(s4f.func == 0);          // getter
    assert((s4f.func = 1) == 1);    // setter

    S4p s4p;
    assert(s4p.prop == 0);          // getter
    assert((s4p.prop = 1) == 1);    // setter
}

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

struct S5f
{
    template opDispatch(string name)
    {
        ref int opDispatch(A...)(A args)
        {
            static int n;
            n = args.length;
            return n;
        }
    }
}
struct S5p
{
    template opDispatch(string name)
    {
        @property ref int opDispatch(A...)(A args)
        {
            static int n;
            n = args.length;
            return n;
        }
    }
}
void test5()
{
    S5f s5f;
    assert(s5f.prop == 0);          // getter   ng -> ok
    assert((s5f.prop = 1) == 1);    // setter

    S5p s5p;
    assert(s5p.prop == 0);          // getter   ng -> ok
    assert((s5p.prop = 1) == 1);    // setter
}

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

struct S6f
{
    template opDispatch(string name)
    {
        template opDispatch(T)
        {
            ref int opDispatch(A...)(A args)
            {
                static int n;
                n = args.length;
                return n;
            }
        }
    }
}
struct S6p
{
    template opDispatch(string name)
    {
        template opDispatch(T)
        {
            @property ref int opDispatch(A...)(A args)
            {
                static int n;
                n = args.length;
                return n;
            }
        }
    }
}
void test6()
{
    S6f s6f;
    assert(s6f.prop!int == 0);          // getter   ng -> ok
    assert((s6f.prop!int = 1) == 1);    // setter

    S6p s6p;
    assert(s6p.prop!int == 0);          // getter   ng -> ok
    assert((s6p.prop!int = 1) == 1);    // setter
}

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

struct Foo7578
{
    static int[] opDispatch(string op, Args...)(Args)
    {
        return [0];
    }
}

void test7578()
{
    Foo7578.attrs[0] = 1;
}

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

int main()
{
    test1();
    test2();
    test3();
    test4();
    test5();
    test6();
    test7578();

    printf("Success\n");
    return 0;
}
