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

difftreelog

refactor drop im-rc

tnwykoutYaroslav Bolyukin2026-05-05parent: #5eebad4.patch.diff
in: master
Not worth it. It gives some improvements in some benchmarks, but at the
cost of much complex memory layout, I do not consider that a win

5 files changed

modifiedCargo.lockdiffbeforeafterboth
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -163,15 +163,6 @@
 checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3"
 
 [[package]]
-name = "bitmaps"
-version = "2.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "031043d04099746d8db04daf1fa424b2bc8bd69d92b25962dcde24da39ab64a2"
-dependencies = [
- "typenum",
-]
-
-[[package]]
 name = "block-buffer"
 version = "0.12.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -702,21 +693,6 @@
 checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954"
 
 [[package]]
-name = "im-rc"
-version = "15.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "af1955a75fa080c677d3972822ec4bad316169ab1cfc6c257a942c2265dbe5fe"
-dependencies = [
- "bitmaps",
- "rand_core 0.6.4",
- "rand_xoshiro",
- "refpool",
- "sized-chunks",
- "typenum",
- "version_check",
-]
-
-[[package]]
 name = "indexmap"
 version = "2.14.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -829,7 +805,6 @@
  "drop_bomb",
  "educe",
  "hi-doc",
- "im-rc",
  "insta",
  "jrsonnet-gcmodule",
  "jrsonnet-interner",
@@ -881,7 +856,6 @@
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "21dd97b40cbfb2043094219f95d96519858ba1aee4e8260eb048a1774832a517"
 dependencies = [
- "im-rc",
  "jrsonnet-gcmodule-derive",
 ]
 
@@ -1356,7 +1330,7 @@
 checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea"
 dependencies = [
  "rand_chacha",
- "rand_core 0.9.5",
+ "rand_core",
 ]
 
 [[package]]
@@ -1366,14 +1340,8 @@
 checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
 dependencies = [
  "ppv-lite86",
- "rand_core 0.9.5",
+ "rand_core",
 ]
-
-[[package]]
-name = "rand_core"
-version = "0.6.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
 
 [[package]]
 name = "rand_core"
@@ -1382,15 +1350,6 @@
 checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c"
 dependencies = [
  "getrandom 0.3.4",
-]
-
-[[package]]
-name = "rand_xoshiro"
-version = "0.6.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6f97cdb2a36ed4183de61b2f824cc45c9f1037f28afe0a322e9fff4c108b5aaa"
-dependencies = [
- "rand_core 0.6.4",
 ]
 
 [[package]]
@@ -1432,12 +1391,6 @@
 ]
 
 [[package]]
-name = "refpool"
-version = "0.4.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "369e86b80fa7dc8c561dd9613a5bf25c59d2d3073cd66c47fd9e39802f0ecb58"
-
-[[package]]
 name = "regex"
 version = "1.12.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1639,17 +1592,6 @@
 version = "2.7.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa"
-
-[[package]]
-name = "sized-chunks"
-version = "0.6.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "16d69225bde7a69b235da73377861095455d298f2b970996eec25ddbb42b3d1e"
-dependencies = [
- "bitmaps",
- "refpool",
- "typenum",
-]
 
 [[package]]
 name = "smallvec"
modifiedCargo.tomldiffbeforeafterboth
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -22,7 +22,7 @@
 jrsonnet-cli = { path = "./crates/jrsonnet-cli", version = "0.5.0-pre98" }
 jrsonnet-types = { path = "./crates/jrsonnet-types", version = "0.5.0-pre98" }
 jrsonnet-formatter = { path = "./crates/jrsonnet-formatter", version = "0.5.0-pre98" }
-jrsonnet-gcmodule = { version = "0.4.4", features = ["im-rc"] }
+jrsonnet-gcmodule = { version = "0.4.4" }
 # Diagnostics.
 # hi-doc is my library, which handles text formatting very well, but isn't polished enough yet
 # Previous implementation was based on annotate-snippets, which I don't like for many reasons.
