// REQUIRED_ARGS: -preview=in
// PERMUTE_ARGS: -g
// EXTRA_CPP_SOURCES: cppb.cpp
// EXTRA_FILES: extra-files/cppb.h
// CXXFLAGS(linux freebsd osx openbsd netbsd dragonflybsd): -std=c++11
// druntime isn't linked, this prevents missing symbols '_d_arraybounds_slicep':
// REQUIRED_ARGS: -checkaction=C
// TRANSFORM_OUTPUT: remove_lines("warning: vsprintf\(\) is often misused")

// N.B MSVC doesn't have a C++11 switch, but it defaults to the latest fully-supported standard

import core.stdc.stdio;
import core.stdc.stdarg;
import core.stdc.config;
import core.stdc.stdint;

extern (C++)
        int foob(int i, int j, int k);

class C
{
    extern (C++) int bar(int i, int j, int k)
    {
        printf("this = %p\n", this);
        printf("i = %d\n", i);
        printf("j = %d\n", j);
        printf("k = %d\n", k);
        return 1;
    }
}


extern (C++)
        int foo(int i, int j, int k)
{
    printf("i = %d\n", i);
    printf("j = %d\n", j);
    printf("k = %d\n", k);
    assert(i == 1);
    assert(j == 2);
    assert(k == 3);
    return 1;
}

void test1()
{
    foo(1, 2, 3);

    auto i = foob(1, 2, 3);
    assert(i == 7);

    C c = new C();
    c.bar(4, 5, 6);
}

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

extern (C++) interface D
{
    int bar(int i, int j, int k);
}

extern (C++) D getD();

void test2()
{
    D d = getD();
    int i = d.bar(9,10,11);
    assert(i == 8);
}

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

extern (C++) int callE(E);

extern (C++) interface E
{
    int bar(int i, int j, int k);
}

class F : E
{
    extern (C++) int bar(int i, int j, int k)
    {
        printf("F.bar: i = %d\n", i);
        printf("F.bar: j = %d\n", j);
        printf("F.bar: k = %d\n", k);
        assert(i == 11);
        assert(j == 12);
        assert(k == 13);
        return 8;
    }
}

void test3()
{
    F f = new F();
    int i = callE(f);
    assert(i == 8);
}

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

extern (C++) void foo4(char* p);

void test4()
{
    foo4(null);
}

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

extern(C++)
{
  struct foo5 { int i; int j; void* p; }

  interface bar5{
    foo5 getFoo(int i);
  }

  bar5 newBar();
}

void test5()
{
  bar5 b = newBar();
  foo5 f = b.getFoo(4);
  printf("f.p = %p, b = %p\n", f.p, cast(void*)b);
  assert(f.p == cast(void*)b);
}


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

extern(C++)
{
    struct S6
    {
        int i;
        double d;
    }

    union S6_2
    {
        int i;
        double d;
    }

    enum S6_3
    {
        A, B
    }

    S6 foo6();
    S6_2 foo6_2();
    S6_3 foo6_3();
}

extern (C) int foosize6();

void test6()
{
    S6 f = foo6();
    printf("%d %zd\n", foosize6(), S6.sizeof);
    assert(foosize6() == S6.sizeof);
version (X86)
{
    assert(f.i == 42);
    printf("f.d = %g\n", f.d);
    assert(f.d == 2.5);
    assert(foo6_2().i == 42);
    assert(foo6_3() == S6_3.A);
}
}

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

extern (C) int foo7();

struct S
{
    int i;
    long l;
}

void test7()
{
    printf("%d %zd\n", foo7(), S.sizeof);
    assert(foo7() == S.sizeof);
}

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

extern (C++) void foo8(const(char)*);

void test8()
{
    char c;
    foo8(&c);
}

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

struct elem9 { }

extern(C++) void foobar9(elem9*, elem9*);

void test9()
{
    elem9 *a;
    foobar9(a, a);
}

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


struct A11802;
struct B11802;

extern(C++) class C11802
{
    int x;
    void fun(A11802*) { x += 2; }
    void fun(B11802*) { x *= 2; }
}

extern(C++) class D11802 : C11802
{
    override void fun(A11802*) { x += 3; }
    override void fun(B11802*) { x *= 3; }
}

extern(C++) void test11802x(D11802);

