// REQUIRED_ARGS: -extern-std=c++98
// EXTRA_FILES: imports/plainpackage/plainmodule.d imports/pkgmodule/package.d imports/pkgmodule/plainmodule.d

// This file is intended to contain all compilable traits-related tests in an
// effort to keep the number of files in the `compilable` folder to a minimum.

// https://issues.dlang.org/show_bug.cgi?id=19152
module traits;

class C19152
{
    int OnExecute()
    {
        auto name = __traits(getOverloads, this, "OnExecute").stringof;
        return 0;
    }
}

static assert(is(typeof(__traits(getTargetInfo, "cppRuntimeLibrary")) == string));
version (CppRuntime_Microsoft)
{
    static assert(__traits(getTargetInfo, "cppRuntimeLibrary") == "libcmt" ||
                  __traits(getTargetInfo, "cppRuntimeLibrary")[0..6] == "msvcrt"); // includes mingw import libs
}

version (D_HardFloat)
    static assert(__traits(getTargetInfo, "floatAbi") == "hard");

version (Win64)
    static assert(__traits(getTargetInfo, "objectFormat") == "coff");
version (OSX)
    static assert(__traits(getTargetInfo, "objectFormat") == "macho");
version (linux)
    static assert(__traits(getTargetInfo, "objectFormat") == "elf");

static assert(__traits(getTargetInfo, "cppStd") == 199711);

import imports.plainpackage.plainmodule;
import imports.pkgmodule.plainmodule;

#line 40
struct MyStruct;

alias a = imports.plainpackage;
alias b = imports.pkgmodule.plainmodule;

static assert(__traits(isPackage, imports.plainpackage));
static assert(__traits(isPackage, a));
static assert(!__traits(isPackage, imports.plainpackage.plainmodule));
static assert(!__traits(isPackage, b));
static assert(__traits(isPackage, imports.pkgmodule));
static assert(!__traits(isPackage, MyStruct));

static assert(!__traits(isModule, imports.plainpackage));
static assert(!__traits(isModule, a));
static assert(__traits(isModule, imports.plainpackage.plainmodule));
static assert(__traits(isModule, b));
// This is supposed to work even though we haven't directly imported imports.pkgmodule.
static assert(__traits(isModule, imports.pkgmodule));
static assert(!__traits(isModule, MyStruct));

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

static assert(!__traits(compiles, { a.init; }));
static assert(!__traits(compiles, { import m : a; a.init; }));

version(Windows)
    static assert(__traits(getLocation, MyStruct)[0] == `compilable\traits.d`);
else
    static assert(__traits(getLocation, MyStruct)[0] == "compilable/traits.d");
static assert(__traits(getLocation, MyStruct)[1] == 40);
static assert(__traits(getLocation, MyStruct)[2] == 1);

int foo();
int foo(int);

static assert(__traits(getLocation, __traits(getOverloads, traits, "foo")[1])[1] == 74);

mixin("int bar;");
static assert(__traits(getLocation, bar)[1] == 78);

struct Outer
{
    struct Nested{}

    void method() {}
}
static assert(__traits(getLocation, Outer.Nested)[1] == 83);
static assert(__traits(getLocation, Outer.method)[1] == 85);

/******************************************/
// https://issues.dlang.org/show_bug.cgi?id=19902
// Define hasElaborateCopyConstructor trait
// but done as two independent traits per conversation
// in https://github.com/dlang/dmd/pull/10265

struct S
{
    this (ref S rhs) {}
}

struct OuterS
{
    struct S
    {
        this (ref S rhs) {}
    }

    S s;
}

void foo(T)()
{
    struct S(U)
    {
        this (ref S rhs) {}
    }
    static assert (__traits(hasCopyConstructor, S!int));
}

struct U(T)
{
    this (ref U rhs) {}
}

struct SPostblit
{
    this(this) {}
}

struct DisabledPostblit
{
    @disable this(this);
}

struct NoCpCtor { }
class C19902 { }