modifiedcrates/jrsonnet-evaluator/Cargo.tomldiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/Cargo.toml
+++ b/crates/jrsonnet-evaluator/Cargo.toml
@@ -76,7 +76,6 @@
   "Hash",
   "PartialEq",
 ] }
-im-rc = { version = "15.1.0", features = ["pool"] }
 smallvec = "1.15.1"
 drop_bomb.workspace = true
 
modifiedcrates/jrsonnet-evaluator/src/obj/mod.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/obj/mod.rs
+++ b/crates/jrsonnet-evaluator/src/obj/mod.rs
@@ -11,7 +11,6 @@
 };
 
 use educe::Educe;
-use im_rc::{Vector, vector};
 use jrsonnet_gcmodule::{Acyclic, Cc, Trace, Weak, cc_dyn};
 use jrsonnet_interner::IStr;
 use jrsonnet_ir::Span;
@@ -97,7 +96,7 @@
 
 // 0 - add
 //  12 - visibility
-#[derive(Clone, Copy)]
+#[derive(Clone, Copy, Acyclic)]
 pub struct ObjFieldFlags(u8);
 impl ObjFieldFlags {
 	fn new(add: bool, visibility: Visibility) -> Self {
@@ -136,7 +135,6 @@
 #[allow(clippy::module_name_repetitions)]
 #[derive(Debug, Trace)]
 pub struct ObjMember {
-	#[trace(skip)]
 	flags: ObjFieldFlags,
 	original_index: FieldIndex,
 	pub invoke: MaybeUnbound,
@@ -240,7 +238,7 @@
 #[derive(Trace, Educe)]
 #[educe(Debug)]
 struct ObjValueInner {
-	cores: Vector<CcObjectCore>,
+	cores: Vec<CcObjectCore>,
 	assertions_ran: Cell<bool>,
 	has_assertions: bool,
 	value_cache: RefCell<FxHashMap<(IStr, CoreIdx), CacheValue>>,
@@ -268,7 +266,7 @@
 
 thread_local! {
 	static EMPTY_OBJ: ObjValue = ObjValue(Cc::new(ObjValueInner {
-		cores: vector![],
+		cores: vec![],
 		assertions_ran: Cell::new(true),
 		has_assertions: false,
 		value_cache: RefCell::default(),
@@ -495,7 +493,10 @@
 
 	#[must_use]
 	pub fn extend_from(&self, sup: Self) -> Self {
-		let cores = sup.0.cores.clone() + self.0.cores.clone();
+		let mut cores = Vec::with_capacity(sup.0.cores.len() + self.0.cores.len());
+		cores.extend(sup.0.cores.iter().cloned());
+		cores.extend(self.0.cores.iter().cloned());
+
 		let has_assertions = sup.0.has_assertions || self.0.has_assertions;
 		ObjValue(Cc::new(ObjValueInner {
 			cores,
modifiedcrates/jrsonnet-evaluator/src/obj/oop.rsdiffbeforeafterboth
after · crates/jrsonnet-evaluator/src/obj/oop.rs
1use std::{2	cell::{Cell, RefCell},3	fmt, mem,4	ops::ControlFlow,5};67use jrsonnet_gcmodule::{Cc, Trace};8use jrsonnet_ir::IStr;9use rustc_hash::{FxHashMap, FxHashSet};1011use super::{12	CcObjectAssertion, CcObjectCore, EnumFields, EnumFieldsHandler, FieldVisibility, GetFor,13	HasFieldIncludeHidden, ObjMember, ObjMemberBuilder, ObjValue, ObjValueInner, ObjectAssertion,14	ObjectCore, OmitFieldsCore, SupThis,15	ordering::{FieldIndex, SuperDepth},16};17use crate::{18	CcUnbound, MaybeUnbound, Result, Thunk, Unbound, Val, bail,19	error::ErrorKind::*,20	function::{CallLocation, FuncVal},21	gc::WithCapacityExt as _,22	in_frame,23};2425#[allow(clippy::module_name_repetitions)]26#[derive(Trace, Default)]27#[trace(tracking(force))]28pub struct OopObject {29	assertion: Option<CcObjectAssertion>,30	this_entries: FxHashMap<IStr, ObjMember>,31}32impl fmt::Debug for OopObject {33	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {34		f.debug_struct("OopObject")35			.field("this_entries", &self.this_entries)36			.finish_non_exhaustive()37	}38}39impl OopObject {40	fn is_empty(&self) -> bool {41		self.assertion.is_none() && self.this_entries.is_empty()42	}43}44impl OopObject {45	pub fn new(46		this_entries: FxHashMap<IStr, ObjMember>,47		assertion: Option<CcObjectAssertion>,48	) -> Self {49		Self {50			assertion,51			this_entries,52		}53	}54}5556impl ObjectCore for OopObject {57	fn enum_fields_core(58		&self,59		super_depth: &mut SuperDepth,60		handler: &mut EnumFieldsHandler<'_>,61	) -> bool {62		for (name, member) in &self.this_entries {63			if matches!(64				handler(65					*super_depth,66					member.original_index,67					name.clone(),68					EnumFields::Normal(member.flags.visibility()),69				),70				ControlFlow::Break(())71			) {72				return false;73			}74		}75		true76	}7778	fn has_field_include_hidden_core(&self, name: IStr) -> HasFieldIncludeHidden {79		if self.this_entries.contains_key(&name) {80			HasFieldIncludeHidden::Exists81		} else {82			HasFieldIncludeHidden::NotFound83		}84	}8586	fn get_for_core(&self, key: IStr, sup_this: SupThis, omit_only: bool) -> Result<GetFor> {87		if omit_only {88			return Ok(GetFor::NotFound);89		}90		match self.this_entries.get(&key) {91			Some(k) => {92				let v = k.invoke.evaluate(sup_this)?;93				Ok(if k.flags.add() {94					GetFor::SuperPlus(v)95				} else {96					GetFor::Final(v)97				})98			}99			None => Ok(GetFor::NotFound),100		}101	}102	fn field_visibility_core(&self, name: IStr) -> FieldVisibility {103		self.this_entries104			.get(&name)105			.map_or(FieldVisibility::NotFound, |f| {106				FieldVisibility::Found(f.flags.visibility())107			})108	}109110	fn run_assertions_core(&self, sup_this: SupThis) -> Result<()> {111		if let Some(assertion) = &self.assertion {112			assertion.0.run(sup_this)?;113		}114		Ok(())115	}116}117118#[allow(clippy::module_name_repetitions)]119pub struct ObjValueBuilder {120	sup: Vec<CcObjectCore>,121	has_assertions: bool,122123	new: OopObject,124	next_field_index: FieldIndex,125}126impl ObjValueBuilder {127	pub fn new() -> Self {128		Self::with_capacity(0)129	}130	pub fn with_capacity(capacity: usize) -> Self {131		Self {132			sup: Vec::new(),133			has_assertions: false,134			new: OopObject::new(FxHashMap::with_capacity(capacity), None),135			next_field_index: FieldIndex::default(),136		}137	}138	pub fn reserve_fields(&mut self, capacity: usize) {139		self.new.this_entries.reserve(capacity);140	}141	pub fn with_super(&mut self, super_obj: ObjValue) -> &mut Self {142		self.has_assertions |= super_obj.0.has_assertions;143		self.sup.clone_from(&super_obj.0.cores);144		self145	}146147	pub fn assert(&mut self, assertion: impl ObjectAssertion + 'static) -> &mut Self {148		assert!(149			self.new.assertion.is_none(),150			"one OopObject can only have one assertion"151		);152		self.has_assertions = true;153		self.new.assertion = Some(CcObjectAssertion::new(assertion));154		self155	}156	pub fn field(&mut self, name: impl Into<IStr>) -> ObjMemberBuilder<ValueBuilder<'_>> {157		let field_index = self.next_field_index;158		self.next_field_index = self.next_field_index.next();159		ObjMemberBuilder::new(ValueBuilder(self), name.into(), field_index)160	}161	/// Preset for common method definiton pattern:162	/// Create a hidden field with the function value.163	///164	/// `.field(name).hide().value(Val::function(value))`165	pub fn method(&mut self, name: impl Into<IStr>, value: impl Into<FuncVal>) -> &mut Self {166		self.field(name).hide().value(Val::Func(value.into()));167		self168	}169	pub fn try_method(170		&mut self,171		name: impl Into<IStr>,172		value: impl Into<FuncVal>,173	) -> Result<&mut Self> {174		self.field(name).hide().try_value(Val::Func(value.into()))?;175		Ok(self)176	}177178	pub fn extend_with_core(&mut self, core: impl ObjectCore) {179		self.commit();180		self.sup.push(CcObjectCore::new(core));181	}182183	fn commit(&mut self) {184		if !self.new.is_empty() {185			self.sup.push(CcObjectCore::new(mem::take(&mut self.new)));186		}187		self.next_field_index = FieldIndex::default();188	}189190	pub fn with_fields_omitted(&mut self, omit: FxHashSet<IStr>) {191		self.commit();192		self.sup.push(CcObjectCore::new(OmitFieldsCore {193			omit,194			prev_layers: self.sup.len(),195		}));196	}197198	pub fn build(mut self) -> ObjValue {199		self.commit();200		if self.sup.is_empty() {201			return ObjValue::empty();202		}203		let has_assertions = self.has_assertions;204		ObjValue(Cc::new(ObjValueInner {205			cores: self.sup,206			assertions_ran: Cell::new(!has_assertions),207			has_assertions,208			value_cache: RefCell::default(),209		}))210	}211}212impl Default for ObjValueBuilder {213	fn default() -> Self {214		Self::with_capacity(0)215	}216}217218pub struct ValueBuilder<'v>(&'v mut ObjValueBuilder);219impl ObjMemberBuilder<ValueBuilder<'_>> {220	/// Inserts value, replacing if it is already defined221	pub fn value(self, value: impl Into<Val>) {222		let (receiver, name, member) =223			self.build_member(MaybeUnbound::Bound(Thunk::evaluated(value.into())));224		let entry = receiver.0.new.this_entries.entry(name);225		entry.insert_entry(member);226	}227	/// Inserts thunk, replacing if it is already defined228	pub fn thunk(self, value: impl Into<Thunk<Val>>) {229		let (receiver, name, member) = self.build_member(MaybeUnbound::Bound(value.into()));230		let entry = receiver.0.new.this_entries.entry(name);231		entry.insert_entry(member);232	}233234	/// Tries to insert value, returns an error if it was already defined235	pub fn try_value(self, value: impl Into<Val>) -> Result<()> {236		self.try_thunk(Thunk::evaluated(value.into()))237	}238	pub fn try_thunk(self, value: impl Into<Thunk<Val>>) -> Result<()> {239		self.binding(MaybeUnbound::Bound(value.into()))240	}241	pub fn bindable(self, bindable: impl Unbound<Bound = Val>) -> Result<()> {242		self.binding(MaybeUnbound::Unbound(CcUnbound::new(bindable)))243	}244	pub fn binding(self, binding: MaybeUnbound) -> Result<()> {245		let (receiver, name, member) = self.build_member(binding);246		let location = member.location.clone();247		let old = receiver.0.new.this_entries.insert(name.clone(), member);248		if old.is_some() {249			in_frame(250				CallLocation(location.as_ref()),251				|| format!("field <{}> initializtion", name.clone()),252				|| bail!(DuplicateFieldName(name.clone())),253			)?;254		}255		Ok(())256	}257}