
import libc;
import errors.backtrace.(showBacktrace);



//
// Exception, NoException, currentException
//

variant Exception (NoException);

record NoException ();

overload Exception() = Exception(NoException());

private exceptionObject(exp:RawPointer) = ref Pointer[Exception](exp)^;


//
// throwValue, continueException
//

throwValue(x) {
    errorNoThrow(x);
}

[ | ExceptionsEnabled?]
overload throwValue(forward x) {
    var exp = allocateObject(Exception(x));
    signalException(RawPointer(exp));
}

continueException(exp:RawPointer) {
    signalException(exp);
}

private signalException(exp:RawPointer) __llvm__{
    %expv = load i8** %exp
    ret i8* %expv
}



//
// throwButPropagate
//

throwButPropagate(ex, ..T) {
    if (true)
        throw ex;
    else
        return forward ..mapValues(typeToLValue, ..T);
}

alias throwButPropagateLike(ex, ..expr) {
    if (true)
        throw ex;
    else
        return forward ..expr;
}



//
// exceptionIs?, exceptionAs, exceptionAsAny
//

[T]
exceptionIs?(static T, exp:RawPointer) = nestedVariantIs?(exceptionObject(exp), T);

[T]
exceptionAs(static T, exp:RawPointer) --> ex:T {
    ex <-- moveUnsafe(nestedVariantAs(exceptionObject(exp), T));
    freeRawMemory(exp);
}

exceptionAsAny(exp:RawPointer) --> ex:Exception {
    ex <-- moveUnsafe(exceptionObject(exp));
    freeRawMemory(exp);
}



//
// unhandledExceptionInExternal
// exceptionInInitializer, exceptionInFinalizer
//

private inline errorMsg(msg) {
    libc.fprintf(libc.stderr, cstring(msg));
    showBacktrace();
}

unhandledExceptionInExternal() {
    errorMsg("unhandled exception in external procedure\n");
    libc.abort();
}

exceptionInInitializer() {
    errorMsg("exception when initializing globals\n");
    libc.abort();
}

exceptionInFinalizer() {
    errorMsg("exception when destroying globals\n");
    libc.abort();
}
