import core.stdc.stdio;

struct S { int a,b,c,d; }

alias int delegate() dg_t;
alias int delegate(int) dg1_t;

void fill()
{
    int[100] x;
}

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

dg_t foo()
{
    int x = 7;

    int bar()
    {
        return x + 3;
    }

    return &bar;
}

void test1()
{
    dg_t dg = foo();
    fill();
    printf("bar = %d\n", dg());
    assert(dg() == 10);
}

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

dg_t foo2()
{
    dg_t abc()
    {
        int x = 7;

        int bar()
        {
            return x + 3;
        }

        return &bar;
    }

    return abc();
}

void test2()
{
    dg_t dg = foo2();
    fill();
    printf("bar = %d\n", dg());
    assert(dg() == 10);
}

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

dg_t foo3()
{
    dg_t abc(int x)
    {
        int bar()
        {
            return x + 3;
        }

        return &bar;
    }

    return abc(7);
}

void test3()
{
    dg_t dg = foo3();
    fill();
    printf("bar = %d\n", dg());
    assert(dg() == 10);
}

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

dg_t foo4()
{
    S s;

    s = S(4,5,6,7);

    dg_t abc(S t)
    {
        int bar()
        {
            return t.d + 3;
        }

        return &bar;
    }

    return abc(s);
}

void test4()
{
    dg_t dg = foo4();
    fill();
    printf("bar = %d\n", dg());
    assert(dg() == 10);
}

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

void test5()
{
    int x = 7;

    dg_t abc(ref int y)
    {
        int bar()
        {
            y += 4;
            return y + 3;
        }

        return &bar;
    }

    dg_t dg = abc(x);
    fill();
    assert(x == 7);
    auto i = dg();
    assert(x == 11);
    assert(i == 14);

    x = 8;
    i = dg();
    assert(x == 12);
    assert(i == 15);
}

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

void test6()
{
    int x = 7;

    dg_t abc(out int y)
    {
        int bar()
        {
            y += 4;
            return y + 3;
        }

        return &bar;
    }

    dg_t dg = abc(x);
    fill();

    assert(x == 0);
    auto i = dg();
    assert(x == 4);
    assert(i == 7);

    x = 8;
    i = dg();
    assert(x == 12);
    assert(i == 15);
}

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

void test7()
{
    int[3] a = [10,11,12];

    dg_t abc(int[3] y)
    {
        int bar()
        {
            y[2] += 4;
            return y[2] + 3;
        }

        return &bar;
    }

    dg_t dg = abc(a);
    fill();

    assert(a[2] == 12);
    auto i = dg();
    assert(a[2] == 12);
    assert(i == 19);
}

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

void test8()
{
    S s = S(7,8,9,10);

    dg_t abc(ref S t)
    {
        int bar()
        {
            t.d += 4;
            return t.c + 3;
        }

        return &bar;
    }

    dg_t dg = abc(s);
    fill();

    assert(s.d == 10);
    auto i = dg();
    assert(s.d == 14);
    assert(i == 12);
}

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

S foo9(out dg_t dg)
{
    S s1 = S(7,8,9,10);

    dg_t abc()
    {
        int bar()
        {
            s1.d += 4;
            return s1.c + 3;
        }

        return &bar;
    }

    dg = abc();
    return s1;
}

void test9()
{
    dg_t dg;

    S s = foo9(dg);
    fill();
    assert(s.a == 7);
    assert(s.b == 8);
    assert(s.c == 9);
    assert(s.d == 10);

    auto i = dg();
    assert(s.d == 10);
    assert(i == 12);
}

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

dg_t foo10()
{
    dg_t abc()
    {
        int x = 7;

        int bar()
        {
            int def()
            {
                return x + 3;
            }
            return def();
        }

        return &bar;
    }

    return abc();
}

void test10()
{
    dg_t dg = foo10();
    fill();
    printf("bar = %d\n", dg());
    assert(dg() == 10);
}


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

dg_t foo11()
{
    int x = 7;

    class T
    {
        int bar()
        {
            return x + 3;
        }
    }

    T t = new T;

    return &t.bar;
}

void test11()
{
    dg_t dg = foo11();
    fill();
    printf("bar = %d\n", dg());
    assert(dg() == 10);
}

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

dg_t foo12()
{
    int x = 7;

    class T
    {
        int bar()
        {
            return x + 3;
        }

        int xyz()
        {
            return bar();
        }
    }

    T t = new T;

    return &t.xyz;
}

void test12()
{
    dg_t dg = foo12();
    fill();
    printf("bar = %d\n", dg());
    assert(dg() == 10);
}

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

dg_t foo13()
{
    int x = 7;

    class T
    {
        int xyz()
        {
            int bar()
            {
                return x + 3;
            }

            return bar();
        }
    }

    T t = new T;

    return &t.xyz;
}

void test13()
{
    dg_t dg = foo13();
    fill();
    printf("bar = %d\n", dg());
    assert(dg() == 10);
}


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

dg_t foo14()
{
    class T
    {
        int xyz()
        {
            int x = 7;

            int bar()
            {
                return x + 3;
            }

            return bar();
        }
    }

    T t = new T;

    return &t.xyz;
}

void test14()
{
    dg_t dg = foo14();
    fill();
    printf("bar = %d\n", dg());
    assert(dg() == 10);
}

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

dg_t foo15()
{
    class T
    {
        int x = 7;

        int xyz()
        {
            int bar()
            {
                return x + 3;
            }

            return bar();
        }
    }

    T t = new T;

    return &t.xyz;
}

void test15()
{
    dg_t dg = foo15();
    fill();
    printf("bar = %d\n", dg());
    assert(dg() == 10);
}

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

dg_t foo16()
{
    int a = 5;

    class T
    {
        int x = 7;

        int xyz()
        {
            int y = 8;
            int bar()
            {
                return a + x + y + 3;
            }

            return bar();
        }
    }

    T t = new T;

    return &t.xyz;
}

void test16()
{
    dg_t dg = foo16();
    fill();
    printf("bar = %d\n", dg());
    assert(dg() == 23);
}

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

dg_t foo17()
{
    int a = 5;

    class T
    {
        int x = 7;

        dg_t xyz()
        {
            int y = 8;

            int bar()
            {
                return a + x + y + 3;
            }

            return &bar;
        }
    }

    T t = new T;

    return t.xyz();
}

void test17()
{
    dg_t dg = foo17();
    fill();
    printf("bar = %d\n", dg());
    assert(dg() == 23);
}

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

dg_t dg18;

void bar18()
{
    int a = 7;
    int foo() { return a + 3; }

    dg18 = &foo;
    int i = dg18();
    assert(i == 10);
}

void test18()
{
    bar18();
    fill();
    int i = dg18();
    assert(i == 10);
}

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

void abc19(void delegate() dg)
{
    dg();
    dg();
    dg();
}

struct S19
{
    static S19 call(int v)
    {
        S19 result;

        result.v = v;
        void nest()
        {
            result.v += 1;
        }
        abc19(&nest);
        return result;
    }
    int a;
    int v;
    int x,y,z;
}

int foo19()
{
    auto s = S19.call(5);
    return s.v;
}

void test19()
{
    int i = foo19();
    printf("%d\n", i);
    assert(i == 8);
}

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

void enforce20(lazy int msg)
{
}


void test20()
{
    int x;
    foreach (j; 0 .. 10)
    {
        printf("%d\n", j);
        assert(j == x);
        x++;
        enforce20(j);
    }
}

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

void thrash21() { char[128] x = '\xfe'; }

void delegate() dg21;
int g_input = 11, g_output;

void f21()
{
    int i = g_input + 2;

    class X
    {
        // both 'private' and 'final' to make non-virtual
        private final void actual()
        {
            g_output = i;
        }

        void go()
        {
            actual();
        }
    }

    dg21 = & (new X).go;
}

void test21()
{
    f21();
    thrash21();
    dg21();
    assert(g_output == 13);
}

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

void thrash22() { char[128] x = '\xfe'; }
int gi22;
void delegate() dg22;

class A22
{
    int x = 42;

    void am()
    {
        int j; /* Making f access this variable causes f's chain to be am's
                  frame.  Otherwise, f's chain would be the A instance. */
        void f()
        {
            int k = j;

            void g()
            {
                class B
                {
                    void bm()
                    {
                        gi22 = x; /* No checkedNestedReference for A.am.this,
                                   so it is never placed in a closure. */
                    }
                }

                (new B).bm();
            }

            dg22 = &g;
        }

        f();
    }
}

void test22()
{
    (new A22).am();
    thrash22();
    dg22();
    assert(gi22 == 42);
}

/************************************/
// 1759

void test1759()
{
    struct S { int a, b, c; }
    struct SS { S obj; }

    static int delegate() makeSum1(S s)
    {
        with (s) return { return a + b + c; };
    }
    static int delegate() makeSum2(S[1] sa)
    {
        with (sa[0]) return { return a + b + c; };
    }
    static int delegate() makeSum3(SS ss)
    {
        with (ss.obj) return { return a + b + c; };
    }
    static int delegate() makeSum4(SS[1] ssa)
    {
        with (ssa[0].obj) return { return a + b + c; };
    }

    S s = {15, 30, 45};
    SS ss = {s};
    int delegate() sum;

    sum = makeSum1(s);      assert(sum() == 90);
    sum = makeSum2([s]);    assert(sum() == 90);
    sum = makeSum3(ss);     assert(sum() == 90);
    sum = makeSum4([ss]);   assert(sum() == 90);
}

/************************************/
// 1841

int delegate() foo1841()
{
    int stack;
    int heap = 3;

    int nested_func()
    {
        ++heap;
        return heap;
    }
    return delegate int() { return nested_func(); };
}

int delegate() foo1841b()
{
    int stack;
    int heap = 7;

    int nested_func()
    {
        ++heap;
        return heap;
    }
    int more_nested() { return nested_func(); }
    return delegate int() { return more_nested(); };
}

