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

difftreelog

refactor switch gc to gcmodule

Yaroslav Bolyukin2021-11-27parent: #09a4bd7.patch.diff
in: master

39 files changed

modified.github/workflows/release.ymldiffbeforeafterboth
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -21,7 +21,7 @@
           args: --all
 
   cargo-release:
-    if: startsWith(github.ref, 'refs/tags/')
+    if: startsWith(github.ref, 'refs/tags/') && !endsWith(github.ref, '-test')
     needs: [test]
     runs-on: ubuntu-latest
     steps:
modifiedCargo.tomldiffbeforeafterboth
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,11 +1,6 @@
 [workspace]
 members = [
-	"crates/jrsonnet-interner",
-	"crates/jrsonnet-parser",
-	"crates/jrsonnet-evaluator",
-	"crates/jrsonnet-stdlib",
-	"crates/jrsonnet-cli",
-	"crates/jrsonnet-types",
+	"crates/*",
 	"bindings/jsonnet",
 	"cmds/jrsonnet",
 ]
modifiedbindings/jsonnet/Cargo.tomldiffbeforeafterboth
--- a/bindings/jsonnet/Cargo.toml
+++ b/bindings/jsonnet/Cargo.toml
@@ -10,7 +10,7 @@
 [dependencies]
 jrsonnet-evaluator = { path = "../../crates/jrsonnet-evaluator", version = "0.4.2" }
 jrsonnet-parser = { path = "../../crates/jrsonnet-parser", version = "0.4.2" }
-jrsonnet-gc = { version = "0.4.2", features = ["derive"] }
+gcmodule = { git = "https://github.com/CertainLach/gcmodule", branch = "jrsonnet" }
 
 [lib]
 crate-type = ["cdylib"]
modifiedbindings/jsonnet/src/native.rsdiffbeforeafterboth
--- a/bindings/jsonnet/src/native.rs
+++ b/bindings/jsonnet/src/native.rs
@@ -1,9 +1,10 @@
+use gcmodule::Cc;
 use jrsonnet_evaluator::{
 	error::{Error, LocError},
+	gc::TraceBox,
 	native::{NativeCallback, NativeCallbackHandler},
 	EvaluationState, Val,
 };
-use jrsonnet_gc::{unsafe_empty_trace, Finalize, Gc, Trace};
 use jrsonnet_parser::{Param, ParamsDesc};
 use std::{
 	ffi::{c_void, CStr},
@@ -18,16 +19,15 @@
 	success: *mut c_int,
 ) -> *mut Val;
 
+#[derive(gcmodule::Trace)]
 struct JsonnetNativeCallbackHandler {
+	#[skip_trace]
 	ctx: *const c_void,
+	#[skip_trace]
 	cb: JsonnetNativeCallback,
-}
-impl Finalize for JsonnetNativeCallbackHandler {}
-unsafe impl Trace for JsonnetNativeCallbackHandler {
-	unsafe_empty_trace!();
 }
 impl NativeCallbackHandler for JsonnetNativeCallbackHandler {
-	fn call(&self, _from: Option<Rc<Path>>, args: &[Val]) -> Result<Val, LocError> {
+	fn call(&self, _from: 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())));
@@ -74,9 +74,9 @@
 
 	vm.add_native(
 		name,
-		Gc::new(NativeCallback::new(
+		Cc::new(NativeCallback::new(
 			params,
-			Box::new(JsonnetNativeCallbackHandler { ctx, cb }),
+			TraceBox(Box::new(JsonnetNativeCallbackHandler { ctx, cb })),
 		)),
 	)
 }
modifiedbindings/jsonnet/src/val_make.rsdiffbeforeafterboth
--- a/bindings/jsonnet/src/val_make.rs
+++ b/bindings/jsonnet/src/val_make.rs
@@ -1,7 +1,7 @@
 //! Create values in VM
 
+use gcmodule::Cc;
 use jrsonnet_evaluator::{ArrValue, EvaluationState, ObjValue, Val};
-use jrsonnet_gc::Gc;
 use std::{
 	ffi::CStr,
 	os::raw::{c_char, c_double, c_int},
@@ -38,7 +38,7 @@
 
 #[no_mangle]
 pub extern "C" fn jsonnet_json_make_array(_vm: &EvaluationState) -> *mut Val {
-	Box::into_raw(Box::new(Val::Arr(ArrValue::Eager(Gc::new(Vec::new())))))
+	Box::into_raw(Box::new(Val::Arr(ArrValue::Eager(Cc::new(Vec::new())))))
 }
 
 #[no_mangle]
modifiedbindings/jsonnet/src/val_modify.rsdiffbeforeafterboth
--- a/bindings/jsonnet/src/val_modify.rs
+++ b/bindings/jsonnet/src/val_modify.rs
@@ -2,8 +2,8 @@
 //! Only tested with variables, which haven't altered by code before appearing here
 //! In jrsonnet every value is immutable, and this code is probally broken
 
+use gcmodule::Cc;
 use jrsonnet_evaluator::{ArrValue, EvaluationState, LazyBinding, LazyVal, ObjMember, Val};
-use jrsonnet_gc::Gc;
 use jrsonnet_parser::Visibility;
 use std::{ffi::CStr, os::raw::c_char};
 
@@ -23,7 +23,7 @@
 				new.push(item);
 			}
 			new.push(LazyVal::new_resolved(val.clone()));
-			*arr = Val::Arr(ArrValue::Lazy(Gc::new(new)));
+			*arr = Val::Arr(ArrValue::Lazy(Cc::new(new)));
 		}
 		_ => panic!("should receive array"),
 	}
modifiedcmds/jrsonnet/Cargo.tomldiffbeforeafterboth
--- a/cmds/jrsonnet/Cargo.toml
+++ b/cmds/jrsonnet/Cargo.toml
@@ -17,6 +17,7 @@
 jrsonnet-cli = { path = "../../crates/jrsonnet-cli", version = "0.4.2" }
 mimallocator = { version = "0.1.3", optional = true }
 thiserror = "1.0"
+gcmodule = { git = "https://github.com/CertainLach/gcmodule", branch = "jrsonnet" }
 
 [dependencies.clap]
 git = "https://github.com/clap-rs/clap"
modifiedcmds/jrsonnet/src/main.rsdiffbeforeafterboth
--- a/cmds/jrsonnet/src/main.rs
+++ b/cmds/jrsonnet/src/main.rs
@@ -136,7 +136,7 @@
 
 	let val = if opts.input.exec {
 		state.evaluate_snippet_raw(
-			PathBuf::from("args").into(),
+			PathBuf::from("<cmdline>").into(),
 			(&opts.input.input as &str).into(),
 		)?
 	} else if opts.input.input == "-" {
modifiedcrates/jrsonnet-cli/Cargo.tomldiffbeforeafterboth
--- a/crates/jrsonnet-cli/Cargo.toml
+++ b/crates/jrsonnet-cli/Cargo.toml
@@ -12,11 +12,7 @@
     "explaining-traces",
 ] }
 jrsonnet-parser = { path = "../../crates/jrsonnet-parser", version = "0.4.2" }
-jrsonnet-gc = { version = "0.4.2", features = [
-    "derive",
-    "unstable-config",
-    "unstable-stats",
-] }
+gcmodule = { git = "https://github.com/CertainLach/gcmodule", branch = "jrsonnet" }
 
 [dependencies.clap]
 git = "https://github.com/clap-rs/clap"
modifiedcrates/jrsonnet-cli/src/lib.rsdiffbeforeafterboth
--- a/crates/jrsonnet-cli/src/lib.rs
+++ b/crates/jrsonnet-cli/src/lib.rs
@@ -103,12 +103,6 @@
 #[derive(Clap)]
 #[clap(help_heading = "GARBAGE COLLECTION")]
 pub struct GcOpts {
-	/// Min bytes allocated to start garbage collection
-	#[clap(long, default_value = "20000000")]
-	gc_initial_threshold: usize,
-	/// How much heap should grow after unsuccessful garbage collection
-	#[clap(long)]
-	gc_used_space_ratio: Option<f64>,
 	/// Do not skip gc on exit
 	#[clap(long)]
 	gc_collect_on_exit: bool,
@@ -122,32 +116,28 @@
 	gc_collect_before_printing_stats: bool,
 }
 impl GcOpts {
+	pub fn configure_global(&self) {
+		if !self.gc_collect_on_exit {
+			gcmodule::set_thread_collect_on_drop(false)
+		}
+	}
 	pub fn stats_printer(&self) -> Option<GcStatsPrinter> {
-		self.gc_print_stats
-			.then(|| GcStatsPrinter(self.gc_collect_before_printing_stats))
+		self.gc_print_stats.then(|| GcStatsPrinter {
+			collect_before_printing_stats: self.gc_collect_before_printing_stats,
+		})
 	}
-	pub fn configure_global(&self) {
-		jrsonnet_gc::configure(|config| {
-			config.leak_on_drop = !self.gc_collect_on_exit;
-			config.threshold = self.gc_initial_threshold;
-			if let Some(used_space_ratio) = self.gc_used_space_ratio {
-				config.used_space_ratio = used_space_ratio;
-			}
-		});
-	}
 }
-pub struct GcStatsPrinter(bool);
+
+pub struct GcStatsPrinter {
+	collect_before_printing_stats: bool,
+}
 impl Drop for GcStatsPrinter {
 	fn drop(&mut self) {
-		if self.0 {
-			jrsonnet_gc::force_collect()
+		eprintln!("=== GC STATS ===");
+		if self.collect_before_printing_stats {
+			let collected = gcmodule::collect_thread_cycles();
+			eprintln!("Collected: {}", collected);
 		}
-		eprintln!("=== GC STATS ===");
-		jrsonnet_gc::configure(|c| {
-			eprintln!("Final threshold: {:?}", c.threshold);
-		});
-		let stats = jrsonnet_gc::stats();
-		eprintln!("Collections performed: {}", stats.collections_performed);
-		eprintln!("Bytes still allocated: {}", stats.bytes_allocated);
+		eprintln!("Tracked: {}", gcmodule::count_thread_tracked())
 	}
 }
modifiedcrates/jrsonnet-evaluator/Cargo.tomldiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/Cargo.toml
+++ b/crates/jrsonnet-evaluator/Cargo.toml
@@ -32,7 +32,7 @@
 rustc-hash = "1.1.0"
 
 thiserror = "1.0"
-jrsonnet-gc = { version = "0.4.2", features = ["derive"] }
+gcmodule = { git = "https://github.com/CertainLach/gcmodule", branch = "jrsonnet" }
 
 [dependencies.anyhow]
 version = "1.0"
@@ -53,7 +53,7 @@
 
 # Explaining traces
 [dependencies.annotate-snippets]
-version = "0.9.0"
+version = "0.9.1"
 features = ["color"]
 optional = true
 
modifiedcrates/jrsonnet-evaluator/build.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/build.rs
+++ b/crates/jrsonnet-evaluator/build.rs
@@ -13,7 +13,6 @@
 		STDLIB_STR,
 		&ParserSettings {
 			file_name: PathBuf::from("std.jsonnet").into(),
-			loc_data: true,
 		},
 	)
 	.expect("parse");
modifiedcrates/jrsonnet-evaluator/src/builtin/format.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/builtin/format.rs
+++ b/crates/jrsonnet-evaluator/src/builtin/format.rs
@@ -2,13 +2,12 @@
 #![allow(clippy::too_many_arguments)]
 
 use crate::{error::Error::*, throw, LocError, ObjValue, Result, Val};
-use jrsonnet_gc::Trace;
+use gcmodule::Trace;
 use jrsonnet_interner::IStr;
 use jrsonnet_types::ValType;
 use thiserror::Error;
 
 #[derive(Debug, Clone, Error, Trace)]
-#[trivially_drop]
 pub enum FormatError {
 	#[error("truncated format code")]
 	TruncatedFormatCode,
modifiedcrates/jrsonnet-evaluator/src/builtin/manifest.rsdiffbeforeafterboth
before · crates/jrsonnet-evaluator/src/builtin/manifest.rs
1use crate::error::Error::*;2use crate::error::Result;3use crate::push_frame;4use crate::{throw, Val};56#[derive(PartialEq, Clone, Copy)]7pub enum ManifestType {8	// Applied in manifestification9	Manifest,10	/// Used for std.manifestJson11	/// Empty array/objects extends to "[\n\n]" instead of "[ ]" as in manifest12	Std,13	/// No line breaks, used in `obj+''`14	ToString,15	/// Minified json16	Minify,17}1819pub struct ManifestJsonOptions<'s> {20	pub padding: &'s str,21	pub mtype: ManifestType,22}2324pub fn manifest_json_ex(val: &Val, options: &ManifestJsonOptions<'_>) -> Result<String> {25	let mut out = String::new();26	manifest_json_ex_buf(val, &mut out, &mut String::new(), options)?;27	Ok(out)28}29fn manifest_json_ex_buf(30	val: &Val,31	buf: &mut String,32	cur_padding: &mut String,33	options: &ManifestJsonOptions<'_>,34) -> Result<()> {35	use std::fmt::Write;36	let mtype = options.mtype;37	match val {38		Val::Bool(v) => {39			if *v {40				buf.push_str("true");41			} else {42				buf.push_str("false");43			}44		}45		Val::Null => buf.push_str("null"),46		Val::Str(s) => escape_string_json_buf(s, buf),47		Val::Num(n) => write!(buf, "{}", n).unwrap(),48		Val::Arr(items) => {49			buf.push('[');50			if !items.is_empty() {51				if mtype != ManifestType::ToString && mtype != ManifestType::Minify {52					buf.push('\n');53				}5455				let old_len = cur_padding.len();56				cur_padding.push_str(options.padding);57				for (i, item) in items.iter().enumerate() {58					if i != 0 {59						buf.push(',');60						if mtype == ManifestType::ToString {61							buf.push(' ');62						} else if mtype != ManifestType::Minify {63							buf.push('\n');64						}65					}66					buf.push_str(cur_padding);67					manifest_json_ex_buf(&item?, buf, cur_padding, options)?;68				}69				cur_padding.truncate(old_len);7071				if mtype != ManifestType::ToString && mtype != ManifestType::Minify {72					buf.push('\n');73					buf.push_str(cur_padding);74				}75			} else if mtype == ManifestType::Std {76				buf.push_str("\n\n");77				buf.push_str(cur_padding);78			} else if mtype == ManifestType::ToString || mtype == ManifestType::Manifest {79				buf.push(' ');80			}81			buf.push(']');82		}83		Val::Obj(obj) => {84			obj.run_assertions()?;85			buf.push('{');86			let fields = obj.fields();87			if !fields.is_empty() {88				if mtype != ManifestType::ToString && mtype != ManifestType::Minify {89					buf.push('\n');90				}9192				let old_len = cur_padding.len();93				cur_padding.push_str(options.padding);94				for (i, field) in fields.into_iter().enumerate() {95					if i != 0 {96						buf.push(',');97						if mtype == ManifestType::ToString {98							buf.push(' ');99						} else if mtype != ManifestType::Minify {100							buf.push('\n');101						}102					}103					buf.push_str(cur_padding);104					escape_string_json_buf(&field, buf);105					buf.push_str(": ");106					push_frame(107						None,108						|| format!("field <{}> manifestification", field.clone()),109						|| {110							let value = obj.get(field.clone())?.unwrap();111							manifest_json_ex_buf(&value, buf, cur_padding, options)?;112							Ok(Val::Null)113						},114					)?;115				}116				cur_padding.truncate(old_len);117118				if mtype != ManifestType::ToString && mtype != ManifestType::Minify {119					buf.push('\n');120					buf.push_str(cur_padding);121				}122			} else if mtype == ManifestType::Std {123				buf.push_str("\n\n");124				buf.push_str(cur_padding);125			} else if mtype == ManifestType::ToString || mtype == ManifestType::Manifest {126				buf.push(' ');127			}128			buf.push('}');129		}130		Val::Func(_) => throw!(RuntimeError("tried to manifest function".into())),131	};132	Ok(())133}134135pub fn escape_string_json(s: &str) -> String {136	let mut buf = String::new();137	escape_string_json_buf(s, &mut buf);138	buf139}140141fn escape_string_json_buf(s: &str, buf: &mut String) {142	use std::fmt::Write;143	buf.push('"');144	for c in s.chars() {145		match c {146			'"' => buf.push_str("\\\""),147			'\\' => buf.push_str("\\\\"),148			'\u{0008}' => buf.push_str("\\b"),149			'\u{000c}' => buf.push_str("\\f"),150			'\n' => buf.push_str("\\n"),151			'\r' => buf.push_str("\\r"),152			'\t' => buf.push_str("\\t"),153			c if c < 32 as char || (c >= 127 as char && c <= 159 as char) => {154				write!(buf, "\\u{:04x}", c as u32).unwrap()155			}156			c => buf.push(c),157		}158	}159	buf.push('"');160}161162pub struct ManifestYamlOptions<'s> {163	/// Padding before fields, i.e164	/// ```yaml165	/// a:166	///   b:167	/// ## <- this168	/// ```169	pub padding: &'s str,170	/// Padding before array elements in objects171	/// ```yaml172	/// a:173	///   - 1174	/// ## <- this175	/// ```176	pub arr_element_padding: &'s str,177}178179pub fn manifest_yaml_ex(val: &Val, options: &ManifestYamlOptions<'_>) -> Result<String> {180	let mut out = String::new();181	manifest_yaml_ex_buf(val, &mut out, &mut String::new(), options)?;182	Ok(out)183}184fn manifest_yaml_ex_buf(185	val: &Val,186	buf: &mut String,187	cur_padding: &mut String,188	options: &ManifestYamlOptions<'_>,189) -> Result<()> {190	use std::fmt::Write;191	match val {192		Val::Bool(v) => {193			if *v {194				buf.push_str("true")195			} else {196				buf.push_str("false")197			}198		}199		Val::Null => buf.push_str("null"),200		Val::Str(s) => {201			if s.is_empty() {202				buf.push_str("\"\"");203			} else if let Some(s) = s.strip_suffix('\n') {204				buf.push('|');205				for line in s.split('\n') {206					buf.push('\n');207					buf.push_str(options.padding);208					buf.push_str(line);209				}210			} else {211				escape_string_json_buf(s, buf)212			}213		}214		Val::Num(n) => write!(buf, "{}", *n).unwrap(),215		Val::Arr(a) => {216			if a.is_empty() {217				buf.push_str("[]");218			} else {219				for (i, item) in a.iter().enumerate() {220					if i != 0 {221						buf.push('\n');222						buf.push_str(cur_padding);223					}224					let item = item?;225					buf.push('-');226					match &item {227						Val::Arr(a) if !a.is_empty() => {228							buf.push('\n');229							buf.push_str(cur_padding);230							buf.push_str(options.padding);231						}232						_ => buf.push(' '),233					}234					let extra_padding = match &item {235						Val::Arr(a) => !a.is_empty(),236						Val::Obj(o) => !o.is_empty(),237						_ => false,238					};239					let prev_len = cur_padding.len();240					if extra_padding {241						cur_padding.push_str(options.padding);242					}243					manifest_yaml_ex_buf(&item, buf, cur_padding, options)?;244					cur_padding.truncate(prev_len);245				}246			}247		}248		Val::Obj(o) => {249			if o.is_empty() {250				buf.push_str("{}");251			} else {252				for (i, key) in o.fields().iter().enumerate() {253					if i != 0 {254						buf.push('\n');255						buf.push_str(cur_padding);256					}257					escape_string_json_buf(key, buf);258					buf.push(':');259					let prev_len = cur_padding.len();260					let item = o.get(key.clone())?.expect("field exists");261					match &item {262						Val::Arr(a) if !a.is_empty() => {263							buf.push('\n');264							buf.push_str(cur_padding);265							buf.push_str(options.arr_element_padding);266							cur_padding.push_str(options.arr_element_padding);267						}268						Val::Obj(o) if !o.is_empty() => {269							buf.push('\n');270							buf.push_str(cur_padding);271							buf.push_str(options.padding);272							cur_padding.push_str(options.padding);273						}274						_ => buf.push(' '),275					}276					manifest_yaml_ex_buf(&item, buf, cur_padding, options)?;277					cur_padding.truncate(prev_len);278				}279			}280		}281		Val::Func(_) => throw!(RuntimeError("tried to manifest function".into())),282	}283	Ok(())284}
after · crates/jrsonnet-evaluator/src/builtin/manifest.rs
1use crate::error::Error::*;2use crate::error::Result;3use crate::push_description_frame;4use crate::{throw, Val};56#[derive(PartialEq, Clone, Copy)]7pub enum ManifestType {8	// Applied in manifestification9	Manifest,10	/// Used for std.manifestJson11	/// Empty array/objects extends to "[\n\n]" instead of "[ ]" as in manifest12	Std,13	/// No line breaks, used in `obj+''`14	ToString,15	/// Minified json16	Minify,17}1819pub struct ManifestJsonOptions<'s> {20	pub padding: &'s str,21	pub mtype: ManifestType,22}2324pub fn manifest_json_ex(val: &Val, options: &ManifestJsonOptions<'_>) -> Result<String> {25	let mut out = String::new();26	manifest_json_ex_buf(val, &mut out, &mut String::new(), options)?;27	Ok(out)28}29fn manifest_json_ex_buf(30	val: &Val,31	buf: &mut String,32	cur_padding: &mut String,33	options: &ManifestJsonOptions<'_>,34) -> Result<()> {35	use std::fmt::Write;36	let mtype = options.mtype;37	match val {38		Val::Bool(v) => {39			if *v {40				buf.push_str("true");41			} else {42				buf.push_str("false");43			}44		}45		Val::Null => buf.push_str("null"),46		Val::Str(s) => escape_string_json_buf(s, buf),47		Val::Num(n) => write!(buf, "{}", n).unwrap(),48		Val::Arr(items) => {49			buf.push('[');50			if !items.is_empty() {51				if mtype != ManifestType::ToString && mtype != ManifestType::Minify {52					buf.push('\n');53				}5455				let old_len = cur_padding.len();56				cur_padding.push_str(options.padding);57				for (i, item) in items.iter().enumerate() {58					if i != 0 {59						buf.push(',');60						if mtype == ManifestType::ToString {61							buf.push(' ');62						} else if mtype != ManifestType::Minify {63							buf.push('\n');64						}65					}66					buf.push_str(cur_padding);67					manifest_json_ex_buf(&item?, buf, cur_padding, options)?;68				}69				cur_padding.truncate(old_len);7071				if mtype != ManifestType::ToString && mtype != ManifestType::Minify {72					buf.push('\n');73					buf.push_str(cur_padding);74				}75			} else if mtype == ManifestType::Std {76				buf.push_str("\n\n");77				buf.push_str(cur_padding);78			} else if mtype == ManifestType::ToString || mtype == ManifestType::Manifest {79				buf.push(' ');80			}81			buf.push(']');82		}83		Val::Obj(obj) => {84			obj.run_assertions()?;85			buf.push('{');86			let fields = obj.fields();87			if !fields.is_empty() {88				if mtype != ManifestType::ToString && mtype != ManifestType::Minify {89					buf.push('\n');90				}9192				let old_len = cur_padding.len();93				cur_padding.push_str(options.padding);94				for (i, field) in fields.into_iter().enumerate() {95					if i != 0 {96						buf.push(',');97						if mtype == ManifestType::ToString {98							buf.push(' ');99						} else if mtype != ManifestType::Minify {100							buf.push('\n');101						}102					}103					buf.push_str(cur_padding);104					escape_string_json_buf(&field, buf);105					buf.push_str(": ");106					push_description_frame(107						|| format!("field <{}> manifestification", field.clone()),108						|| {109							let value = obj.get(field.clone())?.unwrap();110							manifest_json_ex_buf(&value, buf, cur_padding, options)?;111							Ok(Val::Null)112						},113					)?;114				}115				cur_padding.truncate(old_len);116117				if mtype != ManifestType::ToString && mtype != ManifestType::Minify {118					buf.push('\n');119					buf.push_str(cur_padding);120				}121			} else if mtype == ManifestType::Std {122				buf.push_str("\n\n");123				buf.push_str(cur_padding);124			} else if mtype == ManifestType::ToString || mtype == ManifestType::Manifest {125				buf.push(' ');126			}127			buf.push('}');128		}129		Val::Func(_) => throw!(RuntimeError("tried to manifest function".into())),130	};131	Ok(())132}133134pub fn escape_string_json(s: &str) -> String {135	let mut buf = String::new();136	escape_string_json_buf(s, &mut buf);137	buf138}139140fn escape_string_json_buf(s: &str, buf: &mut String) {141	use std::fmt::Write;142	buf.push('"');143	for c in s.chars() {144		match c {145			'"' => buf.push_str("\\\""),146			'\\' => buf.push_str("\\\\"),147			'\u{0008}' => buf.push_str("\\b"),148			'\u{000c}' => buf.push_str("\\f"),149			'\n' => buf.push_str("\\n"),150			'\r' => buf.push_str("\\r"),151			'\t' => buf.push_str("\\t"),152			c if c < 32 as char || (c >= 127 as char && c <= 159 as char) => {153				write!(buf, "\\u{:04x}", c as u32).unwrap()154			}155			c => buf.push(c),156		}157	}158	buf.push('"');159}160161pub struct ManifestYamlOptions<'s> {162	/// Padding before fields, i.e163	/// ```yaml164	/// a:165	///   b:166	/// ## <- this167	/// ```168	pub padding: &'s str,169	/// Padding before array elements in objects170	/// ```yaml171	/// a:172	///   - 1173	/// ## <- this174	/// ```175	pub arr_element_padding: &'s str,176}177178pub fn manifest_yaml_ex(val: &Val, options: &ManifestYamlOptions<'_>) -> Result<String> {179	let mut out = String::new();180	manifest_yaml_ex_buf(val, &mut out, &mut String::new(), options)?;181	Ok(out)182}183fn manifest_yaml_ex_buf(184	val: &Val,185	buf: &mut String,186	cur_padding: &mut String,187	options: &ManifestYamlOptions<'_>,188) -> Result<()> {189	use std::fmt::Write;190	match val {191		Val::Bool(v) => {192			if *v {193				buf.push_str("true")194			} else {195				buf.push_str("false")196			}197		}198		Val::Null => buf.push_str("null"),199		Val::Str(s) => {200			if s.is_empty() {201				buf.push_str("\"\"");202			} else if let Some(s) = s.strip_suffix('\n') {203				buf.push('|');204				for line in s.split('\n') {205					buf.push('\n');206					buf.push_str(options.padding);207					buf.push_str(line);208				}209			} else {210				escape_string_json_buf(s, buf)211			}212		}213		Val::Num(n) => write!(buf, "{}", *n).unwrap(),214		Val::Arr(a) => {215			if a.is_empty() {216				buf.push_str("[]");217			} else {218				for (i, item) in a.iter().enumerate() {219					if i != 0 {220						buf.push('\n');221						buf.push_str(cur_padding);222					}223					let item = item?;224					buf.push('-');225					match &item {226						Val::Arr(a) if !a.is_empty() => {227							buf.push('\n');228							buf.push_str(cur_padding);229							buf.push_str(options.padding);230						}231						_ => buf.push(' '),232					}233					let extra_padding = match &item {234						Val::Arr(a) => !a.is_empty(),235						Val::Obj(o) => !o.is_empty(),236						_ => false,237					};238					let prev_len = cur_padding.len();239					if extra_padding {240						cur_padding.push_str(options.padding);241					}242					manifest_yaml_ex_buf(&item, buf, cur_padding, options)?;243					cur_padding.truncate(prev_len);244				}245			}246		}247		Val::Obj(o) => {248			if o.is_empty() {249				buf.push_str("{}");250			} else {251				for (i, key) in o.fields().iter().enumerate() {252					if i != 0 {253						buf.push('\n');254						buf.push_str(cur_padding);255					}256					escape_string_json_buf(key, buf);257					buf.push(':');258					let prev_len = cur_padding.len();259					let item = o.get(key.clone())?.expect("field exists");260					match &item {261						Val::Arr(a) if !a.is_empty() => {262							buf.push('\n');263							buf.push_str(cur_padding);264							buf.push_str(options.arr_element_padding);265							cur_padding.push_str(options.arr_element_padding);266						}267						Val::Obj(o) if !o.is_empty() => {268							buf.push('\n');269							buf.push_str(cur_padding);270							buf.push_str(options.padding);271							cur_padding.push_str(options.padding);272						}273						_ => buf.push(' '),274					}275					manifest_yaml_ex_buf(&item, buf, cur_padding, options)?;276					cur_padding.truncate(prev_len);277				}278			}279		}280		Val::Func(_) => throw!(RuntimeError("tried to manifest function".into())),281	}282	Ok(())283}
modifiedcrates/jrsonnet-evaluator/src/builtin/mod.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/builtin/mod.rs
+++ b/crates/jrsonnet-evaluator/src/builtin/mod.rs
@@ -7,7 +7,7 @@
 	EvaluationState, FuncVal, IndexableVal, LazyVal, Val,
 };
 use format::{format_arr, format_obj};
-use jrsonnet_gc::Gc;
+use gcmodule::Cc;
 use jrsonnet_interner::IStr;
 use jrsonnet_parser::{ArgsDesc, ExprLocation};
 use jrsonnet_types::ty;
