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
--- a/crates/jrsonnet-evaluator/src/builtin/manifest.rs
+++ b/crates/jrsonnet-evaluator/src/builtin/manifest.rs
@@ -1,6 +1,6 @@
 use crate::error::Error::*;
 use crate::error::Result;
-use crate::push_frame;
+use crate::push_description_frame;
 use crate::{throw, Val};
 
 #[derive(PartialEq, Clone, Copy)]
@@ -103,8 +103,7 @@
 					buf.push_str(cur_padding);
 					escape_string_json_buf(&field, buf);
 					buf.push_str(": ");
-					push_frame(
-						None,
+					push_description_frame(
 						|| format!("field <{}> manifestification", field.clone()),
 						|| {
 							let value = obj.get(field.clone())?.unwrap();
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
9pub use expr::*;9pub use expr::*;
10pub use jrsonnet_interner::IStr;10pub use jrsonnet_interner::IStr;
11pub use peg;11pub use peg;
12mod unescape;
1213
13pub struct ParserSettings {14pub struct ParserSettings {
14 pub loc_data: bool,
15 pub file_name: Rc<Path>,15 pub file_name: Rc<Path>,
16}16}
1717
104 [' ' | '\t']*<, {prefix.len() - 1}> "|||"104 [' ' | '\t']*<, {prefix.len() - 1}> "|||"
105 {let mut l = empty_lines.to_owned(); l.push_str(first_line); l.extend(lines); l}105 {let mut l = empty_lines.to_owned(); l.push_str(first_line); l.extend(lines); l}
106
107 rule hex_char()
108 = quiet! { ['0'..='9' | 'a'..='f' | 'A'..='F'] } / expected!("<hex char>")
109
110 rule string_char(c: rule<()>)
111 = (!['\\']!c()[_])+
112 / "\\\\"
113 / "\\u" hex_char() hex_char() hex_char() hex_char()
114 / "\\x" hex_char() hex_char()
115 / ['\\'] (quiet! { ['b' | 'f' | 'n' | 'r' | 't'] / c() } / expected!("<escape character>"))
106 pub rule string() -> String116 pub rule string() -> String
107 = quiet!{ "\"" str:$(("\\\"" / "\\\\" / (!['"'][_]))*) "\"" {unescape::unescape(str).unwrap()}117 = ['"'] str:$(string_char(<['"']>)*) ['"'] {? unescape::unescape(str).ok_or("<escaped string>")}
108 / "'" str:$(("\\'" / "\\\\" / (!['\''][_]))*) "'" {unescape::unescape(str).unwrap()}118 / ['\''] str:$(string_char(<['\'']>)*) ['\''] {? unescape::unescape(str).ok_or("<escaped string>")}
109 / "@'" str:$(("''" / (!['\''][_]))*) "'" {str.replace("''", "'")}119 / quiet!{ "@'" str:$(("''" / (!['\''][_]))*) "'" {str.replace("''", "'")}
110 / "@\"" str:$(("\"\"" / (!['"'][_]))*) "\"" {str.replace("\"\"", "\"")}120 / "@\"" str:$(("\"\"" / (!['"'][_]))*) "\"" {str.replace("\"\"", "\"")}
111 / string_block() } / expected!("<string>")121 / string_block() } / expected!("<string>")
112122
113 pub rule field_name(s: &ParserSettings) -> expr::FieldName123 pub rule field_name(s: &ParserSettings) -> expr::FieldName
114 = name:$(id()) {expr::FieldName::Fixed(name.into())}124 = name:$(id()) {expr::FieldName::Fixed(name.into())}
177 = n:number() { expr::Expr::Num(n) }187 = n:number() { expr::Expr::Num(n) }
178 pub rule var_expr(s: &ParserSettings) -> Expr188 pub rule var_expr(s: &ParserSettings) -> Expr
179 = n:$(id()) { expr::Expr::Var(n.into()) }189 = n:$(id()) { expr::Expr::Var(n.into()) }
190 pub rule id_loc(s: &ParserSettings) -> LocExpr
191 = a:position!() n:$(id()) b:position!() { LocExpr(Rc::new(expr::Expr::Str(n.into())), ExprLocation(s.file_name.clone(), a,b)) }
180 pub rule if_then_else_expr(s: &ParserSettings) -> Expr192 pub rule if_then_else_expr(s: &ParserSettings) -> Expr
181 = cond:ifspec(s) _ keyword("then") _ cond_then:expr(s) cond_else:(_ keyword("else") _ e:expr(s) {e})? {Expr::IfElse{193 = cond:ifspec(s) _ keyword("then") _ cond_then:expr(s) cond_else:(_ keyword("else") _ e:expr(s) {e})? {Expr::IfElse{
182 cond,194 cond,
240 use UnaryOpType::*;252 use UnaryOpType::*;
241 rule expr(s: &ParserSettings) -> LocExpr253 rule expr(s: &ParserSettings) -> LocExpr
242 = precedence! {254 = precedence! {
243 start:position!() v:@ end:position!() { loc_expr!(v, s.loc_data, (s.file_name.clone(), start, end)) }255 start:position!() v:@ end:position!() { LocExpr(Rc::new(v), ExprLocation(s.file_name.clone(), start, end)) }
244 --256 --
245 a:(@) _ binop(<"||">) _ b:@ {expr_bin!(a Or b)}257 a:(@) _ binop(<"||">) _ b:@ {expr_bin!(a Or b)}
246 --258 --
276 unaryop(<"~">) _ b:@ {expr_un!(BitNot b)}288 unaryop(<"~">) _ b:@ {expr_un!(BitNot b)}
277 --289 --
278 a:(@) _ "[" _ e:slice_desc(s) _ "]" {Expr::Slice(a, e)}290 a:(@) _ "[" _ e:slice_desc(s) _ "]" {Expr::Slice(a, e)}
279 a:(@) _ "." _ e:$(id()) {Expr::Index(a, el!(Expr::Str(e.into())))}291 a:(@) _ "." _ a:position!() e:id_loc(s) b:position!() {Expr::Index(a, e)}
280 a:(@) _ "[" _ e:expr(s) _ "]" {Expr::Index(a, e)}292 a:(@) _ "[" _ e:expr(s) _ "]" {Expr::Index(a, e)}
281 a:(@) _ "(" _ args:args(s) _ ")" ts:(_ keyword("tailstrict"))? {Expr::Apply(a, args, ts.is_some())}293 a:(@) _ "(" _ args:args(s) _ ")" ts:(_ keyword("tailstrict"))? {Expr::Apply(a, args, ts.is_some())}
282 a:(@) _ "{" _ body:objinside(s) _ "}" {Expr::ObjExtend(a, body)}294 a:(@) _ "{" _ body:objinside(s) _ "}" {Expr::ObjExtend(a, body)}
294 jsonnet_parser::jsonnet(str, settings)306 jsonnet_parser::jsonnet(str, settings)
295}307}
296
297#[macro_export]
298macro_rules! el {
299 ($expr:expr) => {
300 LocExpr(std::rc::Rc::new($expr), None)
301 };
302}
303308
304#[cfg(test)]309#[cfg(test)]
305pub mod tests {310pub mod tests {
313 parse(318 parse(
314 $s,319 $s,
315 &ParserSettings {320 &ParserSettings {
316 loc_data: false,
317 file_name: PathBuf::from("/test.jsonnet").into(),321 file_name: PathBuf::from("test.jsonnet").into(),
318 },322 },
319 )323 )
320 .unwrap()324 .unwrap()
321 };325 };
322 }326 }
323327
324 macro_rules! el_loc {328 macro_rules! el {
325 ($expr:expr, $loc:expr$(,)?) => {329 ($expr:expr, $from:expr, $to:expr$(,)?) => {
326 LocExpr(std::rc::Rc::new($expr), Some($loc))330 LocExpr(
331 std::rc::Rc::new($expr),
332 ExprLocation(PathBuf::from("test.jsonnet").into(), $from, $to),
333 )
327 };334 };
328 }335 }
329
330 mod expressions {
331 use super::*;
332
333 pub fn basic_math() -> LocExpr {
334 el!(Expr::BinaryOp(
335 el!(Expr::Num(2.0)),
336 Add,
337 el!(Expr::BinaryOp(
338 el!(Expr::Num(2.0)),
339 Mul,
340 el!(Expr::Num(2.0)),
341 )),
342 ))
343 }
344 }
345336
346 #[test]337 #[test]
347 fn multiline_string() {338 fn multiline_string() {
348 assert_eq!(339 assert_eq!(
349 parse!("|||\n Hello world!\n a\n|||"),340 parse!("|||\n Hello world!\n a\n|||"),
350 el!(Expr::Str("Hello world!\n a\n".into())),341 el!(Expr::Str("Hello world!\n a\n".into()), 0, 31),
351 );342 );
352 assert_eq!(343 assert_eq!(
353 parse!("|||\n Hello world!\n a\n|||"),344 parse!("|||\n Hello world!\n a\n|||"),
354 el!(Expr::Str("Hello world!\n a\n".into())),345 el!(Expr::Str("Hello world!\n a\n".into()), 0, 27),
355 );346 );
356 assert_eq!(347 assert_eq!(
357 parse!("|||\n\t\tHello world!\n\t\t\ta\n|||"),348 parse!("|||\n\t\tHello world!\n\t\t\ta\n|||"),
358 el!(Expr::Str("Hello world!\n\ta\n".into())),349 el!(Expr::Str("Hello world!\n\ta\n".into()), 0, 27),
359 );350 );
360 assert_eq!(351 assert_eq!(
361 parse!("|||\n Hello world!\n a\n |||"),352 parse!("|||\n Hello world!\n a\n |||"),
362 el!(Expr::Str("Hello world!\n a\n".into())),353 el!(Expr::Str("Hello world!\n a\n".into()), 0, 30),
363 );354 );
364 }355 }
365356
376 fn string_escaping() {367 fn string_escaping() {
377 assert_eq!(368 assert_eq!(
378 parse!(r#""Hello, \"world\"!""#),369 parse!(r#""Hello, \"world\"!""#),
379 el!(Expr::Str(r#"Hello, "world"!"#.into())),370 el!(Expr::Str(r#"Hello, "world"!"#.into()), 0, 19),
380 );371 );
381 assert_eq!(372 assert_eq!(
382 parse!(r#"'Hello \'world\'!'"#),373 parse!(r#"'Hello \'world\'!'"#),
383 el!(Expr::Str("Hello 'world'!".into())),374 el!(Expr::Str("Hello 'world'!".into()), 0, 18),
384 );375 );
385 assert_eq!(parse!(r#"'\\\\'"#), el!(Expr::Str("\\\\".into())),);376 assert_eq!(parse!(r#"'\\\\'"#), el!(Expr::Str("\\\\".into()), 0, 6));
386 }377 }
387378
388 #[test]379 #[test]
389 fn string_unescaping() {380 fn string_unescaping() {
390 assert_eq!(381 assert_eq!(
391 parse!(r#""Hello\nWorld""#),382 parse!(r#""Hello\nWorld""#),
392 el!(Expr::Str("Hello\nWorld".into())),383 el!(Expr::Str("Hello\nWorld".into()), 0, 14),
393 );384 );
394 }385 }
395386
396 #[test]387 #[test]
397 fn string_verbantim() {388 fn string_verbantim() {
398 assert_eq!(389 assert_eq!(
399 parse!(r#"@"Hello\n""World""""#),390 parse!(r#"@"Hello\n""World""""#),
400 el!(Expr::Str("Hello\\n\"World\"".into())),391 el!(Expr::Str("Hello\\n\"World\"".into()), 0, 19),
401 );392 );
402 }393 }
403394
404 #[test]395 #[test]
405 fn imports() {396 fn imports() {
406 assert_eq!(397 assert_eq!(
407 parse!("import \"hello\""),398 parse!("import \"hello\""),
408 el!(Expr::Import(PathBuf::from("hello"))),399 el!(Expr::Import(PathBuf::from("hello")), 0, 14),
409 );400 );
410 assert_eq!(401 assert_eq!(
411 parse!("importstr \"garnish.txt\""),402 parse!("importstr \"garnish.txt\""),
412 el!(Expr::ImportStr(PathBuf::from("garnish.txt")))403 el!(Expr::ImportStr(PathBuf::from("garnish.txt")), 0, 23)
413 );404 );
414 }405 }
415406
416 #[test]407 #[test]
417 fn empty_object() {408 fn empty_object() {
418 assert_eq!(parse!("{}"), el!(Expr::Obj(ObjBody::MemberList(vec![]))));409 assert_eq!(
410 parse!("{}"),
411 el!(Expr::Obj(ObjBody::MemberList(vec![])), 0, 2)
412 );
419 }413 }
420414
421 #[test]415 #[test]
422 fn basic_math() {416 fn basic_math() {
423 assert_eq!(417 assert_eq!(
424 parse!("2+2*2"),418 parse!("2+2*2"),
425 el!(Expr::BinaryOp(419 el!(
420 Expr::BinaryOp(
426 el!(Expr::Num(2.0)),421 el!(Expr::Num(2.0), 0, 1),
427 Add,422 Add,
428 el!(Expr::BinaryOp(423 el!(
429 el!(Expr::Num(2.0)),424 Expr::BinaryOp(el!(Expr::Num(2.0), 2, 3), Mul, el!(Expr::Num(2.0), 4, 5)),
430 Mul,425 2,
431 el!(Expr::Num(2.0))426 5
432 ))427 )
433 ))428 ),
429 0,
430 5
431 )
434 );432 );
435 }433 }
436434
437 #[test]435 #[test]
438 fn basic_math_with_indents() {436 fn basic_math_with_indents() {
439 assert_eq!(parse!("2 + 2 * 2 "), expressions::basic_math());437 assert_eq!(
438 parse!("2 + 2 * 2 "),
439 el!(
440 Expr::BinaryOp(
441 el!(Expr::Num(2.0), 0, 1),
442 Add,
443 el!(
444 Expr::BinaryOp(el!(Expr::Num(2.0), 7, 8), Mul, el!(Expr::Num(2.0), 13, 14),),
445 7,
446 14
447 ),
448 ),
449 0,
450 14
451 )
452 );
440 }453 }
441454
445 parse!("2+(2+2*2)"),458 parse!("2+(2+2*2)"),
446 el!(Expr::BinaryOp(459 el!(
460 Expr::BinaryOp(
447 el!(Expr::Num(2.0)),461 el!(Expr::Num(2.0), 0, 1),
448 Add,462 Add,
449 el!(Expr::Parened(expressions::basic_math())),463 el!(
464 Expr::Parened(el!(
465 Expr::BinaryOp(
466 el!(Expr::Num(2.0), 3, 4),
467 Add,
468 el!(
469 Expr::BinaryOp(
470 el!(Expr::Num(2.0), 5, 6),
471 Mul,
472 el!(Expr::Num(2.0), 7, 8),
473 ),
474 5,
475 8
476 ),
477 ),
478 3,
479 8
480 )),
481 2,
482 9
483 ),
450 ))484 ),
485 0,
486 9
487 )
451 );488 );
452 }489 }
458 parse!("2//comment\n+//comment\n3/*test*/*/*test*/4"),495 parse!("2//comment\n+//comment\n3/*test*/*/*test*/4"),
459 el!(Expr::BinaryOp(496 el!(
497 Expr::BinaryOp(
460 el!(Expr::Num(2.0)),498 el!(Expr::Num(2.0), 0, 1),
461 Add,499 Add,
462 el!(Expr::BinaryOp(500 el!(
501 Expr::BinaryOp(
463 el!(Expr::Num(3.0)),502 el!(Expr::Num(3.0), 22, 23),
464 Mul,503 Mul,
465 el!(Expr::Num(4.0))504 el!(Expr::Num(4.0), 40, 41)
466 ))505 ),
506 22,
507 41
508 )
467 ))509 ),
510 0,
511 41
512 )
468 );513 );
469 }514 }
474 assert_eq!(519 assert_eq!(
475 parse!("2/*\\*/+*/ - 22"),520 parse!("2/*\\*/+*/ - 22"),
476 el!(Expr::BinaryOp(521 el!(
477 el!(Expr::Num(2.0)),522 Expr::BinaryOp(el!(Expr::Num(2.0), 0, 1), Sub, el!(Expr::Num(22.0), 12, 14)),
478 Sub,523 0,
479 el!(Expr::Num(22.0))524 14
480 ))525 )
481 );526 );
482 }527 }
492 #[test]537 #[test]
493 fn array_comp() {538 fn array_comp() {
494 use Expr::*;539 use Expr::*;
540 /*
541 `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`,
542 `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`
543 */
495 assert_eq!(544 assert_eq!(
496 parse!("[std.deepJoin(x) for x in arr]"),545 parse!("[std.deepJoin(x) for x in arr]"),
497 el!(ArrComp(546 el!(
547 ArrComp(
498 el!(Apply(548 el!(
549 Apply(
499 el!(Index(el!(Var("std".into())), el!(Str("deepJoin".into())))),550 el!(
551 Index(
552 el!(Var("std".into()), 1, 4),
553 el!(Str("deepJoin".into()), 5, 13)
554 ),
555 1,
556 13
557 ),
500 ArgsDesc::new(vec![el!(Var("x".into()))], vec![]),558 ArgsDesc::new(vec![el!(Var("x".into()), 14, 15)], vec![]),
501 false,559 false,
502 )),560 ),
561 1,
562 16
563 ),
503 vec![CompSpec::ForSpec(ForSpecData(564 vec![CompSpec::ForSpec(ForSpecData(
504 "x".into(),565 "x".into(),
505 el!(Var("arr".into()))566 el!(Var("arr".into()), 26, 29)
506 ))]567 ))]
507 )),568 ),
569 0,
570 30
571 ),
508 )572 )
509 }573 }
510574
511 #[test]575 #[test]
512 fn reserved() {576 fn reserved() {
513 use Expr::*;577 use Expr::*;
514 assert_eq!(parse!("null"), el!(Literal(LiteralType::Null)));578 assert_eq!(parse!("null"), el!(Literal(LiteralType::Null), 0, 4));
515 assert_eq!(parse!("nulla"), el!(Var("nulla".into())));579 assert_eq!(parse!("nulla"), el!(Var("nulla".into()), 0, 5));
516 }580 }
517581
518 #[test]582 #[test]
527 parse!("!a && !b"),591 parse!("!a && !b"),
528 el!(BinaryOp(592 el!(
593 BinaryOp(
529 el!(UnaryOp(UnaryOpType::Not, el!(Var("a".into())))),594 el!(UnaryOp(UnaryOpType::Not, el!(Var("a".into()), 1, 2)), 0, 2),
530 And,595 And,
531 el!(UnaryOp(UnaryOpType::Not, el!(Var("b".into()))))596 el!(UnaryOp(UnaryOpType::Not, el!(Var("b".into()), 7, 8)), 6, 8)
532 ))597 ),
598 0,
599 8
600 )
533 );601 );
534 }602 }
540 parse!("!a / !b"),608 parse!("!a / !b"),
541 el!(BinaryOp(609 el!(
610 BinaryOp(
542 el!(UnaryOp(UnaryOpType::Not, el!(Var("a".into())))),611 el!(UnaryOp(UnaryOpType::Not, el!(Var("a".into()), 1, 2)), 0, 2),
543 Div,612 Div,
544 el!(UnaryOp(UnaryOpType::Not, el!(Var("b".into()))))613 el!(UnaryOp(UnaryOpType::Not, el!(Var("b".into()), 6, 7)), 5, 7)
545 ))614 ),
615 0,
616 7
617 )
546 );618 );
547 }619 }
554 el!(UnaryOp(626 el!(
627 UnaryOp(
555 UnaryOpType::Not,628 UnaryOpType::Not,
556 el!(UnaryOp(UnaryOpType::Not, el!(Var("a".into()))))629 el!(UnaryOp(UnaryOpType::Not, el!(Var("a".into()), 2, 3)), 1, 3)
557 ))630 ),
631 0,
632 3
633 )
558 )634 )
559 }635 }
587 fn add_location_info_to_all_sub_expressions() {663 fn add_location_info_to_all_sub_expressions() {
588 use Expr::*;664 use Expr::*;
589665
590 let file_name: std::rc::Rc<std::path::Path> = PathBuf::from("/test.jsonnet").into();666 let file_name: std::rc::Rc<std::path::Path> = PathBuf::from("test.jsonnet").into();
591 let expr = parse(667 let expr = parse(
592 "{} { local x = 1, x: x } + {}",668 "{} { local x = 1, x: x } + {}",
593 &ParserSettings {669 &ParserSettings {
594 loc_data: true,
595 file_name: file_name.clone(),670 file_name: file_name.clone(),
596 },671 },
597 )672 )
598 .unwrap();673 .unwrap();
599 assert_eq!(674 assert_eq!(
600 expr,675 expr,
601 el_loc!(676 el!(
602 BinaryOp(677 BinaryOp(
603 el_loc!(678 el!(
604 ObjExtend(679 ObjExtend(
605 el_loc!(680 el!(Obj(ObjBody::MemberList(vec![])), 0, 2),
606 Obj(ObjBody::MemberList(vec![])),
607 ExprLocation(file_name.clone(), 0, 2)
608 ),
609 ObjBody::MemberList(vec![681 ObjBody::MemberList(vec![
610 Member::BindStmt(BindSpec {682 Member::BindStmt(BindSpec {
611 name: "x".into(),683 name: "x".into(),
612 params: None,684 params: None,
613 value: el_loc!(685 value: el!(Num(1.0), 15, 16)
614 Num(1.0),
615 ExprLocation(file_name.clone(), 15, 16)
616 )
617 }),686 }),
618 Member::Field(FieldMember {687 Member::Field(FieldMember {
619 name: FieldName::Fixed("x".into()),688 name: FieldName::Fixed("x".into()),
620 plus: false,689 plus: false,
621 params: None,690 params: None,
622 visibility: Visibility::Normal,691 visibility: Visibility::Normal,
623 value: el_loc!(692 value: el!(Var("x".into()), 21, 22),
624 Var("x".into()),
625 ExprLocation(file_name.clone(), 21, 22)
626 ),
627 })693 })
628 ])694 ])
629 ),695 ),
630 ExprLocation(file_name.clone(), 0, 24)696 0,
697 24
631 ),698 ),
632 BinaryOpType::Add,699 BinaryOpType::Add,
633 el_loc!(700 el!(Obj(ObjBody::MemberList(vec![])), 27, 29),
634 Obj(ObjBody::MemberList(vec![])),
635 ExprLocation(file_name.clone(), 27, 29)
636 ),
637 ),701 ),
638 ExprLocation(file_name.clone(), 0, 29),702 0,
703 29
639 ),704 ),
640 );705 );
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,