about summary refs log tree commit diff
path: root/tvix/eval/src/value/mod.rs
diff options
context:
space:
mode:
Diffstat (limited to 'tvix/eval/src/value/mod.rs')
-rw-r--r--tvix/eval/src/value/mod.rs118
1 files changed, 61 insertions, 57 deletions
diff --git a/tvix/eval/src/value/mod.rs b/tvix/eval/src/value/mod.rs
index c171c9a04eb8..1775c6f71adb 100644
--- a/tvix/eval/src/value/mod.rs
+++ b/tvix/eval/src/value/mod.rs
@@ -5,8 +5,10 @@ use std::fmt::Display;
 use std::num::{NonZeroI32, NonZeroUsize};
 use std::path::PathBuf;
 use std::rc::Rc;
+use std::sync::LazyLock;
 
 use bstr::{BString, ByteVec};
+use codemap::Span;
 use lexical_core::format::CXX_LITERAL;
 use serde::Deserialize;
 
@@ -23,7 +25,6 @@ mod thunk;
 
 use crate::errors::{CatchableErrorKind, ErrorKind};
 use crate::opcode::StackIdx;
-use crate::spans::LightSpan;
 use crate::vm::generators::{self, GenCo};
 use crate::AddContext;
 pub use attrs::NixAttrs;
@@ -37,8 +38,6 @@ pub use thunk::Thunk;
 
 pub use self::thunk::ThunkSet;
 
-use lazy_static::lazy_static;
-
 #[warn(variant_size_differences)]
 #[derive(Clone, Debug, Deserialize)]
 #[serde(untagged)]
@@ -77,14 +76,11 @@ pub enum Value {
     DeferredUpvalue(StackIdx),
     #[serde(skip)]
     UnresolvedPath(Box<PathBuf>),
-    #[serde(skip)]
-    Json(Box<(serde_json::Value, NixContext)>),
 
     #[serde(skip)]
     FinaliseRequest(bool),
 
     #[serde(skip)]
-    // TODO(tazjin): why is this in a Box?
     Catchable(Box<CatchableErrorKind>),
 }
 
@@ -108,16 +104,15 @@ where
     }
 }
 
-lazy_static! {
-    static ref WRITE_FLOAT_OPTIONS: lexical_core::WriteFloatOptions =
-        lexical_core::WriteFloatOptionsBuilder::new()
-            .trim_floats(true)
-            .round_mode(lexical_core::write_float_options::RoundMode::Round)
-            .positive_exponent_break(Some(NonZeroI32::new(5).unwrap()))
-            .max_significant_digits(Some(NonZeroUsize::new(6).unwrap()))
-            .build()
-            .unwrap();
-}
+static WRITE_FLOAT_OPTIONS: LazyLock<lexical_core::WriteFloatOptions> = LazyLock::new(|| {
+    lexical_core::WriteFloatOptionsBuilder::new()
+        .trim_floats(true)
+        .round_mode(lexical_core::write_float_options::RoundMode::Round)
+        .positive_exponent_break(Some(NonZeroI32::new(5).unwrap()))
+        .max_significant_digits(Some(NonZeroUsize::new(6).unwrap()))
+        .build()
+        .unwrap()
+});
 
 // Helper macros to generate the to_*/as_* macros while accounting for
 // thunks.
@@ -187,6 +182,21 @@ pub struct CoercionKind {
     pub import_paths: bool,
 }
 
+impl From<CoercionKind> for u8 {
+    fn from(k: CoercionKind) -> u8 {
+        k.strong as u8 | (k.import_paths as u8) << 1
+    }
+}
+
+impl From<u8> for CoercionKind {
+    fn from(byte: u8) -> Self {
+        CoercionKind {
+            strong: byte & 0x01 != 0,
+            import_paths: byte & 0x02 != 0,
+        }
+    }
+}
+
 impl<T> From<T> for Value
 where
     T: Into<NixString>,
@@ -196,14 +206,6 @@ where
     }
 }
 
-/// Constructors
-impl Value {
-    /// Construct a [`Value::Attrs`] from a [`NixAttrs`].
-    pub fn attrs(attrs: NixAttrs) -> Self {
-        Self::Attrs(Box::new(attrs))
-    }
-}
-
 /// Controls what kind of by-pointer equality comparison is allowed.
 ///
 /// See `//tvix/docs/value-pointer-equality.md` for details.
