/*
REQUIRED_ARGS: -HC -c -o-
PERMUTE_ARGS:
TEST_OUTPUT:
---
// Automatically generated by Digital Mars D Compiler

#pragma once

#include <assert.h>
#include <math.h>
#include <stddef.h>
#include <stdint.h>

struct Outer final
{
    int32_t a;
    struct Member final
    {
        typedef int32_t Nested;
        Member()
        {
        }
    };

    Outer() :
        a()
    {
    }
    Outer(int32_t a) :
        a(a)
        {}
};

enum : int32_t { SomeOtherLength = 1 };

struct ActualBuffer final
{
    ActualBuffer()
    {
    }
};

template <typename T>
struct A final
{
    T x;
    enum : int32_t { Enum = 42 };

    static int32_t GsharedNum;
    const int32_t MemNum;
    void foo();
    A()
    {
    }
};

template <typename T>
struct NotInstantiated final
{
    NotInstantiated()
    {
    }
};

struct B final
{
    A<int32_t > x;
    B() :
        x()
    {
    }
    B(A<int32_t > x) :
        x(x)
        {}
};

template <typename T>
struct Foo final
{
    T val;
    Foo()
    {
    }
};

template <typename T>
struct Bar final
{
    Foo<T > v;
    Bar()
    {
    }
};

template <typename T>
struct Array final
{
    typedef Array This;
    typedef typeof(1 + 2) Int;
    typedef typeof(T::a) IC;
    Array(size_t dim);
    ~Array();
    void get() const;
    template <typename T>
    bool opCast() const;
    typename T::Member i;
    typename Outer::Member::Nested j;
    void visit(typename T::Member::Nested i);
    Array()
    {
    }
};

template <typename T, typename U>
extern T foo(U u);

extern A<A<int32_t > > aaint;

template <typename T>
class Parent
{
public:
    T parentMember;
    void parentFinal();
    virtual void parentVirtual();
};

template <typename T>
class Child final : public Parent<T >
{
public:
    T childMember;
    void parentVirtual() override;
    T childFinal();
};

extern void withDefTempl(A<int32_t > a = A<int32_t >(2, 13));

template <typename T>
extern void withDefTempl2(A<T > a = static_cast<A<T >>(A<T >(2)));

class ChildInt : public Parent<int32_t >
{
};

struct HasMixins final
{
    void foo(int32_t t);
    HasMixins()
    {
    }
};

template <typename T>
struct HasMixinsTemplate final
{
    void foo(T t);
    HasMixinsTemplate()
    {
    }
};

extern HasMixinsTemplate<bool > hmti;

template <typename T>
struct NotAA final
{
    enum : int32_t { length = 12 };

    T buffer[length];
    T otherBuffer[SomeOtherLength];
    T calcBuffer[foo(1)];
    NotAA()
    {
    }
};

template <typename Buffer>
struct BufferTmpl final
{
    Buffer buffer;
    Buffer buffer2;
    BufferTmpl()
    {
    }
};

struct ImportedBuffer final
{
    typedef ActualBuffer Buffer;
    ActualBuffer buffer2;
    ImportedBuffer() :
        buffer2()
    {
    }
    ImportedBuffer(ActualBuffer buffer2) :
        buffer2(buffer2)
        {}
};
---
*/

extern (C++) struct A(T)
{
    T x;
    enum Enum = 42;
    __gshared GsharedNum = 43;
    immutable MemNum = 13;
    void foo() {}
}

// Invalid declarations accepted because it's not instantiated
extern (C++) struct NotInstantiated(T)
{
    enum T noInit;
    enum missingSem = T.init;
}

extern (C++) struct B
{
    A!int x;
}

// https://issues.dlang.org/show_bug.cgi?id=20604
extern(C++)
{
    struct Foo (T)
    {
        T val;
    }

    struct Bar (T)
    {
        Foo!T v;
    }
}

extern (C++) struct Array(T)
{
    alias This = typeof(this);
    alias Int = typeof(1 + 2);
    alias IC = typeof(T.a);

    this(size_t dim) pure nothrow {}
    @disable this(this);
    ~this() {}
    void get() const {}

    bool opCast(T)() const pure nothrow @nogc @safe
    if (is(T == bool))
    {
        return str.ptr !is null;
    }

    T.Member i;
    Outer.Member.Nested j;
    void visit(T.Member.Nested i) {}
}

struct Outer
{
    int a;
    static struct Member
    {
        alias Nested = int;
    }
}

// alias AO = Array!Outer;

extern(C++) T foo(T, U)(U u) { return T.init; }

extern(C++) __gshared A!(A!int) aaint;

extern(C++) class Parent(T)
{
    T parentMember;
    final void parentFinal() {}
    void parentVirtual() {}
}

extern(C++) final class Child(T) : Parent!T
{
    T childMember;
    override void parentVirtual() {}
    T childFinal() { return T.init; }
}

extern(C++) void withDefTempl(A!int a = A!int(2)) {}

extern(C++) void withDefTempl2(T)(A!T a = A!T(2)) {}

extern(C++) alias withDefTempl2Inst = withDefTempl2!int;

extern(C++) class ChildInt : Parent!int {}

/******************************************************
 * Mixins
 */

extern (C++):

mixin template MixinA(T)
{
    void foo(T t) {}
}

mixin template MixinB() {}

struct HasMixins
{
    mixin MixinA!int;
    mixin MixinB;
}

struct HasMixinsTemplate(T)
{
    mixin MixinA!T;
    mixin MixinB;
}

__gshared HasMixinsTemplate!bool hmti;

/// Declarations that look like associative arrays

extern(D) enum SomeOtherLength = 1;

struct NotAA(T)
{
    private:
    enum length = 12;
    public:
    T[length] buffer;
    T[SomeOtherLength] otherBuffer;
    T[foo(1)] calcBuffer;
}

// Same name but hidden by the template paramter
extern (D) struct Buffer {}
extern (D) struct ActualBuffer {}

struct BufferTmpl(Buffer)
{
    Buffer buffer;
    mixin BufferMixin!();
}

struct ImportedBuffer
{
    alias Buffer = ActualBuffer;
    mixin BufferMixin!();
}

mixin template BufferMixin()
{
    Buffer buffer2;
}
