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:
parent
926459ce69
commit
05f42519b5
16 changed files with 320 additions and 247 deletions
|
|
@ -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"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue