module core.internal.traits;

alias AliasSeq(T...) = T;

template allSatisfy(alias F, T...)
{
	static foreach (Ti; T)
	{
		static if (!is(typeof(allSatisfy) == bool) && // not yet defined
							 !F!(Ti))
		{
			enum allSatisfy = false;
		}
	}
	static if (!is(typeof(allSatisfy) == bool)) // if not yet defined
	{
		enum allSatisfy = true;
	}
}

template Unqual(T : const U, U)
{
	static if (is(U == shared V, V))
		alias Unqual = V;
	else
		alias Unqual = U;
}

alias Unconst(T : const U, U) = U;

template hasElaborateMove(S)
{
	static if (__traits(isStaticArray, S))
	{
		enum bool hasElaborateMove = S.sizeof && hasElaborateMove!(BaseElemOf!S);
	}
	else static if (is(S == struct))
	{
		enum hasElaborateMove = (is(typeof(S.init.opPostMove(lvalueOf!S))) &&
																!is(typeof(S.init.opPostMove(rvalueOf!S)))) ||
														anySatisfy!(.hasElaborateMove, Fields!S);
	}
	else
	{
		enum bool hasElaborateMove = false;
	}
}

template substInout(T)
{
    static if (is(T == immutable))
    {
        alias substInout = T;
    }
    else static if (is(T : shared const U, U) || is(T : const U, U))
    {
        // U is top-unqualified
        mixin("alias substInout = "
            ~ (is(T == shared) ? "shared " : "")
            ~ (is(T == const) || is(T == inout) ? "const " : "")    // substitute inout to const
            ~ "substInoutForm!U;");
    }
    else
        static assert(0);
}

template anySatisfy(alias F, Ts...)
{
    static foreach (T; Ts)
    {
        static if (!is(typeof(anySatisfy) == bool) && // not yet defined
                   F!T)
        {
            enum anySatisfy = true;
        }
    }
    static if (!is(typeof(anySatisfy) == bool)) // if not yet defined
    {
        enum anySatisfy = false;
    }
}

template hasElaborateCopyConstructor(S)
{
    static if (__traits(isStaticArray, S))
    {
        enum bool hasElaborateCopyConstructor = S.sizeof && hasElaborateCopyConstructor!(BaseElemOf!S);
    }
    else static if (is(S == struct))
    {
        enum hasElaborateCopyConstructor = __traits(hasCopyConstructor, S) || __traits(hasPostblit, S);
    }
    else
    {
        enum bool hasElaborateCopyConstructor = false;
    }
}

template hasElaborateDestructor(S)
{
    static if (__traits(isStaticArray, S))
    {
        enum bool hasElaborateDestructor = S.sizeof && hasElaborateDestructor!(BaseElemOf!S);
    }
    else static if (is(S == struct))
    {
        // Once https://issues.dlang.org/show_bug.cgi?id=24865 is fixed, then
        // this should be the implementation, but until that's fixed, we need the
        // uncommented code.
        // enum hasElaborateDestructor = __traits(hasMember, S, "__xdtor");

        enum hasElaborateDestructor = hasDtor([__traits(allMembers, S)]);
    }
    else
    {
        enum bool hasElaborateDestructor = false;
    }
}

enum bool isAggregateType(T) = is(T == struct) || is(T == union) ||
                               is(T == class) || is(T == interface);

enum bool isPointer(T) = is(T == U*, U) && !isAggregateType!T;