@@ -24,7 +24,7 @@
 
 pub fn std_format(str: IStr, vals: Val) -> Result<Val> {
 	push_frame(
-		Some(&ExprLocation(Rc::from(PathBuf::from("std.jsonnet")), 0, 0)),
+		&ExprLocation(Rc::from(PathBuf::from("std.jsonnet")), 0, 0),
 		|| format!("std.format of {}", str),
 		|| {
 			Ok(match vals {
@@ -68,7 +68,7 @@
 	}
 }
 
-type Builtin = fn(context: Context, loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val>;
+type Builtin = fn(context: Context, loc: &ExprLocation, args: &ArgsDesc) -> Result<Val>;
 
 type BuiltinsType = HashMap<Box<str>, Builtin>;
 
@@ -136,7 +136,7 @@
 	};
 }
 
-fn builtin_length(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {
+fn builtin_length(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {
 	parse_args!(context, "length", args, 1, [
 		0, x: ty!((string | object | array));
 	], {
@@ -154,7 +154,7 @@
 	})
 }
 
-fn builtin_type(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {
+fn builtin_type(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {
 	parse_args!(context, "type", args, 1, [
 		0, x: ty!(any);
 	], {
@@ -162,11 +162,7 @@
 	})
 }
 
-fn builtin_make_array(
-	context: Context,
-	_loc: Option<&ExprLocation>,
-	args: &ArgsDesc,
-) -> Result<Val> {
+fn builtin_make_array(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {
 	parse_args!(context, "makeArray", args, 2, [
 		0, sz: ty!(BoundedNumber<(Some(0.0)), (None)>) => Val::Num;
 		1, func: ty!(function) => Val::Func;
@@ -182,11 +178,7 @@
 	})
 }
 
-fn builtin_codepoint(
-	context: Context,
-	_loc: Option<&ExprLocation>,
-	args: &ArgsDesc,
-) -> Result<Val> {
+fn builtin_codepoint(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {
 	parse_args!(context, "codepoint", args, 1, [
 		0, str: ty!(char) => Val::Str;
 	], {
@@ -194,11 +186,7 @@
 	})
 }
 
-fn builtin_object_fields_ex(
-	context: Context,
-	_loc: Option<&ExprLocation>,
-	args: &ArgsDesc,
-) -> Result<Val> {
+fn builtin_object_fields_ex(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {
 	parse_args!(context, "objectFieldsEx", args, 2, [
 		0, obj: ty!(object) => Val::Obj;
 		1, inc_hidden: ty!(boolean) => Val::Bool;
@@ -208,11 +196,7 @@
 	})
 }
 
-fn builtin_object_has_ex(
-	context: Context,
-	_loc: Option<&ExprLocation>,
-	args: &ArgsDesc,
-) -> Result<Val> {
+fn builtin_object_has_ex(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {
 	parse_args!(context, "objectHasEx", args, 3, [
 		0, obj: ty!(object) => Val::Obj;
 		1, f: ty!(string) => Val::Str;
@@ -222,11 +206,7 @@
 	})
 }
 
-fn builtin_parse_json(
-	context: Context,
-	_loc: Option<&ExprLocation>,
-	args: &ArgsDesc,
-) -> Result<Val> {
+fn builtin_parse_json(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {
 	parse_args!(context, "parseJson", args, 1, [
 		0, s: ty!(string) => Val::Str;
 	], {
@@ -236,7 +216,7 @@
 	})
 }
 
-fn builtin_slice(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {
+fn builtin_slice(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {
 	parse_args!(context, "slice", args, 4, [
 		0, indexable: ty!((string | array));
 		1, index: ty!((number | null));
@@ -252,7 +232,7 @@
 	})
 }
 
-fn builtin_substr(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {
+fn builtin_substr(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {
 	parse_args!(context, "substr", args, 3, [
 		0, str: ty!(string) => Val::Str;
 		1, from: ty!(BoundedNumber<(Some(0.0)), (None)>) => Val::Num;
@@ -263,11 +243,7 @@
 	})
 }
 
-fn builtin_primitive_equals(
-	context: Context,
-	_loc: Option<&ExprLocation>,
-	args: &ArgsDesc,
-) -> Result<Val> {
+fn builtin_primitive_equals(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {
 	parse_args!(context, "primitiveEquals", args, 2, [
 		0, a: ty!(any);
 		1, b: ty!(any);
@@ -276,7 +252,7 @@
 	})
 }
 
-fn builtin_equals(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {
+fn builtin_equals(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {
 	parse_args!(context, "equals", args, 2, [
 		0, a: ty!(any);
 		1, b: ty!(any);
@@ -285,7 +261,7 @@
 	})
 }
 
-fn builtin_modulo(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {
+fn builtin_modulo(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {
 	parse_args!(context, "modulo", args, 2, [
 		0, a: ty!(number) => Val::Num;
 		1, b: ty!(number) => Val::Num;
@@ -294,7 +270,7 @@
 	})
 }
 
-fn builtin_mod(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {
+fn builtin_mod(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {
 	parse_args!(context, "mod", args, 2, [
 		0, a: ty!((number | string));
 		1, b: ty!(any);
@@ -303,7 +279,7 @@
 	})
 }
 
-fn builtin_floor(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {
+fn builtin_floor(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {
 	parse_args!(context, "floor", args, 1, [
 		0, x: ty!(number) => Val::Num;
 	], {
@@ -311,7 +287,7 @@
 	})
 }
 
-fn builtin_ceil(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {
+fn builtin_ceil(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {
 	parse_args!(context, "ceil", args, 1, [
 		0, x: ty!(number) => Val::Num;
 	], {
@@ -319,7 +295,7 @@
 	})
 }
 
-fn builtin_log(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {
+fn builtin_log(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {
 	parse_args!(context, "log", args, 1, [
 		0, n: ty!(number) => Val::Num;
 	], {
@@ -327,7 +303,7 @@
 	})
 }
 
-fn builtin_pow(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {
+fn builtin_pow(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {
 	parse_args!(context, "pow", args, 2, [
 		0, x: ty!(number) => Val::Num;
 		1, n: ty!(number) => Val::Num;
@@ -336,7 +312,7 @@
 	})
 }
 
-fn builtin_sqrt(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {
+fn builtin_sqrt(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {
 	parse_args!(context, "sqrt", args, 1, [
 		0, x: ty!(BoundedNumber<(Some(0.0)), (None)>) => Val::Num;
 	], {
@@ -344,7 +320,7 @@
 	})
 }
 
-fn builtin_sin(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {
+fn builtin_sin(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {
 	parse_args!(context, "sin", args, 1, [
 		0, x: ty!(number) => Val::Num;
 	], {
@@ -352,7 +328,7 @@
 	})
 }
 
-fn builtin_cos(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {
+fn builtin_cos(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {
 	parse_args!(context, "cos", args, 1, [
 		0, x: ty!(number) => Val::Num;
 	], {
@@ -360,7 +336,7 @@
 	})
 }
 
-fn builtin_tan(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {
+fn builtin_tan(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {
 	parse_args!(context, "tan", args, 1, [
 		0, x: ty!(number) => Val::Num;
 	], {
@@ -368,7 +344,7 @@
 	})
 }
 
-fn builtin_asin(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {
+fn builtin_asin(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {
 	parse_args!(context, "asin", args, 1, [
 		0, x: ty!(number) => Val::Num;
 	], {
@@ -376,7 +352,7 @@
 	})
 }
 
-fn builtin_acos(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {
+fn builtin_acos(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {
 	parse_args!(context, "acos", args, 1, [
 		0, x: ty!(number) => Val::Num;
 	], {
@@ -384,7 +360,7 @@
 	})
 }
 
-fn builtin_atan(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {
+fn builtin_atan(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {
 	parse_args!(context, "atan", args, 1, [
 		0, x: ty!(number) => Val::Num;
 	], {
@@ -392,7 +368,7 @@
 	})
 }
 
-fn builtin_exp(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {
+fn builtin_exp(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {
 	parse_args!(context, "exp", args, 1, [
 		0, x: ty!(number) => Val::Num;
 	], {
@@ -411,7 +387,7 @@
 	}
 }
 
-fn builtin_mantissa(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {
+fn builtin_mantissa(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {
 	parse_args!(context, "mantissa", args, 1, [
 		0, x: ty!(number) => Val::Num;
 	], {
@@ -419,7 +395,7 @@
 	})
 }
 
-fn builtin_exponent(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {
+fn builtin_exponent(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {
 	parse_args!(context, "exponent", args, 1, [
 		0, x: ty!(number) => Val::Num;
 	], {
@@ -427,7 +403,7 @@
 	})
 }
 
-fn builtin_ext_var(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {
+fn builtin_ext_var(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {
 	parse_args!(context, "extVar", args, 1, [
 		0, x: ty!(string) => Val::Str;
 	], {
@@ -435,15 +411,15 @@
 	})
 }
 
-fn builtin_native(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {
+fn builtin_native(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {
 	parse_args!(context, "native", args, 1, [
 		0, x: ty!(string) => Val::Str;
 	], {
-		Ok(with_state(|s| s.settings().ext_natives.get(&x).cloned()).map(|v| Val::Func(Gc::new(FuncVal::NativeExt(x.clone(), v)))).ok_or(UndefinedExternalFunction(x))?)
+		Ok(with_state(|s| s.settings().ext_natives.get(&x).cloned()).map(|v| Val::Func(Cc::new(FuncVal::NativeExt(x.clone(), v)))).ok_or(UndefinedExternalFunction(x))?)
 	})
 }
 
-fn builtin_filter(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {
+fn builtin_filter(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {
 	parse_args!(context, "filter", args, 2, [
 		0, func: ty!(function) => Val::Func;
 		1, arr: ty!(array) => Val::Arr;
@@ -454,7 +430,7 @@
 	})
 }
 
-fn builtin_map(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {
+fn builtin_map(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {
 	parse_args!(context, "map", args, 2, [
 		0, func: ty!(function) => Val::Func;
 		1, arr: ty!(array) => Val::Arr;
@@ -464,7 +440,7 @@
 	})
 }
 
-fn builtin_flatmap(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {
+fn builtin_flatmap(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {
 	parse_args!(context, "flatMap", args, 2, [
 		0, func: ty!(function) => Val::Func;
 		1, arr: ty!((array | string));
@@ -498,7 +474,7 @@
 	})
 }
 
-fn builtin_foldl(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {
+fn builtin_foldl(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {
 	parse_args!(context, "foldl", args, 3, [
 		0, func: ty!(function) => Val::Func;
 		1, arr: ty!(array) => Val::Arr;
@@ -512,7 +488,7 @@
 	})
 }
 
-fn builtin_foldr(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {
+fn builtin_foldr(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {
 	parse_args!(context, "foldr", args, 3, [
 		0, func: ty!(function) => Val::Func;
 		1, arr: ty!(array) => Val::Arr;
@@ -527,11 +503,7 @@
 }
 
 #[allow(non_snake_case)]
-fn builtin_sort_impl(
-	context: Context,
-	_loc: Option<&ExprLocation>,
-	args: &ArgsDesc,
-) -> Result<Val> {
+fn builtin_sort_impl(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {
 	parse_args!(context, "sort", args, 2, [
 		0, arr: ty!(array) => Val::Arr;
 		1, keyF: ty!(function) => Val::Func;
@@ -543,7 +515,7 @@
 	})
 }
 
-fn builtin_format(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {
+fn builtin_format(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {
 	parse_args!(context, "format", args, 2, [
 		0, str: ty!(string) => Val::Str;
 		1, vals: ty!(any)
@@ -552,7 +524,7 @@
 	})
 }
 
-fn builtin_range(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {
+fn builtin_range(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {
 	parse_args!(context, "range", args, 2, [
 		0, from: ty!(number) => Val::Num;
 		1, to: ty!(number) => Val::Num;
@@ -568,7 +540,7 @@
 	})
 }
 
-fn builtin_char(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {
+fn builtin_char(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {
 	parse_args!(context, "char", args, 1, [
 		0, n: ty!(number) => Val::Num;
 	], {
@@ -580,11 +552,7 @@
 	})
 }
 
-fn builtin_encode_utf8(
-	context: Context,
-	_loc: Option<&ExprLocation>,
-	args: &ArgsDesc,
-) -> Result<Val> {
+fn builtin_encode_utf8(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {
 	parse_args!(context, "encodeUTF8", args, 1, [
 		0, str: ty!(string) => Val::Str;
 	], {
@@ -592,11 +560,7 @@
 	})
 }
 
-fn builtin_decode_utf8(
-	context: Context,
-	_loc: Option<&ExprLocation>,
-	args: &ArgsDesc,
-) -> Result<Val> {
+fn builtin_decode_utf8(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {
 	parse_args!(context, "decodeUTF8", args, 1, [
 		0, arr: ty!((Array<ubyte>)) => Val::Arr;
 	], {
@@ -609,7 +573,7 @@
 	})
 }
 
-fn builtin_md5(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {
+fn builtin_md5(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {
 	parse_args!(context, "md5", args, 1, [
 		0, str: ty!(string) => Val::Str;
 	], {
@@ -617,24 +581,22 @@
 	})
 }
 
-fn builtin_trace(context: Context, loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {
+fn builtin_trace(context: Context, loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {
 	parse_args!(context, "trace", args, 2, [
 		0, str: ty!(string) => Val::Str;
 		1, rest: ty!(any);
 	], {
 		eprint!("TRACE:");
-		if let Some(loc) = loc {
 			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);
 			});
-		}
 		eprintln!(" {}", str);
 		Ok(rest)
 	})
 }
 
-fn builtin_base64(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {
+fn builtin_base64(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {
 	parse_args!(context, "base64", args, 1, [
 		0, input: ty!((string | (Array<number>)));
 	], {
@@ -654,7 +616,7 @@
 
 fn builtin_base64_decode_bytes(
 	context: Context,
-	_loc: Option<&ExprLocation>,
+	_loc: &ExprLocation,
 	args: &ArgsDesc,
 ) -> Result<Val> {
 	parse_args!(context, "base64DecodeBytes", args, 1, [
@@ -669,11 +631,7 @@
 	})
 }
 
-fn builtin_base64_decode(
-	context: Context,
-	_loc: Option<&ExprLocation>,
-	args: &ArgsDesc,
-) -> Result<Val> {
+fn builtin_base64_decode(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {
 	parse_args!(context, "base64Decode", args, 1, [
 		0, input: ty!(string) => Val::Str;
 	], {
@@ -685,7 +643,7 @@
 	})
 }
 
-fn builtin_join(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {
+fn builtin_join(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {
 	parse_args!(context, "join", args, 2, [
 		0, sep: ty!((string | array));
 		1, arr: ty!(array) => Val::Arr;
@@ -744,7 +702,7 @@
 
 fn builtin_escape_string_json(
 	context: Context,
-	_loc: Option<&ExprLocation>,
+	_loc: &ExprLocation,
 	args: &ArgsDesc,
 ) -> Result<Val> {
 	parse_args!(context, "escapeStringJson", args, 1, [
@@ -754,11 +712,7 @@
 	})
 }
 
-fn builtin_manifest_json_ex(
-	context: Context,
-	_loc: Option<&ExprLocation>,
-	args: &ArgsDesc,
-) -> Result<Val> {
+fn builtin_manifest_json_ex(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {
 	parse_args!(context, "manifestJsonEx", args, 2, [
 		0, value: ty!(any);
 		1, indent: ty!(string) => Val::Str;
@@ -772,7 +726,7 @@
 
 fn builtin_manifest_yaml_doc(
 	context: Context,
-	_loc: Option<&ExprLocation>,
+	_loc: &ExprLocation,
 	args: &ArgsDesc,
 ) -> Result<Val> {
 	parse_args!(context, "manifestYamlDoc", args, 2, [
@@ -786,7 +740,7 @@
 	})
 }
 
-fn builtin_reverse(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {
+fn builtin_reverse(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {
 	parse_args!(context, "reverse", args, 1, [
 		0, value: ty!(array) => Val::Arr;
 	], {
@@ -794,7 +748,7 @@
 	})
 }
 
-fn builtin_id(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {
+fn builtin_id(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {
 	parse_args!(context, "id", args, 1, [
 		0, v: ty!(any);
 	], {
@@ -802,11 +756,7 @@
 	})
 }
 
-fn builtin_str_replace(
-	context: Context,
-	_loc: Option<&ExprLocation>,
-	args: &ArgsDesc,
-) -> Result<Val> {
+fn builtin_str_replace(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {
 	parse_args!(context, "strReplace", args, 3, [
 		0, str: ty!(string) => Val::Str;
 		1, from: ty!(string) => Val::Str;
@@ -816,11 +766,7 @@
 	})
 }
 
-fn builtin_splitlimit(
-	context: Context,
-	_loc: Option<&ExprLocation>,
-	args: &ArgsDesc,
-) -> Result<Val> {
+fn builtin_splitlimit(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {
 	parse_args!(context, "splitLimit", args, 3, [
 		0, str: ty!(string) => Val::Str;
 		1, c: ty!(char) => Val::Str;
@@ -839,11 +785,7 @@
 	})
 }
 
-fn builtin_ascii_upper(
-	context: Context,
-	_loc: Option<&ExprLocation>,
-	args: &ArgsDesc,
-) -> Result<Val> {
+fn builtin_ascii_upper(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {
 	parse_args!(context, "asciiUpper", args, 1, [
 		0, str: ty!(string) => Val::Str;
 	], {
@@ -851,11 +793,7 @@
 	})
 }
 
-fn builtin_ascii_lower(
-	context: Context,
-	_loc: Option<&ExprLocation>,
-	args: &ArgsDesc,
-) -> Result<Val> {
+fn builtin_ascii_lower(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {
 	parse_args!(context, "asciiLower", args, 1, [
 		0, str: ty!(string) => Val::Str;
 	], {
@@ -863,7 +801,7 @@
 	})
 }
 
-fn builtin_member(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {
+fn builtin_member(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {
 	parse_args!(context, "member", args, 2, [
 		0, arr: ty!((array | string));
 		1, x: ty!(any);
@@ -887,7 +825,7 @@
 	})
 }
 
-fn builtin_count(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {
+fn builtin_count(context: Context, _loc: &ExprLocation, args: &ArgsDesc) -> Result<Val> {
 	parse_args!(context, "count", args, 2, [
 		0, arr: ty!(array) => Val::Arr;
 		1, x: ty!(any);
@@ -905,7 +843,7 @@
 
 pub fn call_builtin(
 	context: Context,
-	loc: Option<&ExprLocation>,
+	loc: &ExprLocation,
 	name: &str,
 	args: &ArgsDesc,
 ) -> Result<Val> {
modifiedcrates/jrsonnet-evaluator/src/builtin/sort.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/builtin/sort.rs
+++ b/crates/jrsonnet-evaluator/src/builtin/sort.rs
@@ -2,9 +2,9 @@
 	error::{Error, LocError, Result},
 	throw, Context, FuncVal, Val,
 };
-use jrsonnet_gc::{Finalize, Gc, Trace};
+use gcmodule::{Cc, Trace};
 
-#[derive(Debug, Clone, thiserror::Error, Trace, Finalize)]
+#[derive(Debug, Clone, thiserror::Error, Trace)]
 pub enum SortError {
 	#[error("sort key should be string or number")]
 	SortKeyShouldBeStringOrNumber,
@@ -59,7 +59,7 @@
 	Ok(sort_type)
 }
 
-pub fn sort(ctx: Context, values: Gc<Vec<Val>>, key_getter: &FuncVal) -> Result<Gc<Vec<Val>>> {
+pub fn sort(ctx: Context, values: Cc<Vec<Val>>, key_getter: &FuncVal) -> Result<Cc<Vec<Val>>> {
 	if values.len() <= 1 {
 		return Ok(values);
 	}
@@ -77,7 +77,7 @@
 			}),
 			SortKeyType::Unknown => unreachable!(),
 		};
-		Ok(Gc::new(mvalues))
+		Ok(Cc::new(mvalues))
 	} else {
 		let mut vk = Vec::with_capacity(values.len());
 		for value in values.iter() {
@@ -98,6 +98,6 @@
 			}),
 			SortKeyType::Unknown => unreachable!(),
 		};
-		Ok(Gc::new(vk.into_iter().map(|v| v.0).collect()))
+		Ok(Cc::new(vk.into_iter().map(|v| v.0).collect()))
 	}
 }
modifiedcrates/jrsonnet-evaluator/src/builtin/stdlib.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/builtin/stdlib.rs
+++ b/crates/jrsonnet-evaluator/src/builtin/stdlib.rs
@@ -15,7 +15,6 @@
 		jrsonnet_parser::parse(
 			jrsonnet_stdlib::STDLIB_STR,
 			&ParserSettings {
-				loc_data: true,
 				file_name: PathBuf::from("std.jsonnet").into(),
 			},
 		)
modifiedcrates/jrsonnet-evaluator/src/ctx.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/ctx.rs
+++ b/crates/jrsonnet-evaluator/src/ctx.rs
@@ -1,16 +1,15 @@
+use crate::cc_ptr_eq;
+use crate::gc::GcHashMap;
 use crate::{
 	error::Error::*, map::LayeredHashMap, FutureWrapper, LazyBinding, LazyVal, ObjValue, Result,
 	Val,
 };
-use jrsonnet_gc::{Gc, Trace};
+use gcmodule::{Cc, Trace};
 use jrsonnet_interner::IStr;
-use rustc_hash::FxHashMap;
 use std::fmt::Debug;
-use std::hash::BuildHasherDefault;
 
 #[derive(Clone, Trace)]
-#[trivially_drop]
-pub struct ContextCreator(pub Context, pub FutureWrapper<FxHashMap<IStr, LazyBinding>>);
+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> {
 		self.0.clone().extend_unbound(
@@ -23,7 +22,6 @@
 }
 
 #[derive(Trace)]
-#[trivially_drop]
 struct ContextInternals {
 	dollar: Option<ObjValue>,
 	this: Option<ObjValue>,
@@ -37,8 +35,7 @@
 }
 
 #[derive(Debug, Clone, Trace)]
-#[trivially_drop]
-pub struct Context(Gc<ContextInternals>);
+pub struct Context(Cc<ContextInternals>);
 impl Context {
 	pub fn new_future() -> FutureWrapper<Self> {
 		FutureWrapper::new()
@@ -57,7 +54,7 @@
 	}
 
 	pub fn new() -> Self {
-		Self(Gc::new(ContextInternals {
+		Self(Cc::new(ContextInternals {
 			dollar: None,
 			this: None,
 			super_obj: None,
@@ -84,19 +81,18 @@
 	}
 
 	pub fn with_var(self, name: IStr, value: Val) -> Self {
-		let mut new_bindings =
-			FxHashMap::with_capacity_and_hasher(1, BuildHasherDefault::default());
+		let mut new_bindings = GcHashMap::with_capacity(1);
 		new_bindings.insert(name, LazyVal::new_resolved(value));
 		self.extend(new_bindings, None, None, None)
 	}
 
 	pub fn with_this_super(self, new_this: ObjValue, new_super_obj: Option<ObjValue>) -> Self {
-		self.extend(FxHashMap::default(), None, Some(new_this), new_super_obj)
+		self.extend(GcHashMap::new(), None, Some(new_this), new_super_obj)
 	}
 
 	pub fn extend(
 		self,
-		new_bindings: FxHashMap<IStr, LazyVal>,
+		new_bindings: GcHashMap<IStr, LazyVal>,
 		new_dollar: Option<ObjValue>,
 		new_this: Option<ObjValue>,
 		new_super_obj: Option<ObjValue>,
@@ -110,30 +106,29 @@
 		} else {
 			ctx.bindings.clone().extend(new_bindings)
 		};
-		Self(Gc::new(ContextInternals {
+		Self(Cc::new(ContextInternals {
 			dollar,
 			this,
 			super_obj,
 			bindings,
 		}))
 	}
-	pub fn extend_bound(self, new_bindings: FxHashMap<IStr, LazyVal>) -> Self {
+	pub fn extend_bound(self, new_bindings: GcHashMap<IStr, LazyVal>) -> Self {
 		let new_this = self.0.this.clone();
 		let new_super_obj = self.0.super_obj.clone();
 		self.extend(new_bindings, None, new_this, new_super_obj)
 	}
 	pub fn extend_unbound(
 		self,
-		new_bindings: FxHashMap<IStr, LazyBinding>,
+		new_bindings: GcHashMap<IStr, LazyBinding>,
 		new_dollar: Option<ObjValue>,
 		new_this: Option<ObjValue>,
 		new_super_obj: Option<ObjValue>,
 	) -> Result<Self> {
 		let this = new_this.or_else(|| self.0.this.clone());
 		let super_obj = new_super_obj.or_else(|| self.0.super_obj.clone());
-		let mut new =
-			FxHashMap::with_capacity_and_hasher(new_bindings.len(), BuildHasherDefault::default());
-		for (k, v) in new_bindings.into_iter() {
+		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())?);
 		}
 		Ok(self.extend(new, new_dollar, this, super_obj))
@@ -152,6 +147,6 @@
 
 impl PartialEq for Context {
 	fn eq(&self, other: &Self) -> bool {
-		Gc::ptr_eq(&self.0, &other.0)
+		cc_ptr_eq(&self.0, &other.0)
 	}
 }
modifiedcrates/jrsonnet-evaluator/src/dynamic.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/dynamic.rs
+++ b/crates/jrsonnet-evaluator/src/dynamic.rs
@@ -1,11 +1,12 @@
-use jrsonnet_gc::{Gc, GcCell, Trace};
+use std::cell::RefCell;
 
+use gcmodule::{Cc, Trace};
+
 #[derive(Clone, Trace)]
-#[trivially_drop]
-pub struct FutureWrapper<V: Trace + 'static>(pub Gc<GcCell<Option<V>>>);
+pub struct FutureWrapper<V: Trace + 'static>(pub Cc<RefCell<Option<V>>>);
 impl<T: Trace + 'static> FutureWrapper<T> {
 	pub fn new() -> Self {
-		Self(Gc::new(GcCell::new(None)))
+		Self(Cc::new(RefCell::new(None)))
 	}
 	pub fn fill(self, value: T) {
 		assert!(self.0.borrow().is_none(), "wrapper is filled already");
modifiedcrates/jrsonnet-evaluator/src/error.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/error.rs
+++ b/crates/jrsonnet-evaluator/src/error.rs
@@ -1,5 +1,8 @@
-use crate::{Val, builtin::{format::FormatError, sort::SortError}, typed::TypeLocError};
-use jrsonnet_gc::Trace;
+use crate::{
+	builtin::{format::FormatError, sort::SortError},
+	typed::TypeLocError,
+};
+use gcmodule::Trace;
 use jrsonnet_interner::IStr;
 use jrsonnet_parser::{BinaryOpType, ExprLocation, UnaryOpType};
 use jrsonnet_types::ValType;
@@ -10,7 +13,6 @@
 use thiserror::Error;
 
 #[derive(Error, Debug, Clone, Trace)]
-#[trivially_drop]
 pub enum Error {
 	#[error("intrinsic not found: {0}")]
 	IntrinsicNotFound(IStr),
@@ -85,14 +87,15 @@
 	#[error("tried to import {1} from {0}, but imports is not supported")]
 	ImportNotSupported(PathBuf, PathBuf),
 	#[error(
-		"syntax error, expected one of {}, got {:?}",
+		"syntax error: expected {}, got {:?}",
 		.error.expected,
 		.source_code.chars().nth(error.location.offset).map(|c| c.to_string()).unwrap_or_else(|| "EOF".into())
 	)]
 	ImportSyntaxError {
+		#[skip_trace]
 		path: Rc<Path>,
 		source_code: IStr,
-		#[unsafe_ignore_trace]
+		#[skip_trace]
 		error: Box<jrsonnet_parser::ParseError>,
 	},
 
@@ -150,17 +153,14 @@
 }
 
 #[derive(Clone, Debug, Trace)]
-#[trivially_drop]
 pub struct StackTraceElement {
 	pub location: Option<ExprLocation>,
 	pub desc: String,
 }
 #[derive(Debug, Clone, Trace)]
-#[trivially_drop]
 pub struct StackTrace(pub Vec<StackTraceElement>);
 
 #[derive(Debug, Clone, Trace)]
-#[trivially_drop]
 pub struct LocError(Box<(Error, StackTrace)>);
 impl LocError {
 	pub fn new(e: Error) -> Self {
modifiedcrates/jrsonnet-evaluator/src/evaluate/mod.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/evaluate/mod.rs
+++ b/crates/jrsonnet-evaluator/src/evaluate/mod.rs
@@ -2,19 +2,18 @@
 	builtin::std_slice,
 	error::Error::*,
 	evaluate::operator::{evaluate_add_op, evaluate_binary_op_special, evaluate_unary_op},
+	gc::TraceBox,
 	push_frame, throw, with_state, ArrValue, Bindable, Context, ContextCreator, FuncDesc, FuncVal,
-	FutureWrapper, LazyBinding, LazyVal, LazyValValue, ObjValue, ObjValueBuilder, ObjectAssertion,
-	Result, Val,
+	FutureWrapper, GcHashMap, LazyBinding, LazyVal, LazyValValue, ObjValue,
+	ObjValueBuilder, ObjectAssertion, Result, Val,
 };
-use jrsonnet_gc::{Gc, Trace};
+use gcmodule::{Cc, Trace};
 use jrsonnet_interner::IStr;
 use jrsonnet_parser::{
 	ArgsDesc, AssertStmt, BindSpec, CompSpec, Expr, ExprLocation, FieldMember, ForSpecData,
 	IfSpecData, LiteralType, LocExpr, Member, ObjBody, ParamsDesc,
 };
 use jrsonnet_types::ValType;
-use rustc_hash::{FxHashMap, FxHasher};
-use std::{collections::HashMap, hash::BuildHasherDefault};
 pub mod operator;
 
 pub fn evaluate_binding_in_future(
@@ -26,7 +25,6 @@
 		let params = params.clone();
 
 		#[derive(Trace)]
-		#[trivially_drop]
 		struct LazyMethodBinding {
 			context_creator: FutureWrapper<Context>,
 			name: IStr,
@@ -44,15 +42,14 @@
 			}
 		}
 
-		LazyVal::new(Box::new(LazyMethodBinding {
+		LazyVal::new(TraceBox(Box::new(LazyMethodBinding {
 			context_creator,
 			name: b.name.clone(),
 			params,
 			value: b.value.clone(),
-		}))
+		})))
 	} else {
 		#[derive(Trace)]
-		#[trivially_drop]
 		struct LazyNamedBinding {
 			context_creator: FutureWrapper<Context>,
 			name: IStr,
@@ -63,11 +60,11 @@
 				evaluate_named(self.context_creator.unwrap(), &self.value, self.name)
 			}
 		}
-		LazyVal::new(Box::new(LazyNamedBinding {
+		LazyVal::new(TraceBox(Box::new(LazyNamedBinding {
 			context_creator,
 			name: b.name.clone(),
 			value: b.value,
-		}))
+		})))
 	}
 }
 
@@ -77,7 +74,6 @@
 		let params = params.clone();
 
 		#[derive(Trace)]
-		#[trivially_drop]
 		struct BindableMethodLazyVal {
 			this: Option<ObjValue>,
 			super_obj: Option<ObjValue>,
@@ -99,7 +95,6 @@
 		}
 
 		#[derive(Trace)]
-		#[trivially_drop]
 		struct BindableMethod {
 			context_creator: ContextCreator,
 			name: IStr,
@@ -108,30 +103,31 @@
 		}
 		impl Bindable for BindableMethod {
 			fn bind(&self, this: Option<ObjValue>, super_obj: Option<ObjValue>) -> Result<LazyVal> {
-				Ok(LazyVal::new(Box::new(BindableMethodLazyVal {
-					this,
-					super_obj,
+				Ok(LazyVal::new(TraceBox(Box::new(
+					BindableMethodLazyVal {
+						this,
+						super_obj,
 
-					context_creator: self.context_creator.clone(),
-					name: self.name.clone(),
-					params: self.params.clone(),
-					value: self.value.clone(),
-				})))
+						context_creator: self.context_creator.clone(),
+						name: self.name.clone(),
+						params: self.params.clone(),
+						value: self.value.clone(),
+					},
+				))))
 			}
 		}
 
 		(
 			b.name.clone(),
-			LazyBinding::Bindable(Gc::new(Box::new(BindableMethod {
+			LazyBinding::Bindable(Cc::new(TraceBox(Box::new(BindableMethod {
 				context_creator,
 				name: b.name.clone(),
 				params,
 				value: b.value.clone(),
-			}))),
+			})))),
 		)
 	} else {
 		#[derive(Trace)]
-		#[trivially_drop]
 		struct BindableNamedLazyVal {
 			this: Option<ObjValue>,
 			super_obj: Option<ObjValue>,
@@ -151,7 +147,6 @@
 		}
 
 		#[derive(Trace)]
-		#[trivially_drop]
 		struct BindableNamed {
 			context_creator: ContextCreator,
 			name: IStr,
@@ -159,30 +154,32 @@
 		}
 		impl Bindable for BindableNamed {
 			fn bind(&self, this: Option<ObjValue>, super_obj: Option<ObjValue>) -> Result<LazyVal> {
-				Ok(LazyVal::new(Box::new(BindableNamedLazyVal {
-					this,
-					super_obj,
+				Ok(LazyVal::new(TraceBox(Box::new(
+					BindableNamedLazyVal {
+						this,
+						super_obj,
 
-					context_creator: self.context_creator.clone(),
-					name: self.name.clone(),
-					value: self.value.clone(),
-				})))
+						context_creator: self.context_creator.clone(),
+						name: self.name.clone(),
+						value: self.value.clone(),
+					},
+				))))
 			}
 		}
 
 		(
 			b.name.clone(),
-			LazyBinding::Bindable(Gc::new(Box::new(BindableNamed {
+			LazyBinding::Bindable(Cc::new(TraceBox(Box::new(BindableNamed {
 				context_creator,
 				name: b.name.clone(),
 				value: b.value.clone(),
-			}))),
+			})))),
 		)
 	}
 }
 
 pub fn evaluate_method(ctx: Context, name: IStr, params: ParamsDesc, body: LocExpr) -> Val {
-	Val::Func(Gc::new(FuncVal::Normal(FuncDesc {
+	Val::Func(Cc::new(FuncVal::Normal(FuncDesc {
 		name,
 		ctx,
 		params,
@@ -240,8 +237,7 @@
 	let future_this = FutureWrapper::new();
 	let context_creator = ContextCreator(context.clone(), new_bindings.clone());
 	{
-		let mut bindings: FxHashMap<IStr, LazyBinding> =
-			FxHashMap::with_capacity_and_hasher(members.len(), BuildHasherDefault::default());
+		let mut bindings: GcHashMap<IStr, LazyBinding> = GcHashMap::with_capacity(members.len());
 		for (n, b) in members
 			.iter()
 			.filter_map(|m| match m {
@@ -272,7 +268,6 @@
 				let name = name.unwrap();
 
 				#[derive(Trace)]
-				#[trivially_drop]
 				struct ObjMemberBinding {
 					context_creator: ContextCreator,
 					value: LocExpr,
@@ -296,11 +291,11 @@
 					.with_add(*plus)
 					.with_visibility(*visibility)
 					.with_location(value.1.clone())
-					.bindable(Box::new(ObjMemberBinding {
+					.bindable(TraceBox(Box::new(ObjMemberBinding {
 						context_creator: context_creator.clone(),
 						value: value.clone(),
 						name,
-					}));
+					})));
 			}
 			Member::Field(FieldMember {
 				name,
@@ -314,7 +309,6 @@
 				}
 				let name = name.unwrap();
 				#[derive(Trace)]
-				#[trivially_drop]
 				struct ObjMemberBinding {
 					context_creator: ContextCreator,
 					value: LocExpr,
@@ -339,17 +333,16 @@
 					.member(name.clone())
 					.hide()
 					.with_location(value.1.clone())
-					.bindable(Box::new(ObjMemberBinding {
+					.bindable(TraceBox(Box::new(ObjMemberBinding {
 						context_creator: context_creator.clone(),
 						value: value.clone(),
 						params: params.clone(),
 						name,
-					}));
+					})));
 			}
 			Member::BindStmt(_) => {}
 			Member::AssertStmt(stmt) => {
 				#[derive(Trace)]
-				#[trivially_drop]
 				struct ObjectAssert {
 					context_creator: ContextCreator,
 					assert: AssertStmt,
@@ -364,10 +357,10 @@
 						evaluate_assert(ctx, &self.assert)
 					}
 				}
-				builder.assert(Box::new(ObjectAssert {
+				builder.assert(TraceBox(Box::new(ObjectAssert {
 					context_creator: context_creator.clone(),
 					assert: stmt.clone(),
-				}));
+				})));
 			}
 		}
 	}
@@ -385,11 +378,8 @@
 			evaluate_comp(context.clone(), &obj.compspecs, &mut |ctx| {
 				let new_bindings = FutureWrapper::new();
 				let context_creator = ContextCreator(context.clone(), new_bindings.clone());
-				let mut bindings: FxHashMap<IStr, LazyBinding> =
-					FxHashMap::with_capacity_and_hasher(
-						obj.pre_locals.len() + obj.post_locals.len(),
-						BuildHasherDefault::default(),
-					);
+				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()
@@ -406,7 +396,6 @@
 					Val::Null => {}
 					Val::Str(n) => {
 						#[derive(Trace)]
-						#[trivially_drop]
 						struct ObjCompBinding {
 							context: Context,
 							value: LocExpr,
@@ -418,12 +407,9 @@
 								_super_obj: Option<ObjValue>,
 							) -> Result<LazyVal> {
 								Ok(LazyVal::new_resolved(evaluate(
-									self.context.clone().extend(
-										FxHashMap::default(),
-										None,
-										this,
-										None,
-									),
+									self.context
+										.clone()
+										.extend(GcHashMap::new(), None, this, None),
 									&self.value,
 								)?))
 							}
@@ -432,10 +418,10 @@
 							.member(n)
 							.with_location(obj.value.1.clone())
 							.with_add(obj.plus)
-							.bindable(Box::new(ObjCompBinding {
+							.bindable(TraceBox(Box::new(ObjCompBinding {
 								context: ctx,
 								value: obj.value.clone(),
-							}));
+							})));
 					}
 					v => throw!(FieldMustBeStringGot(v.value_type())),
 				}
@@ -454,7 +440,7 @@
 	context: Context,
 	value: &LocExpr,
 	args: &ArgsDesc,
-	loc: Option<&ExprLocation>,
+	loc: &ExprLocation,
 	tailstrict: bool,
 ) -> Result<Val> {
 	let value = evaluate(context.clone(), value)?;
@@ -475,7 +461,7 @@
 	let value = &assertion.0;
 	let msg = &assertion.1;
 	let assertion_result = push_frame(
-		value.1.as_ref(),
+		&value.1,
 		|| "assertion condition".to_owned(),
 		|| {
 			evaluate(context.clone(), value)?
@@ -484,7 +470,7 @@
 	)?;
 	if !assertion_result {
 		push_frame(
-			value.1.as_ref(),
+			&value.1,
 			|| "assertion failure".to_owned(),
 			|| {
 				if let Some(msg) = msg {
@@ -534,8 +520,8 @@
 		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(
-			loc.as_ref(),
-			|| format!("variable <{}>", name),
+			loc,
+			|| format!("variable <{}> access", name),
 			|| context.binding(name.clone())?.evaluate(),
 		)?,
 		Index(value, index) => {
@@ -543,7 +529,7 @@
 				(Val::Obj(v), Val::Str(s)) => {
 					let sn = s.clone();
 					push_frame(
-						loc.as_ref(),
+						loc,
 						|| format!("field <{}> access", sn),
 						|| {
 							if let Some(v) = v.get(s.clone())? {
@@ -591,10 +577,8 @@
 			}
 		}
 		LocalExpr(bindings, returned) => {
-			let mut new_bindings: FxHashMap<IStr, LazyVal> = HashMap::with_capacity_and_hasher(
-				bindings.len(),
-				BuildHasherDefault::<FxHasher>::default(),
-			);
+			let mut new_bindings: GcHashMap<IStr, LazyVal> =
+				GcHashMap::with_capacity(bindings.len());
 			let future_context = Context::new_future();
 			for b in bindings {
 				new_bindings.insert(
@@ -612,7 +596,6 @@
 			for item in items {
 				// TODO: Implement ArrValue::Lazy with same context for every element?
 				#[derive(Trace)]
-				#[trivially_drop]
 				struct ArrayElement {
 					context: Context,
 					item: LocExpr,
@@ -622,10 +605,10 @@
 						evaluate(self.context, &self.item)
 					}
 				}
-				out.push(LazyVal::new(Box::new(ArrayElement {
+				out.push(LazyVal::new(TraceBox(Box::new(ArrayElement {
 					context: context.clone(),
 					item: item.clone(),
-				})));
+				}))));
 			}
 			Val::Arr(out.into())
 		}
@@ -635,26 +618,24 @@
 				out.push(evaluate(ctx, expr)?);
 				Ok(())
 			})?;
-			Val::Arr(ArrValue::Eager(Gc::new(out)))
+			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)?),
 		)?,
-		Apply(value, args, tailstrict) => {
-			evaluate_apply(context, value, args, loc.as_ref(), *tailstrict)?
-		}
+		Apply(value, args, tailstrict) => evaluate_apply(context, value, args, loc, *tailstrict)?,
 		Function(params, body) => {
 			evaluate_method(context, "anonymous".into(), params.clone(), body.clone())
 		}
-		Intrinsic(name) => Val::Func(Gc::new(FuncVal::Intrinsic(name.clone()))),
+		Intrinsic(name) => Val::Func(Cc::new(FuncVal::Intrinsic(name.clone()))),
 		AssertExpr(assert, returned) => {
 			evaluate_assert(context.clone(), assert)?;
 			evaluate(context, returned)?
 		}
 		ErrorStmt(e) => push_frame(
-			loc.as_ref(),
+			loc,
 			|| "error statement".to_owned(),
 			|| {
 				throw!(RuntimeError(
@@ -668,7 +649,7 @@
 			cond_else,
 		} => {
 			if push_frame(
-				loc.as_ref(),
+				loc,
 				|| "if condition".to_owned(),
 				|| evaluate(context.clone(), &cond.0)?.try_cast_bool("in if condition"),
 			)? {
@@ -703,23 +684,17 @@
 			std_slice(indexable.into_indexable()?, start, end, step)?
 		}
 		Import(path) => {
-			let tmp = loc
-				.clone()
-				.expect("imports cannot be used without loc_data")
-				.0;
+			let tmp = loc.clone().0;
 			let mut import_location = tmp.to_path_buf();
 			import_location.pop();
 			push_frame(
-				loc.as_ref(),
+				loc,
 				|| format!("import {:?}", path),
 				|| with_state(|s| s.import_file(&import_location, path)),
 			)?
 		}
 		ImportStr(path) => {
-			let tmp = loc
-				.clone()
-				.expect("imports cannot be used without loc_data")
-				.0;
+			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))?)
modifiedcrates/jrsonnet-evaluator/src/function.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/function.rs
+++ b/crates/jrsonnet-evaluator/src/function.rs
@@ -1,18 +1,13 @@
-use crate::{
-	error::Error::*, evaluate, evaluate_named, throw, Context, FutureWrapper, LazyVal,
-	LazyValValue, Result, Val,
-};
-use jrsonnet_gc::Trace;
+use crate::{Context, FutureWrapper, GcHashMap, LazyVal, LazyValValue, Result, Val, error::Error::*, evaluate, evaluate_named, gc::TraceBox, throw};
+use gcmodule::Trace;
 use jrsonnet_interner::IStr;
 use jrsonnet_parser::{ArgsDesc, LocExpr, ParamsDesc};
-use rustc_hash::FxHashMap;
-use std::{collections::HashMap, hash::BuildHasherDefault};
+use std::collections::HashMap;
 
 const NO_DEFAULT_CONTEXT: &str =
 	"no default context set for call with defined default parameter value";
 
 #[derive(Trace)]
-#[trivially_drop]
 struct EvaluateLazyVal {
 	context: Context,
 	expr: LocExpr,
@@ -38,8 +33,7 @@
 	args: &ArgsDesc,
 	tailstrict: bool,
 ) -> Result<Context> {
-	let mut passed_args =
-		HashMap::with_capacity_and_hasher(params.len(), BuildHasherDefault::default());
+	let mut passed_args = GcHashMap::with_capacity(params.len());
 	if args.unnamed.len() > params.len() {
 		throw!(TooManyArgsFunctionHas(params.len()))
 	}
@@ -53,10 +47,10 @@
 			if tailstrict {
 				LazyVal::new_resolved(evaluate(ctx.clone(), arg)?)
 			} else {
-				LazyVal::new(Box::new(EvaluateLazyVal {
+				LazyVal::new(TraceBox(Box::new(EvaluateLazyVal {
 					context: ctx.clone(),
 					expr: arg.clone(),
-				}))
+				})))
 			},
 		);
 		filled_args += 1;
@@ -73,10 +67,10 @@
 				if tailstrict {
 					LazyVal::new_resolved(evaluate(ctx.clone(), value)?)
 				} else {
-					LazyVal::new(Box::new(EvaluateLazyVal {
+					LazyVal::new(TraceBox(Box::new(EvaluateLazyVal {
 						context: ctx.clone(),
 						expr: value.clone(),
-					}))
+					})))
 				},
 			)
 			.is_some()
@@ -90,17 +84,13 @@
 		// Some args are unset, but maybe we have defaults for them
 		// Default values should be created in newly created context
 		let future_context = FutureWrapper::<Context>::new();
-		let mut defaults = HashMap::with_capacity_and_hasher(
-			params.len() - filled_args,
-			BuildHasherDefault::default(),
-		);
+		let mut defaults = GcHashMap::with_capacity(params.len() - filled_args);
 
 		for param in params.iter().filter(|p| p.1.is_some()) {
 			if passed_args.contains_key(&param.0.clone()) {
 				continue;
 			}
 			#[derive(Trace)]
-			#[trivially_drop]
 			struct LazyNamedBinding {
 				future_context: FutureWrapper<Context>,
 				name: IStr,
@@ -111,19 +101,19 @@
 					evaluate_named(self.future_context.unwrap(), &self.value, self.name)
 				}
 			}
-			LazyVal::new(Box::new(LazyNamedBinding {
+			LazyVal::new(TraceBox(Box::new(LazyNamedBinding {
 				future_context: future_context.clone(),
 				name: param.0.clone(),
 				value: param.1.clone().unwrap(),
-			}));
+			})));
 
 			defaults.insert(
 				param.0.clone(),
-				LazyVal::new(Box::new(LazyNamedBinding {
+				LazyVal::new(TraceBox(Box::new(LazyNamedBinding {
 					future_context: future_context.clone(),
 					name: param.0.clone(),
 					value: param.1.clone().unwrap(),
-				})),
+				}))),
 			);
 			filled_args += 1;
 		}
@@ -155,7 +145,7 @@
 	args: &HashMap<IStr, Val>,
 	tailstrict: bool,
 ) -> Result<Context> {
-	let mut out = FxHashMap::with_capacity_and_hasher(params.len(), BuildHasherDefault::default());
+	let mut out = GcHashMap::with_capacity(params.len());
 	let mut positioned_args = vec![None; params.0.len()];
 	for (name, val) in args.iter() {
 		let idx = params
@@ -185,7 +175,6 @@
 				let body_ctx = body_ctx.clone();
 				let default = default.clone();
 				#[derive(Trace)]
-				#[trivially_drop]
 				struct EvaluateLazyVal {
 					body_ctx: Option<Context>,
 					default: LocExpr,
@@ -198,7 +187,10 @@
 						)
 					}
 				}
-				LazyVal::new(Box::new(EvaluateLazyVal { body_ctx, default }))
+				LazyVal::new(TraceBox(Box::new(EvaluateLazyVal {
+					body_ctx,
+					default,
+				})))
 			}
 		} else {
 			throw!(FunctionParameterNotBoundInCall(p.0.clone()));
@@ -215,7 +207,7 @@
 	params: &ParamsDesc,
 	args: &[Val],
 ) -> Result<Context> {
-	let mut out = FxHashMap::with_capacity_and_hasher(params.len(), BuildHasherDefault::default());
+	let mut out = GcHashMap::with_capacity(params.len());
 	let mut positioned_args = vec![None; params.0.len()];
 	for (id, arg) in args.iter().enumerate() {
 		if id >= params.len() {
@@ -243,7 +235,7 @@
 	($ctx: expr, $fn_name: expr, $args: expr, $total_args: expr, [
 		$($id: expr, $name: ident: $ty: expr $(=>$match: path)?);+ $(;)?
 	], $handler:block) => {{
-		use $crate::{error::Error::*, throw, evaluate, push_frame, typed::CheckType};
+		use $crate::{error::Error::*, throw, evaluate, push_description_frame, typed::CheckType};
 
 		let args = $args;
 		if args.unnamed.len() + args.named.len() > $total_args {
@@ -263,7 +255,7 @@
 			} else {
 				&$args.unnamed[$id]
 			};
-			let $name = push_frame(None, || format!("evaluating argument"), || {
+			let $name = push_description_frame(|| format!("evaluating builtin argument {}", stringify!($name)), || {
 				let value = evaluate($ctx.clone(), &$name)?;
 				$ty.check(&value)?;
 				Ok(value)
addedcrates/jrsonnet-evaluator/src/gc.rsdiffbeforeafterboth
--- /dev/null
+++ b/crates/jrsonnet-evaluator/src/gc.rs
@@ -0,0 +1,141 @@
+/// Macros to help deal with Gc
+use std::{
+	borrow::{Borrow, BorrowMut},
+	hash::BuildHasherDefault,
+	ops::{Deref, DerefMut},
+};
+
+use gcmodule::{Trace, Tracer};
+use rustc_hash::{FxHashMap, FxHashSet};
+
+/// Replacement for box, which assumes that the underlying type is [`Trace`]
+#[derive(Debug, Clone)]
+pub struct TraceBox<T: ?Sized>(pub Box<T>);
+
+impl<T: ?Sized + Trace> Trace for TraceBox<T> {
+	fn trace(&self, tracer: &mut Tracer) {
+		self.0.trace(tracer)
+	}
+
+	fn is_type_tracked() -> bool {
+		return true;
+	}
+}
+
+// TODO: Replace with CoerceUnsized
+impl<T: ?Sized> From<Box<T>> for TraceBox<T> {
+	fn from(inner: Box<T>) -> Self {
+		Self(inner)
+	}
+}
+
+impl<T: ?Sized> Deref for TraceBox<T> {
+	type Target = T;
+
+	fn deref(&self) -> &Self::Target {
+		&self.0
+	}
+}
+impl<T: Trace + ?Sized> DerefMut for TraceBox<T> {
+	fn deref_mut(&mut self) -> &mut Self::Target {
+		&mut self.0
+	}
+}
+
+impl<T: ?Sized> Borrow<T> for TraceBox<T> {
+	fn borrow(&self) -> &T {
+		&*self.0
+	}
+}
+
+impl<T: ?Sized> BorrowMut<T> for TraceBox<T> {
+	fn borrow_mut(&mut self) -> &mut T {
+		&mut *self.0
+	}
+}
+
+impl<T: ?Sized> AsRef<T> for TraceBox<T> {
+	fn as_ref(&self) -> &T {
+		&*self.0
+	}
+}
+
+impl<T: ?Sized> AsMut<T> for TraceBox<T> {
+	fn as_mut(&mut self) -> &mut T {
+		&mut *self.0
+	}
+}
+
+#[derive(Clone)]
+pub struct GcHashSet<V>(pub FxHashSet<V>);
+impl<V> GcHashSet<V> {
+	pub fn new() -> Self {
+		Self(Default::default())
+	}
+	pub fn with_capacity(capacity: usize) -> Self {
+		Self(FxHashSet::with_capacity_and_hasher(
+			capacity,
+			BuildHasherDefault::default(),
+		))
+	}
+}
+impl<V> Trace for GcHashSet<V>
+where
+	V: Trace,
+{
+	fn trace(&self, tracer: &mut gcmodule::Tracer) {
+		for v in &self.0 {
+			v.trace(tracer);
+		}
+	}
+}
+impl<V> Deref for GcHashSet<V> {
+	type Target = FxHashSet<V>;
+
+	fn deref(&self) -> &Self::Target {
+		&self.0
+	}
+}
+impl<V> DerefMut for GcHashSet<V> {
+	fn deref_mut(&mut self) -> &mut Self::Target {
+		&mut self.0
+	}
+}
+
+#[derive(Clone)]
+pub struct GcHashMap<K, V>(pub FxHashMap<K, V>);
+impl<K, V> GcHashMap<K, V> {
+	pub fn new() -> Self {
+		Self(Default::default())
+	}
+	pub fn with_capacity(capacity: usize) -> Self {
+		Self(FxHashMap::with_capacity_and_hasher(
+			capacity,
+			BuildHasherDefault::default(),
+		))
+	}
+}
+impl<K, V> Trace for GcHashMap<K, V>
+where
+	K: Trace,
+	V: Trace,
+{
+	fn trace(&self, tracer: &mut gcmodule::Tracer) {
+		for (k, v) in &self.0 {
+			k.trace(tracer);
+			v.trace(tracer);
+		}
+	}
+}
+impl<K, V> Deref for GcHashMap<K, V> {
+	type Target = FxHashMap<K, V>;
+
+	fn deref(&self) -> &Self::Target {
+		&self.0
+	}
+}
+impl<K, V> DerefMut for GcHashMap<K, V> {
+	fn deref_mut(&mut self) -> &mut Self::Target {
+		&mut self.0
+	}
+}
modifiedcrates/jrsonnet-evaluator/src/lib.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/lib.rs
+++ b/crates/jrsonnet-evaluator/src/lib.rs
@@ -25,30 +25,31 @@
 use error::{Error::*, LocError, Result, StackTraceElement};
 pub use evaluate::*;
 pub use function::parse_function_call;
+use gc::{GcHashMap, TraceBox};
+use gcmodule::{Cc, Trace};
 pub use import::*;
-use jrsonnet_gc::{Finalize, Gc, Trace};
 pub use jrsonnet_interner::IStr;
 use jrsonnet_parser::*;
 use native::NativeCallback;
 pub use obj::*;
-use rustc_hash::FxHashMap;
 use std::{
 	cell::{Ref, RefCell, RefMut},
 	collections::HashMap,
 	fmt::Debug,
-	hash::BuildHasherDefault,
 	path::{Path, PathBuf},
 	rc::Rc,
 };
 use trace::{location_to_offset, offset_to_location, CodeLocation, CompactFormat, TraceFormat};
 pub use val::*;
+pub mod gc;
 
-pub trait Bindable: Trace {
+pub trait Bindable: Trace + 'static {
 	fn bind(&self, this: Option<ObjValue>, super_obj: Option<ObjValue>) -> Result<LazyVal>;
 }
-#[derive(Trace, Finalize, Clone)]
+
+#[derive(Clone, Trace)]
 pub enum LazyBinding {
-	Bindable(Gc<Box<dyn Bindable>>),
+	Bindable(Cc<TraceBox<dyn Bindable>>),
 	Bound(LazyVal),
 }
 
@@ -74,7 +75,7 @@
 	/// Used for s`td.extVar`
 	pub ext_vars: HashMap<IStr, Val>,
 	/// Used for ext.native
-	pub ext_natives: HashMap<IStr, Gc<NativeCallback>>,
+	pub ext_natives: HashMap<IStr, Cc<NativeCallback>>,
 	/// TLA vars
 	pub tla_vars: HashMap<IStr, Val>,
 	/// Global variables are inserted in default context
@@ -172,19 +173,27 @@
 	EVAL_STATE.with(|s| f(s.borrow().as_ref().unwrap()))
 }
 pub(crate) fn push_frame<T>(
-	e: Option<&ExprLocation>,
+	e: &ExprLocation,
 	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(crate) fn push_val_frame(
-	e: Option<&ExprLocation>,
+	e: &ExprLocation,
 	frame_desc: impl FnOnce() -> String,
 	f: impl FnOnce() -> Result<Val>,
 ) -> Result<Val> {
-	with_state(|s| s.push(e, frame_desc, f))
+	with_state(|s| s.push_val(e, frame_desc, f))
+}
+#[allow(dead_code)]
+pub(crate) 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
@@ -201,7 +210,6 @@
 				&source_code,
 				&ParserSettings {
 					file_name: path.clone(),
-					loc_data: true,
 				},
 			)
 			.map_err(|error| ImportSyntaxError {
@@ -246,7 +254,7 @@
 		ro_map.get(name).map(|value| value.source_code.clone())
 	}
 	pub fn map_source_locations(&self, file: &Path, locs: &[usize]) -> Vec<CodeLocation> {
-		offset_to_location(&self.get_source(file).unwrap(), locs)
+		offset_to_location(&self.get_source(file).unwrap_or("".into()), locs)
 	}
 	pub fn map_from_source_location(
 		&self,
@@ -322,8 +330,7 @@
 	/// Creates context with all passed global variables
 	pub fn create_default_context(&self) -> Context {
 		let globals = &self.settings().globals;
-		let mut new_bindings: FxHashMap<IStr, LazyVal> =
-			FxHashMap::with_capacity_and_hasher(globals.len(), BuildHasherDefault::default());
+		let mut new_bindings = GcHashMap::with_capacity(globals.len());
 		for (name, value) in globals.iter() {
 			new_bindings.insert(name.clone(), LazyVal::new_resolved(value.clone()));
 		}
@@ -333,7 +340,7 @@
 	/// Executes code creating a new stack frame
 	pub fn push<T>(
 		&self,
-		e: Option<&ExprLocation>,
+		e: &ExprLocation,
 		frame_desc: impl FnOnce() -> String,
 		f: impl FnOnce() -> Result<T>,
 	) -> Result<T> {
@@ -353,25 +360,21 @@
 			let mut data = self.data_mut();
 			data.stack_depth -= 1;
 			data.stack_generation += 1;
-			// if let Some(e) = e {
-			// 	result =
-			// 		data.breakpoints
-			// 			.insert(data.stack_depth, data.stack_generation, &e, result)
-			// }
 		}
 		if let Err(mut err) = result {
 			err.trace_mut().0.push(StackTraceElement {
-				location: e.cloned(),
+				location: Some(e.clone()),
 				desc: frame_desc(),
 			});
 			return Err(err);
 		}
 		result
 	}
+
 	/// Executes code creating a new stack frame
 	pub fn push_val(
 		&self,
-		e: Option<&ExprLocation>,
+		e: &ExprLocation,
 		frame_desc: impl FnOnce() -> String,
 		f: impl FnOnce() -> Result<Val>,
 	) -> Result<Val> {
@@ -391,15 +394,45 @@
 			let mut data = self.data_mut();
 			data.stack_depth -= 1;
 			data.stack_generation += 1;
-			if let Some(e) = e {
-				result =
-					data.breakpoints
-						.insert(data.stack_depth, data.stack_generation, &e, result)
+			result = data
+				.breakpoints
+				.insert(data.stack_depth, data.stack_generation, &e, result);
+		}
+		if let Err(mut err) = result {
+			err.trace_mut().0.push(StackTraceElement {
+				location: Some(e.clone()),
+				desc: frame_desc(),
+			});
+			return Err(err);
+		}
+		result
+	}
+	/// Executes code creating a new stack frame
+	pub fn push_description<T>(
+		&self,
+		frame_desc: impl FnOnce() -> String,
+		f: impl FnOnce() -> Result<T>,
+	) -> Result<T> {
+		{
+			let mut data = self.data_mut();
+			let stack_depth = &mut data.stack_depth;
+			if *stack_depth > self.max_stack() {
+				// Error creation uses data, so i drop guard here
+				drop(data);
+				throw!(StackOverflow);
+			} else {
+				*stack_depth += 1;
 			}
 		}
+		let result = f();
+		{
+			let mut data = self.data_mut();
+			data.stack_depth -= 1;
+			data.stack_generation += 1;
+		}
 		if let Err(mut err) = result {
 			err.trace_mut().0.push(StackTraceElement {
-				location: e.cloned(),
+				location: None,
 				desc: frame_desc(),
 			});
 			return Err(err);
@@ -451,7 +484,12 @@
 	}
 
 	pub fn manifest(&self, val: Val) -> Result<IStr> {
-		self.run_in_state(|| val.manifest(&self.manifest_format()))
+		self.run_in_state(|| {
+			push_description_frame(
+				|| format!("manifestification"),
+				|| val.manifest(&self.manifest_format()),
+			)
+		})
 	}
 	pub fn manifest_multi(&self, val: Val) -> Result<Vec<(IStr, IStr)>> {
 		self.run_in_state(|| val.manifest_multi(&self.manifest_format()))
@@ -464,8 +502,7 @@
 	pub fn with_tla(&self, val: Val) -> Result<Val> {
 		self.run_in_state(|| {
 			Ok(match val {
-				Val::Func(func) => push_frame(
-					None,
+				Val::Func(func) => push_description_frame(
 					|| "during TLA call".to_owned(),
 					|| {
 						func.evaluate_map(
@@ -511,7 +548,6 @@
 			&code,
 			&ParserSettings {
 				file_name: source.clone(),
-				loc_data: true,
 			},
 		)
 		.map_err(|e| ImportSyntaxError {
@@ -570,7 +606,7 @@
 		self.settings_mut().import_resolver = resolver;
 	}
 
-	pub fn add_native(&self, name: IStr, cb: Gc<NativeCallback>) {
+	pub fn add_native(&self, name: IStr, cb: Cc<NativeCallback>) {
 		self.settings_mut().ext_natives.insert(name, cb);
 	}
 
@@ -603,13 +639,20 @@
 	}
 }
 
+pub fn cc_ptr_eq<T>(a: &Cc<T>, b: &Cc<T>) -> bool {
+	let a = &a as &T;
+	let b = &b as &T;
+	std::ptr::eq(a, b)
+}
+
 #[cfg(test)]
 pub mod tests {
 	use super::Val;
 	use crate::{
-		error::Error::*, native::NativeCallbackHandler, primitive_equals, EvaluationState,
+		error::Error::*, gc::TraceBox, native::NativeCallbackHandler, primitive_equals,
+		EvaluationState,
 	};
-	use jrsonnet_gc::{Finalize, Gc, Trace};
+	use gcmodule::{Cc, Trace};
 	use jrsonnet_interner::IStr;
 	use jrsonnet_parser::*;
 	use std::{
@@ -624,11 +667,11 @@
 		state.run_in_state(|| {
 			state
 				.push(
-					Some(&ExprLocation(PathBuf::from("test1.jsonnet").into(), 10, 20)),
+					&ExprLocation(PathBuf::from("test1.jsonnet").into(), 10, 20),
 					|| "outer".to_owned(),
 					|| {
 						state.push(
-							Some(&ExprLocation(PathBuf::from("test2.jsonnet").into(), 30, 40)),
+							&ExprLocation(PathBuf::from("test2.jsonnet").into(), 30, 40),
 							|| "inner".to_owned(),
 							|| Err(RuntimeError("".into()).into()),
 						)?;
@@ -1028,7 +1071,6 @@
 				"{ x: 1, y: 2 } == { x: 1, y: 2 }",
 				&ParserSettings {
 					file_name: PathBuf::from("equality").into(),
-					loc_data: true,
 				}
 			)
 		);
@@ -1042,14 +1084,11 @@
 
 		evaluator.with_stdlib();
 
-		#[derive(Trace, Finalize)]
+		#[derive(Trace)]
 		struct NativeAdd;
 		impl NativeCallbackHandler for NativeAdd {
-			fn call(&self, from: Option<Rc<Path>>, args: &[Val]) -> crate::error::Result<Val> {
-				assert_eq!(
-					&from.unwrap() as &Path,
-					&PathBuf::from("native_caller.jsonnet")
-				);
+			fn call(&self, from: Rc<Path>, args: &[Val]) -> crate::error::Result<Val> {
+				assert_eq!(&from as &Path, &PathBuf::from("native_caller.jsonnet"));
 				match (&args[0], &args[1]) {
 					(Val::Num(a), Val::Num(b)) => Ok(Val::Num(a + b)),
 					(_, _) => unreachable!(),
@@ -1058,12 +1097,12 @@
 		}
 		evaluator.settings_mut().ext_natives.insert(
 			"native_add".into(),
-			Gc::new(NativeCallback::new(
+			Cc::new(NativeCallback::new(
 				ParamsDesc(Rc::new(vec![
 					Param("a".into(), None),
 					Param("b".into(), None),
 				])),
-				Box::new(NativeAdd),
+				TraceBox(Box::new(NativeAdd)),
 			)),
 		);
 		evaluator.evaluate_snippet_raw(
modifiedcrates/jrsonnet-evaluator/src/map.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/map.rs
+++ b/crates/jrsonnet-evaluator/src/map.rs
@@ -1,23 +1,21 @@
-use jrsonnet_gc::{Gc, Trace};
+use gcmodule::{Cc, Trace};
 use jrsonnet_interner::IStr;
-use rustc_hash::FxHashMap;
 
-use crate::LazyVal;
+use crate::{GcHashMap, LazyVal};
 
 #[derive(Trace)]
-#[trivially_drop]
+#[force_tracking]
 pub struct LayeredHashMapInternals {
 	parent: Option<LayeredHashMap>,
-	current: FxHashMap<IStr, LazyVal>,
+	current: GcHashMap<IStr, LazyVal>,
 }
 
 #[derive(Trace)]
-#[trivially_drop]
-pub struct LayeredHashMap(Gc<LayeredHashMapInternals>);
+pub struct LayeredHashMap(Cc<LayeredHashMapInternals>);
 
 impl LayeredHashMap {
-	pub fn extend(self, new_layer: FxHashMap<IStr, LazyVal>) -> Self {
-		Self(Gc::new(LayeredHashMapInternals {
+	pub fn extend(self, new_layer: GcHashMap<IStr, LazyVal>) -> Self {
+		Self(Cc::new(LayeredHashMapInternals {
 			parent: Some(self),
 			current: new_layer,
 		}))
@@ -49,9 +47,9 @@
 
 impl Default for LayeredHashMap {
 	fn default() -> Self {
-		Self(Gc::new(LayeredHashMapInternals {
+		Self(Cc::new(LayeredHashMapInternals {
 			parent: None,
-			current: FxHashMap::default(),
+			current: GcHashMap::new(),
 		}))
 	}
 }
modifiedcrates/jrsonnet-evaluator/src/native.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/native.rs
+++ b/crates/jrsonnet-evaluator/src/native.rs
@@ -1,27 +1,27 @@
 #![allow(clippy::type_complexity)]
 
+use crate::gc::TraceBox;
 use crate::{error::Result, Val};
-use jrsonnet_gc::Trace;
+use gcmodule::Trace;
 use jrsonnet_parser::ParamsDesc;
 use std::fmt::Debug;
 use std::path::Path;
 use std::rc::Rc;
 
 pub trait NativeCallbackHandler: Trace {
-	fn call(&self, from: Option<Rc<Path>>, args: &[Val]) -> Result<Val>;
+	fn call(&self, from: Rc<Path>, args: &[Val]) -> Result<Val>;
 }
 
 #[derive(Trace)]
-#[trivially_drop]
 pub struct NativeCallback {
 	pub params: ParamsDesc,
-	handler: Box<dyn NativeCallbackHandler>,
+	handler: TraceBox<dyn NativeCallbackHandler>,
 }
 impl NativeCallback {
-	pub fn new(params: ParamsDesc, handler: Box<dyn NativeCallbackHandler>) -> Self {
+	pub fn new(params: ParamsDesc, handler: TraceBox<dyn NativeCallbackHandler>) -> Self {
 		Self { params, handler }
 	}
-	pub fn call(&self, caller: Option<Rc<Path>>, args: &[Val]) -> Result<Val> {
+	pub fn call(&self, caller: Rc<Path>, args: &[Val]) -> Result<Val> {
 		self.handler.call(caller, args)
 	}
 }
modifiedcrates/jrsonnet-evaluator/src/obj.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/obj.rs
+++ b/crates/jrsonnet-evaluator/src/obj.rs
@@ -1,15 +1,15 @@
+use crate::gc::{GcHashMap, GcHashSet, TraceBox};
 use crate::operator::evaluate_add_op;
-use crate::{Bindable, LazyBinding, LazyVal, Result, Val};
-use jrsonnet_gc::{Gc, GcCell, Trace};
+use crate::{cc_ptr_eq, Bindable, LazyBinding, LazyVal, Result, Val};
+use gcmodule::{Cc, Trace};
 use jrsonnet_interner::IStr;
 use jrsonnet_parser::{ExprLocation, Visibility};
-use rustc_hash::{FxHashMap, FxHashSet, FxHasher};
-use std::collections::HashMap;
+use rustc_hash::FxHashMap;
+use std::cell::RefCell;
+use std::fmt::Debug;
 use std::hash::{Hash, Hasher};
-use std::{fmt::Debug, hash::BuildHasherDefault};
 
 #[derive(Debug, Trace)]
-#[trivially_drop]
 pub struct ObjMember {
 	pub add: bool,
 	pub visibility: Visibility,
@@ -24,19 +24,18 @@
 // Field => This
 type CacheKey = (IStr, ObjValue);
 #[derive(Trace)]
-#[trivially_drop]
+#[force_tracking]
 pub struct ObjValueInternals {
 	super_obj: Option<ObjValue>,
-	assertions: Gc<Vec<Box<dyn ObjectAssertion>>>,
-	assertions_ran: GcCell<FxHashSet<ObjValue>>,
+	assertions: Cc<Vec<TraceBox<dyn ObjectAssertion>>>,
+	assertions_ran: RefCell<GcHashSet<ObjValue>>,
 	this_obj: Option<ObjValue>,
-	this_entries: Gc<FxHashMap<IStr, ObjMember>>,
-	value_cache: GcCell<FxHashMap<CacheKey, Option<Val>>>,
+	this_entries: Cc<GcHashMap<IStr, ObjMember>>,
+	value_cache: RefCell<GcHashMap<CacheKey, Option<Val>>>,
 }
 
 #[derive(Clone, Trace)]
-#[trivially_drop]
-pub struct ObjValue(pub(crate) Gc<ObjValueInternals>);
+pub struct ObjValue(pub(crate) Cc<ObjValueInternals>);
 impl Debug for ObjValue {
 	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 		if let Some(super_obj) = self.0.super_obj.as_ref() {
@@ -65,20 +64,20 @@
 impl ObjValue {
 	pub fn new(
 		super_obj: Option<Self>,
-		this_entries: Gc<FxHashMap<IStr, ObjMember>>,
-		assertions: Gc<Vec<Box<dyn ObjectAssertion>>>,
+		this_entries: Cc<GcHashMap<IStr, ObjMember>>,
+		assertions: Cc<Vec<TraceBox<dyn ObjectAssertion>>>,
 	) -> Self {
-		Self(Gc::new(ObjValueInternals {
+		Self(Cc::new(ObjValueInternals {
 			super_obj,
 			assertions,
-			assertions_ran: GcCell::new(FxHashSet::default()),
+			assertions_ran: RefCell::new(GcHashSet::new()),
 			this_obj: None,
 			this_entries,
-			value_cache: GcCell::new(FxHashMap::default()),
+			value_cache: RefCell::new(GcHashMap::new()),
 		}))
 	}
 	pub fn new_empty() -> Self {
-		Self::new(None, Gc::new(FxHashMap::default()), Gc::new(Vec::new()))
+		Self::new(None, Cc::new(GcHashMap::new()), Cc::new(Vec::new()))
 	}
 	pub fn extend_from(&self, super_obj: Self) -> Self {
 		match &self.0.super_obj {
@@ -95,13 +94,13 @@
 		}
 	}
 	pub fn with_this(&self, this_obj: Self) -> Self {
-		Self(Gc::new(ObjValueInternals {
+		Self(Cc::new(ObjValueInternals {
 			super_obj: self.0.super_obj.clone(),
 			assertions: self.0.assertions.clone(),
-			assertions_ran: GcCell::new(FxHashSet::default()),
+			assertions_ran: RefCell::new(GcHashSet::new()),
 			this_obj: Some(this_obj),
 			this_entries: self.0.this_entries.clone(),
-			value_cache: GcCell::new(FxHashMap::default()),
+			value_cache: RefCell::new(GcHashMap::new()),
 		}))
 	}
 
@@ -117,14 +116,14 @@
 	}
 
 	/// Run callback for every field found in object
-	pub(crate) fn enum_fields(&self, handler: &mut impl FnMut(&IStr, &Visibility) -> bool) -> bool {
+	pub(crate) fn enum_fields(&self, handler: &mut impl FnMut(&IStr, &ObjMember) -> bool) -> bool {
 		if let Some(s) = &self.0.super_obj {
 			if s.enum_fields(handler) {
 				return true;
 			}
 		}
 		for (name, member) in self.0.this_entries.iter() {
-			if handler(name, &member.visibility) {
+			if handler(name, &member) {
 				return true;
 			}
 		}
@@ -133,8 +132,8 @@
 
 	pub fn fields_visibility(&self) -> FxHashMap<IStr, bool> {
 		let mut out = FxHashMap::default();
-		self.enum_fields(&mut |name, visibility| {
-			match visibility {
+		self.enum_fields(&mut |name, member| {
+			match member.visibility {
 				Visibility::Normal => {
 					let entry = out.entry(name.to_owned());
 					entry.or_insert(true);
@@ -211,9 +210,9 @@
 	}
 
 	pub fn extend_with_field(self, key: IStr, value: ObjMember) -> Self {
-		let mut new = FxHashMap::with_capacity_and_hasher(1, BuildHasherDefault::default());
+		let mut new = GcHashMap::with_capacity(1);
 		new.insert(key, value);
-		Self::new(Some(self), Gc::new(new), Gc::new(Vec::new()))
+		Self::new(Some(self), Cc::new(new), Cc::new(Vec::new()))
 	}
 
 	fn get_raw(&self, key: IStr, real_this: Option<&Self>) -> Result<Option<Val>> {
@@ -270,13 +269,13 @@
 	}
 
 	pub fn ptr_eq(a: &Self, b: &Self) -> bool {
-		Gc::ptr_eq(&a.0, &b.0)
+		cc_ptr_eq(&a.0, &b.0)
 	}
 }
 
 impl PartialEq for ObjValue {
 	fn eq(&self, other: &Self) -> bool {
-		Gc::ptr_eq(&self.0, &other.0)
+		cc_ptr_eq(&self.0, &other.0)
 	}
 }
 
@@ -289,8 +288,8 @@
 
 pub struct ObjValueBuilder {
 	super_obj: Option<ObjValue>,
-	map: FxHashMap<IStr, ObjMember>,
-	assertions: Vec<Box<dyn ObjectAssertion>>,
+	map: GcHashMap<IStr, ObjMember>,
+	assertions: Vec<TraceBox<dyn ObjectAssertion>>,
 }
 impl ObjValueBuilder {
 	pub fn new() -> Self {
@@ -299,10 +298,7 @@
 	pub fn with_capacity(capacity: usize) -> Self {
 		Self {
 			super_obj: None,
-			map: HashMap::with_capacity_and_hasher(
-				capacity,
-				BuildHasherDefault::<FxHasher>::default(),
-			),
+			map: GcHashMap::with_capacity(capacity),
 			assertions: Vec::new(),
 		}
 	}
@@ -315,7 +311,7 @@
 		self
 	}
 
-	pub fn assert(&mut self, assertion: Box<dyn ObjectAssertion>) -> &mut Self {
+	pub fn assert(&mut self, assertion: TraceBox<dyn ObjectAssertion>) -> &mut Self {
 		self.assertions.push(assertion);
 		self
 	}
@@ -330,7 +326,7 @@
 	}
 
 	pub fn build(self) -> ObjValue {
-		ObjValue::new(self.super_obj, Gc::new(self.map), Gc::new(self.assertions))
+		ObjValue::new(self.super_obj, Cc::new(self.map), Cc::new(self.assertions))
 	}
 }
 impl Default for ObjValueBuilder {
@@ -364,15 +360,15 @@
 	pub fn hide(self) -> Self {
 		self.with_visibility(Visibility::Hidden)
 	}
-	pub fn with_location(mut self, location: Option<ExprLocation>) -> Self {
-		self.location = location;
+	pub fn with_location(mut self, location: ExprLocation) -> Self {
+		self.location = Some(location);
 		self
 	}
 	pub fn value(self, value: Val) -> &'v mut ObjValueBuilder {
 		self.binding(LazyBinding::Bound(LazyVal::new_resolved(value)))
 	}
-	pub fn bindable(self, bindable: Box<dyn Bindable>) -> &'v mut ObjValueBuilder {
-		self.binding(LazyBinding::Bindable(Gc::new(bindable)))
+	pub fn bindable(self, bindable: TraceBox<dyn Bindable>) -> &'v mut ObjValueBuilder {
+		self.binding(LazyBinding::Bindable(Cc::new(bindable)))
 	}
 	pub fn binding(self, binding: LazyBinding) -> &'v mut ObjValueBuilder {
 		self.value.map.insert(
modifiedcrates/jrsonnet-evaluator/src/trace/mod.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/trace/mod.rs
+++ b/crates/jrsonnet-evaluator/src/trace/mod.rs
@@ -119,16 +119,21 @@
 			.trace()
 			.0
 			.iter()
-			.map(|el| {
-				el.location.as_ref().map(|l| {
-					use std::fmt::Write;
-					let mut resolved_path = self.resolver.resolve(&l.0);
+			.map(|el| &el.location)
+			.map(|location| {
+				use std::fmt::Write;
+				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(&l.0, &[l.1, l.2]);
+					let location = evaluation_state
+						.map_source_locations(&location.0, &[location.1, location.2]);
 					write!(resolved_path, ":").unwrap();
 					print_code_location(&mut resolved_path, &location[0], &location[1]).unwrap();
-					resolved_path
-				})
+					write!(resolved_path, ":").unwrap();
+					Some(resolved_path)
+				} else {
+					None
+				}
 			})
 			.collect::<Vec<_>>();
 		let align = file_names
@@ -139,15 +144,19 @@
 			.unwrap_or(0);
 		for (el, file) in error.trace().0.iter().zip(file_names) {
 			writeln!(out)?;
-			write!(
-				out,
-				"{:<p$}{:<w$}: {}",
-				"",
-				file.unwrap_or_else(|| "".to_owned()),
-				el.desc,
-				p = self.padding,
-				w = align
-			)?;
+			if let Some(file) = file {
+				write!(
+					out,
+					"{:<p$}{:<w$} {}",
+					"",
+					file,
+					el.desc,
+					p = self.padding,
+					w = align
+				)?;
+			} else {
+				write!(out, "{:<p$}{}", "", el.desc, p = self.padding,)?;
+			}
 		}
 		Ok(())
 	}
@@ -178,7 +187,7 @@
 					start_end[0].column,
 				)?;
 			} else {
-				write!(out, "    at {}", desc,)?;
+				write!(out, "    during {}", desc)?;
 			}
 		}
 		Ok(())
@@ -206,25 +215,21 @@
 		} = error.error()
 		{
 			writeln!(out)?;
-			let mut offset = error.location.offset;
-			if offset >= source_code.len() {
-				offset = source_code.len() - 1;
-			}
-			let mut location = offset_to_location(source_code, &[offset])
+			let offset = error.location.offset;
+			let location = offset_to_location(source_code, &[offset])
 				.into_iter()
 				.next()
 				.unwrap();
-			if location.column >= 1 {
-				location.column -= 1;
-			}
+			let mut end_location = location.clone();
+			end_location.offset += 1;
 
 			self.print_snippet(
 				out,
 				source_code,
 				path,
 				&location,
-				&location,
-				"^ syntax error",
+				&end_location,
+				"syntax error",
 			)?;
 		}
 		let trace = &error.trace();
@@ -289,7 +294,7 @@
 					annotation_type: AnnotationType::Error,
 					range: (
 						start.offset - start.line_start_offset,
-						end.offset - start.line_start_offset,
+						(end.offset - start.line_start_offset).min(source_fragment.len()),
 					),
 				}],
 			}],
modifiedcrates/jrsonnet-evaluator/src/typed.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/typed.rs
+++ b/crates/jrsonnet-evaluator/src/typed.rs
@@ -1,11 +1,7 @@
 use std::{fmt::Display, rc::Rc};
 
-use crate::{
-	error::{Error, LocError, Result},
-	push_frame, Val,
-};
-use jrsonnet_gc::Trace;
-use jrsonnet_parser::ExprLocation;
+use crate::{Val, error::{Error, LocError, Result}, push_description_frame};
+use gcmodule::Trace;
 use jrsonnet_types::{ComplexValType, ValType};
 use thiserror::Error;
 
@@ -22,12 +18,11 @@
 }
 
 #[derive(Debug, Error, Clone, Trace)]
-#[trivially_drop]
 pub enum TypeError {
 	#[error("expected {0}, got {1}")]
 	ExpectedGot(ComplexValType, ValType),
 	#[error("missing property {0} from {1:?}")]
-	MissingProperty(Rc<str>, ComplexValType),
+	MissingProperty(#[skip_trace] Rc<str>, ComplexValType),
 	#[error("every failed from {0}:\n{1}")]
 	UnionFailed(ComplexValType, TypeLocErrorList),
 	#[error(
@@ -44,7 +39,6 @@
 }
 
 #[derive(Debug, Clone, Trace)]
-#[trivially_drop]
 pub struct TypeLocError(Box<TypeError>, ValuePathStack);
 impl From<TypeError> for TypeLocError {
 	fn from(e: TypeError) -> Self {
@@ -67,7 +61,6 @@
 }
 
 #[derive(Debug, Clone, Trace)]
-#[trivially_drop]
 pub struct TypeLocErrorList(Vec<TypeLocError>);
 impl Display for TypeLocErrorList {
 	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
@@ -97,13 +90,12 @@
 	}
 }
 
-fn push_type(
-	location: Option<&ExprLocation>,
+fn push_type_description(
 	error_reason: impl Fn() -> String,
 	path: impl Fn() -> ValuePathItem,
 	item: impl Fn() -> Result<()>,
 ) -> Result<()> {
-	push_frame(location, error_reason, || match item() {
+	push_description_frame(error_reason, || match item() {
 		Ok(_) => Ok(()),
 		Err(mut e) => {
 			if let Error::TypeError(e) = &mut e.error_mut() {
@@ -131,9 +123,8 @@
 }
 
 #[derive(Clone, Debug, Trace)]
-#[trivially_drop]
 enum ValuePathItem {
-	Field(Rc<str>),
+	Field(#[skip_trace] Rc<str>),
 	Index(u64),
 }
 impl Display for ValuePathItem {
@@ -147,7 +138,6 @@
 }
 
 #[derive(Clone, Debug, Trace)]
-#[trivially_drop]
 struct ValuePathStack(Vec<ValuePathItem>);
 impl Display for ValuePathStack {
 	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
@@ -183,8 +173,7 @@
 			Self::Array(elem_type) => match value {
 				Val::Arr(a) => {
 					for (i, item) in a.iter().enumerate() {
-						push_type(
-							None,
+						push_type_description(
 							|| format!("array index {}", i),
 							|| ValuePathItem::Index(i as u64),
 							|| elem_type.check(&item.clone()?),
@@ -197,8 +186,7 @@
 			Self::ArrayRef(elem_type) => match value {
 				Val::Arr(a) => {
 					for (i, item) in a.iter().enumerate() {
-						push_type(
-							None,
+						push_type_description(
 							|| format!("array index {}", i),
 							|| ValuePathItem::Index(i as u64),
 							|| elem_type.check(&item.clone()?),
@@ -212,8 +200,7 @@
 				Val::Obj(obj) => {
 					for (k, v) in elems.iter() {
 						if let Some(got_v) = obj.get((*k).into())? {
-							push_type(
-								None,
+							push_type_description(
 								|| format!("property {}", k),
 								|| ValuePathItem::Field((*k).into()),
 								|| v.check(&got_v),
modifiedcrates/jrsonnet-evaluator/src/val.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/val.rs
+++ b/crates/jrsonnet-evaluator/src/val.rs
@@ -6,40 +6,40 @@
 			ManifestYamlOptions,
 		},
 	},
+	cc_ptr_eq,
 	error::{Error::*, LocError},
 	evaluate,
 	function::{parse_function_call, parse_function_call_map, place_args},
+	gc::TraceBox,
 	native::NativeCallback,
 	throw, Context, ObjValue, Result,
 };
-use jrsonnet_gc::{Gc, GcCell, Trace};
+use gcmodule::{Cc, Trace};
 use jrsonnet_interner::IStr;
 use jrsonnet_parser::{ArgsDesc, ExprLocation, LocExpr, ParamsDesc};
 use jrsonnet_types::ValType;
-use std::{collections::HashMap, fmt::Debug, rc::Rc};
+use std::{cell::RefCell, collections::HashMap, fmt::Debug, rc::Rc};
 
 pub trait LazyValValue: Trace {
 	fn get(self: Box<Self>) -> Result<Val>;
 }
 
 #[derive(Trace)]
-#[trivially_drop]
 enum LazyValInternals {
 	Computed(Val),
 	Errored(LocError),
-	Waiting(Box<dyn LazyValValue>),
+	Waiting(TraceBox<dyn LazyValValue>),
 	Pending,
 }
 
 #[derive(Clone, Trace)]
-#[trivially_drop]
-pub struct LazyVal(Gc<GcCell<LazyValInternals>>);
+pub struct LazyVal(Cc<RefCell<LazyValInternals>>);
 impl LazyVal {
-	pub fn new(f: Box<dyn LazyValValue>) -> Self {
-		Self(Gc::new(GcCell::new(LazyValInternals::Waiting(f))))
+	pub fn new(f: TraceBox<dyn LazyValValue>) -> Self {
+		Self(Cc::new(RefCell::new(LazyValInternals::Waiting(f))))
 	}
 	pub fn new_resolved(val: Val) -> Self {
-		Self(Gc::new(GcCell::new(LazyValInternals::Computed(val))))
+		Self(Cc::new(RefCell::new(LazyValInternals::Computed(val))))
 	}
 	pub fn evaluate(&self) -> Result<Val> {
 		match &*self.0.borrow() {
@@ -55,7 +55,7 @@
 		} else {
 			unreachable!()
 		};
-		let new_value = match value.get() {
+		let new_value = match value.0.get() {
 			Ok(v) => v,
 			Err(e) => {
 				*self.0.borrow_mut() = LazyValInternals::Errored(e.clone());
@@ -74,12 +74,11 @@
 }
 impl PartialEq for LazyVal {
 	fn eq(&self, other: &Self) -> bool {
-		Gc::ptr_eq(&self.0, &other.0)
+		cc_ptr_eq(&self.0, &other.0)
 	}
 }
 
 #[derive(Debug, PartialEq, Trace)]
-#[trivially_drop]
 pub struct FuncDesc {
 	pub name: IStr,
 	pub ctx: Context,
@@ -88,14 +87,13 @@
 }
 
 #[derive(Debug, Trace)]
-#[trivially_drop]
 pub enum FuncVal {
 	/// Plain function implemented in jsonnet
 	Normal(FuncDesc),
 	/// Standard library function
 	Intrinsic(IStr),
 	/// Library functions implemented in native
-	NativeExt(IStr, Gc<NativeCallback>),
+	NativeExt(IStr, Cc<NativeCallback>),
 }
 
 impl PartialEq for FuncVal {
@@ -122,7 +120,7 @@
 	pub fn evaluate(
 		&self,
 		call_ctx: Context,
-		loc: Option<&ExprLocation>,
+		loc: &ExprLocation,
 		args: &ArgsDesc,
 		tailstrict: bool,
 	) -> Result<Val> {
@@ -145,7 +143,7 @@
 				for p in handler.params.0.iter() {
 					out_args.push(args.binding(p.0.clone())?.evaluate()?);
 				}
-				Ok(handler.call(loc.map(|l| l.0.clone()), &out_args)?)
+				Ok(handler.call(loc.0.clone(), &out_args)?)
 			}
 		}
 	}
@@ -194,15 +192,15 @@
 }
 
 #[derive(Debug, Clone, Trace)]
-#[trivially_drop]
+#[force_tracking]
 pub enum ArrValue {
-	Lazy(Gc<Vec<LazyVal>>),
-	Eager(Gc<Vec<Val>>),
+	Lazy(Cc<Vec<LazyVal>>),
+	Eager(Cc<Vec<Val>>),
 	Extended(Box<(Self, Self)>),
 }
 impl ArrValue {
 	pub fn new_eager() -> Self {
-		Self::Eager(Gc::new(Vec::new()))
+		Self::Eager(Cc::new(Vec::new()))
 	}
 
 	pub fn len(&self) -> usize {
@@ -253,14 +251,14 @@
 		}
 	}
 
-	pub fn evaluated(&self) -> Result<Gc<Vec<Val>>> {
+	pub fn evaluated(&self) -> Result<Cc<Vec<Val>>> {
 		Ok(match self {
 			Self::Lazy(vec) => {
 				let mut out = Vec::with_capacity(vec.len());
 				for item in vec.iter() {
 					out.push(item.evaluate()?);
 				}
-				Gc::new(out)
+				Cc::new(out)
 			}
 			Self::Eager(vec) => vec.clone(),
 			Self::Extended(_v) => {
@@ -268,7 +266,7 @@
 				for item in self.iter() {
 					out.push(item?);
 				}
-				Gc::new(out)
+				Cc::new(out)
 			}
 		})
 	}
@@ -294,12 +292,12 @@
 			Self::Lazy(vec) => {
 				let mut out = (&vec as &Vec<_>).clone();
 				out.reverse();
-				Self::Lazy(Gc::new(out))
+				Self::Lazy(Cc::new(out))
 			}
 			Self::Eager(vec) => {
 				let mut out = (&vec as &Vec<_>).clone();
 				out.reverse();
-				Self::Eager(Gc::new(out))
+				Self::Eager(Cc::new(out))
 			}
 			Self::Extended(b) => Self::Extended(Box::new((b.1.reversed(), b.0.reversed()))),
 		}
@@ -312,7 +310,7 @@
 			out.push(mapper(value?)?);
 		}
 
-		Ok(Self::Eager(Gc::new(out)))
+		Ok(Self::Eager(Cc::new(out)))
 	}
 
 	pub fn filter(self, filter: impl Fn(&Val) -> Result<bool>) -> Result<Self> {
@@ -325,13 +323,13 @@
 			}
 		}
 
-		Ok(Self::Eager(Gc::new(out)))
+		Ok(Self::Eager(Cc::new(out)))
 	}
 
 	pub fn ptr_eq(a: &Self, b: &Self) -> bool {
 		match (a, b) {
-			(Self::Lazy(a), Self::Lazy(b)) => Gc::ptr_eq(a, b),
-			(Self::Eager(a), Self::Eager(b)) => Gc::ptr_eq(a, b),
+			(Self::Lazy(a), Self::Lazy(b)) => cc_ptr_eq(a, b),
+			(Self::Eager(a), Self::Eager(b)) => cc_ptr_eq(a, b),
 			_ => false,
 		}
 	}
@@ -339,13 +337,13 @@
 
 impl From<Vec<LazyVal>> for ArrValue {
 	fn from(v: Vec<LazyVal>) -> Self {
-		Self::Lazy(Gc::new(v))
+		Self::Lazy(Cc::new(v))
 	}
 }
 
 impl From<Vec<Val>> for ArrValue {
 	fn from(v: Vec<Val>) -> Self {
-		Self::Eager(Gc::new(v))
+		Self::Eager(Cc::new(v))
 	}
 }
 
@@ -355,7 +353,6 @@
 }
 
 #[derive(Debug, Clone, Trace)]
-#[trivially_drop]
 pub enum Val {
 	Bool(bool),
 	Null,
@@ -363,7 +360,7 @@
 	Num(f64),
 	Arr(ArrValue),
 	Obj(ObjValue),
-	Func(Gc<FuncVal>),
+	Func(Cc<FuncVal>),
 }
 
 macro_rules! matches_unwrap {
@@ -402,7 +399,7 @@
 	pub fn unwrap_arr(self) -> Result<ArrValue> {
 		Ok(matches_unwrap!(self, Self::Arr(v), v))
 	}
-	pub fn unwrap_func(self) -> Result<Gc<FuncVal>> {
+	pub fn unwrap_func(self) -> Result<Cc<FuncVal>> {
 		Ok(matches_unwrap!(self, Self::Func(v), v))
 	}
 	pub fn try_cast_bool(self, context: &'static str) -> Result<bool> {
modifiedcrates/jrsonnet-interner/Cargo.tomldiffbeforeafterboth
--- a/crates/jrsonnet-interner/Cargo.toml
+++ b/crates/jrsonnet-interner/Cargo.toml
@@ -9,4 +9,4 @@
 [dependencies]
 serde = { version = "1.0" }
 rustc-hash = "1.1.0"
-jrsonnet-gc = { version = "0.4.2", features = ["derive"] }
+gcmodule = { git = "https://github.com/CertainLach/gcmodule", branch = "jrsonnet" }
modifiedcrates/jrsonnet-interner/src/lib.rsdiffbeforeafterboth
--- a/crates/jrsonnet-interner/src/lib.rs
+++ b/crates/jrsonnet-interner/src/lib.rs
@@ -1,4 +1,4 @@
-use jrsonnet_gc::{unsafe_empty_trace, Finalize, Trace};
+use gcmodule::Trace;
 use rustc_hash::FxHashMap;
 use serde::{Deserialize, Serialize};
 use std::{
@@ -11,9 +11,10 @@
 
 #[derive(Clone, PartialOrd, Ord, Eq)]
 pub struct IStr(Rc<str>);
-impl Finalize for IStr {}
-unsafe impl Trace for IStr {
-	unsafe_empty_trace!();
+impl Trace for IStr {
+	fn is_type_tracked() -> bool {
+		false
+	}
 }
 
 impl Deref for IStr {
modifiedcrates/jrsonnet-parser/Cargo.tomldiffbeforeafterboth
--- a/crates/jrsonnet-parser/Cargo.toml
+++ b/crates/jrsonnet-parser/Cargo.toml
@@ -15,10 +15,9 @@
 jrsonnet-interner = { path = "../jrsonnet-interner", version = "0.4.2" }
 
 peg = "0.7.0"
-unescape = "0.1.0"
 
 serde = { version = "1.0", features = ["derive", "rc"], optional = true }
-jrsonnet-gc = { version = "0.4.2", features = ["derive"] }
+gcmodule = { git = "https://github.com/CertainLach/gcmodule", branch = "jrsonnet" }
 
 [dev-dependencies]
 jrsonnet-stdlib = { path = "../jrsonnet-stdlib", version = "0.4.2" }
modifiedcrates/jrsonnet-parser/src/expr.rsdiffbeforeafterboth
--- a/crates/jrsonnet-parser/src/expr.rs
+++ b/crates/jrsonnet-parser/src/expr.rs
@@ -1,4 +1,4 @@
-use jrsonnet_gc::{unsafe_empty_trace, Finalize, Trace};
+use gcmodule::Trace;
 use jrsonnet_interner::IStr;
 #[cfg(feature = "deserialize")]
 use serde::Deserialize;
@@ -14,7 +14,6 @@
 #[cfg_attr(feature = "serialize", derive(Serialize))]
 #[cfg_attr(feature = "deserialize", derive(Deserialize))]
 #[derive(Debug, PartialEq, Trace)]
-#[trivially_drop]
 pub enum FieldName {
 	/// {fixed: 2}
 	Fixed(IStr),
@@ -25,7 +24,6 @@
 #[cfg_attr(feature = "serialize", derive(Serialize))]
 #[cfg_attr(feature = "deserialize", derive(Deserialize))]
 #[derive(Debug, Clone, Copy, PartialEq, Trace)]
-#[trivially_drop]
 pub enum Visibility {
 	/// :
 	Normal,
@@ -44,13 +42,11 @@
 #[cfg_attr(feature = "serialize", derive(Serialize))]
 #[cfg_attr(feature = "deserialize", derive(Deserialize))]
 #[derive(Clone, Debug, PartialEq, Trace)]
-#[trivially_drop]
 pub struct AssertStmt(pub LocExpr, pub Option<LocExpr>);
 
 #[cfg_attr(feature = "serialize", derive(Serialize))]
 #[cfg_attr(feature = "deserialize", derive(Deserialize))]
 #[derive(Debug, PartialEq, Trace)]
-#[trivially_drop]
 pub struct FieldMember {
 	pub name: FieldName,
 	pub plus: bool,
@@ -62,7 +58,6 @@
 #[cfg_attr(feature = "serialize", derive(Serialize))]
 #[cfg_attr(feature = "deserialize", derive(Deserialize))]
 #[derive(Debug, PartialEq, Trace)]
-#[trivially_drop]
 pub enum Member {
 	Field(FieldMember),
 	BindStmt(BindSpec),
@@ -72,7 +67,6 @@
 #[cfg_attr(feature = "serialize", derive(Serialize))]
 #[cfg_attr(feature = "deserialize", derive(Deserialize))]
 #[derive(Debug, Clone, Copy, PartialEq, Trace)]
-#[trivially_drop]
 pub enum UnaryOpType {
 	Plus,
 	Minus,
@@ -99,7 +93,6 @@
 #[cfg_attr(feature = "serialize", derive(Serialize))]
 #[cfg_attr(feature = "deserialize", derive(Deserialize))]
 #[derive(Debug, Clone, Copy, PartialEq, Trace)]
-#[trivially_drop]
 pub enum BinaryOpType {
 	Mul,
 	Div,
@@ -167,21 +160,13 @@
 #[cfg_attr(feature = "serialize", derive(Serialize))]
 #[cfg_attr(feature = "deserialize", derive(Deserialize))]
 #[derive(Debug, PartialEq, Trace)]
-#[trivially_drop]
 pub struct Param(pub IStr, pub Option<LocExpr>);
 
 /// Defined function parameters
 #[cfg_attr(feature = "serialize", derive(Serialize))]
 #[cfg_attr(feature = "deserialize", derive(Deserialize))]
-#[derive(Debug, Clone, PartialEq)]
+#[derive(Debug, Clone, PartialEq, Trace)]
 pub struct ParamsDesc(pub Rc<Vec<Param>>);
-
-/// Safety:
-/// AST is acyclic, and there should be no gc pointers
-unsafe impl Trace for ParamsDesc {
-	unsafe_empty_trace!();
-}
-impl Finalize for ParamsDesc {}
 
 impl Deref for ParamsDesc {
 	type Target = Vec<Param>;
@@ -193,7 +178,6 @@
 #[cfg_attr(feature = "serialize", derive(Serialize))]
 #[cfg_attr(feature = "deserialize", derive(Deserialize))]
 #[derive(Debug, PartialEq, Trace)]
-#[trivially_drop]
 pub struct ArgsDesc {
 	pub unnamed: Vec<LocExpr>,
 	pub named: Vec<(IStr, LocExpr)>,
@@ -207,7 +191,6 @@
 #[cfg_attr(feature = "serialize", derive(Serialize))]
 #[cfg_attr(feature = "deserialize", derive(Deserialize))]
 #[derive(Debug, Clone, PartialEq, Trace)]
-#[trivially_drop]
 pub struct BindSpec {
 	pub name: IStr,
 	pub params: Option<ParamsDesc>,
@@ -217,19 +200,16 @@
 #[cfg_attr(feature = "serialize", derive(Serialize))]
 #[cfg_attr(feature = "deserialize", derive(Deserialize))]
 #[derive(Debug, PartialEq, Trace)]
-#[trivially_drop]
 pub struct IfSpecData(pub LocExpr);
 
 #[cfg_attr(feature = "serialize", derive(Serialize))]
 #[cfg_attr(feature = "deserialize", derive(Deserialize))]
 #[derive(Debug, PartialEq, Trace)]
-#[trivially_drop]
 pub struct ForSpecData(pub IStr, pub LocExpr);
 
 #[cfg_attr(feature = "serialize", derive(Serialize))]
 #[cfg_attr(feature = "deserialize", derive(Deserialize))]
 #[derive(Debug, PartialEq, Trace)]
-#[trivially_drop]
 pub enum CompSpec {
 	IfSpec(IfSpecData),
 	ForSpec(ForSpecData),
@@ -238,7 +218,6 @@
 #[cfg_attr(feature = "serialize", derive(Serialize))]
 #[cfg_attr(feature = "deserialize", derive(Deserialize))]
 #[derive(Debug, PartialEq, Trace)]
-#[trivially_drop]
 pub struct ObjComp {
 	pub pre_locals: Vec<BindSpec>,
 	pub key: LocExpr,
@@ -251,7 +230,6 @@
 #[cfg_attr(feature = "serialize", derive(Serialize))]
 #[cfg_attr(feature = "deserialize", derive(Deserialize))]
 #[derive(Debug, PartialEq, Trace)]
-#[trivially_drop]
 pub enum ObjBody {
 	MemberList(Vec<Member>),
 	ObjComp(ObjComp),
@@ -260,7 +238,6 @@
 #[cfg_attr(feature = "serialize", derive(Serialize))]
 #[cfg_attr(feature = "deserialize", derive(Deserialize))]
 #[derive(Debug, PartialEq, Clone, Copy, Trace)]
-#[trivially_drop]
 pub enum LiteralType {
 	This,
 	Super,
@@ -273,7 +250,6 @@
 #[cfg_attr(feature = "serialize", derive(Serialize))]
 #[cfg_attr(feature = "deserialize", derive(Deserialize))]
 #[derive(Debug, PartialEq, Trace)]
-#[trivially_drop]
 pub struct SliceDesc {
 	pub start: Option<LocExpr>,
 	pub end: Option<LocExpr>,
@@ -284,7 +260,6 @@
 #[cfg_attr(feature = "serialize", derive(Serialize))]
 #[cfg_attr(feature = "deserialize", derive(Deserialize))]
 #[derive(Debug, PartialEq, Trace)]
-#[trivially_drop]
 pub enum Expr {
 	Literal(LiteralType),
 
@@ -354,7 +329,7 @@
 #[cfg_attr(feature = "serialize", derive(Serialize))]
 #[cfg_attr(feature = "deserialize", derive(Deserialize))]
 #[derive(Clone, PartialEq, Trace)]
-#[trivially_drop]
+#[skip_trace]
 pub struct ExprLocation(pub Rc<Path>, pub usize, pub usize);
 impl ExprLocation {
 	pub fn belongs_to(&self, other: &ExprLocation) -> bool {
@@ -371,14 +346,8 @@
 /// Holds AST expression and its location in source file
 #[cfg_attr(feature = "serialize", derive(Serialize))]
 #[cfg_attr(feature = "deserialize", derive(Deserialize))]
-#[derive(Clone, PartialEq)]
-pub struct LocExpr(pub Rc<Expr>, pub Option<ExprLocation>);
-/// Safety:
-/// AST is acyclic, and there should be no gc pointers
-unsafe impl Trace for LocExpr {
-	unsafe_empty_trace!();
-}
-impl Finalize for LocExpr {}
+#[derive(Clone, PartialEq, Trace)]
+pub struct LocExpr(pub Rc<Expr>, pub ExprLocation);
 
 impl Debug for LocExpr {
 	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
@@ -387,24 +356,7 @@
 		} else {
 			write!(f, "{:?}", self.0)?;
 		}
-		if let Some(loc) = &self.1 {
-			write!(f, " from {:?}", loc)?;
-		}
+		write!(f, " from {:?}", self.1)?;
 		Ok(())
 	}
-}
-
-/// Creates LocExpr from Expr and ExprLocation components
-#[macro_export]
-macro_rules! loc_expr {
-	($expr:expr, $need_loc:expr,($name:expr, $start:expr, $end:expr)) => {
-		LocExpr(
-			std::rc::Rc::new($expr),
-			if $need_loc {
-				Some(ExprLocation($name, $start, $end))
-			} else {
-				None
-			},
-		)
-	};
 }
modifiedcrates/jrsonnet-parser/src/lib.rsdiffbeforeafterboth
--- a/crates/jrsonnet-parser/src/lib.rs
+++ b/crates/jrsonnet-parser/src/lib.rs
@@ -9,9 +9,9 @@
 pub use expr::*;
 pub use jrsonnet_interner::IStr;
 pub use peg;
+mod unescape;
 
 pub struct ParserSettings {
-	pub loc_data: bool,
 	pub file_name: Rc<Path>,
 }
 
@@ -103,10 +103,20 @@
 			  lines:("\n" {"\n"} / [' ' | '\t']*<{prefix.len()}> s:whole_line() {s})*
 			  [' ' | '\t']*<, {prefix.len() - 1}> "|||"
 			  {let mut l = empty_lines.to_owned(); l.push_str(first_line); l.extend(lines); l}
+
+		rule hex_char()
+			= quiet! { ['0'..='9' | 'a'..='f' | 'A'..='F'] } / expected!("<hex char>")
+
+		rule string_char(c: rule<()>)
+			= (!['\\']!c()[_])+
+			/ "\\\\"
+			/ "\\u" hex_char() hex_char() hex_char() hex_char()
+			/ "\\x" hex_char() hex_char()
+			/ ['\\'] (quiet! { ['b' | 'f' | 'n' | 'r' | 't'] / c() } / expected!("<escape character>"))
 		pub rule string() -> String
-			= quiet!{ "\"" str:$(("\\\"" / "\\\\" / (!['"'][_]))*) "\"" {unescape::unescape(str).unwrap()}
-			/ "'" str:$(("\\'" / "\\\\" / (!['\''][_]))*) "'" {unescape::unescape(str).unwrap()}
-			/ "@'" str:$(("''" / (!['\''][_]))*) "'" {str.replace("''", "'")}
+			= ['"'] str:$(string_char(<['"']>)*) ['"'] {? unescape::unescape(str).ok_or("<escaped string>")}
+			/ ['\''] str:$(string_char(<['\'']>)*) ['\''] {? unescape::unescape(str).ok_or("<escaped string>")}
+			/ quiet!{ "@'" str:$(("''" / (!['\''][_]))*) "'" {str.replace("''", "'")}
 			/ "@\"" str:$(("\"\"" / (!['"'][_]))*) "\"" {str.replace("\"\"", "\"")}
 			/ string_block() } / expected!("<string>")
 
@@ -177,6 +187,8 @@
 			= n:number() { expr::Expr::Num(n) }
 		pub rule var_expr(s: &ParserSettings) -> Expr
 			= n:$(id()) { expr::Expr::Var(n.into()) }
+		pub rule id_loc(s: &ParserSettings) -> LocExpr
+			= a:position!() n:$(id()) b:position!() { LocExpr(Rc::new(expr::Expr::Str(n.into())), ExprLocation(s.file_name.clone(), a,b)) }
 		pub rule if_then_else_expr(s: &ParserSettings) -> Expr
 			= cond:ifspec(s) _ keyword("then") _ cond_then:expr(s) cond_else:(_ keyword("else") _ e:expr(s) {e})? {Expr::IfElse{
 				cond,
@@ -240,7 +252,7 @@
 		use UnaryOpType::*;
 		rule expr(s: &ParserSettings) -> LocExpr
 			= precedence! {
-				start:position!() v:@ end:position!() { loc_expr!(v, s.loc_data, (s.file_name.clone(), start, end)) }
+				start:position!() v:@ end:position!() { LocExpr(Rc::new(v), ExprLocation(s.file_name.clone(), start, end)) }
 				--
 				a:(@) _ binop(<"||">) _ b:@ {expr_bin!(a Or b)}
 				--
@@ -276,7 +288,7 @@
 						unaryop(<"~">) _ b:@ {expr_un!(BitNot b)}
 				--
 				a:(@) _ "[" _ e:slice_desc(s) _ "]" {Expr::Slice(a, e)}
-				a:(@) _ "." _ e:$(id()) {Expr::Index(a, el!(Expr::Str(e.into())))}
+				a:(@) _ "." _ a:position!() e:id_loc(s) b:position!() {Expr::Index(a, e)}
 				a:(@) _ "[" _ e:expr(s) _ "]" {Expr::Index(a, e)}
 				a:(@) _ "(" _ args:args(s) _ ")" ts:(_ keyword("tailstrict"))? {Expr::Apply(a, args, ts.is_some())}
 				a:(@) _ "{" _ body:objinside(s) _ "}" {Expr::ObjExtend(a, body)}
@@ -292,13 +304,6 @@
 pub type ParseError = peg::error::ParseError<peg::str::LineCol>;
 pub fn parse(str: &str, settings: &ParserSettings) -> Result<LocExpr, ParseError> {
 	jsonnet_parser::jsonnet(str, settings)
-}
-
-#[macro_export]
-macro_rules! el {
-	($expr:expr) => {
-		LocExpr(std::rc::Rc::new($expr), None)
-	};
 }
 
 #[cfg(test)]
@@ -313,53 +318,39 @@
 			parse(
 				$s,
 				&ParserSettings {
-					loc_data: false,
-					file_name: PathBuf::from("/test.jsonnet").into(),
+					file_name: PathBuf::from("test.jsonnet").into(),
 				},
 			)
 			.unwrap()
 		};
 	}
 
-	macro_rules! el_loc {
-		($expr:expr, $loc:expr$(,)?) => {
-			LocExpr(std::rc::Rc::new($expr), Some($loc))
+	macro_rules! el {
+		($expr:expr, $from:expr, $to:expr$(,)?) => {
+			LocExpr(
+				std::rc::Rc::new($expr),
+				ExprLocation(PathBuf::from("test.jsonnet").into(), $from, $to),
+			)
 		};
-	}
-
-	mod expressions {
-		use super::*;
-
-		pub fn basic_math() -> LocExpr {
-			el!(Expr::BinaryOp(
-				el!(Expr::Num(2.0)),
-				Add,
-				el!(Expr::BinaryOp(
-					el!(Expr::Num(2.0)),
-					Mul,
-					el!(Expr::Num(2.0)),
-				)),
-			))
-		}
 	}
 
 	#[test]
 	fn multiline_string() {
 		assert_eq!(
 			parse!("|||\n    Hello world!\n     a\n|||"),
-			el!(Expr::Str("Hello world!\n a\n".into())),
+			el!(Expr::Str("Hello world!\n a\n".into()), 0, 31),
 		);
 		assert_eq!(
 			parse!("|||\n  Hello world!\n   a\n|||"),
-			el!(Expr::Str("Hello world!\n a\n".into())),
+			el!(Expr::Str("Hello world!\n a\n".into()), 0, 27),
 		);
 		assert_eq!(
 			parse!("|||\n\t\tHello world!\n\t\t\ta\n|||"),
-			el!(Expr::Str("Hello world!\n\ta\n".into())),
+			el!(Expr::Str("Hello world!\n\ta\n".into()), 0, 27),
 		);
 		assert_eq!(
 			parse!("|||\n   Hello world!\n    a\n |||"),
-			el!(Expr::Str("Hello world!\n a\n".into())),
+			el!(Expr::Str("Hello world!\n a\n".into()), 0, 30),
 		);
 	}
 
@@ -376,20 +367,20 @@
 	fn string_escaping() {
 		assert_eq!(
 			parse!(r#""Hello, \"world\"!""#),
-			el!(Expr::Str(r#"Hello, "world"!"#.into())),
+			el!(Expr::Str(r#"Hello, "world"!"#.into()), 0, 19),
 		);
 		assert_eq!(
 			parse!(r#"'Hello \'world\'!'"#),
-			el!(Expr::Str("Hello 'world'!".into())),
+			el!(Expr::Str("Hello 'world'!".into()), 0, 18),
 		);
-		assert_eq!(parse!(r#"'\\\\'"#), el!(Expr::Str("\\\\".into())),);
+		assert_eq!(parse!(r#"'\\\\'"#), el!(Expr::Str("\\\\".into()), 0, 6));
 	}
 
 	#[test]
 	fn string_unescaping() {
 		assert_eq!(
 			parse!(r#""Hello\nWorld""#),
-			el!(Expr::Str("Hello\nWorld".into())),
+			el!(Expr::Str("Hello\nWorld".into()), 0, 14),
 		);
 	}
 
@@ -397,7 +388,7 @@
 	fn string_verbantim() {
 		assert_eq!(
 			parse!(r#"@"Hello\n""World""""#),
-			el!(Expr::Str("Hello\\n\"World\"".into())),
+			el!(Expr::Str("Hello\\n\"World\"".into()), 0, 19),
 		);
 	}
 
@@ -405,49 +396,95 @@
 	fn imports() {
 		assert_eq!(
 			parse!("import \"hello\""),
-			el!(Expr::Import(PathBuf::from("hello"))),
+			el!(Expr::Import(PathBuf::from("hello")), 0, 14),
 		);
 		assert_eq!(
 			parse!("importstr \"garnish.txt\""),
-			el!(Expr::ImportStr(PathBuf::from("garnish.txt")))
+			el!(Expr::ImportStr(PathBuf::from("garnish.txt")), 0, 23)
 		);
 	}
 
 	#[test]
 	fn empty_object() {
-		assert_eq!(parse!("{}"), el!(Expr::Obj(ObjBody::MemberList(vec![]))));
+		assert_eq!(
+			parse!("{}"),
+			el!(Expr::Obj(ObjBody::MemberList(vec![])), 0, 2)
+		);
 	}
 
 	#[test]
 	fn basic_math() {
 		assert_eq!(
 			parse!("2+2*2"),
-			el!(Expr::BinaryOp(
-				el!(Expr::Num(2.0)),
-				Add,
-				el!(Expr::BinaryOp(
-					el!(Expr::Num(2.0)),
-					Mul,
-					el!(Expr::Num(2.0))
-				))
-			))
+			el!(
+				Expr::BinaryOp(
+					el!(Expr::Num(2.0), 0, 1),
+					Add,
+					el!(
+						Expr::BinaryOp(el!(Expr::Num(2.0), 2, 3), Mul, el!(Expr::Num(2.0), 4, 5)),
+						2,
+						5
+					)
+				),
+				0,
+				5
+			)
 		);
 	}
 
 	#[test]
 	fn basic_math_with_indents() {
-		assert_eq!(parse!("2	+ 	  2	  *	2   	"), expressions::basic_math());
+		assert_eq!(
+			parse!("2	+ 	  2	  *	2   	"),
+			el!(
+				Expr::BinaryOp(
+					el!(Expr::Num(2.0), 0, 1),
+					Add,
+					el!(
+						Expr::BinaryOp(el!(Expr::Num(2.0), 7, 8), Mul, el!(Expr::Num(2.0), 13, 14),),
+						7,
+						14
+					),
+				),
+				0,
+				14
+			)
+		);
 	}
 
 	#[test]
 	fn basic_math_parened() {
 		assert_eq!(
 			parse!("2+(2+2*2)"),
-			el!(Expr::BinaryOp(
-				el!(Expr::Num(2.0)),
-				Add,
-				el!(Expr::Parened(expressions::basic_math())),
-			))
+			el!(
+				Expr::BinaryOp(
+					el!(Expr::Num(2.0), 0, 1),
+					Add,
+					el!(
+						Expr::Parened(el!(
+							Expr::BinaryOp(
+								el!(Expr::Num(2.0), 3, 4),
+								Add,
+								el!(
+									Expr::BinaryOp(
+										el!(Expr::Num(2.0), 5, 6),
+										Mul,
+										el!(Expr::Num(2.0), 7, 8),
+									),
+									5,
+									8
+								),
+							),
+							3,
+							8
+						)),
+						2,
+						9
+					),
+				),
+				0,
+				9
+			)
 		);
 	}
 
@@ -456,15 +493,23 @@
 	fn comments() {
 		assert_eq!(
 			parse!("2//comment\n+//comment\n3/*test*/*/*test*/4"),
-			el!(Expr::BinaryOp(
-				el!(Expr::Num(2.0)),
-				Add,
-				el!(Expr::BinaryOp(
-					el!(Expr::Num(3.0)),
-					Mul,
-					el!(Expr::Num(4.0))
-				))
-			))
+			el!(
+				Expr::BinaryOp(
+					el!(Expr::Num(2.0), 0, 1),
+					Add,
+					el!(
+						Expr::BinaryOp(
+							el!(Expr::Num(3.0), 22, 23),
+							Mul,
+							el!(Expr::Num(4.0), 40, 41)
+						),
+						22,
+						41
+					)
+				),
+				0,
+				41
+			)
 		);
 	}
 
@@ -473,11 +518,11 @@
 	fn comment_escaping() {
 		assert_eq!(
 			parse!("2/*\\*/+*/ - 22"),
-			el!(Expr::BinaryOp(
-				el!(Expr::Num(2.0)),
-				Sub,
-				el!(Expr::Num(22.0))
-			))
+			el!(
+				Expr::BinaryOp(el!(Expr::Num(2.0), 0, 1), Sub, el!(Expr::Num(22.0), 12, 14)),
+				0,
+				14
+			)
 		);
 	}
 
@@ -492,27 +537,46 @@
 	#[test]
 	fn array_comp() {
 		use Expr::*;
+		/*
+		`ArrComp(Apply(Index(Var("std") from "test.jsonnet":1-4, Var("deepJoin") from "test.jsonnet":5-13) from "test.jsonnet":1-13, ArgsDesc { unnamed: [Var("x") from "test.jsonnet":14-15], named: [] }, false) from "test.jsonnet":1-16, [ForSpec(ForSpecData("x", Var("arr") from "test.jsonnet":26-29))]) from "test.jsonnet":0-30`,
+		`ArrComp(Apply(Index(Var("std") from "test.jsonnet":1-4, Str("deepJoin") from "test.jsonnet":5-13) from "test.jsonnet":1-13, ArgsDesc { unnamed: [Var("x") from "test.jsonnet":14-15], named: [] }, false) from "test.jsonnet":1-16, [ForSpec(ForSpecData("x", Var("arr") from "test.jsonnet":26-29))]) from "test.jsonnet":0-30`
+				*/
 		assert_eq!(
 			parse!("[std.deepJoin(x) for x in arr]"),
-			el!(ArrComp(
-				el!(Apply(
-					el!(Index(el!(Var("std".into())), el!(Str("deepJoin".into())))),
-					ArgsDesc::new(vec![el!(Var("x".into()))], vec![]),
-					false,
-				)),
-				vec![CompSpec::ForSpec(ForSpecData(
-					"x".into(),
-					el!(Var("arr".into()))
-				))]
-			)),
+			el!(
+				ArrComp(
+					el!(
+						Apply(
+							el!(
+								Index(
+									el!(Var("std".into()), 1, 4),
+									el!(Str("deepJoin".into()), 5, 13)
+								),
+								1,
+								13
+							),
+							ArgsDesc::new(vec![el!(Var("x".into()), 14, 15)], vec![]),
+							false,
+						),
+						1,
+						16
+					),
+					vec![CompSpec::ForSpec(ForSpecData(
+						"x".into(),
+						el!(Var("arr".into()), 26, 29)
+					))]
+				),
+				0,
+				30
+			),
 		)
 	}
 
 	#[test]
 	fn reserved() {
 		use Expr::*;
-		assert_eq!(parse!("null"), el!(Literal(LiteralType::Null)));
-		assert_eq!(parse!("nulla"), el!(Var("nulla".into())));
+		assert_eq!(parse!("null"), el!(Literal(LiteralType::Null), 0, 4));
+		assert_eq!(parse!("nulla"), el!(Var("nulla".into()), 0, 5));
 	}
 
 	#[test]
@@ -525,11 +589,15 @@
 		use Expr::*;
 		assert_eq!(
 			parse!("!a && !b"),
-			el!(BinaryOp(
-				el!(UnaryOp(UnaryOpType::Not, el!(Var("a".into())))),
-				And,
-				el!(UnaryOp(UnaryOpType::Not, el!(Var("b".into()))))
-			))
+			el!(
+				BinaryOp(
+					el!(UnaryOp(UnaryOpType::Not, el!(Var("a".into()), 1, 2)), 0, 2),
+					And,
+					el!(UnaryOp(UnaryOpType::Not, el!(Var("b".into()), 7, 8)), 6, 8)
+				),
+				0,
+				8
+			)
 		);
 	}
 
@@ -538,11 +606,15 @@
 		use Expr::*;
 		assert_eq!(
 			parse!("!a / !b"),
-			el!(BinaryOp(
-				el!(UnaryOp(UnaryOpType::Not, el!(Var("a".into())))),
-				Div,
-				el!(UnaryOp(UnaryOpType::Not, el!(Var("b".into()))))
-			))
+			el!(
+				BinaryOp(
+					el!(UnaryOp(UnaryOpType::Not, el!(Var("a".into()), 1, 2)), 0, 2),
+					Div,
+					el!(UnaryOp(UnaryOpType::Not, el!(Var("b".into()), 6, 7)), 5, 7)
+				),
+				0,
+				7
+			)
 		);
 	}
 
@@ -551,10 +623,14 @@
 		use Expr::*;
 		assert_eq!(
 			parse!("!!a"),
-			el!(UnaryOp(
-				UnaryOpType::Not,
-				el!(UnaryOp(UnaryOpType::Not, el!(Var("a".into()))))
-			))
+			el!(
+				UnaryOp(
+					UnaryOpType::Not,
+					el!(UnaryOp(UnaryOpType::Not, el!(Var("a".into()), 2, 3)), 1, 3)
+				),
+				0,
+				3
+			)
 		)
 	}
 
@@ -587,55 +663,44 @@
 	fn add_location_info_to_all_sub_expressions() {
 		use Expr::*;
 
-		let file_name: std::rc::Rc<std::path::Path> = PathBuf::from("/test.jsonnet").into();
+		let file_name: std::rc::Rc<std::path::Path> = PathBuf::from("test.jsonnet").into();
 		let expr = parse(
 			"{} { local x = 1, x: x } + {}",
 			&ParserSettings {
-				loc_data: true,
 				file_name: file_name.clone(),
 			},
 		)
 		.unwrap();
 		assert_eq!(
 			expr,
-			el_loc!(
+			el!(
 				BinaryOp(
-					el_loc!(
+					el!(
 						ObjExtend(
-							el_loc!(
-								Obj(ObjBody::MemberList(vec![])),
-								ExprLocation(file_name.clone(), 0, 2)
-							),
+							el!(Obj(ObjBody::MemberList(vec![])), 0, 2),
 							ObjBody::MemberList(vec![
 								Member::BindStmt(BindSpec {
 									name: "x".into(),
 									params: None,
-									value: el_loc!(
-										Num(1.0),
-										ExprLocation(file_name.clone(), 15, 16)
-									)
+									value: el!(Num(1.0), 15, 16)
 								}),
 								Member::Field(FieldMember {
 									name: FieldName::Fixed("x".into()),
 									plus: false,
 									params: None,
 									visibility: Visibility::Normal,
-									value: el_loc!(
-										Var("x".into()),
-										ExprLocation(file_name.clone(), 21, 22)
-									),
+									value: el!(Var("x".into()), 21, 22),
 								})
 							])
 						),
-						ExprLocation(file_name.clone(), 0, 24)
+						0,
+						24
 					),
 					BinaryOpType::Add,
-					el_loc!(
-						Obj(ObjBody::MemberList(vec![])),
-						ExprLocation(file_name.clone(), 27, 29)
-					),
+					el!(Obj(ObjBody::MemberList(vec![])), 27, 29),
 				),
-				ExprLocation(file_name.clone(), 0, 29),
+				0,
+				29
 			),
 		);
 	}
addedcrates/jrsonnet-parser/src/unescape.rsdiffbeforeafterboth
--- /dev/null
+++ b/crates/jrsonnet-parser/src/unescape.rs
@@ -0,0 +1,38 @@
+pub fn unescape(s: &str) -> Option<String> {
+	let mut chars = s.chars();
+	let mut out = String::with_capacity(s.len());
+
+	while let Some(c) = chars.next() {
+		if c != '\\' {
+			out.push(c);
+			continue;
+		}
+		match chars.next()? {
+			c @ ('\\' | '"' | '\'') => out.push(c),
+			'b' => out.push('\u{0008}'),
+			'f' => out.push('\u{000c}'),
+			'n' => out.push('\n'),
+			'r' => out.push('\r'),
+			't' => out.push('\t'),
+			'u' => {
+				let c = IntoIterator::into_iter([
+					chars.next()?,
+					chars.next()?,
+					chars.next()?,
+					chars.next()?,
+				])
+				.map(|c| c.to_digit(16))
+				.try_fold(0u32, |acc, v| Some((acc << 8) | (v?)))?;
+				out.push(char::from_u32(c)?)
+			}
+			'x' => {
+				let c = IntoIterator::into_iter([chars.next()?, chars.next()?])
+					.map(|c| c.to_digit(16))
+					.try_fold(0u32, |acc, v| Some((acc << 8) | (v?)))?;
+				out.push(char::from_u32(c)?)
+			}
+			_ => return None,
+		}
+	}
+	Some(out)
+}
modifiedcrates/jrsonnet-stdlib/src/std.jsonnetdiffbeforeafterboth
--- a/crates/jrsonnet-stdlib/src/std.jsonnet
+++ b/crates/jrsonnet-stdlib/src/std.jsonnet
@@ -370,7 +370,7 @@
         ch;
     std.foldl(function(a, b) a + trans(b), std.stringChars(str), ''),
 
-  manifestJson(value):: std.manifestJsonEx(value, '    '),
+  manifestJson(value):: std.manifestJsonEx(value, '    ') tailstrict,
 
   manifestJsonEx:: $intrinsic(manifestJsonEx),
 
modifiedcrates/jrsonnet-types/Cargo.tomldiffbeforeafterboth
--- a/crates/jrsonnet-types/Cargo.toml
+++ b/crates/jrsonnet-types/Cargo.toml
@@ -8,4 +8,4 @@
 
 [dependencies]
 peg = "0.7.0"
-jrsonnet-gc = { version = "0.4.2", features = ["derive"] }
+gcmodule = { git = "https://github.com/CertainLach/gcmodule", branch = "jrsonnet" }
modifiedcrates/jrsonnet-types/src/lib.rsdiffbeforeafterboth
--- a/crates/jrsonnet-types/src/lib.rs
+++ b/crates/jrsonnet-types/src/lib.rs
@@ -1,6 +1,6 @@
 #![allow(clippy::redundant_closure_call)]
 
-use jrsonnet_gc::Trace;
+use gcmodule::Trace;
 use std::fmt::Display;
 
 #[macro_export]
@@ -82,7 +82,6 @@
 }
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Trace)]
-#[trivially_drop]
 pub enum ValType {
 	Bool,
 	Null,
@@ -115,7 +114,7 @@
 }
 
 #[derive(Debug, Clone, PartialEq, Trace)]
-#[trivially_drop]
+#[skip_trace]
 pub enum ComplexValType {
 	Any,
 	Char,