git.delta.rocks / jrsonnet / refs/commits / 23d571a0df03

difftreelog

refactor move arrays to use dyn ArrayLike

Yaroslav Bolyukin2023-08-06parent: #09dae32.patch.diff
in: master

8 files changed

modifiedcrates/jrsonnet-evaluator/src/arr/mod.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/arr/mod.rs
+++ b/crates/jrsonnet-evaluator/src/arr/mod.rs
@@ -1,44 +1,20 @@
-use std::rc::Rc;
+use std::any::Any;
 
 use jrsonnet_gcmodule::{Cc, Trace};
 use jrsonnet_interner::IBytes;
 use jrsonnet_parser::LocExpr;
 
-use crate::{function::FuncVal, Context, Result, Thunk, Val};
+use crate::{function::FuncVal, gc::TraceBox, tb, Context, Result, Thunk, Val};
 
 mod spec;
-use spec::*;
+pub use spec::ArrayLike;
+pub(crate) use spec::*;
 
 /// Represents a Jsonnet array value.
 #[derive(Debug, Clone, Trace)]
 // may contrain other ArrValue
 #[trace(tracking(force))]
-pub enum ArrValue {
-	/// Layout optimized byte array.
-	Bytes(BytesArray),
-	/// Layout optimized char array.
-	Chars(CharArray),
-	/// Every element is lazy evaluated.
-	Lazy(LazyArray),
-	/// Every element is defined somewhere in source code
-	Expr(ExprArray),
-	/// Every field is already evaluated.
-	Eager(EagerArray),
-	/// Concatenation of two arrays of any kind.
-	Extended(Cc<ExtendedArray>),
-	/// Represents a integer array in form `[start, start + 1, ... end - 1, end]`.
-	/// This kind of arrays is generated by `std.range(start, end)` call, and used for loops.
-	Range(RangeArray),
-	/// Sliced array view.
-	Slice(Cc<SliceArray>),
-	/// Reversed array view.
-	/// Returned by `std.reverse(other)` call
-	Reverse(Cc<ReverseArray>),
-	/// Returned by `std.map` call
-	Mapped(MappedArray),
-	/// Returned by `std.repeat` call
-	Repeated(RepeatedArray),
-}
+pub struct ArrValue(Cc<TraceBox<dyn ArrayLike>>);
 
 pub trait ArrayLikeIter<T>: Iterator<Item = T> + DoubleEndedIterator + ExactSizeIterator {}
 impl<I, T> ArrayLikeIter<T> for I where
@@ -47,36 +23,39 @@
 }
 
 impl ArrValue {
+	pub fn new(v: impl ArrayLike) -> Self {
+		Self(Cc::new(tb!(v)))
+	}
 	pub fn empty() -> Self {
-		Self::Range(RangeArray::empty())
+		Self::new(RangeArray::empty())
 	}
 
 	pub fn expr(ctx: Context, exprs: impl IntoIterator<Item = LocExpr>) -> Self {
-		Self::Expr(ExprArray::new(ctx, exprs))
+		Self::new(ExprArray::new(ctx, exprs))
 	}
 
-	pub fn lazy(thunks: Cc<Vec<Thunk<Val>>>) -> Self {
-		Self::Lazy(LazyArray(thunks))
+	pub fn lazy(thunks: Vec<Thunk<Val>>) -> Self {
+		Self::new(LazyArray(thunks))
 	}
 
 	pub fn eager(values: Vec<Val>) -> Self {
-		Self::Eager(EagerArray(Cc::new(values)))
+		Self::new(EagerArray(values))
 	}
 
 	pub fn repeated(data: ArrValue, repeats: usize) -> Option<Self> {
-		Some(Self::Repeated(RepeatedArray::new(data, repeats)?))
+		Some(Self::new(RepeatedArray::new(data, repeats)?))
 	}
 
 	pub fn bytes(bytes: IBytes) -> Self {
-		Self::Bytes(BytesArray(bytes))
+		Self::new(BytesArray(bytes))
 	}
 	pub fn chars(chars: impl Iterator<Item = char>) -> Self {
-		Self::Chars(CharArray(Rc::new(chars.collect())))
+		Self::new(CharArray(chars.collect()))
 	}
 
 	#[must_use]
 	pub fn map(self, mapper: FuncVal) -> Self {
-		Self::Mapped(MappedArray::new(self, mapper))
+		Self::new(MappedArray::new(self, mapper))
 	}
 
 	pub fn filter(self, filter: impl Fn(&Val) -> Result<bool>) -> Result<Self> {
@@ -100,7 +79,7 @@
 		} else if b.is_empty() {
 			a
 		} else if a.len() + b.len() > ARR_EXTEND_THRESHOLD {
-			Self::Extended(Cc::new(ExtendedArray::new(a, b)))
+			Self::new(ExtendedArray::new(a, b))
 		} else if let (Some(a), Some(b)) = (a.iter_cheap(), b.iter_cheap()) {
 			let mut out = Vec::with_capacity(a.len() + b.len());
 			out.extend(a);
@@ -110,15 +89,15 @@
 			let mut out = Vec::with_capacity(a.len() + b.len());
 			out.extend(a.iter_lazy());
 			out.extend(b.iter_lazy());
-			Self::lazy(Cc::new(out))
+			Self::lazy(out)
 		}
 	}
 
 	pub fn range_exclusive(a: i32, b: i32) -> Self {
-		Self::Range(RangeArray::new_exclusive(a, b))
+		Self::new(RangeArray::new_exclusive(a, b))
 	}
 	pub fn range_inclusive(a: i32, b: i32) -> Self {
-		Self::Range(RangeArray::new_inclusive(a, b))
+		Self::new(RangeArray::new_inclusive(a, b))
 	}
 
 	#[must_use]
@@ -136,53 +115,42 @@
 		if from >= to || step == 0 {
 			return None;
 		}
-		// match self {
-		// 	ArrValue::Slice(slice) => {
-		// 		return Some(Self::Slice(Cc::new(SliceArray {
-		// 			inner: slice.inner.clone(),
-		// 			from: slice.from + slice.step * (from as u32),
-		// 			to: slice.from + (to as u32) * slice.step,
-		// 			step: slice.step * step as u32,
-		// 		})))
-		// 	}
-		// 	_ => {}
-		// }
 
-		Some(Self::Slice(Cc::new(SliceArray {
+		Some(Self::new(SliceArray {
 			inner: self,
 			from: from as u32,
 			to: to as u32,
 			step: step as u32,
-		})))
+		}))
 	}
 
 	/// Array length.
 	pub fn len(&self) -> usize {
-		pass!(self.len())
+		self.0.len()
 	}
 
 	/// Is array contains no elements?
 	pub fn is_empty(&self) -> bool {
-		pass!(self.is_empty())
+		self.0.is_empty()
 	}
 
 	/// Get array element by index, evaluating it, if it is lazy.
 	///
 	/// Returns `None` on out-of-bounds condition.
 	pub fn get(&self, index: usize) -> Result<Option<Val>> {
-		pass!(self.get(index))
+		self.0.get(index)
 	}
 
 	/// Returns None if get is either non cheap, or out of bounds
 	fn get_cheap(&self, index: usize) -> Option<Val> {
-		pass!(self.get_cheap(index))
+		self.0.get_cheap(index)
 	}
 
 	/// Get array element by index, without evaluation.
 	///
 	/// Returns `None` on out-of-bounds condition.
 	pub fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {
-		pass!(self.get_lazy(index))
+		self.0.get_lazy(index)
 	}
 
 	pub fn iter(&self) -> impl ArrayLikeIter<Result<Val>> + '_ {
@@ -205,33 +173,20 @@
 	/// Return a reversed view on current array.
 	#[must_use]
 	pub fn reversed(self) -> Self {
-		Self::Reverse(Cc::new(ReverseArray(self)))
+		Self::new(ReverseArray(self))
 	}
 
 	pub fn ptr_eq(a: &Self, b: &Self) -> bool {
-		match (a, b) {
-			(ArrValue::Bytes(a), ArrValue::Bytes(b)) => a.0 == b.0,
-			(ArrValue::Lazy(a), ArrValue::Lazy(b)) => Cc::ptr_eq(&a.0, &b.0),
-			(ArrValue::Expr(a), ArrValue::Expr(b)) => Cc::ptr_eq(&a.0, &b.0),
-			(ArrValue::Eager(a), ArrValue::Eager(b)) => Cc::ptr_eq(&a.0, &b.0),
-			(ArrValue::Extended(a), ArrValue::Extended(b)) => Cc::ptr_eq(a, b),
-			(ArrValue::Range(a), ArrValue::Range(b)) => a == b,
-			_ => false,
-		}
+		Cc::ptr_eq(&a.0, &b.0)
 	}
 
 	/// Is this vec supports `.get_cheap()?`
 	pub fn is_cheap(&self) -> bool {
-		match self {
-			ArrValue::Eager(_) | ArrValue::Range(..) | ArrValue::Bytes(_) | ArrValue::Chars(_) => {
-				true
-			}
-			ArrValue::Extended(v) => v.a.is_cheap() && v.b.is_cheap(),
-			ArrValue::Slice(r) => r.inner.is_cheap(),
-			ArrValue::Reverse(i) => i.0.is_cheap(),
-			ArrValue::Repeated(v) => v.is_cheap(),
-			ArrValue::Expr(_) | ArrValue::Lazy(_) | ArrValue::Mapped(_) => false,
-		}
+		self.0.is_cheap()
+	}
+
+	pub fn as_any(&self) -> &dyn Any {
+		&self.0
 	}
 }
 impl From<Vec<Val>> for ArrValue {
@@ -241,7 +196,7 @@
 }
 impl From<Vec<Thunk<Val>>> for ArrValue {
 	fn from(value: Vec<Thunk<Val>>) -> Self {
-		Self::lazy(Cc::new(value))
+		Self::lazy(value)
 	}
 }
 impl FromIterator<Val> for ArrValue {
@@ -249,6 +204,27 @@
 		Self::eager(iter.into_iter().collect())
 	}
 }