void test11802()
{
    auto x = new D11802();
    x.x = 0;
    test11802x(x);
    assert(x.x == 9);
}


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

struct S13956
{
}

extern(C++) void func13956(S13956 arg0, int arg1, int arg2, int arg3, int arg4, int arg5, int arg6);

extern(C++) void check13956(S13956 arg0, int arg1, int arg2, int arg3, int arg4, int arg5, int arg6)
{
    assert(arg0 == S13956());
    assert(arg1 == 1);
    assert(arg2 == 2);
    assert(arg3 == 3);
    assert(arg4 == 4);
    assert(arg5 == 5);
    assert(arg6 == 6);
}

void test13956()
{
    func13956(S13956(), 1, 2, 3, 4, 5, 6);
}

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

extern (C++)
{
    void foo10(const(char)*, const(char)*);
    void foo10(const int, const int);
    void foo10(const char, const char);
    void foo10(bool, bool);

    struct MyStructType { }
    void foo10(const MyStructType s, const MyStructType t);

    enum MyEnumType { onemember }
    void foo10(const MyEnumType s, const MyEnumType t);
}

void test10()
{
    char* p;
    foo10(p, p);
    foo10(1,2);
    foo10('c','d');
    MyStructType s;
    foo10(s,s);
    MyEnumType e;
    foo10(e,e);
}

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

extern (C++, N11.M) { void bar11(); }

extern (C++, A11.B) { extern (C++, C) { void bar(); }}

void test11()
{
    bar11();
    A11.B.C.bar();
}
/****************************************/

struct Struct10071
{
    void *p;
    c_long_double r;
}

extern(C++) size_t offset10071();
void test10071()
{
    assert(offset10071() == Struct10071.r.offsetof);
}

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

char[100] valistbuffer;

extern(C++) void myvprintfx(const(char)* format, va_list va)
{
    vsprintf(valistbuffer.ptr, format, va);
}
extern(C++) void myvprintf(const(char)*, va_list);
extern(C++) void myprintf(const(char)* format, ...)
{
    va_list ap;
    va_start(ap, format);
    myvprintf(format, ap);
    va_end(ap);
}

void testvalist()
{
    myprintf("hello %d", 999);
    assert(valistbuffer[0..9] == "hello 999");
}

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

extern(C++) class C12825
{
    uint a = 0x12345678;
}

void test12825()
{
    auto c = new C12825();
}

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

struct S13955a
{
    float a;
    double b;
}

struct S13955b
{
    double a;
    float b;
}

struct S13955c
{
    float a;
    float b;
}

struct S13955d
{
    double a;
    double b;
}

extern(C++) void check13955(S13955a a, S13955b b, S13955c c, S13955d d)
{
    assert(a.a == 2);
    assert(a.b == 4);
    assert(b.a == 8);
    assert(b.b == 16);
    assert(c.a == 32);
    assert(c.b == 64);
    assert(d.a == 128);
    assert(d.b == 256);
}

extern(C++) void func13955(S13955a a, S13955b b, S13955c c, S13955d d);

void test13955()
{
    func13955(S13955a(2, 4), S13955b(8, 16), S13955c(32, 64), S13955d(128, 256));
}

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

extern(C++) class C13161
{
    void dummyfunc();
    long val_5;
    uint val_9;
}

extern(C++) class Test : C13161
{
    uint val_0;
    long val_1;
}

extern(C++) size_t getoffset13161();

extern(C++) class C13161a
{
    void dummyfunc();
    c_long_double val_5;
    uint val_9;
}

extern(C++) class Testa : C13161a
{
    bool val_0;
}

extern(C++) size_t getoffset13161a();

void test13161()
{
    assert(getoffset13161() == Test.val_0.offsetof);
    assert(getoffset13161a() == Testa.val_0.offsetof);
}

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

extern (C++, std)
{
    extern (C++, class) struct allocator(T)
    {
    }

    class vector(T, A = allocator!T)
    {
        final void push_back(ref const T);
    }

    struct char_traits(T)
    {
    }

    version (CppRuntime_GNU)
    {
        // https://gcc.gnu.org/onlinedocs/libstdc++/manual/using_dual_abi.html
        static if (__traits(getTargetInfo, "cppStd") >= 201103)
        {
            extern (C++, __cxx11)
            {
                struct basic_string(T, C = char_traits!T, A = allocator!T)
                {
                }
            }
        }
        else
        {
            extern (C++, class) struct basic_string(T, C = char_traits!T, A = allocator!T)
            {
            }
        }
    }
    else
    {
        extern (C++, class) struct basic_string(T, C = char_traits!T, A = allocator!T)
        {
        }
    }

    struct basic_istream(T, C = char_traits!T)
    {
    }

    struct basic_ostream(T, C = char_traits!T)
    {
    }

    struct basic_iostream(T, C = char_traits!T)
    {
    }

    class exception { }

    // https://issues.dlang.org/show_bug.cgi?id=14956
    extern(C++, N14956)
    {
        struct S14956 { }
    }
}

extern (C++)
{
    version (linux)
    {
        void foo14(std.vector!(int) p);
        void foo14a(std.basic_string!(char) *p);
        void foo14b(std.basic_string!(int) *p);
        void foo14c(std.basic_istream!(char) *p);
        void foo14d(std.basic_ostream!(char) *p);
        void foo14e(std.basic_iostream!(char) *p);

        void foo14f(std.char_traits!char* x, std.basic_string!char* p, std.basic_string!char* q);
    }
}

void test14()
{
    version (linux)
    {
        std.vector!int p;
        foo14(p);

        foo14a(null);
        foo14b(null);
        foo14c(null);
        foo14d(null);
        foo14e(null);
        foo14f(null, null, null);
    }
}

version (linux)
{
    void gun(std.vector!int pa)
    {
    int x = 42;
    pa.push_back(x);
    }
}

void test13289()
{
    assert(f13289_cpp_wchar_t('a') == 'A');
    assert(f13289_cpp_wchar_t('B') == 'B');
    assert(f13289_d_wchar('c') == 'C');
    assert(f13289_d_wchar('D') == 'D');
    assert(f13289_d_dchar('e') == 'E');
    assert(f13289_d_dchar('F') == 'F');
    assert(f13289_cpp_test());
}

version(Posix)
{
    enum __c_wchar_t : dchar;
}
else version(Windows)
{
    enum __c_wchar_t : wchar;
}
alias wchar_t = __c_wchar_t;
extern(C++)
{
    bool f13289_cpp_test();


    wchar_t f13289_cpp_wchar_t(wchar_t);


    wchar f13289_d_wchar(wchar ch)
    {
        if (ch <= 'z' && ch >= 'a')
        {
            return cast(wchar)(ch - ('a' - 'A'));
        }
        else
        {
            return ch;
        }
    }
    dchar f13289_d_dchar(dchar ch)
    {
        if (ch <= 'z' && ch >= 'a')
        {
            return ch - ('a' - 'A');
        }
        else
        {
            return ch;
        }
    }
    wchar_t f13289_d_wchar_t(wchar_t ch)
    {
        if (ch <= 'z' && ch >= 'a')
        {
            return cast(wchar_t)(ch - ('a' - 'A'));
        }
        else
        {
            return ch;
        }
    }
}

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

version (CRuntime_Microsoft)
{
    enum __c_long_double : double;
    alias __c_long_double myld;
}
else
    alias c_long_double myld;

extern (C++) myld testld(myld);
extern (C++) myld testldld(myld, myld);


void test15()
{
    myld ld = 5.0;
    ld = testld(ld);
    assert(ld == 6.0);

    myld ld2 = 5.0;
    ld2 = testldld(ld2, ld2);
    assert(ld2 == 6.0);
}

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

version( Windows )
{
    alias int   x_long;
    alias uint  x_ulong;
}
else
{
  static if( (void*).sizeof > int.sizeof )
  {
    alias long  x_long;
    alias ulong x_ulong;
  }
  else
  {
    alias int   x_long;
    alias uint  x_ulong;
  }
}

enum __c_long : x_long;
enum __c_ulong : x_ulong;
alias __c_long mylong;
alias __c_ulong myulong;

extern (C++) mylong testl(mylong);
extern (C++) myulong testul(myulong);


