


//
// typeId[T]
//

private var _nextTypeId = 0;

private nextTypeId() {
    var x = _nextTypeId;
    _nextTypeId += 1;
    return x;
}

private var typeId[T] = nextTypeId();



//
// Any
//

record Any (
    id : Int,
    ptr : Pointer[Byte],
    size : SizeT,
    alignment : SizeT,
    copyConstructor : CodePointer[[Opaque], [Opaque]],
    destructor : CodePointer[[Opaque], []],
);

// disable default value semantics
overload RegularRecord?(static Any) = false;

overload Any() {
    return Any(
        -1,
        null(Byte),
        SizeT(0),
        SizeT(0),
        CodePointer[[Opaque],[Opaque]](0),
        CodePointer[[Opaque],[]](0)
    );
}

overload Any(x:Any) {
    var ptr = allocateRawMemoryAligned(Byte, x.size, x.alignment);
    try {
        ptr^ <-- x.copyConstructor(x.ptr^);
    }
    catch (e) {
        freeRawMemoryAligned(ptr);
        throw e;
    }
    return Any(
        x.id,
        ptr,
        x.size,
        x.alignment,
        x.copyConstructor,
        x.destructor
    );
}

overload BitwiseMovedType?(static Any) = true;

overload resetUnsafe(x:Any) {
    x <-- Any();
}

overload destroy(x:Any) {
    if (not null?(x.ptr)) {
        x.destructor(x.ptr^);
        freeRawMemoryAligned(x.ptr);
    }
}

overload assign(ref dest:Any, ref src:Any) {
    if (&dest == &src)
        return;
    dest = Any(src);
}

[T]
alias box(x:T) {
    var ptr = allocateRawMemoryAligned(Byte, TypeSize(T), TypeAlignment(T));
    try {
        Pointer[T](ptr)^ <-- x;
    }
    catch (e) {
        freeRawMemoryAligned(ptr);
        throw e;
    }
    var copyConstructor = makeCodePointer(T, T);
    var destructor = makeCodePointer(destroy, T);
    return Any(
        typeId[T],
        Pointer[Byte](ptr),
        TypeSize(T),
        TypeAlignment(T),
        CodePointer[[Opaque],[Opaque]](copyConstructor),
        CodePointer[[Opaque],[]](destructor)
    );
}

[T]
anyIs?(x:Any, static T) = (x.id == typeId[T]);

[T]
unbox(x:Any, static T) {
    assert(x.id == typeId[T]);
    return ref Pointer[T](x.ptr)^;
}
