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…
	
	Add table
		Add a link
		
	
		Reference in a new issue