feat(tvix/eval): track source spans for OpForce
instructions
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
This commit is contained in:
parent
1ea88fcb65
commit
6cbd580ba5
1 changed files with 23 additions and 23 deletions
|
@ -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,
|
||||
|
|
Loading…
Reference in a new issue