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

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

struct S7511(T)
{
    // this is a pure function for T==int
    T foo(T x)
    {
        return 2 * x;
    }
}

void test7511a() pure
{
    S7511!int s;
    s.foo(2); // error -> OK
}

/**********************************/
// certain case - wrapper range

//import std.range;
@property bool empty(T)(in T[] a) { return !a.length; }
@property ref T front(T)(T[] a) { return a[0]; }
void popFront(T)(ref T[] a) { a = a[1 .. $]; }

struct S(T)
{
    int foo()
    {
        auto t = T();
        return t.bar();
    }
}

struct Wrap(R)
{
    R original;
    this(T : R)(T t) { original = t; }
    this(A...)(A args) { original = R(args); }
    @property auto empty() { return original.empty; }
    @property auto front() { return original.front; }
    void popFront() { original.popFront(); }
}

void test7511b() pure @safe
{
    static struct Iota
    {
        size_t curr;
        size_t max;
        @property bool empty() pure @safe { return curr == max; }
        @property size_t front() pure @safe { return curr; }
        void popFront() pure @safe { ++curr; }
    }
    {
        auto a = Iota(0, 3);
        size_t i = 0;
        foreach (e; a) { assert(e == i++); } // OK
    }
    {
        auto a = Wrap!(int[])([0,1,2]);
        size_t i = 0;
        foreach (e; a) { assert(e == i++); } // errors!
    }
    {
        auto a = Wrap!Iota(0, 3);
        size_t i = 0;
        foreach (e; a) { assert(e == i++); } // errors!
    }
}

/**********************************/
// with attribute inheritance

struct X
{
    static int bar() pure nothrow @safe
    {
        return 1;
    }
}

class Class(T)
{
    int foo()
    {   // inferred to pure nothrow @safe
        return T.bar();
    }
}

alias Class!X C;

class D : C
{
    override int foo()
    {   // inherits attributes from Class!X.foo
        return 2;
    }
}

void test7511c() pure nothrow @safe
{
// Disabled for Bigzilla 9952
/+
    assert((new C()).foo() == 1);
    assert((new D()).foo() == 2);
    static assert(typeof(&C.init.foo).stringof == "int delegate() pure nothrow @safe");
    static assert(typeof(&D.init.foo).stringof == "int delegate() pure nothrow @safe");
+/
}

/**********************************/
// curiously recurring template pattern (CRTP)

class BX(T, bool mutual)
{
    int foo()
    {
        static if (mutual)
            return (cast(T)this).foo();
        else
            return 0;
    }
}

class D1 : BX!(D1, true)
{
    alias typeof(super) B;
    int val;
    this(int n) { val = n; }
    override int foo() { return val; }
}
class D2 : BX!(D2, false)
{
    alias typeof(super) B;
    int val;
    this(int n) { val = n; }
    override int foo() { return val; }
}
class D3 : BX!(D3, true)
{
    alias typeof(super) B;
    int val;
    this(int n) { val = n; }
    override int foo() pure nothrow { return val; }
}
class D4 : BX!(D4, false)
{
    alias typeof(super) B;
    int val;
    this(int n) { val = n; }
    override int foo() pure nothrow { return val; }
}

void test7511d()
{
// Disabled for Bigzilla 9952
/+
    // mutual dependent and attribute inference impure, un-@safe, and may throw is default.
    auto d1 = new D1(10);
    static assert(is(typeof(&d1.B.foo) == int function()));
    static assert(is(typeof(&d1.foo) == int delegate()));
    assert(d1.foo() == 10);

    // no mutual dependent.
    auto d2 = new D2(10);
    static assert(is(typeof(&d2.B.foo) == int function() pure nothrow @safe));
    static assert(is(typeof(&d2  .foo) == int delegate() pure nothrow @safe));
    assert(d2.foo() == 10);

    // mutual dependent with explicit attribute specification.
    auto d3 = new D3(10);
    static assert(is(typeof(&d3.B.foo) == int function() pure nothrow));
    static assert(is(typeof(&d3  .foo) == int delegate() pure nothrow));
    assert(d3.foo() == 10);

    // no mutual dependent with explicit attribute specification.
    auto d4 = new D4(10);
    static assert(is(typeof(&d4.B.foo) == int function() pure nothrow @safe));
    static assert(is(typeof(&d4  .foo) == int delegate() pure nothrow @safe));
    assert(d4.foo() == 10);
+/
}

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

