about summary refs log tree commit diff
diff options
context:
space:
mode:
authorVincent Ambo <mail@tazj.in>2022-09-01T16·13+0300
committertazjin <tazjin@tvl.su>2022-09-07T20·04+0000
commit6cbd580ba53885d6c4dff1e3a81406604ae13f3a (patch)
treec896f755fb383ff2619f592ed3d3a7e9aab748fe
parent1ea88fcb6587c110acd8c798a5c198b492e04bad (diff)
feat(tvix/eval): track source spans for `OpForce` instructions r/4735
These source spans will always point to the *value* that is being
forced, not the instruction that caused the force to be emitted. This
makes sense so that errors during forcing point at the value and not
the surrounding expression.

Change-Id: I4694414a3281a0de878f71634105b92148ec61f6
Reviewed-on: https://cl.tvl.fyi/c/depot/+/6402
Reviewed-by: sterni <sternenseemann@systemli.org>
Tested-by: BuildkiteCI
-rw-r--r--tvix/eval/src/compiler/mod.rs46
1 files changed, 23 insertions, 23 deletions
diff --git a/tvix/eval/src/compiler/mod.rs b/tvix/eval/src/compiler/mod.rs
index 09123d3a99fb..faeee3dd5723 100644
--- a/tvix/eval/src/compiler/mod.rs
+++ b/tvix/eval/src/compiler/mod.rs
@@ -251,7 +251,7 @@ impl Compiler<'_> {
 
     fn compile_unary_op(&mut self, slot: Option<LocalIdx>, op: ast::UnaryOp) {
         self.compile(slot, op.expr().unwrap());
-        self.emit_force();
+        self.emit_force(&op);
 
         let opcode = match op.operator().unwrap() {
             ast::UnaryOpKind::Invert => OpCode::OpInvert,
@@ -280,10 +280,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.emit_force(&op.lhs().unwrap());
 
         self.compile(slot, op.rhs().unwrap());
-        self.emit_force();
+        self.emit_force(&op.rhs().unwrap());
 
         match op.operator().unwrap() {
             BinOpKind::Add => self.push_op(OpCode::OpAdd, &op),
@@ -319,7 +319,7 @@ impl Compiler<'_> {
 
         // Leave left-hand side value on the stack.
         self.compile(slot, node.lhs().unwrap());
-        self.emit_force();
+        self.emit_force(&node.lhs().unwrap());
 
         // If this value is false, jump over the right-hand side - the
         // whole expression is false.
@@ -330,7 +330,7 @@ impl Compiler<'_> {
         // of the whole expression.
         self.push_op(OpCode::OpPop, &node);
         self.compile(slot, node.rhs().unwrap());
-        self.emit_force();
+        self.emit_force(&node.rhs().unwrap());
 
         self.patch_jump(end_idx);
         self.push_op(OpCode::OpAssertBool, &node);
@@ -345,14 +345,14 @@ impl Compiler<'_> {
 
         // Leave left-hand side value on the stack
         self.compile(slot, node.lhs().unwrap());
-        self.emit_force();
+        self.emit_force(&node.lhs().unwrap());
 
         // Opposite of above: If this value is **true**, we can
         // short-circuit the right-hand side.
         let end_idx = self.push_op(OpCode::OpJumpIfTrue(JumpOffset(0)), &node);
         self.push_op(OpCode::OpPop, &node);
         self.compile(slot, node.rhs().unwrap());
-        self.emit_force();
+        self.emit_force(&node.rhs().unwrap());
 
         self.patch_jump(end_idx);
         self.push_op(OpCode::OpAssertBool, &node);
@@ -367,14 +367,14 @@ impl Compiler<'_> {
 
         // Leave left-hand side value on the stack and invert it.
         self.compile(slot, node.lhs().unwrap());
-        self.emit_force();
+        self.emit_force(&node.lhs().unwrap());
         self.push_op(OpCode::OpInvert, &node);
 
         // Exactly as `||` (because `a -> b` = `!a || b`).
         let end_idx = self.push_op(OpCode::OpJumpIfTrue(JumpOffset(0)), &node);
         self.push_op(OpCode::OpPop, &node);
         self.compile(slot, node.rhs().unwrap());
-        self.emit_force();
+        self.emit_force(&node.rhs().unwrap());
 
         self.patch_jump(end_idx);
         self.push_op(OpCode::OpAssertBool, &node);
@@ -403,12 +403,12 @@ impl Compiler<'_> {
         match node {
             ast::Attr::Dynamic(dynamic) => {
                 self.compile(slot, dynamic.expr().unwrap());
-                self.emit_force();
+                self.emit_force(&dynamic.expr().unwrap());
             }
 
             ast::Attr::Str(s) => {
-                self.compile_str(slot, s);
-                self.emit_force();
+                self.compile_str(slot, s.clone());
+                self.emit_force(&s);
             }
 
             ast::Attr::Ident(ident) => self.emit_literal_ident(&ident),
@@ -469,7 +469,7 @@ impl Compiler<'_> {
                         // than pushing/popping the same attrs
                         // potentially a lot of times.
                         self.compile(slot, from.expr().unwrap());
-                        self.emit_force();
+                        self.emit_force(&from.expr().unwrap());
                         self.emit_literal_ident(&ident);
                         self.push_op(OpCode::OpAttrsSelect, &ident);
                     }
@@ -532,8 +532,8 @@ impl Compiler<'_> {
         }
 
         // Push the set onto the stack
-        self.compile(slot, set);
-        self.emit_force();
+        self.compile(slot, set.clone());
+        self.emit_force(&set);
 
         // Compile each key fragment and emit access instructions.
         //
@@ -581,8 +581,8 @@ impl Compiler<'_> {
         path: ast::Attrpath,
         default: ast::Expr,
     ) {
-        self.compile(slot, set);
-        self.emit_force();
+        self.compile(slot, set.clone());
+        self.emit_force(&set);
         let mut jumps = vec![];
 
         for fragment in path.attrs() {
@@ -686,7 +686,7 @@ impl Compiler<'_> {
                 Some(from) => {
                     for ident in inherit.idents() {
                         self.compile(slot, from.expr().unwrap());
-                        self.emit_force();
+                        self.emit_force(&from.expr().unwrap());
 
                         self.emit_literal_ident(&ident);
                         self.push_op(OpCode::OpAttrsSelect, &ident);
@@ -836,7 +836,7 @@ impl Compiler<'_> {
         // resolve that directly (thus avoiding duplication on the
         // stack).
         self.compile(slot, node.namespace().unwrap());
-        self.emit_force();
+        self.emit_force(&node.namespace().unwrap());
 
         let local_idx = self.scope_mut().declare_phantom();
         let with_idx = self.scope().stack_index(local_idx);
@@ -1224,8 +1224,8 @@ impl Compiler<'_> {
         idx
     }
 
-    fn emit_force(&mut self) {
-        self.push_op_old(OpCode::OpForce);
+    fn emit_force<N: AstNode>(&mut self, node: &N) {
+        self.push_op(OpCode::OpForce, node);
     }
 
     fn emit_warning(&mut self, node: rnix::SyntaxNode, kind: WarningKind) {
@@ -1350,13 +1350,13 @@ pub fn compile<'code>(
         errors: vec![],
     };
 
-    c.compile(None, expr);
+    c.compile(None, expr.clone());
 
     // The final operation of any top-level Nix program must always be
     // `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.emit_force();
+    c.emit_force(&expr);
 
     Ok(CompilationOutput {
         lambda: c.contexts.pop().unwrap().lambda,