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
before · crates/jrsonnet-parser/src/lib.rs
1#![allow(clippy::redundant_closure_call)]23use peg::parser;4use std::{5	path::{Path, PathBuf},6	rc::Rc,7};8mod expr;9pub use expr::*;10pub use jrsonnet_interner::IStr;11pub use peg;1213pub struct ParserSettings {14	pub loc_data: bool,15	pub file_name: Rc<Path>,16}1718macro_rules! expr_bin {19	($a:ident $op:ident $b:ident) => {20		Expr::BinaryOp($a, $op, $b)21	};22}23macro_rules! expr_un {24	($op:ident $a:ident) => {25		Expr::UnaryOp($op, $a)26	};27}2829parser! {30	grammar jsonnet_parser() for str {31		use peg::ParseLiteral;3233		rule eof() = quiet!{![_]} / expected!("<eof>")34		rule eol() = "\n" / eof()3536		/// Standard C-like comments37		rule comment()38			= "//" (!eol()[_])* eol()39			/ "/*" ("\\*/" / "\\\\" / (!("*/")[_]))* "*/"40			/ "#" (!eol()[_])* eol()4142		rule single_whitespace() = quiet!{([' ' | '\r' | '\n' | '\t'] / comment())} / expected!("<whitespace>")43		rule _() = single_whitespace()*4445		/// For comma-delimited elements46		rule comma() = quiet!{_ "," _} / expected!("<comma>")47		rule alpha() -> char = c:$(['_' | 'a'..='z' | 'A'..='Z']) {c.chars().next().unwrap()}48		rule digit() -> char = d:$(['0'..='9']) {d.chars().next().unwrap()}49		rule end_of_ident() = !['0'..='9' | '_' | 'a'..='z' | 'A'..='Z']50		/// Sequence of digits51		rule uint_str() -> &'input str = a:$(digit()+) { a }52		/// Number in scientific notation format53		rule number() -> f64 = quiet!{a:$(uint_str() ("." uint_str())? (['e'|'E'] (s:['+'|'-'])? uint_str())?) {? a.parse().map_err(|_| "<number>") }} / expected!("<number>")5455		/// Reserved word followed by any non-alphanumberic56		rule reserved() = ("assert" / "else" / "error" / "false" / "for" / "function" / "if" / "import" / "importstr" / "in" / "local" / "null" / "tailstrict" / "then" / "self" / "super" / "true") end_of_ident()57		rule id() = quiet!{ !reserved() alpha() (alpha() / digit())*} / expected!("<identifier>")5859		rule keyword(id: &'static str) -> ()60			= ##parse_string_literal(id) end_of_ident()6162		pub rule param(s: &ParserSettings) -> expr::Param = name:$(id()) expr:(_ "=" _ expr:expr(s){expr})? { expr::Param(name.into(), expr) }63		pub rule params(s: &ParserSettings) -> expr::ParamsDesc64			= params:param(s) ** comma() comma()? { expr::ParamsDesc(Rc::new(params)) }65			/ { expr::ParamsDesc(Rc::new(Vec::new())) }6667		pub rule arg(s: &ParserSettings) -> (Option<IStr>, LocExpr)68			= quiet! { name:(s:$(id()) _ "=" _ {s})? expr:expr(s) {(name.map(Into::into), expr)} }69			/ expected!("<argument>")7071		pub rule args(s: &ParserSettings) -> expr::ArgsDesc72			= args:arg(s)**comma() comma()? {?73				let unnamed_count = args.iter().take_while(|(n, _)| n.is_none()).count();74				let mut unnamed = Vec::with_capacity(unnamed_count);75				let mut named = Vec::with_capacity(args.len() - unnamed_count);76				let mut named_started = false;77				for (name, value) in args {78					if let Some(name) = name {79						named_started = true;80						named.push((name, value));81					} else {82						if named_started {83							return Err("<named argument>")84						}85						unnamed.push(value);86					}87				}88				Ok(expr::ArgsDesc::new(unnamed, named))89			}9091		pub rule bind(s: &ParserSettings) -> expr::BindSpec92			= name:$(id()) _ "=" _ expr:expr(s) {expr::BindSpec{name:name.into(), params: None, value: expr}}93			/ name:$(id()) _ "(" _ params:params(s) _ ")" _ "=" _ expr:expr(s) {expr::BindSpec{name:name.into(), params: Some(params), value: expr}}94		pub rule assertion(s: &ParserSettings) -> expr::AssertStmt95			= keyword("assert") _ cond:expr(s) msg:(_ ":" _ e:expr(s) {e})? { expr::AssertStmt(cond, msg) }9697		pub rule whole_line() -> &'input str98			= str:$((!['\n'][_])* "\n") {str}99		pub rule string_block() -> String100			= "|||" (!['\n']single_whitespace())* "\n"101			  empty_lines:$(['\n']*)102			  prefix:[' ' | '\t']+ first_line:whole_line()103			  lines:("\n" {"\n"} / [' ' | '\t']*<{prefix.len()}> s:whole_line() {s})*104			  [' ' | '\t']*<, {prefix.len() - 1}> "|||"105			  {let mut l = empty_lines.to_owned(); l.push_str(first_line); l.extend(lines); l}106		pub rule string() -> String107			= quiet!{ "\"" str:$(("\\\"" / "\\\\" / (!['"'][_]))*) "\"" {unescape::unescape(str).unwrap()}108			/ "'" str:$(("\\'" / "\\\\" / (!['\''][_]))*) "'" {unescape::unescape(str).unwrap()}109			/ "@'" str:$(("''" / (!['\''][_]))*) "'" {str.replace("''", "'")}110			/ "@\"" str:$(("\"\"" / (!['"'][_]))*) "\"" {str.replace("\"\"", "\"")}111			/ string_block() } / expected!("<string>")112113		pub rule field_name(s: &ParserSettings) -> expr::FieldName114			= name:$(id()) {expr::FieldName::Fixed(name.into())}115			/ name:string() {expr::FieldName::Fixed(name.into())}116			/ "[" _ expr:expr(s) _ "]" {expr::FieldName::Dyn(expr)}117		pub rule visibility() -> expr::Visibility118			= ":::" {expr::Visibility::Unhide}119			/ "::" {expr::Visibility::Hidden}120			/ ":" {expr::Visibility::Normal}121		pub rule field(s: &ParserSettings) -> expr::FieldMember122			= name:field_name(s) _ plus:"+"? _ visibility:visibility() _ value:expr(s) {expr::FieldMember{123				name,124				plus: plus.is_some(),125				params: None,126				visibility,127				value,128			}}129			/ name:field_name(s) _ "(" _ params:params(s) _ ")" _ visibility:visibility() _ value:expr(s) {expr::FieldMember{130				name,131				plus: false,132				params: Some(params),133				visibility,134				value,135			}}136		pub rule obj_local(s: &ParserSettings) -> BindSpec137			= keyword("local") _ bind:bind(s) {bind}138		pub rule member(s: &ParserSettings) -> expr::Member139			= bind:obj_local(s) {expr::Member::BindStmt(bind)}140			/ assertion:assertion(s) {expr::Member::AssertStmt(assertion)}141			/ field:field(s) {expr::Member::Field(field)}142		pub rule objinside(s: &ParserSettings) -> expr::ObjBody143			= pre_locals:(b: obj_local(s) comma() {b})* "[" _ key:expr(s) _ "]" _ plus:"+"? _ ":" _ value:expr(s) post_locals:(comma() b:obj_local(s) {b})* _ forspec:forspec(s) others:(_ rest:compspec(s) {rest})? {144				let mut compspecs = vec![CompSpec::ForSpec(forspec)];145				compspecs.extend(others.unwrap_or_default());146				expr::ObjBody::ObjComp(expr::ObjComp{147					pre_locals,148					key,149					plus: plus.is_some(),150					value,151					post_locals,152					compspecs,153				})154			}155			/ members:(member(s) ** comma()) comma()? {expr::ObjBody::MemberList(members)}156		pub rule ifspec(s: &ParserSettings) -> IfSpecData157			= keyword("if") _ expr:expr(s) {IfSpecData(expr)}158		pub rule forspec(s: &ParserSettings) -> ForSpecData159			= keyword("for") _ id:$(id()) _ keyword("in") _ cond:expr(s) {ForSpecData(id.into(), cond)}160		pub rule compspec(s: &ParserSettings) -> Vec<expr::CompSpec>161			= s:(i:ifspec(s) { expr::CompSpec::IfSpec(i) } / f:forspec(s) {expr::CompSpec::ForSpec(f)} ) ** _ {s}162		pub rule local_expr(s: &ParserSettings) -> Expr163			= keyword("local") _ binds:bind(s) ** comma() _ ";" _ expr:expr(s) { Expr::LocalExpr(binds, expr) }164		pub rule string_expr(s: &ParserSettings) -> Expr165			= s:string() {Expr::Str(s.into())}166		pub rule obj_expr(s: &ParserSettings) -> Expr167			= "{" _ body:objinside(s) _ "}" {Expr::Obj(body)}168		pub rule array_expr(s: &ParserSettings) -> Expr169			= "[" _ elems:(expr(s) ** comma()) _ comma()? "]" {Expr::Arr(elems)}170		pub rule array_comp_expr(s: &ParserSettings) -> Expr171			= "[" _ expr:expr(s) _ comma()? _ forspec:forspec(s) _ others:(others: compspec(s) _ {others})? "]" {172				let mut specs = vec![CompSpec::ForSpec(forspec)];173				specs.extend(others.unwrap_or_default());174				Expr::ArrComp(expr, specs)175			}176		pub rule number_expr(s: &ParserSettings) -> Expr177			= n:number() { expr::Expr::Num(n) }178		pub rule var_expr(s: &ParserSettings) -> Expr179			= n:$(id()) { expr::Expr::Var(n.into()) }180		pub rule if_then_else_expr(s: &ParserSettings) -> Expr181			= cond:ifspec(s) _ keyword("then") _ cond_then:expr(s) cond_else:(_ keyword("else") _ e:expr(s) {e})? {Expr::IfElse{182				cond,183				cond_then,184				cond_else,185			}}186187		pub rule literal(s: &ParserSettings) -> Expr188			= v:(189				keyword("null") {LiteralType::Null}190				/ keyword("true") {LiteralType::True}191				/ keyword("false") {LiteralType::False}192				/ keyword("self") {LiteralType::This}193				/ keyword("$") {LiteralType::Dollar}194				/ keyword("super") {LiteralType::Super}195			) {Expr::Literal(v)}196197		pub rule expr_basic(s: &ParserSettings) -> Expr198			= literal(s)199200			/ quiet!{"$intrinsic(" name:$(id()) ")" {Expr::Intrinsic(name.into())}}201202			/ string_expr(s) / number_expr(s)203			/ array_expr(s)204			/ obj_expr(s)205			/ array_expr(s)206			/ array_comp_expr(s)207208			/ keyword("importstr") _ path:string() {Expr::ImportStr(PathBuf::from(path))}209			/ keyword("import") _ path:string() {Expr::Import(PathBuf::from(path))}210211			/ var_expr(s)212			/ local_expr(s)213			/ if_then_else_expr(s)214215			/ keyword("function") _ "(" _ params:params(s) _ ")" _ expr:expr(s) {Expr::Function(params, expr)}216			/ assertion:assertion(s) _ ";" _ expr:expr(s) { Expr::AssertExpr(assertion, expr) }217218			/ keyword("error") _ expr:expr(s) { Expr::ErrorStmt(expr) }219220		rule slice_part(s: &ParserSettings) -> Option<LocExpr>221			= e:(_ e:expr(s) _{e})? {e}222		pub rule slice_desc(s: &ParserSettings) -> SliceDesc223			= start:slice_part(s) ":" pair:(end:slice_part(s) step:(":" e:slice_part(s){e})? {(end, step.flatten())})? {224				let (end, step) = if let Some((end, step)) = pair {225					(end, step)226				}else{227					(None, None)228				};229230				SliceDesc { start, end, step }231			}232233		rule binop(x: rule<()>) -> ()234			= quiet!{ x() } / expected!("<binary op>")235		rule unaryop(x: rule<()>) -> ()236			= quiet!{ x() } / expected!("<unary op>")237238239		use BinaryOpType::*;240		use UnaryOpType::*;241		rule expr(s: &ParserSettings) -> LocExpr242			= precedence! {243				start:position!() v:@ end:position!() { loc_expr!(v, s.loc_data, (s.file_name.clone(), start, end)) }244				--245				a:(@) _ binop(<"||">) _ b:@ {expr_bin!(a Or b)}246				--247				a:(@) _ binop(<"&&">) _ b:@ {expr_bin!(a And b)}248				--249				a:(@) _ binop(<"|">) _ b:@ {expr_bin!(a BitOr b)}250				--251				a:@ _ binop(<"^">) _ b:(@) {expr_bin!(a BitXor b)}252				--253				a:(@) _ binop(<"&">) _ b:@ {expr_bin!(a BitAnd b)}254				--255				a:(@) _ binop(<"==">) _ b:@ {expr_bin!(a Eq b)}256				a:(@) _ binop(<"!=">) _ b:@ {expr_bin!(a Neq b)}257				--258				a:(@) _ binop(<"<">) _ b:@ {expr_bin!(a Lt b)}259				a:(@) _ binop(<">">) _ b:@ {expr_bin!(a Gt b)}260				a:(@) _ binop(<"<=">) _ b:@ {expr_bin!(a Lte b)}261				a:(@) _ binop(<">=">) _ b:@ {expr_bin!(a Gte b)}262				a:(@) _ binop(<keyword("in")>) _ b:@ {expr_bin!(a In b)}263				--264				a:(@) _ binop(<"<<">) _ b:@ {expr_bin!(a Lhs b)}265				a:(@) _ binop(<">>">) _ b:@ {expr_bin!(a Rhs b)}266				--267				a:(@) _ binop(<"+">) _ b:@ {expr_bin!(a Add b)}268				a:(@) _ binop(<"-">) _ b:@ {expr_bin!(a Sub b)}269				--270				a:(@) _ binop(<"*">) _ b:@ {expr_bin!(a Mul b)}271				a:(@) _ binop(<"/">) _ b:@ {expr_bin!(a Div b)}272				a:(@) _ binop(<"%">) _ b:@ {expr_bin!(a Mod b)}273				--274						unaryop(<"-">) _ b:@ {expr_un!(Minus b)}275						unaryop(<"!">) _ b:@ {expr_un!(Not b)}276						unaryop(<"~">) _ b:@ {expr_un!(BitNot b)}277				--278				a:(@) _ "[" _ e:slice_desc(s) _ "]" {Expr::Slice(a, e)}279				a:(@) _ "." _ e:$(id()) {Expr::Index(a, el!(Expr::Str(e.into())))}280				a:(@) _ "[" _ e:expr(s) _ "]" {Expr::Index(a, e)}281				a:(@) _ "(" _ args:args(s) _ ")" ts:(_ keyword("tailstrict"))? {Expr::Apply(a, args, ts.is_some())}282				a:(@) _ "{" _ body:objinside(s) _ "}" {Expr::ObjExtend(a, body)}283				--284				e:expr_basic(s) {e}285				"(" _ e:expr(s) _ ")" {Expr::Parened(e)}286			}287288		pub rule jsonnet(s: &ParserSettings) -> LocExpr = _ e:expr(s) _ {e}289	}290}291292pub type ParseError = peg::error::ParseError<peg::str::LineCol>;293pub fn parse(str: &str, settings: &ParserSettings) -> Result<LocExpr, ParseError> {294	jsonnet_parser::jsonnet(str, settings)295}296297#[macro_export]298macro_rules! el {299	($expr:expr) => {300		LocExpr(std::rc::Rc::new($expr), None)301	};302}303304#[cfg(test)]305pub mod tests {306	use super::{expr::*, parse};307	use crate::ParserSettings;308	use std::path::PathBuf;309	use BinaryOpType::*;310311	macro_rules! parse {312		($s:expr) => {313			parse(314				$s,315				&ParserSettings {316					loc_data: false,317					file_name: PathBuf::from("/test.jsonnet").into(),318				},319			)320			.unwrap()321		};322	}323324	macro_rules! el_loc {325		($expr:expr, $loc:expr$(,)?) => {326			LocExpr(std::rc::Rc::new($expr), Some($loc))327		};328	}329330	mod expressions {331		use super::*;332333		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	}345346	#[test]347	fn multiline_string() {348		assert_eq!(349			parse!("|||\n    Hello world!\n     a\n|||"),350			el!(Expr::Str("Hello world!\n a\n".into())),351		);352		assert_eq!(353			parse!("|||\n  Hello world!\n   a\n|||"),354			el!(Expr::Str("Hello world!\n a\n".into())),355		);356		assert_eq!(357			parse!("|||\n\t\tHello world!\n\t\t\ta\n|||"),358			el!(Expr::Str("Hello world!\n\ta\n".into())),359		);360		assert_eq!(361			parse!("|||\n   Hello world!\n    a\n |||"),362			el!(Expr::Str("Hello world!\n a\n".into())),363		);364	}365366	#[test]367	fn slice() {368		parse!("a[1:]");369		parse!("a[1::]");370		parse!("a[:1:]");371		parse!("a[::1]");372		parse!("str[:len - 1]");373	}374375	#[test]376	fn string_escaping() {377		assert_eq!(378			parse!(r#""Hello, \"world\"!""#),379			el!(Expr::Str(r#"Hello, "world"!"#.into())),380		);381		assert_eq!(382			parse!(r#"'Hello \'world\'!'"#),383			el!(Expr::Str("Hello 'world'!".into())),384		);385		assert_eq!(parse!(r#"'\\\\'"#), el!(Expr::Str("\\\\".into())),);386	}387388	#[test]389	fn string_unescaping() {390		assert_eq!(391			parse!(r#""Hello\nWorld""#),392			el!(Expr::Str("Hello\nWorld".into())),393		);394	}395396	#[test]397	fn string_verbantim() {398		assert_eq!(399			parse!(r#"@"Hello\n""World""""#),400			el!(Expr::Str("Hello\\n\"World\"".into())),401		);402	}403404	#[test]405	fn imports() {406		assert_eq!(407			parse!("import \"hello\""),408			el!(Expr::Import(PathBuf::from("hello"))),409		);410		assert_eq!(411			parse!("importstr \"garnish.txt\""),412			el!(Expr::ImportStr(PathBuf::from("garnish.txt")))413		);414	}415416	#[test]417	fn empty_object() {418		assert_eq!(parse!("{}"), el!(Expr::Obj(ObjBody::MemberList(vec![]))));419	}420421	#[test]422	fn basic_math() {423		assert_eq!(424			parse!("2+2*2"),425			el!(Expr::BinaryOp(426				el!(Expr::Num(2.0)),427				Add,428				el!(Expr::BinaryOp(429					el!(Expr::Num(2.0)),430					Mul,431					el!(Expr::Num(2.0))432				))433			))434		);435	}436437	#[test]438	fn basic_math_with_indents() {439		assert_eq!(parse!("2	+ 	  2	  *	2   	"), expressions::basic_math());440	}441442	#[test]443	fn basic_math_parened() {444		assert_eq!(445			parse!("2+(2+2*2)"),446			el!(Expr::BinaryOp(447				el!(Expr::Num(2.0)),448				Add,449				el!(Expr::Parened(expressions::basic_math())),450			))451		);452	}453454	/// Comments should not affect parsing455	#[test]456	fn comments() {457		assert_eq!(458			parse!("2//comment\n+//comment\n3/*test*/*/*test*/4"),459			el!(Expr::BinaryOp(460				el!(Expr::Num(2.0)),461				Add,462				el!(Expr::BinaryOp(463					el!(Expr::Num(3.0)),464					Mul,465					el!(Expr::Num(4.0))466				))467			))468		);469	}470471	/// Comments should be able to be escaped472	#[test]473	fn comment_escaping() {474		assert_eq!(475			parse!("2/*\\*/+*/ - 22"),476			el!(Expr::BinaryOp(477				el!(Expr::Num(2.0)),478				Sub,479				el!(Expr::Num(22.0))480			))481		);482	}483484	#[test]485	fn suffix() {486		// assert_eq!(parse!("std.test"), el!(Expr::Num(2.2)));487		// assert_eq!(parse!("std(2)"), el!(Expr::Num(2.2)));488		// assert_eq!(parse!("std.test(2)"), el!(Expr::Num(2.2)));489		// assert_eq!(parse!("a[b]"), el!(Expr::Num(2.2)))490	}491492	#[test]493	fn array_comp() {494		use Expr::*;495		assert_eq!(496			parse!("[std.deepJoin(x) for x in arr]"),497			el!(ArrComp(498				el!(Apply(499					el!(Index(el!(Var("std".into())), el!(Str("deepJoin".into())))),500					ArgsDesc::new(vec![el!(Var("x".into()))], vec![]),501					false,502				)),503				vec![CompSpec::ForSpec(ForSpecData(504					"x".into(),505					el!(Var("arr".into()))506				))]507			)),508		)509	}510511	#[test]512	fn reserved() {513		use Expr::*;514		assert_eq!(parse!("null"), el!(Literal(LiteralType::Null)));515		assert_eq!(parse!("nulla"), el!(Var("nulla".into())));516	}517518	#[test]519	fn multiple_args_buf() {520		parse!("a(b, null_fields)");521	}522523	#[test]524	fn infix_precedence() {525		use Expr::*;526		assert_eq!(527			parse!("!a && !b"),528			el!(BinaryOp(529				el!(UnaryOp(UnaryOpType::Not, el!(Var("a".into())))),530				And,531				el!(UnaryOp(UnaryOpType::Not, el!(Var("b".into()))))532			))533		);534	}535536	#[test]537	fn infix_precedence_division() {538		use Expr::*;539		assert_eq!(540			parse!("!a / !b"),541			el!(BinaryOp(542				el!(UnaryOp(UnaryOpType::Not, el!(Var("a".into())))),543				Div,544				el!(UnaryOp(UnaryOpType::Not, el!(Var("b".into()))))545			))546		);547	}548549	#[test]550	fn double_negation() {551		use Expr::*;552		assert_eq!(553			parse!("!!a"),554			el!(UnaryOp(555				UnaryOpType::Not,556				el!(UnaryOp(UnaryOpType::Not, el!(Var("a".into()))))557			))558		)559	}560561	#[test]562	fn array_test_error() {563		parse!("[a for a in b if c for e in f]");564		//                    ^^^^ failed code565	}566567	#[test]568	fn missing_newline_between_comment_and_eof() {569		parse!(570			"{a:1}571572			//+213"573		);574	}575576	#[test]577	fn default_param_before_nondefault() {578		parse!("local x(foo = 'foo', bar) = null; null");579	}580581	#[test]582	fn can_parse_stdlib() {583		parse!(jrsonnet_stdlib::STDLIB_STR);584	}585586	#[test]587	fn add_location_info_to_all_sub_expressions() {588		use Expr::*;589590		let file_name: std::rc::Rc<std::path::Path> = PathBuf::from("/test.jsonnet").into();591		let expr = parse(592			"{} { local x = 1, x: x } + {}",593			&ParserSettings {594				loc_data: true,595				file_name: file_name.clone(),596			},597		)598		.unwrap();599		assert_eq!(600			expr,601			el_loc!(602				BinaryOp(603					el_loc!(604						ObjExtend(605							el_loc!(606								Obj(ObjBody::MemberList(vec![])),607								ExprLocation(file_name.clone(), 0, 2)608							),609							ObjBody::MemberList(vec![610								Member::BindStmt(BindSpec {611									name: "x".into(),612									params: None,613									value: el_loc!(614										Num(1.0),615										ExprLocation(file_name.clone(), 15, 16)616									)617								}),618								Member::Field(FieldMember {619									name: FieldName::Fixed("x".into()),620									plus: false,621									params: None,622									visibility: Visibility::Normal,623									value: el_loc!(624										Var("x".into()),625										ExprLocation(file_name.clone(), 21, 22)626									),627								})628							])629						),630						ExprLocation(file_name.clone(), 0, 24)631					),632					BinaryOpType::Add,633					el_loc!(634						Obj(ObjBody::MemberList(vec![])),635						ExprLocation(file_name.clone(), 27, 29)636					),637				),638				ExprLocation(file_name.clone(), 0, 29),639			),640		);641	}642	// From source code643	/*644	#[bench]645	fn bench_parse_peg(b: &mut Bencher) {646		b.iter(|| parse!(jrsonnet_stdlib::STDLIB_STR))647	}648	*/649}
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,