diff options
author | Vincent Ambo <mail@tazj.in> | 2022-08-29T18·57+0300 |
---|---|---|
committer | tazjin <tazjin@tvl.su> | 2022-09-07T15·25+0000 |
commit | 5b4f3811e6d1683c30ce293b4019eb2ecb50cb09 (patch) | |
tree | f9ce0381e616880d99ff33cd6b976fb6c13858e9 | |
parent | 6a42d9bddffa488fbc6251e5282e9e3d6e8518a3 (diff) |
feat(tvix/eval): insert strictness points for unary/binary operators r/4693
The arguments of all unary/binary operators that are built in to Nix are forced when encountered. This emits the necessary OpForce operations. Change-Id: I691fcdbebfe7586cfe217c68d44b10b1192f82d1 Reviewed-on: https://cl.tvl.fyi/c/depot/+/6357 Tested-by: BuildkiteCI Reviewed-by: sterni <sternenseemann@systemli.org>
-rw-r--r-- | tvix/eval/src/compiler/mod.rs | 18 |
1 files changed, 17 insertions, 1 deletions
diff --git a/tvix/eval/src/compiler/mod.rs b/tvix/eval/src/compiler/mod.rs index d34564a75992..b3315ee9119e 100644 --- a/tvix/eval/src/compiler/mod.rs +++ b/tvix/eval/src/compiler/mod.rs @@ -217,6 +217,7 @@ impl Compiler { fn compile_unary_op(&mut self, slot: Option<LocalIdx>, op: ast::UnaryOp) { self.compile(slot, op.expr().unwrap()); + self.emit_force(); let opcode = match op.operator().unwrap() { ast::UnaryOpKind::Invert => OpCode::OpInvert, @@ -245,7 +246,10 @@ impl Compiler { // the stack in the correct order before pushing the // instruction for the operation itself. self.compile(slot, op.lhs().unwrap()); + self.emit_force(); + self.compile(slot, op.rhs().unwrap()); + self.emit_force(); match op.operator().unwrap() { BinOpKind::Add => self.chunk().push_op(OpCode::OpAdd), @@ -281,6 +285,7 @@ impl Compiler { // Leave left-hand side value on the stack. self.compile(slot, node.lhs().unwrap()); + self.emit_force(); // If this value is false, jump over the right-hand side - the // whole expression is false. @@ -291,6 +296,7 @@ impl Compiler { // of the whole expression. self.chunk().push_op(OpCode::OpPop); self.compile(slot, node.rhs().unwrap()); + self.emit_force(); self.patch_jump(end_idx); self.chunk().push_op(OpCode::OpAssertBool); @@ -305,12 +311,15 @@ impl Compiler { // Leave left-hand side value on the stack self.compile(slot, node.lhs().unwrap()); + self.emit_force(); // Opposite of above: If this value is **true**, we can // short-circuit the right-hand side. let end_idx = self.chunk().push_op(OpCode::OpJumpIfTrue(JumpOffset(0))); self.chunk().push_op(OpCode::OpPop); self.compile(slot, node.rhs().unwrap()); + self.emit_force(); + self.patch_jump(end_idx); self.chunk().push_op(OpCode::OpAssertBool); } @@ -324,12 +333,15 @@ impl Compiler { // Leave left-hand side value on the stack and invert it. self.compile(slot, node.lhs().unwrap()); + self.emit_force(); self.chunk().push_op(OpCode::OpInvert); // Exactly as `||` (because `a -> b` = `!a || b`). let end_idx = self.chunk().push_op(OpCode::OpJumpIfTrue(JumpOffset(0))); self.chunk().push_op(OpCode::OpPop); self.compile(slot, node.rhs().unwrap()); + self.emit_force(); + self.patch_jump(end_idx); self.chunk().push_op(OpCode::OpAssertBool); } @@ -1144,6 +1156,10 @@ impl Compiler { idx } + fn emit_force(&mut self) { + self.chunk().push_op(OpCode::OpForce); + } + fn emit_warning(&mut self, node: rnix::SyntaxNode, kind: WarningKind) { self.warnings.push(EvalWarning { node, kind }) } @@ -1270,7 +1286,7 @@ pub fn compile( // `OpForce`. A thunk should not be returned to the user in an // unevaluated state (though in practice, a value *containing* a // thunk might be returned). - c.chunk().push_op(OpCode::OpForce); + c.emit_force(); Ok(CompilationOutput { lambda: c.contexts.pop().unwrap().lambda, |