@system void writeln9952(int) {}    // impure throwable

class C9952(T)
{
    T foo()
    {
        return 2;
    }
}

class D9952 : C9952!int
{
    override int foo()
    {
        writeln9952(super.foo());
        return 3;
    }
}

void test9952()
{
    static assert(typeof(&C9952!int.init.foo).stringof == "int delegate()");
    static assert(typeof(&D9952    .init.foo).stringof == "int delegate()");
}

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

template isMutable10373(T)
{
    enum isMutable10373 = !is(T == const) && !is(T == immutable) && !is(T == inout);
}

struct Test10373a(T, int N = 0)
{
    static if (N == 0) T[ ] mBuffer;
    else               T[N] mBuffer;

    static if (is(T == class))
    {}
    else
    {
        T* at_(size_t n) { return &mBuffer[n]; }

        static if (is(typeof(*at_(0) = T.init)))
        {
            T opIndexAssign(T v, size_t i)
            {
                return (*at_(i) = v);
            }
        }
    }
}
struct Test10373b(T, int N = 0)
{
    static if (is(T == class))
    {}
    else
    {
        T* at_(size_t n) { return &mBuffer[n]; }

        static if (is(typeof(*at_(0) = T.init)))
        {
            T opIndexAssign(T v, size_t i)
            {
                return (*at_(i) = v);
            }
        }
    }

    static if (N == 0) T[ ] mBuffer;
    else               T[N] mBuffer;
}
struct Test10373c(T, int N = 0)
{
    static if (is(T == class))
    {}
    else
    {
        T* at_(size_t n) { return &mBuffer[n]; }

        static if (isMutable10373!T)
        {
            T opIndexAssign(T v, size_t i)
            {
                return (*at_(i) = v);
            }
        }
    }

    static if (N == 0) T[ ] mBuffer;
    else               T[N] mBuffer;
}

void test10373()
{
    static assert(is(Test10373a!(int, 2)));
    static assert(is(Test10373b!(int, 2)));
    static assert(is(Test10373c!(int, 2)));

    Test10373a!(int, 2) testa;  // dmd2.062:OK  dmd2.063:OK
    Test10373b!(int, 2) testb;  // dmd2.062:OK  dmd2.063:NG
    Test10373c!(int, 2) testc;  // dmd2.062:OK  dmd2.063:OK
}

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

auto foo10329(T)(T arg)
{
    auto bar()
    {
        return arg;
    }
    static assert(is(typeof(&bar) == T delegate() pure nothrow @nogc @safe));
    return bar();
}

auto make10329(T)(T arg)
{
    struct S
    {
        auto front() { return T.init; }
    }
    S s;
    static assert(is(typeof(&s.front) == T delegate() pure nothrow @nogc @safe));
    return s;
}

void test10329() pure nothrow @safe
{
    assert(foo10329(1) == 1);

    auto s = make10329(1);
    assert(s.front() == 0);
}

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

class Foo11896a(T = int)
{
    static if (!__traits(isVirtualMethod, zoo)) {} else { Undefined x; }

    static void bar() {}
    static void bar(Foo11896a foo) {}

    static void zoo()
    {
        bar(new Foo11896a);
    }
}
Foo11896a!(int) baz11896a;

// ----

Frop11896b!(int) frop11896b;

mixin template baz11896b()
{
    public void bar11896b() {}
}

mixin baz11896b;

class Foo11896b(T)
{
    static if (! __traits(isVirtualMethod, zoo)) {}

    static void zoo()
    {
        bar11896b();
    }
}

class Frop11896b(T) : Foo11896b!T {}

// ----

static bool flag11896c = false;

class Bar11896c {}

class Foo11896c(T = Bar11896c)
{
    static if (! __traits(isVirtualMethod, foo)) {}
    alias Foo11896c!(T) this_type;
    this()
    {
        flag11896c = true;
    }
    static public this_type foo()
    {
        auto c = new this_type();
        return flag11896c ? c : null;
    }
}

void test11896c()
{
    alias Foo11896c!Bar11896c FooBar;
    assert(FooBar.foo() !is null);
}

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

void f12392(T)() {}
alias fa12392 = f12392;

void test12392() pure nothrow @safe
{
    fa12392!int();
}

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

int main()
{
    test7511a();
    test7511b();
    test7511c();
    test7511d();
    test9952();
    test10373();
    test10329();
    test11896c();

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