@@ -220,11 +222,16 @@ pub enum PointerEquality {
 }
 
 impl Value {
+    /// Construct a [`Value::Attrs`] from a [`NixAttrs`].
+    pub fn attrs(attrs: NixAttrs) -> Self {
+        Self::Attrs(Box::new(attrs))
+    }
+
     /// Deeply forces a value, traversing e.g. lists and attribute sets and forcing
     /// their contents, too.
     ///
     /// This is a generator function.
-    pub(super) async fn deep_force(self, co: GenCo, span: LightSpan) -> Result<Value, ErrorKind> {
+    pub(super) async fn deep_force(self, co: GenCo, span: Span) -> Result<Value, ErrorKind> {
         if let Some(v) = Self::deep_force_(self.clone(), co, span).await? {
             Ok(v)
         } else {
@@ -233,11 +240,7 @@ impl Value {
     }
 
     /// Returns Some(v) or None to indicate the returned value is myself
-    async fn deep_force_(
-        myself: Value,
-        co: GenCo,
-        span: LightSpan,
-    ) -> Result<Option<Value>, ErrorKind> {
+    async fn deep_force_(myself: Value, co: GenCo, span: Span) -> Result<Option<Value>, ErrorKind> {
         // This is a stack of values which still remain to be forced.
         let mut vals = vec![myself];
 
@@ -256,7 +259,7 @@ impl Value {
                 if !thunk_set.insert(t) {
                     continue;
                 }
-                Thunk::force_(t.clone(), &co, span.clone()).await?
+                Thunk::force_(t.clone(), &co, span).await?
             } else {
                 v
             };
@@ -294,7 +297,6 @@ impl Value {
                 | Value::Blueprint(_)
                 | Value::DeferredUpvalue(_)
                 | Value::UnresolvedPath(_)
-                | Value::Json(..)
                 | Value::FinaliseRequest(_) => panic!(
                     "Tvix bug: internal value left on stack: {}",
                     value.type_of()
@@ -307,7 +309,7 @@ impl Value {
         self,
         co: GenCo,
         kind: CoercionKind,
-        span: LightSpan,
+        span: Span,
     ) -> Result<Value, ErrorKind> {
         self.coerce_to_string_(&co, kind, span).await
     }
@@ -318,7 +320,7 @@ impl Value {
         self,
         co: &GenCo,
         kind: CoercionKind,
-        span: LightSpan,
+        span: Span,
     ) -> Result<Value, ErrorKind> {
         let mut result = BString::default();
         let mut vals = vec![self];
@@ -331,15 +333,15 @@ impl Value {
 
         loop {
             let value = if let Some(v) = vals.pop() {
-                v.force(co, span.clone()).await?
+                v.force(co, span).await?
             } else {
                 return Ok(Value::String(NixString::new_context_from(context, result)));
             };
             let coerced: Result<BString, _> = match (value, kind) {
                 // coercions that are always done
                 (Value::String(mut s), _) => {
-                    if let Some(ctx) = s.context_mut() {
-                        context = context.join(ctx);
+                    if let Some(ctx) = s.take_context() {
+                        context.extend(ctx.into_iter());
                     }
                     Ok((*s).into())
                 }
@@ -377,7 +379,7 @@ impl Value {
                 // `__toString` is preferred.
                 (Value::Attrs(attrs), kind) => {
                     if let Some(to_string) = attrs.select("__toString") {
-                        let callable = to_string.clone().force(co, span.clone()).await?;
+                        let callable = to_string.clone().force(co, span).await?;
 
                         // Leave the attribute set on the stack as an argument
                         // to the function call.
@@ -444,7 +446,6 @@ impl Value {
                 | (Value::Blueprint(_), _)
                 | (Value::DeferredUpvalue(_), _)
                 | (Value::UnresolvedPath(_), _)
-                | (Value::Json(..), _)
                 | (Value::FinaliseRequest(_), _) => {
                     panic!("tvix bug: .coerce_to_string() called on internal value")
                 }
@@ -467,7 +468,7 @@ impl Value {
         other: Value,
         co: GenCo,
         ptr_eq: PointerEquality,
-        span: LightSpan,
+        span: Span,
     ) -> Result<Value, ErrorKind> {
         self.nix_eq(other, &co, ptr_eq, span).await
     }
@@ -487,7 +488,7 @@ impl Value {
         other: Value,
         co: &GenCo,
         ptr_eq: PointerEquality,
-        span: LightSpan,
+        span: Span,
     ) -> Result<Value, ErrorKind> {
         // this is a stack of ((v1,v2),peq) triples to be compared;
         // after each triple is popped off of the stack, v1 is
@@ -513,13 +514,13 @@ impl Value {
                         }
                     };
 
-                    Thunk::force_(thunk, co, span.clone()).await?
+                    Thunk::force_(thunk, co, span).await?
                 }
 
                 _ => a,
             };
 
-            let b = b.force(co, span.clone()).await?;
+            let b = b.force(co, span).await?;
 
             debug_assert!(!matches!(a, Value::Thunk(_)));
             debug_assert!(!matches!(b, Value::Thunk(_)));
@@ -568,11 +569,11 @@ impl Value {
                     #[allow(clippy::single_match)] // might need more match arms later
                     match (a1.select("type"), a2.select("type")) {
                         (Some(v1), Some(v2)) => {
-                            let s1 = v1.clone().force(co, span.clone()).await?;
+                            let s1 = v1.clone().force(co, span).await?;
                             if s1.is_catchable() {
                                 return Ok(s1);
                             }
-                            let s2 = v2.clone().force(co, span.clone()).await?;
+                            let s2 = v2.clone().force(co, span).await?;
                             if s2.is_catchable() {
                                 return Ok(s2);
                             }
@@ -593,8 +594,8 @@ impl Value {
                                         .context("comparing derivations")?
                                         .clone();
 
-                                    let out1 = out1.clone().force(co, span.clone()).await?;
-                                    let out2 = out2.clone().force(co, span.clone()).await?;
+                                    let out1 = out1.clone().force(co, span).await?;
+                                    let out2 = out2.clone().force(co, span).await?;
 
                                     if out1.is_catchable() {
                                         return Ok(out1);
@@ -681,7 +682,6 @@ impl Value {
             Value::Blueprint(_) => "internal[blueprint]",
             Value::DeferredUpvalue(_) => "internal[deferred_upvalue]",
             Value::UnresolvedPath(_) => "internal[unresolved_path]",
-            Value::Json(..) => "internal[json]",
             Value::FinaliseRequest(_) => "internal[finaliser_sentinel]",
             Value::Catchable(_) => "internal[catchable]",
         }
@@ -745,7 +745,7 @@ impl Value {
         self,
         other: Self,
         co: GenCo,
-        span: LightSpan,
+        span: Span,
     ) -> Result<Result<Ordering, CatchableErrorKind>, ErrorKind> {
         Self::nix_cmp_ordering_(self, other, co, span).await
     }
@@ -754,7 +754,7 @@ impl Value {
         myself: Self,
         other: Self,
         co: GenCo,
-        span: LightSpan,
+        span: Span,
     ) -> Result<Result<Ordering, CatchableErrorKind>, ErrorKind> {
         // this is a stack of ((v1,v2),peq) triples to be compared;
         // after each triple is popped off of the stack, v1 is
@@ -770,14 +770,14 @@ impl Value {
             };
             if ptr_eq == PointerEquality::AllowAll {
                 if a.clone()
-                    .nix_eq(b.clone(), &co, PointerEquality::AllowAll, span.clone())
+                    .nix_eq(b.clone(), &co, PointerEquality::AllowAll, span)
                     .await?
                     .as_bool()?
                 {
                     continue;
                 }
-                a = a.force(&co, span.clone()).await?;
-                b = b.force(&co, span.clone()).await?;
+                a = a.force(&co, span).await?;
+                b = b.force(&co, span).await?;
             }
             let result = match (a, b) {
                 (Value::Catchable(c), _) => return Ok(Err(*c)),
@@ -820,7 +820,7 @@ impl Value {
     }
 
     // TODO(amjoseph): de-asyncify this (when called directly by the VM)
-    pub async fn force(self, co: &GenCo, span: LightSpan) -> Result<Value, ErrorKind> {
+    pub async fn force(self, co: &GenCo, span: Span) -> Result<Value, ErrorKind> {
         if let Value::Thunk(thunk) = self {
             // TODO(amjoseph): use #[tailcall::mutual]
             return Thunk::force_(thunk, co, span).await;
@@ -829,7 +829,7 @@ impl Value {
     }
 
     // need two flavors, because async
-    pub async fn force_owned_genco(self, co: GenCo, span: LightSpan) -> Result<Value, ErrorKind> {
+    pub async fn force_owned_genco(self, co: GenCo, span: Span) -> Result<Value, ErrorKind> {
         if let Value::Thunk(thunk) = self {
             // TODO(amjoseph): use #[tailcall::mutual]
             return Thunk::force_(thunk, &co, span).await;
@@ -877,10 +877,15 @@ impl Value {
             | Value::Blueprint(_)
             | Value::DeferredUpvalue(_)
             | Value::UnresolvedPath(_)
-            | Value::Json(..)
             | Value::FinaliseRequest(_) => "an internal Tvix evaluator value".into(),
         }
     }
+
+    /// Constructs a thunk that will be evaluated lazily at runtime. This lets
+    /// users of Tvix implement their own lazy builtins and so on.
+    pub fn suspended_native_thunk(native: Box<dyn Fn() -> Result<Value, ErrorKind>>) -> Self {
+        Value::Thunk(Thunk::new_suspended_native(native))
+    }
 }
 
 trait TotalDisplay {
@@ -991,7 +996,6 @@ 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]"),
             Value::FinaliseRequest(_) => f.write_str("internal[finaliser_sentinel]"),
 
             // Delegate thunk display to the type, as it must handle