Implement reraise correctly: don't destroy the parent frame

This commit is contained in:
Konstantin Nazarov 2024-10-13 20:37:54 +01:00
parent b9fa84ba0e
commit 5f2636c62f
Signed by: knazarov
GPG key ID: 4CFE0A42FA409C22
5 changed files with 73 additions and 49 deletions

View file

@ -63,6 +63,8 @@ const char* errname(ErrorCode errcode) {
return "runtime-error";
case ErrorCode::Raise:
return "raise";
case ErrorCode::Reraise:
return "reraise";
case ErrorCode::TaskFailed:
return "task-failed";
}

View file

@ -23,6 +23,7 @@ enum class ErrorCode {
SyntaxError,
RuntimeError,
Raise,
Reraise,
TaskFailed,
};

View file

@ -417,48 +417,9 @@ Result<StackFrame> stdlib_reraise(const StackFrame& stack) {
if (!cont_val.is<Continuation>()) return ERROR(TypeMismatch);
Continuation& cont = *cont_val.to<Continuation>();
StackFrame cont_stack = TRY(cont.frame());
auto new_cont = Value(TRY(Continuation::create(cont_val, stack)));
StackFrame cur = TRY(stack.copy());
uint64_t depth = 0;
while (true) {
if (cur.guard()) {
auto state = TRY(cur.get(1));
if (!state.is<Int64>()) return ERROR(TypeMismatch);
auto state_int = state.to<Int64>()->value();
if (state_int > 1) {
auto val = TRY(cont.value());
auto remaining_stack = TRY(stack.detach(depth - 1));
Value fun = TRY(params.get(0));
cur = TRY(cur.set(1, Value(TRY(Int64::create((int64_t)0)))));
cur = TRY(cur.set(2, Value(std::move(fun))));
cur = TRY(cur.settop(3));
cur = TRY(remaining_stack.attach(cur));
cur = TRY(cont_stack.attach(cur));
uint64_t start_from = depth + TRY(cont_stack.depth()) + 1;
auto res = TRY(cur.unwind(val, start_from));
if (res.is<StackFrame>())
return res.to<StackFrame>()->copy();
else
break;
}
}
auto parent = TRY(cur.parent());
if (parent.is<Nil>()) break;
cur = TRY(parent.to<StackFrame>()->copy());
++depth;
}
return ERROR_OBJ(Raise, cont_val);
return ERROR_OBJ(Reraise, new_cont);
}
Result<StackFrame> stdlib_task(const StackFrame& stack) {

View file

@ -302,17 +302,16 @@ Result<void> VM::step_bytecode() {
return Result<void>();
}
Result<Value> VM::unwind(const Continuation& cont) {
Result<Value> VM::unwind(const Continuation& cont, uint64_t start_from) {
auto val = TRY(cont.value());
auto cont_stack = TRY(cont.frame());
return cont_stack.unwind(val);
return cont_stack.unwind(val, start_from);
}
Result<StackFrame> VM::handle_raise(const Continuation& cont) {
// TODO: this should do proper stack unwinding
auto unwind_res = TRY(unwind(cont));
Result<StackFrame> VM::handle_raise(const Continuation& cont,
uint64_t start_from) {
auto unwind_res = TRY(unwind(cont, start_from));
if (!unwind_res.is<Nil>()) {
auto new_stack = TRY(unwind_res.to<StackFrame>()->copy());
@ -358,6 +357,59 @@ Result<StackFrame> VM::handle_raise(const Continuation& cont) {
return ERROR(TypeMismatch);
}
Result<StackFrame> VM::handle_reraise(const Continuation& parent_cont) {
auto stack = TRY(parent_cont.frame());
auto cont_val = TRY(parent_cont.value());
if (!cont_val.is<Continuation>()) return ERROR(TypeMismatch);
Continuation& cont = *cont_val.to<Continuation>();
StackFrame cont_stack = TRY(cont.frame());
StackFrame cur = TRY(stack.copy());
uint64_t depth = 0;
while (true) {
if (cur.guard()) {
auto state = TRY(cur.get(1));
if (!state.is<Int64>()) return ERROR(TypeMismatch);
auto state_int = state.to<Int64>()->value();
if (state_int > 1) {
auto val = TRY(cont.value());
auto remaining_stack = TRY(stack.detach(depth - 1));
auto params = TRY(cur.get(0));
Value fun = TRY(params.get(0));
cur = TRY(cur.set(1, Value(TRY(Int64::create((int64_t)0)))));
cur = TRY(cur.settop(2));
cur = TRY(cont_stack.attach(cur));
uint64_t start_from = TRY(cont_stack.depth()) + 1;
auto res = TRY(cur.unwind(val, start_from));
if (res.is<StackFrame>()) {
return res.to<StackFrame>()->copy();
} else {
auto new_cont = TRY(Continuation::create(val, cur));
return handle_raise(new_cont, start_from);
}
}
}
auto parent = TRY(cur.parent());
if (parent.is<Nil>()) break;
cur = TRY(parent.to<StackFrame>()->copy());
++depth;
}
auto new_cont = TRY(Continuation::create(TRY(cont.value()), stack));
return handle_raise(new_cont, 0);
}
Result<void> VM::step_native() {
auto fun = TRY(_stack.fun());
if (!fun.is<StdlibFunction>()) return ERROR(TypeMismatch);
@ -392,6 +444,12 @@ Result<void> VM::step() {
return Result<void>();
}
}
if (res.error() == ErrorCode::Reraise) {
if (obj.is<Continuation>()) {
_stack = TRY(handle_reraise(*obj.to<Continuation>()));
return Result<void>();
}
}
auto cont = TRY(Continuation::create(obj, _stack));
_stack = TRY(handle_raise(cont));

View file

@ -63,8 +63,10 @@ class VM {
Result<void> vm_global_load(Opcode& oc);
Result<void> vm_global_store(Opcode& oc);
Result<Value> unwind(const Continuation& cont);
Result<StackFrame> handle_raise(const Continuation& cont);
Result<Value> unwind(const Continuation& cont, uint64_t start_from = 0);
Result<StackFrame> handle_raise(const Continuation& cont,
uint64_t start_from = 0);
Result<StackFrame> handle_reraise(const Continuation& cont);
Result<Value> get(bool is_const, uint64_t idx);
Result<Value> getconst(uint64_t idx);