git.delta.rocks / jrsonnet / refs/commits / 4c3faa0f088c

difftreelog

refactor reduce callsite boilerplate

Yaroslav Bolyukin2022-12-08parent: #ccafbf7.patch.diff
in: master

38 files changed

modifiedbindings/jsonnet/src/import.rsdiffbeforeafterboth
--- a/bindings/jsonnet/src/import.rs
+++ b/bindings/jsonnet/src/import.rs
@@ -116,12 +116,11 @@
 	cb: JsonnetImportCallback,
 	ctx: *mut c_void,
 ) {
-	vm.state
-		.set_import_resolver(Box::new(CallbackImportResolver {
-			cb,
-			ctx,
-			out: RefCell::new(HashMap::new()),
-		}))
+	vm.state.set_import_resolver(CallbackImportResolver {
+		cb,
+		ctx,
+		out: RefCell::new(HashMap::new()),
+	})
 }
 
 /// # Safety
modifiedbindings/jsonnet/src/native.rsdiffbeforeafterboth
--- a/bindings/jsonnet/src/native.rs
+++ b/bindings/jsonnet/src/native.rs
@@ -7,11 +7,9 @@
 use jrsonnet_evaluator::{
 	error::{Error, ErrorKind},
 	function::builtin::{NativeCallback, NativeCallbackHandler},
-	tb,
 	typed::Typed,
 	IStr, Val,
 };
-use jrsonnet_gcmodule::Cc;
 
 use crate::VM;
 
@@ -102,9 +100,6 @@
 		.add_native(
 			name,
 			#[allow(deprecated)]
-			Cc::new(tb!(NativeCallback::new(
-				params,
-				tb!(JsonnetNativeCallbackHandler { ctx, cb }),
-			))),
+			NativeCallback::new(params, JsonnetNativeCallbackHandler { ctx, cb }),
 		)
 }
modifiedbindings/jsonnet/src/val_extract.rsdiffbeforeafterboth
--- a/bindings/jsonnet/src/val_extract.rs
+++ b/bindings/jsonnet/src/val_extract.rs
@@ -13,7 +13,10 @@
 #[no_mangle]
 pub extern "C" fn jsonnet_json_extract_string(_vm: &VM, v: &Val) -> *mut c_char {
 	match v {
-		Val::Str(s) => CString::new(s as &str).unwrap().into_raw(),
+		Val::Str(s) => {
+			let s = s.clone().into_flat();
+			CString::new(s.as_str()).unwrap().into_raw()
+		}
 		_ => std::ptr::null_mut(),
 	}
 }
modifiedbindings/jsonnet/src/val_make.rsdiffbeforeafterboth
--- a/bindings/jsonnet/src/val_make.rs
+++ b/bindings/jsonnet/src/val_make.rs
@@ -5,8 +5,10 @@
 	os::raw::{c_char, c_double, c_int},
 };
 
-use jrsonnet_evaluator::{val::ArrValue, ObjValue, Val};
-use jrsonnet_gcmodule::Cc;
+use jrsonnet_evaluator::{
+	val::{ArrValue, StrValue},
+	ObjValue, Val,
+};
 
 use crate::VM;
 
@@ -19,7 +21,7 @@
 pub unsafe extern "C" fn jsonnet_json_make_string(_vm: &VM, val: *const c_char) -> *mut Val {
 	let val = CStr::from_ptr(val);
 	let val = val.to_str().expect("string is not utf-8");
-	Box::into_raw(Box::new(Val::Str(val.into())))
+	Box::into_raw(Box::new(Val::Str(StrValue::Flat(val.into()))))
 }
 
 /// Convert the given double to a `JsonnetJsonValue`.
@@ -46,7 +48,7 @@
 /// Assign elements with [`jsonnet_json_array_append`].
 #[no_mangle]
 pub extern "C" fn jsonnet_json_make_array(_vm: &VM) -> *mut Val {
-	Box::into_raw(Box::new(Val::Arr(ArrValue::eager(Cc::new(Vec::new())))))
+	Box::into_raw(Box::new(Val::Arr(ArrValue::eager(Vec::new()))))
 }
 
 /// Make a `JsonnetJsonValue` representing an object.
modifiedcrates/jrsonnet-cli/src/lib.rsdiffbeforeafterboth
--- a/crates/jrsonnet-cli/src/lib.rs
+++ b/crates/jrsonnet-cli/src/lib.rs
@@ -54,7 +54,7 @@
 			library_paths.extend(env::split_paths(path.as_os_str()));
 		}
 
-		s.set_import_resolver(Box::new(FileImportResolver::new(library_paths)));
+		s.set_import_resolver(FileImportResolver::new(library_paths));
 
 		set_stack_depth_limit(self.max_stack);
 		Ok(())
modifiedcrates/jrsonnet-evaluator/src/arr/mod.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/arr/mod.rs
+++ b/crates/jrsonnet-evaluator/src/arr/mod.rs
@@ -55,8 +55,8 @@
 		Self::Lazy(LazyArray(thunks))
 	}
 
-	pub fn eager(values: Cc<Vec<Val>>) -> Self {
-		Self::Eager(EagerArray(values))
+	pub fn eager(values: Vec<Val>) -> Self {
+		Self::Eager(EagerArray(Cc::new(values)))
 	}
 
 	pub fn repeated(data: ArrValue, repeats: usize) -> Option<Self> {
@@ -81,7 +81,7 @@
 				out.push(i);
 			};
 		}
-		Ok(Self::eager(Cc::new(out)))
+		Ok(Self::eager(out))
 	}
 
 	pub fn extended(a: ArrValue, b: ArrValue) -> Self {
@@ -98,7 +98,7 @@
 			let mut out = Vec::with_capacity(a.len() + b.len());
 			out.extend(a);
 			out.extend(b);
-			Self::eager(Cc::new(out))
+			Self::eager(out)
 		} else {
 			let mut out = Vec::with_capacity(a.len() + b.len());
 			out.extend(a.iter_lazy());
@@ -235,7 +235,7 @@
 }
 impl From<Vec<Val>> for ArrValue {
 	fn from(value: Vec<Val>) -> Self {
-		Self::eager(Cc::new(value))
+		Self::eager(value)
 	}
 }
 impl From<Vec<Thunk<Val>>> for ArrValue {
@@ -243,6 +243,11 @@
 		Self::lazy(Cc::new(value))
 	}
 }
+impl FromIterator<Val> for ArrValue {
+	fn from_iter<T: IntoIterator<Item = Val>>(iter: T) -> Self {
+		Self::eager(iter.into_iter().collect())
+	}
+}
 
 #[cfg(target_pointer_width = "64")]
 static_assertions::assert_eq_size!(ArrValue, [u8; 16]);
modifiedcrates/jrsonnet-evaluator/src/arr/spec.rsdiffbeforeafterboth
before · crates/jrsonnet-evaluator/src/arr/spec.rs
1//! Those implementations are a bit sketchy, as this is mostly performance experiments2//! of not yet finished nightly rust features34use std::{cell::RefCell, iter, mem::replace};56use jrsonnet_gcmodule::{Cc, Trace};7use jrsonnet_interner::IBytes;8use jrsonnet_parser::LocExpr;910use super::{ArrValue, ArrayLikeIter};11use crate::{12	error::ErrorKind::InfiniteRecursionDetected, evaluate, function::FuncVal, tb, typed::Any,13	val::ThunkValue, Context, Error, Result, Thunk, Val,14};1516pub trait ArrayLike: Sized + Into<ArrValue> {17	#[cfg(feature = "nightly")]18	type Iter<'t>19	where20		Self: 't;21	#[cfg(feature = "nightly")]22	type IterLazy<'t>23	where24		Self: 't;25	#[cfg(feature = "nightly")]26	type IterCheap<'t>27	where28		Self: 't;2930	fn len(&self) -> usize;31	fn is_empty(&self) -> bool {32		self.len() == 033	}34	fn get(&self, index: usize) -> Result<Option<Val>>;35	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>>;36	fn get_cheap(&self, index: usize) -> Option<Val>;37	#[cfg(feature = "nightly")]38	#[allow(clippy::iter_not_returning_iterator)]39	fn iter(&self) -> Self::Iter<'_>;40	#[cfg(feature = "nightly")]41	fn iter_lazy(&self) -> Self::IterLazy<'_>;42	#[cfg(feature = "nightly")]43	fn iter_cheap(&self) -> Option<Self::IterCheap<'_>>;4445	fn reverse(self) -> ArrValue {46		ArrValue::Reverse(Cc::new(ReverseArray(self.into())))47	}48}4950#[derive(Debug, Clone, Trace)]51pub struct SliceArray {52	pub(crate) inner: ArrValue,53	pub(crate) from: u32,54	pub(crate) to: u32,55	pub(crate) step: u32,56}5758impl SliceArray {59	#[cfg(not(feature = "nightly"))]60	fn iter(&self) -> impl Iterator<Item = Result<Val>> + '_ {61		self.inner62			.iter()63			.skip(self.from as usize)64			.take((self.to - self.from) as usize)65			.step_by(self.step as usize)66	}6768	#[cfg(not(feature = "nightly"))]69	fn iter_lazy(&self) -> impl Iterator<Item = Thunk<Val>> + '_ {70		self.inner71			.iter_lazy()72			.skip(self.from as usize)73			.take((self.to - self.from) as usize)74			.step_by(self.step as usize)75	}7677	#[cfg(not(feature = "nightly"))]78	fn iter_cheap(&self) -> Option<impl ArrayLikeIter<Val> + '_> {79		Some(80			self.inner81				.iter_cheap()?82				.skip(self.from as usize)83				.take((self.to - self.from) as usize)84				.step_by(self.step as usize),85		)86	}87}88#[cfg(feature = "nightly")]89type SliceArrayIter<'t> = impl DoubleEndedIterator<Item = Result<Val>> + ExactSizeIterator + 't;90#[cfg(feature = "nightly")]91type SliceArrayLazyIter<'t> = impl DoubleEndedIterator<Item = Thunk<Val>> + ExactSizeIterator + 't;92#[cfg(feature = "nightly")]93type SliceArrayCheapIter<'t> = impl DoubleEndedIterator<Item = Val> + ExactSizeIterator + 't;94impl ArrayLike for SliceArray {95	#[cfg(feature = "nightly")]96	type Iter<'t> = SliceArrayIter<'t>;97	#[cfg(feature = "nightly")]98	type IterLazy<'t> = SliceArrayLazyIter<'t>;99	#[cfg(feature = "nightly")]100	type IterCheap<'t> = SliceArrayCheapIter<'t>;101102	fn len(&self) -> usize {103		iter::repeat(())104			.take((self.to - self.from) as usize)105			.step_by(self.step as usize)106			.count()107	}108109	fn get(&self, index: usize) -> Result<Option<Val>> {110		self.iter().nth(index).transpose()111	}112113	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {114		self.iter_lazy().nth(index)115	}116117	fn get_cheap(&self, index: usize) -> Option<Val> {118		self.iter_cheap()?.nth(index)119	}120121	#[cfg(feature = "nightly")]122	fn iter(&self) -> SliceArrayIter<'_> {123		self.inner124			.iter()125			.skip(self.from as usize)126			.take((self.to - self.from) as usize)127			.step_by(self.step as usize)128	}129130	#[cfg(feature = "nightly")]131	fn iter_lazy(&self) -> SliceArrayLazyIter<'_> {132		self.inner133			.iter_lazy()134			.skip(self.from as usize)135			.take((self.to - self.from) as usize)136			.step_by(self.step as usize)137	}138139	#[cfg(feature = "nightly")]140	fn iter_cheap(&self) -> Option<SliceArrayCheapIter<'_>> {141		Some(142			self.inner143				.iter_cheap()?144				.skip(self.from as usize)145				.take((self.to - self.from) as usize)146				.step_by(self.step as usize),147		)148	}149}150impl From<SliceArray> for ArrValue {151	fn from(value: SliceArray) -> Self {152		Self::Slice(Cc::new(value))153	}154}155156#[derive(Trace, Debug, Clone)]157pub struct BytesArray(pub IBytes);158#[cfg(feature = "nightly")]159type BytesArrayIter<'t> = impl DoubleEndedIterator<Item = Result<Val>> + ExactSizeIterator + 't;160#[cfg(feature = "nightly")]161type BytesArrayLazyIter<'t> = impl DoubleEndedIterator<Item = Thunk<Val>> + ExactSizeIterator + 't;162#[cfg(feature = "nightly")]163type BytesArrayCheapIter<'t> = impl DoubleEndedIterator<Item = Val> + ExactSizeIterator + 't;164impl ArrayLike for BytesArray {165	#[cfg(feature = "nightly")]166	type Iter<'t> = BytesArrayIter<'t>;167	#[cfg(feature = "nightly")]168	type IterLazy<'t> = BytesArrayLazyIter<'t>;169	#[cfg(feature = "nightly")]170	type IterCheap<'t> = BytesArrayCheapIter<'t>;171172	fn len(&self) -> usize {173		self.0.len()174	}175176	fn get(&self, index: usize) -> Result<Option<Val>> {177		Ok(self.get_cheap(index))178	}179180	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {181		self.get_cheap(index).map(Thunk::evaluated)182	}183184	fn get_cheap(&self, index: usize) -> Option<Val> {185		self.0.get(index).map(|v| Val::Num(f64::from(*v)))186	}187188	#[cfg(feature = "nightly")]189	fn iter(&self) -> BytesArrayIter<'_> {190		self.0.iter().map(|v| Ok(Val::Num(f64::from(*v))))191	}192193	#[cfg(feature = "nightly")]194	fn iter_lazy(&self) -> BytesArrayLazyIter<'_> {195		self.0196			.iter()197			.map(|v| Thunk::evaluated(Val::Num(f64::from(*v))))198	}199200	#[cfg(feature = "nightly")]201	fn iter_cheap(&self) -> Option<BytesArrayCheapIter<'_>> {202		Some(self.0.iter().map(|v| Val::Num(f64::from(*v))))203	}204}205impl From<BytesArray> for ArrValue {206	fn from(value: BytesArray) -> Self {207		ArrValue::Bytes(value)208	}209}210211#[derive(Debug, Trace, Clone)]212enum ArrayThunk<T: 'static + Trace> {213	Computed(Val),214	Errored(Error),215	Waiting(T),216	Pending,217}218219#[derive(Debug, Trace)]220pub struct ExprArrayInner {221	ctx: Context,222	cached: RefCell<Vec<ArrayThunk<LocExpr>>>,223}224#[derive(Debug, Trace, Clone)]225pub struct ExprArray(pub Cc<ExprArrayInner>);226impl ExprArray {227	pub fn new(ctx: Context, items: impl IntoIterator<Item = LocExpr>) -> Self {228		Self(Cc::new(ExprArrayInner {229			ctx,230			cached: RefCell::new(items.into_iter().map(ArrayThunk::Waiting).collect()),231		}))232	}233}234#[cfg(feature = "nightly")]235type ExprArrayIter<'t> = impl DoubleEndedIterator<Item = Result<Val>> + ExactSizeIterator + 't;236#[cfg(feature = "nightly")]237type ExprArrayLazyIter<'t> = impl DoubleEndedIterator<Item = Thunk<Val>> + ExactSizeIterator + 't;238#[cfg(feature = "nightly")]239type ExprArrayCheapIter<'t> = iter::Empty<Val>;240impl ArrayLike for ExprArray {241	#[cfg(feature = "nightly")]242	type Iter<'t> = ExprArrayIter<'t>;243	#[cfg(feature = "nightly")]244	type IterLazy<'t> = ExprArrayLazyIter<'t>;245	#[cfg(feature = "nightly")]246	type IterCheap<'t> = ExprArrayCheapIter<'t>;247248	fn len(&self) -> usize {249		self.0.cached.borrow().len()250	}251	fn get(&self, index: usize) -> Result<Option<Val>> {252		if index >= self.len() {253			return Ok(None);254		}255		match &self.0.cached.borrow()[index] {256			ArrayThunk::Computed(c) => return Ok(Some(c.clone())),257			ArrayThunk::Errored(e) => return Err(e.clone()),258			ArrayThunk::Pending => return Err(InfiniteRecursionDetected.into()),259			ArrayThunk::Waiting(..) => {}260		};261262		let ArrayThunk::Waiting(expr) = replace(&mut self.0.cached.borrow_mut()[index], ArrayThunk::Pending) else {263			unreachable!()264		};265266		let new_value = match evaluate(self.0.ctx.clone(), &expr) {267			Ok(v) => v,268			Err(e) => {269				self.0.cached.borrow_mut()[index] = ArrayThunk::Errored(e.clone());270				return Err(e);271			}272		};273		self.0.cached.borrow_mut()[index] = ArrayThunk::Computed(new_value.clone());274		Ok(Some(new_value))275	}276	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {277		#[derive(Trace)]278		struct ArrayElement {279			arr_thunk: ExprArray,280			index: usize,281		}282283		impl ThunkValue for ArrayElement {284			type Output = Val;285286			fn get(self: Box<Self>) -> Result<Self::Output> {287				self.arr_thunk288					.get(self.index)289					.transpose()290					.expect("index checked")291			}292		}293294		if index >= self.len() {295			return None;296		}297		match &self.0.cached.borrow()[index] {298			ArrayThunk::Computed(c) => return Some(Thunk::evaluated(c.clone())),299			ArrayThunk::Errored(e) => return Some(Thunk::errored(e.clone())),300			ArrayThunk::Waiting(_) | ArrayThunk::Pending => {}301		};302303		Some(Thunk::new(tb!(ArrayElement {304			arr_thunk: self.clone(),305			index,306		})))307	}308	fn get_cheap(&self, _index: usize) -> Option<Val> {309		None310	}311312	#[cfg(feature = "nightly")]313	fn iter(&self) -> ExprArrayIter<'_> {314		(0..self.len()).map(|i| self.get(i).transpose().expect("index checked"))315	}316	#[cfg(feature = "nightly")]317	fn iter_lazy(&self) -> ExprArrayLazyIter<'_> {318		(0..self.len()).map(|i| self.get_lazy(i).expect("index checked"))319	}320	#[cfg(feature = "nightly")]321	fn iter_cheap(&self) -> Option<Self::IterCheap<'_>> {322		None323	}324}325impl From<ExprArray> for ArrValue {326	fn from(value: ExprArray) -> Self {327		Self::Expr(value)328	}329}330331#[derive(Trace, Debug, Clone)]332pub struct ExtendedArray {333	pub a: ArrValue,334	pub b: ArrValue,335	split: usize,336	len: usize,337}338#[cfg(feature = "nightly")]339340type ExtendedArrayIter<'t> = impl DoubleEndedIterator<Item = Result<Val>> + ExactSizeIterator + 't;341#[cfg(feature = "nightly")]342type ExtendedArrayLazyIter<'t> =343	impl DoubleEndedIterator<Item = Thunk<Val>> + ExactSizeIterator + 't;344#[cfg(feature = "nightly")]345type ExtendedArrayCheapIter<'t> = impl DoubleEndedIterator<Item = Val> + ExactSizeIterator + 't;346impl ExtendedArray {347	pub fn new(a: ArrValue, b: ArrValue) -> Self {348		let a_len = a.len();349		let b_len = b.len();350		Self {351			a,352			b,353			split: a_len,354			len: a_len.checked_add(b_len).expect("too large array value"),355		}356	}357}358359struct WithExactSize<I>(I, usize);360impl<I, T> Iterator for WithExactSize<I>361where362	I: Iterator<Item = T>,363{364	type Item = T;365366	fn next(&mut self) -> Option<Self::Item> {367		self.0.next()368	}369	fn nth(&mut self, n: usize) -> Option<Self::Item> {370		self.0.nth(n)371	}372	fn size_hint(&self) -> (usize, Option<usize>) {373		(self.1, Some(self.1))374	}375}376impl<I> DoubleEndedIterator for WithExactSize<I>377where378	I: DoubleEndedIterator,379{380	fn next_back(&mut self) -> Option<Self::Item> {381		self.0.next_back()382	}383	fn nth_back(&mut self, n: usize) -> Option<Self::Item> {384		self.0.nth_back(n)385	}386}387impl<I> ExactSizeIterator for WithExactSize<I>388where389	I: Iterator,390{391	fn len(&self) -> usize {392		self.1393	}394}395impl ArrayLike for ExtendedArray {396	#[cfg(feature = "nightly")]397	type Iter<'t> = ExtendedArrayIter<'t>;398	#[cfg(feature = "nightly")]399	type IterLazy<'t> = ExtendedArrayLazyIter<'t>;400	#[cfg(feature = "nightly")]401	type IterCheap<'t> = ExtendedArrayCheapIter<'t>;402403	fn get(&self, index: usize) -> Result<Option<Val>> {404		if self.split > index {405			self.a.get(index)406		} else {407			self.b.get(index - self.split)408		}409	}410	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {411		if self.split > index {412			self.a.get_lazy(index)413		} else {414			self.b.get_lazy(index - self.split)415		}416	}417418	fn len(&self) -> usize {419		self.len420	}421422	fn get_cheap(&self, index: usize) -> Option<Val> {423		if self.split > index {424			self.a.get_cheap(index)425		} else {426			self.b.get_cheap(index - self.split)427		}428	}429430	#[cfg(feature = "nightly")]431	fn iter(&self) -> ExtendedArrayIter<'_> {432		WithExactSize(self.a.iter().chain(self.b.iter()), self.len)433	}434	#[cfg(feature = "nightly")]435	fn iter_lazy(&self) -> ExtendedArrayLazyIter<'_> {436		WithExactSize(self.a.iter_lazy().chain(self.b.iter_lazy()), self.len)437	}438	#[cfg(feature = "nightly")]439	fn iter_cheap(&self) -> Option<ExtendedArrayCheapIter<'_>> {440		let a = self.a.iter_cheap()?;441		let b = self.b.iter_cheap()?;442		Some(WithExactSize(a.chain(b), self.len))443	}444}445impl From<ExtendedArray> for ArrValue {446	fn from(value: ExtendedArray) -> Self {447		Self::Extended(Cc::new(value))448	}449}450451#[derive(Trace, Debug, Clone)]452pub struct LazyArray(pub Cc<Vec<Thunk<Val>>>);453#[cfg(feature = "nightly")]454type LazyArrayIter<'t> = impl DoubleEndedIterator<Item = Result<Val>> + ExactSizeIterator + 't;455#[cfg(feature = "nightly")]456type LazyArrayLazyIter<'t> = impl DoubleEndedIterator<Item = Thunk<Val>> + ExactSizeIterator + 't;457#[cfg(feature = "nightly")]458type LazyArrayCheapIter<'t> = iter::Empty<Val>;459impl ArrayLike for LazyArray {460	#[cfg(feature = "nightly")]461	type Iter<'t> = LazyArrayIter<'t>;462463	#[cfg(feature = "nightly")]464	type IterLazy<'t> = LazyArrayLazyIter<'t>;465466	#[cfg(feature = "nightly")]467	type IterCheap<'t> = LazyArrayCheapIter<'t>;468469	fn len(&self) -> usize {470		self.0.len()471	}472	fn get(&self, index: usize) -> Result<Option<Val>> {473		let Some(v) = self.0.get(index) else {474			return Ok(None);475		};476		v.evaluate().map(Some)477	}478	fn get_cheap(&self, _index: usize) -> Option<Val> {479		None480	}481	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {482		self.0.get(index).cloned()483	}484	#[cfg(feature = "nightly")]485	fn iter(&self) -> LazyArrayIter<'_> {486		self.0.iter().map(Thunk::evaluate)487	}488	#[cfg(feature = "nightly")]489	fn iter_lazy(&self) -> LazyArrayLazyIter<'_> {490		self.0.iter().cloned()491	}492	#[cfg(feature = "nightly")]493	fn iter_cheap(&self) -> Option<LazyArrayCheapIter<'_>> {494		None495	}496}497impl From<LazyArray> for ArrValue {498	fn from(value: LazyArray) -> Self {499		Self::Lazy(value)500	}501}502503#[derive(Trace, Debug, Clone)]504pub struct EagerArray(pub Cc<Vec<Val>>);505#[cfg(feature = "nightly")]506type EagerArrayIter<'t> = impl DoubleEndedIterator<Item = Result<Val>> + ExactSizeIterator + 't;507#[cfg(feature = "nightly")]508type EagerArrayLazyIter<'t> = impl DoubleEndedIterator<Item = Thunk<Val>> + ExactSizeIterator + 't;509#[cfg(feature = "nightly")]510type EagerArrayCheapIter<'t> = impl DoubleEndedIterator<Item = Val> + ExactSizeIterator + 't;511impl ArrayLike for EagerArray {512	#[cfg(feature = "nightly")]513	type Iter<'t> = EagerArrayIter<'t>;514515	#[cfg(feature = "nightly")]516	type IterLazy<'t> = EagerArrayLazyIter<'t>;517518	#[cfg(feature = "nightly")]519	type IterCheap<'t> = EagerArrayCheapIter<'t>;520521	fn len(&self) -> usize {522		self.0.len()523	}524525	fn get(&self, index: usize) -> Result<Option<Val>> {526		Ok(self.0.get(index).cloned())527	}528529	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {530		self.0.get(index).cloned().map(Thunk::evaluated)531	}532533	fn get_cheap(&self, index: usize) -> Option<Val> {534		self.0.get(index).cloned()535	}536537	#[cfg(feature = "nightly")]538	fn iter(&self) -> EagerArrayIter<'_> {539		self.0.iter().cloned().map(Ok)540	}541542	#[cfg(feature = "nightly")]543	fn iter_lazy(&self) -> EagerArrayLazyIter<'_> {544		self.0.iter().cloned().map(Thunk::evaluated)545	}546547	#[cfg(feature = "nightly")]548	fn iter_cheap(&self) -> Option<EagerArrayCheapIter<'_>> {549		Some(self.0.iter().cloned())550	}551}552impl From<EagerArray> for ArrValue {553	fn from(value: EagerArray) -> Self {554		Self::Eager(value)555	}556}557558/// Inclusive range type559#[derive(Debug, Trace, Clone, PartialEq, Eq)]560pub struct RangeArray {561	start: i32,562	end: i32,563}564impl RangeArray {565	pub fn empty() -> Self {566		Self::new_exclusive(0, 0)567	}568	pub fn new_exclusive(start: i32, end: i32) -> Self {569		end.checked_sub(1)570			.map_or_else(Self::empty, |end| Self { start, end })571	}572	pub fn new_inclusive(start: i32, end: i32) -> Self {573		Self { start, end }574	}575	fn range(&self) -> impl Iterator<Item = i32> + ExactSizeIterator + DoubleEndedIterator {576		WithExactSize(577			self.start..=self.end,578			(self.end as usize)579				.wrapping_sub(self.start as usize)580				.wrapping_add(1),581		)582	}583}584585#[cfg(feature = "nightly")]586type RangeArrayIter<'t> = impl DoubleEndedIterator<Item = Result<Val>> + ExactSizeIterator + 't;587#[cfg(feature = "nightly")]588type RangeArrayLazyIter<'t> = impl DoubleEndedIterator<Item = Thunk<Val>> + ExactSizeIterator + 't;589#[cfg(feature = "nightly")]590type RangeArrayCheapIter<'t> = impl DoubleEndedIterator<Item = Val> + ExactSizeIterator + 't;591impl ArrayLike for RangeArray {592	#[cfg(feature = "nightly")]593	type Iter<'t> = RangeArrayIter<'t>;594595	#[cfg(feature = "nightly")]596	type IterLazy<'t> = RangeArrayLazyIter<'t>;597598	#[cfg(feature = "nightly")]599	type IterCheap<'t> = RangeArrayCheapIter<'t>;600601	fn len(&self) -> usize {602		self.range().len()603	}604	fn is_empty(&self) -> bool {605		self.range().len() == 0606	}607608	fn get(&self, index: usize) -> Result<Option<Val>> {609		Ok(self.get_cheap(index))610	}611612	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {613		self.get_cheap(index).map(Thunk::evaluated)614	}615616	fn get_cheap(&self, index: usize) -> Option<Val> {617		self.range().nth(index).map(|i| Val::Num(f64::from(i)))618	}619620	#[cfg(feature = "nightly")]621	fn iter(&self) -> RangeArrayIter<'_> {622		self.range().map(|i| Ok(Val::Num(f64::from(i))))623	}624625	#[cfg(feature = "nightly")]626	fn iter_lazy(&self) -> RangeArrayLazyIter<'_> {627		self.range()628			.map(|i| Thunk::evaluated(Val::Num(f64::from(i))))629	}630631	#[cfg(feature = "nightly")]632	fn iter_cheap(&self) -> Option<RangeArrayCheapIter<'_>> {633		Some(self.range().map(|i| Val::Num(f64::from(i))))634	}635}636impl From<RangeArray> for ArrValue {637	fn from(value: RangeArray) -> Self {638		Self::Range(value)639	}640}641642#[derive(Debug, Trace, Clone)]643pub struct ReverseArray(pub ArrValue);644impl ArrayLike for ReverseArray {645	#[cfg(feature = "nightly")]646	type Iter<'t> = iter::Rev<UnknownArrayIter<'t>>;647648	#[cfg(feature = "nightly")]649	type IterLazy<'t> = iter::Rev<UnknownArrayIterLazy<'t>>;650651	#[cfg(feature = "nightly")]652	type IterCheap<'t> = iter::Rev<UnknownArrayIterCheap<'t>>;653654	fn len(&self) -> usize {655		self.0.len()656	}657658	fn get(&self, index: usize) -> Result<Option<Val>> {659		self.0.get(self.0.len() - index - 1)660	}661662	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {663		self.0.get_lazy(self.0.len() - index - 1)664	}665666	fn get_cheap(&self, index: usize) -> Option<Val> {667		self.0.get_cheap(self.0.len() - index - 1)668	}669670	#[cfg(feature = "nightly")]671	fn iter(&self) -> iter::Rev<UnknownArrayIter<'_>> {672		self.0.iter().rev()673	}674675	#[cfg(feature = "nightly")]676	fn iter_lazy(&self) -> iter::Rev<UnknownArrayIterLazy<'_>> {677		self.0.iter_lazy().rev()678	}679680	#[cfg(feature = "nightly")]681	fn iter_cheap(&self) -> Option<iter::Rev<UnknownArrayIterCheap<'_>>> {682		Some(self.0.iter_cheap()?.rev())683	}684	fn reverse(self) -> ArrValue {685		self.0686	}687}688impl From<ReverseArray> for ArrValue {689	fn from(value: ReverseArray) -> Self {690		Self::Reverse(Cc::new(value))691	}692}693694#[derive(Trace, Debug)]695pub struct MappedArrayInner {696	inner: ArrValue,697	cached: RefCell<Vec<ArrayThunk<()>>>,698	mapper: FuncVal,699}700#[derive(Trace, Debug, Clone)]701pub struct MappedArray(Cc<MappedArrayInner>);702impl MappedArray {703	pub fn new(inner: ArrValue, mapper: FuncVal) -> Self {704		let len = inner.len();705		Self(Cc::new(MappedArrayInner {706			inner,707			cached: RefCell::new(vec![ArrayThunk::Waiting(()); len]),708			mapper,709		}))710	}711}712#[cfg(feature = "nightly")]713type MappedArrayIter<'t> = impl DoubleEndedIterator<Item = Result<Val>> + ExactSizeIterator + 't;714#[cfg(feature = "nightly")]715type MappedArrayLazyIter<'t> = impl DoubleEndedIterator<Item = Thunk<Val>> + ExactSizeIterator + 't;716#[cfg(feature = "nightly")]717type MappedArrayCheapIter<'t> = iter::Empty<Val>;718impl ArrayLike for MappedArray {719	#[cfg(feature = "nightly")]720	type Iter<'t> = MappedArrayIter<'t>;721	#[cfg(feature = "nightly")]722	type IterLazy<'t> = MappedArrayLazyIter<'t>;723	#[cfg(feature = "nightly")]724	type IterCheap<'t> = MappedArrayCheapIter<'t>;725726	fn len(&self) -> usize {727		self.0.cached.borrow().len()728	}729730	fn get(&self, index: usize) -> Result<Option<Val>> {731		if index >= self.len() {732			return Ok(None);733		}734		match &self.0.cached.borrow()[index] {735			ArrayThunk::Computed(c) => return Ok(Some(c.clone())),736			ArrayThunk::Errored(e) => return Err(e.clone()),737			ArrayThunk::Pending => return Err(InfiniteRecursionDetected.into()),738			ArrayThunk::Waiting(..) => {}739		};740741		let ArrayThunk::Waiting(_) = replace(&mut self.0.cached.borrow_mut()[index], ArrayThunk::Pending) else {742			unreachable!()743		};744745		let val = self746			.0747			.inner748			.get(index)749			.transpose()750			.expect("index checked")751			.and_then(|r| self.0.mapper.evaluate_simple(&(Any(r),)));752753		let new_value = match val {754			Ok(v) => v,755			Err(e) => {756				self.0.cached.borrow_mut()[index] = ArrayThunk::Errored(e.clone());757				return Err(e);758			}759		};760		self.0.cached.borrow_mut()[index] = ArrayThunk::Computed(new_value.clone());761		Ok(Some(new_value))762	}763	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {764		#[derive(Trace)]765		struct ArrayElement {766			arr_thunk: MappedArray,767			index: usize,768		}769770		impl ThunkValue for ArrayElement {771			type Output = Val;772773			fn get(self: Box<Self>) -> Result<Self::Output> {774				self.arr_thunk775					.get(self.index)776					.transpose()777					.expect("index checked")778			}779		}780781		if index >= self.len() {782			return None;783		}784		match &self.0.cached.borrow()[index] {785			ArrayThunk::Computed(c) => return Some(Thunk::evaluated(c.clone())),786			ArrayThunk::Errored(e) => return Some(Thunk::errored(e.clone())),787			ArrayThunk::Waiting(_) | ArrayThunk::Pending => {}788		};789790		Some(Thunk::new(tb!(ArrayElement {791			arr_thunk: self.clone(),792			index,793		})))794	}795796	fn get_cheap(&self, _index: usize) -> Option<Val> {797		None798	}799800	#[cfg(feature = "nightly")]801	fn iter(&self) -> MappedArrayIter<'_> {802		(0..self.len()).map(|i| self.get(i).transpose().expect("length checked"))803	}804805	#[cfg(feature = "nightly")]806	fn iter_lazy(&self) -> MappedArrayLazyIter<'_> {807		(0..self.len()).map(|i| self.get_lazy(i).expect("length checked"))808	}809810	#[cfg(feature = "nightly")]811	fn iter_cheap(&self) -> Option<Self::IterCheap<'_>> {812		None813	}814}815impl From<MappedArray> for ArrValue {816	fn from(value: MappedArray) -> Self {817		Self::Mapped(value)818	}819}820821#[derive(Trace, Debug)]822pub struct RepeatedArrayInner {823	data: ArrValue,824	repeats: usize,825	total_len: usize,826}827#[derive(Trace, Debug, Clone)]828pub struct RepeatedArray(Cc<RepeatedArrayInner>);829impl RepeatedArray {830	pub fn new(data: ArrValue, repeats: usize) -> Option<Self> {831		let total_len = data.len().checked_mul(repeats)?;832		Some(Self(Cc::new(RepeatedArrayInner {833			data,834			repeats,835			total_len,836		})))837	}838	pub fn is_cheap(&self) -> bool {839		self.0.data.is_cheap()840	}841}842843#[cfg(feature = "nightly")]844type RepeatedArrayIter<'t> = impl DoubleEndedIterator<Item = Result<Val>> + ExactSizeIterator + 't;845#[cfg(feature = "nightly")]846type RepeatedArrayLazyIter<'t> =847	impl DoubleEndedIterator<Item = Thunk<Val>> + ExactSizeIterator + 't;848#[cfg(feature = "nightly")]849type RepeatedArrayCheapIter<'t> = impl DoubleEndedIterator<Item = Val> + ExactSizeIterator + 't;850impl ArrayLike for RepeatedArray {851	#[cfg(feature = "nightly")]852	type Iter<'t> = RepeatedArrayIter<'t>;853	#[cfg(feature = "nightly")]854	type IterLazy<'t> = RepeatedArrayLazyIter<'t>;855	#[cfg(feature = "nightly")]856	type IterCheap<'t> = RepeatedArrayCheapIter<'t>;857858	fn len(&self) -> usize {859		self.0.total_len860	}861862	fn get(&self, index: usize) -> Result<Option<Val>> {863		if index > self.0.total_len {864			return Ok(None);865		}866		self.0.data.get(index % self.0.data.len())867	}868869	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {870		if index > self.0.total_len {871			return None;872		}873		self.0.data.get_lazy(index % self.0.data.len())874	}875876	fn get_cheap(&self, index: usize) -> Option<Val> {877		if index > self.0.total_len {878			return None;879		}880		self.0.data.get_cheap(index % self.0.data.len())881	}882883	#[cfg(feature = "nightly")]884	fn iter(&self) -> RepeatedArrayIter<'_> {885		(0..self.0.total_len)886			.map(|i| self.get(i))887			.map(Result::transpose)888			.map(Option::unwrap)889	}890891	#[cfg(feature = "nightly")]892	fn iter_lazy(&self) -> RepeatedArrayLazyIter<'_> {893		(0..self.0.total_len)894			.map(|i| self.get_lazy(i))895			.map(Option::unwrap)896	}897898	#[cfg(feature = "nightly")]899	fn iter_cheap(&self) -> Option<RepeatedArrayCheapIter<'_>> {900		if !self.0.data.is_cheap() {901			return None;902		}903		Some(904			(0..self.0.total_len)905				.map(|i| self.get_cheap(i))906				.map(Option::unwrap),907		)908	}909}910impl From<RepeatedArray> for ArrValue {911	fn from(value: RepeatedArray) -> Self {912		Self::Repeated(value)913	}914}915916#[cfg(feature = "nightly")]917macro_rules! impl_iter_enum {918	($n:ident => $v:ident) => {919		pub enum $n<'t> {920			Bytes(<BytesArray as ArrayLike>::$v<'t>),921			Expr(<ExprArray as ArrayLike>::$v<'t>),922			Lazy(<LazyArray as ArrayLike>::$v<'t>),923			Eager(<EagerArray as ArrayLike>::$v<'t>),924			Range(<RangeArray as ArrayLike>::$v<'t>),925			Slice(Box<<SliceArray as ArrayLike>::$v<'t>>),926			Extended(Box<<ExtendedArray as ArrayLike>::$v<'t>>),927			Reverse(Box<<ReverseArray as ArrayLike>::$v<'t>>),928			Mapped(Box<<MappedArray as ArrayLike>::$v<'t>>),929			Repeated(Box<<RepeatedArray as ArrayLike>::$v<'t>>),930		}931	};932}933934macro_rules! pass {935	($t:ident.$m:ident($($ident:ident),*)) => {936		match $t {937			Self::Bytes(e) => e.$m($($ident)*),938			Self::Expr(e) => e.$m($($ident)*),939			Self::Lazy(e) => e.$m($($ident)*),940			Self::Eager(e) => e.$m($($ident)*),941			Self::Range(e) => e.$m($($ident)*),942			Self::Slice(e) => e.$m($($ident)*),943			Self::Extended(e) => e.$m($($ident)*),944			Self::Reverse(e) => e.$m($($ident)*),945			Self::Mapped(e) => e.$m($($ident)*),946			Self::Repeated(e) => e.$m($($ident)*),947		}948	};949}950pub(super) use pass;951952#[cfg(feature = "nightly")]953macro_rules! pass_iter_call {954	($t:ident.$c:ident $(in $wrap:ident)? => $e:ident) => {955		match $t {956			ArrValue::Bytes(e) => $e::Bytes($($wrap!)?(e.$c())),957			ArrValue::Lazy(e) => $e::Lazy($($wrap!)?(e.$c())),958			ArrValue::Expr(e) => $e::Expr($($wrap!)?(e.$c())),959			ArrValue::Eager(e) => $e::Eager($($wrap!)?(e.$c())),960			ArrValue::Range(e) => $e::Range($($wrap!)?(e.$c())),961			ArrValue::Slice(e) => $e::Slice(Box::new($($wrap!)?(e.$c()))),962			ArrValue::Extended(e) => $e::Extended(Box::new($($wrap!)?(e.$c()))),963			ArrValue::Reverse(e) => $e::Reverse(Box::new($($wrap!)?(e.$c()))),964			ArrValue::Mapped(e) => $e::Mapped(Box::new($($wrap!)?(e.$c()))),965			ArrValue::Repeated(e) => $e::Repeated(Box::new($($wrap!)?(e.$c()))),966		}967	};968}969#[cfg(feature = "nightly")]970pub(super) use pass_iter_call;971972#[cfg(feature = "nightly")]973macro_rules! impl_iter {974	($t:ident => $out:ty) => {975		impl Iterator for $t<'_> {976			type Item = $out;977978			fn next(&mut self) -> Option<Self::Item> {979				pass!(self.next())980			}981			fn nth(&mut self, count: usize) -> Option<Self::Item> {982				pass!(self.nth(count))983			}984			fn size_hint(&self) -> (usize, Option<usize>) {985				pass!(self.size_hint())986			}987		}988		impl DoubleEndedIterator for $t<'_> {989			fn next_back(&mut self) -> Option<Self::Item> {990				pass!(self.next_back())991			}992			fn nth_back(&mut self, count: usize) -> Option<Self::Item> {993				pass!(self.nth_back(count))994			}995		}996		impl ExactSizeIterator for $t<'_> {997			fn len(&self) -> usize {998				match self {999					Self::Bytes(e) => e.len(),1000					Self::Expr(e) => e.len(),1001					Self::Lazy(e) => e.len(),1002					Self::Eager(e) => e.len(),1003					Self::Range(e) => e.len(),1004					Self::Slice(e) => e.len(),1005					Self::Extended(e) => {1006						e.size_hint().1.expect("overflow is checked in constructor")1007					}1008					Self::Reverse(e) => e.len(),1009					Self::Mapped(e) => e.len(),1010					Self::Repeated(e) => e.len(),1011				}1012			}1013		}1014	};1015}10161017#[cfg(feature = "nightly")]1018impl_iter_enum!(UnknownArrayIter => Iter);1019#[cfg(feature = "nightly")]1020impl_iter!(UnknownArrayIter => Result<Val>);1021#[cfg(feature = "nightly")]1022impl_iter_enum!(UnknownArrayIterLazy => IterLazy);1023#[cfg(feature = "nightly")]1024impl_iter!(UnknownArrayIterLazy => Thunk<Val>);1025#[cfg(feature = "nightly")]1026impl_iter_enum!(UnknownArrayIterCheap => IterCheap);1027#[cfg(feature = "nightly")]1028impl_iter!(UnknownArrayIterCheap => Val);
after · crates/jrsonnet-evaluator/src/arr/spec.rs
1//! Those implementations are a bit sketchy, as this is mostly performance experiments2//! of not yet finished nightly rust features34use std::{cell::RefCell, iter, mem::replace};56use jrsonnet_gcmodule::{Cc, Trace};7use jrsonnet_interner::IBytes;8use jrsonnet_parser::LocExpr;910use super::ArrValue;11use crate::{12	error::ErrorKind::InfiniteRecursionDetected, evaluate, function::FuncVal, val::ThunkValue,13	Context, Error, Result, Thunk, Val,14};1516pub trait ArrayLike: Sized + Into<ArrValue> {17	#[cfg(feature = "nightly")]18	type Iter<'t>19	where20		Self: 't;21	#[cfg(feature = "nightly")]22	type IterLazy<'t>23	where24		Self: 't;25	#[cfg(feature = "nightly")]26	type IterCheap<'t>27	where28		Self: 't;2930	fn len(&self) -> usize;31	fn is_empty(&self) -> bool {32		self.len() == 033	}34	fn get(&self, index: usize) -> Result<Option<Val>>;35	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>>;36	fn get_cheap(&self, index: usize) -> Option<Val>;37	#[cfg(feature = "nightly")]38	#[allow(clippy::iter_not_returning_iterator)]39	fn iter(&self) -> Self::Iter<'_>;40	#[cfg(feature = "nightly")]41	fn iter_lazy(&self) -> Self::IterLazy<'_>;42	#[cfg(feature = "nightly")]43	fn iter_cheap(&self) -> Option<Self::IterCheap<'_>>;4445	fn reverse(self) -> ArrValue {46		ArrValue::Reverse(Cc::new(ReverseArray(self.into())))47	}48}4950#[derive(Debug, Clone, Trace)]51pub struct SliceArray {52	pub(crate) inner: ArrValue,53	pub(crate) from: u32,54	pub(crate) to: u32,55	pub(crate) step: u32,56}5758impl SliceArray {59	#[cfg(not(feature = "nightly"))]60	fn iter(&self) -> impl Iterator<Item = Result<Val>> + '_ {61		self.inner62			.iter()63			.skip(self.from as usize)64			.take((self.to - self.from) as usize)65			.step_by(self.step as usize)66	}6768	#[cfg(not(feature = "nightly"))]69	fn iter_lazy(&self) -> impl Iterator<Item = Thunk<Val>> + '_ {70		self.inner71			.iter_lazy()72			.skip(self.from as usize)73			.take((self.to - self.from) as usize)74			.step_by(self.step as usize)75	}7677	#[cfg(not(feature = "nightly"))]78	fn iter_cheap(&self) -> Option<impl crate::arr::ArrayLikeIter<Val> + '_> {79		Some(80			self.inner81				.iter_cheap()?82				.skip(self.from as usize)83				.take((self.to - self.from) as usize)84				.step_by(self.step as usize),85		)86	}87}88#[cfg(feature = "nightly")]89type SliceArrayIter<'t> = impl DoubleEndedIterator<Item = Result<Val>> + ExactSizeIterator + 't;90#[cfg(feature = "nightly")]91type SliceArrayLazyIter<'t> = impl DoubleEndedIterator<Item = Thunk<Val>> + ExactSizeIterator + 't;92#[cfg(feature = "nightly")]93type SliceArrayCheapIter<'t> = impl DoubleEndedIterator<Item = Val> + ExactSizeIterator + 't;94impl ArrayLike for SliceArray {95	#[cfg(feature = "nightly")]96	type Iter<'t> = SliceArrayIter<'t>;97	#[cfg(feature = "nightly")]98	type IterLazy<'t> = SliceArrayLazyIter<'t>;99	#[cfg(feature = "nightly")]100	type IterCheap<'t> = SliceArrayCheapIter<'t>;101102	fn len(&self) -> usize {103		iter::repeat(())104			.take((self.to - self.from) as usize)105			.step_by(self.step as usize)106			.count()107	}108109	fn get(&self, index: usize) -> Result<Option<Val>> {110		self.iter().nth(index).transpose()111	}112113	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {114		self.iter_lazy().nth(index)115	}116117	fn get_cheap(&self, index: usize) -> Option<Val> {118		self.iter_cheap()?.nth(index)119	}120121	#[cfg(feature = "nightly")]122	fn iter(&self) -> SliceArrayIter<'_> {123		self.inner124			.iter()125			.skip(self.from as usize)126			.take((self.to - self.from) as usize)127			.step_by(self.step as usize)128	}129130	#[cfg(feature = "nightly")]131	fn iter_lazy(&self) -> SliceArrayLazyIter<'_> {132		self.inner133			.iter_lazy()134			.skip(self.from as usize)135			.take((self.to - self.from) as usize)136			.step_by(self.step as usize)137	}138139	#[cfg(feature = "nightly")]140	fn iter_cheap(&self) -> Option<SliceArrayCheapIter<'_>> {141		Some(142			self.inner143				.iter_cheap()?144				.skip(self.from as usize)145				.take((self.to - self.from) as usize)146				.step_by(self.step as usize),147		)148	}149}150impl From<SliceArray> for ArrValue {151	fn from(value: SliceArray) -> Self {152		Self::Slice(Cc::new(value))153	}154}155156#[derive(Trace, Debug, Clone)]157pub struct BytesArray(pub IBytes);158#[cfg(feature = "nightly")]159type BytesArrayIter<'t> = impl DoubleEndedIterator<Item = Result<Val>> + ExactSizeIterator + 't;160#[cfg(feature = "nightly")]161type BytesArrayLazyIter<'t> = impl DoubleEndedIterator<Item = Thunk<Val>> + ExactSizeIterator + 't;162#[cfg(feature = "nightly")]163type BytesArrayCheapIter<'t> = impl DoubleEndedIterator<Item = Val> + ExactSizeIterator + 't;164impl ArrayLike for BytesArray {165	#[cfg(feature = "nightly")]166	type Iter<'t> = BytesArrayIter<'t>;167	#[cfg(feature = "nightly")]168	type IterLazy<'t> = BytesArrayLazyIter<'t>;169	#[cfg(feature = "nightly")]170	type IterCheap<'t> = BytesArrayCheapIter<'t>;171172	fn len(&self) -> usize {173		self.0.len()174	}175176	fn get(&self, index: usize) -> Result<Option<Val>> {177		Ok(self.get_cheap(index))178	}179180	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {181		self.get_cheap(index).map(Thunk::evaluated)182	}183184	fn get_cheap(&self, index: usize) -> Option<Val> {185		self.0.get(index).map(|v| Val::Num(f64::from(*v)))186	}187188	#[cfg(feature = "nightly")]189	fn iter(&self) -> BytesArrayIter<'_> {190		self.0.iter().map(|v| Ok(Val::Num(f64::from(*v))))191	}192193	#[cfg(feature = "nightly")]194	fn iter_lazy(&self) -> BytesArrayLazyIter<'_> {195		self.0196			.iter()197			.map(|v| Thunk::evaluated(Val::Num(f64::from(*v))))198	}199200	#[cfg(feature = "nightly")]201	fn iter_cheap(&self) -> Option<BytesArrayCheapIter<'_>> {202		Some(self.0.iter().map(|v| Val::Num(f64::from(*v))))203	}204}205impl From<BytesArray> for ArrValue {206	fn from(value: BytesArray) -> Self {207		ArrValue::Bytes(value)208	}209}210211#[derive(Debug, Trace, Clone)]212enum ArrayThunk<T: 'static + Trace> {213	Computed(Val),214	Errored(Error),215	Waiting(T),216	Pending,217}218219#[derive(Debug, Trace)]220pub struct ExprArrayInner {221	ctx: Context,222	cached: RefCell<Vec<ArrayThunk<LocExpr>>>,223}224#[derive(Debug, Trace, Clone)]225pub struct ExprArray(pub Cc<ExprArrayInner>);226impl ExprArray {227	pub fn new(ctx: Context, items: impl IntoIterator<Item = LocExpr>) -> Self {228		Self(Cc::new(ExprArrayInner {229			ctx,230			cached: RefCell::new(items.into_iter().map(ArrayThunk::Waiting).collect()),231		}))232	}233}234#[cfg(feature = "nightly")]235type ExprArrayIter<'t> = impl DoubleEndedIterator<Item = Result<Val>> + ExactSizeIterator + 't;236#[cfg(feature = "nightly")]237type ExprArrayLazyIter<'t> = impl DoubleEndedIterator<Item = Thunk<Val>> + ExactSizeIterator + 't;238#[cfg(feature = "nightly")]239type ExprArrayCheapIter<'t> = iter::Empty<Val>;240impl ArrayLike for ExprArray {241	#[cfg(feature = "nightly")]242	type Iter<'t> = ExprArrayIter<'t>;243	#[cfg(feature = "nightly")]244	type IterLazy<'t> = ExprArrayLazyIter<'t>;245	#[cfg(feature = "nightly")]246	type IterCheap<'t> = ExprArrayCheapIter<'t>;247248	fn len(&self) -> usize {249		self.0.cached.borrow().len()250	}251	fn get(&self, index: usize) -> Result<Option<Val>> {252		if index >= self.len() {253			return Ok(None);254		}255		match &self.0.cached.borrow()[index] {256			ArrayThunk::Computed(c) => return Ok(Some(c.clone())),257			ArrayThunk::Errored(e) => return Err(e.clone()),258			ArrayThunk::Pending => return Err(InfiniteRecursionDetected.into()),259			ArrayThunk::Waiting(..) => {}260		};261262		let ArrayThunk::Waiting(expr) = replace(&mut self.0.cached.borrow_mut()[index], ArrayThunk::Pending) else {263			unreachable!()264		};265266		let new_value = match evaluate(self.0.ctx.clone(), &expr) {267			Ok(v) => v,268			Err(e) => {269				self.0.cached.borrow_mut()[index] = ArrayThunk::Errored(e.clone());270				return Err(e);271			}272		};273		self.0.cached.borrow_mut()[index] = ArrayThunk::Computed(new_value.clone());274		Ok(Some(new_value))275	}276	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {277		#[derive(Trace)]278		struct ArrayElement {279			arr_thunk: ExprArray,280			index: usize,281		}282283		impl ThunkValue for ArrayElement {284			type Output = Val;285286			fn get(self: Box<Self>) -> Result<Self::Output> {287				self.arr_thunk288					.get(self.index)289					.transpose()290					.expect("index checked")291			}292		}293294		if index >= self.len() {295			return None;296		}297		match &self.0.cached.borrow()[index] {298			ArrayThunk::Computed(c) => return Some(Thunk::evaluated(c.clone())),299			ArrayThunk::Errored(e) => return Some(Thunk::errored(e.clone())),300			ArrayThunk::Waiting(_) | ArrayThunk::Pending => {}301		};302303		Some(Thunk::new(ArrayElement {304			arr_thunk: self.clone(),305			index,306		}))307	}308	fn get_cheap(&self, _index: usize) -> Option<Val> {309		None310	}311312	#[cfg(feature = "nightly")]313	fn iter(&self) -> ExprArrayIter<'_> {314		(0..self.len()).map(|i| self.get(i).transpose().expect("index checked"))315	}316	#[cfg(feature = "nightly")]317	fn iter_lazy(&self) -> ExprArrayLazyIter<'_> {318		(0..self.len()).map(|i| self.get_lazy(i).expect("index checked"))319	}320	#[cfg(feature = "nightly")]321	fn iter_cheap(&self) -> Option<Self::IterCheap<'_>> {322		None323	}324}325impl From<ExprArray> for ArrValue {326	fn from(value: ExprArray) -> Self {327		Self::Expr(value)328	}329}330331#[derive(Trace, Debug, Clone)]332pub struct ExtendedArray {333	pub a: ArrValue,334	pub b: ArrValue,335	split: usize,336	len: usize,337}338#[cfg(feature = "nightly")]339340type ExtendedArrayIter<'t> = impl DoubleEndedIterator<Item = Result<Val>> + ExactSizeIterator + 't;341#[cfg(feature = "nightly")]342type ExtendedArrayLazyIter<'t> =343	impl DoubleEndedIterator<Item = Thunk<Val>> + ExactSizeIterator + 't;344#[cfg(feature = "nightly")]345type ExtendedArrayCheapIter<'t> = impl DoubleEndedIterator<Item = Val> + ExactSizeIterator + 't;346impl ExtendedArray {347	pub fn new(a: ArrValue, b: ArrValue) -> Self {348		let a_len = a.len();349		let b_len = b.len();350		Self {351			a,352			b,353			split: a_len,354			len: a_len.checked_add(b_len).expect("too large array value"),355		}356	}357}358359struct WithExactSize<I>(I, usize);360impl<I, T> Iterator for WithExactSize<I>361where362	I: Iterator<Item = T>,363{364	type Item = T;365366	fn next(&mut self) -> Option<Self::Item> {367		self.0.next()368	}369	fn nth(&mut self, n: usize) -> Option<Self::Item> {370		self.0.nth(n)371	}372	fn size_hint(&self) -> (usize, Option<usize>) {373		(self.1, Some(self.1))374	}375}376impl<I> DoubleEndedIterator for WithExactSize<I>377where378	I: DoubleEndedIterator,379{380	fn next_back(&mut self) -> Option<Self::Item> {381		self.0.next_back()382	}383	fn nth_back(&mut self, n: usize) -> Option<Self::Item> {384		self.0.nth_back(n)385	}386}387impl<I> ExactSizeIterator for WithExactSize<I>388where389	I: Iterator,390{391	fn len(&self) -> usize {392		self.1393	}394}395impl ArrayLike for ExtendedArray {396	#[cfg(feature = "nightly")]397	type Iter<'t> = ExtendedArrayIter<'t>;398	#[cfg(feature = "nightly")]399	type IterLazy<'t> = ExtendedArrayLazyIter<'t>;400	#[cfg(feature = "nightly")]401	type IterCheap<'t> = ExtendedArrayCheapIter<'t>;402403	fn get(&self, index: usize) -> Result<Option<Val>> {404		if self.split > index {405			self.a.get(index)406		} else {407			self.b.get(index - self.split)408		}409	}410	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {411		if self.split > index {412			self.a.get_lazy(index)413		} else {414			self.b.get_lazy(index - self.split)415		}416	}417418	fn len(&self) -> usize {419		self.len420	}421422	fn get_cheap(&self, index: usize) -> Option<Val> {423		if self.split > index {424			self.a.get_cheap(index)425		} else {426			self.b.get_cheap(index - self.split)427		}428	}429430	#[cfg(feature = "nightly")]431	fn iter(&self) -> ExtendedArrayIter<'_> {432		WithExactSize(self.a.iter().chain(self.b.iter()), self.len)433	}434	#[cfg(feature = "nightly")]435	fn iter_lazy(&self) -> ExtendedArrayLazyIter<'_> {436		WithExactSize(self.a.iter_lazy().chain(self.b.iter_lazy()), self.len)437	}438	#[cfg(feature = "nightly")]439	fn iter_cheap(&self) -> Option<ExtendedArrayCheapIter<'_>> {440		let a = self.a.iter_cheap()?;441		let b = self.b.iter_cheap()?;442		Some(WithExactSize(a.chain(b), self.len))443	}444}445impl From<ExtendedArray> for ArrValue {446	fn from(value: ExtendedArray) -> Self {447		Self::Extended(Cc::new(value))448	}449}450451#[derive(Trace, Debug, Clone)]452pub struct LazyArray(pub Cc<Vec<Thunk<Val>>>);453#[cfg(feature = "nightly")]454type LazyArrayIter<'t> = impl DoubleEndedIterator<Item = Result<Val>> + ExactSizeIterator + 't;455#[cfg(feature = "nightly")]456type LazyArrayLazyIter<'t> = impl DoubleEndedIterator<Item = Thunk<Val>> + ExactSizeIterator + 't;457#[cfg(feature = "nightly")]458type LazyArrayCheapIter<'t> = iter::Empty<Val>;459impl ArrayLike for LazyArray {460	#[cfg(feature = "nightly")]461	type Iter<'t> = LazyArrayIter<'t>;462463	#[cfg(feature = "nightly")]464	type IterLazy<'t> = LazyArrayLazyIter<'t>;465466	#[cfg(feature = "nightly")]467	type IterCheap<'t> = LazyArrayCheapIter<'t>;468469	fn len(&self) -> usize {470		self.0.len()471	}472	fn get(&self, index: usize) -> Result<Option<Val>> {473		let Some(v) = self.0.get(index) else {474			return Ok(None);475		};476		v.evaluate().map(Some)477	}478	fn get_cheap(&self, _index: usize) -> Option<Val> {479		None480	}481	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {482		self.0.get(index).cloned()483	}484	#[cfg(feature = "nightly")]485	fn iter(&self) -> LazyArrayIter<'_> {486		self.0.iter().map(Thunk::evaluate)487	}488	#[cfg(feature = "nightly")]489	fn iter_lazy(&self) -> LazyArrayLazyIter<'_> {490		self.0.iter().cloned()491	}492	#[cfg(feature = "nightly")]493	fn iter_cheap(&self) -> Option<LazyArrayCheapIter<'_>> {494		None495	}496}497impl From<LazyArray> for ArrValue {498	fn from(value: LazyArray) -> Self {499		Self::Lazy(value)500	}501}502503#[derive(Trace, Debug, Clone)]504pub struct EagerArray(pub Cc<Vec<Val>>);505#[cfg(feature = "nightly")]506type EagerArrayIter<'t> = impl DoubleEndedIterator<Item = Result<Val>> + ExactSizeIterator + 't;507#[cfg(feature = "nightly")]508type EagerArrayLazyIter<'t> = impl DoubleEndedIterator<Item = Thunk<Val>> + ExactSizeIterator + 't;509#[cfg(feature = "nightly")]510type EagerArrayCheapIter<'t> = impl DoubleEndedIterator<Item = Val> + ExactSizeIterator + 't;511impl ArrayLike for EagerArray {512	#[cfg(feature = "nightly")]513	type Iter<'t> = EagerArrayIter<'t>;514515	#[cfg(feature = "nightly")]516	type IterLazy<'t> = EagerArrayLazyIter<'t>;517518	#[cfg(feature = "nightly")]519	type IterCheap<'t> = EagerArrayCheapIter<'t>;520521	fn len(&self) -> usize {522		self.0.len()523	}524525	fn get(&self, index: usize) -> Result<Option<Val>> {526		Ok(self.0.get(index).cloned())527	}528529	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {530		self.0.get(index).cloned().map(Thunk::evaluated)531	}532533	fn get_cheap(&self, index: usize) -> Option<Val> {534		self.0.get(index).cloned()535	}536537	#[cfg(feature = "nightly")]538	fn iter(&self) -> EagerArrayIter<'_> {539		self.0.iter().cloned().map(Ok)540	}541542	#[cfg(feature = "nightly")]543	fn iter_lazy(&self) -> EagerArrayLazyIter<'_> {544		self.0.iter().cloned().map(Thunk::evaluated)545	}546547	#[cfg(feature = "nightly")]548	fn iter_cheap(&self) -> Option<EagerArrayCheapIter<'_>> {549		Some(self.0.iter().cloned())550	}551}552impl From<EagerArray> for ArrValue {553	fn from(value: EagerArray) -> Self {554		Self::Eager(value)555	}556}557558/// Inclusive range type559#[derive(Debug, Trace, Clone, PartialEq, Eq)]560pub struct RangeArray {561	start: i32,562	end: i32,563}564impl RangeArray {565	pub fn empty() -> Self {566		Self::new_exclusive(0, 0)567	}568	pub fn new_exclusive(start: i32, end: i32) -> Self {569		end.checked_sub(1)570			.map_or_else(Self::empty, |end| Self { start, end })571	}572	pub fn new_inclusive(start: i32, end: i32) -> Self {573		Self { start, end }574	}575	fn range(&self) -> impl Iterator<Item = i32> + ExactSizeIterator + DoubleEndedIterator {576		WithExactSize(577			self.start..=self.end,578			(self.end as usize)579				.wrapping_sub(self.start as usize)580				.wrapping_add(1),581		)582	}583}584585#[cfg(feature = "nightly")]586type RangeArrayIter<'t> = impl DoubleEndedIterator<Item = Result<Val>> + ExactSizeIterator + 't;587#[cfg(feature = "nightly")]588type RangeArrayLazyIter<'t> = impl DoubleEndedIterator<Item = Thunk<Val>> + ExactSizeIterator + 't;589#[cfg(feature = "nightly")]590type RangeArrayCheapIter<'t> = impl DoubleEndedIterator<Item = Val> + ExactSizeIterator + 't;591impl ArrayLike for RangeArray {592	#[cfg(feature = "nightly")]593	type Iter<'t> = RangeArrayIter<'t>;594595	#[cfg(feature = "nightly")]596	type IterLazy<'t> = RangeArrayLazyIter<'t>;597598	#[cfg(feature = "nightly")]599	type IterCheap<'t> = RangeArrayCheapIter<'t>;600601	fn len(&self) -> usize {602		self.range().len()603	}604	fn is_empty(&self) -> bool {605		self.range().len() == 0606	}607608	fn get(&self, index: usize) -> Result<Option<Val>> {609		Ok(self.get_cheap(index))610	}611612	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {613		self.get_cheap(index).map(Thunk::evaluated)614	}615616	fn get_cheap(&self, index: usize) -> Option<Val> {617		self.range().nth(index).map(|i| Val::Num(f64::from(i)))618	}619620	#[cfg(feature = "nightly")]621	fn iter(&self) -> RangeArrayIter<'_> {622		self.range().map(|i| Ok(Val::Num(f64::from(i))))623	}624625	#[cfg(feature = "nightly")]626	fn iter_lazy(&self) -> RangeArrayLazyIter<'_> {627		self.range()628			.map(|i| Thunk::evaluated(Val::Num(f64::from(i))))629	}630631	#[cfg(feature = "nightly")]632	fn iter_cheap(&self) -> Option<RangeArrayCheapIter<'_>> {633		Some(self.range().map(|i| Val::Num(f64::from(i))))634	}635}636impl From<RangeArray> for ArrValue {637	fn from(value: RangeArray) -> Self {638		Self::Range(value)639	}640}641642#[derive(Debug, Trace, Clone)]643pub struct ReverseArray(pub ArrValue);644impl ArrayLike for ReverseArray {645	#[cfg(feature = "nightly")]646	type Iter<'t> = iter::Rev<UnknownArrayIter<'t>>;647648	#[cfg(feature = "nightly")]649	type IterLazy<'t> = iter::Rev<UnknownArrayIterLazy<'t>>;650651	#[cfg(feature = "nightly")]652	type IterCheap<'t> = iter::Rev<UnknownArrayIterCheap<'t>>;653654	fn len(&self) -> usize {655		self.0.len()656	}657658	fn get(&self, index: usize) -> Result<Option<Val>> {659		self.0.get(self.0.len() - index - 1)660	}661662	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {663		self.0.get_lazy(self.0.len() - index - 1)664	}665666	fn get_cheap(&self, index: usize) -> Option<Val> {667		self.0.get_cheap(self.0.len() - index - 1)668	}669670	#[cfg(feature = "nightly")]671	fn iter(&self) -> iter::Rev<UnknownArrayIter<'_>> {672		self.0.iter().rev()673	}674675	#[cfg(feature = "nightly")]676	fn iter_lazy(&self) -> iter::Rev<UnknownArrayIterLazy<'_>> {677		self.0.iter_lazy().rev()678	}679680	#[cfg(feature = "nightly")]681	fn iter_cheap(&self) -> Option<iter::Rev<UnknownArrayIterCheap<'_>>> {682		Some(self.0.iter_cheap()?.rev())683	}684	fn reverse(self) -> ArrValue {685		self.0686	}687}688impl From<ReverseArray> for ArrValue {689	fn from(value: ReverseArray) -> Self {690		Self::Reverse(Cc::new(value))691	}692}693694#[derive(Trace, Debug)]695pub struct MappedArrayInner {696	inner: ArrValue,697	cached: RefCell<Vec<ArrayThunk<()>>>,698	mapper: FuncVal,699}700#[derive(Trace, Debug, Clone)]701pub struct MappedArray(Cc<MappedArrayInner>);702impl MappedArray {703	pub fn new(inner: ArrValue, mapper: FuncVal) -> Self {704		let len = inner.len();705		Self(Cc::new(MappedArrayInner {706			inner,707			cached: RefCell::new(vec![ArrayThunk::Waiting(()); len]),708			mapper,709		}))710	}711}712#[cfg(feature = "nightly")]713type MappedArrayIter<'t> = impl DoubleEndedIterator<Item = Result<Val>> + ExactSizeIterator + 't;714#[cfg(feature = "nightly")]715type MappedArrayLazyIter<'t> = impl DoubleEndedIterator<Item = Thunk<Val>> + ExactSizeIterator + 't;716#[cfg(feature = "nightly")]717type MappedArrayCheapIter<'t> = iter::Empty<Val>;718impl ArrayLike for MappedArray {719	#[cfg(feature = "nightly")]720	type Iter<'t> = MappedArrayIter<'t>;721	#[cfg(feature = "nightly")]722	type IterLazy<'t> = MappedArrayLazyIter<'t>;723	#[cfg(feature = "nightly")]724	type IterCheap<'t> = MappedArrayCheapIter<'t>;725726	fn len(&self) -> usize {727		self.0.cached.borrow().len()728	}729730	fn get(&self, index: usize) -> Result<Option<Val>> {731		if index >= self.len() {732			return Ok(None);733		}734		match &self.0.cached.borrow()[index] {735			ArrayThunk::Computed(c) => return Ok(Some(c.clone())),736			ArrayThunk::Errored(e) => return Err(e.clone()),737			ArrayThunk::Pending => return Err(InfiniteRecursionDetected.into()),738			ArrayThunk::Waiting(..) => {}739		};740741		let ArrayThunk::Waiting(_) = replace(&mut self.0.cached.borrow_mut()[index], ArrayThunk::Pending) else {742			unreachable!()743		};744745		let val = self746			.0747			.inner748			.get(index)749			.transpose()750			.expect("index checked")751			.and_then(|r| self.0.mapper.evaluate_simple(&(r,)));752753		let new_value = match val {754			Ok(v) => v,755			Err(e) => {756				self.0.cached.borrow_mut()[index] = ArrayThunk::Errored(e.clone());757				return Err(e);758			}759		};760		self.0.cached.borrow_mut()[index] = ArrayThunk::Computed(new_value.clone());761		Ok(Some(new_value))762	}763	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {764		#[derive(Trace)]765		struct ArrayElement {766			arr_thunk: MappedArray,767			index: usize,768		}769770		impl ThunkValue for ArrayElement {771			type Output = Val;772773			fn get(self: Box<Self>) -> Result<Self::Output> {774				self.arr_thunk775					.get(self.index)776					.transpose()777					.expect("index checked")778			}779		}780781		if index >= self.len() {782			return None;783		}784		match &self.0.cached.borrow()[index] {785			ArrayThunk::Computed(c) => return Some(Thunk::evaluated(c.clone())),786			ArrayThunk::Errored(e) => return Some(Thunk::errored(e.clone())),787			ArrayThunk::Waiting(_) | ArrayThunk::Pending => {}788		};789790		Some(Thunk::new(ArrayElement {791			arr_thunk: self.clone(),792			index,793		}))794	}795796	fn get_cheap(&self, _index: usize) -> Option<Val> {797		None798	}799800	#[cfg(feature = "nightly")]801	fn iter(&self) -> MappedArrayIter<'_> {802		(0..self.len()).map(|i| self.get(i).transpose().expect("length checked"))803	}804805	#[cfg(feature = "nightly")]806	fn iter_lazy(&self) -> MappedArrayLazyIter<'_> {807		(0..self.len()).map(|i| self.get_lazy(i).expect("length checked"))808	}809810	#[cfg(feature = "nightly")]811	fn iter_cheap(&self) -> Option<Self::IterCheap<'_>> {812		None813	}814}815impl From<MappedArray> for ArrValue {816	fn from(value: MappedArray) -> Self {817		Self::Mapped(value)818	}819}820821#[derive(Trace, Debug)]822pub struct RepeatedArrayInner {823	data: ArrValue,824	repeats: usize,825	total_len: usize,826}827#[derive(Trace, Debug, Clone)]828pub struct RepeatedArray(Cc<RepeatedArrayInner>);829impl RepeatedArray {830	pub fn new(data: ArrValue, repeats: usize) -> Option<Self> {831		let total_len = data.len().checked_mul(repeats)?;832		Some(Self(Cc::new(RepeatedArrayInner {833			data,834			repeats,835			total_len,836		})))837	}838	pub fn is_cheap(&self) -> bool {839		self.0.data.is_cheap()840	}841}842843#[cfg(feature = "nightly")]844type RepeatedArrayIter<'t> = impl DoubleEndedIterator<Item = Result<Val>> + ExactSizeIterator + 't;845#[cfg(feature = "nightly")]846type RepeatedArrayLazyIter<'t> =847	impl DoubleEndedIterator<Item = Thunk<Val>> + ExactSizeIterator + 't;848#[cfg(feature = "nightly")]849type RepeatedArrayCheapIter<'t> = impl DoubleEndedIterator<Item = Val> + ExactSizeIterator + 't;850impl ArrayLike for RepeatedArray {851	#[cfg(feature = "nightly")]852	type Iter<'t> = RepeatedArrayIter<'t>;853	#[cfg(feature = "nightly")]854	type IterLazy<'t> = RepeatedArrayLazyIter<'t>;855	#[cfg(feature = "nightly")]856	type IterCheap<'t> = RepeatedArrayCheapIter<'t>;857858	fn len(&self) -> usize {859		self.0.total_len860	}861862	fn get(&self, index: usize) -> Result<Option<Val>> {863		if index > self.0.total_len {864			return Ok(None);865		}866		self.0.data.get(index % self.0.data.len())867	}868869	fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {870		if index > self.0.total_len {871			return None;872		}873		self.0.data.get_lazy(index % self.0.data.len())874	}875876	fn get_cheap(&self, index: usize) -> Option<Val> {877		if index > self.0.total_len {878			return None;879		}880		self.0.data.get_cheap(index % self.0.data.len())881	}882883	#[cfg(feature = "nightly")]884	fn iter(&self) -> RepeatedArrayIter<'_> {885		(0..self.0.total_len)886			.map(|i| self.get(i))887			.map(Result::transpose)888			.map(Option::unwrap)889	}890891	#[cfg(feature = "nightly")]892	fn iter_lazy(&self) -> RepeatedArrayLazyIter<'_> {893		(0..self.0.total_len)894			.map(|i| self.get_lazy(i))895			.map(Option::unwrap)896	}897898	#[cfg(feature = "nightly")]899	fn iter_cheap(&self) -> Option<RepeatedArrayCheapIter<'_>> {900		if !self.0.data.is_cheap() {901			return None;902		}903		Some(904			(0..self.0.total_len)905				.map(|i| self.get_cheap(i))906				.map(Option::unwrap),907		)908	}909}910impl From<RepeatedArray> for ArrValue {911	fn from(value: RepeatedArray) -> Self {912		Self::Repeated(value)913	}914}915916#[cfg(feature = "nightly")]917macro_rules! impl_iter_enum {918	($n:ident => $v:ident) => {919		pub enum $n<'t> {920			Bytes(<BytesArray as ArrayLike>::$v<'t>),921			Expr(<ExprArray as ArrayLike>::$v<'t>),922			Lazy(<LazyArray as ArrayLike>::$v<'t>),923			Eager(<EagerArray as ArrayLike>::$v<'t>),924			Range(<RangeArray as ArrayLike>::$v<'t>),925			Slice(Box<<SliceArray as ArrayLike>::$v<'t>>),926			Extended(Box<<ExtendedArray as ArrayLike>::$v<'t>>),927			Reverse(Box<<ReverseArray as ArrayLike>::$v<'t>>),928			Mapped(Box<<MappedArray as ArrayLike>::$v<'t>>),929			Repeated(Box<<RepeatedArray as ArrayLike>::$v<'t>>),930		}931	};932}933934macro_rules! pass {935	($t:ident.$m:ident($($ident:ident),*)) => {936		match $t {937			Self::Bytes(e) => e.$m($($ident)*),938			Self::Expr(e) => e.$m($($ident)*),939			Self::Lazy(e) => e.$m($($ident)*),940			Self::Eager(e) => e.$m($($ident)*),941			Self::Range(e) => e.$m($($ident)*),942			Self::Slice(e) => e.$m($($ident)*),943			Self::Extended(e) => e.$m($($ident)*),944			Self::Reverse(e) => e.$m($($ident)*),945			Self::Mapped(e) => e.$m($($ident)*),946			Self::Repeated(e) => e.$m($($ident)*),947		}948	};949}950pub(super) use pass;951952#[cfg(feature = "nightly")]953macro_rules! pass_iter_call {954	($t:ident.$c:ident $(in $wrap:ident)? => $e:ident) => {955		match $t {956			ArrValue::Bytes(e) => $e::Bytes($($wrap!)?(e.$c())),957			ArrValue::Lazy(e) => $e::Lazy($($wrap!)?(e.$c())),958			ArrValue::Expr(e) => $e::Expr($($wrap!)?(e.$c())),959			ArrValue::Eager(e) => $e::Eager($($wrap!)?(e.$c())),960			ArrValue::Range(e) => $e::Range($($wrap!)?(e.$c())),961			ArrValue::Slice(e) => $e::Slice(Box::new($($wrap!)?(e.$c()))),962			ArrValue::Extended(e) => $e::Extended(Box::new($($wrap!)?(e.$c()))),963			ArrValue::Reverse(e) => $e::Reverse(Box::new($($wrap!)?(e.$c()))),964			ArrValue::Mapped(e) => $e::Mapped(Box::new($($wrap!)?(e.$c()))),965			ArrValue::Repeated(e) => $e::Repeated(Box::new($($wrap!)?(e.$c()))),966		}967	};968}969#[cfg(feature = "nightly")]970pub(super) use pass_iter_call;971972#[cfg(feature = "nightly")]973macro_rules! impl_iter {974	($t:ident => $out:ty) => {975		impl Iterator for $t<'_> {976			type Item = $out;977978			fn next(&mut self) -> Option<Self::Item> {979				pass!(self.next())980			}981			fn nth(&mut self, count: usize) -> Option<Self::Item> {982				pass!(self.nth(count))983			}984			fn size_hint(&self) -> (usize, Option<usize>) {985				pass!(self.size_hint())986			}987		}988		impl DoubleEndedIterator for $t<'_> {989			fn next_back(&mut self) -> Option<Self::Item> {990				pass!(self.next_back())991			}992			fn nth_back(&mut self, count: usize) -> Option<Self::Item> {993				pass!(self.nth_back(count))994			}995		}996		impl ExactSizeIterator for $t<'_> {997			fn len(&self) -> usize {998				match self {999					Self::Bytes(e) => e.len(),1000					Self::Expr(e) => e.len(),1001					Self::Lazy(e) => e.len(),1002					Self::Eager(e) => e.len(),1003					Self::Range(e) => e.len(),1004					Self::Slice(e) => e.len(),1005					Self::Extended(e) => {1006						e.size_hint().1.expect("overflow is checked in constructor")1007					}1008					Self::Reverse(e) => e.len(),1009					Self::Mapped(e) => e.len(),1010					Self::Repeated(e) => e.len(),1011				}1012			}1013		}1014	};1015}10161017#[cfg(feature = "nightly")]1018impl_iter_enum!(UnknownArrayIter => Iter);1019#[cfg(feature = "nightly")]1020impl_iter!(UnknownArrayIter => Result<Val>);1021#[cfg(feature = "nightly")]1022impl_iter_enum!(UnknownArrayIterLazy => IterLazy);1023#[cfg(feature = "nightly")]1024impl_iter!(UnknownArrayIterLazy => Thunk<Val>);1025#[cfg(feature = "nightly")]1026impl_iter_enum!(UnknownArrayIterCheap => IterCheap);1027#[cfg(feature = "nightly")]1028impl_iter!(UnknownArrayIterCheap => Val);
modifiedcrates/jrsonnet-evaluator/src/async_import.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/async_import.rs
+++ b/crates/jrsonnet-evaluator/src/async_import.rs
@@ -294,7 +294,6 @@
 		path: handler.resolve(path.as_ref()).await?,
 		parse: true,
 	}];
