about summary refs log tree commit diff
diff options
context:
space:
mode:
authorVincent Ambo <mail@tazj.in>2022-08-29T18·57+0300
committertazjin <tazjin@tvl.su>2022-09-07T15·25+0000
commit5b4f3811e6d1683c30ce293b4019eb2ecb50cb09 (patch)
treef9ce0381e616880d99ff33cd6b976fb6c13858e9
parent6a42d9bddffa488fbc6251e5282e9e3d6e8518a3 (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.rs18
1 files changed, 17 insertions, 1 deletions
diff --git a/tvix/eval/src/compiler/mod.rs b/tvix/eval/src/compiler/mod.rs
index d34564a759..b3315ee911 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,