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

pure nothrow @safe int h6265() {
    return 1;
}
int f6265a(alias g)() {
    return g();
}
pure nothrow @safe int i6265a() {
    return f6265a!h6265();
}

int f6265b()() {
    return h6265();
}
pure nothrow @safe int i6265b() {
    return f6265b();
}

pure nothrow @safe int i6265c() {
    return {
        return h6265();
    }();
}

/***************************************************/
// Make sure a function is not infered as pure if it isn't.

int fNPa() {
    return 1;
}
int gNPa()() {
    return fNPa();
}
static assert( __traits(compiles, function int ()         { return gNPa(); }));
static assert(!__traits(compiles, function int () pure    { return gNPa(); }));
static assert(!__traits(compiles, function int () nothrow { return gNPa(); }));
static assert(!__traits(compiles, function int () @safe   { return gNPa(); }));

/***************************************************/
// Need to ensure the comment in Expression::checkPurity is not violated.

void fECPa() {
    void g()() {
        void h() {
        }
        h();
    }
    static assert( is(typeof(&g!()) == void delegate() pure nothrow @nogc @safe));
    static assert(!is(typeof(&g!()) == void delegate()));
}

void fECPb() {
    void g()() {
        void h() {
        }
        fECPb();
    }
    static assert(!is(typeof(&g!()) == void delegate() pure));
    static assert( is(typeof(&g!()) == void delegate()));
}

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

pure bool foo5635(R = int)(string x)
{
    bool result = false;
    foreach (dchar d; x)
        result = true;
    return result;
}

void test5635()
{
    foo5635("hi");
}

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

auto bug5936c(R)(R i) @safe pure nothrow {
    return true;
}
static assert( bug5936c(0) );

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

void bug6351(alias dg)()
{
    dg();
}

void test6351()
{
    void delegate(int[] a...) deleg6351 = (int[] a...){};
    alias bug6351!(deleg6351) baz6531;
}

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

void    impure6359()      nothrow @safe @nogc {}
void throwable6359() pure         @safe @nogc {}
void    system6359() pure nothrow       @nogc {}
void    gcable6359() pure nothrow @safe       {}

int global6359;

void f6359() pure nothrow @safe @nogc
{
    static assert(!__traits(compiles,    impure6359()));
    static assert(!__traits(compiles, throwable6359()));
    static assert(!__traits(compiles,    system6359()));
    static assert(!__traits(compiles,    gcable6359()));
    static assert(!__traits(compiles,    global6359++));

    static assert(!__traits(compiles, {    impure6359(); }()));
    static assert(!__traits(compiles, { throwable6359(); }()));
    static assert(!__traits(compiles, {    system6359(); }()));
    static assert(!__traits(compiles, {    gcable6359(); }()));
    static assert(!__traits(compiles, {    global6359++; }()));
}

void g6359()() pure nothrow @safe @nogc
{
    static assert(!__traits(compiles,    impure6359()));
    static assert(!__traits(compiles, throwable6359()));
    static assert(!__traits(compiles,    system6359()));
    static assert(!__traits(compiles,    gcable6359()));
    static assert(!__traits(compiles,    global6359++));

    static assert(!__traits(compiles, {    impure6359(); }()));
    static assert(!__traits(compiles, { throwable6359(); }()));
    static assert(!__traits(compiles, {    system6359(); }()));
    static assert(!__traits(compiles, {    gcable6359(); }()));
    static assert(!__traits(compiles, {    global6359++; }()));
}

// attribute inference is not affected by the expressions inside __traits(compiles)
void h6359()()
{
    static assert( __traits(compiles,    impure6359()));
    static assert( __traits(compiles, throwable6359()));
    static assert( __traits(compiles,    system6359()));
    static assert( __traits(compiles,    gcable6359()));
    static assert( __traits(compiles,    global6359++));

    static assert( __traits(compiles, {    impure6359(); }()));
    static assert( __traits(compiles, { throwable6359(); }()));
    static assert( __traits(compiles, {    system6359(); }()));
    static assert( __traits(compiles, {    gcable6359(); }()));
    static assert( __traits(compiles, {    global6359++; }()));
}

void test6359() pure nothrow @safe @nogc
{
    f6359();
    g6359();
    h6359();
}

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

template map7017(fun...) if (fun.length >= 1)
{
    auto map7017()
    {
        struct Result {
            this(int dummy){}   // impure member function -> inferred to pure by fixing https://issues.dlang.org/show_bug.cgi?id=10329
        }
        return Result(0);   // impure call -> inferred to pure by fixing https://issues.dlang.org/show_bug.cgi?id=10329
    }
}