-	// let mut resolved = HashMap::<(SourcePath, IStr), (SourcePath, bool)>::new();
 	while let Some(job) = queue.pop() {
 		match job {
 			Job::LoadFile { path, parse } => {
@@ -349,8 +348,8 @@
 			}
 		}
 	}
-	s.set_import_resolver(Box::new(ResolvedImportResolver {
+	s.set_import_resolver(ResolvedImportResolver {
 		resolved: RefCell::new(resolved),
-	}));
+	});
 	Ok(())
 }
modifiedcrates/jrsonnet-evaluator/src/ctx.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/ctx.rs
+++ b/crates/jrsonnet-evaluator/src/ctx.rs
@@ -39,16 +39,16 @@
 			.expect("used state from dummy context")
 	}
 
-	pub fn dollar(&self) -> &Option<ObjValue> {
-		&self.0.dollar
+	pub fn dollar(&self) -> Option<&ObjValue> {
+		self.0.dollar.as_ref()
 	}
 
-	pub fn this(&self) -> &Option<ObjValue> {
-		&self.0.this
+	pub fn this(&self) -> Option<&ObjValue> {
+		self.0.this.as_ref()
 	}
 
-	pub fn super_obj(&self) -> &Option<ObjValue> {
-		&self.0.sup
+	pub fn super_obj(&self) -> Option<&ObjValue> {
+		self.0.sup.as_ref()
 	}
 
 	#[cfg(not(feature = "friendly-errors"))]
@@ -130,12 +130,6 @@
 		}))
 	}
 }
