fix(tvix/eval): never use partial_cmp() (partial fix b/338)
This is part of a fix for b/338. We should never use PartialOrd::partial_cmp(). All Nix types except floats are obviously totally-ordered. In addition, it turns out that because Nix treats division by zero rather than producing a NaN, and because it does not support "negative zero", even floats are in fact totally ordered in Nix. Therefore, every call to PartialOrd::partial_cmp() in tvix is an error. We have to *implement* this function, but we should never call it on built-in types. Moreover, nix_cmp_ordering() currently returns an Option<Ordering>. I'm not sure what was going on there, since it's impossible for it to return None. This commit fixes it to return simply Ordering rather than Option<Ordering>. Change-Id: If5c084164cf19cfb38c5a15554c0422faa5f895d Reviewed-on: https://cl.tvl.fyi/c/depot/+/10218 Autosubmit: Adam Joseph <adam@westernsemico.com> Reviewed-by: tazjin <tazjin@tvl.su> Tested-by: BuildkiteCI
This commit is contained in:
parent
19d13eb070
commit
8a40f75c2d
3 changed files with 16 additions and 23 deletions
|
@ -612,7 +612,7 @@ mod pure_builtins {
|
||||||
async fn builtin_less_than(co: GenCo, x: Value, y: Value) -> Result<Value, ErrorKind> {
|
async fn builtin_less_than(co: GenCo, x: Value, y: Value) -> Result<Value, ErrorKind> {
|
||||||
Ok(Value::Bool(matches!(
|
Ok(Value::Bool(matches!(
|
||||||
x.nix_cmp_ordering(y, co).await?,
|
x.nix_cmp_ordering(y, co).await?,
|
||||||
Some(Ordering::Less)
|
Ordering::Less
|
||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -600,18 +600,9 @@ impl Value {
|
||||||
// TODO(amjoseph): de-asyncify this (when called directly by the VM)
|
// TODO(amjoseph): de-asyncify this (when called directly by the VM)
|
||||||
/// Compare `self` against other using (fallible) Nix ordering semantics.
|
/// Compare `self` against other using (fallible) Nix ordering semantics.
|
||||||
///
|
///
|
||||||
/// Note that as this returns an `Option<Ordering>` it can not directly be
|
|
||||||
/// used as a generator function in the VM. The exact use depends on the
|
|
||||||
/// callsite, as the meaning is interpreted in different ways e.g. based on
|
|
||||||
/// the comparison operator used.
|
|
||||||
///
|
|
||||||
/// The function is intended to be used from within other generator
|
/// The function is intended to be used from within other generator
|
||||||
/// functions or `gen!` blocks.
|
/// functions or `gen!` blocks.
|
||||||
pub async fn nix_cmp_ordering(
|
pub async fn nix_cmp_ordering(self, other: Self, co: GenCo) -> Result<Ordering, ErrorKind> {
|
||||||
self,
|
|
||||||
other: Self,
|
|
||||||
co: GenCo,
|
|
||||||
) -> Result<Option<Ordering>, ErrorKind> {
|
|
||||||
Self::nix_cmp_ordering_(self, other, co).await
|
Self::nix_cmp_ordering_(self, other, co).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -619,19 +610,21 @@ impl Value {
|
||||||
mut myself: Self,
|
mut myself: Self,
|
||||||
mut other: Self,
|
mut other: Self,
|
||||||
co: GenCo,
|
co: GenCo,
|
||||||
) -> Result<Option<Ordering>, ErrorKind> {
|
) -> Result<Ordering, ErrorKind> {
|
||||||
'outer: loop {
|
'outer: loop {
|
||||||
match (myself, other) {
|
match (myself, other) {
|
||||||
// same types
|
// same types
|
||||||
(Value::Integer(i1), Value::Integer(i2)) => return Ok(i1.partial_cmp(&i2)),
|
// Nix does not support NaN or "negative infinity",
|
||||||
(Value::Float(f1), Value::Float(f2)) => return Ok(f1.partial_cmp(&f2)),
|
// so its floats are in fact totally ordered.
|
||||||
(Value::String(s1), Value::String(s2)) => return Ok(s1.partial_cmp(&s2)),
|
(Value::Integer(i1), Value::Integer(i2)) => return Ok(i1.cmp(&i2)),
|
||||||
|
(Value::Float(f1), Value::Float(f2)) => return Ok(f1.total_cmp(&f2)),
|
||||||
|
(Value::String(s1), Value::String(s2)) => return Ok(s1.cmp(&s2)),
|
||||||
(Value::List(l1), Value::List(l2)) => {
|
(Value::List(l1), Value::List(l2)) => {
|
||||||
for i in 0.. {
|
for i in 0.. {
|
||||||
if i == l2.len() {
|
if i == l2.len() {
|
||||||
return Ok(Some(Ordering::Greater));
|
return Ok(Ordering::Greater);
|
||||||
} else if i == l1.len() {
|
} else if i == l1.len() {
|
||||||
return Ok(Some(Ordering::Less));
|
return Ok(Ordering::Less);
|
||||||
} else if !generators::check_equality(
|
} else if !generators::check_equality(
|
||||||
&co,
|
&co,
|
||||||
l1[i].clone(),
|
l1[i].clone(),
|
||||||
|
@ -651,8 +644,8 @@ impl Value {
|
||||||
}
|
}
|
||||||
|
|
||||||
// different types
|
// different types
|
||||||
(Value::Integer(i1), Value::Float(f2)) => return Ok((i1 as f64).partial_cmp(&f2)),
|
(Value::Integer(i1), Value::Float(f2)) => return Ok((i1 as f64).total_cmp(&f2)),
|
||||||
(Value::Float(f1), Value::Integer(i2)) => return Ok(f1.partial_cmp(&(i2 as f64))),
|
(Value::Float(f1), Value::Integer(i2)) => return Ok(f1.total_cmp(&(i2 as f64))),
|
||||||
|
|
||||||
// unsupported types
|
// unsupported types
|
||||||
(lhs, rhs) => {
|
(lhs, rhs) => {
|
||||||
|
|
|
@ -53,18 +53,18 @@ macro_rules! cmp_op {
|
||||||
}};
|
}};
|
||||||
|
|
||||||
(@order < $ordering:expr) => {
|
(@order < $ordering:expr) => {
|
||||||
$ordering == Some(Ordering::Less)
|
$ordering == Ordering::Less
|
||||||
};
|
};
|
||||||
|
|
||||||
(@order > $ordering:expr) => {
|
(@order > $ordering:expr) => {
|
||||||
$ordering == Some(Ordering::Greater)
|
$ordering == Ordering::Greater
|
||||||
};
|
};
|
||||||
|
|
||||||
(@order <= $ordering:expr) => {
|
(@order <= $ordering:expr) => {
|
||||||
!matches!($ordering, None | Some(Ordering::Greater))
|
matches!($ordering, Ordering::Equal | Ordering::Less)
|
||||||
};
|
};
|
||||||
|
|
||||||
(@order >= $ordering:expr) => {
|
(@order >= $ordering:expr) => {
|
||||||
!matches!($ordering, None | Some(Ordering::Less))
|
matches!($ordering, Ordering::Equal | Ordering::Greater)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue