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
after · crates/jrsonnet-evaluator/src/arr/spec.rs
1use std::{any::Any, cell::RefCell, fmt::Debug, iter, mem::replace};23use jrsonnet_gcmodule::{Cc, Trace};4use jrsonnet_interner::{IBytes, IStr};5use jrsonnet_parser::LocExpr;67use super::ArrValue;8use crate::{9	error::ErrorKind::InfiniteRecursionDetected,10	evaluate,11	function::FuncVal,12	val::{StrValue, ThunkValue},13	Context, Error, Result, Thunk, Val,14};1516pub trait ArrayLike: Any + Trace + Debug {17	fn len(&self) -> usize;18	fn is_empty(&self) -> bool {19		self.len() == 020	}21	fn get(&self, index: usize) -> Result<Option<Val>>;22	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>>;23	fn get_cheap(&self, index: usize) -> Option<Val>;2425	fn is_cheap(&self) -> bool;26}2728#[derive(Debug, Trace)]29pub struct SliceArray {30	pub(crate) inner: ArrValue,31	pub(crate) from: u32,32	pub(crate) to: u32,33	pub(crate) step: u32,34}3536impl SliceArray {37	fn iter(&self) -> impl Iterator<Item = Result<Val>> + '_ {38		self.inner39			.iter()40			.skip(self.from as usize)41			.take((self.to - self.from) as usize)42			.step_by(self.step as usize)43	}4445	fn iter_lazy(&self) -> impl Iterator<Item = Thunk<Val>> + '_ {46		self.inner47			.iter_lazy()48			.skip(self.from as usize)49			.take((self.to - self.from) as usize)50			.step_by(self.step as usize)51	}5253	fn iter_cheap(&self) -> Option<impl crate::arr::ArrayLikeIter<Val> + '_> {54		Some(55			self.inner56				.iter_cheap()?57				.skip(self.from as usize)58				.take((self.to - self.from) as usize)59				.step_by(self.step as usize),60		)61	}62}63impl ArrayLike for SliceArray {64	fn len(&self) -> usize {65		iter::repeat(())66			.take((self.to - self.from) as usize)67			.step_by(self.step as usize)68			.count()69	}7071	fn get(&self, index: usize) -> Result<Option<Val>> {72		self.iter().nth(index).transpose()73	}7475	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {76		self.iter_lazy().nth(index)77	}7879	fn get_cheap(&self, index: usize) -> Option<Val> {80		self.iter_cheap()?.nth(index)81	}82	fn is_cheap(&self) -> bool {83		self.inner.is_cheap()84	}85}8687#[derive(Trace, Debug)]88pub struct CharArray(pub Vec<char>);89impl ArrayLike for CharArray {90	fn len(&self) -> usize {91		self.0.len()92	}9394	fn get(&self, index: usize) -> Result<Option<Val>> {95		Ok(self.get_cheap(index))96	}9798	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {99		self.get_cheap(index).map(Thunk::evaluated)100	}101102	fn get_cheap(&self, index: usize) -> Option<Val> {103		self.0104			.get(index)105			.map(|v| Val::Str(StrValue::Flat(IStr::from(*v))))106	}107	fn is_cheap(&self) -> bool {108		true109	}110}111112#[derive(Trace, Debug)]113pub struct BytesArray(pub IBytes);114impl ArrayLike for BytesArray {115	fn len(&self) -> usize {116		self.0.len()117	}118119	fn get(&self, index: usize) -> Result<Option<Val>> {120		Ok(self.get_cheap(index))121	}122123	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {124		self.get_cheap(index).map(Thunk::evaluated)125	}126127	fn get_cheap(&self, index: usize) -> Option<Val> {128		self.0.get(index).map(|v| Val::Num(f64::from(*v)))129	}130	fn is_cheap(&self) -> bool {131		true132	}133}134135#[derive(Debug, Trace, Clone)]136enum ArrayThunk<T: 'static + Trace> {137	Computed(Val),138	Errored(Error),139	Waiting(T),140	Pending,141}142143#[derive(Debug, Trace, Clone)]144pub struct ExprArray {145	ctx: Context,146	cached: Cc<RefCell<Vec<ArrayThunk<LocExpr>>>>,147}148impl ExprArray {149	pub fn new(ctx: Context, items: impl IntoIterator<Item = LocExpr>) -> Self {150		Self {151			ctx,152			cached: Cc::new(RefCell::new(153				items.into_iter().map(ArrayThunk::Waiting).collect(),154			)),155		}156	}157}158impl ArrayLike for ExprArray {159	fn len(&self) -> usize {160		self.cached.borrow().len()161	}162	fn get(&self, index: usize) -> Result<Option<Val>> {163		if index >= self.len() {164			return Ok(None);165		}166		match &self.cached.borrow()[index] {167			ArrayThunk::Computed(c) => return Ok(Some(c.clone())),168			ArrayThunk::Errored(e) => return Err(e.clone()),169			ArrayThunk::Pending => return Err(InfiniteRecursionDetected.into()),170			ArrayThunk::Waiting(..) => {}171		};172173		let ArrayThunk::Waiting(expr) =174			replace(&mut self.cached.borrow_mut()[index], ArrayThunk::Pending)175		else {176			unreachable!()177		};178179		let new_value = match evaluate(self.ctx.clone(), &expr) {180			Ok(v) => v,181			Err(e) => {182				self.cached.borrow_mut()[index] = ArrayThunk::Errored(e.clone());183				return Err(e);184			}185		};186		self.cached.borrow_mut()[index] = ArrayThunk::Computed(new_value.clone());187		Ok(Some(new_value))188	}189	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {190		#[derive(Trace)]191		struct ArrayElement {192			arr_thunk: ExprArray,193			index: usize,194		}195196		impl ThunkValue for ArrayElement {197			type Output = Val;198199			fn get(self: Box<Self>) -> Result<Self::Output> {200				self.arr_thunk201					.get(self.index)202					.transpose()203					.expect("index checked")204			}205		}206207		if index >= self.len() {208			return None;209		}210		match &self.cached.borrow()[index] {211			ArrayThunk::Computed(c) => return Some(Thunk::evaluated(c.clone())),212			ArrayThunk::Errored(e) => return Some(Thunk::errored(e.clone())),213			ArrayThunk::Waiting(_) | ArrayThunk::Pending => {}214		};215216		Some(Thunk::new(ArrayElement {217			arr_thunk: self.clone(),218			index,219		}))220	}221	fn get_cheap(&self, _index: usize) -> Option<Val> {222		None223	}224	fn is_cheap(&self) -> bool {225		false226	}227}228229#[derive(Trace, Debug)]230pub struct ExtendedArray {231	pub a: ArrValue,232	pub b: ArrValue,233	split: usize,234	len: usize,235}236impl ExtendedArray {237	pub fn new(a: ArrValue, b: ArrValue) -> Self {238		let a_len = a.len();239		let b_len = b.len();240		Self {241			a,242			b,243			split: a_len,244			len: a_len.checked_add(b_len).expect("too large array value"),245		}246	}247}248249struct WithExactSize<I>(I, usize);250impl<I, T> Iterator for WithExactSize<I>251where252	I: Iterator<Item = T>,253{254	type Item = T;255256	fn next(&mut self) -> Option<Self::Item> {257		self.0.next()258	}259	fn nth(&mut self, n: usize) -> Option<Self::Item> {260		self.0.nth(n)261	}262	fn size_hint(&self) -> (usize, Option<usize>) {263		(self.1, Some(self.1))264	}265}266impl<I> DoubleEndedIterator for WithExactSize<I>267where268	I: DoubleEndedIterator,269{270	fn next_back(&mut self) -> Option<Self::Item> {271		self.0.next_back()272	}273	fn nth_back(&mut self, n: usize) -> Option<Self::Item> {274		self.0.nth_back(n)275	}276}277impl<I> ExactSizeIterator for WithExactSize<I>278where279	I: Iterator,280{281	fn len(&self) -> usize {282		self.1283	}284}285impl ArrayLike for ExtendedArray {286	fn get(&self, index: usize) -> Result<Option<Val>> {287		if self.split > index {288			self.a.get(index)289		} else {290			self.b.get(index - self.split)291		}292	}293	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {294		if self.split > index {295			self.a.get_lazy(index)296		} else {297			self.b.get_lazy(index - self.split)298		}299	}300301	fn len(&self) -> usize {302		self.len303	}304305	fn get_cheap(&self, index: usize) -> Option<Val> {306		if self.split > index {307			self.a.get_cheap(index)308		} else {309			self.b.get_cheap(index - self.split)310		}311	}312	fn is_cheap(&self) -> bool {313		self.a.is_cheap() && self.b.is_cheap()314	}315}316317#[derive(Trace, Debug)]318pub struct LazyArray(pub Vec<Thunk<Val>>);319impl ArrayLike for LazyArray {320	fn len(&self) -> usize {321		self.0.len()322	}323	fn get(&self, index: usize) -> Result<Option<Val>> {324		let Some(v) = self.0.get(index) else {325			return Ok(None);326		};327		v.evaluate().map(Some)328	}329	fn get_cheap(&self, _index: usize) -> Option<Val> {330		None331	}332	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {333		self.0.get(index).cloned()334	}335	fn is_cheap(&self) -> bool {336		false337	}338}339340#[derive(Trace, Debug)]341pub struct EagerArray(pub Vec<Val>);342impl ArrayLike for EagerArray {343	fn len(&self) -> usize {344		self.0.len()345	}346347	fn get(&self, index: usize) -> Result<Option<Val>> {348		Ok(self.0.get(index).cloned())349	}350351	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {352		self.0.get(index).cloned().map(Thunk::evaluated)353	}354355	fn get_cheap(&self, index: usize) -> Option<Val> {356		self.0.get(index).cloned()357	}358	fn is_cheap(&self) -> bool {359		true360	}361}362363/// Inclusive range type364#[derive(Debug, Trace, PartialEq, Eq)]365pub struct RangeArray {366	start: i32,367	end: i32,368}369impl RangeArray {370	pub fn empty() -> Self {371		Self::new_exclusive(0, 0)372	}373	pub fn new_exclusive(start: i32, end: i32) -> Self {374		end.checked_sub(1)375			.map_or_else(Self::empty, |end| Self { start, end })376	}377	pub fn new_inclusive(start: i32, end: i32) -> Self {378		Self { start, end }379	}380	fn range(&self) -> impl Iterator<Item = i32> + ExactSizeIterator + DoubleEndedIterator {381		WithExactSize(382			self.start..=self.end,383			(self.end as usize)384				.wrapping_sub(self.start as usize)385				.wrapping_add(1),386		)387	}388}389390impl ArrayLike for RangeArray {391	fn len(&self) -> usize {392		self.range().len()393	}394	fn is_empty(&self) -> bool {395		self.range().len() == 0396	}397398	fn get(&self, index: usize) -> Result<Option<Val>> {399		Ok(self.get_cheap(index))400	}401402	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {403		self.get_cheap(index).map(Thunk::evaluated)404	}405406	fn get_cheap(&self, index: usize) -> Option<Val> {407		self.range().nth(index).map(|i| Val::Num(f64::from(i)))408	}409	fn is_cheap(&self) -> bool {410		true411	}412}413414#[derive(Debug, Trace)]415pub struct ReverseArray(pub ArrValue);416impl ArrayLike for ReverseArray {417	fn len(&self) -> usize {418		self.0.len()419	}420421	fn get(&self, index: usize) -> Result<Option<Val>> {422		self.0.get(self.0.len() - index - 1)423	}424425	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {426		self.0.get_lazy(self.0.len() - index - 1)427	}428429	fn get_cheap(&self, index: usize) -> Option<Val> {430		self.0.get_cheap(self.0.len() - index - 1)431	}432	fn is_cheap(&self) -> bool {433		self.0.is_cheap()434	}435}436437#[derive(Trace, Debug, Clone)]438pub struct MappedArray {439	inner: ArrValue,440	cached: Cc<RefCell<Vec<ArrayThunk<()>>>>,441	mapper: FuncVal,442}443impl MappedArray {444	pub fn new(inner: ArrValue, mapper: FuncVal) -> Self {445		let len = inner.len();446		Self {447			inner,448			cached: Cc::new(RefCell::new(vec![ArrayThunk::Waiting(()); len])),449			mapper,450		}451	}452}453impl ArrayLike for MappedArray {454	fn len(&self) -> usize {455		self.cached.borrow().len()456	}457458	fn get(&self, index: usize) -> Result<Option<Val>> {459		if index >= self.len() {460			return Ok(None);461		}462		match &self.cached.borrow()[index] {463			ArrayThunk::Computed(c) => return Ok(Some(c.clone())),464			ArrayThunk::Errored(e) => return Err(e.clone()),465			ArrayThunk::Pending => return Err(InfiniteRecursionDetected.into()),466			ArrayThunk::Waiting(..) => {}467		};468469		let ArrayThunk::Waiting(_) =470			replace(&mut self.cached.borrow_mut()[index], ArrayThunk::Pending)471		else {472			unreachable!()473		};474475		let val = self476			.inner477			.get(index)478			.transpose()479			.expect("index checked")480			.and_then(|r| self.mapper.evaluate_simple(&(r,), false));481482		let new_value = match val {483			Ok(v) => v,484			Err(e) => {485				self.cached.borrow_mut()[index] = ArrayThunk::Errored(e.clone());486				return Err(e);487			}488		};489		self.cached.borrow_mut()[index] = ArrayThunk::Computed(new_value.clone());490		Ok(Some(new_value))491	}492	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {493		#[derive(Trace)]494		struct ArrayElement {495			arr_thunk: MappedArray,496			index: usize,497		}498499		impl ThunkValue for ArrayElement {500			type Output = Val;501502			fn get(self: Box<Self>) -> Result<Self::Output> {503				self.arr_thunk504					.get(self.index)505					.transpose()506					.expect("index checked")507			}508		}509510		if index >= self.len() {511			return None;512		}513		match &self.cached.borrow()[index] {514			ArrayThunk::Computed(c) => return Some(Thunk::evaluated(c.clone())),515			ArrayThunk::Errored(e) => return Some(Thunk::errored(e.clone())),516			ArrayThunk::Waiting(_) | ArrayThunk::Pending => {}517		};518519		Some(Thunk::new(ArrayElement {520			arr_thunk: self.clone(),521			index,522		}))523	}524525	fn get_cheap(&self, _index: usize) -> Option<Val> {526		None527	}528	fn is_cheap(&self) -> bool {529		false530	}531}532533#[derive(Trace, Debug)]534pub struct RepeatedArray {535	data: ArrValue,536	repeats: usize,537	total_len: usize,538}539impl RepeatedArray {540	pub fn new(data: ArrValue, repeats: usize) -> Option<Self> {541		let total_len = data.len().checked_mul(repeats)?;542		Some(Self {543			data,544			repeats,545			total_len,546		})547	}548}549550impl ArrayLike for RepeatedArray {551	fn len(&self) -> usize {552		self.total_len553	}554555	fn get(&self, index: usize) -> Result<Option<Val>> {556		if index > self.total_len {557			return Ok(None);558		}559		self.data.get(index % self.data.len())560	}561562	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {563		if index > self.total_len {564			return None;565		}566		self.data.get_lazy(index % self.data.len())567	}568569	fn get_cheap(&self, index: usize) -> Option<Val> {570		if index > self.total_len {571			return None;572		}573		self.data.get_cheap(index % self.data.len())574	}575	fn is_cheap(&self) -> bool {576		self.data.is_cheap()577	}578}
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
--- a/crates/jrsonnet-evaluator/src/typed/conversions.rs
+++ b/crates/jrsonnet-evaluator/src/typed/conversions.rs
@@ -6,7 +6,7 @@
 use jrsonnet_types::{ComplexValType, ValType};
 
 use crate::{
-	arr::ArrValue,
+	arr::{ArrValue, BytesArray},
 	error::Result,
 	function::{native::NativeDesc, FuncDesc, FuncVal},
 	throw,
@@ -434,12 +434,13 @@
 	}
 
 	fn from_untyped(value: Val) -> Result<Self> {
-		if let Val::Arr(ArrValue::Bytes(bytes)) = value {
-			return Ok(bytes.0);
-		}
-		<Self as Typed>::TYPE.check(&value)?;
-		match value {
+		match &value {
 			Val::Arr(a) => {
+				if let Some(bytes) = a.as_any().downcast_ref::<BytesArray>() {
+					return Ok(bytes.0.as_slice().into());
+				};
+				<Self as Typed>::TYPE.check(&value)?;
+				// Any::downcast_ref::<ByteArray>(&a);
 				let mut out = Vec::with_capacity(a.len());
 				for e in a.iter() {
 					let r = e?;
@@ -447,7 +448,10 @@
 				}
 				Ok(out.as_slice().into())
 			}
-			_ => unreachable!(),
+			_ => {
+				<Self as Typed>::TYPE.check(&value)?;
+				unreachable!()
+			}
 		}
 	}
 }
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))
 	}
 }