-
-// impl Default for Context {
-// 	fn default() -> Self {
-// 		Self::new()
-// 	}
-// }
 
 impl PartialEq for Context {
 	fn eq(&self, other: &Self) -> bool {
modifiedcrates/jrsonnet-evaluator/src/evaluate/destructure.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/evaluate/destructure.rs
+++ b/crates/jrsonnet-evaluator/src/evaluate/destructure.rs
@@ -6,7 +6,7 @@
 	error::{ErrorKind::*, Result},
 	evaluate, evaluate_method, evaluate_named,
 	gc::GcHashMap,
-	tb, throw,
+	throw,
 	val::ThunkValue,
 	Context, Pending, Thunk, Val,
 };
@@ -63,11 +63,11 @@
 				}
 			}
 
-			let full = Thunk::new(tb!(DataThunk {
+			let full = Thunk::new(DataThunk {
 				min_len: start.len() + end.len(),
 				has_rest: rest.is_some(),
 				parent,
-			}));
+			});
 
 			{
 				#[derive(Trace)]
@@ -86,10 +86,10 @@
 				for (i, d) in start.iter().enumerate() {
 					destruct(
 						d,
-						Thunk::new(tb!(BaseThunk {
+						Thunk::new(BaseThunk {
 							full: full.clone(),
 							index: i,
-						})),
+						}),
 						fctx.clone(),
 						new_bindings,
 					)?;
@@ -119,11 +119,11 @@
 
 					destruct(
 						&Destruct::Full(v.clone()),
-						Thunk::new(tb!(RestThunk {
+						Thunk::new(RestThunk {
 							full: full.clone(),
 							start: start.len(),
 							end: end.len(),
-						})),
+						}),
 						fctx.clone(),
 						new_bindings,
 					)?;
@@ -151,11 +151,11 @@
 				for (i, d) in end.iter().enumerate() {
 					destruct(
 						d,
-						Thunk::new(tb!(EndThunk {
+						Thunk::new(EndThunk {
 							full: full.clone(),
 							index: i,
 							end: end.len(),
-						})),
+						}),
 						fctx.clone(),
 						new_bindings,
 					)?;
@@ -199,11 +199,11 @@
 				.filter(|f| f.2.is_none())
 				.map(|f| f.0.clone())
 				.collect();
-			let full = Thunk::new(tb!(DataThunk {
+			let full = Thunk::new(DataThunk {
 				parent,
 				field_names,
-				has_rest: rest.is_some()
-			}));
+				has_rest: rest.is_some(),
+			});
 
 			for (field, d, default) in fields {
 				#[derive(Trace)]
@@ -225,11 +225,11 @@
 						}
 					}
 				}
-				let value = Thunk::new(tb!(FieldThunk {
+				let value = Thunk::new(FieldThunk {
 					full: full.clone(),
 					field: field.clone(),
 					default: default.clone().map(|e| (fctx.clone(), e)),
-				}));
+				});
 				if let Some(d) = d {
 					destruct(d, value, fctx.clone(), new_bindings)?;
 				} else {
@@ -268,11 +268,11 @@
 					)
 				}
 			}