void test1841()
{
    auto z = foo1841();
    auto p = foo1841();
    assert(z() == 4);
    p();
    assert(z() == 5);
    z = foo1841b();
    p = foo1841b();
    assert(z() == 8);
    p();
    assert(z() == 9);
}

/************************************/
// 5911

void writeln5911(const(char)[] str) {}

void logout5911(lazy const(char)[] msg) { writeln5911(msg); }

void test5911()
{
    string str = "hello world";
    logout5911((){ return str; }());    // closure 1

    try
    {
        throw new Exception("exception!!");
    }
    catch (Exception e)
    {
        assert(e !is null);
        logout5911(e.toString());       // closure2 SEGV : e is null.
    }
}

/************************************/
// 9685

auto get9685a(alias fun)()
{
    int x = 10;
    struct Foo
    {
        size_t data;

        @property clone()
        {
            return Foo(15);
        }
    }
    return Foo(5);
}
void test9685a()
{
    uint a = 42;
    auto bar = get9685a!(() => a)();
    auto qux = bar.clone;
    //printf("bar context pointer : %p\n", bar.tupleof[$-1]);
    //printf("qux context pointer : %p\n", qux.tupleof[$-1]);
    assert(bar.tupleof[$-1] == qux.tupleof[$-1]);
    assert(qux.data == 15);
}

auto get9685b(alias fun)()
{
    int x = 10;
    struct Foo
    {
        size_t data;

        @property clone()
        {
            return Foo(data + x);
        }
    }
    return Foo(5);
}
void test9685b()
{
    uint a = 42;
    auto bar = get9685b!(() => a)();
    auto qux = bar.clone;
    //printf("bar context pointer : %p\n", bar.tupleof[$-1]);
    //printf("qux context pointer : %p\n", qux.tupleof[$-1]);
    assert(bar.tupleof[$-1] == qux.tupleof[$-1]);
    assert(qux.data == 15);
}

/************************************/
// 12406

auto createDg12406()
{
    static struct Dg
    {
        Dg delegate() action;
    }

    static void fn(void delegate()) { }

    int x; fn({ x++; }); // required

    Dg dg;

    Dg createDg2()
    {
        int x; void unusedFun() { x++; } // required

        return Dg(() => dg); // lambda returns garbage instead of dg
    }

    return dg = Dg(&createDg2);
}

void test12406()
{
    auto dgs = [createDg12406()];
    //printf("dgs[%2d].action = %p:%p\n", 0, dgs[$-1].action.ptr, dgs[$-1].action.funcptr);
    foreach (i; 1 .. 10+1)
    {
        dgs ~= dgs[i-1].action();
        //printf("dgs[%2d].action = %p:%p\n", i, dgs[$-1].action.ptr, dgs[$-1].action.funcptr);
    }

    foreach (i, dgx; dgs)
    {
        if (i % 2 == 0)
        {
            // All closures are equal with dgs[0].
            assert(dgx.action.ptr     is dgs[0].action.ptr);
            assert(dgx.action.funcptr is dgs[0].action.funcptr);    // is: createDg2
        }
        else
        {
            // Each closures has unique context.
            for (size_t j = i + 2; j < dgs.length; j += 2)
                assert(dgx.action.ptr !is dgs[j].action.ptr);
            assert(dgx.action.funcptr is dgs[1].action.funcptr);    // is: lambda () => dg
        }
    }
}

/************************************/
// 14730

void test14730()
{
    static auto makeS(int x)
    {
        struct S
        {
            int n;
            int get() { return x; }     // x will be a closure variable
        }
        return S(x);
    }
    auto s = makeS(1);
    assert(s.get() == 1);
    // By inlining get() function call, it's rewritten to:
    // assert(*(s.tupleof[$-1] + x.offset) == 1);
    // --> In DotVarExp::toElem(), x->offset should be already nonzero.
}

// ----

// This is questionable case. Currently it works without any errors,
// but not sure it's really intentional

struct S14730x(alias f)
{
    auto foo()() { return f(0); }

    void dummy() {}
}

auto makeS14730x() //@nogc
{
    int x = 10;
    S14730x!(a => x) s;
    //assert(s.foo() == 10);
    return s;
}

void test14730x()
{
    auto s = makeS14730x();
    assert(s.tupleof[$-1] !is null);

    // instantiationg foo outside of makeS will place the variable x in closure
    // *after* the semantic3 completion of makeS() function.
    assert(s.foo() == 10);
}

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

int main()
{
    test1();
    test2();
    test3();
    test4();
    test5();
    test6();
    test7();
    test8();
    test9();
    test10();
    test11();
    test12();
    test13();
    test14();
    test15();
    test16();
    test17();
    test18();
    test19();
    test20();
    test21();
    test22();
    test1759();
    test1841();
    test5911();
    test9685a();
    test9685b();
    test12406();
    test14730();
    test14730x();

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