static assert(__traits(hasCopyConstructor, S));
static assert(__traits(hasCopyConstructor, OuterS.S));
static assert(__traits(hasCopyConstructor, OuterS));
static assert(__traits(compiles, foo!int));
static assert(__traits(compiles, foo!S));
static assert(__traits(hasCopyConstructor, U!int));
static assert(__traits(hasCopyConstructor, U!S));
static assert(!__traits(hasPostblit, U!S));
static assert(__traits(hasPostblit, SPostblit));
static assert(!__traits(hasCopyConstructor, SPostblit));

static assert(!__traits(hasCopyConstructor, NoCpCtor));
static assert(!__traits(hasCopyConstructor, C19902));
static assert(!__traits(hasCopyConstructor, int));
static assert(!__traits(hasPostblit, NoCpCtor));
static assert(!__traits(hasPostblit, C19902));
static assert(!__traits(hasPostblit, int));

static assert(__traits(isCopyable, int));
static assert(!__traits(isCopyable, DisabledPostblit));
struct S1 {}                        // Fine. Can be copied
struct S2 { this(this) {} }         // Fine. Can be copied
struct S3 { @disable this(this);  } // Not fine. Copying is disabled.
struct S4 { S3 s; }                 // Not fine. A field has copying disabled.
class C1 {}
static assert( __traits(isCopyable, S1));
static assert( __traits(isCopyable, S2));
static assert(!__traits(isCopyable, S3));
static assert(!__traits(isCopyable, S4));
static assert(__traits(isCopyable, C1));
static assert(__traits(isCopyable, int));
static assert(__traits(isCopyable, int[]));

enum E1 : S1 { a = S1(), }
enum E2 : S2 { a = S2(), }
enum E3 : S3 { a = S3(), }
enum E4 : S4 { a = S4(), }

static assert(__traits(isCopyable, E1));
static assert(__traits(isCopyable, E2));
static assert(!__traits(isCopyable, E3));
static assert(!__traits(isCopyable, E4));

struct S5
{
    @disable this(ref S5);
}
static assert(!__traits(isCopyable, S5));

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

struct S20884
{
  int x;
}

alias T20884 = immutable(S20884);
enum m20884 = "x";

static assert(is(typeof(__traits(getMember, T20884, m20884)) == immutable(int))); // OK now
static assert(is(          typeof(mixin("T20884." ~ m20884)) == immutable(int)));
static assert(is(                           typeof(T20884.x) == immutable(int)));

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

alias Seq(T...) = T;

static assert(__traits(isSame, Seq!(1, 2), Seq!(1, 2)));
static assert(!__traits(isSame, Seq!(1, 1), Seq!(2, 2)));
static assert(!__traits(isSame, Seq!(1, 1, 2), Seq!(1, 1)));
static assert(!__traits(isSame, Seq!(1, 1), Seq!(1, 1, 2)));

static assert(__traits(isSame,
    Seq!(string, wstring),
    Seq!(immutable(char)[], immutable(wchar)[]))
);

static assert(__traits(isSame,
    Seq!(i => i.value, (a, b) => a + b),
    Seq!(a => a.value, (x, y) => x + y)
));

static assert(__traits(isSame,
    Seq!(float, Seq!(double, Seq!real)),
    Seq!(Seq!(Seq!float, double), real)
));

static assert(!__traits(isSame,
    Seq!(int, Seq!(a => a + a)),
    Seq!(int, Seq!(a => a * a))
));

// Do these out of order to ensure there are no forward refencing bugs

extern(C++, __traits(getCppNamespaces,GetNamespaceTest1)) struct GetNamespaceTest4 {}
static assert (__traits(getCppNamespaces,GetNamespaceTest1) ==
               __traits(getCppNamespaces,GetNamespaceTest4));

extern(C++, "ns") struct GetNamespaceTest1 {}
extern(C++, "multiple", "namespaces") struct GetNamespaceTest2 {}
extern(C++, mixin("Seq!(`ns`, `nt`)")) struct GetNamespaceTest3 {}
static assert(__traits(getCppNamespaces,GetNamespaceTest1)[0] == "ns");
static assert(__traits(getCppNamespaces,GetNamespaceTest2) == Seq!("multiple","namespaces"));
static assert(__traits(getCppNamespaces,GetNamespaceTest3) == Seq!("ns", "nt"));

extern(C++, __traits(getCppNamespaces,GetNamespaceTest5)) struct GetNamespaceTest8 {}
static assert (__traits(getCppNamespaces,GetNamespaceTest5) ==
               __traits(getCppNamespaces,GetNamespaceTest8));

extern(C++, ns) struct GetNamespaceTest5 {}
extern(C++, multiple) extern(C++, namespaces) struct GetNamespaceTest6 {}
static assert(__traits(getCppNamespaces,GetNamespaceTest5)[0] == "ns");
static assert(__traits(getCppNamespaces,GetNamespaceTest6) == Seq!("multiple","namespaces"));

extern(C++, NS)
{
    struct GetNamespaceTest9 {}
    extern(C++, nested)
    {
        struct GetNamespaceTest10 {}
        extern(C++,"nested2")
            struct GetNamespaceTest11 {}
    }
    extern (C++, "nested3")
    {
        extern(C++, nested4)
            struct GetNamespaceTest12 {}
    }
}
static assert (__traits(getCppNamespaces,NS.GetNamespaceTest9)[0] == "NS");
static assert (__traits(getCppNamespaces,NS.GetNamespaceTest10) == Seq!("NS", "nested"));
static assert (__traits(getCppNamespaces,NS.GetNamespaceTest11) == Seq!("NS", "nested", "nested2"));
static assert (__traits(getCppNamespaces,NS.GetNamespaceTest12) == Seq!("NS", "nested4", "nested3"));

extern(C++, `ns`) struct GetNamespaceTestTemplated(T) {}
extern(C++, `ns`)
template GetNamespaceTestTemplated2(T)
{
    struct GetNamespaceTestTemplated2 {}
}

template GetNamespaceTestTemplated3(T)
{
    extern(C++, `ns`)
    struct GetNamespaceTestTemplated3 {}
}

static assert (__traits(getCppNamespaces,GetNamespaceTestTemplated!int)  == Seq!("ns"));
static assert (__traits(getCppNamespaces,GetNamespaceTestTemplated2!int) == Seq!("ns"));
static assert (__traits(getCppNamespaces,GetNamespaceTestTemplated3!int) == Seq!("ns"));
extern(C++, `ns2`)
template GetNamespaceTestTemplated4(T)
{
    extern(C++, `ns`)
    struct GetNamespaceTestTemplated4
    {
        struct GetNamespaceTestTemplated5 {}
        struct GetNamespaceTestTemplated6(T) {}
    }
}

static assert (__traits(getCppNamespaces,GetNamespaceTestTemplated4!int) == Seq!("ns2","ns"));
static assert (__traits(getCppNamespaces,GetNamespaceTestTemplated4!int.GetNamespaceTestTemplated5) == Seq!("ns2","ns"));
static assert (__traits(getCppNamespaces,GetNamespaceTestTemplated4!int.GetNamespaceTestTemplated6!int) == Seq!("ns2","ns"));

// Currently ignored due to https://issues.dlang.org/show_bug.cgi?id=21373
extern(C++, `decl`)
mixin template GetNamespaceTestTemplatedMixin()
{
    extern(C++, `f`)
    void foo() {}
}

extern(C++, `inst`)
mixin GetNamespaceTestTemplatedMixin!() GNTT;

static assert (__traits(getCppNamespaces, GNTT.foo) == Seq!(`inst`,/*`decl`,*/ `f`));

int[1] arr;
// test that index assignment parses as an expression, not a type
enum _ = __traits(compiles, arr[0] = 0);
