fix(tvix/eval): implement cppnix JSON-serialisation semantics

This drops the usage of serde::Serialize, as the trait can not be used
to implement the correct semantics (function colouring!).

Instead, a manual JSON serialisation function is written which
correctly handles toString, outPath and other similar weirdnesses.

Unexpectedly, the eval-okay-tojson test from the C++ Nix test suite
now passes, too.

This fixes an issue where serialising data structures containing
derivations to JSON would fail.

Change-Id: I5c39e3d8356ee93a07eda481410f88610f6dd9f8
Reviewed-on: https://cl.tvl.fyi/c/depot/+/8209
Reviewed-by: raitobezarius <tvl@lahfa.xyz>
Tested-by: BuildkiteCI
This commit is contained in:
Vincent Ambo 2023-03-04 02:12:06 +03:00 committed by tazjin
parent 1e37f8b52e
commit 939cebd0f1
12 changed files with 138 additions and 70 deletions

View file

@ -9,13 +9,14 @@ use std::pin::Pin;
use std::rc::Rc;
use lexical_core::format::CXX_LITERAL;
use serde::{Deserialize, Serialize};
use serde::Deserialize;
#[cfg(feature = "arbitrary")]
mod arbitrary;
mod attrs;
mod builtin;
mod function;
mod json;
mod list;
mod path;
mod string;
@ -39,7 +40,7 @@ pub use self::thunk::{SharedThunkSet, ThunkSet};
use lazy_static::lazy_static;
#[warn(variant_size_differences)]
#[derive(Clone, Debug, Serialize, Deserialize)]
#[derive(Clone, Debug, Deserialize)]
#[serde(untagged)]
pub enum Value {
Null,
@ -76,6 +77,8 @@ pub enum Value {
DeferredUpvalue(StackIdx),
#[serde(skip)]
UnresolvedPath(Box<PathBuf>),
#[serde(skip)]
Json(serde_json::Value),
}
lazy_static! {
@ -231,7 +234,8 @@ impl Value {
Value::AttrNotFound
| Value::Blueprint(_)
| Value::DeferredUpvalue(_)
| Value::UnresolvedPath(_) => panic!(
| Value::UnresolvedPath(_)
| Value::Json(_) => panic!(
"Tvix bug: internal value left on stack: {}",
value.type_of()
),
@ -322,7 +326,8 @@ impl Value {
(Value::AttrNotFound, _)
| (Value::Blueprint(_), _)
| (Value::DeferredUpvalue(_), _)
| (Value::UnresolvedPath(_), _) => {
| (Value::UnresolvedPath(_), _)
| (Value::Json(_), _) => {
panic!("tvix bug: .coerce_to_string() called on internal value")
}
}
@ -512,6 +517,7 @@ impl Value {
Value::Blueprint(_) => "internal[blueprint]",
Value::DeferredUpvalue(_) => "internal[deferred_upvalue]",
Value::UnresolvedPath(_) => "internal[unresolved_path]",
Value::Json(_) => "internal[json]",
}
}
@ -643,7 +649,8 @@ impl Value {
Value::AttrNotFound
| Value::Blueprint(_)
| Value::DeferredUpvalue(_)
| Value::UnresolvedPath(_) => "an internal Tvix evaluator value".into(),
| Value::UnresolvedPath(_)
| Value::Json(_) => "an internal Tvix evaluator value".into(),
}
}
}
@ -756,6 +763,7 @@ impl TotalDisplay for Value {
Value::Blueprint(_) => f.write_str("internal[blueprint]"),
Value::DeferredUpvalue(_) => f.write_str("internal[deferred_upvalue]"),
Value::UnresolvedPath(_) => f.write_str("internal[unresolved_path]"),
Value::Json(_) => f.write_str("internal[json]"),
// Delegate thunk display to the type, as it must handle
// the case of already evaluated or cyclic thunks.