fix(tvix/eval): fix b/281 by adding Value::Catchable

This commit makes catchable errors a variant of Value.

The main downside of this approach is that we lose the ability to
use Rust's `?` syntax for propagating catchable errors.

Change-Id: Ibe89438d8a70dcec29e016df692b5bf88a5cad13
Reviewed-on: https://cl.tvl.fyi/c/depot/+/9289
Reviewed-by: tazjin <tazjin@tvl.su>
Autosubmit: Adam Joseph <adam@westernsemico.com>
Tested-by: BuildkiteCI
This commit is contained in:
Adam Joseph 2023-09-09 22:02:56 -07:00 committed by clbot
parent 926459ce69
commit 05f42519b5
16 changed files with 320 additions and 247 deletions

View file

@ -20,7 +20,7 @@ mod path;
mod string;
mod thunk;
use crate::errors::ErrorKind;
use crate::errors::{CatchableErrorKind, ErrorKind};
use crate::opcode::StackIdx;
use crate::spans::LightSpan;
use crate::vm::generators::{self, GenCo};
@ -81,6 +81,24 @@ pub enum Value {
#[serde(skip)]
FinaliseRequest(bool),
#[serde(skip)]
Catchable(CatchableErrorKind),
}
impl From<CatchableErrorKind> for Value {
fn from(c: CatchableErrorKind) -> Value {
Value::Catchable(c)
}
}
impl<V> From<Result<V, CatchableErrorKind>> for Value
where
Value: From<V>,
{
fn from(v: Result<V, CatchableErrorKind>) -> Value {
v.map_or_else(|cek| Value::Catchable(cek), |v| v.into())
}
}
lazy_static! {
@ -222,18 +240,28 @@ impl Value {
Value::List(list) => {
for val in list {
generators::request_deep_force(&co, val.clone(), thunk_set.clone()).await;
if let c @ Value::Catchable(_) =
generators::request_deep_force(&co, val.clone(), thunk_set.clone()).await
{
return Ok(c);
}
}
}
Value::Attrs(attrs) => {
for (_, val) in attrs.iter() {
generators::request_deep_force(&co, val.clone(), thunk_set.clone()).await;
if let c @ Value::Catchable(_) =
generators::request_deep_force(&co, val.clone(), thunk_set.clone()).await
{
return Ok(c);
}
}
}
Value::Thunk(_) => panic!("Tvix bug: force_value() returned a thunk"),
Value::Catchable(_) => return Ok(value),
Value::AttrNotFound
| Value::Blueprint(_)
| Value::DeferredUpvalue(_)
@ -279,8 +307,12 @@ impl Value {
}
if let Some(out_path) = attrs.select("outPath") {
let s = generators::request_string_coerce(&co, out_path.clone(), kind).await;
return Ok(Value::String(s));
return match generators::request_string_coerce(&co, out_path.clone(), kind)
.await
{
Ok(s) => Ok(Value::String(s)),
Err(c) => Ok(Value::Catchable(c)),
};
}
Err(ErrorKind::NotCoercibleToString { from: "set", kind })
@ -308,8 +340,10 @@ impl Value {
out.push(' ');
}
let s = generators::request_string_coerce(&co, elem, kind).await;
out.push_str(s.as_str());
match generators::request_string_coerce(&co, elem, kind).await {
Ok(s) => out.push_str(s.as_str()),
Err(c) => return Ok(Value::Catchable(c)),
}
}
Ok(Value::String(out.into()))
@ -328,6 +362,8 @@ impl Value {
kind,
}),
(c @ Value::Catchable(_), _) => return Ok(c),
(Value::AttrNotFound, _)
| (Value::Blueprint(_), _)
| (Value::DeferredUpvalue(_), _)
@ -384,6 +420,8 @@ impl Value {
let result = match (a, b) {
// Trivial comparisons
(c @ Value::Catchable(_), _) => return Ok(c),
(_, c @ Value::Catchable(_)) => return Ok(c),
(Value::Null, Value::Null) => true,
(Value::Bool(b1), Value::Bool(b2)) => b1 == b2,
(Value::String(s1), Value::String(s2)) => s1 == s2,
@ -526,6 +564,7 @@ impl Value {
Value::UnresolvedPath(_) => "internal[unresolved_path]",
Value::Json(_) => "internal[json]",
Value::FinaliseRequest(_) => "internal[finaliser_sentinel]",
Value::Catchable(_) => "internal[catchable]",
}
}
@ -533,6 +572,7 @@ impl Value {
gen_cast!(as_int, i64, "int", Value::Integer(x), *x);
gen_cast!(as_float, f64, "float", Value::Float(x), *x);
gen_cast!(to_str, NixString, "string", Value::String(s), s.clone());
gen_cast!(to_path, Box<PathBuf>, "path", Value::Path(p), p.clone());
gen_cast!(to_attrs, Box<NixAttrs>, "set", Value::Attrs(a), a.clone());
gen_cast!(to_list, NixList, "list", Value::List(l), l.clone());
gen_cast!(
@ -660,6 +700,8 @@ impl Value {
// TODO: handle suspended thunks with a different explanation instead of panicking
Value::Thunk(t) => t.value().explain(),
Value::Catchable(_) => "a catchable failure".into(),
Value::AttrNotFound
| Value::Blueprint(_)
| Value::DeferredUpvalue(_)
@ -785,6 +827,7 @@ impl TotalDisplay for Value {
// Delegate thunk display to the type, as it must handle
// the case of already evaluated or cyclic thunks.
Value::Thunk(t) => t.total_fmt(f, set),
Value::Catchable(_) => panic!("total_fmt() called on a CatchableErrorKind"),
}
}
}