int foo7017(immutable int x) pure nothrow { return 1; }

void test7017a() pure
{
    int bar7017(immutable int x) pure nothrow { return 1; }

    static assert(__traits(compiles, map7017!((){})()));
    static assert(__traits(compiles, map7017!q{ 1 }()));
    static assert(__traits(compiles, map7017!foo7017()));
    static assert(__traits(compiles, map7017!bar7017()));
}

/***************************************************/
// https://issues.dlang.org/show_bug.cgi?id=7017 (little simpler cases)

auto map7017a(alias fun)() { return fun();     }    // depends on purity of fun
auto map7017b(alias fun)() { return;           }    // always pure
auto map7017c(alias fun)() { return yyy7017(); }    // always impure

int xxx7017() pure { return 1; }
int yyy7017() { return 1; }

void test7017b() pure
{
    static assert( __traits(compiles, map7017a!xxx7017() ));
    static assert(!__traits(compiles, map7017a!yyy7017() ));

    static assert( __traits(compiles, map7017b!xxx7017() ));
    static assert( __traits(compiles, map7017b!yyy7017() ));

    static assert(!__traits(compiles, map7017c!xxx7017() ));
    static assert(!__traits(compiles, map7017c!yyy7017() ));
}

/***************************************************/
// Test case from std.process

auto escapeArgumentImpl(alias allocator)()
{
    return allocator();
}

auto escapeShellArgument(alias allocator)()
{
    return escapeArgumentImpl!allocator();
}

pure string escapeShellArguments()
{
    char[] allocator()
    {
        return new char[1];
    }

    /* Both escape!allocator and escapeImpl!allocator are impure,
     * but they are nested template function that instantiated here.
     * Then calling them from here doesn't break purity.
     */
    return escapeShellArgument!allocator();
}

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

void test8234()
{
    immutable int x = 0;

    alias FP = typeof({ enum e = x; return e; });
    static assert(is(FP : int function()));

    auto fp = { enum e = x; return e; };
    static assert(is(typeof(fp) : int function()));

    alias DG = typeof({ auto e = x; return e; });
    static assert(is(DG : int delegate()));

    auto dg = { auto e = x; return e; };
    static assert(is(typeof(dg) : int delegate()));
}

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

import core.demangle : demangle;

void foo8504()()
{
    static assert(typeof(foo8504!()).stringof == "void()");
    static assert(typeof(foo8504!()).mangleof == "FZv");
//    static assert(demangle(foo8504!().mangleof) == "void testInference.foo8504!().foo8504()");
}

auto toDelegate8504a(F)(auto ref F fp) { return fp; }
   F toDelegate8504b(F)(auto ref F fp) { return fp; }

extern(C) void testC8504() {}

void test8504()
{
    static assert(typeof(foo8504!()).stringof == "pure nothrow @nogc @safe void()");
    static assert(typeof(foo8504!()).mangleof == "FNaNbNiNfZv");
    static assert(demangle(foo8504!().mangleof) == "pure nothrow @nogc @safe void testInference.foo8504!().foo8504()");

    auto fp1 = toDelegate8504a(&testC8504);
    auto fp2 = toDelegate8504b(&testC8504);
    static assert(is(typeof(fp1) == typeof(fp2)));
    static assert(typeof(fp1).stringof == "extern (C) void function()");
    static assert(typeof(fp2).stringof == "extern (C) void function()");
    static assert(typeof(fp1).mangleof == "PUZv");
    static assert(typeof(fp2).mangleof == "PUZv");
}

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

alias bool delegate(in int) pure Bar8751;
Bar8751 foo8751a(immutable int x) pure
{
    return y => x > y; // OK
}
Bar8751 foo8751b(const int x) pure
{
    return y => x > y; // error -> OK
}

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

alias bool delegate(in int) pure Dg8793;
alias bool function(in int) pure Fp8793;

Dg8793 foo8793fp1(immutable Fp8793 f) pure { return x => (*f)(x); } // OK
Dg8793 foo8793fp2(    const Fp8793 f) pure { return x => (*f)(x); } // OK

Dg8793 foo8793dg1(immutable Dg8793 f) pure { return x => f(x); } // OK
Dg8793 foo8793dg2(    const Dg8793 f) pure { return x => f(x); } // OK <- error

Dg8793 foo8793pfp1(immutable Fp8793* f) pure { return x => (*f)(x); } // OK
Dg8793 foo8793pdg1(immutable Dg8793* f) pure { return x => (*f)(x); } // OK

Dg8793 foo8793pfp2(const Fp8793* f) pure { return x => (*f)(x); } // OK <- error
Dg8793 foo8793pdg2(const Dg8793* f) pure { return x => (*f)(x); } // OK <- error

// general case for the hasPointer type
Dg8793 foo8793ptr1(immutable int* p) pure { return x => *p == x; } // OK

Dg8793 foo8793ptr2(const int* p) pure { return x => *p == x; } // OK <- error

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

struct A9072(T)
{
    this(U)(U x) {}
    ~this() {}
}
void test9072()
{
    A9072!int a = A9072!short();
}

/***************************************************/
// https://issues.dlang.org/show_bug.cgi?id=5933
// https://issues.dlang.org/show_bug.cgi?id=8504
// Template attribute inferrence doesn't work

int foo5933()(int a) { return a*a; }
struct S5933
{
    double foo()(double a) { return a * a; }
}
// outside function
static assert(typeof(foo5933!()).stringof == "pure nothrow @nogc @safe int(int a)");
static assert(typeof(S5933.init.foo!()).stringof == "pure nothrow @nogc @safe double(double a)");

void test5933()
{
    // inside function
    static assert(typeof(foo5933!()).stringof == "pure nothrow @nogc @safe int(int a)");
    static assert(typeof(S5933.init.foo!()).stringof == "pure nothrow @nogc @safe double(double a)");
}

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

void test9148a() pure
{
    static int g;
    int x;

    void foo1() /+pure+/
    {
        static assert(!__traits(compiles, g++));
        x++;
    }
    void foo2() pure
    {
        static assert(!__traits(compiles, g++));
        x++;
    }
    foo1();
    static assert(is(typeof(&foo1) == void delegate() pure nothrow @nogc @safe));
    foo2();
    static assert(is(typeof(&foo2) == void delegate() pure nothrow @nogc @safe));

    void bar1() immutable /+pure+/
    {
        static assert(!__traits(compiles, g++));
        static assert(!__traits(compiles, x++));
    }
    void bar2() immutable pure
    {
        static assert(!__traits(compiles, g++));
        static assert(!__traits(compiles, x++));
    }
    bar1();
    static assert(is(typeof(&bar1) == void delegate() pure immutable nothrow @nogc @safe));
    bar2();
    static assert(is(typeof(&bar2) == void delegate() pure immutable nothrow @nogc @safe));

    struct S
    {
        void foo1() /+pure+/
        {
            static assert(!__traits(compiles, g++));
            x++;
        }
        void foo2() pure
        {
            static assert(!__traits(compiles, g++));
            x++;
        }
        void bar1() immutable /+pure+/
        {
            static assert(!__traits(compiles, g++));
            static assert(!__traits(compiles, x++));
        }
        void bar2() immutable pure
        {
            static assert(!__traits(compiles, g++));
            static assert(!__traits(compiles, x++));
        }
    }

    S sm;
    sm.foo1();
    static assert(is(typeof(&sm.foo1) == void delegate() pure));
    sm.foo2();
    static assert(is(typeof(&sm.foo2) == void delegate() pure));

    immutable S si;
    si.bar1();
    static assert(is(typeof(&si.bar1) == void delegate() pure immutable));
    si.bar2();
    static assert(is(typeof(&si.bar2) == void delegate() pure immutable));
}

// ----
// inheritance of pure and @safe

void test9148b() pure nothrow @nogc @safe
{
    void nf() {}
    static assert(is(typeof(&nf) == void delegate() pure nothrow @nogc @safe));

    struct NS
    {
        void mf() {}
        static void sf() {}
    }
    NS ns;
    static assert(is(typeof(&ns.mf) == void delegate() pure nothrow @nogc @safe));
    static assert(is(typeof(&NS.sf) == void function() pure nothrow @nogc @safe));

    static void sf() {}
    static assert(is(typeof(&sf) == void function() pure nothrow @nogc @safe));

    static struct SS
    {
        void mf() {}
        static void sf() {}
    }
    SS ss;
    static assert(is(typeof(&ss.mf) == void delegate() pure nothrow @nogc @safe));
    static assert(is(typeof(&SS.sf) == void function() pure nothrow @nogc @safe));
}

void impureSystem9148b() {}
void func9148b()()
{
    void bar()  // do not inherit PUREfwdref
    {
        static assert(is(typeof(&bar) == void delegate()));
        impureSystem9148b();
    }
    static assert(is(typeof(&bar) == void delegate()));
}
static assert(is(typeof(&func9148b!()) == void function() pure nothrow @nogc @safe));

// ----
// from fail_compilation/fail283.d

pure int double_sqr9148c(int x)
{
    int y = x;
    void do_sqr() pure { y *= y; }
    do_sqr();
    return y;
}

void test9148c()
{
    assert(double_sqr9148c(10) == 100);
}

// ----
// from fail_compilation/fail348.d

void test9148d() pure
{
    void g()    // implicitly marked as 'pure'
    {
        void h() pure
        {
            // i() and j() are implicitly marked as 'pure'
            void i() { }
            void j() { i(); g(); }  // can call i() and g()
        }
    }
}

void test9148e()
{
    int x;
    static assert(is(typeof((int a){ return a + x; }) == int delegate(int) pure nothrow @nogc @safe));

    auto dg = (int a){ return a + x; };
    static assert(is(typeof(dg) == int delegate(int) pure nothrow @nogc @safe));
}

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

struct S12912(alias fun)
{
    void f() { fun(); }
}

class C12912
{
    int n;

    void f() pure
    {
        S12912!(() => n) s;
        // Here lambda should be inferred to weak purity.

        s.f();
        // And this call will be a pure member function call.
    }
}

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

void impure10002() {}
void remove10002(alias pred, bool impure = false, Range)(Range range)
{
    pred(range[0]);
    static if (impure) impure10002();
}
class Node10002
{
    Node10002 parent;
    Node10002[] children;

    void foo() pure
    {
        parent.children.remove10002!(n => n is parent)();
        remove10002!(n => n is parent)(parent.children);
        static assert(!__traits(compiles, parent.children.remove10002x!(n => n is parent, true)()));
        static assert(!__traits(compiles, remove10002x!(n => n is parent, true)(parent.children)));

        Node10002 p;
        p.children.remove10002!(n => n is p)();
        remove10002!(n => n is p)(p.children);
        static assert(!__traits(compiles, p.children.remove10002x!(n => n is p, true)()));
        static assert(!__traits(compiles, remove10002x!(n => n is p, true)(p.children)));
    }
}

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

void fa10148() {}  // fa is @system

auto fb10148(T)()
{
    struct A(S)
    {
        // [4] Parent function fb is already inferred to @safe, then
        // fc is forcely marked @safe on default until 2.052.
        // But fc should keep attribute inference ability
        // by overriding the inherited @safe-ty from its parent.
        void fc(T2)()
        {
            // [5] During semantic3 process, fc is not @safe on default.
            static assert(is(typeof(&fc) == void delegate()));
            fa10148();
        }
        // [1] this is now inferred to @safe by implementing https://issues.dlang.org/show_bug.cgi?id=7511
        this(S a) {}
    }

    // [2] A!int(0) is now calling @safe function, then fb!T also be inferred to @safe
    return A!int(0);
}

void test10148()
{
    fb10148!int.fc!int;  // [0] instantiate fb
                         // [3] instantiate fc

    // [6] After semantic3 done, fc!int is deduced to @system.
    static assert(is(typeof(&fb10148!int.fc!int) == void delegate() @system));
}

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

void test10289()
{
    void foo(E)()
    {
        throw new E("");
    }
    void bar(E1, E2)()
    {
        throw new E1("");
        throw new E2("");
    }
    void baz(E1, E2)(bool cond)
    {
        if (cond)
            throw new E1("");
        else
            throw new E2("");
    }

    import core.exception;
    static class MyException : Exception
    {
        this(string) @safe pure nothrow { super(""); }
    }

    static assert( __traits(compiles, () nothrow { foo!Error(); }));
    static assert( __traits(compiles, () nothrow { foo!AssertError(); }));

    static assert(!__traits(compiles, () nothrow { foo!Exception(); }));
    static assert(!__traits(compiles, () nothrow { foo!MyException(); }));

    static assert( __traits(compiles, () nothrow { bar!(Error, Exception)(); }));
    static assert(!__traits(compiles, () nothrow { bar!(Exception, Error)(); }));

    static assert(!__traits(compiles, () nothrow { baz!(Error, Exception)(); }));
    static assert(!__traits(compiles, () nothrow { baz!(Exception, Error)(); }));
}

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

