feat(tvix/eval): compile creation of closure objects

Fully implements the instructions for compiling closure
objects (without runtime handling yet).

Closure (and thunk) objects are created at runtime by capturing all
known upvalues. To represent this, the instructions for creating them
need to have a variable number of arguments. Due to that, this commit
introduces new variants in OpCode that are not actually operations,
but data.

If the VM is implemented correctly, the instruction pointer should
never point at these. Due to this, the VM will panic if it sees a data
operand during an execution run.

Change-Id: Ic56b49b3a42736dc437751e76df0e89c8d0619c6
Reviewed-on: https://cl.tvl.fyi/c/depot/+/6291
Tested-by: BuildkiteCI
Reviewed-by: grfn <grfn@gws.fyi>
This commit is contained in:
Vincent Ambo 2022-08-27 00:21:08 +03:00 committed by tazjin
parent e4fadfaaf8
commit 69d8f17a26
4 changed files with 51 additions and 6 deletions

View file

@ -856,9 +856,27 @@ impl Compiler {
crate::disassembler::disassemble_chunk(&compiled.lambda.chunk);
}
self.emit_constant(Value::Closure(Closure {
lambda: compiled.lambda,
}));
// If the function is not a closure, just emit it directly and
// move on.
if compiled.lambda.upvalue_count == 0 {
self.emit_constant(Value::Closure(Closure::new(compiled.lambda)));
return;
}
// If the function is a closure, we need to emit the variable
// number of operands that allow the runtime to close over the
// upvalues.
let closure_idx = self
.chunk()
.push_constant(Value::Closure(Closure::new(compiled.lambda)));
self.chunk().push_op(OpCode::OpClosure(closure_idx));
for upvalue in compiled.scope.upvalues {
match upvalue {
Upvalue::Stack(idx) => self.chunk().push_op(OpCode::DataLocalIdx(idx)),
Upvalue::Upvalue(idx) => self.chunk().push_op(OpCode::DataUpvalueIdx(idx)),
};
}
}
fn compile_apply(&mut self, node: ast::Apply) {