void test16()
{
  {
    mylong ld = 5;
    ld = testl(ld);
    printf("ld = %lld, mylong.sizeof = %lld\n", cast(long)ld, cast(long)mylong.sizeof);
    assert(ld == 5 + mylong.sizeof);
  }
  {
    myulong ld = 5;
    ld = testul(ld);
    assert(ld == 5 + myulong.sizeof);
  }

  static if (__c_long.sizeof == long.sizeof)
  {
    static assert(__c_long.max == long.max);
    static assert(__c_long.min == long.min);
    static assert(__c_long.init == long.init);

    static assert(__c_ulong.max == ulong.max);
    static assert(__c_ulong.min == ulong.min);
    static assert(__c_ulong.init == ulong.init);

    __c_long cl = 0;
    cl = cl + 1;
    long l = cl;
    cl = l;

    __c_ulong cul = 0;
    cul = cul + 1;
    ulong ul = cul;
    cul = ul;
  }
  else static if (__c_long.sizeof == int.sizeof)
  {
    static assert(__c_long.max == int.max);
    static assert(__c_long.min == int.min);
    static assert(__c_long.init == int.init);

    static assert(__c_ulong.max == uint.max);
    static assert(__c_ulong.min == uint.min);
    static assert(__c_ulong.init == uint.init);

    __c_long cl = 0;
    cl = cl + 1;
    int i = cl;
    cl = i;

    __c_ulong cul = 0;
    cul = cul + 1;
    uint u = cul;
    cul = u;
  }
  else
    static assert(0);
}

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

struct S13707
{
    void* a;
    void* b;
    this(void* a, void* b)
    {
        this.a = a;
        this.b = b;
    }
}

extern(C++) S13707 func13707();

void test13707()
{
    auto p = func13707();
    assert(p.a == null);
    assert(p.b == null);
}

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

struct S13932(int x)
{
        int member;
}

extern(C++) void func13932(S13932!(-1) s);

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

extern(C++, N13337.M13337)
{
  struct S13337{}
  void foo13337(S13337 s);
}

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

struct Delegate1(T) {}
struct Delegate2(T1, T2) {}

template Signature(T)
{
    alias Signature = typeof(*(T.init));
}

extern(C++)
{
    alias del1_t = Delegate1!(Signature!(void function()));
    alias del2_t = Delegate2!(Signature!(int function(float, double)), Signature!(int function(float, double)));
    void test14195a(del1_t);
    void test14195b(del2_t);
}

void test14195()
{
    test14195a(del1_t());
    test14195b(del2_t());
}


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

template Tuple14200(T...)
{
  alias Tuple14200 = T;
}

extern(C++) void test14200a(Tuple14200!(int));
extern(C++) void test14200b(float, Tuple14200!(int, double));

void test14200()
{
  test14200a(1);
  test14200b(1.0f, 1, 1.0);
}

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

extern(C++) void test14956(S14956 s);

/****************************************/
// check order of overloads in vtable

extern (C++) class Statement {}
extern (C++) class ErrorStatement {}
extern (C++) class PeelStatement {}
extern (C++) class ExpStatement {}
extern (C++) class DtorExpStatement {}

extern (C++) class Visitor
{
public:
    int visit(Statement) { return 1; }
    int visit(ErrorStatement) { return 2; }
    int visit(PeelStatement) { return 3; }
}

extern (C++) class Visitor2 : Visitor
{
    int visit2(ExpStatement) { return 4; }
    int visit2(DtorExpStatement) { return 5; }
}

extern(C++) bool testVtableCpp(Visitor2 sv);
extern(C++) Visitor2 getVisitor2();

bool testVtableD(Visitor2 sv)
{
    Statement s1;
    ErrorStatement s2;
    PeelStatement s3;
    ExpStatement s4;
    DtorExpStatement s5;

    if (sv.visit(s1) != 1) return false;
    if (sv.visit(s2) != 2) return false;
    if (sv.visit(s3) != 3) return false;
    if (sv.visit2(s4) != 4) return false;
    if (sv.visit2(s5) != 5) return false;
    return true;
}

void testVtable()
{
    Visitor2 dinst = new Visitor2;
    if (!testVtableCpp(dinst))
        assert(0);

    Visitor2 cppinst = getVisitor2();
    if (!testVtableD(cppinst))
        assert(0);
}

/****************************************/
/* problems detected by fuzzer */
extern(C++) void fuzz1_cppvararg(int64_t arg10, int64_t arg11, bool arg12);
extern(C++) void fuzz1_dvararg(int64_t arg10, int64_t arg11, bool arg12)
{
    fuzz1_checkValues(arg10, arg11, arg12);
}

extern(C++) void fuzz1_checkValues(int64_t arg10, int64_t arg11, bool arg12)
{
    assert(arg10 == 103);
    assert(arg11 == 104);
    assert(arg12 == false);
}

void fuzz1()
{
    long arg10 = 103;
    long arg11 = 104;
    bool arg12 = false;
    fuzz1_dvararg(arg10, arg11, arg12);
    fuzz1_cppvararg(arg10, arg11, arg12);
}

////////
extern(C++) void fuzz2_cppvararg(uint64_t arg10, uint64_t arg11, bool arg12);
extern(C++) void fuzz2_dvararg(uint64_t arg10, uint64_t arg11, bool arg12)
{
    fuzz2_checkValues(arg10, arg11, arg12);
}

extern(C++) void fuzz2_checkValues(uint64_t arg10, uint64_t arg11, bool arg12)
{
    assert(arg10 == 103);
    assert(arg11 == 104);
    assert(arg12 == false);
}

void fuzz2()
{
    ulong arg10 = 103;
    ulong arg11 = 104;
    bool arg12 = false;
    fuzz2_dvararg(arg10, arg11, arg12);
    fuzz2_cppvararg(arg10, arg11, arg12);
}

////////
version(CppRuntime_Microsoft)
    enum UNICODE = false; //VS2013 doesn't support them
else
    enum UNICODE = true;

static if (UNICODE)
{
extern(C++) void fuzz3_cppvararg(wchar arg10, dchar arg11, bool arg12);
extern(C++) void fuzz3_dvararg(wchar arg10, dchar arg11, bool arg12)
{
    fuzz2_checkValues(arg10, arg11, arg12);
}

extern(C++) void fuzz3_checkValues(wchar arg10, dchar arg11, bool arg12)
{
    assert(arg10 == 103);
    assert(arg11 == 104);
    assert(arg12 == false);
}

void fuzz3()
{
    wchar arg10 = 103;
    dchar arg11 = 104;
    bool arg12 = false;
    fuzz3_dvararg(arg10, arg11, arg12);
    fuzz3_cppvararg(arg10, arg11, arg12);
}
}

void fuzz()
{
    fuzz1();
    fuzz2();
    static if (UNICODE) fuzz3();
}

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

version (LDC)
{
    version (CRuntime_Microsoft)
        version = LDC_CppEH;
    else version (CppRuntime_GNU)
        version = LDC_CppEH;
    // FIXME: support libc++ too
}

extern (C++)
{
    void throwit();
}

void testeh()
{
    printf("testeh()\n");

    version (LDC_CppEH) // LDC: was `version (linux)`
    {
        version (all)   // LDC: was `version (X86_64)`
        {
            bool caught;
            try
            {
                try
                {
                    throwit();
                }
                catch (Throwable t)
                {
                    assert(false, "caught a Throwable, expected an std::exception");
                }
            }
            catch (std.exception e)
            {
                caught = true;
            }
            assert(caught);
        }
    }
}

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

version (LDC_CppEH) // LDC: was `version (linux)`
{
    version (all)   // LDC: was `version (X86_64)`
    {
        bool raii_works = false;
        struct RAIITest
        {
           ~this()
           {
               raii_works = true;
           }
        }

        void dFunction()
        {
            RAIITest rt;
            throwit();
        }

        void testeh2()
        {
            printf("testeh2()\n");
            try
            {
                dFunction();
            }
            catch(std.exception e)
            {
                assert(raii_works);
            }
        }
    }
    else
        void testeh2() { }
}
else
    void testeh2() { }

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

extern (C++) { void throwle(); void throwpe(); }

void testeh3()
{
    printf("testeh3()\n");
    version (LDC_CppEH) // LDC: was `version (linux)`
    {
        version (all)   // LDC: was `version (X86_64)`
        {
            bool caught = false;
            try
            {
               throwle();
            }
            catch (std.exception e)  //polymorphism test.
            {
                caught = true;
            }
            assert(caught);
        }
    }
}

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

extern (C++, ns15576)
{
    extern __gshared int global15576;

    extern (C++, ns)
    {
        extern __gshared int n_global15576;
    }
}

void test15576()
{
    global15576 = n_global15576 = 123;
}

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

version (DigitalMars)
{
    version (linux)
    {
        // Test removed for DMD/linux-only.
        // https://issues.dlang.org/show_bug.cgi?id=23660
    }
    else
        version = TEST15579;
}
else
    version = TEST15579;

version (TEST15579)
{
    extern (C++)
    {
        class Base
        {
            //~this() {}
            void based() { }
            ubyte x = 4;
        }

        interface Interface
        {
            int MethodCPP();
            int MethodD();
        }

        class Derived : Base, Interface
        {
            short y = 5;
            int MethodCPP();
            int MethodD() {
                printf("Derived.MethodD(): this = %p, x = %d, y = %d\n", this, x, y);
                Derived p = this;
                //p = cast(Derived)(cast(void*)p - 16);
                assert(p.x == 4 || p.x == 7);
                assert(p.y == 5 || p.y == 8);
                return 3;
            }
            int Method() { return 6; }
        }

        Derived cppfoo(Derived);
        Interface cppfooi(Interface);
    }

    void test15579()
    {
        Derived d = new Derived();
        printf("d = %p\n", d);
        assert(d.x == 4);
        assert(d.y == 5);
        assert((cast(Interface)d).MethodCPP() == 30);
        assert((cast(Interface)d).MethodD() == 3);
        assert(d.MethodCPP() == 30);
        assert(d.MethodD() == 3);
        assert(d.Method() == 6);

        d = cppfoo(d);
        assert(d.x == 7);
        assert(d.y == 8);

        printf("d2 = %p\n", d);

        /* Casting to an interface involves thunks in the vtbl[].
         * g++ puts the thunks for MethodD in the same COMDAT as MethodD.
         * But D doesn't, so when the linker "picks one" of the D generated MethodD
         * or the g++ generated MethodD, it may wind up with a messed up thunk,
         * resulting in a seg fault. The solution is to not expect objects of the same
         * type to be constructed on both sides of the D/C++ divide if the same member
         * function (in this case, MethodD) is also defined on both sides.
         */
        version (Windows)
        {
            assert((cast(Interface)d).MethodD() == 3);
        }
        assert((cast(Interface)d).MethodCPP() == 30);

        assert(d.Method() == 6);

        printf("d = %p, i = %p\n", d, cast(Interface)d);
        version (Windows)
        {
            Interface i = cppfooi(d);
            printf("i2: %p\n", i);
            assert(i.MethodD() == 3);
            assert(i.MethodCPP() == 30);
        }
        printf("test15579() done\n");
    }
}

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

extern(C++) class Base2
{
    int i;
    void baser() { }
}

extern(C++) interface Interface2 { abstract void f(); }

extern(C++) class Derived2 : Base2, Interface2
{
    final
        override void f();
}


void test15610()
{
    auto c = new Derived2();
    printf("test15610(): c = %p\n", c);
    c.i = 3;
    c.f();
}

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

struct X6
{
    ushort a;
    ushort b;
    ubyte c;
    ubyte d;
}

static assert(X6.sizeof == 6);

struct X8
{
    ushort a;
    X6 b;
}

static assert(X8.sizeof == 8);

void test15455a(X8 s)
{
    assert(s.a == 1);
    assert(s.b.a == 2);
    assert(s.b.b == 3);
    assert(s.b.c == 4);
    assert(s.b.d == 5);
}

extern (C++) void test15455b(X8 s);

void test15455()
{
    X8 s;

    s.a = 1;
    s.b.a = 2;
    s.b.b = 3;
    s.b.c = 4;
    s.b.d = 5;
    test15455a(s);
    test15455b(s);
}

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

extern(C++) int foo15372(T)(int v);

void test15372()
{
    version(Windows){}
    else
        assert(foo15372!int(1) == 1);
}

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

extern(C++) {
    template Foo15802(T) {
        static int boo(T v);
    }
}

void test15802()
{
    version(Windows){}
    else
        assert(Foo15802!(int).boo(1) == 1);
}

/****************************************/
// https://issues.dlang.org/show_bug.cgi?id=16536
// mangling mismatch on OSX

version(OSX) extern(C++) uint64_t pass16536(uint64_t);

void test16536()
{
    version(OSX) assert(pass16536(123) == 123);
}

/****************************************/
// https://issues.dlang.org/show_bug.cgi?id=15589
// extern(C++) virtual destructors are not put in vtbl[]

extern(C++)
{
    class A15589
    {
        extern(D) static int[] dtorSeq;
        struct S
        {
            this(int x) { this.x = x; }
            ~this() { dtorSeq ~= x; }
            int x;
        }
        int foo() { return 100; } // shift dtor to slot 1
        ~this() { dtorSeq ~= 10; }
        S s1 = S(1);
        S s2 = S(2);
    }
    class B15589 : A15589
    {
        int bar() { return 200;} // add an additional function AFTER the dtor at slot 2
        ~this() { dtorSeq ~= 20; }
        S s3 = S(3);
    }

    void test15589b(A15589 p);
}

void test15589()
{
    A15589 c = new B15589;
    assert(A15589.dtorSeq == null);
    assert(c.foo() == 100);
    assert((cast(B15589)c).bar() == 200);
    c.__xdtor(); // virtual dtor call
    assert(A15589.dtorSeq[] == [ 20, 3, 10, 2, 1 ]); // destroyed full hierarchy!

    A15589.dtorSeq = null;
    test15589b(c);
    assert(A15589.dtorSeq[] == [ 20, 3, 10, 2, 1 ]); // destroyed full hierarchy!
}

extern(C++)
{
    class Cpp15589Base
    {
    public:
        final ~this();

        void nonVirtual();
        int a;
    }

    class Cpp15589Derived : Cpp15589Base
    {
    public:
        this() scope;
        final ~this();
        int b;
    }

    class Cpp15589BaseVirtual
    {
    public:
        void beforeDtor();

        this() scope;
        ~this();

        void afterDtor();
        int c = 1;
    }

    class Cpp15589DerivedVirtual : Cpp15589BaseVirtual
    {
    public:
        this() scope;
        ~this();

        override void afterDtor();

        int d;
    }

    class Cpp15589IntroducingVirtual : Cpp15589Base
    {
    public:
        this() scope;
        void beforeIntroducedVirtual();
        ~this();
        void afterIntroducedVirtual(int);

        int e;
    }

    struct Cpp15589Struct
    {
        ~this();
        int s;
    }

    void trace15589(int ch)
    {
        traceBuf[traceBufPos++] = cast(char) ch;
    }
}

__gshared char[32] traceBuf;
__gshared size_t traceBufPos;

// workaround for https://issues.dlang.org/show_bug.cgi?id=18986
version(OSX)
    enum cppCtorReturnsThis = false;
else version(FreeBSD)
    enum cppCtorReturnsThis = false;
else
    enum cppCtorReturnsThis = true;

mixin template scopeAllocCpp(C)
{
    static if (cppCtorReturnsThis)
        scope C ptr = new C;
    else
    {
        ubyte[__traits(classInstanceSize, C)] data;
        C ptr = (){ auto p = cast(C) data.ptr; p.__ctor(); return p; }();
    }
}

void test15589b()
{
    traceBufPos = 0;
    {
        Cpp15589Struct struc = Cpp15589Struct();
        mixin scopeAllocCpp!Cpp15589Derived derived;
        mixin scopeAllocCpp!Cpp15589DerivedVirtual derivedVirtual;
        mixin scopeAllocCpp!Cpp15589IntroducingVirtual introducingVirtual;

        // `scope` instances are destroyed automatically
        static if (!cppCtorReturnsThis)
        {
            introducingVirtual.ptr.destroy();
            derivedVirtual.ptr.destroy();
            derived.ptr.destroy();
        }
    }
    printf("traceBuf15589 %.*s\n", cast(int)traceBufPos, traceBuf.ptr);
    assert(traceBuf[0..traceBufPos] == "IbVvBbs");
}

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

// https://issues.dlang.org/show_bug.cgi?id=18928
// Win64: extern(C++) bad codegen, wrong calling convention

extern(C++) struct Small18928
{
    int x;
}

extern(C++) class CC18928
{
    Small18928 getVirtual(); // { return S(3); }
    final Small18928 getFinal(); // { return S(4); }
    static Small18928 getStatic(); // { return S(5); }
}

extern(C++) CC18928 newCC18928();

void test18928()
{
    auto cc = newCC18928();
    Small18928 v = cc.getVirtual();
    assert(v.x == 3);
    Small18928 f = cc.getFinal();
    assert(f.x == 4);
    Small18928 s = cc.getStatic();
    assert(s.x == 5);
}

/****************************************/
// https://issues.dlang.org/show_bug.cgi?id=18953
// Win32: extern(C++) struct destructor not called correctly through runtime