void foo10296()()
{
    int[3] a;

    void bar()() { a[1] = 2; }
    bar();
    static assert(typeof(bar!()).stringof == "pure nothrow @nogc @safe void()");    // nothrow @safe void()
}
pure void test10296()
{
    foo10296();
}

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

struct Foo12025
{
    int[5] bar;
}

void test12025a() pure
{
    enum n1 = typeof(Foo12025.bar).length;  // OK
    enum n2 =        Foo12025.bar .length;  // OK <- error

    auto x1 = typeof(Foo12025.bar).length;  // OK
    auto x2 =        Foo12025.bar .length;  // OK <- error
}

void test12025b() pure
{
    static int[5] bar;

    enum n1 = typeof(bar).length;  // OK
    enum n2 =        bar .length;  // OK <- error

    auto x1 = typeof(bar).length;  // OK
    auto x2 =        bar .length;  // OK <- error
}

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

int logOf12542(T)(T n)
{
    if (n)
        return 1 + logOf12542(n/2);
    return 0;
}

void test12542() @safe nothrow pure
{
    int log = logOf12542(9);
}

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

void foo12704() @system;
alias FP12704 = typeof(function() { foo12704(); });
static assert(is(FP12704 == void function() @system));

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

@system { @safe void f12970a() {} }
@system { void f12970b() @safe {} }
static assert(is(typeof(&f12970a) == void function() @safe));
static assert(is(typeof(&f12970b) == void function() @safe));

@system { @trusted void f12970c() {} }
@system { void f12970d() @trusted {} }
static assert(is(typeof(&f12970c) == void function() @trusted));
static assert(is(typeof(&f12970d) == void function() @trusted));

@safe { @system void f12970e() {} }
@safe { void f12970f() @system {} }
static assert(is(typeof(&f12970e) == void function() @system));
static assert(is(typeof(&f12970f) == void function() @system));

@safe { @trusted void f12970g() {} }
@safe { void f12970h() @trusted {} }
static assert(is(typeof(&f12970g) == void function() @trusted));
static assert(is(typeof(&f12970h) == void function() @trusted));

@trusted { @safe void f12970i() {} }
@trusted { void f12970j() @safe {} }
static assert(is(typeof(&f12970i) == void function() @safe));
static assert(is(typeof(&f12970j) == void function() @safe));

@trusted { @system void f12970k() {} }
@trusted { void f12970l() @system {} }
static assert(is(typeof(&f12970k) == void function() @system));
static assert(is(typeof(&f12970l) == void function() @system));

/***************************************************/
// Parsing prefix STC_FUNCATTR for variable declaration

__gshared immutable pure nothrow @property @nogc @safe void function() prefix_qualified_fp1;
__gshared{immutable{pure{nothrow{@property{@nogc{@safe{void function() prefix_qualified_fp2;}}}}}}}
static assert(typeof(prefix_qualified_fp1).stringof == typeof(prefix_qualified_fp2).stringof);
static assert(typeof(prefix_qualified_fp1).stringof
        == "immutable(void function() pure nothrow @nogc @property @safe)");

const pure nothrow @property @nogc @safe void function()[] prefix_qualified_fp_array1;
const{pure{nothrow{@property{@nogc{@safe{void function()[] prefix_qualified_fp_array2;}}}}}}
static assert(typeof(prefix_qualified_fp_array1).stringof == typeof(prefix_qualified_fp_array2).stringof);
static assert(typeof(prefix_qualified_fp_array1).stringof
        == "const(void function() pure nothrow @nogc @property @safe[])");

/***************************************************/
// Parsing prefix, intermediate, or postfix @safe for alias declaration

@safe alias void function() AliasDecl_FP1;
alias @safe void function() AliasDecl_FP2;    // is not @safe
alias void function() @safe AliasDecl_FP3;
static assert(AliasDecl_FP1.stringof == "void function() @safe");
static assert(AliasDecl_FP2.stringof == "void function()");
static assert(AliasDecl_FP3.stringof == "void function() @safe");

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

void writeln13217(string) {}

nothrow void a13217(T)(T x)
{
    try
    {
        () { writeln13217("a"); } ();
    }
    catch (Exception e) {}
}

void test13217()
{
    a13217(1);
}

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

struct Foo13840
{
    int opApply(int delegate(int))
    {
        return 0;
    }
}

void func13840()
{
}

void test13840() nothrow
{
    try
    {
        foreach (i; Foo13840()) // generated delegate is throwable
        {
            func13840();        // throwable function call
        }
    }
    catch(Throwable)
    {}
}

// Add more tests regarding inferences later.