-			let data = Thunk::new(tb!(EvaluateThunkValue {
+			let data = Thunk::new(EvaluateThunkValue {
 				name: into.name(),
 				fctx: fctx.clone(),
 				expr: value.clone(),
-			}));
+			});
 			destruct(into, data, fctx, new_bindings)?;
 		}
 		BindSpec::Function {
@@ -302,12 +302,12 @@
 
 			let old = new_bindings.insert(
 				name.clone(),
-				Thunk::new(tb!(MethodThunk {
+				Thunk::new(MethodThunk {
 					fctx,
 					name: name.clone(),
 					params: params.clone(),
-					value: value.clone()
-				})),
+					value: value.clone(),
+				}),
 			);
 			if old.is_some() {
 				throw!(DuplicateLocalVar(name.clone()))
modifiedcrates/jrsonnet-evaluator/src/evaluate/mod.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/evaluate/mod.rs
+++ b/crates/jrsonnet-evaluator/src/evaluate/mod.rs
@@ -15,7 +15,7 @@
 	error::ErrorKind::*,
 	evaluate::operator::{evaluate_add_op, evaluate_binary_op_special, evaluate_unary_op},
 	function::{CallLocation, FuncDesc, FuncVal},
-	tb, throw,
+	throw,
 	typed::Typed,
 	val::{CachedUnbound, IndexableVal, StrValue, Thunk, ThunkValue},
 	Context, GcHashMap, ObjValue, ObjValueBuilder, ObjectAssertion, Pending, Result, State,
@@ -45,12 +45,12 @@
 			if n.iter().any(|e| !is_trivial(e)) {
 				return None;
 			}
-			Val::Arr(ArrValue::eager(Cc::new(
+			Val::Arr(ArrValue::eager(
 				n.iter()
 					.map(evaluate_trivial)
 					.map(|e| e.expect("checked trivial"))
 					.collect(),
-			)))
+			))
 		}
 		Expr::Parened(e) => evaluate_trivial(e)?,
 		_ => return None,
@@ -136,10 +136,10 @@
 					let mut new_bindings = GcHashMap::with_capacity(var.capacity_hint());
 					let value = Thunk::evaluated(Val::Arr(ArrValue::lazy(Cc::new(vec![
 						Thunk::evaluated(Val::Str(StrValue::Flat(field.clone()))),
-						Thunk::new(tb!(ObjectFieldThunk {
+						Thunk::new(ObjectFieldThunk {
 							field: field.clone(),
 							obj: obj.clone(),
-						})),
+						}),
 					]))));
 					destruct(var, value, fctx.clone(), &mut new_bindings)?;
 					let ctx = ctx
@@ -180,7 +180,7 @@
 			}
 
 			let ctx = self.fctx.unwrap();
-			let new_dollar = ctx.dollar().clone().or_else(|| this.clone());
+			let new_dollar = ctx.dollar().cloned().or_else(|| this.clone());
 
 			let ctx = ctx
 				.extend(new_bindings, new_dollar, sup, this)
@@ -230,11 +230,11 @@
 				.with_add(*plus)
 				.with_visibility(*visibility)
 				.with_location(value.1.clone())
-				.bindable(tb!(UnboundValue {
+				.bindable(UnboundValue {
 					uctx,
 					value: value.clone(),
 					name,
-				}))?;
+				})?;
 		}
 		FieldMember {
 			params: Some(params),
@@ -265,12 +265,12 @@
 				.member(name.clone())
 				.with_visibility(*visibility)
 				.with_location(value.1.clone())
-				.bindable(tb!(UnboundMethod {
+				.bindable(UnboundMethod {
 					uctx,
 					value: value.clone(),
 					params: params.clone(),
 					name,
-				}))?;
+				})?;
 		}
 	}
 	Ok(())
@@ -311,10 +311,10 @@
 						evaluate_assert(ctx, &self.assert)
 					}
 				}
-				builder.assert(tb!(ObjectAssert {
+				builder.assert(ObjectAssert {
 					uctx: uctx.clone(),
 					assert: stmt.clone(),
-				}));
+				});
 			}
 			Member::BindStmt(_) => {
 				// Already handled
@@ -420,17 +420,17 @@
 	let LocExpr(expr, loc) = expr;
 	Ok(match &**expr {
 		Literal(LiteralType::This) => {
-			Val::Obj(ctx.this().clone().ok_or(CantUseSelfOutsideOfObject)?)
+			Val::Obj(ctx.this().ok_or(CantUseSelfOutsideOfObject)?.clone())
 		}
 		Literal(LiteralType::Super) => Val::Obj(
-			ctx.super_obj().clone().ok_or(NoSuperFound)?.with_this(
+			ctx.super_obj().ok_or(NoSuperFound)?.with_this(
 				ctx.this()
-					.clone()
-					.expect("if super exists - then this should too"),
+					.expect("if super exists - then this should too")
+					.clone(),
 			),
 		),
 		Literal(LiteralType::Dollar) => {
-			Val::Obj(ctx.dollar().clone().ok_or(NoTopLevelObjectFound)?)
+			Val::Obj(ctx.dollar().ok_or(NoTopLevelObjectFound)?.clone())
 		}
 		Literal(LiteralType::True) => Val::Bool(true),
 		Literal(LiteralType::False) => Val::Bool(false),
@@ -455,9 +455,8 @@
 				))
 			};
 			ctx.super_obj()
-				.clone()
 				.expect("no super found")
-				.get_for(name.into_flat(), ctx.this().clone().expect("no this found"))?
+				.get_for(name.into_flat(), ctx.this().expect("no this found").clone())?
 				.expect("value not found")
 		}
 		Index(value, index) => match (evaluate(ctx.clone(), value)?, evaluate(ctx, index)?) {
@@ -563,12 +562,10 @@
 						evaluate(self.ctx, &self.item)
 					}
 				}
-				Val::Arr(ArrValue::lazy(Cc::new(vec![Thunk::new(tb!(
-					ArrayElement {
-						ctx,
-						item: items[0].clone(),
-					}
-				))])))
+				Val::Arr(ArrValue::lazy(Cc::new(vec![Thunk::new(ArrayElement {
+					ctx,
+					item: items[0].clone(),
+				})])))
 			} else {
 				Val::Arr(ArrValue::expr(ctx, items.iter().cloned()))
 			}
@@ -579,7 +576,7 @@
 				out.push(evaluate(ctx, expr)?);
 				Ok(())
 			})?;
-			Val::Arr(ArrValue::eager(Cc::new(out)))
+			Val::Arr(ArrValue::eager(out))
 		}
 		Obj(body) => Val::Obj(evaluate_object(ctx, body)?),
 		ObjExtend(a, b) => evaluate_add_op(
@@ -623,7 +620,7 @@
 			fn parse_idx<T: Typed>(
 				loc: CallLocation<'_>,
 				ctx: &Context,
-				expr: &Option<LocExpr>,
+				expr: Option<&LocExpr>,
 				desc: &'static str,
 			) -> Result<Option<T>> {
 				if let Some(value) = expr {
@@ -640,9 +637,9 @@
 			let indexable = evaluate(ctx.clone(), value)?;
 			let loc = CallLocation::new(loc);
 
-			let start = parse_idx(loc, &ctx, &desc.start, "start")?;
-			let end = parse_idx(loc, &ctx, &desc.end, "end")?;
-			let step = parse_idx(loc, &ctx, &desc.step, "step")?;
+			let start = parse_idx(loc, &ctx, desc.start.as_ref(), "start")?;
+			let end = parse_idx(loc, &ctx, desc.end.as_ref(), "end")?;
+			let step = parse_idx(loc, &ctx, desc.step.as_ref(), "step")?;
 
 			IndexableVal::into_untyped(indexable.into_indexable()?.slice(start, end, step)?)?
 		}
modifiedcrates/jrsonnet-evaluator/src/function/arglike.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/function/arglike.rs
+++ b/crates/jrsonnet-evaluator/src/function/arglike.rs
@@ -7,7 +7,6 @@
 	error::Result,
 	evaluate,
 	gc::GcHashMap,
-	tb,
 	typed::Typed,
 	val::{StrValue, ThunkValue},
 	Context, Thunk, Val,
@@ -37,10 +36,10 @@
 		Ok(if tailstrict {
 			Thunk::evaluated(evaluate(ctx, self)?)
 		} else {
-			Thunk::new(tb!(EvaluateThunk {
+			Thunk::new(EvaluateThunk {
 				ctx,
 				expr: (*self).clone(),
-			}))
+			})
 		})
 	}
 }
@@ -69,10 +68,10 @@
 			TlaArg::Code(code) => Ok(if tailstrict {
 				Thunk::evaluated(evaluate(ctx, code)?)
 			} else {
-				Thunk::new(tb!(EvaluateThunk {
+				Thunk::new(EvaluateThunk {
 					ctx,
 					expr: code.clone(),
-				}))
+				})
 			}),
 			TlaArg::Val(val) => Ok(Thunk::evaluated(val.clone())),
 		}
@@ -128,7 +127,6 @@
 	}
 	fn named_names(&self, _handler: &mut dyn FnMut(&IStr)) {}
 }
-impl OptionalContext for Vec<Val> {}
 
 impl ArgsLike for ArgsDesc {
 	fn unnamed_len(&self) -> usize {
@@ -147,10 +145,10 @@
 				if tailstrict {
 					Thunk::evaluated(evaluate(ctx.clone(), arg)?)
 				} else {
-					Thunk::new(tb!(EvaluateThunk {
+					Thunk::new(EvaluateThunk {
 						ctx: ctx.clone(),
 						expr: arg.clone(),
-					}))
+					})
 				},
 			)?;
 		}
@@ -169,10 +167,10 @@
 				if tailstrict {
 					Thunk::evaluated(evaluate(ctx.clone(), arg)?)
 				} else {
-					Thunk::new(tb!(EvaluateThunk {
+					Thunk::new(EvaluateThunk {
 						ctx: ctx.clone(),
 						expr: arg.clone(),
-					}))
+					})
 				},
 			)?;
 		}
modifiedcrates/jrsonnet-evaluator/src/function/builtin.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/function/builtin.rs
+++ b/crates/jrsonnet-evaluator/src/function/builtin.rs
@@ -3,7 +3,7 @@
 use jrsonnet_gcmodule::Trace;
 
 use super::{arglike::ArgsLike, parse::parse_builtin_call, CallLocation};
-use crate::{error::Result, gc::TraceBox, Context, Val};
+use crate::{error::Result, gc::TraceBox, tb, Context, Val};
 
 pub type BuiltinParamName = Cow<'static, str>;
 
@@ -42,10 +42,7 @@
 }
 impl NativeCallback {
 	#[deprecated = "prefer using builtins directly, use this interface only for bindings"]
-	pub fn new(
-		params: Vec<Cow<'static, str>>,
-		handler: TraceBox<dyn NativeCallbackHandler>,
-	) -> Self {
+	pub fn new(params: Vec<Cow<'static, str>>, handler: impl NativeCallbackHandler) -> Self {
 		Self {
 			params: params
 				.into_iter()
@@ -54,7 +51,7 @@
 					has_default: false,
 				})
 				.collect(),
-			handler,
+			handler: tb!(handler),
 		}
 	}
 }
modifiedcrates/jrsonnet-evaluator/src/function/mod.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/function/mod.rs
+++ b/crates/jrsonnet-evaluator/src/function/mod.rs
@@ -12,9 +12,7 @@
 	native::NativeDesc,
 	parse::{parse_default_function_call, parse_function_call},
 };
-use crate::{
-	evaluate, evaluate_trivial, gc::TraceBox, typed::Any, Context, ContextBuilder, Result, Val,
-};
+use crate::{evaluate, evaluate_trivial, gc::TraceBox, tb, Context, ContextBuilder, Result, Val};
 
 pub mod arglike;
 pub mod builtin;
@@ -116,6 +114,9 @@
 }
 
 impl FuncVal {
+	pub fn builtin(builtin: impl Builtin) -> Self {
+		Self::Builtin(Cc::new(tb!(builtin)))
+	}
 	/// Amount of non-default required arguments
 	pub fn params_len(&self) -> usize {
 		match self {
@@ -148,8 +149,8 @@
 			Self::Id => {
 				#[allow(clippy::unnecessary_wraps)]
 				#[builtin]
-				const fn builtin_id(v: Any) -> Result<Any> {
-					Ok(v)
+				const fn builtin_id(x: Val) -> Val {
+					x
 				}
 				static ID: &builtin_id = &builtin_id {};
 
modifiedcrates/jrsonnet-evaluator/src/function/parse.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/function/parse.rs
+++ b/crates/jrsonnet-evaluator/src/function/parse.rs
@@ -10,7 +10,7 @@
 	error::{ErrorKind::*, Result},
 	evaluate_named,
 	gc::GcHashMap,
-	tb, throw,
+	throw,
 	val::ThunkValue,
 	Context, Pending, Thunk, Val,
 };
@@ -100,11 +100,11 @@
 
 			destruct(
 				&param.0,
-				Thunk::new(tb!(EvaluateNamedThunk {
+				Thunk::new(EvaluateNamedThunk {
 					ctx: fctx.clone(),
 					name: param.0.name().unwrap_or_else(|| "<destruct>".into()),
 					value: param.1.clone().expect("default exists"),
-				})),
+				}),
 				fctx.clone(),
 				&mut defaults,
 			)?;
@@ -250,21 +250,21 @@
 		if let Some(v) = &param.1 {
 			destruct(
 				&param.0.clone(),
-				Thunk::new(tb!(EvaluateNamedThunk {
+				Thunk::new(EvaluateNamedThunk {
 					ctx: fctx.clone(),
 					name: param.0.name().unwrap_or_else(|| "<destruct>".into()),
 					value: v.clone(),
-				})),
+				}),
 				fctx.clone(),
 				&mut bindings,
 			)?;
 		} else {
 			destruct(
 				&param.0,
-				Thunk::new(tb!(DependsOnUnbound(
+				Thunk::new(DependsOnUnbound(
 					param.0.name().unwrap_or_else(|| "<destruct>".into()),
-					params.clone()
-				))),
+					params.clone(),
+				)),
 				fctx.clone(),
 				&mut bindings,
 			)?;
modifiedcrates/jrsonnet-evaluator/src/integrations/serde.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/integrations/serde.rs
+++ b/crates/jrsonnet-evaluator/src/integrations/serde.rs
@@ -1,6 +1,5 @@
 use std::borrow::Cow;
 
-use jrsonnet_gcmodule::Cc;
 use serde::{
 	de::Visitor,
 	ser::{Error, SerializeMap, SerializeSeq},
@@ -117,7 +116,7 @@
 					out.push(val);
 				}
 
-				Ok(Val::Arr(ArrValue::eager(Cc::new(out))))
+				Ok(Val::Arr(ArrValue::eager(out)))
 			}
 
 			fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
modifiedcrates/jrsonnet-evaluator/src/lib.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/lib.rs
+++ b/crates/jrsonnet-evaluator/src/lib.rs
@@ -433,10 +433,13 @@
 	pub fn import_resolver(&self) -> Ref<'_, dyn ImportResolver> {
 		Ref::map(self.settings(), |s| &*s.import_resolver)
 	}
-	pub fn set_import_resolver(&self, resolver: Box<dyn ImportResolver>) {
-		self.settings_mut().import_resolver = TraceBox(resolver);
+	pub fn set_import_resolver(&self, resolver: impl ImportResolver) {
+		self.settings_mut().import_resolver = tb!(resolver);
 	}
 	pub fn context_initializer(&self) -> Ref<'_, dyn ContextInitializer> {
 		Ref::map(self.settings(), |s| &*s.context_initializer)
 	}
+	pub fn set_context_initializer(&self, initializer: impl ContextInitializer) {
+		self.settings_mut().context_initializer = tb!(initializer);
+	}
 }
modifiedcrates/jrsonnet-evaluator/src/obj.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/obj.rs
+++ b/crates/jrsonnet-evaluator/src/obj.rs
@@ -15,7 +15,7 @@
 	function::CallLocation,
 	gc::{GcHashMap, GcHashSet, TraceBox},
 	operator::evaluate_add_op,
-	throw, MaybeUnbound, Result, State, Thunk, Unbound, Val,
+	tb, throw, MaybeUnbound, Result, State, Thunk, Unbound, Val,
 };
 
 #[cfg(not(feature = "exp-preserve-order"))]
@@ -538,8 +538,8 @@
 		self
 	}
 
-	pub fn assert(&mut self, assertion: TraceBox<dyn ObjectAssertion>) -> &mut Self {
-		self.assertions.push(assertion);
+	pub fn assert(&mut self, assertion: impl ObjectAssertion + 'static) -> &mut Self {
+		self.assertions.push(tb!(assertion));
 		self
 	}
 	pub fn member(&mut self, name: IStr) -> ObjMemberBuilder<ValueBuilder<'_>> {
@@ -631,8 +631,8 @@
 	pub fn thunk(self, value: Thunk<Val>) -> Result<()> {
 		self.binding(MaybeUnbound::Bound(value))
 	}
-	pub fn bindable(self, bindable: TraceBox<dyn Unbound<Bound = Val>>) -> Result<()> {
-		self.binding(MaybeUnbound::Unbound(Cc::new(bindable)))
+	pub fn bindable(self, bindable: impl Unbound<Bound = Val>) -> Result<()> {
+		self.binding(MaybeUnbound::Unbound(Cc::new(tb!(bindable))))
 	}
 	pub fn binding(self, binding: MaybeUnbound) -> Result<()> {
 		let (receiver, name, member) = self.build_member(binding);
modifiedcrates/jrsonnet-evaluator/src/typed/conversions.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/typed/conversions.rs
+++ b/crates/jrsonnet-evaluator/src/typed/conversions.rs
@@ -29,6 +29,14 @@
 	const TYPE: &'static ComplexValType;
 	fn into_untyped(typed: Self) -> Result<Val>;
 	fn from_untyped(untyped: Val) -> Result<Self>;
+
+	/// Hack to make builtins be able to return non-result values, and make macros able to convert those values to result
+	/// This method returns identity in impl Typed for Result, and should not be overriden
+	#[doc(hidden)]
+	fn into_result(typed: Self) -> Result<Val> {
+		let value = Self::into_untyped(typed)?;
+		Ok(value)
+	}
 }
 
 const MAX_SAFE_INTEGER: f64 = ((1u64 << (f64::MANTISSA_DIGITS + 1)) - 1) as f64;
@@ -238,61 +246,54 @@
 	const TYPE: &'static ComplexValType = &ComplexValType::ArrayRef(T::TYPE);
 
 	fn into_untyped(value: Self) -> Result<Val> {
-		let mut o = Vec::with_capacity(value.len());
-		for i in value {
-			o.push(T::into_untyped(i)?);
-		}
-		Ok(Val::Arr(o.into()))
+		Ok(Val::Arr(
+			value
+				.into_iter()
+				.map(T::into_untyped)
+				.collect::<Result<ArrValue>>()?,
+		))
 	}
 
 	fn from_untyped(value: Val) -> Result<Self> {
-		<Self as Typed>::TYPE.check(&value)?;
-		match value {
-			Val::Arr(a) => {
-				let mut o = Self::with_capacity(a.len());
-				for i in a.iter() {
-					o.push(T::from_untyped(i?)?);
-				}
-				Ok(o)
-			}
-			_ => unreachable!(),
-		}
+		let Val::Arr(a) = value else {
+			<Self as Typed>::TYPE.check(&value)?;
+			unreachable!("typecheck should fail")
+		};
+		a.iter()
+			.map(|r| r.and_then(T::from_untyped))
+			.collect::<Result<Vec<T>>>()
 	}
 }
 
-/// To be used in Vec<Any>
-/// Regular Val can't be used here, because it has wrong `TryFrom::Error` type
-#[derive(Clone)]
-pub struct Any(pub Val);
-
-impl Typed for Any {
+impl Typed for Val {
 	const TYPE: &'static ComplexValType = &ComplexValType::Any;
 
-	fn into_untyped(value: Self) -> Result<Val> {
-		Ok(value.0)
+	fn into_untyped(typed: Self) -> Result<Val> {
+		Ok(typed)
 	}
-
-	fn from_untyped(value: Val) -> Result<Self> {
-		Ok(Self(value))
+	fn from_untyped(untyped: Val) -> Result<Self> {
+		Ok(untyped)
 	}
 }
 
-/// Specialization, provides faster `TryFrom<VecVal>` for Val
-pub struct VecVal(pub Vec<Val>);
+// Hack
+#[doc(hidden)]
+impl<T> Typed for Result<T>
+where
+	T: Typed,
+{
+	const TYPE: &'static ComplexValType = &ComplexValType::Any;
 
-impl Typed for VecVal {
-	const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Arr);
+	fn into_untyped(_typed: Self) -> Result<Val> {
+		panic!("do not use this conversion")
+	}
 
-	fn into_untyped(value: Self) -> Result<Val> {
-		Ok(Val::Arr(ArrValue::eager(Cc::new(value.0))))
+	fn from_untyped(_untyped: Val) -> Result<Self> {
+		panic!("do not use this conversion")
 	}
 
-	fn from_untyped(value: Val) -> Result<Self> {
-		<Self as Typed>::TYPE.check(&value)?;
-		match value {
-			Val::Arr(a) => Ok(Self(a.iter().collect::<Result<Vec<_>>>()?)),
-			_ => unreachable!(),
-		}
+	fn into_result(typed: Self) -> Result<Val> {
+		typed.map(T::into_untyped)?
 	}
 }
 
modifiedcrates/jrsonnet-evaluator/src/val.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/val.rs
+++ b/crates/jrsonnet-evaluator/src/val.rs
@@ -15,7 +15,7 @@
 	function::FuncVal,
 	gc::{GcHashMap, TraceBox},
 	manifest::{ManifestFormat, ToStringFormat},
-	throw,
+	tb, throw,
 	typed::BoundedUsize,
 	ObjValue, Result, Unbound, WeakObjValue,
 };
@@ -41,8 +41,8 @@
 	pub fn evaluated(val: T) -> Self {
 		Self(Cc::new(RefCell::new(ThunkInner::Computed(val))))
 	}
-	pub fn new(f: TraceBox<dyn ThunkValue<Output = T>>) -> Self {
-		Self(Cc::new(RefCell::new(ThunkInner::Waiting(f))))
+	pub fn new(f: impl ThunkValue<Output = T> + 'static) -> Self {
+		Self(Cc::new(RefCell::new(ThunkInner::Waiting(tb!(f)))))
 	}
 	pub fn errored(e: Error) -> Self {
 		Self(Cc::new(RefCell::new(ThunkInner::Errored(e))))
modifiedcrates/jrsonnet-macros/src/lib.rsdiffbeforeafterboth
--- a/crates/jrsonnet-macros/src/lib.rs
+++ b/crates/jrsonnet-macros/src/lib.rs
@@ -192,38 +192,27 @@
 	item: proc_macro::TokenStream,
 ) -> proc_macro::TokenStream {
 	let attr = parse_macro_input!(attr as BuiltinAttrs);
-	let item: ItemFn = parse_macro_input!(item);
+	let item_fn = item.clone();
+	let item_fn: ItemFn = parse_macro_input!(item_fn);
 
-	match builtin_inner(attr, item) {
+	match builtin_inner(attr, item_fn, item.into()) {
 		Ok(v) => v.into(),
 		Err(e) => e.into_compile_error().into(),
 	}
 }
 
-fn builtin_inner(attr: BuiltinAttrs, fun: ItemFn) -> syn::Result<TokenStream> {
+fn builtin_inner(
+	attr: BuiltinAttrs,
+	fun: ItemFn,
+	item: proc_macro2::TokenStream,
+) -> syn::Result<TokenStream> {
 	let ReturnType::Type(_, result) = &fun.sig.output else {
 		return Err(Error::new(
 			fun.sig.span(),
 			"builtin should return something",
 		))
 	};
-
-	let Some(args) = type_is_path(result, "Result") else {
-		return Err(Error::new(result.span(), "return value should be result"));
 
-	};
-	let PathArguments::AngleBracketed(params) = args else {
-		return Err(Error::new(args.span(), "missing result generic"));
-	};
-	let generic_arg = params.args.iter().next().unwrap();
-	// This argument must be a type:
-	let GenericArgument::Type(result_inner) = generic_arg else {
-		return Err(Error::new(
-			generic_arg.span(),
-			"option generic should be a type",
-		))
-	};
-
 	let name = fun.sig.ident.to_string();
 	let args = fun
 		.sig
@@ -355,7 +344,8 @@
 	};
 
 	Ok(quote! {
-		#fun
+		#item
+
 		#[doc(hidden)]
 		#[allow(non_camel_case_types)]
 		#[derive(Clone, jrsonnet_gcmodule::Trace #static_derive_copy)]
@@ -388,8 +378,7 @@
 					let parsed = parse_builtin_call(ctx.clone(), &PARAMS, args, false)?;
 
 					let result: #result = #name(#(#pass)*);
-					let result = result?;
-					<#result_inner>::into_untyped(result)
+					<_ as Typed>::into_result(result)
 				}
 			}
 		};
modifiedcrates/jrsonnet-stdlib/src/arrays.rsdiffbeforeafterboth
--- a/crates/jrsonnet-stdlib/src/arrays.rs
+++ b/crates/jrsonnet-stdlib/src/arrays.rs
@@ -2,11 +2,10 @@
 	error::{ErrorKind::RuntimeError, Result},
 	function::{builtin, FuncVal},
 	throw,
-	typed::{Any, BoundedI32, BoundedUsize, Either2, NativeFn, Typed},
+	typed::{BoundedI32, BoundedUsize, Either2, NativeFn, Typed},
 	val::{equals, ArrValue, IndexableVal, StrValue},
 	Either, IStr, Val,
 };
-use jrsonnet_gcmodule::Cc;
 
 #[builtin]
 pub fn builtin_make_array(sz: BoundedI32<0, { i32::MAX }>, func: FuncVal) -> Result<ArrValue> {
@@ -18,21 +17,21 @@
 		for _ in 0..*sz {
 			out.push(trivial.clone())
 		}
-		Ok(ArrValue::eager(Cc::new(out)))
+		Ok(ArrValue::eager(out))
 	} else {
 		Ok(ArrValue::range_exclusive(0, *sz).map(func))
 	}
 }
 
 #[builtin]
-pub fn builtin_repeat(what: Either![IStr, ArrValue], count: usize) -> Result<Any> {
-	Ok(Any(match what {
+pub fn builtin_repeat(what: Either![IStr, ArrValue], count: usize) -> Result<Val> {
+	Ok(match what {
 		Either2::A(s) => Val::Str(StrValue::Flat(s.repeat(count).into())),
 		Either2::B(arr) => Val::Arr(
 			ArrValue::repeated(arr, count)
 				.ok_or_else(|| RuntimeError("repeated length overflow".into()))?,
 		),
-	}))
+	})
 }
 
 #[builtin]
@@ -41,8 +40,8 @@
 	index: Option<BoundedUsize<0, { i32::MAX as usize }>>,
 	end: Option<BoundedUsize<0, { i32::MAX as usize }>>,
 	step: Option<BoundedUsize<1, { i32::MAX as usize }>>,
-) -> Result<Any> {
-	indexable.slice(index, end, step).map(Val::from).map(Any)
+) -> Result<Val> {
+	indexable.slice(index, end, step).map(Val::from)
 }
 
 #[builtin]
@@ -52,7 +51,7 @@
 
 #[builtin]
 pub fn builtin_flatmap(
-	func: NativeFn<((Either![String, Any],), Any)>,
+	func: NativeFn<((Either![String, Val],), Val)>,
 	arr: IndexableVal,
 ) -> Result<IndexableVal> {
 	use std::fmt::Write;
@@ -60,7 +59,7 @@
 		IndexableVal::Str(str) => {
 			let mut out = String::new();
 			for c in str.chars() {
-				match func(Either2::A(c.to_string()))?.0 {
+				match func(Either2::A(c.to_string()))? {
 					Val::Str(o) => write!(out, "{o}").unwrap(),
 					Val::Null => continue,
 					_ => throw!("in std.join all items should be strings"),
@@ -72,7 +71,7 @@
 			let mut out = Vec::new();
 			for el in a.iter() {
 				let el = el?;
-				match func(Either2::B(Any(el)))?.0 {
+				match func(Either2::B(el))? {
 					Val::Arr(o) => {
 						for oe in o.iter() {
 							out.push(oe?);
@@ -89,25 +88,25 @@
 
 #[builtin]
 pub fn builtin_filter(func: FuncVal, arr: ArrValue) -> Result<ArrValue> {
-	arr.filter(|val| bool::from_untyped(func.evaluate_simple(&(Any(val.clone()),))?))
+	arr.filter(|val| bool::from_untyped(func.evaluate_simple(&(val.clone(),))?))
 }
 
 #[builtin]
-pub fn builtin_foldl(func: FuncVal, arr: ArrValue, init: Any) -> Result<Any> {
-	let mut acc = init.0;
+pub fn builtin_foldl(func: FuncVal, arr: ArrValue, init: Val) -> Result<Val> {
+	let mut acc = init;
 	for i in arr.iter() {
-		acc = func.evaluate_simple(&(Any(acc), Any(i?)))?;
+		acc = func.evaluate_simple(&(acc, i?))?;
 	}
-	Ok(Any(acc))
+	Ok(acc)
 }
 
 #[builtin]
-pub fn builtin_foldr(func: FuncVal, arr: ArrValue, init: Any) -> Result<Any> {
-	let mut acc = init.0;
+pub fn builtin_foldr(func: FuncVal, arr: ArrValue, init: Val) -> Result<Val> {
+	let mut acc = init;
 	for i in arr.iter().rev() {
-		acc = func.evaluate_simple(&(Any(i?), Any(acc)))?;
+		acc = func.evaluate_simple(&(i?, acc))?;
 	}
-	Ok(Any(acc))
+	Ok(acc)
 }
 
 #[builtin]
@@ -175,8 +174,8 @@
 }
 
 #[builtin]
-pub fn builtin_reverse(value: ArrValue) -> Result<ArrValue> {
-	Ok(value.reversed())
+pub fn builtin_reverse(arr: ArrValue) -> ArrValue {
+	arr.reversed()
 }
 
 #[builtin]
@@ -202,16 +201,16 @@
 }
 
 #[builtin]
-pub fn builtin_member(arr: IndexableVal, x: Any) -> Result<bool> {
+pub fn builtin_member(arr: IndexableVal, x: Val) -> Result<bool> {
 	match arr {
 		IndexableVal::Str(str) => {
-			let x: IStr = IStr::from_untyped(x.0)?;
+			let x: IStr = IStr::from_untyped(x)?;
 			Ok(!x.is_empty() && str.contains(&*x))
 		}
 		IndexableVal::Arr(a) => {
 			for item in a.iter() {
 				let item = item?;
-				if equals(&item, &x.0)? {
+				if equals(&item, &x)? {
 					return Ok(true);
 				}
 			}
@@ -221,10 +220,10 @@
 }
 
 #[builtin]
-pub fn builtin_count(arr: Vec<Any>, v: Any) -> Result<usize> {
+pub fn builtin_count(arr: ArrValue, x: Val) -> Result<usize> {
 	let mut count = 0;
-	for item in &arr {
-		if equals(&item.0, &v.0)? {
+	for item in arr.iter() {
+		if equals(&item?, &x)? {
 			count += 1;
 		}
 	}
modifiedcrates/jrsonnet-stdlib/src/encoding.rsdiffbeforeafterboth
--- a/crates/jrsonnet-stdlib/src/encoding.rs
+++ b/crates/jrsonnet-stdlib/src/encoding.rs
@@ -6,8 +6,8 @@
 };
 
 #[builtin]
-pub fn builtin_encode_utf8(str: IStr) -> Result<IBytes> {
-	Ok(str.cast_bytes())
+pub fn builtin_encode_utf8(str: IStr) -> IBytes {
+	str.cast_bytes()
 }
 
 #[builtin]
@@ -18,24 +18,24 @@
 }
 
 #[builtin]
-pub fn builtin_base64(input: Either![IStr, IBytes]) -> Result<String> {
+pub fn builtin_base64(input: Either![IStr, IBytes]) -> String {
 	use Either2::*;
-	Ok(match input {
+	match input {
 		A(l) => base64::encode(l.as_bytes()),
 		B(a) => base64::encode(a.as_slice()),
-	})
+	}
 }
 
 #[builtin]
-pub fn builtin_base64_decode_bytes(input: IStr) -> Result<IBytes> {
-	Ok(base64::decode(input.as_bytes())
+pub fn builtin_base64_decode_bytes(str: IStr) -> Result<IBytes> {
+	Ok(base64::decode(str.as_bytes())
 		.map_err(|_| RuntimeError("bad base64".into()))?
 		.as_slice()
 		.into())
 }
 
 #[builtin]
-pub fn builtin_base64_decode(input: IStr) -> Result<String> {
-	let bytes = base64::decode(input.as_bytes()).map_err(|_| RuntimeError("bad base64".into()))?;
+pub fn builtin_base64_decode(str: IStr) -> Result<String> {
+	let bytes = base64::decode(str.as_bytes()).map_err(|_| RuntimeError("bad base64".into()))?;
 	Ok(String::from_utf8(bytes).map_err(|_| RuntimeError("bad utf8".into()))?)
 }
modifiedcrates/jrsonnet-stdlib/src/hash.rsdiffbeforeafterboth
--- a/crates/jrsonnet-stdlib/src/hash.rs
+++ b/crates/jrsonnet-stdlib/src/hash.rs
@@ -1,13 +1,13 @@
-use jrsonnet_evaluator::{error::Result, function::builtin, IStr};
+use jrsonnet_evaluator::{function::builtin, IStr};
 
 #[builtin]
-pub fn builtin_md5(str: IStr) -> Result<String> {
-	Ok(format!("{:x}", md5::compute(str.as_bytes())))
+pub fn builtin_md5(s: IStr) -> String {
+	format!("{:x}", md5::compute(s.as_bytes()))
 }
 
 #[cfg(feature = "exp-more-hashes")]
 #[builtin]
-pub fn builtin_sha256(str: IStr) -> Result<String> {
+pub fn builtin_sha256(s: IStr) -> String {
 	use sha2::digest::Digest;
-	Ok(format!("{:?}", sha2::Sha256::digest(str.as_bytes())))
+	format!("{:?}", sha2::Sha256::digest(s.as_bytes()))
 }
modifiedcrates/jrsonnet-stdlib/src/lib.rsdiffbeforeafterboth
--- a/crates/jrsonnet-stdlib/src/lib.rs
+++ b/crates/jrsonnet-stdlib/src/lib.rs
@@ -155,23 +155,21 @@
 	builder
 		.member("extVar".into())
 		.hide()
-		.value(Val::Func(FuncVal::Builtin(Cc::new(tb!(builtin_ext_var {
-			settings: settings.clone()
-		})))))
+		.value(Val::Func(FuncVal::builtin(builtin_ext_var {
+			settings: settings.clone(),
+		})))
 		.expect("no conflict");
 	builder
 		.member("native".into())
 		.hide()
-		.value(Val::Func(FuncVal::Builtin(Cc::new(tb!(builtin_native {
-			settings: settings.clone()
-		})))))
+		.value(Val::Func(FuncVal::builtin(builtin_native {
+			settings: settings.clone(),
+		})))
 		.expect("no conflict");
 	builder
 		.member("trace".into())
 		.hide()
-		.value(Val::Func(FuncVal::Builtin(Cc::new(tb!(builtin_trace {
-			settings
-		})))))
+		.value(Val::Func(FuncVal::builtin(builtin_trace { settings })))
 		.expect("no conflict");
 
 	builder
@@ -301,8 +299,10 @@
 			.insert(name.into(), TlaArg::Code(parsed));
 		Ok(())
 	}
-	pub fn add_native(&self, name: IStr, cb: Cc<TraceBox<dyn Builtin>>) {
-		self.settings_mut().ext_natives.insert(name, cb);
+	pub fn add_native(&self, name: IStr, cb: impl Builtin) {
+		self.settings_mut()
+			.ext_natives
+			.insert(name, Cc::new(tb!(cb)));
 	}
 }
 impl jrsonnet_evaluator::ContextInitializer for ContextInitializer {
modifiedcrates/jrsonnet-stdlib/src/manifest/mod.rsdiffbeforeafterboth
--- a/crates/jrsonnet-stdlib/src/manifest/mod.rs
+++ b/crates/jrsonnet-stdlib/src/manifest/mod.rs
@@ -5,7 +5,6 @@
 	error::Result,
 	function::builtin,
 	manifest::{escape_string_json, JsonFormat},
-	typed::Any,
 	IStr, ObjValue, Val,
 };
 pub use toml::TomlFormat;
@@ -18,7 +17,7 @@
 
 #[builtin]
 pub fn builtin_manifest_json_ex(
-	value: Any,
+	value: Val,
 	indent: IStr,
 	newline: Option<IStr>,
 	key_val_sep: Option<IStr>,
@@ -26,7 +25,7 @@
 ) -> Result<String> {
 	let newline = newline.as_deref().unwrap_or("\n");
 	let key_val_sep = key_val_sep.as_deref().unwrap_or(": ");
-	value.0.manifest(JsonFormat::std_to_json(
+	value.manifest(JsonFormat::std_to_json(
 		indent.to_string(),
 		newline,
 		key_val_sep,
@@ -37,12 +36,12 @@
 
 #[builtin]
 pub fn builtin_manifest_yaml_doc(
-	value: Any,
+	value: Val,
 	indent_array_in_object: Option<bool>,
 	quote_keys: Option<bool>,
 	#[cfg(feature = "exp-preserve-order")] preserve_order: Option<bool>,
 ) -> Result<String> {
-	value.0.manifest(YamlFormat::std_to_yaml(
+	value.manifest(YamlFormat::std_to_yaml(
 		indent_array_in_object.unwrap_or(false),
 		quote_keys.unwrap_or(true),
 		#[cfg(feature = "exp-preserve-order")]
modifiedcrates/jrsonnet-stdlib/src/math.rsdiffbeforeafterboth
--- a/crates/jrsonnet-stdlib/src/math.rs
+++ b/crates/jrsonnet-stdlib/src/math.rs
@@ -1,88 +1,92 @@
-use jrsonnet_evaluator::{error::Result, function::builtin, typed::PositiveF64};
+use jrsonnet_evaluator::{function::builtin, typed::PositiveF64};
 
 #[builtin]
-pub fn builtin_abs(n: f64) -> Result<f64> {
-	Ok(n.abs())
+pub fn builtin_abs(n: f64) -> f64 {
+	n.abs()
 }
 
 #[builtin]
-pub fn builtin_sign(n: f64) -> Result<f64> {
-	Ok(if n == 0. { 0. } else { n.signum() })
+pub fn builtin_sign(n: f64) -> f64 {
+	if n == 0. {
+		0.
+	} else {
+		n.signum()
+	}
 }
 
 #[builtin]
-pub fn builtin_max(a: f64, b: f64) -> Result<f64> {
-	Ok(a.max(b))
+pub fn builtin_max(a: f64, b: f64) -> f64 {
+	a.max(b)
 }
 
 #[builtin]
-pub fn builtin_min(a: f64, b: f64) -> Result<f64> {
-	Ok(a.min(b))
+pub fn builtin_min(a: f64, b: f64) -> f64 {
+	a.min(b)
 }
 
 #[builtin]
-pub fn builtin_modulo(a: f64, b: f64) -> Result<f64> {
-	Ok(a % b)
+pub fn builtin_modulo(x: f64, y: f64) -> f64 {
+	x % y
 }
 
 #[builtin]
-pub fn builtin_floor(x: f64) -> Result<f64> {
-	Ok(x.floor())
+pub fn builtin_floor(x: f64) -> f64 {
+	x.floor()
 }
 
 #[builtin]
-pub fn builtin_ceil(x: f64) -> Result<f64> {
-	Ok(x.ceil())
+pub fn builtin_ceil(x: f64) -> f64 {
+	x.ceil()
 }
 
 #[builtin]
-pub fn builtin_log(n: f64) -> Result<f64> {
-	Ok(n.ln())
+pub fn builtin_log(x: f64) -> f64 {
+	x.ln()
 }
 
 #[builtin]
-pub fn builtin_pow(x: f64, n: f64) -> Result<f64> {
-	Ok(x.powf(n))
+pub fn builtin_pow(x: f64, n: f64) -> f64 {
+	x.powf(n)
 }
 
 #[builtin]
-pub fn builtin_sqrt(x: PositiveF64) -> Result<f64> {
-	Ok(x.0.sqrt())
+pub fn builtin_sqrt(x: PositiveF64) -> f64 {
+	x.0.sqrt()
 }
 
 #[builtin]
-pub fn builtin_sin(x: f64) -> Result<f64> {
-	Ok(x.sin())
+pub fn builtin_sin(x: f64) -> f64 {
+	x.sin()
 }
 
 #[builtin]
-pub fn builtin_cos(x: f64) -> Result<f64> {
-	Ok(x.cos())
+pub fn builtin_cos(x: f64) -> f64 {
+	x.cos()
 }
 
 #[builtin]
-pub fn builtin_tan(x: f64) -> Result<f64> {
-	Ok(x.tan())
+pub fn builtin_tan(x: f64) -> f64 {
+	x.tan()
 }
 
 #[builtin]
-pub fn builtin_asin(x: f64) -> Result<f64> {
-	Ok(x.asin())
+pub fn builtin_asin(x: f64) -> f64 {
+	x.asin()
 }
 
 #[builtin]
-pub fn builtin_acos(x: f64) -> Result<f64> {
-	Ok(x.acos())
+pub fn builtin_acos(x: f64) -> f64 {
+	x.acos()
 }
 
 #[builtin]
-pub fn builtin_atan(x: f64) -> Result<f64> {
-	Ok(x.atan())
+pub fn builtin_atan(x: f64) -> f64 {
+	x.atan()
 }
 
 #[builtin]
-pub fn builtin_exp(x: f64) -> Result<f64> {
-	Ok(x.exp())
+pub fn builtin_exp(x: f64) -> f64 {
+	x.exp()
 }
 
 fn frexp(s: f64) -> (f64, i16) {
@@ -97,11 +101,11 @@
 }
 
 #[builtin]
-pub fn builtin_mantissa(x: f64) -> Result<f64> {
-	Ok(frexp(x).0)
+pub fn builtin_mantissa(x: f64) -> f64 {
+	frexp(x).0
 }
 
 #[builtin]
-pub fn builtin_exponent(x: f64) -> Result<i16> {
-	Ok(frexp(x).1)
+pub fn builtin_exponent(x: f64) -> i16 {
+	frexp(x).1
 }
modifiedcrates/jrsonnet-stdlib/src/misc.rsdiffbeforeafterboth
--- a/crates/jrsonnet-stdlib/src/misc.rs
+++ b/crates/jrsonnet-stdlib/src/misc.rs
@@ -4,7 +4,7 @@
 	error::{ErrorKind::*, Result},
 	function::{builtin, ArgLike, CallLocation, FuncVal},
 	throw,
-	typed::{Any, Either2, Either4},
+	typed::{Either2, Either4},
 	val::{equals, ArrValue},
 	Context, Either, IStr, ObjValue, Thunk, Val,
 };
@@ -12,45 +12,41 @@
 use crate::{extvar_source, Settings};
 
 #[builtin]
-pub fn builtin_length(x: Either![IStr, ArrValue, ObjValue, FuncVal]) -> Result<usize> {
+pub fn builtin_length(x: Either![IStr, ArrValue, ObjValue, FuncVal]) -> usize {
 	use Either4::*;
-	Ok(match x {
+	match x {
 		A(x) => x.chars().count(),
 		B(x) => x.len(),
 		C(x) => x.len(),
 		D(f) => f.params_len(),
-	})
+	}
 }
 
 #[builtin(fields(
 	settings: Rc<RefCell<Settings>>,
 ))]
-pub fn builtin_ext_var(this: &builtin_ext_var, ctx: Context, x: IStr) -> Result<Any> {
+pub fn builtin_ext_var(this: &builtin_ext_var, ctx: Context, x: IStr) -> Result<Val> {
 	let ctx = ctx.state().create_default_context(extvar_source(&x, ""));
-	Ok(Any(this
-		.settings
+	this.settings
 		.borrow()
 		.ext_vars
 		.get(&x)
 		.cloned()
 		.ok_or_else(|| UndefinedExternalVariable(x))?
 		.evaluate_arg(ctx, true)?
-		.evaluate()?))
+		.evaluate()
 }
 
 #[builtin(fields(
 	settings: Rc<RefCell<Settings>>,
 ))]
-pub fn builtin_native(this: &builtin_native, name: IStr) -> Result<Any> {
-	Ok(Any(this
-		.settings
+pub fn builtin_native(this: &builtin_native, x: IStr) -> Val {
+	this.settings
 		.borrow()
 		.ext_natives
-		.get(&name)
+		.get(&x)
 		.cloned()
-		.map_or(Val::Null, |v| {
-			Val::Func(FuncVal::Builtin(v.clone()))
-		})))
+		.map_or(Val::Null, |v| Val::Func(FuncVal::Builtin(v.clone())))
 }
 
 #[builtin(fields(
@@ -61,9 +57,9 @@
 	loc: CallLocation,
 	str: IStr,
 	rest: Thunk<Val>,
-) -> Result<Any> {
+) -> Result<Val> {
 	this.settings.borrow().trace_printer.print_trace(loc, str);
-	Ok(Any(rest.evaluate()?))
+	rest.evaluate()
 }
 
 #[allow(clippy::comparison_chain)]
modifiedcrates/jrsonnet-stdlib/src/objects.rsdiffbeforeafterboth
--- a/crates/jrsonnet-stdlib/src/objects.rs
+++ b/crates/jrsonnet-stdlib/src/objects.rs
@@ -1,7 +1,5 @@
 use jrsonnet_evaluator::{
-	error::Result,
 	function::builtin,
-	typed::VecVal,
 	val::{StrValue, Val},
 	IStr, ObjValue,
 };
@@ -9,25 +7,23 @@
 #[builtin]
 pub fn builtin_object_fields_ex(
 	obj: ObjValue,
-	inc_hidden: bool,
+	hidden: bool,
 	#[cfg(feature = "exp-preserve-order")] preserve_order: Option<bool>,
-) -> Result<VecVal> {
+) -> Vec<Val> {
 	#[cfg(feature = "exp-preserve-order")]
 	let preserve_order = preserve_order.unwrap_or(false);
 	let out = obj.fields_ex(
-		inc_hidden,
+		hidden,
 		#[cfg(feature = "exp-preserve-order")]
 		preserve_order,
 	);
-	Ok(VecVal(
-		out.into_iter()
-			.map(StrValue::Flat)
-			.map(Val::Str)
-			.collect::<Vec<_>>(),
-	))
+	out.into_iter()
+		.map(StrValue::Flat)
+		.map(Val::Str)
+		.collect::<Vec<_>>()
 }
 
 #[builtin]
-pub fn builtin_object_has_ex(obj: ObjValue, f: IStr, inc_hidden: bool) -> Result<bool> {
-	Ok(obj.has_field_ex(f, inc_hidden))
+pub fn builtin_object_has_ex(obj: ObjValue, fname: IStr, hidden: bool) -> bool {
+	obj.has_field_ex(fname, hidden)
 }
modifiedcrates/jrsonnet-stdlib/src/operator.rsdiffbeforeafterboth
--- a/crates/jrsonnet-stdlib/src/operator.rs
+++ b/crates/jrsonnet-stdlib/src/operator.rs
@@ -6,34 +6,34 @@
 	function::builtin,
 	operator::evaluate_mod_op,
 	stdlib::std_format,
-	typed::{Any, Either, Either2},
+	typed::{Either, Either2},
 	val::{equals, primitive_equals, StrValue},
 	IStr, Val,
 };
 
 #[builtin]
-pub fn builtin_mod(a: Either![f64, IStr], b: Any) -> Result<Any> {
+pub fn builtin_mod(a: Either![f64, IStr], b: Val) -> Result<Val> {
 	use Either2::*;
-	Ok(Any(evaluate_mod_op(
+	evaluate_mod_op(
 		&match a {
 			A(v) => Val::Num(v),
 			B(s) => Val::Str(StrValue::Flat(s)),
 		},
-		&b.0,
-	)?))
+		&b,
+	)
 }
 
 #[builtin]
-pub fn builtin_primitive_equals(a: Any, b: Any) -> Result<bool> {
-	primitive_equals(&a.0, &b.0)
+pub fn builtin_primitive_equals(x: Val, y: Val) -> Result<bool> {
+	primitive_equals(&x, &y)
 }
 
 #[builtin]
-pub fn builtin_equals(a: Any, b: Any) -> Result<bool> {
-	equals(&a.0, &b.0)
+pub fn builtin_equals(a: Val, b: Val) -> Result<bool> {
+	equals(&a, &b)
 }
 
 #[builtin]
-pub fn builtin_format(str: IStr, vals: Any) -> Result<String> {
-	std_format(&str, vals.0)
+pub fn builtin_format(str: IStr, vals: Val) -> Result<String> {
+	std_format(&str, vals)
 }
modifiedcrates/jrsonnet-stdlib/src/parse.rsdiffbeforeafterboth
--- a/crates/jrsonnet-stdlib/src/parse.rs
+++ b/crates/jrsonnet-stdlib/src/parse.rs
@@ -1,23 +1,22 @@
 use jrsonnet_evaluator::{
 	error::{ErrorKind::RuntimeError, Result},
 	function::builtin,
-	typed::Any,
 	IStr, Val,
 };
 use serde::Deserialize;
 
 #[builtin]
-pub fn builtin_parse_json(s: IStr) -> Result<Any> {
-	let value: Val = serde_json::from_str(&s)
+pub fn builtin_parse_json(str: IStr) -> Result<Val> {
+	let value: Val = serde_json::from_str(&str)
 		.map_err(|e| RuntimeError(format!("failed to parse json: {}", e).into()))?;
-	Ok(Any(value))
+	Ok(value)
 }
 
 #[builtin]
-pub fn builtin_parse_yaml(s: IStr) -> Result<Any> {
+pub fn builtin_parse_yaml(str: IStr) -> Result<Val> {
 	use serde_yaml_with_quirks::DeserializingQuirks;
 	let value = serde_yaml_with_quirks::Deserializer::from_str_with_quirks(
-		&s,
+		&str,
 		DeserializingQuirks { old_octals: true },
 	);
 	let mut out = vec![];
@@ -26,11 +25,11 @@
 			.map_err(|e| RuntimeError(format!("failed to parse yaml: {}", e).into()))?;
 		out.push(val);
 	}
-	Ok(Any(if out.is_empty() {
+	Ok(if out.is_empty() {
 		Val::Null
 	} else if out.len() == 1 {
 		out.into_iter().next().unwrap()
 	} else {
 		Val::Arr(out.into())
-	}))
+	})
 }
modifiedcrates/jrsonnet-stdlib/src/sort.rsdiffbeforeafterboth
--- a/crates/jrsonnet-stdlib/src/sort.rs
+++ b/crates/jrsonnet-stdlib/src/sort.rs
@@ -2,11 +2,9 @@
 	error::Result,
 	function::{builtin, CallLocation, FuncVal},
 	throw,
-	typed::Any,
 	val::ArrValue,
 	Context, Val,
 };
-use jrsonnet_gcmodule::Cc;
 
 #[derive(Copy, Clone)]
 enum SortKeyType {
@@ -78,7 +76,7 @@
 				key_getter.evaluate(
 					ctx.clone(),
 					CallLocation::native(),
-					&(Any(value.clone()),),
+					&(value.clone(),),
 					true,
 				)?,
 			));
@@ -105,9 +103,9 @@
 	if arr.len() <= 1 {
 		return Ok(arr);
 	}
-	Ok(ArrValue::eager(Cc::new(super::sort::sort(
+	Ok(ArrValue::eager(super::sort::sort(
 		ctx,
 		arr.iter().collect::<Result<Vec<_>>>()?,
 		keyF.unwrap_or_else(FuncVal::identity),
-	)?)))
+	)?))
 }
modifiedcrates/jrsonnet-stdlib/src/strings.rsdiffbeforeafterboth
--- a/crates/jrsonnet-stdlib/src/strings.rs
+++ b/crates/jrsonnet-stdlib/src/strings.rs
@@ -2,19 +2,19 @@
 	error::{ErrorKind::*, Result},
 	function::builtin,
 	throw,
-	typed::{Either2, VecVal, M1},
+	typed::{Either2, M1},
 	val::{ArrValue, StrValue},
 	Either, IStr, Val,
 };
 
 #[builtin]
-pub const fn builtin_codepoint(str: char) -> Result<u32> {
-	Ok(str as u32)
+pub const fn builtin_codepoint(str: char) -> u32 {
+	str as u32
 }
 
 #[builtin]
-pub fn builtin_substr(str: IStr, from: usize, len: usize) -> Result<String> {
-	Ok(str.chars().skip(from).take(len).collect())
+pub fn builtin_substr(str: IStr, from: usize, len: usize) -> String {
+	str.chars().skip(from).take(len).collect()
 }
 
 #[builtin]
@@ -23,14 +23,14 @@
 }
 
 #[builtin]
-pub fn builtin_str_replace(str: String, from: IStr, to: IStr) -> Result<String> {
-	Ok(str.replace(&from as &str, &to as &str))
+pub fn builtin_str_replace(str: String, from: IStr, to: IStr) -> String {
+	str.replace(&from as &str, &to as &str)
 }
 
 #[builtin]
-pub fn builtin_splitlimit(str: IStr, c: IStr, maxsplits: Either![usize, M1]) -> Result<VecVal> {
+pub fn builtin_splitlimit(str: IStr, c: IStr, maxsplits: Either![usize, M1]) -> ArrValue {
 	use Either2::*;
-	Ok(VecVal(match maxsplits {
+	match maxsplits {
 		A(n) => str
 			.splitn(n + 1, &c as &str)
 			.map(|s| Val::Str(StrValue::Flat(s.into())))
@@ -39,23 +39,23 @@
 			.split(&c as &str)
 			.map(|s| Val::Str(StrValue::Flat(s.into())))
 			.collect(),
-	}))
+	}
 }
 
 #[builtin]
-pub fn builtin_ascii_upper(str: IStr) -> Result<String> {
-	Ok(str.to_ascii_uppercase())
+pub fn builtin_ascii_upper(str: IStr) -> String {
+	str.to_ascii_uppercase()
 }
 
 #[builtin]
-pub fn builtin_ascii_lower(str: IStr) -> Result<String> {
-	Ok(str.to_ascii_lowercase())
+pub fn builtin_ascii_lower(str: IStr) -> String {
+	str.to_ascii_lowercase()
 }
 
 #[builtin]
-pub fn builtin_find_substr(pat: IStr, str: IStr) -> Result<ArrValue> {
+pub fn builtin_find_substr(pat: IStr, str: IStr) -> ArrValue {
 	if pat.is_empty() || str.is_empty() || pat.len() > str.len() {
-		return Ok(ArrValue::empty());
+		return ArrValue::empty();
 	}
 
 	let str = str.as_str();
@@ -74,7 +74,7 @@
 			out.push(Val::Num(ch_idx as f64))
 		}
 	}
-	Ok(out.into())
+	out.into()
 }
 
 #[builtin]
modifiedcrates/jrsonnet-stdlib/src/types.rsdiffbeforeafterboth
--- a/crates/jrsonnet-stdlib/src/types.rs
+++ b/crates/jrsonnet-stdlib/src/types.rs
@@ -1,31 +1,31 @@
-use jrsonnet_evaluator::{error::Result, function::builtin, typed::Any, IStr, Val};
+use jrsonnet_evaluator::{function::builtin, IStr, Val};
 
 #[builtin]
-pub fn builtin_type(v: Any) -> Result<IStr> {
-	Ok(v.0.value_type().name().into())
+pub fn builtin_type(x: Val) -> IStr {
+	x.value_type().name().into()
 }
 
 #[builtin]
-pub fn builtin_is_string(v: Any) -> Result<bool> {
-	Ok(matches!(v.0, Val::Str(_)))
+pub fn builtin_is_string(v: Val) -> bool {
+	matches!(v, Val::Str(_))
 }
 #[builtin]
-pub fn builtin_is_number(v: Any) -> Result<bool> {
-	Ok(matches!(v.0, Val::Num(_)))
+pub fn builtin_is_number(v: Val) -> bool {
+	matches!(v, Val::Num(_))
 }
 #[builtin]
-pub fn builtin_is_boolean(v: Any) -> Result<bool> {
-	Ok(matches!(v.0, Val::Bool(_)))
+pub fn builtin_is_boolean(v: Val) -> bool {
+	matches!(v, Val::Bool(_))
 }
 #[builtin]
-pub fn builtin_is_object(v: Any) -> Result<bool> {
-	Ok(matches!(v.0, Val::Obj(_)))
+pub fn builtin_is_object(v: Val) -> bool {
+	matches!(v, Val::Obj(_))
 }
 #[builtin]
-pub fn builtin_is_array(v: Any) -> Result<bool> {
-	Ok(matches!(v.0, Val::Arr(_)))
+pub fn builtin_is_array(v: Val) -> bool {
+	matches!(v, Val::Arr(_))
 }
 #[builtin]
-pub fn builtin_is_function(v: Any) -> Result<bool> {
-	Ok(matches!(v.0, Val::Func(_)))
+pub fn builtin_is_function(v: Val) -> bool {
+	matches!(v, Val::Func(_))
 }
modifiedtests/tests/builtin.rsdiffbeforeafterboth
--- a/tests/tests/builtin.rs
+++ b/tests/tests/builtin.rs
@@ -3,11 +3,9 @@
 use jrsonnet_evaluator::{
 	error::Result,
 	function::{builtin, builtin::Builtin, CallLocation, FuncVal},
-	tb,
 	typed::Typed,
 	ContextBuilder, State, Thunk, Val,
 };
-use jrsonnet_gcmodule::Cc;
 use jrsonnet_stdlib::StateExt;
 
 #[builtin]
@@ -63,7 +61,7 @@
 
 #[builtin]
 fn curry_add(a: u32) -> Result<FuncVal> {
-	Ok(FuncVal::Builtin(Cc::new(tb!(curried_add { a }))))
+	Ok(FuncVal::builtin(curried_add { a }))
 }
 
 #[test]
modifiedtests/tests/common.rsdiffbeforeafterboth
--- a/tests/tests/common.rs
+++ b/tests/tests/common.rs
@@ -1,3 +1,5 @@
+use std::borrow::Cow;
+
 use jrsonnet_evaluator::{
 	error::Result,
 	function::{builtin, FuncVal},
@@ -53,13 +55,47 @@
 	Ok(true)
 }
 
+#[builtin]
+fn param_names(fun: FuncVal) -> Vec<String> {
+	match fun {
+		FuncVal::Id => vec!["x".to_string()],
+		FuncVal::Normal(func) => func
+			.params
+			.iter()
+			.map(|p| p.0.name().unwrap_or_else(|| "<unnamed>".into()).to_string())
+			.collect(),
+		FuncVal::StaticBuiltin(b) => b
+			.params()
+			.iter()
+			.map(|p| {
+				p.name
+					.as_ref()
+					.unwrap_or(&Cow::Borrowed("<unnamed>"))
+					.to_string()
+			})
+			.collect(),
+		FuncVal::Builtin(b) => b
+			.params()
+			.iter()
+			.map(|p| {
+				p.name
+					.as_ref()
+					.unwrap_or(&Cow::Borrowed("<unnamed>"))
+					.to_string()
+			})
+			.collect(),
+	}
+}
+
 #[allow(dead_code)]
 pub fn with_test(s: &State) {
 	let mut bobj = ObjValueBuilder::new();
 	bobj.member("assertThrow".into())
 		.hide()
-		.value(Val::Func(FuncVal::StaticBuiltin(assert_throw::INST)))
-		.expect("no error");
+		.value_unchecked(Val::Func(FuncVal::StaticBuiltin(assert_throw::INST)));
+	bobj.member("paramNames".into())
+		.hide()
+		.value_unchecked(Val::Func(FuncVal::StaticBuiltin(param_names::INST)));
 
 	s.add_global("test".into(), Thunk::evaluated(Val::Obj(bobj.build())))
 }
modifiedtests/tests/golden.rsdiffbeforeafterboth
--- a/tests/tests/golden.rs
+++ b/tests/tests/golden.rs
@@ -16,7 +16,7 @@
 	let s = State::default();
 	s.with_stdlib();
 	common::with_test(&s);
-	s.set_import_resolver(Box::new(FileImportResolver::default()));
+	s.set_import_resolver(FileImportResolver::default());
 	let trace_format = CompactFormat {
 		resolver: PathResolver::FileName,
 		max_trace: 20,
modifiedtests/tests/suite.rsdiffbeforeafterboth
--- a/tests/tests/suite.rs
+++ b/tests/tests/suite.rs
@@ -15,7 +15,7 @@
 	let s = State::default();
 	s.with_stdlib();
 	common::with_test(&s);
-	s.set_import_resolver(Box::new(FileImportResolver::default()));
+	s.set_import_resolver(FileImportResolver::default());
 	let trace_format = CompactFormat::default();
 
 	match s.import(file) {