diff options
Diffstat (limited to 'tvix/eval/src/value/mod.rs')
-rw-r--r-- | tvix/eval/src/value/mod.rs | 118 |
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 |