extern(C++)
struct S18953
{
    char x;
    ~this() nothrow @nogc { traceBuf[traceBufPos++] = x; }
}

void test18953()
{
    traceBufPos = 0;
    S18953[] arr = new S18953[3];
    arr[1].x = '1';
    arr[2].x = '2';
    arr.length = 1;
    assumeSafeAppend(arr); // destroys arr[1] and arr[2]
    printf("traceBuf18953 %.*s\n", cast(int)traceBufPos, traceBuf.ptr);
    assert(traceBuf[0..traceBufPos] == "21");
}

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

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

extern(C++):
class Base18966
{
    this() @safe nothrow;
    ~this() @safe;
    void vf();
    int x;
}

class Derived18966 : Base18966
{
    override void vf() { x = 200; }
}

class Explicit18966 : Base18966
{
    this() @safe { super(); }
    override void vf() { x = 250; }
}

class Implicit18966 : Base18966
{
    this() nothrow {}
    override void vf() { x = 300; }
}

// test vptr in full ctor chain of mixed D/C++ class hierarchies

// TODO: Make this a D class and let C++ derive from it. This works on Windows,
//       but results in linker errors on Posix due to extra base ctor (`C2`
//       mangling) being called by the B ctor.
class A18966 // in C++
{
    char[8] calledOverloads = 0;
    int i;
    this();
    void foo();
}

class B18966 : A18966 // in C++
{
    this();
    override void foo();
}

class C18966 : B18966
{
    this() { foo(); }
    override void foo() { calledOverloads[i++] = 'C'; }
}

class D18966 : C18966
{
    this() { foo(); }
    override void foo() { calledOverloads[i++] = 'D'; }
}

void test18966()
{
    Derived18966 d = new Derived18966;
    assert(d.x == 10);
    d.vf();
    assert(d.x == 200);

    Explicit18966 e = new Explicit18966;
    assert(e.x == 10);
    e.vf();
    assert(e.x == 250);

    Implicit18966 i = new Implicit18966;
    assert(i.x == 10);
    i.vf();
    assert(i.x == 300);

    auto a = new A18966;
    assert(a.calledOverloads[0..2] == "A\0");

    auto b = new B18966;
    assert(b.calledOverloads[0..3] == "AB\0");

    auto c = new C18966;
    assert(c.calledOverloads[0..4] == "ABC\0");

    auto d2 = new D18966;
    // note: the vptr semantics in ctors of extern(C++) classes may be revised (to "ABCD")
    assert(d2.calledOverloads[0..5] == "ABDD\0");
}

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

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

class Base19134
{
    int a = 123;
    this() { a += 42; }
    int foo() const { return a; }
}

class Derived19134 : Base19134
{
    int b = 666;
    this()
    {
        a *= 2;
        b -= 6;
    }
    override int foo() const { return b; }
}

void test19134()
{
    static const d = new Derived19134;
    assert(d.a == (123 + 42) * 2);
    assert(d.b == 666 - 6);
    assert(d.foo() == 660);
}

// https://issues.dlang.org/show_bug.cgi?id=18955
version (linux)
    alias std_string = std.basic_string!(char);
else
{
    import core.stdcpp.string : core_basic_string = basic_string;
    alias std_string = core_basic_string!(char);
}

extern(C++) void callback18955(ref const(std_string) str)
{
}
extern(C++) void test18955();

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

extern(C++) void testPreviewIn();

extern(C++) void previewInFunction(in int a, in std_string b, ref const(std_string) c)
{
    assert(a == 42);
    assert(&b is &c);
}

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

void main()
{
    test1();
    test2();
    test3();
    test4();
    test13956();
    test5();
    test6();
    test10071();
    test7();
    test8();
    test11802();
    test9();
    test10();
    test13955();
    test11();
    testvalist();
    test12825();
    test13161();
    test14();
    test13289();
    test15();
    test16();
    func13707();
    func13932(S13932!(-1)(0));
    foo13337(S13337());
    test14195();
    test14200();
    test14956(S14956());
    testVtable();
    fuzz();
    testeh();
    testeh2();
    testeh3();
    test15576();
    version (TEST15579) test15579();
    test15610();
    test15455();
    test15372();
    test15802();
    test16536();
    test15589();
    test15589b();
    test18928();
    test18953();
    test18966();
    test19134();
    test18955();
    testPreviewIn();

    printf("Success\n");
}
