fix(tvix/eval): add operation to assert boolean type
This operation is required because both sides of the logical operators are strictly evaluated by Nix, even if the resulting value is not used further. For example, in our implementation of `&&`, if the left-hand side is `true`, then the result of the expression is simply the right-hand side value. This value must be asserted to be a boolean for the semantics of the language to work correctly. Change-Id: I34f5364f2a444753fa1d8b0a1a2b2d9cdf7c6700 Reviewed-on: https://cl.tvl.fyi/c/depot/+/6157 Tested-by: BuildkiteCI Reviewed-by: sterni <sternenseemann@systemli.org> Reviewed-by: grfn <grfn@gws.fyi>
This commit is contained in:
parent
aaa994137a
commit
671915837a
4 changed files with 24 additions and 0 deletions
|
@ -350,6 +350,7 @@ impl Compiler {
|
|||
self.compile(node.rhs().unwrap())?;
|
||||
|
||||
self.patch_jump(end_idx);
|
||||
self.chunk.add_op(OpCode::OpAssertBool);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -370,6 +371,7 @@ impl Compiler {
|
|||
self.chunk.add_op(OpCode::OpPop);
|
||||
self.compile(node.rhs().unwrap())?;
|
||||
self.patch_jump(end_idx);
|
||||
self.chunk.add_op(OpCode::OpAssertBool);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -390,6 +392,7 @@ impl Compiler {
|
|||
self.chunk.add_op(OpCode::OpPop);
|
||||
self.compile(node.rhs().unwrap())?;
|
||||
self.patch_jump(end_idx);
|
||||
self.chunk.add_op(OpCode::OpAssertBool);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -53,4 +53,7 @@ pub enum OpCode {
|
|||
|
||||
// Strings
|
||||
OpInterpolate(usize),
|
||||
|
||||
// Type assertion operators
|
||||
OpAssertBool,
|
||||
}
|
||||
|
|
|
@ -91,6 +91,10 @@ impl Value {
|
|||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_bool(&self) -> bool {
|
||||
matches!(self, Value::Bool(_))
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Value {
|
||||
|
|
|
@ -187,6 +187,20 @@ impl VM {
|
|||
self.ip += offset;
|
||||
}
|
||||
}
|
||||
|
||||
// These assertion operations error out if the stack
|
||||
// top is not of the expected type. This is necessary
|
||||
// to implement some specific behaviours of Nix
|
||||
// exactly.
|
||||
OpCode::OpAssertBool => {
|
||||
let val = self.peek(0);
|
||||
if !val.is_bool() {
|
||||
return Err(Error::TypeError {
|
||||
expected: "bool",
|
||||
actual: val.type_of(),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if self.ip == self.chunk.code.len() {
|
||||
|
|
Loading…
Reference in a new issue