+impl ArrayLike for ArrValue {
+	fn len(&self) -> usize {
+		self.0.len()
+	}
 
+	fn get(&self, index: usize) -> Result<Option<Val>> {
+		self.0.get(index)
+	}
+
+	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {
+		self.0.get_lazy(index)
+	}
+
+	fn get_cheap(&self, index: usize) -> Option<Val> {
+		self.0.get_cheap(index)
+	}
+
+	fn is_cheap(&self) -> bool {
+		self.0.is_cheap()
+	}
+}
+
 #[cfg(target_pointer_width = "64")]
-static_assertions::assert_eq_size!(ArrValue, [u8; 16]);
+static_assertions::assert_eq_size!(ArrValue, [u8; 8]);
modifiedcrates/jrsonnet-evaluator/src/arr/spec.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/arr/spec.rs
+++ b/crates/jrsonnet-evaluator/src/arr/spec.rs
@@ -1,4 +1,4 @@
-use std::{cell::RefCell, iter, mem::replace, rc::Rc};
+use std::{any::Any, cell::RefCell, fmt::Debug, iter, mem::replace};
 
 use jrsonnet_gcmodule::{Cc, Trace};
 use jrsonnet_interner::{IBytes, IStr};
@@ -13,7 +13,7 @@
 	Context, Error, Result, Thunk, Val,
 };
 
-pub trait ArrayLike: Sized + Into<ArrValue> {
+pub trait ArrayLike: Any + Trace + Debug {
 	fn len(&self) -> usize;
 	fn is_empty(&self) -> bool {
 		self.len() == 0
@@ -22,12 +22,10 @@
 	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>>;
 	fn get_cheap(&self, index: usize) -> Option<Val>;
 
-	fn reverse(self) -> ArrValue {
-		ArrValue::Reverse(Cc::new(ReverseArray(self.into())))
-	}
+	fn is_cheap(&self) -> bool;
 }
 
-#[derive(Debug, Clone, Trace)]
+#[derive(Debug, Trace)]
 pub struct SliceArray {
 	pub(crate) inner: ArrValue,
 	pub(crate) from: u32,
@@ -81,15 +79,13 @@
 	fn get_cheap(&self, index: usize) -> Option<Val> {
 		self.iter_cheap()?.nth(index)
 	}
-}
-impl From<SliceArray> for ArrValue {
-	fn from(value: SliceArray) -> Self {
-		Self::Slice(Cc::new(value))
+	fn is_cheap(&self) -> bool {
+		self.inner.is_cheap()
 	}
 }
 
-#[derive(Trace, Debug, Clone)]
-pub struct CharArray(pub Rc<Vec<char>>);
+#[derive(Trace, Debug)]
+pub struct CharArray(pub Vec<char>);
 impl ArrayLike for CharArray {
 	fn len(&self) -> usize {
 		self.0.len()
@@ -108,14 +104,12 @@
 			.get(index)
 			.map(|v| Val::Str(StrValue::Flat(IStr::from(*v))))
 	}
-}
-impl From<CharArray> for ArrValue {
-	fn from(value: CharArray) -> Self {
-		ArrValue::Chars(value)
+	fn is_cheap(&self) -> bool {
+		true
 	}
 }
 
-#[derive(Trace, Debug, Clone)]
+#[derive(Trace, Debug)]
 pub struct BytesArray(pub IBytes);
 impl ArrayLike for BytesArray {
 	fn len(&self) -> usize {
@@ -133,10 +127,8 @@
 	fn get_cheap(&self, index: usize) -> Option<Val> {
 		self.0.get(index).map(|v| Val::Num(f64::from(*v)))
 	}
-}
-impl From<BytesArray> for ArrValue {
-	fn from(value: BytesArray) -> Self {
-		ArrValue::Bytes(value)
+	fn is_cheap(&self) -> bool {
+		true
 	}
 }
 
@@ -148,30 +140,30 @@
 	Pending,
 }
 
-#[derive(Debug, Trace)]
-pub struct ExprArrayInner {
+#[derive(Debug, Trace, Clone)]
+pub struct ExprArray {
 	ctx: Context,
-	cached: RefCell<Vec<ArrayThunk<LocExpr>>>,
+	cached: Cc<RefCell<Vec<ArrayThunk<LocExpr>>>>,
 }
-#[derive(Debug, Trace, Clone)]
-pub struct ExprArray(pub Cc<ExprArrayInner>);
 impl ExprArray {
 	pub fn new(ctx: Context, items: impl IntoIterator<Item = LocExpr>) -> Self {
-		Self(Cc::new(ExprArrayInner {
+		Self {
 			ctx,
-			cached: RefCell::new(items.into_iter().map(ArrayThunk::Waiting).collect()),
-		}))
+			cached: Cc::new(RefCell::new(
+				items.into_iter().map(ArrayThunk::Waiting).collect(),
+			)),
+		}
 	}
 }
 impl ArrayLike for ExprArray {
 	fn len(&self) -> usize {
-		self.0.cached.borrow().len()
+		self.cached.borrow().len()
 	}
 	fn get(&self, index: usize) -> Result<Option<Val>> {
 		if index >= self.len() {
 			return Ok(None);
 		}
-		match &self.0.cached.borrow()[index] {
+		match &self.cached.borrow()[index] {
 			ArrayThunk::Computed(c) => return Ok(Some(c.clone())),
 			ArrayThunk::Errored(e) => return Err(e.clone()),
 			ArrayThunk::Pending => return Err(InfiniteRecursionDetected.into()),
@@ -179,19 +171,19 @@
 		};
 
 		let ArrayThunk::Waiting(expr) =
-			replace(&mut self.0.cached.borrow_mut()[index], ArrayThunk::Pending)
+			replace(&mut self.cached.borrow_mut()[index], ArrayThunk::Pending)
 		else {
 			unreachable!()
 		};
 
-		let new_value = match evaluate(self.0.ctx.clone(), &expr) {
+		let new_value = match evaluate(self.ctx.clone(), &expr) {
 			Ok(v) => v,
 			Err(e) => {
-				self.0.cached.borrow_mut()[index] = ArrayThunk::Errored(e.clone());
+				self.cached.borrow_mut()[index] = ArrayThunk::Errored(e.clone());
 				return Err(e);
 			}
 		};
-		self.0.cached.borrow_mut()[index] = ArrayThunk::Computed(new_value.clone());
+		self.cached.borrow_mut()[index] = ArrayThunk::Computed(new_value.clone());
 		Ok(Some(new_value))
 	}
 	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {
@@ -215,7 +207,7 @@
 		if index >= self.len() {
 			return None;
 		}
-		match &self.0.cached.borrow()[index] {
+		match &self.cached.borrow()[index] {
 			ArrayThunk::Computed(c) => return Some(Thunk::evaluated(c.clone())),
 			ArrayThunk::Errored(e) => return Some(Thunk::errored(e.clone())),
 			ArrayThunk::Waiting(_) | ArrayThunk::Pending => {}
@@ -229,14 +221,12 @@
 	fn get_cheap(&self, _index: usize) -> Option<Val> {
 		None
 	}
-}
-impl From<ExprArray> for ArrValue {
-	fn from(value: ExprArray) -> Self {
-		Self::Expr(value)
+	fn is_cheap(&self) -> bool {
+		false
 	}
 }
 
-#[derive(Trace, Debug, Clone)]
+#[derive(Trace, Debug)]
 pub struct ExtendedArray {
 	pub a: ArrValue,
 	pub b: ArrValue,
@@ -319,15 +309,13 @@
 			self.b.get_cheap(index - self.split)
 		}
 	}
-}
-impl From<ExtendedArray> for ArrValue {
-	fn from(value: ExtendedArray) -> Self {
-		Self::Extended(Cc::new(value))
+	fn is_cheap(&self) -> bool {
+		self.a.is_cheap() && self.b.is_cheap()
 	}
 }
 
-#[derive(Trace, Debug, Clone)]
-pub struct LazyArray(pub Cc<Vec<Thunk<Val>>>);
+#[derive(Trace, Debug)]
+pub struct LazyArray(pub Vec<Thunk<Val>>);
 impl ArrayLike for LazyArray {
 	fn len(&self) -> usize {
 		self.0.len()
@@ -344,15 +332,13 @@
 	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {
 		self.0.get(index).cloned()
 	}
-}
-impl From<LazyArray> for ArrValue {
-	fn from(value: LazyArray) -> Self {
-		Self::Lazy(value)
+	fn is_cheap(&self) -> bool {
+		false
 	}
 }
 
-#[derive(Trace, Debug, Clone)]
-pub struct EagerArray(pub Cc<Vec<Val>>);
+#[derive(Trace, Debug)]
+pub struct EagerArray(pub Vec<Val>);
 impl ArrayLike for EagerArray {
 	fn len(&self) -> usize {
 		self.0.len()
@@ -369,15 +355,13 @@
 	fn get_cheap(&self, index: usize) -> Option<Val> {
 		self.0.get(index).cloned()
 	}
-}
-impl From<EagerArray> for ArrValue {
-	fn from(value: EagerArray) -> Self {
-		Self::Eager(value)
+	fn is_cheap(&self) -> bool {
+		true
 	}
 }
 
 /// Inclusive range type
-#[derive(Debug, Trace, Clone, PartialEq, Eq)]
+#[derive(Debug, Trace, PartialEq, Eq)]
 pub struct RangeArray {
 	start: i32,
 	end: i32,
@@ -422,14 +406,12 @@
 	fn get_cheap(&self, index: usize) -> Option<Val> {
 		self.range().nth(index).map(|i| Val::Num(f64::from(i)))
 	}
-}
-impl From<RangeArray> for ArrValue {
-	fn from(value: RangeArray) -> Self {
-		Self::Range(value)
+	fn is_cheap(&self) -> bool {
+		true
 	}
 }
 
-#[derive(Debug, Trace, Clone)]
+#[derive(Debug, Trace)]
 pub struct ReverseArray(pub ArrValue);
 impl ArrayLike for ReverseArray {
 	fn len(&self) -> usize {
@@ -447,44 +429,37 @@
 	fn get_cheap(&self, index: usize) -> Option<Val> {
 		self.0.get_cheap(self.0.len() - index - 1)
 	}
-	fn reverse(self) -> ArrValue {
-		self.0
-	}
-}
-impl From<ReverseArray> for ArrValue {
-	fn from(value: ReverseArray) -> Self {
-		Self::Reverse(Cc::new(value))
+	fn is_cheap(&self) -> bool {
+		self.0.is_cheap()
 	}
 }
 
-#[derive(Trace, Debug)]
-pub struct MappedArrayInner {
+#[derive(Trace, Debug, Clone)]
+pub struct MappedArray {
 	inner: ArrValue,
-	cached: RefCell<Vec<ArrayThunk<()>>>,
+	cached: Cc<RefCell<Vec<ArrayThunk<()>>>>,
 	mapper: FuncVal,
 }
-#[derive(Trace, Debug, Clone)]
-pub struct MappedArray(Cc<MappedArrayInner>);
 impl MappedArray {
 	pub fn new(inner: ArrValue, mapper: FuncVal) -> Self {
 		let len = inner.len();
-		Self(Cc::new(MappedArrayInner {
+		Self {
 			inner,
-			cached: RefCell::new(vec![ArrayThunk::Waiting(()); len]),
+			cached: Cc::new(RefCell::new(vec![ArrayThunk::Waiting(()); len])),
 			mapper,
-		}))
+		}
 	}
 }
 impl ArrayLike for MappedArray {
 	fn len(&self) -> usize {
-		self.0.cached.borrow().len()
+		self.cached.borrow().len()
 	}
 
 	fn get(&self, index: usize) -> Result<Option<Val>> {
 		if index >= self.len() {
 			return Ok(None);
 		}
-		match &self.0.cached.borrow()[index] {
+		match &self.cached.borrow()[index] {
 			ArrayThunk::Computed(c) => return Ok(Some(c.clone())),
 			ArrayThunk::Errored(e) => return Err(e.clone()),
 			ArrayThunk::Pending => return Err(InfiniteRecursionDetected.into()),
@@ -492,27 +467,26 @@
 		};
 
 		let ArrayThunk::Waiting(_) =
-			replace(&mut self.0.cached.borrow_mut()[index], ArrayThunk::Pending)
+			replace(&mut self.cached.borrow_mut()[index], ArrayThunk::Pending)
 		else {
 			unreachable!()
 		};
 
 		let val = self
-			.0
 			.inner
 			.get(index)
 			.transpose()
 			.expect("index checked")
-			.and_then(|r| self.0.mapper.evaluate_simple(&(r,), false));
+			.and_then(|r| self.mapper.evaluate_simple(&(r,), false));
 
 		let new_value = match val {
 			Ok(v) => v,
 			Err(e) => {
-				self.0.cached.borrow_mut()[index] = ArrayThunk::Errored(e.clone());
+				self.cached.borrow_mut()[index] = ArrayThunk::Errored(e.clone());
 				return Err(e);
 			}
 		};
-		self.0.cached.borrow_mut()[index] = ArrayThunk::Computed(new_value.clone());
+		self.cached.borrow_mut()[index] = ArrayThunk::Computed(new_value.clone());
 		Ok(Some(new_value))
 	}
 	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {
@@ -536,7 +510,7 @@
 		if index >= self.len() {
 			return None;
 		}
-		match &self.0.cached.borrow()[index] {
+		match &self.cached.borrow()[index] {
 			ArrayThunk::Computed(c) => return Some(Thunk::evaluated(c.clone())),
 			ArrayThunk::Errored(e) => return Some(Thunk::errored(e.clone())),
 			ArrayThunk::Waiting(_) | ArrayThunk::Pending => {}
@@ -551,82 +525,54 @@
 	fn get_cheap(&self, _index: usize) -> Option<Val> {
 		None
 	}
-}
-impl From<MappedArray> for ArrValue {
-	fn from(value: MappedArray) -> Self {
-		Self::Mapped(value)
+	fn is_cheap(&self) -> bool {
+		false
 	}
 }
 
 #[derive(Trace, Debug)]
-pub struct RepeatedArrayInner {
+pub struct RepeatedArray {
 	data: ArrValue,
 	repeats: usize,
 	total_len: usize,
 }
-#[derive(Trace, Debug, Clone)]
-pub struct RepeatedArray(Cc<RepeatedArrayInner>);
 impl RepeatedArray {
 	pub fn new(data: ArrValue, repeats: usize) -> Option<Self> {
 		let total_len = data.len().checked_mul(repeats)?;
-		Some(Self(Cc::new(RepeatedArrayInner {
+		Some(Self {
 			data,
 			repeats,
 			total_len,
-		})))
-	}
-	pub fn is_cheap(&self) -> bool {
-		self.0.data.is_cheap()
+		})
 	}
 }
 
 impl ArrayLike for RepeatedArray {
 	fn len(&self) -> usize {
-		self.0.total_len
+		self.total_len
 	}
 
 	fn get(&self, index: usize) -> Result<Option<Val>> {
-		if index > self.0.total_len {
+		if index > self.total_len {
 			return Ok(None);
 		}
-		self.0.data.get(index % self.0.data.len())
+		self.data.get(index % self.data.len())
 	}
 
 	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {
-		if index > self.0.total_len {
+		if index > self.total_len {
 			return None;
 		}
-		self.0.data.get_lazy(index % self.0.data.len())
+		self.data.get_lazy(index % self.data.len())
 	}
 
 	fn get_cheap(&self, index: usize) -> Option<Val> {
-		if index > self.0.total_len {
+		if index > self.total_len {
 			return None;
 		}
-		self.0.data.get_cheap(index % self.0.data.len())
+		self.data.get_cheap(index % self.data.len())
 	}
-}
-impl From<RepeatedArray> for ArrValue {
-	fn from(value: RepeatedArray) -> Self {
-		Self::Repeated(value)
+	fn is_cheap(&self) -> bool {
+		self.data.is_cheap()
 	}
 }
-
-macro_rules! pass {
-	($t:ident.$m:ident($($ident:ident),*)) => {
-		match $t {
-			Self::Bytes(e) => e.$m($($ident)*),
-			Self::Chars(e) => e.$m($($ident)*),
-			Self::Expr(e) => e.$m($($ident)*),
-			Self::Lazy(e) => e.$m($($ident)*),
-			Self::Eager(e) => e.$m($($ident)*),
-			Self::Range(e) => e.$m($($ident)*),
-			Self::Slice(e) => e.$m($($ident)*),
-			Self::Extended(e) => e.$m($($ident)*),
-			Self::Reverse(e) => e.$m($($ident)*),
-			Self::Mapped(e) => e.$m($($ident)*),
-			Self::Repeated(e) => e.$m($($ident)*),
-		}
-	};
-}
-pub(super) use pass;
modifiedcrates/jrsonnet-evaluator/src/evaluate/mod.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/evaluate/mod.rs
+++ b/crates/jrsonnet-evaluator/src/evaluate/mod.rs
@@ -573,10 +573,10 @@
 						evaluate(self.ctx, &self.item)
 					}
 				}
-				Val::Arr(ArrValue::lazy(Cc::new(vec![Thunk::new(ArrayElement {
+				Val::Arr(ArrValue::lazy(vec![Thunk::new(ArrayElement {
 					ctx,
 					item: items[0].clone(),
-				})])))
+				})]))
 			} else {
 				Val::Arr(ArrValue::expr(ctx, items.iter().cloned()))
 			}
modifiedcrates/jrsonnet-evaluator/src/obj.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/obj.rs
+++ b/crates/jrsonnet-evaluator/src/obj.rs
@@ -17,7 +17,7 @@
 	operator::evaluate_add_op,
 	tb, throw,
 	val::ThunkValue,
-	MaybeUnbound, Result, ResultExt, State, Thunk, Unbound, Val,
+	MaybeUnbound, Result, State, Thunk, Unbound, Val,
 };
 
 #[cfg(not(feature = "exp-preserve-order"))]
modifiedcrates/jrsonnet-evaluator/src/typed/conversions.rsdiffbeforeafterboth
before · crates/jrsonnet-evaluator/src/typed/conversions.rs
1use std::{collections::BTreeMap, marker::PhantomData, ops::Deref};23use jrsonnet_gcmodule::{Cc, Trace};4use jrsonnet_interner::{IBytes, IStr};5pub use jrsonnet_macros::Typed;6use jrsonnet_types::{ComplexValType, ValType};78use crate::{9	arr::ArrValue,10	error::Result,11	function::{native::NativeDesc, FuncDesc, FuncVal},12	throw,13	typed::CheckType,14	val::{IndexableVal, StrValue, ThunkMapper},15	ObjValue, ObjValueBuilder, Thunk, Val,16};1718#[derive(Trace)]19struct FromUntyped<K: Trace>(PhantomData<fn() -> K>);20impl<K> ThunkMapper<Val> for FromUntyped<K>21where22	K: Typed + Trace,23{24	type Output = K;2526	fn map(self, from: Val) -> Result<Self::Output> {27		K::from_untyped(from)28	}29}30impl<K: Trace> Default for FromUntyped<K> {31	fn default() -> Self {32		Self(PhantomData)33	}34}3536pub trait TypedObj: Typed {37	fn serialize(self, out: &mut ObjValueBuilder) -> Result<()>;38	fn parse(obj: &ObjValue) -> Result<Self>;39	fn into_object(self) -> Result<ObjValue> {40		let mut builder = ObjValueBuilder::new();41		self.serialize(&mut builder)?;42		Ok(builder.build())43	}44}4546pub trait Typed: Sized {47	const TYPE: &'static ComplexValType;48	fn into_untyped(typed: Self) -> Result<Val>;49	fn into_lazy_untyped(typed: Self) -> Thunk<Val> {50		Thunk::from(Self::into_untyped(typed))51	}52	fn from_untyped(untyped: Val) -> Result<Self>;53	fn from_lazy_untyped(lazy: Thunk<Val>) -> Result<Self> {54		Self::from_untyped(lazy.evaluate()?)55	}5657	// Whatever caller should use `into_lazy_untyped` instead of `into_untyped`58	fn provides_lazy() -> bool {59		false60	}6162	// Whatever caller should use `from_lazy_untyped` instead of `from_untyped` when possible63	fn wants_lazy() -> bool {64		false65	}6667	/// Hack to make builtins be able to return non-result values, and make macros able to convert those values to result68	/// This method returns identity in impl Typed for Result, and should not be overriden69	#[doc(hidden)]70	fn into_result(typed: Self) -> Result<Val> {71		let value = Self::into_untyped(typed)?;72		Ok(value)73	}74}7576impl<T> Typed for Thunk<T>77where78	T: Typed + Trace + Clone,79{80	const TYPE: &'static ComplexValType = &ComplexValType::Lazy(T::TYPE);8182	fn into_untyped(typed: Self) -> Result<Val> {83		T::into_untyped(typed.evaluate()?)84	}8586	fn from_untyped(untyped: Val) -> Result<Self> {87		Self::from_lazy_untyped(Thunk::evaluated(untyped))88	}8990	fn provides_lazy() -> bool {91		true92	}9394	fn into_lazy_untyped(inner: Self) -> Thunk<Val> {95		#[derive(Trace)]96		struct IntoUntyped<K: Trace>(PhantomData<fn() -> K>);97		impl<K> ThunkMapper<K> for IntoUntyped<K>98		where99			K: Typed + Trace,100		{101			type Output = Val;102103			fn map(self, from: K) -> Result<Self::Output> {104				K::into_untyped(from)105			}106		}107		impl<K: Trace> Default for IntoUntyped<K> {108			fn default() -> Self {109				Self(PhantomData)110			}111		}112		inner.map(<IntoUntyped<T>>::default())113	}114115	fn wants_lazy() -> bool {116		true117	}118119	fn from_lazy_untyped(inner: Thunk<Val>) -> Result<Self> {120		Ok(inner.map(<FromUntyped<T>>::default()))121	}122}123124const MAX_SAFE_INTEGER: f64 = ((1u64 << (f64::MANTISSA_DIGITS + 1)) - 1) as f64;125126macro_rules! impl_int {127	($($ty:ty)*) => {$(128		impl Typed for $ty {129			const TYPE: &'static ComplexValType =130				&ComplexValType::BoundedNumber(Some(Self::MIN as f64), Some(Self::MAX as f64));131			fn from_untyped(value: Val) -> Result<Self> {132				<Self as Typed>::TYPE.check(&value)?;133				match value {134					Val::Num(n) => {135						#[allow(clippy::float_cmp)]136						if n.trunc() != n {137							throw!(138								"cannot convert number with fractional part to {}",139								stringify!($ty)140							)141						}142						Ok(n as Self)143					}144					_ => unreachable!(),145				}146			}147			fn into_untyped(value: Self) -> Result<Val> {148				Ok(Val::Num(value as f64))149			}150		}151	)*};152}153154impl_int!(i8 u8 i16 u16 i32 u32);155156macro_rules! impl_bounded_int {157	($($name:ident = $ty:ty)*) => {$(158		#[derive(Clone, Copy)]159		pub struct $name<const MIN: $ty, const MAX: $ty>($ty);160		impl<const MIN: $ty, const MAX: $ty> $name<MIN, MAX> {161			pub const fn new(value: $ty) -> Option<$name<MIN, MAX>> {162				if value >= MIN && value <= MAX {163					Some(Self(value))164				} else {165					None166				}167			}168			pub const fn value(self) -> $ty {169				self.0170			}171		}172		impl<const MIN: $ty, const MAX: $ty> Deref for $name<MIN, MAX> {173			type Target = $ty;174			fn deref(&self) -> &Self::Target {175				&self.0176			}177		}178179		impl<const MIN: $ty, const MAX: $ty> Typed for $name<MIN, MAX> {180			const TYPE: &'static ComplexValType =181				&ComplexValType::BoundedNumber(182					Some(MIN as f64),183					Some(MAX as f64),184				);185186			fn from_untyped(value: Val) -> Result<Self> {187				<Self as Typed>::TYPE.check(&value)?;188				match value {189					Val::Num(n) => {190						#[allow(clippy::float_cmp)]191						if n.trunc() != n {192							throw!(193								"cannot convert number with fractional part to {}",194								stringify!($ty)195							)196						}197						Ok(Self(n as $ty))198					}199					_ => unreachable!(),200				}201			}202203			fn into_untyped(value: Self) -> Result<Val> {204				Ok(Val::Num(value.0 as f64))205			}206		}207	)*};208}209210impl_bounded_int!(211	BoundedI8 = i8212	BoundedI16 = i16213	BoundedI32 = i32214	BoundedI64 = i64215	BoundedUsize = usize216);217218impl Typed for f64 {219	const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Num);220221	fn into_untyped(value: Self) -> Result<Val> {222		Ok(Val::Num(value))223	}224225	fn from_untyped(value: Val) -> Result<Self> {226		<Self as Typed>::TYPE.check(&value)?;227		match value {228			Val::Num(n) => Ok(n),229			_ => unreachable!(),230		}231	}232}233234pub struct PositiveF64(pub f64);235impl Typed for PositiveF64 {236	const TYPE: &'static ComplexValType = &ComplexValType::BoundedNumber(Some(0.0), None);237238	fn into_untyped(value: Self) -> Result<Val> {239		Ok(Val::Num(value.0))240	}241242	fn from_untyped(value: Val) -> Result<Self> {243		<Self as Typed>::TYPE.check(&value)?;244		match value {245			Val::Num(n) => Ok(Self(n)),246			_ => unreachable!(),247		}248	}249}250impl Typed for usize {251	const TYPE: &'static ComplexValType =252		&ComplexValType::BoundedNumber(Some(0.0), Some(MAX_SAFE_INTEGER));253254	fn into_untyped(value: Self) -> Result<Val> {255		if value > MAX_SAFE_INTEGER as Self {256			throw!("number is too large")257		}258		Ok(Val::Num(value as f64))259	}260261	fn from_untyped(value: Val) -> Result<Self> {262		<Self as Typed>::TYPE.check(&value)?;263		match value {264			Val::Num(n) => {265				#[allow(clippy::float_cmp)]266				if n.trunc() != n {267					throw!("cannot convert number with fractional part to usize")268				}269				Ok(n as Self)270			}271			_ => unreachable!(),272		}273	}274}275276impl Typed for IStr {277	const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Str);278279	fn into_untyped(value: Self) -> Result<Val> {280		Ok(Val::Str(StrValue::Flat(value)))281	}282283	fn from_untyped(value: Val) -> Result<Self> {284		<Self as Typed>::TYPE.check(&value)?;285		match value {286			Val::Str(s) => Ok(s.into_flat()),287			_ => unreachable!(),288		}289	}290}291292impl Typed for String {293	const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Str);294295	fn into_untyped(value: Self) -> Result<Val> {296		Ok(Val::Str(StrValue::Flat(value.into())))297	}298299	fn from_untyped(value: Val) -> Result<Self> {300		<Self as Typed>::TYPE.check(&value)?;301		match value {302			Val::Str(s) => Ok(s.to_string()),303			_ => unreachable!(),304		}305	}306}307308impl Typed for char {309	const TYPE: &'static ComplexValType = &ComplexValType::Char;310311	fn into_untyped(value: Self) -> Result<Val> {312		Ok(Val::Str(StrValue::Flat(value.to_string().into())))313	}314315	fn from_untyped(value: Val) -> Result<Self> {316		<Self as Typed>::TYPE.check(&value)?;317		match value {318			Val::Str(s) => Ok(s.into_flat().chars().next().unwrap()),319			_ => unreachable!(),320		}321	}322}323324impl<T> Typed for Vec<T>325where326	T: Typed,327{328	const TYPE: &'static ComplexValType = &ComplexValType::ArrayRef(T::TYPE);329330	fn into_untyped(value: Self) -> Result<Val> {331		Ok(Val::Arr(332			value333				.into_iter()334				.map(T::into_untyped)335				.collect::<Result<ArrValue>>()?,336		))337	}338339	fn from_untyped(value: Val) -> Result<Self> {340		let Val::Arr(a) = value else {341			<Self as Typed>::TYPE.check(&value)?;342			unreachable!("typecheck should fail")343		};344		a.iter()345			.map(|r| r.and_then(T::from_untyped))346			.collect::<Result<Vec<T>>>()347	}348}349350impl<K: Typed + Ord, V: Typed> Typed for BTreeMap<K, V> {351	const TYPE: &'static ComplexValType = &ComplexValType::AttrsOf(V::TYPE);352353	fn into_untyped(typed: Self) -> Result<Val> {354		let mut out = ObjValueBuilder::with_capacity(typed.len());355		for (k, v) in typed {356			let Some(key) = K::into_untyped(k)?.as_str() else {357				throw!("map key should serialize to string");358			};359			let value = V::into_untyped(v)?;360			out.member(key).value_unchecked(value);361		}362		Ok(Val::Obj(out.build()))363	}364365	fn from_untyped(value: Val) -> Result<Self> {366		Self::TYPE.check(&value)?;367		let obj = value.as_obj().expect("typecheck should fail");368369		let mut out = BTreeMap::new();370		if V::wants_lazy() {371			for key in obj.fields_ex(372				false,373				#[cfg(feature = "exp-preserve-order")]374				false,375			) {376				let value = obj.get_lazy(key.clone()).expect("field exists");377				let value = V::from_lazy_untyped(value)?;378				let key = K::from_untyped(Val::Str(key.into()))?;379				let _ = out.insert(key, value);380			}381		} else {382			for (key, value) in obj.iter(383				#[cfg(feature = "exp-preserve-order")]384				false,385			) {386				let key = K::from_untyped(Val::Str(key.into()))?;387				let value = V::from_untyped(value?)?;388				let _ = out.insert(key, value);389			}390		}391		Ok(out)392	}393}394395impl Typed for Val {396	const TYPE: &'static ComplexValType = &ComplexValType::Any;397398	fn into_untyped(typed: Self) -> Result<Val> {399		Ok(typed)400	}401	fn from_untyped(untyped: Val) -> Result<Self> {402		Ok(untyped)403	}404}405406// Hack407#[doc(hidden)]408impl<T> Typed for Result<T>409where410	T: Typed,411{412	const TYPE: &'static ComplexValType = &ComplexValType::Any;413414	fn into_untyped(_typed: Self) -> Result<Val> {415		panic!("do not use this conversion")416	}417418	fn from_untyped(_untyped: Val) -> Result<Self> {419		panic!("do not use this conversion")420	}421422	fn into_result(typed: Self) -> Result<Val> {423		typed.map(T::into_untyped)?424	}425}426427/// Specialization428impl Typed for IBytes {429	const TYPE: &'static ComplexValType =430		&ComplexValType::ArrayRef(&ComplexValType::BoundedNumber(Some(0.0), Some(255.0)));431432	fn into_untyped(value: Self) -> Result<Val> {433		Ok(Val::Arr(ArrValue::bytes(value)))434	}435436	fn from_untyped(value: Val) -> Result<Self> {437		if let Val::Arr(ArrValue::Bytes(bytes)) = value {438			return Ok(bytes.0);439		}440		<Self as Typed>::TYPE.check(&value)?;441		match value {442			Val::Arr(a) => {443				let mut out = Vec::with_capacity(a.len());444				for e in a.iter() {445					let r = e?;446					out.push(u8::from_untyped(r)?);447				}448				Ok(out.as_slice().into())449			}450			_ => unreachable!(),451		}452	}453}454455pub struct M1;456impl Typed for M1 {457	const TYPE: &'static ComplexValType = &ComplexValType::BoundedNumber(Some(-1.0), Some(-1.0));458459	fn into_untyped(_: Self) -> Result<Val> {460		Ok(Val::Num(-1.0))461	}462463	fn from_untyped(value: Val) -> Result<Self> {464		<Self as Typed>::TYPE.check(&value)?;465		Ok(Self)466	}467}468469macro_rules! decl_either {470	($($name: ident, $($id: ident)*);*) => {$(471		#[derive(Clone)]472		pub enum $name<$($id),*> {473			$($id($id)),*474		}475		impl<$($id),*> Typed for $name<$($id),*>476		where477			$($id: Typed,)*478		{479			const TYPE: &'static ComplexValType = &ComplexValType::UnionRef(&[$($id::TYPE),*]);480481			fn into_untyped(value: Self) -> Result<Val> {482				match value {$(483					$name::$id(v) => $id::into_untyped(v)484				),*}485			}486487			fn from_untyped(value: Val) -> Result<Self> {488				$(489					if $id::TYPE.check(&value).is_ok() {490						$id::from_untyped(value).map(Self::$id)491					} else492				)* {493					<Self as Typed>::TYPE.check(&value)?;494					unreachable!()495				}496			}497		}498	)*}499}500decl_either!(501	Either1, A;502	Either2, A B;503	Either3, A B C;504	Either4, A B C D;505	Either5, A B C D E;506	Either6, A B C D E F;507	Either7, A B C D E F G508);509#[macro_export]510macro_rules! Either {511	($a:ty) => {Either1<$a>};512	($a:ty, $b:ty) => {Either2<$a, $b>};513	($a:ty, $b:ty, $c:ty) => {Either3<$a, $b, $c>};514	($a:ty, $b:ty, $c:ty, $d:ty) => {Either4<$a, $b, $c, $d>};515	($a:ty, $b:ty, $c:ty, $d:ty, $e:ty) => {Either5<$a, $b, $c, $d, $e>};516	($a:ty, $b:ty, $c:ty, $d:ty, $e:ty, $f:ty) => {Either6<$a, $b, $c, $d, $e, $f>};517	($a:ty, $b:ty, $c:ty, $d:ty, $e:ty, $f:ty, $g:ty) => {Either7<$a, $b, $c, $d, $e, $f, $g>};518}519pub use Either;520521pub type MyType = Either![u32, f64, String];522523impl Typed for ArrValue {524	const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Arr);525526	fn into_untyped(value: Self) -> Result<Val> {527		Ok(Val::Arr(value))528	}529530	fn from_untyped(value: Val) -> Result<Self> {531		<Self as Typed>::TYPE.check(&value)?;532		match value {533			Val::Arr(a) => Ok(a),534			_ => unreachable!(),535		}536	}537}538539impl Typed for FuncVal {540	const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Func);541542	fn into_untyped(value: Self) -> Result<Val> {543		Ok(Val::Func(value))544	}545546	fn from_untyped(value: Val) -> Result<Self> {547		<Self as Typed>::TYPE.check(&value)?;548		match value {549			Val::Func(a) => Ok(a),550			_ => unreachable!(),551		}552	}553}554555impl Typed for Cc<FuncDesc> {556	const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Func);557558	fn into_untyped(value: Self) -> Result<Val> {559		Ok(Val::Func(FuncVal::Normal(value)))560	}561562	fn from_untyped(value: Val) -> Result<Self> {563		<Self as Typed>::TYPE.check(&value)?;564		match value {565			Val::Func(FuncVal::Normal(desc)) => Ok(desc),566			Val::Func(_) => throw!("expected normal function, not builtin"),567			_ => unreachable!(),568		}569	}570}571572impl Typed for ObjValue {573	const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Obj);574575	fn into_untyped(value: Self) -> Result<Val> {576		Ok(Val::Obj(value))577	}578579	fn from_untyped(value: Val) -> Result<Self> {580		<Self as Typed>::TYPE.check(&value)?;581		match value {582			Val::Obj(a) => Ok(a),583			_ => unreachable!(),584		}585	}586}587588impl Typed for bool {589	const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Bool);590591	fn into_untyped(value: Self) -> Result<Val> {592		Ok(Val::Bool(value))593	}594595	fn from_untyped(value: Val) -> Result<Self> {596		<Self as Typed>::TYPE.check(&value)?;597		match value {598			Val::Bool(a) => Ok(a),599			_ => unreachable!(),600		}601	}602}603impl Typed for IndexableVal {604	const TYPE: &'static ComplexValType = &ComplexValType::UnionRef(&[605		&ComplexValType::Simple(ValType::Arr),606		&ComplexValType::Simple(ValType::Str),607	]);608609	fn into_untyped(value: Self) -> Result<Val> {610		match value {611			IndexableVal::Str(s) => Ok(Val::Str(StrValue::Flat(s))),612			IndexableVal::Arr(a) => Ok(Val::Arr(a)),613		}614	}615616	fn from_untyped(value: Val) -> Result<Self> {617		<Self as Typed>::TYPE.check(&value)?;618		value.into_indexable()619	}620}621622pub struct Null;623impl Typed for Null {624	const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Null);625626	fn into_untyped(_: Self) -> Result<Val> {627		Ok(Val::Null)628	}629630	fn from_untyped(value: Val) -> Result<Self> {631		<Self as Typed>::TYPE.check(&value)?;632		Ok(Self)633	}634}635636pub struct NativeFn<D: NativeDesc>(D::Value);637impl<D: NativeDesc> Deref for NativeFn<D> {638	type Target = D::Value;639640	fn deref(&self) -> &Self::Target {641		&self.0642	}643}644impl<D: NativeDesc> Typed for NativeFn<D> {645	const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Func);646647	fn into_untyped(_typed: Self) -> Result<Val> {648		throw!("can only convert functions from jsonnet to native")649	}650651	fn from_untyped(untyped: Val) -> Result<Self> {652		Ok(Self(653			untyped654				.as_func()655				.expect("shape is checked")656				.into_native::<D>(),657		))658	}659}
after · crates/jrsonnet-evaluator/src/typed/conversions.rs
1use std::{collections::BTreeMap, marker::PhantomData, ops::Deref};23use jrsonnet_gcmodule::{Cc, Trace};4use jrsonnet_interner::{IBytes, IStr};5pub use jrsonnet_macros::Typed;6use jrsonnet_types::{ComplexValType, ValType};78use crate::{9	arr::{ArrValue, BytesArray},10	error::Result,11	function::{native::NativeDesc, FuncDesc, FuncVal},12	throw,13	typed::CheckType,14	val::{IndexableVal, StrValue, ThunkMapper},15	ObjValue, ObjValueBuilder, Thunk, Val,16};1718#[derive(Trace)]19struct FromUntyped<K: Trace>(PhantomData<fn() -> K>);20impl<K> ThunkMapper<Val> for FromUntyped<K>21where22	K: Typed + Trace,23{24	type Output = K;2526	fn map(self, from: Val) -> Result<Self::Output> {27		K::from_untyped(from)28	}29}30impl<K: Trace> Default for FromUntyped<K> {31	fn default() -> Self {32		Self(PhantomData)33	}34}3536pub trait TypedObj: Typed {37	fn serialize(self, out: &mut ObjValueBuilder) -> Result<()>;38	fn parse(obj: &ObjValue) -> Result<Self>;39	fn into_object(self) -> Result<ObjValue> {40		let mut builder = ObjValueBuilder::new();41		self.serialize(&mut builder)?;42		Ok(builder.build())43	}44}4546pub trait Typed: Sized {47	const TYPE: &'static ComplexValType;48	fn into_untyped(typed: Self) -> Result<Val>;49	fn into_lazy_untyped(typed: Self) -> Thunk<Val> {50		Thunk::from(Self::into_untyped(typed))51	}52	fn from_untyped(untyped: Val) -> Result<Self>;53	fn from_lazy_untyped(lazy: Thunk<Val>) -> Result<Self> {54		Self::from_untyped(lazy.evaluate()?)55	}5657	// Whatever caller should use `into_lazy_untyped` instead of `into_untyped`58	fn provides_lazy() -> bool {59		false60	}6162	// Whatever caller should use `from_lazy_untyped` instead of `from_untyped` when possible63	fn wants_lazy() -> bool {64		false65	}6667	/// Hack to make builtins be able to return non-result values, and make macros able to convert those values to result68	/// This method returns identity in impl Typed for Result, and should not be overriden69	#[doc(hidden)]70	fn into_result(typed: Self) -> Result<Val> {71		let value = Self::into_untyped(typed)?;72		Ok(value)73	}74}7576impl<T> Typed for Thunk<T>77where78	T: Typed + Trace + Clone,79{80	const TYPE: &'static ComplexValType = &ComplexValType::Lazy(T::TYPE);8182	fn into_untyped(typed: Self) -> Result<Val> {83		T::into_untyped(typed.evaluate()?)84	}8586	fn from_untyped(untyped: Val) -> Result<Self> {87		Self::from_lazy_untyped(Thunk::evaluated(untyped))88	}8990	fn provides_lazy() -> bool {91		true92	}9394	fn into_lazy_untyped(inner: Self) -> Thunk<Val> {95		#[derive(Trace)]96		struct IntoUntyped<K: Trace>(PhantomData<fn() -> K>);97		impl<K> ThunkMapper<K> for IntoUntyped<K>98		where99			K: Typed + Trace,100		{101			type Output = Val;102103			fn map(self, from: K) -> Result<Self::Output> {104				K::into_untyped(from)105			}106		}107		impl<K: Trace> Default for IntoUntyped<K> {108			fn default() -> Self {109				Self(PhantomData)110			}111		}112		inner.map(<IntoUntyped<T>>::default())113	}114115	fn wants_lazy() -> bool {116		true117	}118119	fn from_lazy_untyped(inner: Thunk<Val>) -> Result<Self> {120		Ok(inner.map(<FromUntyped<T>>::default()))121	}122}123124const MAX_SAFE_INTEGER: f64 = ((1u64 << (f64::MANTISSA_DIGITS + 1)) - 1) as f64;125126macro_rules! impl_int {127	($($ty:ty)*) => {$(128		impl Typed for $ty {129			const TYPE: &'static ComplexValType =130				&ComplexValType::BoundedNumber(Some(Self::MIN as f64), Some(Self::MAX as f64));131			fn from_untyped(value: Val) -> Result<Self> {132				<Self as Typed>::TYPE.check(&value)?;133				match value {134					Val::Num(n) => {135						#[allow(clippy::float_cmp)]136						if n.trunc() != n {137							throw!(138								"cannot convert number with fractional part to {}",139								stringify!($ty)140							)141						}142						Ok(n as Self)143					}144					_ => unreachable!(),145				}146			}147			fn into_untyped(value: Self) -> Result<Val> {148				Ok(Val::Num(value as f64))149			}150		}151	)*};152}153154impl_int!(i8 u8 i16 u16 i32 u32);155156macro_rules! impl_bounded_int {157	($($name:ident = $ty:ty)*) => {$(158		#[derive(Clone, Copy)]159		pub struct $name<const MIN: $ty, const MAX: $ty>($ty);160		impl<const MIN: $ty, const MAX: $ty> $name<MIN, MAX> {161			pub const fn new(value: $ty) -> Option<$name<MIN, MAX>> {162				if value >= MIN && value <= MAX {163					Some(Self(value))164				} else {165					None166				}167			}168			pub const fn value(self) -> $ty {169				self.0170			}171		}172		impl<const MIN: $ty, const MAX: $ty> Deref for $name<MIN, MAX> {173			type Target = $ty;174			fn deref(&self) -> &Self::Target {175				&self.0176			}177		}178179		impl<const MIN: $ty, const MAX: $ty> Typed for $name<MIN, MAX> {180			const TYPE: &'static ComplexValType =181				&ComplexValType::BoundedNumber(182					Some(MIN as f64),183					Some(MAX as f64),184				);185186			fn from_untyped(value: Val) -> Result<Self> {187				<Self as Typed>::TYPE.check(&value)?;188				match value {189					Val::Num(n) => {190						#[allow(clippy::float_cmp)]191						if n.trunc() != n {192							throw!(193								"cannot convert number with fractional part to {}",194								stringify!($ty)195							)196						}197						Ok(Self(n as $ty))198					}199					_ => unreachable!(),200				}201			}202203			fn into_untyped(value: Self) -> Result<Val> {204				Ok(Val::Num(value.0 as f64))205			}206		}207	)*};208}209210impl_bounded_int!(211	BoundedI8 = i8212	BoundedI16 = i16213	BoundedI32 = i32214	BoundedI64 = i64215	BoundedUsize = usize216);217218impl Typed for f64 {219	const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Num);220221	fn into_untyped(value: Self) -> Result<Val> {222		Ok(Val::Num(value))223	}224225	fn from_untyped(value: Val) -> Result<Self> {226		<Self as Typed>::TYPE.check(&value)?;227		match value {228			Val::Num(n) => Ok(n),229			_ => unreachable!(),230		}231	}232}233234pub struct PositiveF64(pub f64);235impl Typed for PositiveF64 {236	const TYPE: &'static ComplexValType = &ComplexValType::BoundedNumber(Some(0.0), None);237238	fn into_untyped(value: Self) -> Result<Val> {239		Ok(Val::Num(value.0))240	}241242	fn from_untyped(value: Val) -> Result<Self> {243		<Self as Typed>::TYPE.check(&value)?;244		match value {245			Val::Num(n) => Ok(Self(n)),246			_ => unreachable!(),247		}248	}249}250impl Typed for usize {251	const TYPE: &'static ComplexValType =252		&ComplexValType::BoundedNumber(Some(0.0), Some(MAX_SAFE_INTEGER));253254	fn into_untyped(value: Self) -> Result<Val> {255		if value > MAX_SAFE_INTEGER as Self {256			throw!("number is too large")257		}258		Ok(Val::Num(value as f64))259	}260261	fn from_untyped(value: Val) -> Result<Self> {262		<Self as Typed>::TYPE.check(&value)?;263		match value {264			Val::Num(n) => {265				#[allow(clippy::float_cmp)]266				if n.trunc() != n {267					throw!("cannot convert number with fractional part to usize")268				}269				Ok(n as Self)270			}271			_ => unreachable!(),272		}273	}274}275276impl Typed for IStr {277	const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Str);278279	fn into_untyped(value: Self) -> Result<Val> {280		Ok(Val::Str(StrValue::Flat(value)))281	}282283	fn from_untyped(value: Val) -> Result<Self> {284		<Self as Typed>::TYPE.check(&value)?;285		match value {286			Val::Str(s) => Ok(s.into_flat()),287			_ => unreachable!(),288		}289	}290}291292impl Typed for String {293	const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Str);294295	fn into_untyped(value: Self) -> Result<Val> {296		Ok(Val::Str(StrValue::Flat(value.into())))297	}298299	fn from_untyped(value: Val) -> Result<Self> {300		<Self as Typed>::TYPE.check(&value)?;301		match value {302			Val::Str(s) => Ok(s.to_string()),303			_ => unreachable!(),304		}305	}306}307308impl Typed for char {309	const TYPE: &'static ComplexValType = &ComplexValType::Char;310311	fn into_untyped(value: Self) -> Result<Val> {312		Ok(Val::Str(StrValue::Flat(value.to_string().into())))313	}314315	fn from_untyped(value: Val) -> Result<Self> {316		<Self as Typed>::TYPE.check(&value)?;317		match value {318			Val::Str(s) => Ok(s.into_flat().chars().next().unwrap()),319			_ => unreachable!(),320		}321	}322}323324impl<T> Typed for Vec<T>325where326	T: Typed,327{328	const TYPE: &'static ComplexValType = &ComplexValType::ArrayRef(T::TYPE);329330	fn into_untyped(value: Self) -> Result<Val> {331		Ok(Val::Arr(332			value333				.into_iter()334				.map(T::into_untyped)335				.collect::<Result<ArrValue>>()?,336		))337	}338339	fn from_untyped(value: Val) -> Result<Self> {340		let Val::Arr(a) = value else {341			<Self as Typed>::TYPE.check(&value)?;342			unreachable!("typecheck should fail")343		};344		a.iter()345			.map(|r| r.and_then(T::from_untyped))346			.collect::<Result<Vec<T>>>()347	}348}349350impl<K: Typed + Ord, V: Typed> Typed for BTreeMap<K, V> {351	const TYPE: &'static ComplexValType = &ComplexValType::AttrsOf(V::TYPE);352353	fn into_untyped(typed: Self) -> Result<Val> {354		let mut out = ObjValueBuilder::with_capacity(typed.len());355		for (k, v) in typed {356			let Some(key) = K::into_untyped(k)?.as_str() else {357				throw!("map key should serialize to string");358			};359			let value = V::into_untyped(v)?;360			out.member(key).value_unchecked(value);361		}362		Ok(Val::Obj(out.build()))363	}364365	fn from_untyped(value: Val) -> Result<Self> {366		Self::TYPE.check(&value)?;367		let obj = value.as_obj().expect("typecheck should fail");368369		let mut out = BTreeMap::new();370		if V::wants_lazy() {371			for key in obj.fields_ex(372				false,373				#[cfg(feature = "exp-preserve-order")]374				false,375			) {376				let value = obj.get_lazy(key.clone()).expect("field exists");377				let value = V::from_lazy_untyped(value)?;378				let key = K::from_untyped(Val::Str(key.into()))?;379				let _ = out.insert(key, value);380			}381		} else {382			for (key, value) in obj.iter(383				#[cfg(feature = "exp-preserve-order")]384				false,385			) {386				let key = K::from_untyped(Val::Str(key.into()))?;387				let value = V::from_untyped(value?)?;388				let _ = out.insert(key, value);389			}390		}391		Ok(out)392	}393}394395impl Typed for Val {396	const TYPE: &'static ComplexValType = &ComplexValType::Any;397398	fn into_untyped(typed: Self) -> Result<Val> {399		Ok(typed)400	}401	fn from_untyped(untyped: Val) -> Result<Self> {402		Ok(untyped)403	}404}405406// Hack407#[doc(hidden)]408impl<T> Typed for Result<T>409where410	T: Typed,411{412	const TYPE: &'static ComplexValType = &ComplexValType::Any;413414	fn into_untyped(_typed: Self) -> Result<Val> {415		panic!("do not use this conversion")416	}417418	fn from_untyped(_untyped: Val) -> Result<Self> {419		panic!("do not use this conversion")420	}421422	fn into_result(typed: Self) -> Result<Val> {423		typed.map(T::into_untyped)?424	}425}426427/// Specialization428impl Typed for IBytes {429	const TYPE: &'static ComplexValType =430		&ComplexValType::ArrayRef(&ComplexValType::BoundedNumber(Some(0.0), Some(255.0)));431432	fn into_untyped(value: Self) -> Result<Val> {433		Ok(Val::Arr(ArrValue::bytes(value)))434	}435436	fn from_untyped(value: Val) -> Result<Self> {437		match &value {438			Val::Arr(a) => {439				if let Some(bytes) = a.as_any().downcast_ref::<BytesArray>() {440					return Ok(bytes.0.as_slice().into());441				};442				<Self as Typed>::TYPE.check(&value)?;443				// Any::downcast_ref::<ByteArray>(&a);444				let mut out = Vec::with_capacity(a.len());445				for e in a.iter() {446					let r = e?;447					out.push(u8::from_untyped(r)?);448				}449				Ok(out.as_slice().into())450			}451			_ => {452				<Self as Typed>::TYPE.check(&value)?;453				unreachable!()454			}455		}456	}457}458459pub struct M1;460impl Typed for M1 {461	const TYPE: &'static ComplexValType = &ComplexValType::BoundedNumber(Some(-1.0), Some(-1.0));462463	fn into_untyped(_: Self) -> Result<Val> {464		Ok(Val::Num(-1.0))465	}466467	fn from_untyped(value: Val) -> Result<Self> {468		<Self as Typed>::TYPE.check(&value)?;469		Ok(Self)470	}471}472473macro_rules! decl_either {474	($($name: ident, $($id: ident)*);*) => {$(475		#[derive(Clone)]476		pub enum $name<$($id),*> {477			$($id($id)),*478		}479		impl<$($id),*> Typed for $name<$($id),*>480		where481			$($id: Typed,)*482		{483			const TYPE: &'static ComplexValType = &ComplexValType::UnionRef(&[$($id::TYPE),*]);484485			fn into_untyped(value: Self) -> Result<Val> {486				match value {$(487					$name::$id(v) => $id::into_untyped(v)488				),*}489			}490491			fn from_untyped(value: Val) -> Result<Self> {492				$(493					if $id::TYPE.check(&value).is_ok() {494						$id::from_untyped(value).map(Self::$id)495					} else496				)* {497					<Self as Typed>::TYPE.check(&value)?;498					unreachable!()499				}500			}501		}502	)*}503}504decl_either!(505	Either1, A;506	Either2, A B;507	Either3, A B C;508	Either4, A B C D;509	Either5, A B C D E;510	Either6, A B C D E F;511	Either7, A B C D E F G512);513#[macro_export]514macro_rules! Either {515	($a:ty) => {Either1<$a>};516	($a:ty, $b:ty) => {Either2<$a, $b>};517	($a:ty, $b:ty, $c:ty) => {Either3<$a, $b, $c>};518	($a:ty, $b:ty, $c:ty, $d:ty) => {Either4<$a, $b, $c, $d>};519	($a:ty, $b:ty, $c:ty, $d:ty, $e:ty) => {Either5<$a, $b, $c, $d, $e>};520	($a:ty, $b:ty, $c:ty, $d:ty, $e:ty, $f:ty) => {Either6<$a, $b, $c, $d, $e, $f>};521	($a:ty, $b:ty, $c:ty, $d:ty, $e:ty, $f:ty, $g:ty) => {Either7<$a, $b, $c, $d, $e, $f, $g>};522}523pub use Either;524525pub type MyType = Either![u32, f64, String];526527impl Typed for ArrValue {528	const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Arr);529530	fn into_untyped(value: Self) -> Result<Val> {531		Ok(Val::Arr(value))532	}533534	fn from_untyped(value: Val) -> Result<Self> {535		<Self as Typed>::TYPE.check(&value)?;536		match value {537			Val::Arr(a) => Ok(a),538			_ => unreachable!(),539		}540	}541}542543impl Typed for FuncVal {544	const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Func);545546	fn into_untyped(value: Self) -> Result<Val> {547		Ok(Val::Func(value))548	}549550	fn from_untyped(value: Val) -> Result<Self> {551		<Self as Typed>::TYPE.check(&value)?;552		match value {553			Val::Func(a) => Ok(a),554			_ => unreachable!(),555		}556	}557}558559impl Typed for Cc<FuncDesc> {560	const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Func);561562	fn into_untyped(value: Self) -> Result<Val> {563		Ok(Val::Func(FuncVal::Normal(value)))564	}565566	fn from_untyped(value: Val) -> Result<Self> {567		<Self as Typed>::TYPE.check(&value)?;568		match value {569			Val::Func(FuncVal::Normal(desc)) => Ok(desc),570			Val::Func(_) => throw!("expected normal function, not builtin"),571			_ => unreachable!(),572		}573	}574}575576impl Typed for ObjValue {577	const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Obj);578579	fn into_untyped(value: Self) -> Result<Val> {580		Ok(Val::Obj(value))581	}582583	fn from_untyped(value: Val) -> Result<Self> {584		<Self as Typed>::TYPE.check(&value)?;585		match value {586			Val::Obj(a) => Ok(a),587			_ => unreachable!(),588		}589	}590}591592impl Typed for bool {593	const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Bool);594595	fn into_untyped(value: Self) -> Result<Val> {596		Ok(Val::Bool(value))597	}598599	fn from_untyped(value: Val) -> Result<Self> {600		<Self as Typed>::TYPE.check(&value)?;601		match value {602			Val::Bool(a) => Ok(a),603			_ => unreachable!(),604		}605	}606}607impl Typed for IndexableVal {608	const TYPE: &'static ComplexValType = &ComplexValType::UnionRef(&[609		&ComplexValType::Simple(ValType::Arr),610		&ComplexValType::Simple(ValType::Str),611	]);612613	fn into_untyped(value: Self) -> Result<Val> {614		match value {615			IndexableVal::Str(s) => Ok(Val::Str(StrValue::Flat(s))),616			IndexableVal::Arr(a) => Ok(Val::Arr(a)),617		}618	}619620	fn from_untyped(value: Val) -> Result<Self> {621		<Self as Typed>::TYPE.check(&value)?;622		value.into_indexable()623	}624}625626pub struct Null;627impl Typed for Null {628	const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Null);629630	fn into_untyped(_: Self) -> Result<Val> {631		Ok(Val::Null)632	}633634	fn from_untyped(value: Val) -> Result<Self> {635		<Self as Typed>::TYPE.check(&value)?;636		Ok(Self)637	}638}639640pub struct NativeFn<D: NativeDesc>(D::Value);641impl<D: NativeDesc> Deref for NativeFn<D> {642	type Target = D::Value;643644	fn deref(&self) -> &Self::Target {645		&self.0646	}647}648impl<D: NativeDesc> Typed for NativeFn<D> {649	const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Func);650651	fn into_untyped(_typed: Self) -> Result<Val> {652		throw!("can only convert functions from jsonnet to native")653	}654655	fn from_untyped(untyped: Val) -> Result<Self> {656		Ok(Self(657			untyped658				.as_func()659				.expect("shape is checked")660				.into_native::<D>(),661		))662	}663}
modifiedcrates/jrsonnet-evaluator/src/val.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/val.rs
+++ b/crates/jrsonnet-evaluator/src/val.rs
@@ -9,7 +9,7 @@
 use jrsonnet_interner::IStr;
 use jrsonnet_types::ValType;
 
-pub use crate::arr::ArrValue;
+pub use crate::arr::{ArrValue, ArrayLike};
 use crate::{
 	error::{Error, ErrorKind::*},
 	function::FuncVal,
modifiedcrates/jrsonnet-stdlib/src/sets.rsdiffbeforeafterboth
--- a/crates/jrsonnet-stdlib/src/sets.rs
+++ b/crates/jrsonnet-stdlib/src/sets.rs
@@ -7,7 +7,6 @@
 	val::ArrValue,
 	Thunk, Val,
 };
-use jrsonnet_gcmodule::Cc;
 use jrsonnet_parser::BinaryOpType;
 
 #[builtin]
@@ -70,5 +69,5 @@
 			}
 		};
 	}
-	Ok(ArrValue::lazy(Cc::new(out)))
+	Ok(ArrValue::lazy(out))
 }
modifiedcrates/jrsonnet-stdlib/src/sort.rsdiffbeforeafterboth
--- a/crates/jrsonnet-stdlib/src/sort.rs
+++ b/crates/jrsonnet-stdlib/src/sort.rs
@@ -10,7 +10,6 @@
 	val::{equals, ArrValue},
 	Thunk, Val,
 };
-use jrsonnet_gcmodule::Cc;
 use jrsonnet_parser::BinaryOpType;
 
 use crate::eval_on_empty;
@@ -136,7 +135,7 @@
 			values.iter().collect::<Result<Vec<Val>>>()?,
 		)?))
 	} else {
-		Ok(ArrValue::lazy(Cc::new(sort_keyf(values, key_getter)?)))
+		Ok(ArrValue::lazy(sort_keyf(values, key_getter)?))
 	}
 }
 
@@ -186,7 +185,7 @@
 			arr.iter().collect::<Result<Vec<Val>>>()?,
 		)?))
 	} else {
-		Ok(ArrValue::lazy(Cc::new(uniq_keyf(arr, keyF)?)))
+		Ok(ArrValue::lazy(uniq_keyf(arr, keyF)?))
 	}
 }
 
@@ -204,8 +203,8 @@
 		Ok(ArrValue::eager(arr))
 	} else {
 		let arr = sort_keyf(arr, keyF.clone())?;
-		let arr = uniq_keyf(ArrValue::lazy(Cc::new(arr)), keyF)?;
-		Ok(ArrValue::lazy(Cc::new(arr)))
+		let arr = uniq_keyf(ArrValue::lazy(arr), keyF)?;
+		Ok(ArrValue::lazy(arr))
 	}
 }