module core.lifetime;

void copyEmplace(S, T)(ref S source, ref T target) @system
    if (is(immutable S == immutable T))
{
    import core.internal.traits : BaseElemOf, hasElaborateCopyConstructor, Unconst, Unqual;

    // cannot have the following as simple template constraint due to nested-struct special case...
    static if (!__traits(compiles, (ref S src) { T tgt = src; }))
    {
        alias B = BaseElemOf!T;
        enum isNestedStruct = is(B == struct) && __traits(isNested, B);
        static assert(isNestedStruct, "cannot copy-construct " ~ T.stringof ~ " from " ~ S.stringof);
    }

    void blit()
    {
        import core.stdc.string : memcpy;
        memcpy(cast(Unqual!(T)*) &target, cast(Unqual!(T)*) &source, T.sizeof);
    }

    static if (is(T == struct))
    {
        static if (__traits(hasPostblit, T))
        {
            blit();
            (cast() target).__xpostblit();
        }
        else static if (__traits(hasCopyConstructor, T))
        {
            // https://issues.dlang.org/show_bug.cgi?id=22766
            import core.internal.lifetime : emplaceInitializer;
            emplaceInitializer(*(cast(Unqual!T*)&target));
            static if (__traits(isNested, T))
            {
                 // copy context pointer
                *(cast(void**) &target.tupleof[$-1]) = cast(void*) source.tupleof[$-1];
            }
            target.__ctor(source); // invoke copy ctor
        }
        else
        {
            blit(); // no opAssign
        }
    }
    else static if (is(T == E[n], E, size_t n))
    {
        static if (hasElaborateCopyConstructor!E)
        {
            size_t i;
            try
            {
                for (i = 0; i < n; i++)
                    copyEmplace(source[i], target[i]);
            }
            catch (Exception e)
            {
                // destroy, in reverse order, what we've constructed so far
                while (i--)
                    destroy(*cast(Unconst!(E)*) &target[i]);
                throw e;
            }
        }
        else // trivial copy
        {
            blit(); // all elements at once
        }
    }
    else
    {
        *cast(Unconst!(T)*) &target = *cast(Unconst!(T)*) &source;
    }
}

T* emplace(T)(T* chunk) @safe pure nothrow
{
    import core.internal.lifetime : emplaceRef;

    emplaceRef!T(*chunk);
    return chunk;
}

template forward(args...)
{
    import core.internal.traits : AliasSeq;

    template fwd(alias arg)
    {
        // by ref || lazy || const/immutable
        static if (__traits(isRef,  arg) ||
                   __traits(isOut,  arg) ||
                   __traits(isLazy, arg) ||
                   !is(typeof(move(arg))))
            alias fwd = arg;
        // (r)value
        else
            @property auto fwd()
            {
                version (DigitalMars) { /* @@BUG 23890@@ */ } else pragma(inline, true);
                return move(arg);
            }
    }

    alias Result = AliasSeq!();
    static foreach (arg; args)
        Result = AliasSeq!(Result, fwd!arg);
    static if (Result.length == 1)
        alias forward = Result[0];
    else
        alias forward = Result;
}
