git.delta.rocks / jrsonnet / refs/commits / cb29d2975edf

difftreelog

refactor remove implicit global state

Yaroslav Bolyukin2022-04-21parent: #0d5cefb.patch.diff
in: master

31 files changed

modifiedbindings/jsonnet/src/import.rsdiffbeforeafterboth
--- a/bindings/jsonnet/src/import.rs
+++ b/bindings/jsonnet/src/import.rs
@@ -15,7 +15,7 @@
 
 use jrsonnet_evaluator::{
 	error::{Error::*, Result},
-	throw, EvaluationState, ImportResolver,
+	throw, ImportResolver, State,
 };
 
 pub type JsonnetImportCallback = unsafe extern "C" fn(
@@ -87,7 +87,7 @@
 /// # Safety
 #[no_mangle]
 pub unsafe extern "C" fn jsonnet_import_callback(
-	vm: &EvaluationState,
+	vm: &State,
 	cb: JsonnetImportCallback,
 	ctx: *mut c_void,
 ) {
@@ -141,7 +141,7 @@
 ///
 /// This function is safe, if received v is a pointer to normal C string
 #[no_mangle]
-pub unsafe extern "C" fn jsonnet_jpath_add(vm: &EvaluationState, v: *const c_char) {
+pub unsafe extern "C" fn jsonnet_jpath_add(vm: &State, v: *const c_char) {
 	let cstr = CStr::from_ptr(v);
 	let path = PathBuf::from(cstr.to_str().unwrap());
 	let any_resolver = &vm.settings().import_resolver;
modifiedbindings/jsonnet/src/interop.rsdiffbeforeafterboth
--- a/bindings/jsonnet/src/interop.rs
+++ b/bindings/jsonnet/src/interop.rs
@@ -5,7 +5,7 @@
 	os::raw::{c_char, c_int},
 };
 
-use jrsonnet_evaluator::{EvaluationState, Val};
+use jrsonnet_evaluator::{State, Val};
 
 use crate::{import::jsonnet_import_callback, native::jsonnet_native_callback};
 
@@ -28,17 +28,14 @@
 
 /// # Safety
 #[no_mangle]
-pub unsafe extern "C" fn jrsonnet_apply_static_import_callback(
-	vm: &EvaluationState,
-	ctx: *mut c_void,
-) {
+pub unsafe extern "C" fn jrsonnet_apply_static_import_callback(vm: &State, ctx: *mut c_void) {
 	jsonnet_import_callback(vm, _jrsonnet_static_import_callback, ctx)
 }
 
 /// # Safety
 #[no_mangle]
 pub unsafe extern "C" fn jrsonnet_apply_static_native_callback(
-	vm: &EvaluationState,
+	vm: &State,
 	name: *const c_char,
 	ctx: *mut c_void,
 	raw_params: *const *const c_char,
@@ -47,7 +44,7 @@
 }
 
 #[no_mangle]
-pub extern "C" fn jrsonnet_set_trace_format(vm: &EvaluationState, format: u8) {
+pub extern "C" fn jrsonnet_set_trace_format(vm: &State, format: u8) {
 	use jrsonnet_evaluator::trace::JsFormat;
 	match format {
 		1 => vm.set_trace_format(Box::new(JsFormat)),
modifiedbindings/jsonnet/src/lib.rsdiffbeforeafterboth
--- a/bindings/jsonnet/src/lib.rs
+++ b/bindings/jsonnet/src/lib.rs
@@ -16,7 +16,7 @@
 };
 
 use import::NativeImportResolver;
-use jrsonnet_evaluator::{EvaluationState, IStr, ManifestFormat, Val};
+use jrsonnet_evaluator::{IStr, ManifestFormat, State, Val};
 
 /// WASM stub
 #[cfg(target_arch = "wasm32")]
@@ -29,8 +29,8 @@
 }
 
 #[no_mangle]
-pub extern "C" fn jsonnet_make() -> *mut EvaluationState {
-	let state = EvaluationState::default();
+pub extern "C" fn jsonnet_make() -> *mut State {
+	let state = State::default();
 	state.with_stdlib();
 	state.settings_mut().import_resolver = Box::new(NativeImportResolver::default());
 	Box::into_raw(Box::new(state))
@@ -39,23 +39,23 @@
 /// # Safety
 #[no_mangle]
 #[allow(clippy::boxed_local)]
-pub unsafe extern "C" fn jsonnet_destroy(vm: *mut EvaluationState) {
+pub unsafe extern "C" fn jsonnet_destroy(vm: *mut State) {
 	Box::from_raw(vm);
 }
 
 #[no_mangle]
-pub extern "C" fn jsonnet_max_stack(vm: &EvaluationState, v: c_uint) {
+pub extern "C" fn jsonnet_max_stack(vm: &State, v: c_uint) {
 	vm.settings_mut().max_stack = v as usize;
 }
 
 // jrsonnet currently have no GC, so these functions is no-op
 #[no_mangle]
-pub extern "C" fn jsonnet_gc_min_objects(_vm: &EvaluationState, _v: c_uint) {}
+pub extern "C" fn jsonnet_gc_min_objects(_vm: &State, _v: c_uint) {}
 #[no_mangle]
-pub extern "C" fn jsonnet_gc_growth_trigger(_vm: &EvaluationState, _v: c_double) {}
+pub extern "C" fn jsonnet_gc_growth_trigger(_vm: &State, _v: c_double) {}
 
 #[no_mangle]
-pub extern "C" fn jsonnet_string_output(vm: &EvaluationState, v: c_int) {
+pub extern "C" fn jsonnet_string_output(vm: &State, v: c_int) {
 	match v {
 		1 => vm.set_manifest_format(ManifestFormat::String),
 		0 => vm.set_manifest_format(ManifestFormat::Json {
@@ -71,11 +71,7 @@
 ///
 /// This function is most definitely broken, but it works somehow, see TODO inside
 #[no_mangle]
-pub unsafe extern "C" fn jsonnet_realloc(
-	_vm: &EvaluationState,
-	buf: *mut u8,
-	sz: usize,
-) -> *mut u8 {
+pub unsafe extern "C" fn jsonnet_realloc(_vm: &State, buf: *mut u8, sz: usize) -> *mut u8 {
 	if buf.is_null() {
 		assert!(sz != 0);
 		return std::alloc::alloc(Layout::from_size_align(sz, std::mem::align_of::<u8>()).unwrap());
@@ -95,12 +91,12 @@
 /// # Safety
 #[no_mangle]
 #[allow(clippy::boxed_local)]
-pub unsafe extern "C" fn jsonnet_json_destroy(_vm: &EvaluationState, v: *mut Val) {
+pub unsafe extern "C" fn jsonnet_json_destroy(_vm: &State, v: *mut Val) {
 	Box::from_raw(v);
 }
 
 #[no_mangle]
-pub extern "C" fn jsonnet_max_trace(vm: &EvaluationState, v: c_uint) {
+pub extern "C" fn jsonnet_max_trace(vm: &State, v: c_uint) {
 	vm.set_max_trace(v as usize)
 }
 
@@ -109,28 +105,26 @@
 /// This function is safe, if received v is a pointer to normal C string
 #[no_mangle]
 pub unsafe extern "C" fn jsonnet_evaluate_file(
-	vm: &EvaluationState,
+	vm: &State,
 	filename: *const c_char,
 	error: &mut c_int,
 ) -> *const c_char {
-	vm.run_in_state(|| {
-		let filename = CStr::from_ptr(filename);
-		match vm
-			.evaluate_file_raw_nocwd(&PathBuf::from(filename.to_str().unwrap()))
-			.and_then(|v| vm.with_tla(v))
-			.and_then(|v| vm.manifest(v))
-		{
-			Ok(v) => {
-				*error = 0;
-				CString::new(&*v as &str).unwrap().into_raw()
-			}
-			Err(e) => {
-				*error = 1;
-				let out = vm.stringify_err(&e);
-				CString::new(&out as &str).unwrap().into_raw()
-			}
+	let filename = CStr::from_ptr(filename);
+	match vm
+		.evaluate_file_raw_nocwd(&PathBuf::from(filename.to_str().unwrap()))
+		.and_then(|v| vm.with_tla(v))
+		.and_then(|v| vm.manifest(v))
+	{
+		Ok(v) => {
+			*error = 0;
+			CString::new(&*v as &str).unwrap().into_raw()
+		}
+		Err(e) => {
+			*error = 1;
+			let out = vm.stringify_err(&e);
+			CString::new(&out as &str).unwrap().into_raw()
 		}
-	})
+	}
 }
 
 /// # Safety
@@ -138,33 +132,31 @@
 /// This function is safe, if received v is a pointer to normal C string
 #[no_mangle]
 pub unsafe extern "C" fn jsonnet_evaluate_snippet(
-	vm: &EvaluationState,
+	vm: &State,
 	filename: *const c_char,
 	snippet: *const c_char,
 	error: &mut c_int,
 ) -> *const c_char {
-	vm.run_in_state(|| {
-		let filename = CStr::from_ptr(filename);
-		let snippet = CStr::from_ptr(snippet);
-		match vm
-			.evaluate_snippet_raw(
-				PathBuf::from(filename.to_str().unwrap()).into(),
-				snippet.to_str().unwrap().into(),
-			)
-			.and_then(|v| vm.with_tla(v))
-			.and_then(|v| vm.manifest(v))
-		{
-			Ok(v) => {
-				*error = 0;
-				CString::new(&*v as &str).unwrap().into_raw()
-			}
-			Err(e) => {
-				*error = 1;
-				let out = vm.stringify_err(&e);
-				CString::new(&out as &str).unwrap().into_raw()
-			}
+	let filename = CStr::from_ptr(filename);
+	let snippet = CStr::from_ptr(snippet);
+	match vm
+		.evaluate_snippet_raw(
+			PathBuf::from(filename.to_str().unwrap()).into(),
+			snippet.to_str().unwrap().into(),
+		)
+		.and_then(|v| vm.with_tla(v))
+		.and_then(|v| vm.manifest(v))
+	{
+		Ok(v) => {
+			*error = 0;
+			CString::new(&*v as &str).unwrap().into_raw()
 		}
-	})
+		Err(e) => {
+			*error = 1;
+			let out = vm.stringify_err(&e);
+			CString::new(&out as &str).unwrap().into_raw()
+		}
+	}
 }
 
 fn multi_to_raw(multi: Vec<(IStr, IStr)>) -> *const c_char {
@@ -187,60 +179,56 @@
 /// # Safety
 #[no_mangle]
 pub unsafe extern "C" fn jsonnet_evaluate_file_multi(
-	vm: &EvaluationState,
+	vm: &State,
 	filename: *const c_char,
 	error: &mut c_int,
 ) -> *const c_char {
-	vm.run_in_state(|| {
-		let filename = CStr::from_ptr(filename);
-		match vm
-			.evaluate_file_raw_nocwd(&PathBuf::from(filename.to_str().unwrap()))
-			.and_then(|v| vm.with_tla(v))
-			.and_then(|v| vm.manifest_multi(v))
-		{
-			Ok(v) => {
-				*error = 0;
-				multi_to_raw(v)
-			}
-			Err(e) => {
-				*error = 1;
-				let out = vm.stringify_err(&e);
-				CString::new(&out as &str).unwrap().into_raw()
-			}
+	let filename = CStr::from_ptr(filename);
+	match vm
+		.evaluate_file_raw_nocwd(&PathBuf::from(filename.to_str().unwrap()))
+		.and_then(|v| vm.with_tla(v))
+		.and_then(|v| vm.manifest_multi(v))
+	{
+		Ok(v) => {
+			*error = 0;
+			multi_to_raw(v)
+		}
+		Err(e) => {
+			*error = 1;
+			let out = vm.stringify_err(&e);
+			CString::new(&out as &str).unwrap().into_raw()
 		}
-	})
+	}
 }
 
 /// # Safety
 #[no_mangle]
 pub unsafe extern "C" fn jsonnet_evaluate_snippet_multi(
-	vm: &EvaluationState,
+	vm: &State,
 	filename: *const c_char,
 	snippet: *const c_char,
 	error: &mut c_int,
 ) -> *const c_char {
-	vm.run_in_state(|| {
-		let filename = CStr::from_ptr(filename);
-		let snippet = CStr::from_ptr(snippet);
-		match vm
-			.evaluate_snippet_raw(
-				PathBuf::from(filename.to_str().unwrap()).into(),
-				snippet.to_str().unwrap().into(),
-			)
-			.and_then(|v| vm.with_tla(v))
-			.and_then(|v| vm.manifest_multi(v))
-		{
-			Ok(v) => {
-				*error = 0;
-				multi_to_raw(v)
-			}
-			Err(e) => {
-				*error = 1;
-				let out = vm.stringify_err(&e);
-				CString::new(&out as &str).unwrap().into_raw()
-			}
+	let filename = CStr::from_ptr(filename);
+	let snippet = CStr::from_ptr(snippet);
+	match vm
+		.evaluate_snippet_raw(
+			PathBuf::from(filename.to_str().unwrap()).into(),
+			snippet.to_str().unwrap().into(),
+		)
+		.and_then(|v| vm.with_tla(v))
+		.and_then(|v| vm.manifest_multi(v))
+	{
+		Ok(v) => {
+			*error = 0;
+			multi_to_raw(v)
+		}
+		Err(e) => {
+			*error = 1;
+			let out = vm.stringify_err(&e);
+			CString::new(&out as &str).unwrap().into_raw()
 		}
-	})
+	}
 }
 
 fn stream_to_raw(multi: Vec<IStr>) -> *const c_char {
@@ -261,58 +249,54 @@
 /// # Safety
 #[no_mangle]
 pub unsafe extern "C" fn jsonnet_evaluate_file_stream(
-	vm: &EvaluationState,
+	vm: &State,
 	filename: *const c_char,
 	error: &mut c_int,
 ) -> *const c_char {
-	vm.run_in_state(|| {
-		let filename = CStr::from_ptr(filename);
-		match vm
-			.evaluate_file_raw_nocwd(&PathBuf::from(filename.to_str().unwrap()))
-			.and_then(|v| vm.with_tla(v))
-			.and_then(|v| vm.manifest_stream(v))
-		{
-			Ok(v) => {
-				*error = 0;
-				stream_to_raw(v)
-			}
-			Err(e) => {
-				*error = 1;
-				let out = vm.stringify_err(&e);
-				CString::new(&out as &str).unwrap().into_raw()
-			}
+	let filename = CStr::from_ptr(filename);
+	match vm
+		.evaluate_file_raw_nocwd(&PathBuf::from(filename.to_str().unwrap()))
+		.and_then(|v| vm.with_tla(v))
+		.and_then(|v| vm.manifest_stream(v))
+	{
+		Ok(v) => {
+			*error = 0;
+			stream_to_raw(v)
+		}
+		Err(e) => {
+			*error = 1;
+			let out = vm.stringify_err(&e);
+			CString::new(&out as &str).unwrap().into_raw()
 		}
-	})
+	}
 }
 
 /// # Safety
 #[no_mangle]
 pub unsafe extern "C" fn jsonnet_evaluate_snippet_stream(
-	vm: &EvaluationState,
+	vm: &State,
 	filename: *const c_char,
 	snippet: *const c_char,
 	error: &mut c_int,
 ) -> *const c_char {
-	vm.run_in_state(|| {
-		let filename = CStr::from_ptr(filename);
-		let snippet = CStr::from_ptr(snippet);
-		match vm
-			.evaluate_snippet_raw(
-				PathBuf::from(filename.to_str().unwrap()).into(),
-				snippet.to_str().unwrap().into(),
-			)
-			.and_then(|v| vm.with_tla(v))
-			.and_then(|v| vm.manifest_stream(v))
-		{
-			Ok(v) => {
-				*error = 0;
-				stream_to_raw(v)
-			}
-			Err(e) => {
-				*error = 1;
-				let out = vm.stringify_err(&e);
-				CString::new(&out as &str).unwrap().into_raw()
-			}
+	let filename = CStr::from_ptr(filename);
+	let snippet = CStr::from_ptr(snippet);
+	match vm
+		.evaluate_snippet_raw(
+			PathBuf::from(filename.to_str().unwrap()).into(),
+			snippet.to_str().unwrap().into(),
+		)
+		.and_then(|v| vm.with_tla(v))
+		.and_then(|v| vm.manifest_stream(v))
+	{
+		Ok(v) => {
+			*error = 0;
+			stream_to_raw(v)
 		}
-	})
+		Err(e) => {
+			*error = 1;
+			let out = vm.stringify_err(&e);
+			CString::new(&out as &str).unwrap().into_raw()
+		}
+	}
 }
modifiedbindings/jsonnet/src/native.rsdiffbeforeafterboth
--- a/bindings/jsonnet/src/native.rs
+++ b/bindings/jsonnet/src/native.rs
@@ -1,5 +1,4 @@
 use std::{
-	convert::TryFrom,
 	ffi::{c_void, CStr},
 	os::raw::{c_char, c_int},
 	path::Path,
@@ -12,7 +11,8 @@
 	function::BuiltinParam,
 	gc::TraceBox,
 	native::{NativeCallback, NativeCallbackHandler},
-	EvaluationState, IStr, Val,
+	typed::Typed,
+	IStr, State, Val,
 };
 
 type JsonnetNativeCallback = unsafe extern "C" fn(
@@ -29,7 +29,7 @@
 	cb: JsonnetNativeCallback,
 }
 impl NativeCallbackHandler for JsonnetNativeCallbackHandler {
-	fn call(&self, _from: Option<Rc<Path>>, args: &[Val]) -> Result<Val, LocError> {
+	fn call(&self, s: State, _from: Option<Rc<Path>>, args: &[Val]) -> Result<Val, LocError> {
 		let mut n_args = Vec::new();
 		for a in args {
 			n_args.push(Some(Box::new(a.clone())));
@@ -47,7 +47,7 @@
 		if success == 1 {
 			Ok(v)
 		} else {
-			let e = IStr::try_from(v).expect("error msg");
+			let e = IStr::from_untyped(v, s).expect("error msg");
 			Err(Error::RuntimeError(e).into())
 		}
 	}
@@ -56,7 +56,7 @@
 /// # Safety
 #[no_mangle]
 pub unsafe extern "C" fn jsonnet_native_callback(
-	vm: &EvaluationState,
+	vm: &State,
 	name: *const c_char,
 	cb: JsonnetNativeCallback,
 	ctx: *const c_void,
modifiedbindings/jsonnet/src/val_extract.rsdiffbeforeafterboth
--- a/bindings/jsonnet/src/val_extract.rs
+++ b/bindings/jsonnet/src/val_extract.rs
@@ -5,21 +5,17 @@
 	os::raw::{c_char, c_double, c_int},
 };
 
-use jrsonnet_evaluator::{EvaluationState, Val};
+use jrsonnet_evaluator::{State, Val};
 
 #[no_mangle]
-pub extern "C" fn jsonnet_json_extract_string(_vm: &EvaluationState, v: &Val) -> *mut c_char {
+pub extern "C" fn jsonnet_json_extract_string(_vm: &State, v: &Val) -> *mut c_char {
 	match v {
 		Val::Str(s) => CString::new(&*s as &str).unwrap().into_raw(),
 		_ => std::ptr::null_mut(),
 	}
 }
 #[no_mangle]
-pub extern "C" fn jsonnet_json_extract_number(
-	_vm: &EvaluationState,
-	v: &Val,
-	out: &mut c_double,
-) -> c_int {
+pub extern "C" fn jsonnet_json_extract_number(_vm: &State, v: &Val, out: &mut c_double) -> c_int {
 	match v {
 		Val::Num(n) => {
 			*out = *n;
@@ -29,7 +25,7 @@
 	}
 }
 #[no_mangle]
-pub extern "C" fn jsonnet_json_extract_bool(_vm: &EvaluationState, v: &Val) -> c_int {
+pub extern "C" fn jsonnet_json_extract_bool(_vm: &State, v: &Val) -> c_int {
 	match v {
 		Val::Bool(false) => 0,
 		Val::Bool(true) => 1,
@@ -37,7 +33,7 @@
 	}
 }
 #[no_mangle]
-pub extern "C" fn jsonnet_json_extract_null(_vm: &EvaluationState, v: &Val) -> c_int {
+pub extern "C" fn jsonnet_json_extract_null(_vm: &State, v: &Val) -> c_int {
 	match v {
 		Val::Null => 1,
 		_ => 0,
modifiedbindings/jsonnet/src/val_make.rsdiffbeforeafterboth
--- a/bindings/jsonnet/src/val_make.rs
+++ b/bindings/jsonnet/src/val_make.rs
@@ -6,43 +6,40 @@
 };
 
 use gcmodule::Cc;
-use jrsonnet_evaluator::{val::ArrValue, EvaluationState, ObjValue, Val};
+use jrsonnet_evaluator::{val::ArrValue, ObjValue, State, Val};
 
 /// # Safety
 ///
 /// This function is safe, if received v is a pointer to normal C string
 #[no_mangle]
-pub unsafe extern "C" fn jsonnet_json_make_string(
-	_vm: &EvaluationState,
-	v: *const c_char,
-) -> *mut Val {
+pub unsafe extern "C" fn jsonnet_json_make_string(_vm: &State, v: *const c_char) -> *mut Val {
 	let cstr = CStr::from_ptr(v);
 	let str = cstr.to_str().unwrap();
 	Box::into_raw(Box::new(Val::Str(str.into())))
 }
 
 #[no_mangle]
-pub extern "C" fn jsonnet_json_make_number(_vm: &EvaluationState, v: c_double) -> *mut Val {
+pub extern "C" fn jsonnet_json_make_number(_vm: &State, v: c_double) -> *mut Val {
 	Box::into_raw(Box::new(Val::Num(v)))
 }
 
 #[no_mangle]
-pub extern "C" fn jsonnet_json_make_bool(_vm: &EvaluationState, v: c_int) -> *mut Val {
+pub extern "C" fn jsonnet_json_make_bool(_vm: &State, v: c_int) -> *mut Val {
 	assert!(v == 0 || v == 1);
 	Box::into_raw(Box::new(Val::Bool(v == 1)))
 }
 
 #[no_mangle]
-pub extern "C" fn jsonnet_json_make_null(_vm: &EvaluationState) -> *mut Val {
+pub extern "C" fn jsonnet_json_make_null(_vm: &State) -> *mut Val {
 	Box::into_raw(Box::new(Val::Null))
 }
 
 #[no_mangle]
-pub extern "C" fn jsonnet_json_make_array(_vm: &EvaluationState) -> *mut Val {
+pub extern "C" fn jsonnet_json_make_array(_vm: &State) -> *mut Val {
 	Box::into_raw(Box::new(Val::Arr(ArrValue::Eager(Cc::new(Vec::new())))))
 }
 
 #[no_mangle]
-pub extern "C" fn jsonnet_json_make_object(_vm: &EvaluationState) -> *mut Val {
+pub extern "C" fn jsonnet_json_make_object(_vm: &State) -> *mut Val {
 	Box::into_raw(Box::new(Val::Obj(ObjValue::new_empty())))
 }
modifiedbindings/jsonnet/src/val_modify.rsdiffbeforeafterboth
--- a/bindings/jsonnet/src/val_modify.rs
+++ b/bindings/jsonnet/src/val_modify.rs
@@ -5,17 +5,13 @@
 use std::{ffi::CStr, os::raw::c_char};
 
 use gcmodule::Cc;
-use jrsonnet_evaluator::{val::ArrValue, EvaluationState, LazyVal, Val};
+use jrsonnet_evaluator::{val::ArrValue, LazyVal, State, Val};
 
 /// # Safety
 ///
 /// Received arr value should be correct pointer to array allocated by make_array
 #[no_mangle]
-pub unsafe extern "C" fn jsonnet_json_array_append(
-	_vm: &EvaluationState,
-	arr: &mut Val,
-	val: &Val,
-) {
+pub unsafe extern "C" fn jsonnet_json_array_append(_vm: &State, arr: &mut Val, val: &Val) {
 	match arr {
 		Val::Arr(old) => {
 			let mut new = Vec::new();
@@ -34,7 +30,7 @@
 /// This function is safe if passed name is ok
 #[no_mangle]
 pub unsafe extern "C" fn jsonnet_json_object_append(
-	_vm: &EvaluationState,
+	_vm: &State,
 	obj: &mut Val,
 	name: *const c_char,
 	val: &Val,
modifiedbindings/jsonnet/src/vars_tlas.rsdiffbeforeafterboth
--- a/bindings/jsonnet/src/vars_tlas.rs
+++ b/bindings/jsonnet/src/vars_tlas.rs
@@ -2,15 +2,11 @@
 
 use std::{ffi::CStr, os::raw::c_char};
 
-use jrsonnet_evaluator::EvaluationState;
+use jrsonnet_evaluator::State;
 
 /// # Safety
 #[no_mangle]
-pub unsafe extern "C" fn jsonnet_ext_var(
-	vm: &EvaluationState,
-	name: *const c_char,
-	value: *const c_char,
-) {
+pub unsafe extern "C" fn jsonnet_ext_var(vm: &State, name: *const c_char, value: *const c_char) {
 	let name = CStr::from_ptr(name);
 	let value = CStr::from_ptr(value);
 	vm.add_ext_str(
@@ -21,11 +17,7 @@
 
 /// # Safety
 #[no_mangle]
-pub unsafe extern "C" fn jsonnet_ext_code(
-	vm: &EvaluationState,
-	name: *const c_char,
-	value: *const c_char,
-) {
+pub unsafe extern "C" fn jsonnet_ext_code(vm: &State, name: *const c_char, value: *const c_char) {
 	let name = CStr::from_ptr(name);
 	let value = CStr::from_ptr(value);
 	vm.add_ext_code(
@@ -36,11 +28,7 @@
 }
 /// # Safety
 #[no_mangle]
-pub unsafe extern "C" fn jsonnet_tla_var(
-	vm: &EvaluationState,
-	name: *const c_char,
-	value: *const c_char,
-) {
+pub unsafe extern "C" fn jsonnet_tla_var(vm: &State, name: *const c_char, value: *const c_char) {
 	let name = CStr::from_ptr(name);
 	let value = CStr::from_ptr(value);
 	vm.add_tla_str(
@@ -50,11 +38,7 @@
 }
 /// # Safety
 #[no_mangle]
-pub unsafe extern "C" fn jsonnet_tla_code(
-	vm: &EvaluationState,
-	name: *const c_char,
-	value: *const c_char,
-) {
+pub unsafe extern "C" fn jsonnet_tla_code(vm: &State, name: *const c_char, value: *const c_char) {
 	let name = CStr::from_ptr(name);
 	let value = CStr::from_ptr(value);
 	vm.add_tla_code(
modifiedcmds/jrsonnet/src/main.rsdiffbeforeafterboth
--- a/cmds/jrsonnet/src/main.rs
+++ b/cmds/jrsonnet/src/main.rs
@@ -7,7 +7,7 @@
 use clap::{AppSettings, IntoApp, Parser};
 use clap_complete::Shell;
 use jrsonnet_cli::{ConfigureState, GcOpts, GeneralOpts, InputOpts, ManifestOpts, OutputOpts};
-use jrsonnet_evaluator::{error::LocError, EvaluationState};
+use jrsonnet_evaluator::{error::LocError, State};
 
 #[cfg(feature = "mimalloc")]
 #[global_allocator]
@@ -102,10 +102,10 @@
 
 fn main_catch(opts: Opts) -> bool {
 	let _printer = opts.gc.stats_printer();
-	let state = EvaluationState::default();
-	if let Err(e) = main_real(&state, opts) {
+	let s = State::default();
+	if let Err(e) = main_real(&s, opts) {
 		if let Error::Evaluation(e) = e {
-			eprintln!("{}", state.stringify_err(&e));
+			eprintln!("{}", s.stringify_err(&e));
 		} else {
 			eprintln!("{}", e);
 		}
@@ -114,13 +114,13 @@
 	true
 }
 
-fn main_real(state: &EvaluationState, opts: Opts) -> Result<(), Error> {
+fn main_real(s: &State, opts: Opts) -> Result<(), Error> {
 	opts.gc.configure_global();
-	opts.general.configure(state)?;
-	opts.manifest.configure(state)?;
+	opts.general.configure(s)?;
+	opts.manifest.configure(s)?;
 
 	let val = if opts.input.exec {
-		state.evaluate_snippet_raw(
+		s.evaluate_snippet_raw(
 			PathBuf::from("<cmdline>").into(),
 			(&opts.input.input as &str).into(),
 		)?
@@ -128,12 +128,12 @@
 		let mut input = Vec::new();
 		std::io::stdin().read_to_end(&mut input)?;
 		let input_str = std::str::from_utf8(&input)?.into();
-		state.evaluate_snippet_raw(PathBuf::from("<stdin>").into(), input_str)?
+		s.evaluate_snippet_raw(PathBuf::from("<stdin>").into(), input_str)?
 	} else {
-		state.evaluate_file_raw(&PathBuf::from(opts.input.input))?
+		s.evaluate_file_raw(&PathBuf::from(opts.input.input))?
 	};
 
-	let val = state.with_tla(val)?;
+	let val = s.with_tla(val)?;
 
 	if let Some(multi) = opts.output.multi {
 		if opts.output.create_output_dirs {
@@ -141,7 +141,7 @@
 			dir.pop();
 			create_dir_all(dir)?;
 		}
-		for (file, data) in state.manifest_multi(val)?.iter() {
+		for (file, data) in s.manifest_multi(val)?.iter() {
 			let mut path = multi.clone();
 			path.push(file as &str);
 			if opts.output.create_output_dirs {
@@ -160,9 +160,9 @@
 			create_dir_all(dir)?;
 		}
 		let mut file = File::create(path)?;
-		writeln!(file, "{}", state.manifest(val)?)?;
+		writeln!(file, "{}", s.manifest(val)?)?;
 	} else {
-		let output = state.manifest(val)?;
+		let output = s.manifest(val)?;
 		if !output.is_empty() {
 			println!("{}", output);
 		}
modifiedcrates/jrsonnet-cli/src/ext.rsdiffbeforeafterboth
--- a/crates/jrsonnet-cli/src/ext.rs
+++ b/crates/jrsonnet-cli/src/ext.rs
@@ -1,7 +1,7 @@
 use std::{fs::read_to_string, str::FromStr};
 
 use clap::Parser;
-use jrsonnet_evaluator::{error::Result, EvaluationState};
+use jrsonnet_evaluator::{error::Result, State};
 
 use crate::ConfigureState;
 
@@ -100,18 +100,18 @@
 	ext_code_file: Vec<ExtFile>,
 }
 impl ConfigureState for ExtVarOpts {
-	fn configure(&self, state: &EvaluationState) -> Result<()> {
+	fn configure(&self, s: &State) -> Result<()> {
 		for ext in self.ext_str.iter() {
-			state.add_ext_str((&ext.name as &str).into(), (&ext.value as &str).into());
+			s.add_ext_str((&ext.name as &str).into(), (&ext.value as &str).into());
 		}
 		for ext in self.ext_str_file.iter() {
-			state.add_ext_str((&ext.name as &str).into(), (&ext.value as &str).into());
+			s.add_ext_str((&ext.name as &str).into(), (&ext.value as &str).into());
 		}
 		for ext in self.ext_code.iter() {
-			state.add_ext_code((&ext.name as &str).into(), (&ext.value as &str).into())?;
+			s.add_ext_code((&ext.name as &str).into(), (&ext.value as &str).into())?;
 		}
 		for ext in self.ext_code_file.iter() {
-			state.add_ext_code((&ext.name as &str).into(), (&ext.value as &str).into())?;
+			s.add_ext_code((&ext.name as &str).into(), (&ext.value as &str).into())?;
 		}
 		Ok(())
 	}
modifiedcrates/jrsonnet-cli/src/lib.rsdiffbeforeafterboth
--- a/crates/jrsonnet-cli/src/lib.rs
+++ b/crates/jrsonnet-cli/src/lib.rs
@@ -7,13 +7,13 @@
 
 use clap::Parser;
 pub use ext::*;
-use jrsonnet_evaluator::{error::Result, EvaluationState, FileImportResolver};
+use jrsonnet_evaluator::{error::Result, FileImportResolver, State};
 pub use manifest::*;
 pub use tla::*;
 pub use trace::*;
 
 pub trait ConfigureState {
-	fn configure(&self, state: &EvaluationState) -> Result<()>;
+	fn configure(&self, s: &State) -> Result<()>;
 }
 
 #[derive(Parser)]
@@ -50,9 +50,9 @@
 	jpath: Vec<PathBuf>,
 }
 impl ConfigureState for MiscOpts {
-	fn configure(&self, state: &EvaluationState) -> Result<()> {
+	fn configure(&self, s: &State) -> Result<()> {
 		if !self.no_stdlib {
-			state.with_stdlib();
+			s.with_stdlib();
 		}
 
 		let mut library_paths = self.jpath.clone();
@@ -61,9 +61,9 @@
 			library_paths.extend(env::split_paths(path.as_os_str()));
 		}
 
-		state.set_import_resolver(Box::new(FileImportResolver { library_paths }));
+		s.set_import_resolver(Box::new(FileImportResolver { library_paths }));
 
-		state.set_max_stack(self.max_stack);
+		s.set_max_stack(self.max_stack);
 		Ok(())
 	}
 }
@@ -85,12 +85,12 @@
 }
 
 impl ConfigureState for GeneralOpts {
-	fn configure(&self, state: &EvaluationState) -> Result<()> {
+	fn configure(&self, s: &State) -> Result<()> {
 		// Configure trace first, because tla-code/ext-code can throw
-		self.trace.configure(state)?;
-		self.misc.configure(state)?;
-		self.tla.configure(state)?;
-		self.ext.configure(state)?;
+		self.trace.configure(s)?;
+		self.misc.configure(s)?;
+		self.tla.configure(s)?;
+		self.ext.configure(s)?;
 		Ok(())
 	}
 }
modifiedcrates/jrsonnet-cli/src/manifest.rsdiffbeforeafterboth
--- a/crates/jrsonnet-cli/src/manifest.rs
+++ b/crates/jrsonnet-cli/src/manifest.rs
@@ -1,7 +1,7 @@
 use std::{path::PathBuf, str::FromStr};
 
 use clap::Parser;
-use jrsonnet_evaluator::{error::Result, EvaluationState, ManifestFormat};
+use jrsonnet_evaluator::{error::Result, ManifestFormat, State};
 
 use crate::ConfigureState;
 
@@ -49,20 +49,20 @@
 	exp_preserve_order: bool,
 }
 impl ConfigureState for ManifestOpts {
-	fn configure(&self, state: &EvaluationState) -> Result<()> {
+	fn configure(&self, s: &State) -> Result<()> {
 		if self.string {
-			state.set_manifest_format(ManifestFormat::String);
+			s.set_manifest_format(ManifestFormat::String);
 		} else {
 			#[cfg(feature = "exp-preserve-order")]
 			let preserve_order = self.exp_preserve_order;
 			match self.format {
-				ManifestFormatName::String => state.set_manifest_format(ManifestFormat::String),
-				ManifestFormatName::Json => state.set_manifest_format(ManifestFormat::Json {
+				ManifestFormatName::String => s.set_manifest_format(ManifestFormat::String),
+				ManifestFormatName::Json => s.set_manifest_format(ManifestFormat::Json {
 					padding: self.line_padding.unwrap_or(3),
 					#[cfg(feature = "exp-preserve-order")]
 					preserve_order,
 				}),
-				ManifestFormatName::Yaml => state.set_manifest_format(ManifestFormat::Yaml {
+				ManifestFormatName::Yaml => s.set_manifest_format(ManifestFormat::Yaml {
 					padding: self.line_padding.unwrap_or(2),
 					#[cfg(feature = "exp-preserve-order")]
 					preserve_order,
@@ -70,9 +70,7 @@
 			}
 		}
 		if self.yaml_stream {
-			state.set_manifest_format(ManifestFormat::YamlStream(Box::new(
-				state.manifest_format(),
-			)))
+			s.set_manifest_format(ManifestFormat::YamlStream(Box::new(s.manifest_format())))
 		}
 		Ok(())
 	}
modifiedcrates/jrsonnet-cli/src/tla.rsdiffbeforeafterboth
--- a/crates/jrsonnet-cli/src/tla.rs
+++ b/crates/jrsonnet-cli/src/tla.rs
@@ -1,5 +1,5 @@
 use clap::Parser;
-use jrsonnet_evaluator::{error::Result, EvaluationState};
+use jrsonnet_evaluator::{error::Result, State};
 
 use crate::{ConfigureState, ExtFile, ExtStr};
 
@@ -47,18 +47,18 @@
 	tla_code_file: Vec<ExtFile>,
 }
 impl ConfigureState for TLAOpts {
-	fn configure(&self, state: &EvaluationState) -> Result<()> {
+	fn configure(&self, s: &State) -> Result<()> {
 		for tla in self.tla_str.iter() {
-			state.add_tla_str((&tla.name as &str).into(), (&tla.value as &str).into());
+			s.add_tla_str((&tla.name as &str).into(), (&tla.value as &str).into());
 		}
 		for tla in self.tla_str_file.iter() {
-			state.add_tla_str((&tla.name as &str).into(), (&tla.value as &str).into())
+			s.add_tla_str((&tla.name as &str).into(), (&tla.value as &str).into())
 		}
 		for tla in self.tla_code.iter() {
-			state.add_tla_code((&tla.name as &str).into(), (&tla.value as &str).into())?;
+			s.add_tla_code((&tla.name as &str).into(), (&tla.value as &str).into())?;
 		}
 		for tla in self.tla_code_file.iter() {
-			state.add_tla_code((&tla.name as &str).into(), (&tla.value as &str).into())?;
+			s.add_tla_code((&tla.name as &str).into(), (&tla.value as &str).into())?;
 		}
 		Ok(())
 	}
modifiedcrates/jrsonnet-cli/src/trace.rsdiffbeforeafterboth
--- a/crates/jrsonnet-cli/src/trace.rs
+++ b/crates/jrsonnet-cli/src/trace.rs
@@ -4,7 +4,7 @@
 use jrsonnet_evaluator::{
 	error::Result,
 	trace::{CompactFormat, ExplainingFormat, PathResolver},
-	EvaluationState,
+	State,
 };
 
 use crate::ConfigureState;
@@ -41,22 +41,22 @@
 	max_trace: usize,
 }
 impl ConfigureState for TraceOpts {
-	fn configure(&self, state: &EvaluationState) -> Result<()> {
+	fn configure(&self, s: &State) -> Result<()> {
 		let resolver = PathResolver::Absolute;
 		match self
 			.trace_format
 			.as_ref()
 			.unwrap_or(&TraceFormatName::Compact)
 		{
-			TraceFormatName::Compact => state.set_trace_format(Box::new(CompactFormat {
+			TraceFormatName::Compact => s.set_trace_format(Box::new(CompactFormat {
 				resolver,
 				padding: 4,
 			})),
 			TraceFormatName::Explaining => {
-				state.set_trace_format(Box::new(ExplainingFormat { resolver }))
+				s.set_trace_format(Box::new(ExplainingFormat { resolver }))
 			}
 		}
-		state.set_max_trace(self.max_trace);
+		s.set_max_trace(self.max_trace);
 		Ok(())
 	}
 }
modifiedcrates/jrsonnet-evaluator/src/builtin/format.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/builtin/format.rs
+++ b/crates/jrsonnet-evaluator/src/builtin/format.rs
@@ -1,14 +1,12 @@
 //! faster std.format impl
 #![allow(clippy::too_many_arguments)]
 
-use std::convert::TryFrom;
-
 use gcmodule::Trace;
 use jrsonnet_interner::IStr;
 use jrsonnet_types::ValType;
 use thiserror::Error;
 
-use crate::{error::Error::*, throw, LocError, ObjValue, Result, Val};
+use crate::{error::Error::*, throw, typed::Typed, LocError, ObjValue, Result, State, Val};
 
 #[derive(Debug, Clone, Error, Trace)]
 pub enum FormatError {
@@ -464,6 +462,7 @@
 }
 
 pub fn format_code(
+	s: State,
 	out: &mut String,
 	value: &Val,
 	code: &Code,
@@ -485,9 +484,9 @@
 	let mut tmp_out = String::new();
 
 	match code.convtype {
-		ConvTypeV::String => tmp_out.push_str(&value.clone().to_string()?),
+		ConvTypeV::String => tmp_out.push_str(&value.clone().to_string(s)?),
 		ConvTypeV::Decimal => {
-			let value = f64::try_from(value.clone())?;
+			let value = f64::from_untyped(value.clone(), s)?;
 			render_decimal(
 				&mut tmp_out,
 				value as i64,
@@ -498,7 +497,7 @@
 			);
 		}
 		ConvTypeV::Octal => {
-			let value = f64::try_from(value.clone())?;
+			let value = f64::from_untyped(value.clone(), s)?;
 			render_octal(
 				&mut tmp_out,
 				value as i64,
@@ -510,7 +509,7 @@
 			);
 		}
 		ConvTypeV::Hexadecimal => {
-			let value = f64::try_from(value.clone())?;
+			let value = f64::from_untyped(value.clone(), s)?;
 			render_hexadecimal(
 				&mut tmp_out,
 				value as i64,
@@ -523,7 +522,7 @@
 			);
 		}
 		ConvTypeV::Scientific => {
-			let value = f64::try_from(value.clone())?;
+			let value = f64::from_untyped(value.clone(), s)?;
 			render_float_sci(
 				&mut tmp_out,
 				value,
@@ -537,7 +536,7 @@
 			);
 		}
 		ConvTypeV::Float => {
-			let value = f64::try_from(value.clone())?;
+			let value = f64::from_untyped(value.clone(), s)?;
 			render_float(
 				&mut tmp_out,
 				value,
@@ -550,7 +549,7 @@
 			);
 		}
 		ConvTypeV::Shorter => {
-			let value = f64::try_from(value.clone())?;
+			let value = f64::from_untyped(value.clone(), s)?;
 			let exponent = value.log10().floor();
 			if exponent < -4.0 || exponent >= fpprec as f64 {
 				render_float_sci(
@@ -617,7 +616,7 @@
 	Ok(())
 }
 
-pub fn format_arr(str: &str, mut values: &[Val]) -> Result<String> {
+pub fn format_arr(s: State, str: &str, mut values: &[Val]) -> Result<String> {
 	let codes = parse_codes(str)?;
 	let mut out = String::new();
 
@@ -634,7 +633,7 @@
 						}
 						let value = &values[0];
 						values = &values[1..];
-						usize::try_from(value.clone())?
+						usize::from_untyped(value.clone(), s.clone())?
 					}
 					Width::Fixed(n) => n,
 				};
@@ -645,7 +644,7 @@
 						}
 						let value = &values[0];
 						values = &values[1..];
-						Some(usize::try_from(value.clone())?)
+						Some(usize::from_untyped(value.clone(), s.clone())?)
 					}
 					Some(Width::Fixed(n)) => Some(n),
 					None => None,
@@ -663,7 +662,7 @@
 					value
 				};
 
-				format_code(&mut out, value, &c, width, precision)?;
+				format_code(s.clone(), &mut out, value, &c, width, precision)?;
 			}
 		}
 	}
@@ -671,7 +670,7 @@
 	Ok(out)
 }
 
-pub fn format_obj(str: &str, values: &ObjValue) -> Result<String> {
+pub fn format_obj(s: State, str: &str, values: &ObjValue) -> Result<String> {
 	let codes = parse_codes(str)?;
 	let mut out = String::new();
 
@@ -703,14 +702,14 @@
 					if f.is_empty() {
 						throw!(MappingKeysRequired);
 					}
-					if let Some(v) = values.get(f.clone())? {
+					if let Some(v) = values.get(s.clone(), f.clone())? {
 						v
 					} else {
 						throw!(NoSuchFormatField(f));
 					}
 				};
 
-				format_code(&mut out, &value, &c, width, precision)?;
+				format_code(s.clone(), &mut out, &value, &c, width, precision)?;
 			}
 		}
 	}
modifiedcrates/jrsonnet-evaluator/src/builtin/manifest.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/builtin/manifest.rs
+++ b/crates/jrsonnet-evaluator/src/builtin/manifest.rs
@@ -1,6 +1,6 @@
 use crate::{
 	error::{Error::*, Result},
-	push_description_frame, throw, Val,
+	throw, State, Val,
 };
 
 #[derive(PartialEq, Clone, Copy)]
@@ -25,12 +25,13 @@
 	pub preserve_order: bool,
 }
 
-pub fn manifest_json_ex(val: &Val, options: &ManifestJsonOptions<'_>) -> Result<String> {
+pub fn manifest_json_ex(s: State, val: &Val, options: &ManifestJsonOptions<'_>) -> Result<String> {
 	let mut out = String::new();
-	manifest_json_ex_buf(val, &mut out, &mut String::new(), options)?;
+	manifest_json_ex_buf(s, val, &mut out, &mut String::new(), options)?;
 	Ok(out)
 }
 fn manifest_json_ex_buf(
+	s: State,
 	val: &Val,
 	buf: &mut String,
 	cur_padding: &mut String,
@@ -58,7 +59,7 @@
 
 				let old_len = cur_padding.len();
 				cur_padding.push_str(options.padding);
-				for (i, item) in items.iter().enumerate() {
+				for (i, item) in items.iter(s.clone()).enumerate() {
 					if i != 0 {
 						buf.push(',');
 						if mtype == ManifestType::ToString {
@@ -68,7 +69,7 @@
 						}
 					}
 					buf.push_str(cur_padding);
-					manifest_json_ex_buf(&item?, buf, cur_padding, options)?;
+					manifest_json_ex_buf(s.clone(), &item?, buf, cur_padding, options)?;
 				}
 				cur_padding.truncate(old_len);
 
@@ -85,7 +86,7 @@
 			buf.push(']');
 		}
 		Val::Obj(obj) => {
-			obj.run_assertions()?;
+			obj.run_assertions(s.clone())?;
 			buf.push('{');
 			let fields = obj.fields(
 				#[cfg(feature = "exp-preserve-order")]
@@ -110,11 +111,11 @@
 					buf.push_str(cur_padding);
 					escape_string_json_buf(&field, buf);
 					buf.push_str(options.key_val_sep);
-					push_description_frame(
+					s.push_description(
 						|| format!("field <{}> manifestification", field.clone()),
 						|| {
-							let value = obj.get(field.clone())?.unwrap();
-							manifest_json_ex_buf(&value, buf, cur_padding, options)?;
+							let value = obj.get(s.clone(), field.clone())?.unwrap();
+							manifest_json_ex_buf(s.clone(), &value, buf, cur_padding, options)?;
 							Ok(Val::Null)
 						},
 					)?;
@@ -221,12 +222,13 @@
 		|| string.parse::<f64>().is_ok()
 }
 
-pub fn manifest_yaml_ex(val: &Val, options: &ManifestYamlOptions<'_>) -> Result<String> {
+pub fn manifest_yaml_ex(s: State, val: &Val, options: &ManifestYamlOptions<'_>) -> Result<String> {
 	let mut out = String::new();
-	manifest_yaml_ex_buf(val, &mut out, &mut String::new(), options)?;
+	manifest_yaml_ex_buf(s, val, &mut out, &mut String::new(), options)?;
 	Ok(out)
 }
 fn manifest_yaml_ex_buf(
+	s: State,
 	val: &Val,
 	buf: &mut String,
 	cur_padding: &mut String,
@@ -249,7 +251,7 @@
 				buf.push('|');
 				for line in s.split('\n') {
 					buf.push('\n');
-					buf.push_str(&cur_padding);
+					buf.push_str(cur_padding);
 					buf.push_str(options.padding);
 					buf.push_str(line);
 				}
@@ -264,7 +266,7 @@
 			if a.is_empty() {
 				buf.push_str("[]");
 			} else {
-				for (i, item) in a.iter().enumerate() {
+				for (i, item) in a.iter(s.clone()).enumerate() {
 					if i != 0 {
 						buf.push('\n');
 						buf.push_str(cur_padding);
@@ -288,7 +290,7 @@
 					if extra_padding {
 						cur_padding.push_str(options.padding);
 					}
-					manifest_yaml_ex_buf(&item, buf, cur_padding, options)?;
+					manifest_yaml_ex_buf(s.clone(), &item, buf, cur_padding, options)?;
 					cur_padding.truncate(prev_len);
 				}
 			}
@@ -316,7 +318,7 @@
 					}
 					buf.push(':');
 					let prev_len = cur_padding.len();
-					let item = o.get(key.clone())?.expect("field exists");
+					let item = o.get(s.clone(), key.clone())?.expect("field exists");
 					match &item {
 						Val::Arr(a) if !a.is_empty() => {
 							buf.push('\n');
@@ -332,7 +334,7 @@
 						}
 						_ => buf.push(' '),
 					}
-					manifest_yaml_ex_buf(&item, buf, cur_padding, options)?;
+					manifest_yaml_ex_buf(s.clone(), &item, buf, cur_padding, options)?;
 					cur_padding.truncate(prev_len);
 				}
 			}
modifiedcrates/jrsonnet-evaluator/src/builtin/mod.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/builtin/mod.rs
+++ b/crates/jrsonnet-evaluator/src/builtin/mod.rs
@@ -1,7 +1,4 @@
-use std::{
-	collections::HashMap,
-	convert::{TryFrom, TryInto},
-};
+use std::collections::HashMap;
 
 use format::{format_arr, format_obj};
 use gcmodule::Cc;
@@ -14,10 +11,10 @@
 	error::{Error::*, Result},
 	function::{CallLocation, StaticBuiltin},
 	operator::evaluate_mod_op,
-	push_frame, throw,
-	typed::{Any, BoundedUsize, Bytes, Either2, Either4, PositiveF64, VecVal, M1},
+	throw,
+	typed::{Any, BoundedUsize, Bytes, Either2, Either4, PositiveF64, Typed, VecVal, M1},
 	val::{equals, primitive_equals, ArrValue, FuncVal, IndexableVal, Slice},
-	with_state, Either, ObjValue, Val,
+	Either, ObjValue, State, Val,
 };
 
 pub mod stdlib;
@@ -29,15 +26,15 @@
 pub mod manifest;
 pub mod sort;
 
-pub fn std_format(str: IStr, vals: Val) -> Result<String> {
-	push_frame(
+pub fn std_format(s: State, str: IStr, vals: Val) -> Result<String> {
+	s.push(
 		CallLocation::native(),
 		|| format!("std.format of {}", str),
 		|| {
 			Ok(match vals {
-				Val::Arr(vals) => format_arr(&str, &vals.evaluated()?)?,
-				Val::Obj(obj) => format_obj(&str, &obj)?,
-				o => format_arr(&str, &[o])?,
+				Val::Arr(vals) => format_arr(s.clone(), &str, &vals.evaluated(s.clone())?)?,
+				Val::Obj(obj) => format_obj(s.clone(), &str, &obj)?,
+				o => format_arr(s.clone(), &str, &[o])?,
 			})
 		},
 	)
@@ -173,10 +170,10 @@
 }
 
 #[jrsonnet_macros::builtin]
-fn builtin_make_array(sz: usize, func: FuncVal) -> Result<VecVal> {
+fn builtin_make_array(s: State, sz: usize, func: FuncVal) -> Result<VecVal> {
 	let mut out = Vec::with_capacity(sz);
 	for i in 0..sz {
-		out.push(func.evaluate_simple(&[i as f64].as_slice())?)
+		out.push(func.evaluate_simple(s.clone(), &[i as f64].as_slice())?)
 	}
 	Ok(VecVal(Cc::new(out)))
 }
@@ -210,23 +207,25 @@
 }
 
 #[jrsonnet_macros::builtin]
-fn builtin_parse_json(s: IStr) -> Result<Any> {
-	let value: serde_json::Value = serde_json::from_str(&s)
+fn builtin_parse_json(st: State, s: IStr) -> Result<Any> {
+	use serde_json::Value;
+	let value: Value = serde_json::from_str(&s)
 		.map_err(|e| RuntimeError(format!("failed to parse json: {}", e).into()))?;
-	Ok(Any(Val::try_from(&value)?))
+	Ok(Any(Value::into_untyped(value, st)?))
 }
 
 #[jrsonnet_macros::builtin]
-fn builtin_parse_yaml(s: IStr) -> Result<Any> {
+fn builtin_parse_yaml(st: State, s: IStr) -> Result<Any> {
+	use serde_json::Value;
 	let value = serde_yaml::Deserializer::from_str_with_quirks(
 		&s,
 		DeserializingQuirks { old_octals: true },
 	);
 	let mut out = vec![];
 	for item in value {
-		let value = serde_json::Value::deserialize(item)
+		let value = Value::deserialize(item)
 			.map_err(|e| RuntimeError(format!("failed to parse yaml: {}", e).into()))?;
-		let val = Val::try_from(&value)?;
+		let val = Value::into_untyped(value, st.clone())?;
 		out.push(val);
 	}
 	Ok(Any(if out.is_empty() {
@@ -259,8 +258,8 @@
 }
 
 #[jrsonnet_macros::builtin]
-fn builtin_equals(a: Any, b: Any) -> Result<bool> {
-	equals(&a.0, &b.0)
+fn builtin_equals(s: State, a: Any, b: Any) -> Result<bool> {
+	equals(s, &a.0, &b.0)
 }
 
 #[jrsonnet_macros::builtin]
@@ -269,9 +268,10 @@
 }
 
 #[jrsonnet_macros::builtin]
-fn builtin_mod(a: Either![f64, IStr], b: Any) -> Result<Any> {
+fn builtin_mod(s: State, a: Either![f64, IStr], b: Any) -> Result<Any> {
 	use Either2::*;
 	Ok(Any(evaluate_mod_op(
+		s,
 		&match a {
 			A(v) => Val::Num(v),
 			B(s) => Val::Str(s),
@@ -362,35 +362,49 @@
 }
 
 #[jrsonnet_macros::builtin]
-fn builtin_ext_var(x: IStr) -> Result<Any> {
-	Ok(Any(with_state(|s| s.settings().ext_vars.get(&x).cloned())
+fn builtin_ext_var(s: State, x: IStr) -> Result<Any> {
+	Ok(Any(s
+		.settings()
+		.ext_vars
+		.get(&x)
+		.cloned()
 		.ok_or(UndefinedExternalVariable(x))?))
 }
 
 #[jrsonnet_macros::builtin]
-fn builtin_native(name: IStr) -> Result<FuncVal> {
-	Ok(with_state(|s| s.settings().ext_natives.get(&name).cloned())
+fn builtin_native(s: State, name: IStr) -> Result<FuncVal> {
+	Ok(s.settings()
+		.ext_natives
+		.get(&name)
+		.cloned()
 		.map(|v| FuncVal::Builtin(v.clone()))
 		.ok_or(UndefinedExternalFunction(name))?)
 }
 
 #[jrsonnet_macros::builtin]
-fn builtin_filter(func: FuncVal, arr: ArrValue) -> Result<ArrValue> {
-	arr.filter(|val| bool::try_from(func.evaluate_simple(&[Any(val.clone())].as_slice())?))
+fn builtin_filter(s: State, func: FuncVal, arr: ArrValue) -> Result<ArrValue> {
+	arr.filter(s.clone(), |val| {
+		bool::from_untyped(
+			func.evaluate_simple(s.clone(), &[Any(val.clone())].as_slice())?,
+			s.clone(),
+		)
+	})
 }
 
 #[jrsonnet_macros::builtin]
-fn builtin_map(func: FuncVal, arr: ArrValue) -> Result<ArrValue> {
-	arr.map(|val| func.evaluate_simple(&[Any(val)].as_slice()))
+fn builtin_map(s: State, func: FuncVal, arr: ArrValue) -> Result<ArrValue> {
+	arr.map(s.clone(), |val| {
+		func.evaluate_simple(s.clone(), &[Any(val)].as_slice())
+	})
 }
 
 #[jrsonnet_macros::builtin]
-fn builtin_flatmap(func: FuncVal, arr: IndexableVal) -> Result<IndexableVal> {
+fn builtin_flatmap(s: State, func: FuncVal, arr: IndexableVal) -> Result<IndexableVal> {
 	match arr {
-		IndexableVal::Str(s) => {
+		IndexableVal::Str(str) => {
 			let mut out = String::new();
-			for c in s.chars() {
-				match func.evaluate_simple(&[c.to_string()].as_slice())? {
+			for c in str.chars() {
+				match func.evaluate_simple(s.clone(), &[c.to_string()].as_slice())? {
 					Val::Str(o) => out.push_str(&o),
 					_ => throw!(RuntimeError(
 						"in std.join all items should be strings".into()
@@ -401,11 +415,11 @@
 		}
 		IndexableVal::Arr(a) => {
 			let mut out = Vec::new();
-			for el in a.iter() {
+			for el in a.iter(s.clone()) {
 				let el = el?;
-				match func.evaluate_simple(&[Any(el)].as_slice())? {
+				match func.evaluate_simple(s.clone(), &[Any(el)].as_slice())? {
 					Val::Arr(o) => {
-						for oe in o.iter() {
+						for oe in o.iter(s.clone()) {
 							out.push(oe?)
 						}
 					}
@@ -420,38 +434,39 @@
 }
 
 #[jrsonnet_macros::builtin]
-fn builtin_foldl(func: FuncVal, arr: ArrValue, init: Any) -> Result<Any> {
+fn builtin_foldl(s: State, func: FuncVal, arr: ArrValue, init: Any) -> Result<Any> {
 	let mut acc = init.0;
-	for i in arr.iter() {
-		acc = func.evaluate_simple(&[Any(acc), Any(i?)].as_slice())?;
+	for i in arr.iter(s.clone()) {
+		acc = func.evaluate_simple(s.clone(), &[Any(acc), Any(i?)].as_slice())?;
 	}
 	Ok(Any(acc))
 }
 
 #[jrsonnet_macros::builtin]
-fn builtin_foldr(func: FuncVal, arr: ArrValue, init: Any) -> Result<Any> {
+fn builtin_foldr(s: State, func: FuncVal, arr: ArrValue, init: Any) -> Result<Any> {
 	let mut acc = init.0;
-	for i in arr.iter().rev() {
-		acc = func.evaluate_simple(&[Any(i?), Any(acc)].as_slice())?;
+	for i in arr.iter(s.clone()).rev() {
+		acc = func.evaluate_simple(s.clone(), &[Any(i?), Any(acc)].as_slice())?;
 	}
 	Ok(Any(acc))
 }
 
 #[jrsonnet_macros::builtin]
 #[allow(non_snake_case)]
-fn builtin_sort(arr: ArrValue, keyF: Option<FuncVal>) -> Result<ArrValue> {
+fn builtin_sort(s: State, arr: ArrValue, keyF: Option<FuncVal>) -> Result<ArrValue> {
 	if arr.len() <= 1 {
 		return Ok(arr);
 	}
 	Ok(ArrValue::Eager(sort::sort(
-		arr.evaluated()?,
+		s.clone(),
+		arr.evaluated(s)?,
 		keyF.as_ref(),
 	)?))
 }
 
 #[jrsonnet_macros::builtin]
-fn builtin_format(str: IStr, vals: Any) -> Result<String> {
-	std_format(str, vals.0)
+fn builtin_format(s: State, str: IStr, vals: Any) -> Result<String> {
+	std_format(s, str, vals.0)
 }
 
 #[jrsonnet_macros::builtin]
@@ -485,17 +500,15 @@
 }
 
 #[jrsonnet_macros::builtin]
-fn builtin_trace(loc: CallLocation, str: IStr, rest: Any) -> Result<Any> {
+fn builtin_trace(s: State, loc: CallLocation, str: IStr, rest: Any) -> Result<Any> {
 	eprint!("TRACE:");
 	if let Some(loc) = loc.0 {
-		with_state(|s| {
-			let locs = s.map_source_locations(&loc.0, &[loc.1]);
-			eprint!(
-				" {}:{}",
-				loc.0.file_name().unwrap().to_str().unwrap(),
-				locs[0].line
-			);
-		});
+		let locs = s.map_source_locations(&loc.0, &[loc.1]);
+		eprint!(
+			" {}:{}",
+			loc.0.file_name().unwrap().to_str().unwrap(),
+			locs[0].line
+		);
 	}
 	eprintln!(" {}", str);
 	Ok(rest) as Result<Any>
@@ -526,26 +539,25 @@
 }
 
 #[jrsonnet_macros::builtin]
-fn builtin_join(sep: IndexableVal, arr: ArrValue) -> Result<IndexableVal> {
+fn builtin_join(s: State, sep: IndexableVal, arr: ArrValue) -> Result<IndexableVal> {
 	Ok(match sep {
 		IndexableVal::Arr(joiner_items) => {
 			let mut out = Vec::new();
 
 			let mut first = true;
-			for item in arr.iter() {
+			for item in arr.iter(s.clone()) {
 				let item = item?.clone();
 				if let Val::Arr(items) = item {
 					if !first {
 						out.reserve(joiner_items.len());
 						// TODO: extend
-						for item in joiner_items.iter() {
+						for item in joiner_items.iter(s.clone()) {
 							out.push(item?);
 						}
 					}
 					first = false;
 					out.reserve(items.len());
-					// TODO: extend
-					for item in items.iter() {
+					for item in items.iter(s.clone()) {
 						out.push(item?);
 					}
 				} else {
@@ -561,7 +573,7 @@
 			let mut out = String::new();
 
 			let mut first = true;
-			for item in arr.iter() {
+			for item in arr.iter(s) {
 				let item = item?.clone();
 				if let Val::Str(item) = item {
 					if !first {
@@ -588,6 +600,7 @@
 
 #[jrsonnet_macros::builtin]
 fn builtin_manifest_json_ex(
+	s: State,
 	value: Any,
 	indent: IStr,
 	newline: Option<IStr>,
@@ -597,6 +610,7 @@
 	let newline = newline.as_deref().unwrap_or("\n");
 	let key_val_sep = key_val_sep.as_deref().unwrap_or(": ");
 	manifest_json_ex(
+		s,
 		&value.0,
 		&ManifestJsonOptions {
 			padding: &indent,
@@ -611,12 +625,14 @@
 
 #[jrsonnet_macros::builtin]
 fn builtin_manifest_yaml_doc(
+	s: State,
 	value: Any,
 	indent_array_in_object: Option<bool>,
 	quote_keys: Option<bool>,
 	#[cfg(feature = "exp-preserve-order")] preserve_order: Option<bool>,
 ) -> Result<String> {
 	manifest_yaml_ex(
+		s,
 		&value.0,
 		&ManifestYamlOptions {
 			padding: "  ",
@@ -670,16 +686,16 @@
 }
 
 #[jrsonnet_macros::builtin]
-fn builtin_member(arr: IndexableVal, x: Any) -> Result<bool> {
+fn builtin_member(s: State, arr: IndexableVal, x: Any) -> Result<bool> {
 	match arr {
-		IndexableVal::Str(s) => {
-			let x: IStr = IStr::try_from(x.0)?;
-			Ok(!x.is_empty() && s.contains(&*x))
+		IndexableVal::Str(str) => {
+			let x: IStr = IStr::from_untyped(x.0, s)?;
+			Ok(!x.is_empty() && str.contains(&*x))
 		}
 		IndexableVal::Arr(a) => {
-			for item in a.iter() {
+			for item in a.iter(s.clone()) {
 				let item = item?;
-				if equals(&item, &x.0)? {
+				if equals(s.clone(), &item, &x.0)? {
 					return Ok(true);
 				}
 			}
@@ -689,10 +705,10 @@
 }
 
 #[jrsonnet_macros::builtin]
-fn builtin_count(arr: Vec<Any>, v: Any) -> Result<usize> {
+fn builtin_count(s: State, arr: Vec<Any>, v: Any) -> Result<usize> {
 	let mut count = 0;
 	for item in arr.iter() {
-		if equals(&item.0, &v.0)? {
+		if equals(s.clone(), &item.0, &v.0)? {
 			count += 1;
 		}
 	}
@@ -700,9 +716,9 @@
 }
 
 #[jrsonnet_macros::builtin]
-fn builtin_any(arr: ArrValue) -> Result<bool> {
-	for v in arr.iter() {
-		let v: bool = v?.try_into()?;
+fn builtin_any(s: State, arr: ArrValue) -> Result<bool> {
+	for v in arr.iter(s.clone()) {
+		let v = bool::from_untyped(v?, s.clone())?;
 		if v {
 			return Ok(true);
 		}
@@ -711,9 +727,9 @@
 }
 
 #[jrsonnet_macros::builtin]
-fn builtin_all(arr: ArrValue) -> Result<bool> {
-	for v in arr.iter() {
-		let v: bool = v?.try_into()?;
+fn builtin_all(s: State, arr: ArrValue) -> Result<bool> {
+	for v in arr.iter(s.clone()) {
+		let v = bool::from_untyped(v?, s.clone())?;
 		if !v {
 			return Ok(false);
 		}
modifiedcrates/jrsonnet-evaluator/src/builtin/sort.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/builtin/sort.rs
+++ b/crates/jrsonnet-evaluator/src/builtin/sort.rs
@@ -5,7 +5,7 @@
 	throw,
 	typed::Any,
 	val::FuncVal,
-	Val,
+	State, Val,
 };
 
 #[derive(Debug, Clone, thiserror::Error, Trace)]
@@ -63,7 +63,7 @@
 	Ok(sort_type)
 }
 
-pub fn sort(values: Cc<Vec<Val>>, key_getter: Option<&FuncVal>) -> Result<Cc<Vec<Val>>> {
+pub fn sort(s: State, values: Cc<Vec<Val>>, key_getter: Option<&FuncVal>) -> Result<Cc<Vec<Val>>> {
 	if values.len() <= 1 {
 		return Ok(values);
 	}
@@ -73,7 +73,7 @@
 		for value in values.iter() {
 			vk.push((
 				value.clone(),
-				key_getter.evaluate_simple(&[Any(value.clone())].as_slice())?,
+				key_getter.evaluate_simple(s.clone(), &[Any(value.clone())].as_slice())?,
 			));
 		}
 		let sort_type = get_sort_type(&mut vk, |v| &mut v.1)?;
modifiedcrates/jrsonnet-evaluator/src/ctx.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/ctx.rs
+++ b/crates/jrsonnet-evaluator/src/ctx.rs
@@ -5,14 +5,20 @@
 
 use crate::{
 	cc_ptr_eq, error::Error::*, gc::GcHashMap, map::LayeredHashMap, FutureWrapper, LazyBinding,
-	LazyVal, ObjValue, Result, Val,
+	LazyVal, ObjValue, Result, State, Val,
 };
 
 #[derive(Clone, Trace)]
 pub struct ContextCreator(pub Context, pub FutureWrapper<GcHashMap<IStr, LazyBinding>>);
 impl ContextCreator {
-	pub fn create(&self, this: Option<ObjValue>, super_obj: Option<ObjValue>) -> Result<Context> {
+	pub fn create(
+		&self,
+		s: State,
+		this: Option<ObjValue>,
+		super_obj: Option<ObjValue>,
+	) -> Result<Context> {
 		self.0.clone().extend_unbound(
+			s,
 			self.1.clone().unwrap(),
 			self.0.dollar().clone().or_else(|| this.clone()),
 			this,
@@ -120,6 +126,7 @@
 	}
 	pub fn extend_unbound(
 		self,
+		s: State,
 		new_bindings: GcHashMap<IStr, LazyBinding>,
 		new_dollar: Option<ObjValue>,
 		new_this: Option<ObjValue>,
@@ -129,7 +136,7 @@
 		let super_obj = new_super_obj.or_else(|| self.0.super_obj.clone());
 		let mut new = GcHashMap::with_capacity(new_bindings.len());
 		for (k, v) in new_bindings.0.into_iter() {
-			new.insert(k, v.evaluate(this.clone(), super_obj.clone())?);
+			new.insert(k, v.evaluate(s.clone(), this.clone(), super_obj.clone())?);
 		}
 		Ok(self.extend(new, new_dollar, this, super_obj))
 	}
modifiedcrates/jrsonnet-evaluator/src/evaluate/mod.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/evaluate/mod.rs
+++ b/crates/jrsonnet-evaluator/src/evaluate/mod.rs
@@ -1,5 +1,3 @@
-use std::convert::TryFrom;
-
 use gcmodule::{Cc, Trace};
 use jrsonnet_interner::IStr;
 use jrsonnet_parser::{
@@ -14,33 +12,30 @@
 	evaluate::operator::{evaluate_add_op, evaluate_binary_op_special, evaluate_unary_op},
 	function::CallLocation,
 	gc::TraceBox,
-	push_frame, throw,
-	typed::BoundedUsize,
+	throw,
+	typed::Typed,
 	val::{ArrValue, FuncDesc, FuncVal, LazyValValue},
-	with_state, Bindable, Context, ContextCreator, FutureWrapper, GcHashMap, LazyBinding, LazyVal,
-	ObjValue, ObjValueBuilder, ObjectAssertion, Result, Val,
+	Bindable, Context, ContextCreator, FutureWrapper, GcHashMap, LazyBinding, LazyVal, ObjValue,
+	ObjValueBuilder, ObjectAssertion, Result, State, Val,
 };
 pub mod operator;
 
-pub fn evaluate_binding_in_future(
-	b: &BindSpec,
-	context_creator: FutureWrapper<Context>,
-) -> LazyVal {
+pub fn evaluate_binding_in_future(b: &BindSpec, fctx: FutureWrapper<Context>) -> LazyVal {
 	let b = b.clone();
 	if let Some(params) = &b.params {
 		let params = params.clone();
 
 		#[derive(Trace)]
 		struct LazyMethodBinding {
-			context_creator: FutureWrapper<Context>,
+			fctx: FutureWrapper<Context>,
 			name: IStr,
 			params: ParamsDesc,
 			value: LocExpr,
 		}
 		impl LazyValValue for LazyMethodBinding {
-			fn get(self: Box<Self>) -> Result<Val> {
+			fn get(self: Box<Self>, _: State) -> Result<Val> {
 				Ok(evaluate_method(
-					self.context_creator.unwrap(),
+					self.fctx.unwrap(),
 					self.name,
 					self.params,
 					self.value,
@@ -49,7 +44,7 @@
 		}
 
 		LazyVal::new(TraceBox(Box::new(LazyMethodBinding {
-			context_creator,
+			fctx,
 			name: b.name.clone(),
 			params,
 			value: b.value.clone(),
@@ -57,24 +52,24 @@
 	} else {
 		#[derive(Trace)]
 		struct LazyNamedBinding {
-			context_creator: FutureWrapper<Context>,
+			fctx: FutureWrapper<Context>,
 			name: IStr,
 			value: LocExpr,
 		}
 		impl LazyValValue for LazyNamedBinding {
-			fn get(self: Box<Self>) -> Result<Val> {
-				evaluate_named(self.context_creator.unwrap(), &self.value, self.name)
+			fn get(self: Box<Self>, s: State) -> Result<Val> {
+				evaluate_named(s, self.fctx.unwrap(), &self.value, self.name)
 			}
 		}
 		LazyVal::new(TraceBox(Box::new(LazyNamedBinding {
-			context_creator,
+			fctx,
 			name: b.name.clone(),
 			value: b.value,
 		})))
 	}
 }
 
-pub fn evaluate_binding(b: &BindSpec, context_creator: ContextCreator) -> (IStr, LazyBinding) {
+pub fn evaluate_binding(b: &BindSpec, cctx: ContextCreator) -> (IStr, LazyBinding) {
 	let b = b.clone();
 	if let Some(params) = &b.params {
 		let params = params.clone();
@@ -84,15 +79,15 @@
 			this: Option<ObjValue>,
 			super_obj: Option<ObjValue>,
 
-			context_creator: ContextCreator,
+			cctx: ContextCreator,
 			name: IStr,
 			params: ParamsDesc,
 			value: LocExpr,
 		}
 		impl LazyValValue for BindableMethodLazyVal {
-			fn get(self: Box<Self>) -> Result<Val> {
+			fn get(self: Box<Self>, s: State) -> Result<Val> {
 				Ok(evaluate_method(
-					self.context_creator.create(self.this, self.super_obj)?,
+					self.cctx.create(s, self.this, self.super_obj)?,
 					self.name,
 					self.params,
 					self.value,
@@ -102,18 +97,23 @@
 
 		#[derive(Trace)]
 		struct BindableMethod {
-			context_creator: ContextCreator,
+			cctx: ContextCreator,
 			name: IStr,
 			params: ParamsDesc,
 			value: LocExpr,
 		}
 		impl Bindable for BindableMethod {
-			fn bind(&self, this: Option<ObjValue>, super_obj: Option<ObjValue>) -> Result<LazyVal> {
+			fn bind(
+				&self,
+				_: State,
+				this: Option<ObjValue>,
+				super_obj: Option<ObjValue>,
+			) -> Result<LazyVal> {
 				Ok(LazyVal::new(TraceBox(Box::new(BindableMethodLazyVal {
 					this,
 					super_obj,
 
-					context_creator: self.context_creator.clone(),
+					cctx: self.cctx.clone(),
 					name: self.name.clone(),
 					params: self.params.clone(),
 					value: self.value.clone(),
@@ -124,7 +124,7 @@
 		(
 			b.name.clone(),
 			LazyBinding::Bindable(Cc::new(TraceBox(Box::new(BindableMethod {
-				context_creator,
+				cctx,
 				name: b.name.clone(),
 				params,
 				value: b.value.clone(),
@@ -136,14 +136,15 @@
 			this: Option<ObjValue>,
 			super_obj: Option<ObjValue>,
 
-			context_creator: ContextCreator,
+			cctx: ContextCreator,
 			name: IStr,
 			value: LocExpr,
 		}
 		impl LazyValValue for BindableNamedLazyVal {
-			fn get(self: Box<Self>) -> Result<Val> {
+			fn get(self: Box<Self>, s: State) -> Result<Val> {
 				evaluate_named(
-					self.context_creator.create(self.this, self.super_obj)?,
+					s.clone(),
+					self.cctx.create(s, self.this, self.super_obj)?,
 					&self.value,
 					self.name,
 				)
@@ -152,17 +153,22 @@
 
 		#[derive(Trace)]
 		struct BindableNamed {
-			context_creator: ContextCreator,
+			cctx: ContextCreator,
 			name: IStr,
 			value: LocExpr,
 		}
 		impl Bindable for BindableNamed {
-			fn bind(&self, this: Option<ObjValue>, super_obj: Option<ObjValue>) -> Result<LazyVal> {
+			fn bind(
+				&self,
+				_: State,
+				this: Option<ObjValue>,
+				super_obj: Option<ObjValue>,
+			) -> Result<LazyVal> {
 				Ok(LazyVal::new(TraceBox(Box::new(BindableNamedLazyVal {
 					this,
 					super_obj,
 
-					context_creator: self.context_creator.clone(),
+					cctx: self.cctx.clone(),
 					name: self.name.clone(),
 					value: self.value.clone(),
 				}))))
@@ -172,7 +178,7 @@
 		(
 			b.name.clone(),
 			LazyBinding::Bindable(Cc::new(TraceBox(Box::new(BindableNamed {
-				context_creator,
+				cctx,
 				name: b.name.clone(),
 				value: b.value.clone(),
 			})))),
@@ -190,20 +196,21 @@
 }
 
 pub fn evaluate_field_name(
-	context: Context,
+	s: State,
+	ctx: Context,
 	field_name: &jrsonnet_parser::FieldName,
 ) -> Result<Option<IStr>> {
 	Ok(match field_name {
 		jrsonnet_parser::FieldName::Fixed(n) => Some(n.clone()),
-		jrsonnet_parser::FieldName::Dyn(expr) => push_frame(
+		jrsonnet_parser::FieldName::Dyn(expr) => s.push(
 			CallLocation::new(&expr.1),
 			|| "evaluating field name".to_string(),
 			|| {
-				let value = evaluate(context, expr)?;
+				let value = evaluate(s.clone(), ctx, expr)?;
 				if matches!(value, Val::Null) {
 					Ok(None)
 				} else {
-					Ok(Some(IStr::try_from(value)?))
+					Ok(Some(IStr::from_untyped(value, s.clone())?))
 				}
 			},
 		)?,
@@ -211,37 +218,41 @@
 }
 
 pub fn evaluate_comp(
-	context: Context,
+	s: State,
+	ctx: Context,
 	specs: &[CompSpec],
 	callback: &mut impl FnMut(Context) -> Result<()>,
 ) -> Result<()> {
 	match specs.get(0) {
-		None => callback(context)?,
+		None => callback(ctx)?,
 		Some(CompSpec::IfSpec(IfSpecData(cond))) => {
-			if bool::try_from(evaluate(context.clone(), cond)?)? {
-				evaluate_comp(context, &specs[1..], callback)?
+			if bool::from_untyped(evaluate(s.clone(), ctx.clone(), cond)?, s.clone())? {
+				evaluate_comp(s, ctx, &specs[1..], callback)?
 			}
 		}
-		Some(CompSpec::ForSpec(ForSpecData(var, expr))) => match evaluate(context.clone(), expr)? {
-			Val::Arr(list) => {
-				for item in list.iter() {
-					evaluate_comp(
-						context.clone().with_var(var.clone(), item?.clone()),
-						&specs[1..],
-						callback,
-					)?
+		Some(CompSpec::ForSpec(ForSpecData(var, expr))) => {
+			match evaluate(s.clone(), ctx.clone(), expr)? {
+				Val::Arr(list) => {
+					for item in list.iter(s.clone()) {
+						evaluate_comp(
+							s.clone(),
+							ctx.clone().with_var(var.clone(), item?.clone()),
+							&specs[1..],
+							callback,
+						)?
+					}
 				}
+				_ => throw!(InComprehensionCanOnlyIterateOverArray),
 			}
-			_ => throw!(InComprehensionCanOnlyIterateOverArray),
-		},
+		}
 	}
 	Ok(())
 }
 
-pub fn evaluate_member_list_object(context: Context, members: &[Member]) -> Result<ObjValue> {
+pub fn evaluate_member_list_object(s: State, ctx: Context, members: &[Member]) -> Result<ObjValue> {
 	let new_bindings = FutureWrapper::new();
 	let future_this = FutureWrapper::new();
-	let context_creator = ContextCreator(context.clone(), new_bindings.clone());
+	let cctx = ContextCreator(ctx.clone(), new_bindings.clone());
 	{
 		let mut bindings: GcHashMap<IStr, LazyBinding> = GcHashMap::with_capacity(members.len());
 		for (n, b) in members
@@ -250,7 +261,7 @@
 				Member::BindStmt(b) => Some(b.clone()),
 				_ => None,
 			})
-			.map(|b| evaluate_binding(&b, context_creator.clone()))
+			.map(|b| evaluate_binding(&b, cctx.clone()))
 		{
 			bindings.insert(n, b);
 		}
@@ -267,7 +278,7 @@
 				visibility,
 				value,
 			}) => {
-				let name = evaluate_field_name(context.clone(), name)?;
+				let name = evaluate_field_name(s.clone(), ctx.clone(), name)?;
 				if name.is_none() {
 					continue;
 				}
@@ -275,18 +286,20 @@
 
 				#[derive(Trace)]
 				struct ObjMemberBinding {
-					context_creator: ContextCreator,
+					cctx: ContextCreator,
 					value: LocExpr,
 					name: IStr,
 				}
 				impl Bindable for ObjMemberBinding {
 					fn bind(
 						&self,
+						s: State,
 						this: Option<ObjValue>,
 						super_obj: Option<ObjValue>,
 					) -> Result<LazyVal> {
 						Ok(LazyVal::new_resolved(evaluate_named(
-							self.context_creator.create(this, super_obj)?,
+							s.clone(),
+							self.cctx.create(s, this, super_obj)?,
 							&self.value,
 							self.name.clone(),
 						)?))
@@ -297,11 +310,14 @@
 					.with_add(*plus)
 					.with_visibility(*visibility)
 					.with_location(value.1.clone())
-					.bindable(TraceBox(Box::new(ObjMemberBinding {
-						context_creator: context_creator.clone(),
-						value: value.clone(),
-						name,
-					})))?;
+					.bindable(
+						s.clone(),
+						TraceBox(Box::new(ObjMemberBinding {
+							cctx: cctx.clone(),
+							value: value.clone(),
+							name,
+						})),
+					)?;
 			}
 			Member::Field(FieldMember {
 				name,
@@ -309,14 +325,14 @@
 				value,
 				..
 			}) => {
-				let name = evaluate_field_name(context.clone(), name)?;
+				let name = evaluate_field_name(s.clone(), ctx.clone(), name)?;
 				if name.is_none() {
 					continue;
 				}
 				let name = name.unwrap();
 				#[derive(Trace)]
 				struct ObjMemberBinding {
-					context_creator: ContextCreator,
+					cctx: ContextCreator,
 					value: LocExpr,
 					params: ParamsDesc,
 					name: IStr,
@@ -324,11 +340,12 @@
 				impl Bindable for ObjMemberBinding {
 					fn bind(
 						&self,
+						s: State,
 						this: Option<ObjValue>,
 						super_obj: Option<ObjValue>,
 					) -> Result<LazyVal> {
 						Ok(LazyVal::new_resolved(evaluate_method(
-							self.context_creator.create(this, super_obj)?,
+							self.cctx.create(s, this, super_obj)?,
 							self.name.clone(),
 							self.params.clone(),
 							self.value.clone(),
@@ -339,32 +356,36 @@
 					.member(name.clone())
 					.hide()
 					.with_location(value.1.clone())
-					.bindable(TraceBox(Box::new(ObjMemberBinding {
-						context_creator: context_creator.clone(),
-						value: value.clone(),
-						params: params.clone(),
-						name,
-					})))?;
+					.bindable(
+						s.clone(),
+						TraceBox(Box::new(ObjMemberBinding {
+							cctx: cctx.clone(),
+							value: value.clone(),
+							params: params.clone(),
+							name,
+						})),
+					)?;
 			}
 			Member::BindStmt(_) => {}
 			Member::AssertStmt(stmt) => {
 				#[derive(Trace)]
 				struct ObjectAssert {
-					context_creator: ContextCreator,
+					cctx: ContextCreator,
 					assert: AssertStmt,
 				}
 				impl ObjectAssertion for ObjectAssert {
 					fn run(
 						&self,
+						s: State,
 						this: Option<ObjValue>,
 						super_obj: Option<ObjValue>,
 					) -> Result<()> {
-						let ctx = self.context_creator.create(this, super_obj)?;
-						evaluate_assert(ctx, &self.assert)
+						let ctx = self.cctx.create(s.clone(), this, super_obj)?;
+						evaluate_assert(s, ctx, &self.assert)
 					}
 				}
 				builder.assert(TraceBox(Box::new(ObjectAssert {
-					context_creator: context_creator.clone(),
+					cctx: cctx.clone(),
 					assert: stmt.clone(),
 				})));
 			}
@@ -375,47 +396,47 @@
 	Ok(this)
 }
 
-pub fn evaluate_object(context: Context, object: &ObjBody) -> Result<ObjValue> {
+pub fn evaluate_object(s: State, ctx: Context, object: &ObjBody) -> Result<ObjValue> {
 	Ok(match object {
-		ObjBody::MemberList(members) => evaluate_member_list_object(context, members)?,
+		ObjBody::MemberList(members) => evaluate_member_list_object(s, ctx, members)?,
 		ObjBody::ObjComp(obj) => {
 			let future_this = FutureWrapper::new();
 			let mut builder = ObjValueBuilder::new();
-			evaluate_comp(context.clone(), &obj.compspecs, &mut |ctx| {
+			evaluate_comp(s.clone(), ctx, &obj.compspecs, &mut |ctx| {
 				let new_bindings = FutureWrapper::new();
-				let context_creator = ContextCreator(context.clone(), new_bindings.clone());
+				let cctx = ContextCreator(ctx.clone(), new_bindings.clone());
 				let mut bindings: GcHashMap<IStr, LazyBinding> =
 					GcHashMap::with_capacity(obj.pre_locals.len() + obj.post_locals.len());
 				for (n, b) in obj
 					.pre_locals
 					.iter()
 					.chain(obj.post_locals.iter())
-					.map(|b| evaluate_binding(b, context_creator.clone()))
+					.map(|b| evaluate_binding(b, cctx.clone()))
 				{
 					bindings.insert(n, b);
 				}
 				new_bindings.fill(bindings.clone());
-				let ctx = ctx.extend_unbound(bindings, None, None, None)?;
-				let key = evaluate(ctx.clone(), &obj.key)?;
+				let ctx = ctx.extend_unbound(s.clone(), bindings, None, None, None)?;
+				let key = evaluate(s.clone(), ctx.clone(), &obj.key)?;
 
 				match key {
 					Val::Null => {}
 					Val::Str(n) => {
 						#[derive(Trace)]
 						struct ObjCompBinding {
-							context: Context,
+							ctx: Context,
 							value: LocExpr,
 						}
 						impl Bindable for ObjCompBinding {
 							fn bind(
 								&self,
+								s: State,
 								this: Option<ObjValue>,
 								_super_obj: Option<ObjValue>,
 							) -> Result<LazyVal> {
 								Ok(LazyVal::new_resolved(evaluate(
-									self.context
-										.clone()
-										.extend(GcHashMap::new(), None, this, None),
+									s,
+									self.ctx.clone().extend(GcHashMap::new(), None, this, None),
 									&self.value,
 								)?))
 							}
@@ -424,10 +445,13 @@
 							.member(n)
 							.with_location(obj.value.1.clone())
 							.with_add(obj.plus)
-							.bindable(TraceBox(Box::new(ObjCompBinding {
-								context: ctx,
-								value: obj.value.clone(),
-							})))?;
+							.bindable(
+								s.clone(),
+								TraceBox(Box::new(ObjCompBinding {
+									ctx,
+									value: obj.value.clone(),
+								})),
+							)?;
 					}
 					v => throw!(FieldMustBeStringGot(v.value_type())),
 				}
@@ -443,43 +467,46 @@
 }
 
 pub fn evaluate_apply(
-	context: Context,
+	s: State,
+	ctx: Context,
 	value: &LocExpr,
 	args: &ArgsDesc,
 	loc: CallLocation,
 	tailstrict: bool,
 ) -> Result<Val> {
-	let value = evaluate(context.clone(), value)?;
+	let value = evaluate(s.clone(), ctx.clone(), value)?;
 	Ok(match value {
 		Val::Func(f) => {
-			let body = || f.evaluate(context, loc, args, tailstrict);
+			let body = || f.evaluate(s.clone(), ctx, loc, args, tailstrict);
 			if tailstrict {
 				body()?
 			} else {
-				push_frame(loc, || format!("function <{}> call", f.name()), body)?
+				s.push(loc, || format!("function <{}> call", f.name()), body)?
 			}
 		}
 		v => throw!(OnlyFunctionsCanBeCalledGot(v.value_type())),
 	})
 }
 
-pub fn evaluate_assert(context: Context, assertion: &AssertStmt) -> Result<()> {
+pub fn evaluate_assert(s: State, ctx: Context, assertion: &AssertStmt) -> Result<()> {
 	let value = &assertion.0;
 	let msg = &assertion.1;
-	let assertion_result = push_frame(
+	let assertion_result = s.push(
 		CallLocation::new(&value.1),
 		|| "assertion condition".to_owned(),
-		|| bool::try_from(evaluate(context.clone(), value)?),
+		|| bool::from_untyped(evaluate(s.clone(), ctx.clone(), value)?, s.clone()),
 	)?;
 	if !assertion_result {
-		push_frame(
+		s.push(
 			CallLocation::new(&value.1),
 			|| "assertion failure".to_owned(),
 			|| {
 				if let Some(msg) = msg {
-					throw!(AssertionFailed(evaluate(context, msg)?.to_string()?));
+					throw!(AssertionFailed(
+						evaluate(s.clone(), ctx, msg)?.to_string(s.clone())?
+					));
 				} else {
-					throw!(AssertionFailed(Val::Null.to_string()?));
+					throw!(AssertionFailed(Val::Null.to_string(s.clone())?));
 				}
 			},
 		)?
@@ -487,62 +514,61 @@
 	Ok(())
 }
 
-pub fn evaluate_named(context: Context, lexpr: &LocExpr, name: IStr) -> Result<Val> {
+pub fn evaluate_named(s: State, ctx: Context, lexpr: &LocExpr, name: IStr) -> Result<Val> {
 	use Expr::*;
 	let LocExpr(expr, _loc) = lexpr;
 	Ok(match &**expr {
-		Function(params, body) => evaluate_method(context, name, params.clone(), body.clone()),
-		_ => evaluate(context, lexpr)?,
+		Function(params, body) => evaluate_method(ctx, name, params.clone(), body.clone()),
+		_ => evaluate(s, ctx, lexpr)?,
 	})
 }
 
-pub fn evaluate(context: Context, expr: &LocExpr) -> Result<Val> {
+pub fn evaluate(s: State, ctx: Context, expr: &LocExpr) -> Result<Val> {
 	use Expr::*;
 	let LocExpr(expr, loc) = expr;
 	// let bp = with_state(|s| s.0.stop_at.borrow().clone());
 	Ok(match &**expr {
 		Literal(LiteralType::This) => {
-			Val::Obj(context.this().clone().ok_or(CantUseSelfOutsideOfObject)?)
+			Val::Obj(ctx.this().clone().ok_or(CantUseSelfOutsideOfObject)?)
 		}
 		Literal(LiteralType::Super) => Val::Obj(
-			context
-				.super_obj()
+			ctx.super_obj()
 				.clone()
 				.ok_or(NoSuperFound)?
-				.with_this(context.this().clone().unwrap()),
+				.with_this(ctx.this().clone().unwrap()),
 		),
 		Literal(LiteralType::Dollar) => {
-			Val::Obj(context.dollar().clone().ok_or(NoTopLevelObjectFound)?)
+			Val::Obj(ctx.dollar().clone().ok_or(NoTopLevelObjectFound)?)
 		}
 		Literal(LiteralType::True) => Val::Bool(true),
 		Literal(LiteralType::False) => Val::Bool(false),
 		Literal(LiteralType::Null) => Val::Null,
-		Parened(e) => evaluate(context, e)?,
+		Parened(e) => evaluate(s, ctx, e)?,
 		Str(v) => Val::Str(v.clone()),
 		Num(v) => Val::new_checked_num(*v)?,
-		BinaryOp(v1, o, v2) => evaluate_binary_op_special(context, v1, *o, v2)?,
-		UnaryOp(o, v) => evaluate_unary_op(*o, &evaluate(context, v)?)?,
-		Var(name) => push_frame(
+		BinaryOp(v1, o, v2) => evaluate_binary_op_special(s, ctx, v1, *o, v2)?,
+		UnaryOp(o, v) => evaluate_unary_op(*o, &evaluate(s, ctx, v)?)?,
+		Var(name) => s.push(
 			CallLocation::new(loc),
 			|| format!("variable <{}> access", name),
-			|| context.binding(name.clone())?.evaluate(),
+			|| ctx.binding(name.clone())?.evaluate(s.clone()),
 		)?,
 		Index(value, index) => {
-			match (evaluate(context.clone(), value)?, evaluate(context, index)?) {
-				(Val::Obj(v), Val::Str(s)) => {
-					let sn = s.clone();
-					push_frame(
-						CallLocation::new(loc),
-						|| format!("field <{}> access", sn),
-						|| {
-							if let Some(v) = v.get(s.clone())? {
-								Ok(v)
-							} else {
-								throw!(NoSuchField(s))
-							}
-						},
-					)?
-				}
+			match (
+				evaluate(s.clone(), ctx.clone(), value)?,
+				evaluate(s.clone(), ctx, index)?,
+			) {
+				(Val::Obj(v), Val::Str(key)) => s.push(
+					CallLocation::new(loc),
+					|| format!("field <{}> access", key),
+					|| {
+						if let Some(v) = v.get(s.clone(), key.clone())? {
+							Ok(v)
+						} else {
+							throw!(NoSuchField(key.clone()))
+						}
+					},
+				)?,
 				(Val::Obj(_), n) => throw!(ValueIndexMustBeTypeGot(
 					ValType::Obj,
 					ValType::Str,
@@ -553,7 +579,7 @@
 					if n.fract() > f64::EPSILON {
 						throw!(FractionalIndex)
 					}
-					v.get(n as usize)?
+					v.get(s, n as usize)?
 						.ok_or_else(|| ArrayBoundsError(n as usize, v.len()))?
 				}
 				(Val::Arr(_), Val::Str(n)) => throw!(AttemptedIndexAnArrayWithString(n)),
@@ -582,17 +608,12 @@
 		LocalExpr(bindings, returned) => {
 			let mut new_bindings: GcHashMap<IStr, LazyVal> =
 				GcHashMap::with_capacity(bindings.len());
-			let future_context = Context::new_future();
+			let fctx = Context::new_future();
 			for b in bindings {
-				new_bindings.insert(
-					b.name.clone(),
-					evaluate_binding_in_future(b, future_context.clone()),
-				);
+				new_bindings.insert(b.name.clone(), evaluate_binding_in_future(b, fctx.clone()));
 			}
-			let context = context
-				.extend_bound(new_bindings)
-				.into_future(future_context);
-			evaluate(context, &returned.clone())?
+			let ctx = ctx.extend_bound(new_bindings).into_future(fctx);
+			evaluate(s, ctx, &returned.clone())?
 		}
 		Arr(items) => {
 			let mut out = Vec::with_capacity(items.len());
@@ -600,16 +621,16 @@
 				// TODO: Implement ArrValue::Lazy with same context for every element?
 				#[derive(Trace)]
 				struct ArrayElement {
-					context: Context,
+					ctx: Context,
 					item: LocExpr,
 				}
 				impl LazyValValue for ArrayElement {
-					fn get(self: Box<Self>) -> Result<Val> {
-						evaluate(self.context, &self.item)
+					fn get(self: Box<Self>, s: State) -> Result<Val> {
+						evaluate(s, self.ctx, &self.item)
 					}
 				}
 				out.push(LazyVal::new(TraceBox(Box::new(ArrayElement {
-					context: context.clone(),
+					ctx: ctx.clone(),
 					item: item.clone(),
 				}))));
 			}
@@ -617,22 +638,23 @@
 		}
 		ArrComp(expr, comp_specs) => {
 			let mut out = Vec::new();
-			evaluate_comp(context, comp_specs, &mut |ctx| {
-				out.push(evaluate(ctx, expr)?);
+			evaluate_comp(s.clone(), ctx, comp_specs, &mut |ctx| {
+				out.push(evaluate(s.clone(), ctx, expr)?);
 				Ok(())
 			})?;
 			Val::Arr(ArrValue::Eager(Cc::new(out)))
 		}
-		Obj(body) => Val::Obj(evaluate_object(context, body)?),
-		ObjExtend(s, t) => evaluate_add_op(
-			&evaluate(context.clone(), s)?,
-			&Val::Obj(evaluate_object(context, t)?),
+		Obj(body) => Val::Obj(evaluate_object(s, ctx, body)?),
+		ObjExtend(a, b) => evaluate_add_op(
+			s.clone(),
+			&evaluate(s.clone(), ctx.clone(), a)?,
+			&Val::Obj(evaluate_object(s, ctx, b)?),
 		)?,
 		Apply(value, args, tailstrict) => {
-			evaluate_apply(context, value, args, CallLocation::new(loc), *tailstrict)?
+			evaluate_apply(s, ctx, value, args, CallLocation::new(loc), *tailstrict)?
 		}
 		Function(params, body) => {
-			evaluate_method(context, "anonymous".into(), params.clone(), body.clone())
+			evaluate_method(ctx, "anonymous".into(), params.clone(), body.clone())
 		}
 		Intrinsic(name) => Val::Func(FuncVal::StaticBuiltin(
 			BUILTINS
@@ -640,56 +662,61 @@
 				.ok_or_else(|| IntrinsicNotFound(name.clone()))?,
 		)),
 		AssertExpr(assert, returned) => {
-			evaluate_assert(context.clone(), assert)?;
-			evaluate(context, returned)?
+			evaluate_assert(s.clone(), ctx.clone(), assert)?;
+			evaluate(s, ctx, returned)?
 		}
-		ErrorStmt(e) => push_frame(
+		ErrorStmt(e) => s.push(
 			CallLocation::new(loc),
 			|| "error statement".to_owned(),
-			|| throw!(RuntimeError(evaluate(context, e)?.to_string()?,)),
+			|| {
+				throw!(RuntimeError(
+					evaluate(s.clone(), ctx, e)?.to_string(s.clone())?,
+				))
+			},
 		)?,
 		IfElse {
 			cond,
 			cond_then,
 			cond_else,
 		} => {
-			if push_frame(
+			if s.push(
 				CallLocation::new(loc),
 				|| "if condition".to_owned(),
-				|| bool::try_from(evaluate(context.clone(), &cond.0)?),
+				|| bool::from_untyped(evaluate(s.clone(), ctx.clone(), &cond.0)?, s.clone()),
 			)? {
-				evaluate(context, cond_then)?
+				evaluate(s, ctx, cond_then)?
 			} else {
 				match cond_else {
-					Some(v) => evaluate(context, v)?,
+					Some(v) => evaluate(s, ctx, v)?,
 					None => Val::Null,
 				}
 			}
 		}
 		Slice(value, desc) => {
-			let indexable = evaluate(context.clone(), value)?;
+			let indexable = evaluate(s.clone(), ctx.clone(), value)?;
 			let loc = CallLocation::new(loc);
 
-			fn parse_idx<const MIN: usize>(
+			fn parse_idx<T: Typed>(
 				loc: CallLocation,
-				context: &Context,
+				s: State,
+				ctx: &Context,
 				expr: &Option<LocExpr>,
 				desc: &'static str,
-			) -> Result<Option<BoundedUsize<MIN, { i32::MAX as usize }>>> {
+			) -> Result<Option<T>> {
 				if let Some(value) = expr {
-					Ok(Some(push_frame(
+					Ok(Some(s.push(
 						loc,
 						|| format!("slice {}", desc),
-						|| evaluate(context.clone(), value)?.try_into(),
+						|| T::from_untyped(evaluate(s.clone(), ctx.clone(), value)?, s.clone()),
 					)?))
 				} else {
 					Ok(None)
 				}
 			}
 
-			let start = parse_idx(loc, &context, &desc.start, "start")?;
-			let end = parse_idx(loc, &context, &desc.end, "end")?;
-			let step = parse_idx(loc, &context, &desc.step, "step")?;
+			let start = parse_idx(loc, s.clone(), &ctx, &desc.start, "start")?;
+			let end = parse_idx(loc, s.clone(), &ctx, &desc.end, "end")?;
+			let step = parse_idx(loc, s, &ctx, &desc.step, "step")?;
 
 			std_slice(indexable.into_indexable()?, start, end, step)?
 		}
@@ -697,23 +724,23 @@
 			let tmp = loc.clone().0;
 			let mut import_location = tmp.to_path_buf();
 			import_location.pop();
-			push_frame(
+			s.push(
 				CallLocation::new(loc),
 				|| format!("import {:?}", path),
-				|| with_state(|s| s.import_file(&import_location, path)),
+				|| s.import_file(&import_location, path),
 			)?
 		}
 		ImportStr(path) => {
 			let tmp = loc.clone().0;
 			let mut import_location = tmp.to_path_buf();
 			import_location.pop();
-			Val::Str(with_state(|s| s.import_file_str(&import_location, path))?)
+			Val::Str(s.import_file_str(&import_location, path)?)
 		}
 		ImportBin(path) => {
 			let tmp = loc.clone().0;
 			let mut import_location = tmp.to_path_buf();
 			import_location.pop();
-			let bytes = with_state(|s| s.import_file_bin(&import_location, path))?;
+			let bytes = s.import_file_bin(&import_location, path)?;
 			Val::Arr(ArrValue::Bytes(bytes))
 		}
 	})
modifiedcrates/jrsonnet-evaluator/src/evaluate/operator.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/evaluate/operator.rs
+++ b/crates/jrsonnet-evaluator/src/evaluate/operator.rs
@@ -1,9 +1,8 @@
-use std::convert::TryInto;
-
 use jrsonnet_parser::{BinaryOpType, LocExpr, UnaryOpType};
 
 use crate::{
-	builtin::std_format, error::Error::*, evaluate, throw, val::equals, Context, Result, Val,
+	builtin::std_format, error::Error::*, evaluate, throw, typed::Typed, val::equals, Context,
+	Result, State, Val,
 };
 
 pub fn evaluate_unary_op(op: UnaryOpType, b: &Val) -> Result<Val> {
@@ -17,7 +16,7 @@
 	})
 }
 
-pub fn evaluate_add_op(a: &Val, b: &Val) -> Result<Val> {
+pub fn evaluate_add_op(s: State, a: &Val, b: &Val) -> Result<Val> {
 	use Val::*;
 	Ok(match (a, b) {
 		(Str(v1), Str(v2)) => Str(((**v1).to_owned() + v2).into()),
@@ -26,8 +25,8 @@
 		(Num(n), Str(o)) => Str(format!("{}{}", n, o).into()),
 		(Str(o), Num(n)) => Str(format!("{}{}", o, n).into()),
 
-		(Str(s), o) => Str(format!("{}{}", s, o.clone().to_string()?).into()),
-		(o, Str(s)) => Str(format!("{}{}", o.clone().to_string()?, s).into()),
+		(Str(a), o) => Str(format!("{}{}", a, o.clone().to_string(s)?).into()),
+		(o, Str(a)) => Str(format!("{}{}", o.clone().to_string(s)?, a).into()),
 
 		(Obj(v1), Obj(v2)) => Obj(v2.extend_from(v1.clone())),
 		(Arr(a), Arr(b)) => {
@@ -45,11 +44,13 @@
 	})
 }
 
-pub fn evaluate_mod_op(a: &Val, b: &Val) -> Result<Val> {
+pub fn evaluate_mod_op(s: State, a: &Val, b: &Val) -> Result<Val> {
 	use Val::*;
 	match (a, b) {
 		(Num(a), Num(b)) => Ok(Num(a % b)),
-		(Str(str), vals) => std_format(str.clone(), vals.clone())?.try_into(),
+		(Str(str), vals) => {
+			String::into_untyped(std_format(s.clone(), str.clone(), vals.clone())?, s)
+		}
 		(a, b) => throw!(BinaryOperatorDoesNotOperateOnValues(
 			BinaryOpType::Mod,
 			a.value_type(),
@@ -59,31 +60,32 @@
 }
 
 pub fn evaluate_binary_op_special(
-	context: Context,
+	s: State,
+	ctx: Context,
 	a: &LocExpr,
 	op: BinaryOpType,
 	b: &LocExpr,
 ) -> Result<Val> {
 	use BinaryOpType::*;
 	use Val::*;
-	Ok(match (evaluate(context.clone(), a)?, op, b) {
+	Ok(match (evaluate(s.clone(), ctx.clone(), a)?, op, b) {
 		(Bool(true), Or, _o) => Val::Bool(true),
 		(Bool(false), And, _o) => Val::Bool(false),
-		(a, op, eb) => evaluate_binary_op_normal(&a, op, &evaluate(context, eb)?)?,
+		(a, op, eb) => evaluate_binary_op_normal(s.clone(), &a, op, &evaluate(s, ctx, eb)?)?,
 	})
 }
 
-pub fn evaluate_binary_op_normal(a: &Val, op: BinaryOpType, b: &Val) -> Result<Val> {
+pub fn evaluate_binary_op_normal(s: State, a: &Val, op: BinaryOpType, b: &Val) -> Result<Val> {
 	use BinaryOpType::*;
 	use Val::*;
 	Ok(match (a, op, b) {
-		(a, Add, b) => evaluate_add_op(a, b)?,
+		(a, Add, b) => evaluate_add_op(s, a, b)?,
 
-		(a, Eq, b) => Bool(equals(a, b)?),
-		(a, Neq, b) => Bool(!equals(a, b)?),
+		(a, Eq, b) => Bool(equals(s, a, b)?),
+		(a, Neq, b) => Bool(!equals(s, a, b)?),
 
 		(Str(a), In, Obj(obj)) => Bool(obj.has_field_ex(a.clone(), true)),
-		(a, Mod, b) => evaluate_mod_op(a, b)?,
+		(a, Mod, b) => evaluate_mod_op(s, a, b)?,
 
 		(Str(v1), Mul, Num(v2)) => Str(v1.repeat(*v2 as usize).into()),
 
modifiedcrates/jrsonnet-evaluator/src/function.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/function.rs
+++ b/crates/jrsonnet-evaluator/src/function.rs
@@ -1,4 +1,4 @@
-use std::{borrow::Cow, collections::HashMap, convert::TryFrom};
+use std::{borrow::Cow, collections::HashMap};
 
 use gcmodule::Trace;
 use jrsonnet_interner::IStr;
@@ -6,13 +6,8 @@
 use jrsonnet_parser::{ArgsDesc, ExprLocation, LocExpr, ParamsDesc};
 
 use crate::{
-	error::{Error::*, LocError},
-	evaluate, evaluate_named,
-	gc::TraceBox,
-	throw,
-	typed::Typed,
-	val::LazyValValue,
-	Context, FutureWrapper, GcHashMap, LazyVal, Result, Val,
+	error::Error::*, evaluate, evaluate_named, gc::TraceBox, throw, typed::Typed,
+	val::LazyValValue, Context, FutureWrapper, GcHashMap, LazyVal, Result, State, Val,
 };
 
 #[derive(Clone, Copy)]
@@ -30,37 +25,37 @@
 
 #[derive(Trace)]
 struct EvaluateLazyVal {
-	context: Context,
+	ctx: Context,
 	expr: LocExpr,
 }
 impl LazyValValue for EvaluateLazyVal {
-	fn get(self: Box<Self>) -> Result<Val> {
-		evaluate(self.context, &self.expr)
+	fn get(self: Box<Self>, s: State) -> Result<Val> {
+		evaluate(s, self.ctx, &self.expr)
 	}
 }
 
 #[derive(Trace)]
 struct EvaluateNamedLazyVal {
-	future_context: FutureWrapper<Context>,
+	ctx: FutureWrapper<Context>,
 	name: IStr,
 	value: LocExpr,
 }
 impl LazyValValue for EvaluateNamedLazyVal {
-	fn get(self: Box<Self>) -> Result<Val> {
-		evaluate_named(self.future_context.unwrap(), &self.value, self.name)
+	fn get(self: Box<Self>, s: State) -> Result<Val> {
+		evaluate_named(s, self.ctx.unwrap(), &self.value, self.name)
 	}
 }
 
 pub trait ArgLike {
-	fn evaluate_arg(&self, ctx: Context, tailstrict: bool) -> Result<LazyVal>;
+	fn evaluate_arg(&self, s: State, ctx: Context, tailstrict: bool) -> Result<LazyVal>;
 }
 impl ArgLike for &LocExpr {
-	fn evaluate_arg(&self, ctx: Context, tailstrict: bool) -> Result<LazyVal> {
+	fn evaluate_arg(&self, s: State, ctx: Context, tailstrict: bool) -> Result<LazyVal> {
 		Ok(if tailstrict {
-			LazyVal::new_resolved(evaluate(ctx, self)?)
+			LazyVal::new_resolved(evaluate(s, ctx, self)?)
 		} else {
 			LazyVal::new(TraceBox(Box::new(EvaluateLazyVal {
-				context: ctx,
+				ctx,
 				expr: (*self).clone(),
 			})))
 		})
@@ -69,10 +64,9 @@
 impl<T> ArgLike for T
 where
 	T: Typed + Clone,
-	Val: TryFrom<T, Error = LocError>,
 {
-	fn evaluate_arg(&self, _ctx: Context, _tailstrict: bool) -> Result<LazyVal> {
-		let val: Val = Val::try_from(self.clone())?;
+	fn evaluate_arg(&self, s: State, _ctx: Context, _tailstrict: bool) -> Result<LazyVal> {
+		let val = T::into_untyped(self.clone(), s)?;
 		Ok(LazyVal::new_resolved(val))
 	}
 }
@@ -82,14 +76,14 @@
 	Val(Val),
 }
 impl ArgLike for TlaArg {
-	fn evaluate_arg(&self, ctx: Context, tailstrict: bool) -> Result<LazyVal> {
+	fn evaluate_arg(&self, s: State, ctx: Context, tailstrict: bool) -> Result<LazyVal> {
 		match self {
 			TlaArg::String(s) => Ok(LazyVal::new_resolved(Val::Str(s.clone()))),
 			TlaArg::Code(code) => Ok(if tailstrict {
-				LazyVal::new_resolved(evaluate(ctx, code)?)
+				LazyVal::new_resolved(evaluate(s, ctx, code)?)
 			} else {
 				LazyVal::new(TraceBox(Box::new(EvaluateLazyVal {
-					context: ctx,
+					ctx,
 					expr: code.clone(),
 				})))
 			}),
@@ -102,12 +96,14 @@
 	fn unnamed_len(&self) -> usize;
 	fn unnamed_iter(
 		&self,
+		s: State,
 		ctx: Context,
 		tailstrict: bool,
 		handler: &mut dyn FnMut(usize, LazyVal) -> Result<()>,
 	) -> Result<()>;
 	fn named_iter(
 		&self,
+		s: State,
 		ctx: Context,
 		tailstrict: bool,
 		handler: &mut dyn FnMut(&IStr, LazyVal) -> Result<()>,
@@ -122,6 +118,7 @@
 
 	fn unnamed_iter(
 		&self,
+		s: State,
 		ctx: Context,
 		tailstrict: bool,
 		handler: &mut dyn FnMut(usize, LazyVal) -> Result<()>,
@@ -130,10 +127,10 @@
 			handler(
 				id,
 				if tailstrict {
-					LazyVal::new_resolved(evaluate(ctx.clone(), arg)?)
+					LazyVal::new_resolved(evaluate(s.clone(), ctx.clone(), arg)?)
 				} else {
 					LazyVal::new(TraceBox(Box::new(EvaluateLazyVal {
-						context: ctx.clone(),
+						ctx: ctx.clone(),
 						expr: arg.clone(),
 					})))
 				},
@@ -144,6 +141,7 @@
 
 	fn named_iter(
 		&self,
+		s: State,
 		ctx: Context,
 		tailstrict: bool,
 		handler: &mut dyn FnMut(&IStr, LazyVal) -> Result<()>,
@@ -152,10 +150,10 @@
 			handler(
 				name,
 				if tailstrict {
-					LazyVal::new_resolved(evaluate(ctx.clone(), arg)?)
+					LazyVal::new_resolved(evaluate(s.clone(), ctx.clone(), arg)?)
 				} else {
 					LazyVal::new(TraceBox(Box::new(EvaluateLazyVal {
-						context: ctx.clone(),
+						ctx: ctx.clone(),
 						expr: arg.clone(),
 					})))
 				},
@@ -178,6 +176,7 @@
 
 	fn unnamed_iter(
 		&self,
+		_s: State,
 		_ctx: Context,
 		_tailstrict: bool,
 		_handler: &mut dyn FnMut(usize, LazyVal) -> Result<()>,
@@ -187,12 +186,13 @@
 
 	fn named_iter(
 		&self,
+		s: State,
 		ctx: Context,
 		tailstrict: bool,
 		handler: &mut dyn FnMut(&IStr, LazyVal) -> Result<()>,
 	) -> Result<()> {
 		for (name, val) in self.iter() {
-			handler(name, val.evaluate_arg(ctx.clone(), tailstrict)?)?;
+			handler(name, val.evaluate_arg(s.clone(), ctx.clone(), tailstrict)?)?;
 		}
 		Ok(())
 	}
@@ -211,6 +211,7 @@
 
 	fn unnamed_iter(
 		&self,
+		_s: State,
 		_ctx: Context,
 		_tailstrict: bool,
 		_handler: &mut dyn FnMut(usize, LazyVal) -> Result<()>,
@@ -220,12 +221,16 @@
 
 	fn named_iter(
 		&self,
+		s: State,
 		ctx: Context,
 		tailstrict: bool,
 		handler: &mut dyn FnMut(&IStr, LazyVal) -> Result<()>,
 	) -> Result<()> {
 		for (name, value) in self.iter() {
-			handler(name, value.evaluate_arg(ctx.clone(), tailstrict)?)?;
+			handler(
+				name,
+				value.evaluate_arg(s.clone(), ctx.clone(), tailstrict)?,
+			)?;
 		}
 		Ok(())
 	}
@@ -244,18 +249,20 @@
 
 	fn unnamed_iter(
 		&self,
+		s: State,
 		ctx: Context,
 		tailstrict: bool,
 		handler: &mut dyn FnMut(usize, LazyVal) -> Result<()>,
 	) -> Result<()> {
 		for (i, arg) in self.iter().enumerate() {
-			handler(i, arg.evaluate_arg(ctx.clone(), tailstrict)?)?;
+			handler(i, arg.evaluate_arg(s.clone(), ctx.clone(), tailstrict)?)?;
 		}
 		Ok(())
 	}
 
 	fn named_iter(
 		&self,
+		_s: State,
 		_ctx: Context,
 		_tailstrict: bool,
 		_handler: &mut dyn FnMut(&IStr, LazyVal) -> Result<()>,
@@ -272,20 +279,22 @@
 
 	fn unnamed_iter(
 		&self,
+		s: State,
 		ctx: Context,
 		tailstrict: bool,
 		handler: &mut dyn FnMut(usize, LazyVal) -> Result<()>,
 	) -> Result<()> {
-		(*self).unnamed_iter(ctx, tailstrict, handler)
+		(*self).unnamed_iter(s, ctx, tailstrict, handler)
 	}
 
 	fn named_iter(
 		&self,
+		s: State,
 		ctx: Context,
 		tailstrict: bool,
 		handler: &mut dyn FnMut(&IStr, LazyVal) -> Result<()>,
 	) -> Result<()> {
-		(*self).named_iter(ctx, tailstrict, handler)
+		(*self).named_iter(s, ctx, tailstrict, handler)
 	}
 
 	fn named_names(&self, handler: &mut dyn FnMut(&IStr)) {
@@ -302,6 +311,7 @@
 /// * `args`: passed function arguments
 /// * `tailstrict`: if set to `true` function arguments are eagerly executed, otherwise - lazily
 pub fn parse_function_call(
+	s: State,
 	ctx: Context,
 	body_ctx: Context,
 	params: &ParamsDesc,
@@ -315,14 +325,14 @@
 
 	let mut filled_args = 0;
 
-	args.unnamed_iter(ctx.clone(), tailstrict, &mut |id, arg| {
+	args.unnamed_iter(s.clone(), ctx.clone(), tailstrict, &mut |id, arg| {
 		let name = params[id].0.clone();
 		passed_args.insert(name, arg);
 		filled_args += 1;
 		Ok(())
 	})?;
 
-	args.named_iter(ctx, tailstrict, &mut |name, value| {
+	args.named_iter(s, ctx, tailstrict, &mut |name, value| {
 		// FIXME: O(n) for arg existence check
 		if !params.iter().any(|p| &p.0 == name) {
 			throw!(UnknownFunctionParameter((name as &str).to_owned()));
@@ -337,7 +347,7 @@
 	if filled_args < params.len() {
 		// Some args are unset, but maybe we have defaults for them
 		// Default values should be created in newly created context
-		let future_context = Context::new_future();
+		let fctx = Context::new_future();
 		let mut defaults = GcHashMap::with_capacity(params.len() - filled_args);
 
 		for param in params.iter().filter(|p| p.1.is_some()) {
@@ -345,7 +355,7 @@
 				continue;
 			}
 			LazyVal::new(TraceBox(Box::new(EvaluateNamedLazyVal {
-				future_context: future_context.clone(),
+				ctx: fctx.clone(),
 				name: param.0.clone(),
 				value: param.1.clone().unwrap(),
 			})));
@@ -353,7 +363,7 @@
 			defaults.insert(
 				param.0.clone(),
 				LazyVal::new(TraceBox(Box::new(EvaluateNamedLazyVal {
-					future_context: future_context.clone(),
+					ctx: fctx.clone(),
 					name: param.0.clone(),
 					value: param.1.clone().unwrap(),
 				}))),
@@ -380,7 +390,7 @@
 		Ok(body_ctx
 			.extend(passed_args, None, None, None)
 			.extend_bound(defaults)
-			.into_future(future_context))
+			.into_future(fctx))
 	} else {
 		let body_ctx = body_ctx.extend(passed_args, None, None, None);
 		Ok(body_ctx)
@@ -399,7 +409,7 @@
 pub trait Builtin: Trace {
 	fn name(&self) -> &str;
 	fn params(&self) -> &[BuiltinParam];
-	fn call(&self, context: Context, loc: CallLocation, args: &dyn ArgsLike) -> Result<Val>;
+	fn call(&self, s: State, ctx: Context, loc: CallLocation, args: &dyn ArgsLike) -> Result<Val>;
 }
 
 pub trait StaticBuiltin: Builtin + Send + Sync
@@ -418,6 +428,7 @@
 /// * `args`: passed function arguments
 /// * `tailstrict`: if set to `true` function arguments are eagerly executed, otherwise - lazily
 pub fn parse_builtin_call(
+	s: State,
 	ctx: Context,
 	params: &[BuiltinParam],
 	args: &dyn ArgsLike,
@@ -430,14 +441,14 @@
 
 	let mut filled_args = 0;
 
-	args.unnamed_iter(ctx.clone(), tailstrict, &mut |id, arg| {
+	args.unnamed_iter(s.clone(), ctx.clone(), tailstrict, &mut |id, arg| {
 		let name = params[id].name.clone();
 		passed_args.insert(name, arg);
 		filled_args += 1;
 		Ok(())
 	})?;
 
-	args.named_iter(ctx, tailstrict, &mut |name, arg| {
+	args.named_iter(s, ctx, tailstrict, &mut |name, arg| {
 		// FIXME: O(n) for arg existence check
 		let p = params
 			.iter()
@@ -480,14 +491,14 @@
 /// Creates Context, which has all argument default values applied
 /// and with unbound values causing error to be returned
 pub fn parse_default_function_call(body_ctx: Context, params: &ParamsDesc) -> Context {
-	let ctx = Context::new_future();
+	let fctx = Context::new_future();
 
 	let mut bindings = GcHashMap::new();
 
 	#[derive(Trace)]
 	struct DependsOnUnbound(IStr);
 	impl LazyValValue for DependsOnUnbound {
-		fn get(self: Box<Self>) -> Result<Val> {
+		fn get(self: Box<Self>, _: State) -> Result<Val> {
 			Err(FunctionParameterNotBoundInCall(self.0.clone()).into())
 		}
 	}
@@ -497,7 +508,7 @@
 			bindings.insert(
 				param.0.clone(),
 				LazyVal::new(TraceBox(Box::new(EvaluateNamedLazyVal {
-					future_context: ctx.clone(),
+					ctx: fctx.clone(),
 					name: param.0.clone(),
 					value: v.clone(),
 				}))),
@@ -510,5 +521,7 @@
 		}
 	}
 
-	body_ctx.extend(bindings, None, None, None).into_future(ctx)
+	body_ctx
+		.extend(bindings, None, None, None)
+		.into_future(fctx)
 }
modifiedcrates/jrsonnet-evaluator/src/integrations/serde.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/integrations/serde.rs
+++ b/crates/jrsonnet-evaluator/src/integrations/serde.rs
@@ -1,28 +1,57 @@
-use std::convert::{TryFrom, TryInto};
-
+use jrsonnet_types::ComplexValType;
 use serde_json::{Map, Number, Value};
 
 use crate::{
-	error::{Error::*, LocError, Result},
-	throw, ObjValueBuilder, Val,
+	error::{Error::*, Result},
+	throw,
+	typed::Typed,
+	ObjValueBuilder, State, Val,
 };
 
-impl TryFrom<&Val> for Value {
-	type Error = LocError;
-	fn try_from(v: &Val) -> Result<Self> {
-		Ok(match v {
-			Val::Bool(b) => Self::Bool(*b),
+impl Typed for Value {
+	const TYPE: &'static ComplexValType = &ComplexValType::Any;
+
+	fn into_untyped(value: Self, s: State) -> Result<Val> {
+		Ok(match value {
+			Value::Null => Val::Null,
+			Value::Bool(v) => Val::Bool(v),
+			Value::Number(n) => Val::Num(n.as_f64().ok_or_else(|| {
+				RuntimeError(format!("json number can't be represented as jsonnet: {}", n).into())
+			})?),
+			Value::String(s) => Val::Str((&s as &str).into()),
+			Value::Array(a) => {
+				let mut out: Vec<Val> = Vec::with_capacity(a.len());
+				for v in a {
+					out.push(Self::into_untyped(v, s.clone())?);
+				}
+				Val::Arr(out.into())
+			}
+			Value::Object(o) => {
+				let mut builder = ObjValueBuilder::with_capacity(o.len());
+				for (k, v) in o {
+					builder
+						.member((&k as &str).into())
+						.value(s.clone(), Self::into_untyped(v, s.clone())?)?;
+				}
+				Val::Obj(builder.build())
+			}
+		})
+	}
+
+	fn from_untyped(value: Val, s: State) -> Result<Self> {
+		Ok(match value {
+			Val::Bool(b) => Self::Bool(b),
 			Val::Null => Self::Null,
-			Val::Str(s) => Self::String((s as &str).into()),
+			Val::Str(s) => Self::String((&s as &str).into()),
 			Val::Num(n) => Self::Number(if n.fract() <= f64::EPSILON {
-				(*n as i64).into()
+				(n as i64).into()
 			} else {
-				Number::from_f64(*n).expect("jsonnet numbers can't be infinite or NaN")
+				Number::from_f64(n).expect("jsonnet numbers can't be infinite or NaN")
 			}),
 			Val::Arr(a) => {
 				let mut out = Vec::with_capacity(a.len());
-				for item in a.iter() {
-					out.push(item?.try_into()?);
+				for item in a.iter(s.clone()) {
+					out.push(Self::from_untyped(item?, s.clone())?);
 				}
 				Self::Array(out)
 			}
@@ -34,56 +63,16 @@
 				) {
 					out.insert(
 						(&key as &str).into(),
-						o.get(key)?
-							.expect("key is present in fields, so value should exist")
-							.try_into()?,
+						Self::from_untyped(
+							o.get(s.clone(), key)?
+								.expect("key is present in fields, so value should exist"),
+							s.clone(),
+						)?,
 					);
 				}
 				Self::Object(out)
 			}
 			Val::Func(_) => throw!(RuntimeError("tried to manifest function".into())),
 		})
-	}
-}
-impl TryFrom<Val> for Value {
-	type Error = LocError;
-
-	fn try_from(value: Val) -> Result<Self, Self::Error> {
-		<Self as TryFrom<&Val>>::try_from(&value)
-	}
-}
-
-impl TryFrom<&Value> for Val {
-	type Error = LocError;
-	fn try_from(v: &Value) -> Result<Self> {
-		Ok(match v {
-			Value::Null => Self::Null,
-			Value::Bool(v) => Self::Bool(*v),
-			Value::Number(n) => Self::Num(n.as_f64().ok_or_else(|| {
-				RuntimeError(format!("json number can't be represented as jsonnet: {}", n).into())
-			})?),
-			Value::String(s) => Self::Str((s as &str).into()),
-			Value::Array(a) => {
-				let mut out: Vec<Self> = Vec::with_capacity(a.len());
-				for v in a {
-					out.push(v.try_into()?);
-				}
-				Self::Arr(out.into())
-			}
-			Value::Object(o) => {
-				let mut builder = ObjValueBuilder::with_capacity(o.len());
-				for (k, v) in o {
-					builder.member((k as &str).into()).value(v.try_into()?)?;
-				}
-				Self::Obj(builder.build())
-			}
-		})
-	}
-}
-impl TryFrom<Value> for Val {
-	type Error = LocError;
-
-	fn try_from(value: Value) -> Result<Self, Self::Error> {
-		<Self as TryFrom<&Value>>::try_from(&value)
 	}
 }
modifiedcrates/jrsonnet-evaluator/src/lib.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/lib.rs
+++ b/crates/jrsonnet-evaluator/src/lib.rs
@@ -47,7 +47,12 @@
 pub use val::{LazyVal, ManifestFormat, Val};
 
 pub trait Bindable: Trace + 'static {
-	fn bind(&self, this: Option<ObjValue>, super_obj: Option<ObjValue>) -> Result<LazyVal>;
+	fn bind(
+		&self,
+		s: State,
+		this: Option<ObjValue>,
+		super_obj: Option<ObjValue>,
+	) -> Result<LazyVal>;
 }
 
 #[derive(Clone, Trace)]
@@ -62,9 +67,14 @@
 	}
 }
 impl LazyBinding {
-	pub fn evaluate(&self, this: Option<ObjValue>, super_obj: Option<ObjValue>) -> Result<LazyVal> {
+	pub fn evaluate(
+		&self,
+		s: State,
+		this: Option<ObjValue>,
+		super_obj: Option<ObjValue>,
+	) -> Result<LazyVal> {
 		match self {
-			Self::Bindable(v) => v.bind(this, super_obj),
+			Self::Bindable(v) => v.bind(s, this, super_obj),
 			Self::Bound(v) => Ok(v.clone()),
 		}
 	}
@@ -173,48 +183,11 @@
 	settings: RefCell<EvaluationSettings>,
 }
 
-thread_local! {
-	/// Contains the state for a currently executed file.
-	/// Global state is fine here.
-	pub(crate) static EVAL_STATE: RefCell<Option<EvaluationState>> = RefCell::new(None)
-}
-
-pub(crate) fn with_state<T>(f: impl FnOnce(&EvaluationState) -> T) -> T {
-	EVAL_STATE.with(|s| {
-		f(s.borrow().as_ref().expect(
-			"missing evaluation state, some functions should be called inside of run_in_state call",
-		))
-	})
-}
-pub fn push_frame<T>(
-	e: CallLocation,
-	frame_desc: impl FnOnce() -> String,
-	f: impl FnOnce() -> Result<T>,
-) -> Result<T> {
-	with_state(|s| s.push(e, frame_desc, f))
-}
-
-#[allow(dead_code)]
-pub fn push_val_frame(
-	e: &ExprLocation,
-	frame_desc: impl FnOnce() -> String,
-	f: impl FnOnce() -> Result<Val>,
-) -> Result<Val> {
-	with_state(|s| s.push_val(e, frame_desc, f))
-}
-#[allow(dead_code)]
-pub fn push_description_frame<T>(
-	frame_desc: impl FnOnce() -> String,
-	f: impl FnOnce() -> Result<T>,
-) -> Result<T> {
-	with_state(|s| s.push_description(frame_desc, f))
-}
-
 /// Maintains stack trace and import resolution
 #[derive(Default, Clone)]
-pub struct EvaluationState(Rc<EvaluationStateInternals>);
+pub struct State(Rc<EvaluationStateInternals>);
 
-impl EvaluationState {
+impl State {
 	/// Parses and adds file as loaded
 	pub fn add_file(&self, path: Rc<Path>, source_code: IStr) -> Result<LocExpr> {
 		let parsed = parse(
@@ -317,7 +290,7 @@
 			}
 			value.parsed.clone()
 		};
-		let value = evaluate(self.create_default_context(), &expr)?;
+		let value = evaluate(self.clone(), self.create_default_context(), &expr)?;
 		{
 			self.data_mut()
 				.files
@@ -333,16 +306,15 @@
 	pub fn with_stdlib(&self) -> &Self {
 		use jrsonnet_stdlib::STDLIB_STR;
 		let std_path: Rc<Path> = PathBuf::from("std.jsonnet").into();
-		self.run_in_state(|| {
-			self.add_parsed_file(
-				std_path.clone(),
-				STDLIB_STR.to_owned().into(),
-				builtin::get_parsed_stdlib(),
-			)
-			.unwrap();
-			let val = self.evaluate_loaded_file_raw(&std_path).unwrap();
-			self.settings_mut().globals.insert("std".into(), val);
-		});
+
+		self.add_parsed_file(
+			std_path.clone(),
+			STDLIB_STR.to_owned().into(),
+			builtin::get_parsed_stdlib(),
+		)
+		.unwrap();
+		let val = self.evaluate_loaded_file_raw(&std_path).unwrap();
+		self.settings_mut().globals.insert("std".into(), val);
 		self
 	}
 
@@ -455,41 +427,7 @@
 				desc: frame_desc(),
 			});
 			return Err(err);
-		}
-		result
-	}
-
-	/// Runs passed function in state (required if function needs to modify stack trace)
-	pub fn run_in_state<T>(&self, f: impl FnOnce() -> T) -> T {
-		EVAL_STATE.with(|v| {
-			let has_state = v.borrow().is_some();
-			if !has_state {
-				v.borrow_mut().replace(self.clone());
-			}
-			let result = f();
-			if !has_state {
-				v.borrow_mut().take();
-			}
-			result
-		})
-	}
-	pub fn run_in_state_with_breakpoint(
-		&self,
-		bp: Rc<Breakpoint>,
-		f: impl FnOnce() -> Result<()>,
-	) -> Result<()> {
-		{
-			let mut data = self.data_mut();
-			data.breakpoints.0.push(bp);
-		}
-
-		let result = self.run_in_state(f);
-
-		{
-			let mut data = self.data_mut();
-			data.breakpoints.0.pop();
 		}
-
 		result
 	}
 
@@ -503,43 +441,40 @@
 	}
 
 	pub fn manifest(&self, val: Val) -> Result<IStr> {
-		self.run_in_state(|| {
-			push_description_frame(
-				|| "manifestification".to_string(),
-				|| val.manifest(&self.manifest_format()),
-			)
-		})
+		self.push_description(
+			|| "manifestification".to_string(),
+			|| val.manifest(self.clone(), &self.manifest_format()),
+		)
 	}
 	pub fn manifest_multi(&self, val: Val) -> Result<Vec<(IStr, IStr)>> {
-		self.run_in_state(|| val.manifest_multi(&self.manifest_format()))
+		val.manifest_multi(self.clone(), &self.manifest_format())
 	}
 	pub fn manifest_stream(&self, val: Val) -> Result<Vec<IStr>> {
-		self.run_in_state(|| val.manifest_stream(&self.manifest_format()))
+		val.manifest_stream(self.clone(), &self.manifest_format())
 	}
 
 	/// If passed value is function then call with set TLA
 	pub fn with_tla(&self, val: Val) -> Result<Val> {
-		self.run_in_state(|| {
-			Ok(match val {
-				Val::Func(func) => push_description_frame(
-					|| "during TLA call".to_owned(),
-					|| {
-						func.evaluate(
-							self.create_default_context(),
-							CallLocation::native(),
-							&self.settings().tla_vars,
-							true,
-						)
-					},
-				)?,
-				v => v,
-			})
+		Ok(match val {
+			Val::Func(func) => self.push_description(
+				|| "during TLA call".to_owned(),
+				|| {
+					func.evaluate(
+						self.clone(),
+						self.create_default_context(),
+						CallLocation::native(),
+						&self.settings().tla_vars,
+						true,
+					)
+				},
+			)?,
+			v => v,
 		})
 	}
 }
 
 /// Internals
-impl EvaluationState {
+impl State {
 	fn data(&self) -> Ref<EvaluationData> {
 		self.0.data.borrow()
 	}
@@ -555,12 +490,12 @@
 }
 
 /// Raw methods evaluate passed values but don't perform TLA execution
-impl EvaluationState {
+impl State {
 	pub fn evaluate_file_raw(&self, name: &Path) -> Result<Val> {
-		self.run_in_state(|| self.import_file(&std::env::current_dir().expect("cwd"), name))
+		self.import_file(&std::env::current_dir().expect("cwd"), name)
 	}
 	pub fn evaluate_file_raw_nocwd(&self, name: &Path) -> Result<Val> {
-		self.run_in_state(|| self.import_file(&PathBuf::from("."), name))
+		self.import_file(&PathBuf::from("."), name)
 	}
 	/// Parses and evaluates the given snippet
 	pub fn evaluate_snippet_raw(&self, source: Rc<Path>, code: IStr) -> Result<Val> {
@@ -580,12 +515,12 @@
 	}
 	/// Evaluates the parsed expression
 	pub fn evaluate_expr_raw(&self, code: LocExpr) -> Result<Val> {
-		self.run_in_state(|| evaluate(self.create_default_context(), &code))
+		evaluate(self.clone(), self.create_default_context(), &code)
 	}
 }
 
 /// Settings utilities
-impl EvaluationState {
+impl State {
 	pub fn add_ext_var(&self, name: IStr, value: Val) {
 		self.settings_mut().ext_vars.insert(name, value);
 	}
@@ -712,38 +647,36 @@
 		gc::TraceBox,
 		native::NativeCallbackHandler,
 		val::primitive_equals,
-		EvaluationState,
+		State,
 	};
 
 	#[test]
 	#[should_panic]
 	fn eval_state_stacktrace() {
-		let state = EvaluationState::default();
-		state.run_in_state(|| {
-			state
-				.push(
-					CallLocation::new(&ExprLocation(PathBuf::from("test1.jsonnet").into(), 10, 20)),
-					|| "outer".to_owned(),
-					|| {
-						state.push(
-							CallLocation::new(&ExprLocation(
-								PathBuf::from("test2.jsonnet").into(),
-								30,
-								40,
-							)),
-							|| "inner".to_owned(),
-							|| Err(RuntimeError("".into()).into()),
-						)?;
-						Ok(Val::Null)
-					},
-				)
-				.unwrap();
-		});
+		let state = State::default();
+		state
+			.push(
+				CallLocation::new(&ExprLocation(PathBuf::from("test1.jsonnet").into(), 10, 20)),
+				|| "outer".to_owned(),
+				|| {
+					state.push(
+						CallLocation::new(&ExprLocation(
+							PathBuf::from("test2.jsonnet").into(),
+							30,
+							40,
+						)),
+						|| "inner".to_owned(),
+						|| Err(RuntimeError("".into()).into()),
+					)?;
+					Ok(Val::Null)
+				},
+			)
+			.unwrap();
 	}
 
 	#[test]
 	fn eval_state_standard() {
-		let state = EvaluationState::default();
+		let state = State::default();
 		state.with_stdlib();
 		assert!(primitive_equals(
 			&state
@@ -759,27 +692,23 @@
 
 	macro_rules! eval {
 		($str: expr) => {{
-			let evaluator = EvaluationState::default();
+			let evaluator = State::default();
 			evaluator.with_stdlib();
-			evaluator.run_in_state(|| {
-				evaluator
-					.evaluate_snippet_raw(PathBuf::from("raw.jsonnet").into(), $str.into())
-					.unwrap()
-			})
+			evaluator
+				.evaluate_snippet_raw(PathBuf::from("raw.jsonnet").into(), $str.into())
+				.unwrap()
 		}};
 	}
 	macro_rules! eval_json {
 		($str: expr) => {{
-			let evaluator = EvaluationState::default();
+			let evaluator = State::default();
 			evaluator.with_stdlib();
-			evaluator.run_in_state(|| {
-				evaluator
-					.evaluate_snippet_raw(PathBuf::from("raw.jsonnet").into(), $str.into())
-					.unwrap()
-					.to_json(0)
-					.unwrap()
-					.replace("\n", "")
-			})
+			evaluator
+				.evaluate_snippet_raw(PathBuf::from("raw.jsonnet").into(), $str.into())
+				.unwrap()
+				.to_json(0)
+				.unwrap()
+				.replace("\n", "")
 		}};
 	}
 
@@ -1143,7 +1072,7 @@
 	#[test]
 	fn native_ext() -> crate::error::Result<()> {
 		use super::native::NativeCallback;
-		let evaluator = EvaluationState::default();
+		let evaluator = State::default();
 
 		evaluator.with_stdlib();
 
@@ -1178,10 +1107,12 @@
 				TraceBox(Box::new(NativeAdd)),
 			)))),
 		);
+		dbg!(evaluator.settings().ext_natives.keys().collect::<Vec<_>>());
 		evaluator.evaluate_snippet_raw(
 			PathBuf::from("native_caller.jsonnet").into(),
 			"std.assertEqual(std.native(\"native_add\")(1, 2), 3)".into(),
 		)?;
+		dbg!(evaluator.settings().ext_natives.keys().collect::<Vec<_>>());
 		Ok(())
 	}
 
@@ -1250,14 +1181,14 @@
 
 	#[test]
 	fn issue_23() {
-		let state = EvaluationState::default();
+		let state = State::default();
 		state.set_import_resolver(Box::new(TestImportResolver(r#"import "/test""#.into())));
 		let _ = state.evaluate_file_raw(&PathBuf::from("/test"));
 	}
 
 	#[test]
 	fn issue_40() {
-		let state = EvaluationState::default();
+		let state = State::default();
 		state.with_stdlib();
 
 		let error = state
@@ -1306,7 +1237,7 @@
 	mod derive_typed {
 		use std::path::PathBuf;
 
-		use crate::{typed::Typed, EvaluationState};
+		use crate::{typed::Typed, State};
 
 		#[derive(PartialEq, Debug, Typed)]
 		struct MyTyped {
@@ -1317,9 +1248,9 @@
 
 		#[test]
 		fn test() {
-			let es = EvaluationState::default();
+			let es = State::default();
 			let val = eval!("{a: 14, b: 'Hello, world!'}");
-			let typed = es.run_in_state(|| MyTyped::try_from(val).unwrap());
+			let typed = MyTyped::try_from(val).unwrap();
 
 			assert_eq!(
 				typed,
@@ -1328,10 +1259,9 @@
 					c: "Hello, world!".to_string()
 				}
 			);
-			es.settings_mut().globals.insert(
-				"mytyped".into(),
-				es.run_in_state(|| typed.try_into()).unwrap(),
-			);
+			es.settings_mut()
+				.globals
+				.insert("mytyped".into(), typed.try_into().unwrap());
 
 			let v = es
 				.evaluate_snippet_raw(
modifiedcrates/jrsonnet-evaluator/src/native.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/native.rs
+++ b/crates/jrsonnet-evaluator/src/native.rs
@@ -8,7 +8,7 @@
 	error::Result,
 	function::{parse_builtin_call, ArgsLike, Builtin, BuiltinParam, CallLocation},
 	gc::TraceBox,
-	Context, Val,
+	Context, State, Val,
 };
 
 #[derive(Trace)]
@@ -34,16 +34,16 @@
 		&self.params
 	}
 
-	fn call(&self, context: Context, loc: CallLocation, args: &dyn ArgsLike) -> Result<Val> {
-		let args = parse_builtin_call(context, &self.params, args, true)?;
+	fn call(&self, s: State, ctx: Context, loc: CallLocation, args: &dyn ArgsLike) -> Result<Val> {
+		let args = parse_builtin_call(s.clone(), ctx, &self.params, args, true)?;
 		let mut out_args = Vec::with_capacity(self.params.len());
 		for p in self.params.iter() {
-			out_args.push(args[&p.name].evaluate()?);
+			out_args.push(args[&p.name].evaluate(s.clone())?);
 		}
-		self.handler.call(loc.0.map(|l| l.0.clone()), &out_args)
+		self.handler.call(s, loc.0.map(|l| l.0.clone()), &out_args)
 	}
 }
 
 pub trait NativeCallbackHandler: Trace {
-	fn call(&self, from: Option<Rc<Path>>, args: &[Val]) -> Result<Val>;
+	fn call(&self, s: State, from: Option<Rc<Path>>, args: &[Val]) -> Result<Val>;
 }
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,
-	push_frame, throw, weak_ptr_eq, weak_raw, Bindable, LazyBinding, LazyVal, Result, Val,
+	throw, weak_ptr_eq, weak_raw, Bindable, LazyBinding, LazyVal, Result, State, Val,
 };
 
 #[cfg(not(feature = "exp-preserve-order"))]
@@ -99,7 +99,7 @@
 }
 
 pub trait ObjectAssertion: Trace {
-	fn run(&self, this: Option<ObjValue>, super_obj: Option<ObjValue>) -> Result<()>;
+	fn run(&self, s: State, this: Option<ObjValue>, super_obj: Option<ObjValue>) -> Result<()>;
 }
 
 // Field => This
@@ -360,14 +360,14 @@
 			.unwrap_or(false)
 	}
 
-	pub fn get(&self, key: IStr) -> Result<Option<Val>> {
-		self.run_assertions()?;
-		self.get_raw(key, self.0.this_obj.as_ref())
+	pub fn get(&self, s: State, key: IStr) -> Result<Option<Val>> {
+		self.run_assertions(s.clone())?;
+		self.get_raw(s, key, self.0.this_obj.as_ref())
 	}
 
 	// pub fn extend_with(self, key: )
 
-	fn get_raw(&self, key: IStr, real_this: Option<&Self>) -> Result<Option<Val>> {
+	fn get_raw(&self, s: State, key: IStr, real_this: Option<&Self>) -> Result<Option<Val>> {
 		let real_this = real_this.unwrap_or(self);
 		let cache_key = (key.clone(), WeakObjValue(real_this.0.downgrade()));
 
@@ -384,19 +384,20 @@
 			.borrow_mut()
 			.insert(cache_key.clone(), CacheValue::Pending);
 		let value = match (self.0.this_entries.get(&key), &self.0.super_obj) {
-			(Some(k), None) => Ok(Some(self.evaluate_this(k, real_this)?)),
-			(Some(k), Some(s)) => {
-				let our = self.evaluate_this(k, real_this)?;
+			(Some(k), None) => Ok(Some(self.evaluate_this(s, k, real_this)?)),
+			(Some(k), Some(super_obj)) => {
+				let our = self.evaluate_this(s.clone(), k, real_this)?;
 				if k.add {
-					s.get_raw(key, Some(real_this))?
+					super_obj
+						.get_raw(s.clone(), key, Some(real_this))?
 						.map_or(Ok(Some(our.clone())), |v| {
-							Ok(Some(evaluate_add_op(&v, &our)?))
+							Ok(Some(evaluate_add_op(s.clone(), &v, &our)?))
 						})
 				} else {
 					Ok(Some(our))
 				}
 			}
-			(None, Some(s)) => s.get_raw(key, Some(real_this)),
+			(None, Some(super_obj)) => super_obj.get_raw(s, key, Some(real_this)),
 			(None, None) => Ok(None),
 		};
 		let value = match value {
@@ -418,28 +419,30 @@
 		);
 		Ok(value)
 	}
-	fn evaluate_this(&self, v: &ObjMember, real_this: &Self) -> Result<Val> {
+	fn evaluate_this(&self, s: State, v: &ObjMember, real_this: &Self) -> Result<Val> {
 		v.invoke
-			.evaluate(Some(real_this.clone()), self.0.super_obj.clone())?
-			.evaluate()
+			.evaluate(s.clone(), Some(real_this.clone()), self.0.super_obj.clone())?
+			.evaluate(s)
 	}
 
-	fn run_assertions_raw(&self, real_this: &Self) -> Result<()> {
+	fn run_assertions_raw(&self, s: State, real_this: &Self) -> Result<()> {
 		if self.0.assertions_ran.borrow_mut().insert(real_this.clone()) {
 			for assertion in self.0.assertions.iter() {
-				if let Err(e) = assertion.run(Some(real_this.clone()), self.0.super_obj.clone()) {
+				if let Err(e) =
+					assertion.run(s.clone(), Some(real_this.clone()), self.0.super_obj.clone())
+				{
 					self.0.assertions_ran.borrow_mut().remove(real_this);
 					return Err(e);
 				}
 			}
 			if let Some(super_obj) = &self.0.super_obj {
-				super_obj.run_assertions_raw(real_this)?;
+				super_obj.run_assertions_raw(s, real_this)?;
 			}
 		}
 		Ok(())
 	}
-	pub fn run_assertions(&self) -> Result<()> {
-		self.run_assertions_raw(self)
+	pub fn run_assertions(&self, s: State) -> Result<()> {
+		self.run_assertions_raw(s, self)
 	}
 
 	pub fn ptr_eq(a: &Self, b: &Self) -> bool {
@@ -565,18 +568,18 @@
 
 pub struct ValueBuilder<'v>(&'v mut ObjValueBuilder);
 impl<'v> ObjMemberBuilder<ValueBuilder<'v>> {
-	pub fn value(self, value: Val) -> Result<()> {
-		self.binding(LazyBinding::Bound(LazyVal::new_resolved(value)))
+	pub fn value(self, s: State, value: Val) -> Result<()> {
+		self.binding(s, LazyBinding::Bound(LazyVal::new_resolved(value)))
 	}
-	pub fn bindable(self, bindable: TraceBox<dyn Bindable>) -> Result<()> {
-		self.binding(LazyBinding::Bindable(Cc::new(bindable)))
+	pub fn bindable(self, s: State, bindable: TraceBox<dyn Bindable>) -> Result<()> {
+		self.binding(s, LazyBinding::Bindable(Cc::new(bindable)))
 	}
-	pub fn binding(self, binding: LazyBinding) -> Result<()> {
+	pub fn binding(self, s: State, binding: LazyBinding) -> Result<()> {
 		let (receiver, name, member) = self.build_member(binding);
 		let location = member.location.clone();
 		let old = receiver.0.map.insert(name.clone(), member);
 		if old.is_some() {
-			push_frame(
+			s.push(
 				CallLocation(location.as_ref()),
 				|| format!("field <{}> initializtion", name.clone()),
 				|| throw!(DuplicateFieldName(name.clone())),
modifiedcrates/jrsonnet-evaluator/src/trace/mod.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/trace/mod.rs
+++ b/crates/jrsonnet-evaluator/src/trace/mod.rs
@@ -4,7 +4,7 @@
 
 pub use location::*;
 
-use crate::{error::Error, EvaluationState, LocError};
+use crate::{error::Error, LocError, State};
 
 /// The way paths should be displayed
 pub enum PathResolver {
@@ -39,16 +39,9 @@
 	fn write_trace(
 		&self,
 		out: &mut dyn std::fmt::Write,
-		evaluation_state: &EvaluationState,
+		s: &State,
 		error: &LocError,
 	) -> Result<(), std::fmt::Error>;
-	// fn print_trace(
-	// 	&self,
-	// 	evaluation_state: &EvaluationState,
-	// 	error: &LocError,
-	// ) -> Result<(), std::fmt::Error> {
-	// 	self.write_trace(&mut std::fmt::stdout(), evaluation_state, error)
-	// }
 }
 
 fn print_code_location(
@@ -85,7 +78,7 @@
 	fn write_trace(
 		&self,
 		out: &mut dyn std::fmt::Write,
-		evaluation_state: &EvaluationState,
+		s: &State,
 		error: &LocError,
 	) -> Result<(), std::fmt::Error> {
 		write!(out, "{}", error.error())?;
@@ -128,8 +121,7 @@
 				if let Some(location) = location {
 					let mut resolved_path = self.resolver.resolve(&location.0);
 					// TODO: Process all trace elements first
-					let location = evaluation_state
-						.map_source_locations(&location.0, &[location.1, location.2]);
+					let location = s.map_source_locations(&location.0, &[location.1, location.2]);
 					write!(resolved_path, ":").unwrap();
 					print_code_location(&mut resolved_path, &location[0], &location[1]).unwrap();
 					write!(resolved_path, ":").unwrap();
@@ -170,7 +162,7 @@
 	fn write_trace(
 		&self,
 		out: &mut dyn std::fmt::Write,
-		evaluation_state: &EvaluationState,
+		s: &State,
 		error: &LocError,
 	) -> Result<(), std::fmt::Error> {
 		write!(out, "{}", error.error())?;
@@ -178,8 +170,7 @@
 			writeln!(out)?;
 			let desc = &item.desc;
 			if let Some(source) = &item.location {
-				let start_end =
-					evaluation_state.map_source_locations(&source.0, &[source.1, source.2]);
+				let start_end = s.map_source_locations(&source.0, &[source.1, source.2]);
 
 				write!(
 					out,
@@ -207,7 +198,7 @@
 	fn write_trace(
 		&self,
 		out: &mut dyn std::fmt::Write,
-		evaluation_state: &EvaluationState,
+		s: &State,
 		error: &LocError,
 	) -> Result<(), std::fmt::Error> {
 		write!(out, "{}", error.error())?;
@@ -240,11 +231,10 @@
 			writeln!(out)?;
 			let desc = &item.desc;
 			if let Some(source) = &item.location {
-				let start_end =
-					evaluation_state.map_source_locations(&source.0, &[source.1, source.2]);
+				let start_end = s.map_source_locations(&source.0, &[source.1, source.2]);
 				self.print_snippet(
 					out,
-					&evaluation_state.get_source(&source.0).unwrap(),
+					&s.get_source(&source.0).unwrap(),
 					&source.0,
 					&start_end[0],
 					&start_end[1],
modifiedcrates/jrsonnet-evaluator/src/typed/conversions.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/typed/conversions.rs
+++ b/crates/jrsonnet-evaluator/src/typed/conversions.rs
@@ -1,8 +1,4 @@
-use std::{
-	convert::{TryFrom, TryInto},
-	ops::Deref,
-	rc::Rc,
-};
+use std::{ops::Deref, rc::Rc};
 
 use gcmodule::Cc;
 use jrsonnet_interner::IStr;
@@ -10,11 +6,11 @@
 use jrsonnet_types::{ComplexValType, ValType};
 
 use crate::{
-	error::{Error::*, LocError, Result},
+	error::{Error::*, Result},
 	throw,
 	typed::CheckType,
 	val::{ArrValue, FuncDesc, FuncVal, IndexableVal},
-	ObjValue, ObjValueBuilder, Val,
+	ObjValue, ObjValueBuilder, State, Val,
 };
 
 pub trait TypedObj: Typed {
@@ -27,8 +23,10 @@
 	}
 }
 
-pub trait Typed: TryFrom<Val, Error = LocError> + TryInto<Val, Error = LocError> {
+pub trait Typed: Sized {
 	const TYPE: &'static ComplexValType;
+	fn into_untyped(typed: Self, s: State) -> Result<Val>;
+	fn from_untyped(untyped: Val, s: State) -> Result<Self>;
 }
 
 macro_rules! impl_int {
@@ -36,12 +34,8 @@
 		impl Typed for $ty {
 			const TYPE: &'static ComplexValType =
 				&ComplexValType::BoundedNumber(Some(Self::MIN as f64), Some(Self::MAX as f64));
-		}
-		impl TryFrom<Val> for $ty {
-			type Error = LocError;
-
-			fn try_from(value: Val) -> Result<Self> {
-				<Self as Typed>::TYPE.check(&value)?;
+			fn from_untyped(value: Val, s: State) -> Result<Self> {
+				<Self as Typed>::TYPE.check(s, &value)?;
 				match value {
 					Val::Num(n) => {
 						if n.trunc() != n {
@@ -58,12 +52,8 @@
 					_ => unreachable!(),
 				}
 			}
-		}
-		impl TryFrom<$ty> for Val {
-			type Error = LocError;
-
-			fn try_from(value: $ty) -> Result<Self> {
-				Ok(Self::Num(value as f64))
+			fn into_untyped(value: Self, _: State) -> Result<Val> {
+				Ok(Val::Num(value as f64))
 			}
 		}
 	)*};
@@ -100,12 +90,9 @@
 					Some(MIN as f64),
 					Some(MAX as f64),
 				);
-		}
-		impl<const MIN: $ty, const MAX: $ty> TryFrom<Val> for $name<MIN, MAX> {
-			type Error = LocError;
 
-			fn try_from(value: Val) -> Result<Self> {
-				<Self as Typed>::TYPE.check(&value)?;
+			fn from_untyped(value: Val, s: State) -> Result<Self> {
+				<Self as Typed>::TYPE.check(s, &value)?;
 				match value {
 					Val::Num(n) => {
 						if n.trunc() != n {
@@ -122,12 +109,9 @@
 					_ => unreachable!(),
 				}
 			}
-		}
-		impl<const MIN: $ty, const MAX: $ty> TryFrom<$name<MIN, MAX>> for Val {
-			type Error = LocError;
 
-			fn try_from(value: $name<MIN, MAX>) -> Result<Self> {
-				Ok(Self::Num(value.0 as f64))
+			fn into_untyped(value: Self, _: State) -> Result<Val> {
+				Ok(Val::Num(value.0 as f64))
 			}
 		}
 	)*};
@@ -143,59 +127,50 @@
 
 impl Typed for f64 {
 	const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Num);
-}
-impl TryFrom<Val> for f64 {
-	type Error = LocError;
 
-	fn try_from(value: Val) -> Result<Self> {
-		<Self as Typed>::TYPE.check(&value)?;
+	fn into_untyped(value: Self, _: State) -> Result<Val> {
+		Ok(Val::Num(value))
+	}
+
+	fn from_untyped(value: Val, s: State) -> Result<Self> {
+		<Self as Typed>::TYPE.check(s, &value)?;
 		match value {
 			Val::Num(n) => Ok(n),
 			_ => unreachable!(),
 		}
 	}
 }
-impl TryFrom<f64> for Val {
-	type Error = LocError;
-
-	fn try_from(value: f64) -> Result<Self> {
-		Ok(Self::Num(value))
-	}
-}
 
 pub struct PositiveF64(pub f64);
 impl Typed for PositiveF64 {
 	const TYPE: &'static ComplexValType = &ComplexValType::BoundedNumber(Some(0.0), None);
-}
-impl TryFrom<Val> for PositiveF64 {
-	type Error = LocError;
 
-	fn try_from(value: Val) -> Result<Self> {
-		<Self as Typed>::TYPE.check(&value)?;
+	fn into_untyped(value: Self, _: State) -> Result<Val> {
+		Ok(Val::Num(value.0))
+	}
+
+	fn from_untyped(value: Val, s: State) -> Result<Self> {
+		<Self as Typed>::TYPE.check(s, &value)?;
 		match value {
 			Val::Num(n) => Ok(Self(n)),
 			_ => unreachable!(),
 		}
 	}
 }
-impl TryFrom<PositiveF64> for Val {
-	type Error = LocError;
-
-	fn try_from(value: PositiveF64) -> Result<Self> {
-		Ok(Self::Num(value.0))
-	}
-}
-
 impl Typed for usize {
 	// It is possible to store 54 bits of precision in f64, but leaving u32::MAX here for compatibility
 	const TYPE: &'static ComplexValType =
 		&ComplexValType::BoundedNumber(Some(0.0), Some(4294967295.0));
-}
-impl TryFrom<Val> for usize {
-	type Error = LocError;
 
-	fn try_from(value: Val) -> Result<Self> {
-		<Self as Typed>::TYPE.check(&value)?;
+	fn into_untyped(value: Self, _: State) -> Result<Val> {
+		if value > u32::MAX as Self {
+			throw!(RuntimeError("number is too large".into()))
+		}
+		Ok(Val::Num(value as f64))
+	}
+
+	fn from_untyped(value: Val, s: State) -> Result<Self> {
+		<Self as Typed>::TYPE.check(s, &value)?;
 		match value {
 			Val::Num(n) => {
 				if n.trunc() != n {
@@ -209,127 +184,81 @@
 		}
 	}
 }
-impl TryFrom<usize> for Val {
-	type Error = LocError;
 
-	fn try_from(value: usize) -> Result<Self> {
-		if value > u32::MAX as usize {
-			throw!(RuntimeError("number is too large".into()))
-		}
-		Ok(Self::Num(value as f64))
-	}
-}
-
 impl Typed for IStr {
 	const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Str);
-}
-impl TryFrom<Val> for IStr {
-	type Error = LocError;
 
-	fn try_from(value: Val) -> Result<Self> {
-		<Self as Typed>::TYPE.check(&value)?;
+	fn into_untyped(value: Self, _: State) -> Result<Val> {
+		Ok(Val::Str(value))
+	}
+
+	fn from_untyped(value: Val, s: State) -> Result<Self> {
+		<Self as Typed>::TYPE.check(s, &value)?;
 		match value {
 			Val::Str(s) => Ok(s),
 			_ => unreachable!(),
 		}
-	}
-}
-impl TryFrom<IStr> for Val {
-	type Error = LocError;
-
-	fn try_from(value: IStr) -> Result<Self> {
-		Ok(Self::Str(value))
 	}
 }
 
 impl Typed for String {
 	const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Str);
-}
-impl TryFrom<Val> for String {
-	type Error = LocError;
 
-	fn try_from(value: Val) -> Result<Self> {
-		<Self as Typed>::TYPE.check(&value)?;
+	fn into_untyped(value: Self, _: State) -> Result<Val> {
+		Ok(Val::Str(value.into()))
+	}
+
+	fn from_untyped(value: Val, s: State) -> Result<Self> {
+		<Self as Typed>::TYPE.check(s, &value)?;
 		match value {
 			Val::Str(s) => Ok(s.to_string()),
 			_ => unreachable!(),
 		}
-	}
-}
-impl TryFrom<String> for Val {
-	type Error = LocError;
-
-	fn try_from(value: String) -> Result<Self> {
-		Ok(Self::Str(value.into()))
 	}
 }
 
 impl Typed for char {
 	const TYPE: &'static ComplexValType = &ComplexValType::Char;
-}
-impl TryFrom<Val> for char {
-	type Error = LocError;
 
-	fn try_from(value: Val) -> Result<Self> {
-		<Self as Typed>::TYPE.check(&value)?;
+	fn into_untyped(value: Self, _: State) -> Result<Val> {
+		Ok(Val::Str(value.to_string().into()))
+	}
+
+	fn from_untyped(value: Val, s: State) -> Result<Self> {
+		<Self as Typed>::TYPE.check(s, &value)?;
 		match value {
 			Val::Str(s) => Ok(s.chars().next().unwrap()),
 			_ => unreachable!(),
 		}
 	}
 }
-impl TryFrom<char> for Val {
-	type Error = LocError;
 
-	fn try_from(value: char) -> Result<Self> {
-		Ok(Self::Str(value.to_string().into()))
-	}
-}
-
 impl<T> Typed for Vec<T>
 where
 	T: Typed,
-	T: TryFrom<Val, Error = LocError>,
-	T: TryInto<Val, Error = LocError>,
 {
 	const TYPE: &'static ComplexValType = &ComplexValType::ArrayRef(T::TYPE);
-}
-impl<T> TryFrom<Val> for Vec<T>
-where
-	T: Typed,
-	T: TryFrom<Val, Error = LocError>,
-	T: TryInto<Val, Error = LocError>,
-{
-	type Error = LocError;
 
-	fn try_from(value: Val) -> Result<Self> {
-		<Self as Typed>::TYPE.check(&value)?;
+	fn into_untyped(value: Self, s: State) -> Result<Val> {
+		let mut o = Vec::with_capacity(value.len());
+		for i in value {
+			o.push(T::into_untyped(i, s.clone())?);
+		}
+		Ok(Val::Arr(o.into()))
+	}
+
+	fn from_untyped(value: Val, s: State) -> Result<Self> {
+		<Self as Typed>::TYPE.check(s.clone(), &value)?;
 		match value {
 			Val::Arr(a) => {
 				let mut o = Self::with_capacity(a.len());
-				for i in a.iter() {
-					o.push(T::try_from(i?)?);
+				for i in a.iter(s.clone()) {
+					o.push(T::from_untyped(i?, s.clone())?);
 				}
 				Ok(o)
 			}
 			_ => unreachable!(),
-		}
-	}
-}
-impl<T> TryFrom<Vec<T>> for Val
-where
-	T: Typed,
-	T: TryFrom<Self, Error = LocError>,
-	T: TryInto<Self, Error = LocError>,
-{
-	type Error = LocError;
-
-	fn try_from(value: Vec<T>) -> Result<Self> {
-		let mut o = Vec::with_capacity(value.len());
-		for i in value {
-			o.push(i.try_into()?);
 		}
-		Ok(Self::Arr(o.into()))
 	}
 }
 
@@ -340,19 +269,13 @@
 
 impl Typed for Any {
 	const TYPE: &'static ComplexValType = &ComplexValType::Any;
-}
-impl TryFrom<Val> for Any {
-	type Error = LocError;
 
-	fn try_from(value: Val) -> Result<Self> {
-		Ok(Self(value))
+	fn into_untyped(value: Self, _: State) -> Result<Val> {
+		Ok(value.0)
 	}
-}
-impl TryFrom<Any> for Val {
-	type Error = LocError;
 
-	fn try_from(value: Any) -> Result<Self> {
-		Ok(value.0)
+	fn from_untyped(value: Val, _: State) -> Result<Self> {
+		Ok(Self(value))
 	}
 }
 
@@ -361,47 +284,42 @@
 
 impl Typed for VecVal {
 	const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Arr);
-}
-impl TryFrom<Val> for VecVal {
-	type Error = LocError;
 
-	fn try_from(value: Val) -> Result<Self> {
-		<Self as Typed>::TYPE.check(&value)?;
+	fn into_untyped(value: Self, _: State) -> Result<Val> {
+		Ok(Val::Arr(ArrValue::Eager(value.0)))
+	}
+
+	fn from_untyped(value: Val, s: State) -> Result<Self> {
+		<Self as Typed>::TYPE.check(s.clone(), &value)?;
 		match value {
-			Val::Arr(a) => Ok(Self(a.evaluated()?)),
+			Val::Arr(a) => Ok(Self(a.evaluated(s)?)),
 			_ => unreachable!(),
 		}
 	}
 }
-impl TryFrom<VecVal> for Val {
-	type Error = LocError;
 
-	fn try_from(value: VecVal) -> Result<Self> {
-		Ok(Self::Arr(ArrValue::Eager(value.0)))
-	}
-}
-
 /// Specialization
 pub struct Bytes(pub Rc<[u8]>);
 
 impl Typed for Bytes {
 	const TYPE: &'static ComplexValType =
 		&ComplexValType::ArrayRef(&ComplexValType::BoundedNumber(Some(0.0), Some(255.0)));
-}
-impl TryFrom<Val> for Bytes {
-	type Error = LocError;
 
-	fn try_from(value: Val) -> Result<Self> {
+	fn into_untyped(value: Self, _: State) -> Result<Val> {
+		Ok(Val::Arr(ArrValue::Bytes(value.0)))
+	}
+
+	fn from_untyped(value: Val, s: State) -> Result<Self> {
 		match value {
 			Val::Arr(ArrValue::Bytes(bytes)) => Ok(Self(bytes)),
 			_ => {
-				<Self as Typed>::TYPE.check(&value)?;
+				<Self as Typed>::TYPE.check(s.clone(), &value)?;
 				match value {
 					Val::Arr(a) => {
 						let mut out = Vec::with_capacity(a.len());
-						for e in a.iter() {
+						for e in a.iter(s.clone()) {
 							let r = e?;
-							out.push(u8::try_from(r)?);
+							out.push(u8::from_untyped(r, s.clone())?);
 						}
 						Ok(Self(out.into()))
 					}
@@ -409,33 +327,20 @@
 				}
 			}
 		}
-	}
-}
-impl TryFrom<Bytes> for Val {
-	type Error = LocError;
-
-	fn try_from(value: Bytes) -> Result<Self> {
-		Ok(Self::Arr(ArrValue::Bytes(value.0)))
 	}
 }
 
 pub struct M1;
 impl Typed for M1 {
 	const TYPE: &'static ComplexValType = &ComplexValType::BoundedNumber(Some(-1.0), Some(-1.0));
-}
-impl TryFrom<Val> for M1 {
-	type Error = LocError;
 
-	fn try_from(value: Val) -> Result<Self> {
-		<Self as Typed>::TYPE.check(&value)?;
-		Ok(Self)
+	fn into_untyped(_: Self, _: State) -> Result<Val> {
+		Ok(Val::Num(-1.0))
 	}
-}
-impl TryFrom<M1> for Val {
-	type Error = LocError;
 
-	fn try_from(_: M1) -> Result<Self> {
-		Ok(Self::Num(-1.0))
+	fn from_untyped(value: Val, s: State) -> Result<Self> {
+		<Self as Typed>::TYPE.check(s, &value)?;
+		Ok(Self)
 	}
 }
 
@@ -449,33 +354,22 @@
 			$($id: Typed,)*
 		{
 			const TYPE: &'static ComplexValType = &ComplexValType::UnionRef(&[$($id::TYPE),*]);
-		}
-		impl<$($id),*> TryFrom<Val> for $name<$($id),*>
-		where
-			$($id: Typed,)*
-		{
-			type Error = LocError;
 
-			fn try_from(value: Val) -> Result<Self> {
+			fn into_untyped(value: Self, s: State) -> Result<Val> {
+				match value {$(
+					$name::$id(v) => $id::into_untyped(v, s)
+				),*}
+			}
+
+			fn from_untyped(value: Val, s: State) -> Result<Self> {
 				$(
-					if $id::TYPE.check(&value).is_ok() {
-						$id::try_from(value).map(Self::$id)
+					if $id::TYPE.check(s.clone(), &value).is_ok() {
+						$id::from_untyped(value, s.clone()).map(Self::$id)
 					} else
 				)* {
-					<Self as Typed>::TYPE.check(&value)?;
+					<Self as Typed>::TYPE.check(s, &value)?;
 					unreachable!()
 				}
-			}
-		}
-		impl<$($id),*> TryFrom<$name<$($id),*>> for Val
-		where
-			$($id: Typed,)*
-		{
-			type Error = LocError;
-			fn try_from(value: $name<$($id),*>) -> Result<Self> {
-				match value {$(
-					$name::$id(v) => v.try_into()
-				),*}
 			}
 		}
 	)*}
@@ -504,156 +398,113 @@
 
 impl Typed for ArrValue {
 	const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Arr);
-}
-impl TryFrom<Val> for ArrValue {
-	type Error = LocError;
 
-	fn try_from(value: Val) -> Result<Self> {
-		<Self as Typed>::TYPE.check(&value)?;
+	fn into_untyped(value: Self, _: State) -> Result<Val> {
+		Ok(Val::Arr(value))
+	}
+
+	fn from_untyped(value: Val, s: State) -> Result<Self> {
+		<Self as Typed>::TYPE.check(s, &value)?;
 		match value {
 			Val::Arr(a) => Ok(a),
 			_ => unreachable!(),
 		}
 	}
 }
-impl TryFrom<ArrValue> for Val {
-	type Error = LocError;
 
-	fn try_from(value: ArrValue) -> Result<Self> {
-		Ok(Self::Arr(value))
-	}
-}
-
 impl Typed for FuncVal {
 	const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Func);
-}
-impl TryFrom<Val> for FuncVal {
-	type Error = LocError;
 
-	fn try_from(value: Val) -> Result<Self> {
-		<Self as Typed>::TYPE.check(&value)?;
+	fn into_untyped(value: Self, _: State) -> Result<Val> {
+		Ok(Val::Func(value))
+	}
+
+	fn from_untyped(value: Val, s: State) -> Result<Self> {
+		<Self as Typed>::TYPE.check(s, &value)?;
 		match value {
 			Val::Func(a) => Ok(a),
 			_ => unreachable!(),
 		}
 	}
 }
-impl TryFrom<FuncVal> for Val {
-	type Error = LocError;
 
-	fn try_from(value: FuncVal) -> Result<Self> {
-		Ok(Self::Func(value))
-	}
-}
-
 impl Typed for Cc<FuncDesc> {
 	const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Func);
-}
-impl TryFrom<Val> for Cc<FuncDesc> {
-	type Error = LocError;
 
-	fn try_from(value: Val) -> Result<Self, Self::Error> {
-		<Self as Typed>::TYPE.check(&value)?;
+	fn into_untyped(value: Self, _: State) -> Result<Val> {
+		Ok(Val::Func(FuncVal::Normal(value)))
+	}
+
+	fn from_untyped(value: Val, s: State) -> Result<Self> {
+		<Self as Typed>::TYPE.check(s, &value)?;
 		match value {
 			Val::Func(FuncVal::Normal(desc)) => Ok(desc),
 			Val::Func(_) => throw!(RuntimeError("expected normal function, not builtin".into())),
 			_ => unreachable!(),
 		}
-	}
-}
-impl TryFrom<Cc<FuncDesc>> for Val {
-	type Error = LocError;
-
-	fn try_from(value: Cc<FuncDesc>) -> Result<Self, Self::Error> {
-		Ok(Self::Func(FuncVal::Normal(value)))
 	}
 }
 
 impl Typed for ObjValue {
 	const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Obj);
-}
-impl TryFrom<Val> for ObjValue {
-	type Error = LocError;
 
-	fn try_from(value: Val) -> Result<Self> {
-		<Self as Typed>::TYPE.check(&value)?;
+	fn into_untyped(value: Self, _: State) -> Result<Val> {
+		Ok(Val::Obj(value))
+	}
+
+	fn from_untyped(value: Val, s: State) -> Result<Self> {
+		<Self as Typed>::TYPE.check(s, &value)?;
 		match value {
 			Val::Obj(a) => Ok(a),
 			_ => unreachable!(),
 		}
 	}
 }
-impl TryFrom<ObjValue> for Val {
-	type Error = LocError;
 
-	fn try_from(value: ObjValue) -> Result<Self> {
-		Ok(Self::Obj(value))
-	}
-}
-
 impl Typed for bool {
 	const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Bool);
-}
-impl TryFrom<Val> for bool {
-	type Error = LocError;
 
-	fn try_from(value: Val) -> Result<Self> {
-		<Self as Typed>::TYPE.check(&value)?;
+	fn into_untyped(value: Self, _: State) -> Result<Val> {
+		Ok(Val::Bool(value))
+	}
+
+	fn from_untyped(value: Val, s: State) -> Result<Self> {
+		<Self as Typed>::TYPE.check(s, &value)?;
 		match value {
 			Val::Bool(a) => Ok(a),
 			_ => unreachable!(),
 		}
 	}
 }
-impl TryFrom<bool> for Val {
-	type Error = LocError;
-
-	fn try_from(value: bool) -> Result<Self> {
-		Ok(Self::Bool(value))
-	}
-}
-
 impl Typed for IndexableVal {
 	const TYPE: &'static ComplexValType = &ComplexValType::UnionRef(&[
 		&ComplexValType::Simple(ValType::Arr),
 		&ComplexValType::Simple(ValType::Str),
 	]);
-}
-impl TryFrom<Val> for IndexableVal {
-	type Error = LocError;
 
-	fn try_from(value: Val) -> Result<Self> {
-		<Self as Typed>::TYPE.check(&value)?;
-		value.into_indexable()
+	fn into_untyped(value: Self, _: State) -> Result<Val> {
+		match value {
+			IndexableVal::Str(s) => Ok(Val::Str(s)),
+			IndexableVal::Arr(a) => Ok(Val::Arr(a)),
+		}
 	}
-}
-impl TryFrom<IndexableVal> for Val {
-	type Error = LocError;
 
-	fn try_from(value: IndexableVal) -> Result<Self> {
-		match value {
-			IndexableVal::Str(s) => Ok(Self::Str(s)),
-			IndexableVal::Arr(a) => Ok(Self::Arr(a)),
-		}
+	fn from_untyped(value: Val, s: State) -> Result<Self> {
+		<Self as Typed>::TYPE.check(s, &value)?;
+		value.into_indexable()
 	}
 }
 
 pub struct Null;
 impl Typed for Null {
 	const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Null);
-}
-impl TryFrom<Val> for Null {
-	type Error = LocError;
 
-	fn try_from(value: Val) -> Result<Self> {
-		<Self as Typed>::TYPE.check(&value)?;
-		Ok(Self)
+	fn into_untyped(_: Self, _: State) -> Result<Val> {
+		Ok(Val::Null)
 	}
-}
-impl TryFrom<Null> for Val {
-	type Error = LocError;
 
-	fn try_from(_: Null) -> Result<Self> {
-		Ok(Self::Null)
+	fn from_untyped(value: Val, s: State) -> Result<Self> {
+		<Self as Typed>::TYPE.check(s, &value)?;
+		Ok(Self)
 	}
 }
modifiedcrates/jrsonnet-evaluator/src/typed/mod.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/typed/mod.rs
+++ b/crates/jrsonnet-evaluator/src/typed/mod.rs
@@ -8,7 +8,7 @@
 
 use crate::{
 	error::{Error, LocError, Result},
-	push_description_frame, Val,
+	State, Val,
 };
 
 #[derive(Debug, Error, Clone, Trace)]
@@ -85,11 +85,12 @@
 }
 
 fn push_type_description(
+	s: State,
 	error_reason: impl Fn() -> String,
 	path: impl Fn() -> ValuePathItem,
 	item: impl Fn() -> Result<()>,
 ) -> Result<()> {
-	push_description_frame(error_reason, || match item() {
+	s.push_description(error_reason, || match item() {
 		Ok(_) => Ok(()),
 		Err(mut e) => {
 			if let Error::TypeError(e) = &mut e.error_mut() {
@@ -102,11 +103,11 @@
 
 // TODO: check_fast for fast path of union type checking
 pub trait CheckType {
-	fn check(&self, value: &Val) -> Result<()>;
+	fn check(&self, s: State, value: &Val) -> Result<()>;
 }
 
 impl CheckType for ValType {
-	fn check(&self, value: &Val) -> Result<()> {
+	fn check(&self, _: State, value: &Val) -> Result<()> {
 		let got = value.value_type();
 		if got != *self {
 			let loc_error: TypeLocError = TypeError::ExpectedGot((*self).into(), got).into();
@@ -144,10 +145,10 @@
 }
 
 impl CheckType for ComplexValType {
-	fn check(&self, value: &Val) -> Result<()> {
+	fn check(&self, s: State, value: &Val) -> Result<()> {
 		match self {
 			Self::Any => Ok(()),
-			Self::Simple(s) => s.check(value),
+			Self::Simple(t) => t.check(s, value),
 			Self::Char => match value {
 				Val::Str(s) if s.len() == 1 || s.chars().count() == 1 => Ok(()),
 				v => Err(TypeError::ExpectedGot(self.clone(), v.value_type()).into()),
@@ -166,11 +167,12 @@
 			}
 			Self::Array(elem_type) => match value {
 				Val::Arr(a) => {
-					for (i, item) in a.iter().enumerate() {
+					for (i, item) in a.iter(s.clone()).enumerate() {
 						push_type_description(
+							s.clone(),
 							|| format!("array index {}", i),
 							|| ValuePathItem::Index(i as u64),
-							|| elem_type.check(&item.clone()?),
+							|| elem_type.check(s.clone(), &item.clone()?),
 						)?;
 					}
 					Ok(())
@@ -179,11 +181,12 @@
 			},
 			Self::ArrayRef(elem_type) => match value {
 				Val::Arr(a) => {
-					for (i, item) in a.iter().enumerate() {
+					for (i, item) in a.iter(s.clone()).enumerate() {
 						push_type_description(
+							s.clone(),
 							|| format!("array index {}", i),
 							|| ValuePathItem::Index(i as u64),
-							|| elem_type.check(&item.clone()?),
+							|| elem_type.check(s.clone(), &item.clone()?),
 						)?;
 					}
 					Ok(())
@@ -193,11 +196,12 @@
 			Self::ObjectRef(elems) => match value {
 				Val::Obj(obj) => {
 					for (k, v) in elems.iter() {
-						if let Some(got_v) = obj.get((*k).into())? {
+						if let Some(got_v) = obj.get(s.clone(), (*k).into())? {
 							push_type_description(
+								s.clone(),
 								|| format!("property {}", k),
 								|| ValuePathItem::Field((*k).into()),
-								|| v.check(&got_v),
+								|| v.check(s.clone(), &got_v),
 							)?
 						} else {
 							return Err(
@@ -212,7 +216,7 @@
 			Self::Union(types) => {
 				let mut errors = Vec::new();
 				for ty in types.iter() {
-					match ty.check(value) {
+					match ty.check(s.clone(), value) {
 						Ok(()) => {
 							return Ok(());
 						}
@@ -227,7 +231,7 @@
 			Self::UnionRef(types) => {
 				let mut errors = Vec::new();
 				for ty in types.iter() {
-					match ty.check(value) {
+					match ty.check(s.clone(), value) {
 						Ok(()) => {
 							return Ok(());
 						}
@@ -241,13 +245,13 @@
 			}
 			Self::Sum(types) => {
 				for ty in types.iter() {
-					ty.check(value)?
+					ty.check(s.clone(), value)?
 				}
 				Ok(())
 			}
 			Self::SumRef(types) => {
 				for ty in types.iter() {
-					ty.check(value)?
+					ty.check(s.clone(), value)?
 				}
 				Ok(())
 			}
modifiedcrates/jrsonnet-evaluator/src/val.rsdiffbeforeafterboth
before · crates/jrsonnet-evaluator/src/val.rs
1use std::{cell::RefCell, fmt::Debug, rc::Rc};23use gcmodule::{Cc, Trace};4use jrsonnet_interner::IStr;5use jrsonnet_parser::{LocExpr, ParamsDesc};6use jrsonnet_types::ValType;78use crate::{9	builtin::manifest::{10		manifest_json_ex, manifest_yaml_ex, ManifestJsonOptions, ManifestType, ManifestYamlOptions,11	},12	cc_ptr_eq,13	error::{Error::*, LocError},14	evaluate,15	function::{16		parse_default_function_call, parse_function_call, ArgsLike, Builtin, CallLocation,17		StaticBuiltin,18	},19	gc::TraceBox,20	throw, Context, ObjValue, Result,21};2223pub trait LazyValValue: Trace {24	fn get(self: Box<Self>) -> Result<Val>;25}2627#[derive(Trace)]28enum LazyValInternals {29	Computed(Val),30	Errored(LocError),31	Waiting(TraceBox<dyn LazyValValue>),32	Pending,33}3435#[derive(Clone, Trace)]36pub struct LazyVal(Cc<RefCell<LazyValInternals>>);37impl LazyVal {38	pub fn new(f: TraceBox<dyn LazyValValue>) -> Self {39		Self(Cc::new(RefCell::new(LazyValInternals::Waiting(f))))40	}41	pub fn new_resolved(val: Val) -> Self {42		Self(Cc::new(RefCell::new(LazyValInternals::Computed(val))))43	}44	pub fn force(&self) -> Result<()> {45		self.evaluate()?;46		Ok(())47	}48	pub fn evaluate(&self) -> Result<Val> {49		match &*self.0.borrow() {50			LazyValInternals::Computed(v) => return Ok(v.clone()),51			LazyValInternals::Errored(e) => return Err(e.clone()),52			LazyValInternals::Pending => return Err(InfiniteRecursionDetected.into()),53			_ => (),54		};55		let value = if let LazyValInternals::Waiting(value) =56			std::mem::replace(&mut *self.0.borrow_mut(), LazyValInternals::Pending)57		{58			value59		} else {60			unreachable!()61		};62		let new_value = match value.0.get() {63			Ok(v) => v,64			Err(e) => {65				*self.0.borrow_mut() = LazyValInternals::Errored(e.clone());66				return Err(e);67			}68		};69		*self.0.borrow_mut() = LazyValInternals::Computed(new_value.clone());70		Ok(new_value)71	}72}7374impl Debug for LazyVal {75	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {76		write!(f, "Lazy")77	}78}79impl PartialEq for LazyVal {80	fn eq(&self, other: &Self) -> bool {81		cc_ptr_eq(&self.0, &other.0)82	}83}8485#[derive(Debug, PartialEq, Trace)]86pub struct FuncDesc {87	pub name: IStr,88	pub ctx: Context,89	pub params: ParamsDesc,90	pub body: LocExpr,91}92impl FuncDesc {93	/// Create body context, but fill arguments without defaults with lazy error94	pub fn default_body_context(&self) -> Context {95		parse_default_function_call(self.ctx.clone(), &self.params)96	}9798	/// Create context, with which body code will run99	pub fn call_body_context(100		&self,101		call_ctx: Context,102		args: &dyn ArgsLike,103		tailstrict: bool,104	) -> Result<Context> {105		parse_function_call(call_ctx, self.ctx.clone(), &self.params, args, tailstrict)106	}107}108109#[derive(Trace, Clone)]110pub enum FuncVal {111	/// Plain function implemented in jsonnet112	Normal(Cc<FuncDesc>),113	/// Standard library function114	StaticBuiltin(#[skip_trace] &'static dyn StaticBuiltin),115	/// User-provided function116	Builtin(Cc<TraceBox<dyn Builtin>>),117}118119impl Debug for FuncVal {120	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {121		match self {122			Self::Normal(arg0) => f.debug_tuple("Normal").field(arg0).finish(),123			Self::StaticBuiltin(arg0) => {124				f.debug_tuple("StaticBuiltin").field(&arg0.name()).finish()125			}126			Self::Builtin(arg0) => f.debug_tuple("Builtin").field(&arg0.name()).finish(),127		}128	}129}130131impl FuncVal {132	pub fn args_len(&self) -> usize {133		match self {134			Self::Normal(n) => n.params.iter().filter(|p| p.1.is_none()).count(),135			Self::StaticBuiltin(i) => i.params().iter().filter(|p| !p.has_default).count(),136			Self::Builtin(i) => i.params().iter().filter(|p| !p.has_default).count(),137		}138	}139	pub fn name(&self) -> IStr {140		match self {141			Self::Normal(normal) => normal.name.clone(),142			Self::StaticBuiltin(builtin) => builtin.name().into(),143			Self::Builtin(builtin) => builtin.name().into(),144		}145	}146	pub fn evaluate(147		&self,148		call_ctx: Context,149		loc: CallLocation,150		args: &dyn ArgsLike,151		tailstrict: bool,152	) -> Result<Val> {153		match self {154			Self::Normal(func) => {155				let body_ctx = func.call_body_context(call_ctx, args, tailstrict)?;156				evaluate(body_ctx, &func.body)157			}158			Self::StaticBuiltin(b) => b.call(call_ctx, loc, args),159			Self::Builtin(b) => b.call(call_ctx, loc, args),160		}161	}162	pub fn evaluate_simple(&self, args: &dyn ArgsLike) -> Result<Val> {163		self.evaluate(Context::default(), CallLocation::native(), args, true)164	}165}166167#[derive(Clone)]168pub enum ManifestFormat {169	YamlStream(Box<ManifestFormat>),170	Yaml {171		padding: usize,172		#[cfg(feature = "exp-preserve-order")]173		preserve_order: bool,174	},175	Json {176		padding: usize,177		#[cfg(feature = "exp-preserve-order")]178		preserve_order: bool,179	},180	ToString,181	String,182}183impl ManifestFormat {184	#[cfg(feature = "exp-preserve-order")]185	fn preserve_order(&self) -> bool {186		match self {187			ManifestFormat::YamlStream(s) => s.preserve_order(),188			ManifestFormat::Yaml { preserve_order, .. } => *preserve_order,189			ManifestFormat::Json { preserve_order, .. } => *preserve_order,190			ManifestFormat::ToString => false,191			ManifestFormat::String => false,192		}193	}194}195196#[derive(Debug, Clone, Trace)]197pub struct Slice {198	pub(crate) inner: ArrValue,199	pub(crate) from: u32,200	pub(crate) to: u32,201	pub(crate) step: u32,202}203impl Slice {204	const fn from(&self) -> usize {205		self.from as usize206	}207	const fn to(&self) -> usize {208		self.to as usize209	}210	const fn step(&self) -> usize {211		self.step as usize212	}213	const fn len(&self) -> usize {214		// TODO: use div_ceil215		let diff = self.to() - self.from();216		let rem = diff % self.step();217		let div = diff / self.step();218219		if rem != 0 {220			div + 1221		} else {222			div223		}224	}225}226227#[derive(Debug, Clone, Trace)]228#[force_tracking]229pub enum ArrValue {230	Bytes(#[skip_trace] Rc<[u8]>),231	Lazy(Cc<Vec<LazyVal>>),232	Eager(Cc<Vec<Val>>),233	Extended(Box<(Self, Self)>),234	Range(i32, i32),235	Slice(Box<Slice>),236	Reversed(Box<Self>),237}238impl ArrValue {239	pub fn new_eager() -> Self {240		Self::Eager(Cc::new(Vec::new()))241	}242	pub fn new_range(a: i32, b: i32) -> Self {243		assert!(a <= b);244		Self::Range(a, b)245	}246247	pub fn slice(self, from: Option<usize>, to: Option<usize>, step: Option<usize>) -> Self {248		let len = self.len();249		let from = from.unwrap_or(0);250		let to = to.unwrap_or(len).min(len);251		let step = step.unwrap_or(1);252		assert!(from < to);253		assert!(step > 0);254255		Self::Slice(Box::new(Slice {256			inner: self,257			from: from as u32,258			to: to as u32,259			step: step as u32,260		}))261	}262263	pub fn len(&self) -> usize {264		match self {265			Self::Bytes(i) => i.len(),266			Self::Lazy(l) => l.len(),267			Self::Eager(e) => e.len(),268			Self::Extended(v) => v.0.len() + v.1.len(),269			Self::Range(a, b) => a.abs_diff(*b) as usize + 1,270			Self::Reversed(i) => i.len(),271			Self::Slice(s) => s.len(),272		}273	}274275	pub fn is_empty(&self) -> bool {276		self.len() == 0277	}278279	pub fn get(&self, index: usize) -> Result<Option<Val>> {280		match self {281			Self::Bytes(i) => i282				.get(index)283				.map_or(Ok(None), |v| Ok(Some(Val::Num(*v as f64)))),284			Self::Lazy(vec) => {285				if let Some(v) = vec.get(index) {286					Ok(Some(v.evaluate()?))287				} else {288					Ok(None)289				}290			}291			Self::Eager(vec) => Ok(vec.get(index).cloned()),292			Self::Extended(v) => {293				let a_len = v.0.len();294				if a_len > index {295					v.0.get(index)296				} else {297					v.1.get(index - a_len)298				}299			}300			Self::Range(a, _) => {301				if index >= self.len() {302					return Ok(None);303				}304				Ok(Some(Val::Num(((*a as isize) + index as isize) as f64)))305			}306			Self::Reversed(v) => {307				let len = v.len();308				if index >= len {309					return Ok(None);310				}311				v.get(len - index - 1)312			}313			Self::Slice(s) => {314				let index = s.from() + index * s.step();315				if index >= s.to() {316					return Ok(None);317				}318				s.inner.get(index as usize)319			}320		}321	}322323	pub fn get_lazy(&self, index: usize) -> Option<LazyVal> {324		match self {325			Self::Bytes(i) => i326				.get(index)327				.map(|b| LazyVal::new_resolved(Val::Num(*b as f64))),328			Self::Lazy(vec) => vec.get(index).cloned(),329			Self::Eager(vec) => vec.get(index).cloned().map(LazyVal::new_resolved),330			Self::Extended(v) => {331				let a_len = v.0.len();332				if a_len > index {333					v.0.get_lazy(index)334				} else {335					v.1.get_lazy(index - a_len)336				}337			}338			Self::Range(a, _) => {339				if index >= self.len() {340					return None;341				}342				Some(LazyVal::new_resolved(Val::Num(343					((*a as isize) + index as isize) as f64,344				)))345			}346			Self::Reversed(v) => {347				let len = v.len();348				if index >= len {349					return None;350				}351				v.get_lazy(len - index - 1)352			}353			Self::Slice(s) => {354				let index = s.from() + index * s.step();355				if index >= s.to() {356					return None;357				}358				s.inner.get_lazy(index as usize)359			}360		}361	}362363	pub fn evaluated(&self) -> Result<Cc<Vec<Val>>> {364		Ok(match self {365			Self::Bytes(i) => {366				let mut out = Vec::with_capacity(i.len());367				for v in i.iter() {368					out.push(Val::Num(*v as f64));369				}370				Cc::new(out)371			}372			Self::Lazy(vec) => {373				let mut out = Vec::with_capacity(vec.len());374				for item in vec.iter() {375					out.push(item.evaluate()?);376				}377				Cc::new(out)378			}379			Self::Eager(vec) => vec.clone(),380			Self::Extended(_v) => {381				let mut out = Vec::with_capacity(self.len());382				for item in self.iter() {383					out.push(item?);384				}385				Cc::new(out)386			}387			Self::Range(a, b) => {388				let mut out = Vec::with_capacity(self.len());389				for i in *a..*b {390					out.push(Val::Num(i as f64));391				}392				Cc::new(out)393			}394			Self::Reversed(r) => {395				let mut r = r.evaluated()?;396				Cc::update_with(&mut r, |v| v.reverse());397				r398			}399			Self::Slice(v) => {400				let mut out = Vec::with_capacity(v.inner.len());401				for v in v402					.inner403					.iter_lazy()404					.skip(v.from())405					.take(v.to() - v.from())406					.step_by(v.step())407				{408					out.push(v.evaluate()?)409				}410				Cc::new(out)411			}412		})413	}414415	pub fn iter(&self) -> impl DoubleEndedIterator<Item = Result<Val>> + '_ {416		(0..self.len()).map(move |idx| match self {417			Self::Bytes(b) => Ok(Val::Num(b[idx] as f64)),418			Self::Lazy(l) => l[idx].evaluate(),419			Self::Eager(e) => Ok(e[idx].clone()),420			Self::Extended(_) => self.get(idx).map(|e| e.unwrap()),421			Self::Range(..) => self.get(idx).map(|e| e.unwrap()),422			Self::Reversed(..) => self.get(idx).map(|e| e.unwrap()),423			Self::Slice(..) => self.get(idx).map(|e| e.unwrap()),424		})425	}426427	pub fn iter_lazy(&self) -> impl DoubleEndedIterator<Item = LazyVal> + '_ {428		(0..self.len()).map(move |idx| match self {429			Self::Bytes(b) => LazyVal::new_resolved(Val::Num(b[idx] as f64)),430			Self::Lazy(l) => l[idx].clone(),431			Self::Eager(e) => LazyVal::new_resolved(e[idx].clone()),432			Self::Extended(_) => self.get_lazy(idx).unwrap(),433			Self::Range(..) => self.get_lazy(idx).unwrap(),434			Self::Reversed(..) => self.get_lazy(idx).unwrap(),435			Self::Slice(..) => self.get_lazy(idx).unwrap(),436		})437	}438439	pub fn reversed(self) -> Self {440		Self::Reversed(Box::new(self))441	}442443	pub fn map(self, mapper: impl Fn(Val) -> Result<Val>) -> Result<Self> {444		let mut out = Vec::with_capacity(self.len());445446		for value in self.iter() {447			out.push(mapper(value?)?);448		}449450		Ok(Self::Eager(Cc::new(out)))451	}452453	pub fn filter(self, filter: impl Fn(&Val) -> Result<bool>) -> Result<Self> {454		let mut out = Vec::with_capacity(self.len());455456		for value in self.iter() {457			let value = value?;458			if filter(&value)? {459				out.push(value);460			}461		}462463		Ok(Self::Eager(Cc::new(out)))464	}465466	pub fn ptr_eq(a: &Self, b: &Self) -> bool {467		match (a, b) {468			(Self::Lazy(a), Self::Lazy(b)) => cc_ptr_eq(a, b),469			(Self::Eager(a), Self::Eager(b)) => cc_ptr_eq(a, b),470			_ => false,471		}472	}473}474475impl From<Vec<LazyVal>> for ArrValue {476	fn from(v: Vec<LazyVal>) -> Self {477		Self::Lazy(Cc::new(v))478	}479}480481impl From<Vec<Val>> for ArrValue {482	fn from(v: Vec<Val>) -> Self {483		Self::Eager(Cc::new(v))484	}485}486487pub enum IndexableVal {488	Str(IStr),489	Arr(ArrValue),490}491492#[derive(Debug, Clone, Trace)]493pub enum Val {494	Bool(bool),495	Null,496	Str(IStr),497	Num(f64),498	Arr(ArrValue),499	Obj(ObjValue),500	Func(FuncVal),501}502503impl Val {504	pub const fn as_bool(&self) -> Option<bool> {505		match self {506			Val::Bool(v) => Some(*v),507			_ => None,508		}509	}510	pub const fn as_null(&self) -> Option<()> {511		match self {512			Val::Null => Some(()),513			_ => None,514		}515	}516	pub fn as_str(&self) -> Option<IStr> {517		match self {518			Val::Str(s) => Some(s.clone()),519			_ => None,520		}521	}522	pub const fn as_num(&self) -> Option<f64> {523		match self {524			Val::Num(n) => Some(*n),525			_ => None,526		}527	}528	pub fn as_arr(&self) -> Option<ArrValue> {529		match self {530			Val::Arr(a) => Some(a.clone()),531			_ => None,532		}533	}534	pub fn as_obj(&self) -> Option<ObjValue> {535		match self {536			Val::Obj(o) => Some(o.clone()),537			_ => None,538		}539	}540	pub fn as_func(&self) -> Option<FuncVal> {541		match self {542			Val::Func(f) => Some(f.clone()),543			_ => None,544		}545	}546547	/// Creates `Val::Num` after checking for numeric overflow.548	/// As numbers are `f64`, we can just check for their finity.549	pub fn new_checked_num(num: f64) -> Result<Self> {550		if num.is_finite() {551			Ok(Self::Num(num))552		} else {553			throw!(RuntimeError("overflow".into()))554		}555	}556557	pub const fn value_type(&self) -> ValType {558		match self {559			Self::Str(..) => ValType::Str,560			Self::Num(..) => ValType::Num,561			Self::Arr(..) => ValType::Arr,562			Self::Obj(..) => ValType::Obj,563			Self::Bool(_) => ValType::Bool,564			Self::Null => ValType::Null,565			Self::Func(..) => ValType::Func,566		}567	}568569	pub fn to_string(&self) -> Result<IStr> {570		Ok(match self {571			Self::Bool(true) => "true".into(),572			Self::Bool(false) => "false".into(),573			Self::Null => "null".into(),574			Self::Str(s) => s.clone(),575			v => manifest_json_ex(576				v,577				&ManifestJsonOptions {578					padding: "",579					mtype: ManifestType::ToString,580					newline: "\n",581					key_val_sep: ": ",582					#[cfg(feature = "exp-preserve-order")]583					preserve_order: false,584				},585			)?586			.into(),587		})588	}589590	/// Expects value to be object, outputs (key, manifested value) pairs591	pub fn manifest_multi(&self, ty: &ManifestFormat) -> Result<Vec<(IStr, IStr)>> {592		let obj = match self {593			Self::Obj(obj) => obj,594			_ => throw!(MultiManifestOutputIsNotAObject),595		};596		let keys = obj.fields(597			#[cfg(feature = "exp-preserve-order")]598			ty.preserve_order(),599		);600		let mut out = Vec::with_capacity(keys.len());601		for key in keys {602			let value = obj603				.get(key.clone())?604				.expect("item in object")605				.manifest(ty)?;606			out.push((key, value));607		}608		Ok(out)609	}610611	/// Expects value to be array, outputs manifested values612	pub fn manifest_stream(&self, ty: &ManifestFormat) -> Result<Vec<IStr>> {613		let arr = match self {614			Self::Arr(a) => a,615			_ => throw!(StreamManifestOutputIsNotAArray),616		};617		let mut out = Vec::with_capacity(arr.len());618		for i in arr.iter() {619			out.push(i?.manifest(ty)?);620		}621		Ok(out)622	}623624	pub fn manifest(&self, ty: &ManifestFormat) -> Result<IStr> {625		Ok(match ty {626			ManifestFormat::YamlStream(format) => {627				let arr = match self {628					Self::Arr(a) => a,629					_ => throw!(StreamManifestOutputIsNotAArray),630				};631				let mut out = String::new();632633				match format as &ManifestFormat {634					ManifestFormat::YamlStream(_) => throw!(StreamManifestOutputCannotBeRecursed),635					ManifestFormat::String => throw!(StreamManifestCannotNestString),636					_ => {}637				};638639				if !arr.is_empty() {640					for v in arr.iter() {641						out.push_str("---\n");642						out.push_str(&v?.manifest(format)?);643						out.push('\n');644					}645					out.push_str("...");646				}647648				out.into()649			}650			ManifestFormat::Yaml {651				padding,652				#[cfg(feature = "exp-preserve-order")]653				preserve_order,654			} => self.to_yaml(655				*padding,656				#[cfg(feature = "exp-preserve-order")]657				*preserve_order,658			)?,659			ManifestFormat::Json {660				padding,661				#[cfg(feature = "exp-preserve-order")]662				preserve_order,663			} => self.to_json(664				*padding,665				#[cfg(feature = "exp-preserve-order")]666				*preserve_order,667			)?,668			ManifestFormat::ToString => self.to_string()?,669			ManifestFormat::String => match self {670				Self::Str(s) => s.clone(),671				_ => throw!(StringManifestOutputIsNotAString),672			},673		})674	}675676	/// For manifestification677	pub fn to_json(678		&self,679		padding: usize,680		#[cfg(feature = "exp-preserve-order")] preserve_order: bool,681	) -> Result<IStr> {682		manifest_json_ex(683			self,684			&ManifestJsonOptions {685				padding: &" ".repeat(padding),686				mtype: if padding == 0 {687					ManifestType::Minify688				} else {689					ManifestType::Manifest690				},691				newline: "\n",692				key_val_sep: ": ",693				#[cfg(feature = "exp-preserve-order")]694				preserve_order,695			},696		)697		.map(|s| s.into())698	}699700	/// Calls `std.manifestJson`701	pub fn to_std_json(702		&self,703		padding: usize,704		#[cfg(feature = "exp-preserve-order")] preserve_order: bool,705	) -> Result<Rc<str>> {706		manifest_json_ex(707			self,708			&ManifestJsonOptions {709				padding: &" ".repeat(padding),710				mtype: ManifestType::Std,711				newline: "\n",712				key_val_sep: ": ",713				#[cfg(feature = "exp-preserve-order")]714				preserve_order,715			},716		)717		.map(|s| s.into())718	}719720	pub fn to_yaml(721		&self,722		padding: usize,723		#[cfg(feature = "exp-preserve-order")] preserve_order: bool,724	) -> Result<IStr> {725		let padding = &" ".repeat(padding);726		manifest_yaml_ex(727			self,728			&ManifestYamlOptions {729				padding,730				arr_element_padding: padding,731				quote_keys: false,732				#[cfg(feature = "exp-preserve-order")]733				preserve_order,734			},735		)736		.map(|s| s.into())737	}738	pub fn into_indexable(self) -> Result<IndexableVal> {739		Ok(match self {740			Val::Str(s) => IndexableVal::Str(s),741			Val::Arr(arr) => IndexableVal::Arr(arr),742			_ => throw!(ValueIsNotIndexable(self.value_type())),743		})744	}745}746747const fn is_function_like(val: &Val) -> bool {748	matches!(val, Val::Func(_))749}750751/// Native implementation of `std.primitiveEquals`752pub fn primitive_equals(val_a: &Val, val_b: &Val) -> Result<bool> {753	Ok(match (val_a, val_b) {754		(Val::Bool(a), Val::Bool(b)) => a == b,755		(Val::Null, Val::Null) => true,756		(Val::Str(a), Val::Str(b)) => a == b,757		(Val::Num(a), Val::Num(b)) => (a - b).abs() <= f64::EPSILON,758		(Val::Arr(_), Val::Arr(_)) => throw!(RuntimeError(759			"primitiveEquals operates on primitive types, got array".into(),760		)),761		(Val::Obj(_), Val::Obj(_)) => throw!(RuntimeError(762			"primitiveEquals operates on primitive types, got object".into(),763		)),764		(a, b) if is_function_like(a) && is_function_like(b) => {765			throw!(RuntimeError("cannot test equality of functions".into()))766		}767		(_, _) => false,768	})769}770771/// Native implementation of `std.equals`772pub fn equals(val_a: &Val, val_b: &Val) -> Result<bool> {773	if val_a.value_type() != val_b.value_type() {774		return Ok(false);775	}776	match (val_a, val_b) {777		(Val::Arr(a), Val::Arr(b)) => {778			if ArrValue::ptr_eq(a, b) {779				return Ok(true);780			}781			if a.len() != b.len() {782				return Ok(false);783			}784			for (a, b) in a.iter().zip(b.iter()) {785				if !equals(&a?, &b?)? {786					return Ok(false);787				}788			}789			Ok(true)790		}791		(Val::Obj(a), Val::Obj(b)) => {792			if ObjValue::ptr_eq(a, b) {793				return Ok(true);794			}795			let fields = a.fields(796				#[cfg(feature = "exp-preserve-order")]797				false,798			);799			if fields800				!= b.fields(801					#[cfg(feature = "exp-preserve-order")]802					false,803				) {804				return Ok(false);805			}806			for field in fields {807				if !equals(&a.get(field.clone())?.unwrap(), &b.get(field)?.unwrap())? {808					return Ok(false);809				}810			}811			Ok(true)812		}813		(a, b) => Ok(primitive_equals(a, b)?),814	}815}
after · crates/jrsonnet-evaluator/src/val.rs
1use std::{cell::RefCell, fmt::Debug, rc::Rc};23use gcmodule::{Cc, Trace};4use jrsonnet_interner::IStr;5use jrsonnet_parser::{LocExpr, ParamsDesc};6use jrsonnet_types::ValType;78use crate::{9	builtin::manifest::{10		manifest_json_ex, manifest_yaml_ex, ManifestJsonOptions, ManifestType, ManifestYamlOptions,11	},12	cc_ptr_eq,13	error::{Error::*, LocError},14	evaluate,15	function::{16		parse_default_function_call, parse_function_call, ArgsLike, Builtin, CallLocation,17		StaticBuiltin,18	},19	gc::TraceBox,20	throw, Context, ObjValue, Result, State,21};2223pub trait LazyValValue: Trace {24	fn get(self: Box<Self>, s: State) -> Result<Val>;25}2627#[derive(Trace)]28enum LazyValInternals {29	Computed(Val),30	Errored(LocError),31	Waiting(TraceBox<dyn LazyValValue>),32	Pending,33}3435#[derive(Clone, Trace)]36pub struct LazyVal(Cc<RefCell<LazyValInternals>>);37impl LazyVal {38	pub fn new(f: TraceBox<dyn LazyValValue>) -> Self {39		Self(Cc::new(RefCell::new(LazyValInternals::Waiting(f))))40	}41	pub fn new_resolved(val: Val) -> Self {42		Self(Cc::new(RefCell::new(LazyValInternals::Computed(val))))43	}44	pub fn force(&self, s: State) -> Result<()> {45		self.evaluate(s)?;46		Ok(())47	}48	pub fn evaluate(&self, s: State) -> Result<Val> {49		match &*self.0.borrow() {50			LazyValInternals::Computed(v) => return Ok(v.clone()),51			LazyValInternals::Errored(e) => return Err(e.clone()),52			LazyValInternals::Pending => return Err(InfiniteRecursionDetected.into()),53			_ => (),54		};55		let value = if let LazyValInternals::Waiting(value) =56			std::mem::replace(&mut *self.0.borrow_mut(), LazyValInternals::Pending)57		{58			value59		} else {60			unreachable!()61		};62		let new_value = match value.0.get(s) {63			Ok(v) => v,64			Err(e) => {65				*self.0.borrow_mut() = LazyValInternals::Errored(e.clone());66				return Err(e);67			}68		};69		*self.0.borrow_mut() = LazyValInternals::Computed(new_value.clone());70		Ok(new_value)71	}72}7374impl Debug for LazyVal {75	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {76		write!(f, "Lazy")77	}78}79impl PartialEq for LazyVal {80	fn eq(&self, other: &Self) -> bool {81		cc_ptr_eq(&self.0, &other.0)82	}83}8485#[derive(Debug, PartialEq, Trace)]86pub struct FuncDesc {87	pub name: IStr,88	pub ctx: Context,89	pub params: ParamsDesc,90	pub body: LocExpr,91}92impl FuncDesc {93	/// Create body context, but fill arguments without defaults with lazy error94	pub fn default_body_context(&self) -> Context {95		parse_default_function_call(self.ctx.clone(), &self.params)96	}9798	/// Create context, with which body code will run99	pub fn call_body_context(100		&self,101		s: State,102		call_ctx: Context,103		args: &dyn ArgsLike,104		tailstrict: bool,105	) -> Result<Context> {106		parse_function_call(107			s,108			call_ctx,109			self.ctx.clone(),110			&self.params,111			args,112			tailstrict,113		)114	}115}116117#[derive(Trace, Clone)]118pub enum FuncVal {119	/// Plain function implemented in jsonnet120	Normal(Cc<FuncDesc>),121	/// Standard library function122	StaticBuiltin(#[skip_trace] &'static dyn StaticBuiltin),123	/// User-provided function124	Builtin(Cc<TraceBox<dyn Builtin>>),125}126127impl Debug for FuncVal {128	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {129		match self {130			Self::Normal(arg0) => f.debug_tuple("Normal").field(arg0).finish(),131			Self::StaticBuiltin(arg0) => {132				f.debug_tuple("StaticBuiltin").field(&arg0.name()).finish()133			}134			Self::Builtin(arg0) => f.debug_tuple("Builtin").field(&arg0.name()).finish(),135		}136	}137}138139impl FuncVal {140	pub fn args_len(&self) -> usize {141		match self {142			Self::Normal(n) => n.params.iter().filter(|p| p.1.is_none()).count(),143			Self::StaticBuiltin(i) => i.params().iter().filter(|p| !p.has_default).count(),144			Self::Builtin(i) => i.params().iter().filter(|p| !p.has_default).count(),145		}146	}147	pub fn name(&self) -> IStr {148		match self {149			Self::Normal(normal) => normal.name.clone(),150			Self::StaticBuiltin(builtin) => builtin.name().into(),151			Self::Builtin(builtin) => builtin.name().into(),152		}153	}154	pub fn evaluate(155		&self,156		s: State,157		call_ctx: Context,158		loc: CallLocation,159		args: &dyn ArgsLike,160		tailstrict: bool,161	) -> Result<Val> {162		match self {163			Self::Normal(func) => {164				let body_ctx = func.call_body_context(s.clone(), call_ctx, args, tailstrict)?;165				evaluate(s, body_ctx, &func.body)166			}167			Self::StaticBuiltin(b) => b.call(s, call_ctx, loc, args),168			Self::Builtin(b) => b.call(s, call_ctx, loc, args),169		}170	}171	pub fn evaluate_simple(&self, s: State, args: &dyn ArgsLike) -> Result<Val> {172		self.evaluate(s, Context::default(), CallLocation::native(), args, true)173	}174}175176#[derive(Clone)]177pub enum ManifestFormat {178	YamlStream(Box<ManifestFormat>),179	Yaml {180		padding: usize,181		#[cfg(feature = "exp-preserve-order")]182		preserve_order: bool,183	},184	Json {185		padding: usize,186		#[cfg(feature = "exp-preserve-order")]187		preserve_order: bool,188	},189	ToString,190	String,191}192impl ManifestFormat {193	#[cfg(feature = "exp-preserve-order")]194	fn preserve_order(&self) -> bool {195		match self {196			ManifestFormat::YamlStream(s) => s.preserve_order(),197			ManifestFormat::Yaml { preserve_order, .. } => *preserve_order,198			ManifestFormat::Json { preserve_order, .. } => *preserve_order,199			ManifestFormat::ToString => false,200			ManifestFormat::String => false,201		}202	}203}204205#[derive(Debug, Clone, Trace)]206pub struct Slice {207	pub(crate) inner: ArrValue,208	pub(crate) from: u32,209	pub(crate) to: u32,210	pub(crate) step: u32,211}212impl Slice {213	const fn from(&self) -> usize {214		self.from as usize215	}216	const fn to(&self) -> usize {217		self.to as usize218	}219	const fn step(&self) -> usize {220		self.step as usize221	}222	const fn len(&self) -> usize {223		// TODO: use div_ceil224		let diff = self.to() - self.from();225		let rem = diff % self.step();226		let div = diff / self.step();227228		if rem != 0 {229			div + 1230		} else {231			div232		}233	}234}235236#[derive(Debug, Clone, Trace)]237#[force_tracking]238pub enum ArrValue {239	Bytes(#[skip_trace] Rc<[u8]>),240	Lazy(Cc<Vec<LazyVal>>),241	Eager(Cc<Vec<Val>>),242	Extended(Box<(Self, Self)>),243	Range(i32, i32),244	Slice(Box<Slice>),245	Reversed(Box<Self>),246}247impl ArrValue {248	pub fn new_eager() -> Self {249		Self::Eager(Cc::new(Vec::new()))250	}251	pub fn new_range(a: i32, b: i32) -> Self {252		assert!(a <= b);253		Self::Range(a, b)254	}255256	pub fn slice(self, from: Option<usize>, to: Option<usize>, step: Option<usize>) -> Self {257		let len = self.len();258		let from = from.unwrap_or(0);259		let to = to.unwrap_or(len).min(len);260		let step = step.unwrap_or(1);261		assert!(from < to);262		assert!(step > 0);263264		Self::Slice(Box::new(Slice {265			inner: self,266			from: from as u32,267			to: to as u32,268			step: step as u32,269		}))270	}271272	pub fn len(&self) -> usize {273		match self {274			Self::Bytes(i) => i.len(),275			Self::Lazy(l) => l.len(),276			Self::Eager(e) => e.len(),277			Self::Extended(v) => v.0.len() + v.1.len(),278			Self::Range(a, b) => a.abs_diff(*b) as usize + 1,279			Self::Reversed(i) => i.len(),280			Self::Slice(s) => s.len(),281		}282	}283284	pub fn is_empty(&self) -> bool {285		self.len() == 0286	}287288	pub fn get(&self, s: State, index: usize) -> Result<Option<Val>> {289		match self {290			Self::Bytes(i) => i291				.get(index)292				.map_or(Ok(None), |v| Ok(Some(Val::Num(*v as f64)))),293			Self::Lazy(vec) => {294				if let Some(v) = vec.get(index) {295					Ok(Some(v.evaluate(s)?))296				} else {297					Ok(None)298				}299			}300			Self::Eager(vec) => Ok(vec.get(index).cloned()),301			Self::Extended(v) => {302				let a_len = v.0.len();303				if a_len > index {304					v.0.get(s, index)305				} else {306					v.1.get(s, index - a_len)307				}308			}309			Self::Range(a, _) => {310				if index >= self.len() {311					return Ok(None);312				}313				Ok(Some(Val::Num(((*a as isize) + index as isize) as f64)))314			}315			Self::Reversed(v) => {316				let len = v.len();317				if index >= len {318					return Ok(None);319				}320				v.get(s, len - index - 1)321			}322			Self::Slice(v) => {323				let index = v.from() + index * v.step();324				if index >= v.to() {325					return Ok(None);326				}327				v.inner.get(s, index as usize)328			}329		}330	}331332	pub fn get_lazy(&self, index: usize) -> Option<LazyVal> {333		match self {334			Self::Bytes(i) => i335				.get(index)336				.map(|b| LazyVal::new_resolved(Val::Num(*b as f64))),337			Self::Lazy(vec) => vec.get(index).cloned(),338			Self::Eager(vec) => vec.get(index).cloned().map(LazyVal::new_resolved),339			Self::Extended(v) => {340				let a_len = v.0.len();341				if a_len > index {342					v.0.get_lazy(index)343				} else {344					v.1.get_lazy(index - a_len)345				}346			}347			Self::Range(a, _) => {348				if index >= self.len() {349					return None;350				}351				Some(LazyVal::new_resolved(Val::Num(352					((*a as isize) + index as isize) as f64,353				)))354			}355			Self::Reversed(v) => {356				let len = v.len();357				if index >= len {358					return None;359				}360				v.get_lazy(len - index - 1)361			}362			Self::Slice(s) => {363				let index = s.from() + index * s.step();364				if index >= s.to() {365					return None;366				}367				s.inner.get_lazy(index as usize)368			}369		}370	}371372	pub fn evaluated(&self, s: State) -> Result<Cc<Vec<Val>>> {373		Ok(match self {374			Self::Bytes(i) => {375				let mut out = Vec::with_capacity(i.len());376				for v in i.iter() {377					out.push(Val::Num(*v as f64));378				}379				Cc::new(out)380			}381			Self::Lazy(vec) => {382				let mut out = Vec::with_capacity(vec.len());383				for item in vec.iter() {384					out.push(item.evaluate(s.clone())?);385				}386				Cc::new(out)387			}388			Self::Eager(vec) => vec.clone(),389			Self::Extended(_v) => {390				let mut out = Vec::with_capacity(self.len());391				for item in self.iter(s) {392					out.push(item?);393				}394				Cc::new(out)395			}396			Self::Range(a, b) => {397				let mut out = Vec::with_capacity(self.len());398				for i in *a..*b {399					out.push(Val::Num(i as f64));400				}401				Cc::new(out)402			}403			Self::Reversed(r) => {404				let mut r = r.evaluated(s)?;405				Cc::update_with(&mut r, |v| v.reverse());406				r407			}408			Self::Slice(v) => {409				let mut out = Vec::with_capacity(v.inner.len());410				for v in v411					.inner412					.iter_lazy()413					.skip(v.from())414					.take(v.to() - v.from())415					.step_by(v.step())416				{417					out.push(v.evaluate(s.clone())?)418				}419				Cc::new(out)420			}421		})422	}423424	pub fn iter(&self, s: State) -> impl DoubleEndedIterator<Item = Result<Val>> + '_ {425		(0..self.len()).map(move |idx| match self {426			Self::Bytes(b) => Ok(Val::Num(b[idx] as f64)),427			Self::Lazy(l) => l[idx].evaluate(s.clone()),428			Self::Eager(e) => Ok(e[idx].clone()),429			Self::Extended(_) => self.get(s.clone(), idx).map(|e| e.unwrap()),430			Self::Range(..) => self.get(s.clone(), idx).map(|e| e.unwrap()),431			Self::Reversed(..) => self.get(s.clone(), idx).map(|e| e.unwrap()),432			Self::Slice(..) => self.get(s.clone(), idx).map(|e| e.unwrap()),433		})434	}435436	pub fn iter_lazy(&self) -> impl DoubleEndedIterator<Item = LazyVal> + '_ {437		(0..self.len()).map(move |idx| match self {438			Self::Bytes(b) => LazyVal::new_resolved(Val::Num(b[idx] as f64)),439			Self::Lazy(l) => l[idx].clone(),440			Self::Eager(e) => LazyVal::new_resolved(e[idx].clone()),441			Self::Extended(_) => self.get_lazy(idx).unwrap(),442			Self::Range(..) => self.get_lazy(idx).unwrap(),443			Self::Reversed(..) => self.get_lazy(idx).unwrap(),444			Self::Slice(..) => self.get_lazy(idx).unwrap(),445		})446	}447448	pub fn reversed(self) -> Self {449		Self::Reversed(Box::new(self))450	}451452	pub fn map(self, s: State, mapper: impl Fn(Val) -> Result<Val>) -> Result<Self> {453		let mut out = Vec::with_capacity(self.len());454455		for value in self.iter(s) {456			out.push(mapper(value?)?);457		}458459		Ok(Self::Eager(Cc::new(out)))460	}461462	pub fn filter(self, s: State, filter: impl Fn(&Val) -> Result<bool>) -> Result<Self> {463		let mut out = Vec::with_capacity(self.len());464465		for value in self.iter(s) {466			let value = value?;467			if filter(&value)? {468				out.push(value);469			}470		}471472		Ok(Self::Eager(Cc::new(out)))473	}474475	pub fn ptr_eq(a: &Self, b: &Self) -> bool {476		match (a, b) {477			(Self::Lazy(a), Self::Lazy(b)) => cc_ptr_eq(a, b),478			(Self::Eager(a), Self::Eager(b)) => cc_ptr_eq(a, b),479			_ => false,480		}481	}482}483484impl From<Vec<LazyVal>> for ArrValue {485	fn from(v: Vec<LazyVal>) -> Self {486		Self::Lazy(Cc::new(v))487	}488}489490impl From<Vec<Val>> for ArrValue {491	fn from(v: Vec<Val>) -> Self {492		Self::Eager(Cc::new(v))493	}494}495496pub enum IndexableVal {497	Str(IStr),498	Arr(ArrValue),499}500501#[derive(Debug, Clone, Trace)]502pub enum Val {503	Bool(bool),504	Null,505	Str(IStr),506	Num(f64),507	Arr(ArrValue),508	Obj(ObjValue),509	Func(FuncVal),510}511512impl Val {513	pub const fn as_bool(&self) -> Option<bool> {514		match self {515			Val::Bool(v) => Some(*v),516			_ => None,517		}518	}519	pub const fn as_null(&self) -> Option<()> {520		match self {521			Val::Null => Some(()),522			_ => None,523		}524	}525	pub fn as_str(&self) -> Option<IStr> {526		match self {527			Val::Str(s) => Some(s.clone()),528			_ => None,529		}530	}531	pub const fn as_num(&self) -> Option<f64> {532		match self {533			Val::Num(n) => Some(*n),534			_ => None,535		}536	}537	pub fn as_arr(&self) -> Option<ArrValue> {538		match self {539			Val::Arr(a) => Some(a.clone()),540			_ => None,541		}542	}543	pub fn as_obj(&self) -> Option<ObjValue> {544		match self {545			Val::Obj(o) => Some(o.clone()),546			_ => None,547		}548	}549	pub fn as_func(&self) -> Option<FuncVal> {550		match self {551			Val::Func(f) => Some(f.clone()),552			_ => None,553		}554	}555556	/// Creates `Val::Num` after checking for numeric overflow.557	/// As numbers are `f64`, we can just check for their finity.558	pub fn new_checked_num(num: f64) -> Result<Self> {559		if num.is_finite() {560			Ok(Self::Num(num))561		} else {562			throw!(RuntimeError("overflow".into()))563		}564	}565566	pub const fn value_type(&self) -> ValType {567		match self {568			Self::Str(..) => ValType::Str,569			Self::Num(..) => ValType::Num,570			Self::Arr(..) => ValType::Arr,571			Self::Obj(..) => ValType::Obj,572			Self::Bool(_) => ValType::Bool,573			Self::Null => ValType::Null,574			Self::Func(..) => ValType::Func,575		}576	}577578	pub fn to_string(&self, s: State) -> Result<IStr> {579		Ok(match self {580			Self::Bool(true) => "true".into(),581			Self::Bool(false) => "false".into(),582			Self::Null => "null".into(),583			Self::Str(s) => s.clone(),584			v => manifest_json_ex(585				s,586				v,587				&ManifestJsonOptions {588					padding: "",589					mtype: ManifestType::ToString,590					newline: "\n",591					key_val_sep: ": ",592					#[cfg(feature = "exp-preserve-order")]593					preserve_order: false,594				},595			)?596			.into(),597		})598	}599600	/// Expects value to be object, outputs (key, manifested value) pairs601	pub fn manifest_multi(&self, s: State, ty: &ManifestFormat) -> Result<Vec<(IStr, IStr)>> {602		let obj = match self {603			Self::Obj(obj) => obj,604			_ => throw!(MultiManifestOutputIsNotAObject),605		};606		let keys = obj.fields(607			#[cfg(feature = "exp-preserve-order")]608			ty.preserve_order(),609		);610		let mut out = Vec::with_capacity(keys.len());611		for key in keys {612			let value = obj613				.get(s.clone(), key.clone())?614				.expect("item in object")615				.manifest(s.clone(), ty)?;616			out.push((key, value));617		}618		Ok(out)619	}620621	/// Expects value to be array, outputs manifested values622	pub fn manifest_stream(&self, s: State, ty: &ManifestFormat) -> Result<Vec<IStr>> {623		let arr = match self {624			Self::Arr(a) => a,625			_ => throw!(StreamManifestOutputIsNotAArray),626		};627		let mut out = Vec::with_capacity(arr.len());628		for i in arr.iter(s.clone()) {629			out.push(i?.manifest(s.clone(), ty)?);630		}631		Ok(out)632	}633634	pub fn manifest(&self, s: State, ty: &ManifestFormat) -> Result<IStr> {635		Ok(match ty {636			ManifestFormat::YamlStream(format) => {637				let arr = match self {638					Self::Arr(a) => a,639					_ => throw!(StreamManifestOutputIsNotAArray),640				};641				let mut out = String::new();642643				match format as &ManifestFormat {644					ManifestFormat::YamlStream(_) => throw!(StreamManifestOutputCannotBeRecursed),645					ManifestFormat::String => throw!(StreamManifestCannotNestString),646					_ => {}647				};648649				if !arr.is_empty() {650					for v in arr.iter(s.clone()) {651						out.push_str("---\n");652						out.push_str(&v?.manifest(s.clone(), format)?);653						out.push('\n');654					}655					out.push_str("...");656				}657658				out.into()659			}660			ManifestFormat::Yaml {661				padding,662				#[cfg(feature = "exp-preserve-order")]663				preserve_order,664			} => self.to_yaml(665				s,666				*padding,667				#[cfg(feature = "exp-preserve-order")]668				*preserve_order,669			)?,670			ManifestFormat::Json {671				padding,672				#[cfg(feature = "exp-preserve-order")]673				preserve_order,674			} => self.to_json(675				s,676				*padding,677				#[cfg(feature = "exp-preserve-order")]678				*preserve_order,679			)?,680			ManifestFormat::ToString => self.to_string(s)?,681			ManifestFormat::String => match self {682				Self::Str(s) => s.clone(),683				_ => throw!(StringManifestOutputIsNotAString),684			},685		})686	}687688	/// For manifestification689	pub fn to_json(690		&self,691		s: State,692		padding: usize,693		#[cfg(feature = "exp-preserve-order")] preserve_order: bool,694	) -> Result<IStr> {695		manifest_json_ex(696			s,697			self,698			&ManifestJsonOptions {699				padding: &" ".repeat(padding),700				mtype: if padding == 0 {701					ManifestType::Minify702				} else {703					ManifestType::Manifest704				},705				newline: "\n",706				key_val_sep: ": ",707				#[cfg(feature = "exp-preserve-order")]708				preserve_order,709			},710		)711		.map(|s| s.into())712	}713714	/// Calls `std.manifestJson`715	pub fn to_std_json(716		&self,717		s: State,718		padding: usize,719		#[cfg(feature = "exp-preserve-order")] preserve_order: bool,720	) -> Result<Rc<str>> {721		manifest_json_ex(722			s,723			self,724			&ManifestJsonOptions {725				padding: &" ".repeat(padding),726				mtype: ManifestType::Std,727				newline: "\n",728				key_val_sep: ": ",729				#[cfg(feature = "exp-preserve-order")]730				preserve_order,731			},732		)733		.map(|s| s.into())734	}735736	pub fn to_yaml(737		&self,738		s: State,739		padding: usize,740		#[cfg(feature = "exp-preserve-order")] preserve_order: bool,741	) -> Result<IStr> {742		let padding = &" ".repeat(padding);743		manifest_yaml_ex(744			s,745			self,746			&ManifestYamlOptions {747				padding,748				arr_element_padding: padding,749				quote_keys: false,750				#[cfg(feature = "exp-preserve-order")]751				preserve_order,752			},753		)754		.map(|s| s.into())755	}756	pub fn into_indexable(self) -> Result<IndexableVal> {757		Ok(match self {758			Val::Str(s) => IndexableVal::Str(s),759			Val::Arr(arr) => IndexableVal::Arr(arr),760			_ => throw!(ValueIsNotIndexable(self.value_type())),761		})762	}763}764765const fn is_function_like(val: &Val) -> bool {766	matches!(val, Val::Func(_))767}768769/// Native implementation of `std.primitiveEquals`770pub fn primitive_equals(val_a: &Val, val_b: &Val) -> Result<bool> {771	Ok(match (val_a, val_b) {772		(Val::Bool(a), Val::Bool(b)) => a == b,773		(Val::Null, Val::Null) => true,774		(Val::Str(a), Val::Str(b)) => a == b,775		(Val::Num(a), Val::Num(b)) => (a - b).abs() <= f64::EPSILON,776		(Val::Arr(_), Val::Arr(_)) => throw!(RuntimeError(777			"primitiveEquals operates on primitive types, got array".into(),778		)),779		(Val::Obj(_), Val::Obj(_)) => throw!(RuntimeError(780			"primitiveEquals operates on primitive types, got object".into(),781		)),782		(a, b) if is_function_like(a) && is_function_like(b) => {783			throw!(RuntimeError("cannot test equality of functions".into()))784		}785		(_, _) => false,786	})787}788789/// Native implementation of `std.equals`790pub fn equals(s: State, val_a: &Val, val_b: &Val) -> Result<bool> {791	if val_a.value_type() != val_b.value_type() {792		return Ok(false);793	}794	match (val_a, val_b) {795		(Val::Arr(a), Val::Arr(b)) => {796			if ArrValue::ptr_eq(a, b) {797				return Ok(true);798			}799			if a.len() != b.len() {800				return Ok(false);801			}802			for (a, b) in a.iter(s.clone()).zip(b.iter(s.clone())) {803				if !equals(s.clone(), &a?, &b?)? {804					return Ok(false);805				}806			}807			Ok(true)808		}809		(Val::Obj(a), Val::Obj(b)) => {810			if ObjValue::ptr_eq(a, b) {811				return Ok(true);812			}813			let fields = a.fields(814				#[cfg(feature = "exp-preserve-order")]815				false,816			);817			if fields818				!= b.fields(819					#[cfg(feature = "exp-preserve-order")]820					false,821				) {822				return Ok(false);823			}824			for field in fields {825				if !equals(826					s.clone(),827					&a.get(s.clone(), field.clone())?.unwrap(),828					&b.get(s.clone(), field)?.unwrap(),829				)? {830					return Ok(false);831				}832			}833			Ok(true)834		}835		(a, b) => Ok(primitive_equals(a, b)?),836	}837}
modifiedcrates/jrsonnet-macros/src/lib.rsdiffbeforeafterboth
--- a/crates/jrsonnet-macros/src/lib.rs
+++ b/crates/jrsonnet-macros/src/lib.rs
@@ -129,6 +129,7 @@
 		is_option: bool,
 		name: String,
 	},
+	State,
 	Location,
 	This,
 }
@@ -144,7 +145,9 @@
 			_ => return Err(Error::new(arg.pat.span(), "arg should be plain identifier")),
 		};
 		let ty = &arg.ty;
-		if type_is_path(ty, "CallLocation").is_some() {
+		if type_is_path(ty, "State").is_some() {
+			return Ok(Self::State);
+		} else if type_is_path(ty, "CallLocation").is_some() {
 			return Ok(Self::Location);
 		} else if type_is_path(ty, "Self").is_some() {
 			return Ok(Self::This);
@@ -208,6 +211,24 @@
 		}
 		ReturnType::Type(_, ref ty) => ty.clone(),
 	};
+	let result_inner = if let Some(args) = type_is_path(&result, "Result") {
+		let generic_arg = match args {
+			PathArguments::AngleBracketed(params) => params.args.iter().next().unwrap(),
+			_ => return Err(Error::new(args.span(), "missing result generic")),
+		};
+		// This argument must be a type:
+		match generic_arg {
+			GenericArgument::Type(ty) => ty,
+			_ => {
+				return Err(Error::new(
+					generic_arg.span(),
+					"option generic should be a type",
+				))
+			}
+		}
+	} else {
+		return Err(Error::new(result.span(), "return value should be result"));
+	};
 
 	let args = fun
 		.sig
@@ -235,6 +256,7 @@
 				has_default: #is_option,
 			},
 		}),
+		ArgInfo::State => None,
 		ArgInfo::Location => None,
 		ArgInfo::This => None,
 	});
@@ -246,9 +268,9 @@
 			name,
 			cfg_attrs,
 		} => {
-			let eval = quote! {::jrsonnet_evaluator::push_description_frame(
+			let eval = quote! {s.push_description(
 				|| format!("argument <{}> evaluation", #name),
-				|| <#ty>::try_from(value.evaluate()?),
+				|| <#ty>::from_untyped(value.evaluate(s.clone())?, s.clone()),
 			)?};
 			let value = if *is_option {
 				quote! {if let Some(value) = parsed.get(#name) {
@@ -280,6 +302,7 @@
 				}
 			}
 		}
+		ArgInfo::State => quote! {s.clone(),},
 		ArgInfo::Location => quote! {location,},
 		ArgInfo::This => quote! {self,},
 	});
@@ -320,6 +343,7 @@
 		}
 		const _: () = {
 			use ::jrsonnet_evaluator::{
+				State,
 				function::{Builtin, CallLocation, StaticBuiltin, BuiltinParam, ArgsLike, parse_builtin_call},
 				error::Result, Context,
 				parser::ExprLocation,
@@ -339,12 +363,12 @@
 				fn params(&self) -> &[BuiltinParam] {
 					PARAMS
 				}
-				fn call(&self, context: Context, location: CallLocation, args: &dyn ArgsLike) -> Result<Val> {
-					let parsed = parse_builtin_call(context, &PARAMS, args, false)?;
+				fn call(&self, s: State, ctx: Context, location: CallLocation, args: &dyn ArgsLike) -> Result<Val> {
+					let parsed = parse_builtin_call(s.clone(), ctx, &PARAMS, args, false)?;
 
 					let result: #result = #name(#(#pass)*);
 					let result = result?;
-					result.try_into()
+					<#result_inner>::into_untyped(result, s)
 				}
 			}
 		};
@@ -539,6 +563,18 @@
 			];
 			impl Typed for #ident {
 				const TYPE: &'static ComplexValType = &ComplexValType::ObjectRef(&ITEMS);
+
+				fn from_untyped(value: Val, s: State) -> Result<Self> {
+					let obj = value.as_obj().expect("shape is correct");
+					Self::parse(&obj)
+				}
+
+				fn into_untyped(value: Self, s: State) -> Result<Val> {
+					let mut out = ObjValueBuilder::new();
+					value.serialize(&mut out)?;
+					Ok(Val::Obj(out.build()))
+				}
+
 			}
 		}
 	};
@@ -567,25 +603,8 @@
 					Ok(Self {
 						#(#fields_parse)*
 					})
-				}
-			}
-
-			impl TryFrom<Val> for #ident {
-				type Error = LocError;
-				fn try_from(value: Val) -> Result<Self, Self::Error> {
-					let obj = value.as_obj().expect("shape is correct");
-					Self::parse(&obj)
-				}
-			}
-			impl TryInto<Val> for #ident {
-				type Error = LocError;
-				fn try_into(self) -> Result<Val, Self::Error> {
-					let mut out = ObjValueBuilder::new();
-					self.serialize(&mut out)?;
-					Ok(Val::Obj(out.build()))
 				}
 			}
-			()
 		};
 	})
 }