chore(tvix/eval): Pass in VM to nix_eq
Pass in, but ignore, a mutable reference to the VM to the `nix_eq` functions, in preparation for using that VM to force thunks during comparison. Change-Id: I565435d8dfb33768f930fdb5a6b0fb1365d7e161 Reviewed-on: https://cl.tvl.fyi/c/depot/+/6651 Autosubmit: grfn <grfn@gws.fyi> Tested-by: BuildkiteCI Reviewed-by: tazjin <tazjin@tvl.su>
This commit is contained in:
parent
915ff5ac2a
commit
0b76ed5615
5 changed files with 55 additions and 20 deletions
|
@ -11,6 +11,7 @@ use std::fmt::Display;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use crate::errors::ErrorKind;
|
use crate::errors::ErrorKind;
|
||||||
|
use crate::vm::VM;
|
||||||
|
|
||||||
use super::string::NixString;
|
use super::string::NixString;
|
||||||
use super::Value;
|
use super::Value;
|
||||||
|
@ -287,7 +288,7 @@ impl NixAttrs {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Compare `self` against `other` for equality using Nix equality semantics
|
/// Compare `self` against `other` for equality using Nix equality semantics
|
||||||
pub fn nix_eq(&self, other: &Self) -> Result<bool, ErrorKind> {
|
pub fn nix_eq(&self, other: &Self, vm: &mut VM) -> Result<bool, ErrorKind> {
|
||||||
match (&self.0, &other.0) {
|
match (&self.0, &other.0) {
|
||||||
(AttrsRep::Empty, AttrsRep::Empty) => Ok(true),
|
(AttrsRep::Empty, AttrsRep::Empty) => Ok(true),
|
||||||
|
|
||||||
|
@ -316,7 +317,7 @@ impl NixAttrs {
|
||||||
name: n2,
|
name: n2,
|
||||||
value: v2,
|
value: v2,
|
||||||
},
|
},
|
||||||
) => Ok(n1.nix_eq(n2)? && v1.nix_eq(v2)?),
|
) => Ok(n1.nix_eq(n2, vm)? && v1.nix_eq(v2, vm)?),
|
||||||
|
|
||||||
(AttrsRep::Map(map), AttrsRep::KV { name, value })
|
(AttrsRep::Map(map), AttrsRep::KV { name, value })
|
||||||
| (AttrsRep::KV { name, value }, AttrsRep::Map(map)) => {
|
| (AttrsRep::KV { name, value }, AttrsRep::Map(map)) => {
|
||||||
|
@ -327,7 +328,7 @@ impl NixAttrs {
|
||||||
if let (Some(m_name), Some(m_value)) =
|
if let (Some(m_name), Some(m_value)) =
|
||||||
(map.get(&NixString::NAME), map.get(&NixString::VALUE))
|
(map.get(&NixString::NAME), map.get(&NixString::VALUE))
|
||||||
{
|
{
|
||||||
return Ok(name.nix_eq(m_name)? && value.nix_eq(m_value)?);
|
return Ok(name.nix_eq(m_name, vm)? && value.nix_eq(m_value, vm)?);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(false)
|
Ok(false)
|
||||||
|
@ -340,7 +341,7 @@ impl NixAttrs {
|
||||||
|
|
||||||
for (k, v1) in m1 {
|
for (k, v1) in m1 {
|
||||||
if let Some(v2) = m2.get(k) {
|
if let Some(v2) = m2.get(k) {
|
||||||
if !v1.nix_eq(v2)? {
|
if !v1.nix_eq(v2, vm)? {
|
||||||
return Ok(false);
|
return Ok(false);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -1,24 +1,38 @@
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
mod nix_eq {
|
mod nix_eq {
|
||||||
|
use crate::observer::NoOpObserver;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use proptest::prelude::ProptestConfig;
|
use proptest::prelude::ProptestConfig;
|
||||||
use test_strategy::proptest;
|
use test_strategy::proptest;
|
||||||
|
|
||||||
#[proptest(ProptestConfig { cases: 5, ..Default::default() })]
|
#[proptest(ProptestConfig { cases: 5, ..Default::default() })]
|
||||||
fn reflexive(x: NixAttrs) {
|
fn reflexive(x: NixAttrs) {
|
||||||
assert!(x.nix_eq(&x).unwrap())
|
let mut observer = NoOpObserver {};
|
||||||
|
let mut vm = VM::new(&mut observer);
|
||||||
|
|
||||||
|
assert!(x.nix_eq(&x, &mut vm).unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[proptest(ProptestConfig { cases: 5, ..Default::default() })]
|
#[proptest(ProptestConfig { cases: 5, ..Default::default() })]
|
||||||
fn symmetric(x: NixAttrs, y: NixAttrs) {
|
fn symmetric(x: NixAttrs, y: NixAttrs) {
|
||||||
assert_eq!(x.nix_eq(&y).unwrap(), y.nix_eq(&x).unwrap())
|
let mut observer = NoOpObserver {};
|
||||||
|
let mut vm = VM::new(&mut observer);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
x.nix_eq(&y, &mut vm).unwrap(),
|
||||||
|
y.nix_eq(&x, &mut vm).unwrap()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[proptest(ProptestConfig { cases: 5, ..Default::default() })]
|
#[proptest(ProptestConfig { cases: 5, ..Default::default() })]
|
||||||
fn transitive(x: NixAttrs, y: NixAttrs, z: NixAttrs) {
|
fn transitive(x: NixAttrs, y: NixAttrs, z: NixAttrs) {
|
||||||
if x.nix_eq(&y).unwrap() && y.nix_eq(&z).unwrap() {
|
let mut observer = NoOpObserver {};
|
||||||
assert!(x.nix_eq(&z).unwrap())
|
let mut vm = VM::new(&mut observer);
|
||||||
|
|
||||||
|
if x.nix_eq(&y, &mut vm).unwrap() && y.nix_eq(&z, &mut vm).unwrap() {
|
||||||
|
assert!(x.nix_eq(&z, &mut vm).unwrap())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
|
|
||||||
use crate::errors::ErrorKind;
|
use crate::errors::ErrorKind;
|
||||||
|
use crate::vm::VM;
|
||||||
|
|
||||||
use super::Value;
|
use super::Value;
|
||||||
|
|
||||||
|
@ -83,13 +84,13 @@ impl NixList {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Compare `self` against `other` for equality using Nix equality semantics
|
/// Compare `self` against `other` for equality using Nix equality semantics
|
||||||
pub fn nix_eq(&self, other: &Self) -> Result<bool, ErrorKind> {
|
pub fn nix_eq(&self, other: &Self, vm: &mut VM) -> Result<bool, ErrorKind> {
|
||||||
if self.len() != other.len() {
|
if self.len() != other.len() {
|
||||||
return Ok(false);
|
return Ok(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (v1, v2) in self.iter().zip(other.iter()) {
|
for (v1, v2) in self.iter().zip(other.iter()) {
|
||||||
if !v1.nix_eq(v2)? {
|
if !v1.nix_eq(v2, vm)? {
|
||||||
return Ok(false);
|
return Ok(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -254,13 +254,15 @@ impl Value {
|
||||||
gen_is!(is_number, Value::Integer(_) | Value::Float(_));
|
gen_is!(is_number, Value::Integer(_) | Value::Float(_));
|
||||||
gen_is!(is_bool, Value::Bool(_));
|
gen_is!(is_bool, Value::Bool(_));
|
||||||
|
|
||||||
/// Compare `self` against `other` for equality using Nix equality semantics
|
/// Compare `self` against `other` for equality using Nix equality semantics.
|
||||||
pub fn nix_eq(&self, other: &Self) -> Result<bool, ErrorKind> {
|
///
|
||||||
|
/// Takes a reference to the `VM` to allow forcing thunks during comparison
|
||||||
|
pub fn nix_eq(&self, other: &Self, vm: &mut VM) -> Result<bool, ErrorKind> {
|
||||||
match (self, other) {
|
match (self, other) {
|
||||||
// Trivial comparisons
|
// Trivial comparisons
|
||||||
(Value::Null, Value::Null) => Ok(true),
|
(Value::Null, Value::Null) => Ok(true),
|
||||||
(Value::Bool(b1), Value::Bool(b2)) => Ok(b1 == b2),
|
(Value::Bool(b1), Value::Bool(b2)) => Ok(b1 == b2),
|
||||||
(Value::List(l1), Value::List(l2)) => l1.nix_eq(l2),
|
(Value::List(l1), Value::List(l2)) => l1.nix_eq(l2, vm),
|
||||||
(Value::String(s1), Value::String(s2)) => Ok(s1 == s2),
|
(Value::String(s1), Value::String(s2)) => Ok(s1 == s2),
|
||||||
(Value::Path(p1), Value::Path(p2)) => Ok(p1 == p2),
|
(Value::Path(p1), Value::Path(p2)) => Ok(p1 == p2),
|
||||||
|
|
||||||
|
@ -271,7 +273,7 @@ impl Value {
|
||||||
(Value::Float(f), Value::Integer(i)) => Ok(*i as f64 == *f),
|
(Value::Float(f), Value::Integer(i)) => Ok(*i as f64 == *f),
|
||||||
|
|
||||||
// Optimised attribute set comparison
|
// Optimised attribute set comparison
|
||||||
(Value::Attrs(a1), Value::Attrs(a2)) => Ok(Rc::ptr_eq(a1, a2) || a1.nix_eq(a2)?),
|
(Value::Attrs(a1), Value::Attrs(a2)) => Ok(Rc::ptr_eq(a1, a2) || a1.nix_eq(a2, vm)?),
|
||||||
|
|
||||||
// If either value is a thunk, the inner value must be
|
// If either value is a thunk, the inner value must be
|
||||||
// compared instead. The compiler should ensure that
|
// compared instead. The compiler should ensure that
|
||||||
|
@ -340,33 +342,50 @@ mod tests {
|
||||||
fn test_name() {}
|
fn test_name() {}
|
||||||
|
|
||||||
mod nix_eq {
|
mod nix_eq {
|
||||||
|
use crate::observer::NoOpObserver;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use proptest::prelude::ProptestConfig;
|
use proptest::prelude::ProptestConfig;
|
||||||
use test_strategy::proptest;
|
use test_strategy::proptest;
|
||||||
|
|
||||||
#[proptest(ProptestConfig { cases: 5, ..Default::default() })]
|
#[proptest(ProptestConfig { cases: 5, ..Default::default() })]
|
||||||
fn reflexive(x: Value) {
|
fn reflexive(x: Value) {
|
||||||
assert!(x.nix_eq(&x).unwrap())
|
let mut observer = NoOpObserver {};
|
||||||
|
let mut vm = VM::new(&mut observer);
|
||||||
|
|
||||||
|
assert!(x.nix_eq(&x, &mut vm).unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[proptest(ProptestConfig { cases: 5, ..Default::default() })]
|
#[proptest(ProptestConfig { cases: 5, ..Default::default() })]
|
||||||
fn symmetric(x: Value, y: Value) {
|
fn symmetric(x: Value, y: Value) {
|
||||||
assert_eq!(x.nix_eq(&y).unwrap(), y.nix_eq(&x).unwrap())
|
let mut observer = NoOpObserver {};
|
||||||
|
let mut vm = VM::new(&mut observer);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
x.nix_eq(&y, &mut vm).unwrap(),
|
||||||
|
y.nix_eq(&x, &mut vm).unwrap()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[proptest(ProptestConfig { cases: 5, ..Default::default() })]
|
#[proptest(ProptestConfig { cases: 5, ..Default::default() })]
|
||||||
fn transitive(x: Value, y: Value, z: Value) {
|
fn transitive(x: Value, y: Value, z: Value) {
|
||||||
if x.nix_eq(&y).unwrap() && y.nix_eq(&z).unwrap() {
|
let mut observer = NoOpObserver {};
|
||||||
assert!(x.nix_eq(&z).unwrap())
|
let mut vm = VM::new(&mut observer);
|
||||||
|
|
||||||
|
if x.nix_eq(&y, &mut vm).unwrap() && y.nix_eq(&z, &mut vm).unwrap() {
|
||||||
|
assert!(x.nix_eq(&z, &mut vm).unwrap())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn list_int_float_fungibility() {
|
fn list_int_float_fungibility() {
|
||||||
|
let mut observer = NoOpObserver {};
|
||||||
|
let mut vm = VM::new(&mut observer);
|
||||||
|
|
||||||
let v1 = Value::List(NixList::from(vec![Value::Integer(1)]));
|
let v1 = Value::List(NixList::from(vec![Value::Integer(1)]));
|
||||||
let v2 = Value::List(NixList::from(vec![Value::Float(1.0)]));
|
let v2 = Value::List(NixList::from(vec![Value::Float(1.0)]));
|
||||||
|
|
||||||
assert!(v1.nix_eq(&v2).unwrap())
|
assert!(v1.nix_eq(&v2, &mut vm).unwrap())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -265,7 +265,7 @@ impl<'o> VM<'o> {
|
||||||
OpCode::OpEqual => {
|
OpCode::OpEqual => {
|
||||||
let v2 = self.pop();
|
let v2 = self.pop();
|
||||||
let v1 = self.pop();
|
let v1 = self.pop();
|
||||||
let res = fallible!(self, v1.nix_eq(&v2));
|
let res = fallible!(self, v1.nix_eq(&v2, self));
|
||||||
|
|
||||||
self.push(Value::Bool(res))
|
self.push(Value::Bool(res))
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue